Add ovf export capability to vsphere builders (#8764)

* add ovf export capability to vsphere builders

* remove unneeded floppy ejection

* add prepare step for export. updated output directory to be the actual destination directory

* add step export documentation

* add extra export options

* add ui messages for export step

Co-authored-by: Megan Marsh <megan@hashicorp.com>
Co-authored-by: Wilken Rivera <dev@wilkenrivera.com>
This commit is contained in:
jhawk28 2020-03-19 13:51:43 -04:00 committed by GitHub
parent 9aba27bc83
commit 99b0b98311
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1268 additions and 194 deletions

View File

@ -82,6 +82,17 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
},
)
if b.config.Export != nil {
steps = append(steps, &common.StepExport{
Name: b.config.Export.Name,
Force: b.config.Export.Force,
Images: b.config.Export.Images,
Manifest: b.config.Export.Manifest,
OutputDir: b.config.Export.OutputDir.OutputDir,
Options: b.config.Export.Options,
})
}
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, state)

View File

@ -32,6 +32,8 @@ type Config struct {
// Convert VM to a template. Defaults to `false`.
ConvertToTemplate bool `mapstructure:"convert_to_template"`
Export *common.ExportConfig `mapstructure:"export"`
ctx interpolate.Context
}
@ -53,6 +55,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)
if c.Export != nil {
errs = packer.MultiErrorAppend(errs, c.Export.Prepare(&c.ctx, &c.LocationConfig, &c.PackerConfig)...)
}
if len(errs.Errors) > 0 {
return nil, errs

View File

@ -3,6 +3,7 @@ package clone
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer/builder/vsphere/common"
"github.com/zclconf/go-cty/cty"
)
@ -92,6 +93,7 @@ type FlatConfig struct {
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout"`
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot"`
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template"`
Export *common.FlatExportConfig `mapstructure:"export" cty:"export"`
}
// FlatMapstructure returns a new FlatConfig.
@ -189,6 +191,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"create_snapshot": &hcldec.AttrSpec{Name: "create_snapshot", Type: cty.Bool, Required: false},
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
"export": &hcldec.BlockSpec{TypeName: "export", Nested: hcldec.ObjectSpec((*common.FlatExportConfig)(nil).HCL2Spec())},
}
return s
}

View File

@ -0,0 +1,30 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type OutputConfig
package common
import (
"fmt"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/template/interpolate"
)
type OutputConfig struct {
// This setting specifies the directory that
// artifacts from the build, such as the virtual machine files and disks,
// will be output to. The path to the directory may be relative or
// absolute. If relative, the path is relative to the working directory
// packer is executed from. This directory must not exist or, if
// created, must be empty prior to running the builder. By default this is
// "output-BUILDNAME" where "BUILDNAME" is the name of the build.
OutputDir string `mapstructure:"output_directory" required:"false"`
}
func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
if c.OutputDir == "" {
c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName)
}
return nil
}

View File

@ -0,0 +1,30 @@
// Code generated by "mapstructure-to-hcl2 -type OutputConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatOutputConfig is an auto-generated flat version of OutputConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatOutputConfig struct {
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory"`
}
// FlatMapstructure returns a new FlatOutputConfig.
// FlatOutputConfig is an auto-generated flat version of OutputConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*OutputConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatOutputConfig)
}
// HCL2Spec returns the hcl spec of a OutputConfig.
// This spec is used by HCL to read the fields of OutputConfig.
// The decoded values from this spec will then be applied to a FlatOutputConfig.
func (*FlatOutputConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
}
return s
}

View File

@ -0,0 +1,304 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type ExportConfig
package common
import (
"bytes"
"context"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"io"
"os"
"path/filepath"
"strings"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/pkg/errors"
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
// You may optionally export an ovf from VSphere to the instance running Packer.
//
// Example usage:
//
// ```json
// ...
// "vm_name": "example-ubuntu",
// ...
// "export": {
// "force": true,
// "output_directory": "./output_vsphere"
// },
// ```
// The above configuration would create the following files:
//
// ```
// ./output_vsphere/example-ubuntu-disk-0.vmdk
// ./output_vsphere/example-ubuntu.mf
// ./output_vsphere/example-ubuntu.ovf
// ```
type ExportConfig struct {
// name of the ovf. defaults to the name of the VM
Name string `mapstructure:"name"`
// overwrite ovf if it exists
Force bool `mapstructure:"force"`
// include iso and img image files that are attached to the VM
Images bool `mapstructure:"images"`
// generate manifest using sha1, sha256, sha512. Defaults to 'sha256'. Use 'none' for no manifest.
Manifest string `mapstructure:"manifest"`
OutputDir OutputConfig `mapstructure:",squash"`
// Advanced ovf export options. Options can include:
// * mac - MAC address is exported for all ethernet devices
// * uuid - UUID is exported for all virtual machines
// * extraconfig - all extra configuration options are exported for a virtual machine
// * nodevicesubtypes - resource subtypes for CD/DVD drives, floppy drives, and serial and parallel ports are not exported
//
// For example, adding the following export config option would output the mac addresses for all Ethernet devices in the ovf file:
// ```json
// ...
// "export": {
// "options": ["mac"]
// },
// ```
Options []string `mapstructure:"options"`
}
var sha = map[string]func() hash.Hash{
"none": nil,
"sha1": sha1.New,
"sha256": sha256.New,
"sha512": sha512.New,
}
func (c *ExportConfig) Prepare(ctx *interpolate.Context, lc *LocationConfig, pc *common.PackerConfig) []error {
var errs *packer.MultiError
errs = packer.MultiErrorAppend(errs, c.OutputDir.Prepare(ctx, pc)...)
// manifest should default to sha256
if c.Manifest == "" {
c.Manifest = "sha256"
}
if _, ok := sha[c.Manifest]; !ok {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("unknown hash: %s. available options include available options being 'none', 'sha1', 'sha256', 'sha512'", c.Manifest))
}
if c.Name == "" {
c.Name = lc.VMName
}
target := getTarget(c.OutputDir.OutputDir, c.Name)
if !c.Force {
if _, err := os.Stat(target); err == nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("file already exists: %s", target))
}
}
if err := os.MkdirAll(c.OutputDir.OutputDir, 0750); err != nil {
errs = packer.MultiErrorAppend(errs, errors.Wrap(err, "unable to make directory for export"))
}
if errs != nil && len(errs.Errors) > 0 {
return errs.Errors
}
return nil
}
func getTarget(dir string, name string) string {
return filepath.Join(dir, name+".ovf")
}
type StepExport struct {
Name string
Force bool
Images bool
Manifest string
OutputDir string
Options []string
mf bytes.Buffer
}
func (s *StepExport) Cleanup(multistep.StateBag) {
}
func (s *StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*driver.VirtualMachine)
ui.Message("Starting export...")
lease, err := vm.Export()
if err != nil {
state.Put("error", errors.Wrap(err, "error exporting vm"))
return multistep.ActionHalt
}
info, err := lease.Wait(ctx, nil)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
u := lease.StartUpdater(ctx, info)
defer u.Done()
cdp := types.OvfCreateDescriptorParams{
Name: s.Name,
}
m := vm.NewOvfManager()
if len(s.Options) > 0 {
exportOptions, err := vm.GetOvfExportOptions(m)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
var unknown []string
for _, option := range s.Options {
found := false
for _, exportOpt := range exportOptions {
if exportOpt.Option == option {
found = true
break
}
}
if !found {
unknown = append(unknown, option)
}
cdp.ExportOption = append(cdp.ExportOption, option)
}
// only printing error message because the unknown options are just ignored by vcenter
if len(unknown) > 0 {
ui.Error(fmt.Sprintf("unknown export options %s", strings.Join(unknown, ",")))
}
}
for _, i := range info.Items {
if !s.include(&i) {
continue
}
if !strings.HasPrefix(i.Path, s.Name) {
i.Path = s.Name + "-" + i.Path
}
ui.Message("Downloading: " + i.File().Path)
err = s.Download(ctx, lease, i)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
ui.Message("Exporting file: " + i.File().Path)
cdp.OvfFiles = append(cdp.OvfFiles, i.File())
}
if err = lease.Complete(ctx); err != nil {
state.Put("error", errors.Wrap(err, "unable to complete lease"))
return multistep.ActionHalt
}
desc, err := vm.CreateDescriptor(m, cdp)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to create descriptor"))
return multistep.ActionHalt
}
target := getTarget(s.OutputDir, s.Name)
file, err := os.Create(target)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to create file: "+target))
return multistep.ActionHalt
}
var w io.Writer = file
h, ok := s.newHash()
if ok {
w = io.MultiWriter(file, h)
}
ui.Message("Writing ovf...")
_, err = io.WriteString(w, desc.OvfDescriptor)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to write descriptor"))
return multistep.ActionHalt
}
if err = file.Close(); err != nil {
state.Put("error", errors.Wrap(err, "unable to close descriptor"))
return multistep.ActionHalt
}
if s.Manifest == "none" {
// manifest does not need to be created, return
return multistep.ActionContinue
}
ui.Message("Creating manifest...")
s.addHash(filepath.Base(target), h)
file, err = os.Create(filepath.Join(s.OutputDir, s.Name+".mf"))
if err != nil {
state.Put("error", errors.Wrap(err, "unable to create manifest"))
return multistep.ActionHalt
}
_, err = io.Copy(file, &s.mf)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to write manifest"))
return multistep.ActionHalt
}
err = file.Close()
if err != nil {
state.Put("error", errors.Wrap(err, "unable to close file"))
return multistep.ActionHalt
}
ui.Message("Finished exporting...")
return multistep.ActionContinue
}
func (s *StepExport) include(item *nfc.FileItem) bool {
if s.Images {
return true
}
return filepath.Ext(item.Path) == ".vmdk"
}
func (s *StepExport) newHash() (hash.Hash, bool) {
// check if function is nil to handle the 'none' case
if h, ok := sha[s.Manifest]; ok && h != nil {
return h(), true
}
return nil, false
}
func (s *StepExport) addHash(p string, h hash.Hash) {
_, _ = fmt.Fprintf(&s.mf, "%s(%s)= %x\n", strings.ToUpper(s.Manifest), p, h.Sum(nil))
}
func (s *StepExport) Download(ctx context.Context, lease *nfc.Lease, item nfc.FileItem) error {
path := filepath.Join(s.OutputDir, item.Path)
opts := soap.Download{}
if h, ok := s.newHash(); ok {
opts.Writer = h
defer s.addHash(item.Path, h)
}
return lease.DownloadFile(ctx, path, item, opts)
}

View File

@ -0,0 +1,40 @@
// Code generated by "mapstructure-to-hcl2 -type ExportConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatExportConfig is an auto-generated flat version of ExportConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatExportConfig struct {
Name *string `mapstructure:"name" cty:"name"`
Force *bool `mapstructure:"force" cty:"force"`
Images *bool `mapstructure:"images" cty:"images"`
Manifest *string `mapstructure:"manifest" cty:"manifest"`
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory"`
Options []string `mapstructure:"options" cty:"options"`
}
// FlatMapstructure returns a new FlatExportConfig.
// FlatExportConfig is an auto-generated flat version of ExportConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*ExportConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatExportConfig)
}
// HCL2Spec returns the hcl spec of a ExportConfig.
// This spec is used by HCL to read the fields of ExportConfig.
// The decoded values from this spec will then be applied to a FlatExportConfig.
func (*FlatExportConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false},
"force": &hcldec.AttrSpec{Name: "force", Type: cty.Bool, Required: false},
"images": &hcldec.AttrSpec{Name: "images", Type: cty.Bool, Required: false},
"manifest": &hcldec.AttrSpec{Name: "manifest", Type: cty.String, Required: false},
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
"options": &hcldec.AttrSpec{Name: "options", Type: cty.List(cty.String), Required: false},
}
return s
}

View File

@ -8,6 +8,11 @@ import (
"strings"
"time"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
@ -688,6 +693,27 @@ func (vm *VirtualMachine) AddConfigParams(params map[string]string) error {
return err
}
func (vm *VirtualMachine) Export() (*nfc.Lease, error) {
return vm.vm.Export(vm.driver.ctx)
}
func (vm *VirtualMachine) CreateDescriptor(m *ovf.Manager, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
return m.CreateDescriptor(vm.driver.ctx, vm.vm, cdp)
}
func (vm *VirtualMachine) NewOvfManager() *ovf.Manager {
return ovf.NewManager(vm.vm.Client())
}
func (vm *VirtualMachine) GetOvfExportOptions(m *ovf.Manager) ([]types.OvfOptionInfo, error) {
var mgr mo.OvfManager
err := property.DefaultCollector(vm.vm.Client()).RetrieveOne(vm.driver.ctx, m.Reference(), nil, &mgr)
if err != nil {
return nil, err
}
return mgr.OvfExportOption, nil
}
func findNetworkAdapter(l object.VirtualDeviceList) (types.BaseVirtualEthernetCard, error) {
c := l.SelectByType((*types.VirtualEthernetCard)(nil))
if len(c) == 0 {

View File

@ -132,6 +132,16 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
},
)
if b.config.Export != nil {
steps = append(steps, &common.StepExport{
Name: b.config.Export.Name,
Force: b.config.Export.Force,
Images: b.config.Export.Images,
Manifest: b.config.Export.Manifest,
OutputDir: b.config.Export.OutputDir.OutputDir,
})
}
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, state)

View File

@ -39,6 +39,9 @@ type Config struct {
CreateSnapshot bool `mapstructure:"create_snapshot"`
// Convert VM to a template. Defaults to `false`.
ConvertToTemplate bool `mapstructure:"convert_to_template"`
// Configuration for exporting VM to an ovf file.
// The VM will not be exported if no [Export Configuration](#export-configuration) is specified.
Export *common.ExportConfig `mapstructure:"export"`
ctx interpolate.Context
}
@ -77,6 +80,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)
if c.Export != nil {
errs = packer.MultiErrorAppend(errs, c.Export.Prepare(&c.ctx, &c.LocationConfig, &c.PackerConfig)...)
}
if len(errs.Errors) > 0 {
return warnings, errs

View File

@ -3,6 +3,7 @@ package iso
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer/builder/vsphere/common"
"github.com/zclconf/go-cty/cty"
)
@ -119,6 +120,7 @@ type FlatConfig struct {
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout"`
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot"`
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template"`
Export *common.FlatExportConfig `mapstructure:"export" cty:"export"`
}
// FlatMapstructure returns a new FlatConfig.
@ -243,6 +245,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"create_snapshot": &hcldec.AttrSpec{Name: "create_snapshot", Type: cty.Bool, Required: false},
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
"export": &hcldec.BlockSpec{TypeName: "export", Nested: hcldec.ObjectSpec((*common.FlatExportConfig)(nil).HCL2Spec())},
}
return s
}

78
vendor/github.com/vmware/govmomi/ovf/cim.go generated vendored Normal file
View File

@ -0,0 +1,78 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_VirtualSystemSettingData.xsd
*/
type CIMVirtualSystemSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
AutomaticRecoveryAction *uint8 `xml:"AutomaticRecoveryAction"`
AutomaticShutdownAction *uint8 `xml:"AutomaticShutdownAction"`
AutomaticStartupAction *uint8 `xml:"AutomaticStartupAction"`
AutomaticStartupActionDelay *string `xml:"AutomaticStartupActionDelay>Interval"`
AutomaticStartupActionSequenceNumber *uint16 `xml:"AutomaticStartupActionSequenceNumber"`
Caption *string `xml:"Caption"`
ConfigurationDataRoot *string `xml:"ConfigurationDataRoot"`
ConfigurationFile *string `xml:"ConfigurationFile"`
ConfigurationID *string `xml:"ConfigurationID"`
CreationTime *string `xml:"CreationTime"`
Description *string `xml:"Description"`
LogDataRoot *string `xml:"LogDataRoot"`
Notes []string `xml:"Notes"`
RecoveryFile *string `xml:"RecoveryFile"`
SnapshotDataRoot *string `xml:"SnapshotDataRoot"`
SuspendDataRoot *string `xml:"SuspendDataRoot"`
SwapFileDataRoot *string `xml:"SwapFileDataRoot"`
VirtualSystemIdentifier *string `xml:"VirtualSystemIdentifier"`
VirtualSystemType *string `xml:"VirtualSystemType"`
}
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_ResourceAllocationSettingData.xsd
*/
type CIMResourceAllocationSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
ResourceType *uint16 `xml:"ResourceType"`
OtherResourceType *string `xml:"OtherResourceType"`
ResourceSubType *string `xml:"ResourceSubType"`
AddressOnParent *string `xml:"AddressOnParent"`
Address *string `xml:"Address"`
AllocationUnits *string `xml:"AllocationUnits"`
AutomaticAllocation *bool `xml:"AutomaticAllocation"`
AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
Caption *string `xml:"Caption"`
Connection []string `xml:"Connection"`
ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
Description *string `xml:"Description"`
HostResource []string `xml:"HostResource"`
Limit *uint64 `xml:"Limit"`
MappingBehavior *uint `xml:"MappingBehavior"`
Parent *string `xml:"Parent"`
PoolID *string `xml:"PoolID"`
Reservation *uint64 `xml:"Reservation"`
VirtualQuantity *uint `xml:"VirtualQuantity"`
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
Weight *uint `xml:"Weight"`
}

25
vendor/github.com/vmware/govmomi/ovf/doc.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package ovf provides functionality to unmarshal and inspect the structure
of an OVF file. It is not a complete implementation of the specification and
is intended to be used to import virtual infrastructure into vSphere.
For a complete specification of the OVF standard, refer to:
https://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.0.pdf
*/
package ovf

99
vendor/github.com/vmware/govmomi/ovf/env.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"bytes"
"fmt"
"github.com/vmware/govmomi/vim25/xml"
)
const (
ovfEnvHeader = `<Environment
xmlns="http://schemas.dmtf.org/ovf/environment/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
xmlns:ve="http://www.vmware.com/schema/ovfenv"
oe:id=""
ve:esxId="%s">`
ovfEnvPlatformSection = `<PlatformSection>
<Kind>%s</Kind>
<Version>%s</Version>
<Vendor>%s</Vendor>
<Locale>%s</Locale>
</PlatformSection>`
ovfEnvPropertyHeader = `<PropertySection>`
ovfEnvPropertyEntry = `<Property oe:key="%s" oe:value="%s"/>`
ovfEnvPropertyFooter = `</PropertySection>`
ovfEnvFooter = `</Environment>`
)
type Env struct {
XMLName xml.Name `xml:"http://schemas.dmtf.org/ovf/environment/1 Environment"`
ID string `xml:"id,attr"`
EsxID string `xml:"http://www.vmware.com/schema/ovfenv esxId,attr"`
Platform *PlatformSection `xml:"PlatformSection"`
Property *PropertySection `xml:"PropertySection"`
}
type PlatformSection struct {
Kind string `xml:"Kind"`
Version string `xml:"Version"`
Vendor string `xml:"Vendor"`
Locale string `xml:"Locale"`
}
type PropertySection struct {
Properties []EnvProperty `xml:"Property"`
}
type EnvProperty struct {
Key string `xml:"key,attr"`
Value string `xml:"value,attr"`
}
// Marshal marshals Env to xml by using xml.Marshal.
func (e Env) Marshal() (string, error) {
x, err := xml.Marshal(e)
if err != nil {
return "", err
}
return fmt.Sprintf("%s%s", xml.Header, x), nil
}
// MarshalManual manually marshals Env to xml suitable for a vApp guest.
// It exists to overcome the lack of expressiveness in Go's XML namespaces.
func (e Env) MarshalManual() string {
var buffer bytes.Buffer
buffer.WriteString(xml.Header)
buffer.WriteString(fmt.Sprintf(ovfEnvHeader, e.EsxID))
buffer.WriteString(fmt.Sprintf(ovfEnvPlatformSection, e.Platform.Kind, e.Platform.Version, e.Platform.Vendor, e.Platform.Locale))
buffer.WriteString(fmt.Sprint(ovfEnvPropertyHeader))
for _, p := range e.Property.Properties {
buffer.WriteString(fmt.Sprintf(ovfEnvPropertyEntry, p.Key, p.Value))
}
buffer.WriteString(fmt.Sprint(ovfEnvPropertyFooter))
buffer.WriteString(fmt.Sprint(ovfEnvFooter))
return buffer.String()
}

191
vendor/github.com/vmware/govmomi/ovf/envelope.go generated vendored Normal file
View File

@ -0,0 +1,191 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
type Envelope struct {
References []File `xml:"References>File"`
// Package level meta-data
Annotation *AnnotationSection `xml:"AnnotationSection"`
Product *ProductSection `xml:"ProductSection"`
Network *NetworkSection `xml:"NetworkSection"`
Disk *DiskSection `xml:"DiskSection"`
OperatingSystem *OperatingSystemSection `xml:"OperatingSystemSection"`
Eula *EulaSection `xml:"EulaSection"`
VirtualHardware *VirtualHardwareSection `xml:"VirtualHardwareSection"`
ResourceAllocation *ResourceAllocationSection `xml:"ResourceAllocationSection"`
DeploymentOption *DeploymentOptionSection `xml:"DeploymentOptionSection"`
// Content: A VirtualSystem or a VirtualSystemCollection
VirtualSystem *VirtualSystem `xml:"VirtualSystem"`
}
type VirtualSystem struct {
Content
Annotation []AnnotationSection `xml:"AnnotationSection"`
Product []ProductSection `xml:"ProductSection"`
OperatingSystem []OperatingSystemSection `xml:"OperatingSystemSection"`
Eula []EulaSection `xml:"EulaSection"`
VirtualHardware []VirtualHardwareSection `xml:"VirtualHardwareSection"`
}
type File struct {
ID string `xml:"id,attr"`
Href string `xml:"href,attr"`
Size uint `xml:"size,attr"`
Compression *string `xml:"compression,attr"`
ChunkSize *int `xml:"chunkSize,attr"`
}
type Content struct {
ID string `xml:"id,attr"`
Info string `xml:"Info"`
Name *string `xml:"Name"`
}
type Section struct {
Required *bool `xml:"required,attr"`
Info string `xml:"Info"`
}
type AnnotationSection struct {
Section
Annotation string `xml:"Annotation"`
}
type ProductSection struct {
Section
Class *string `xml:"class,attr"`
Instance *string `xml:"instance,attr"`
Product string `xml:"Product"`
Vendor string `xml:"Vendor"`
Version string `xml:"Version"`
FullVersion string `xml:"FullVersion"`
ProductURL string `xml:"ProductUrl"`
VendorURL string `xml:"VendorUrl"`
AppURL string `xml:"AppUrl"`
Property []Property `xml:"Property"`
}
type Property struct {
Key string `xml:"key,attr"`
Type string `xml:"type,attr"`
Qualifiers *string `xml:"qualifiers,attr"`
UserConfigurable *bool `xml:"userConfigurable,attr"`
Default *string `xml:"value,attr"`
Password *bool `xml:"password,attr"`
Label *string `xml:"Label"`
Description *string `xml:"Description"`
Values []PropertyConfigurationValue `xml:"Value"`
}
type PropertyConfigurationValue struct {
Value string `xml:"value,attr"`
Configuration *string `xml:"configuration,attr"`
}
type NetworkSection struct {
Section
Networks []Network `xml:"Network"`
}
type Network struct {
Name string `xml:"name,attr"`
Description string `xml:"Description"`
}
type DiskSection struct {
Section
Disks []VirtualDiskDesc `xml:"Disk"`
}
type VirtualDiskDesc struct {
DiskID string `xml:"diskId,attr"`
FileRef *string `xml:"fileRef,attr"`
Capacity string `xml:"capacity,attr"`
CapacityAllocationUnits *string `xml:"capacityAllocationUnits,attr"`
Format *string `xml:"format,attr"`
PopulatedSize *int `xml:"populatedSize,attr"`
ParentRef *string `xml:"parentRef,attr"`
}
type OperatingSystemSection struct {
Section
ID int16 `xml:"id,attr"`
Version *string `xml:"version,attr"`
OSType *string `xml:"osType,attr"`
Description *string `xml:"Description"`
}
type EulaSection struct {
Section
License string `xml:"License"`
}
type VirtualHardwareSection struct {
Section
ID *string `xml:"id,attr"`
Transport *string `xml:"transport,attr"`
System *VirtualSystemSettingData `xml:"System"`
Item []ResourceAllocationSettingData `xml:"Item"`
}
type VirtualSystemSettingData struct {
CIMVirtualSystemSettingData
}
type ResourceAllocationSettingData struct {
CIMResourceAllocationSettingData
Required *bool `xml:"required,attr"`
Configuration *string `xml:"configuration,attr"`
Bound *string `xml:"bound,attr"`
}
type ResourceAllocationSection struct {
Section
Item []ResourceAllocationSettingData `xml:"Item"`
}
type DeploymentOptionSection struct {
Section
Configuration []DeploymentOptionConfiguration `xml:"Configuration"`
}
type DeploymentOptionConfiguration struct {
ID string `xml:"id,attr"`
Default *bool `xml:"default,attr"`
Label string `xml:"Label"`
Description string `xml:"Description"`
}

103
vendor/github.com/vmware/govmomi/ovf/manager.go generated vendored Normal file
View File

@ -0,0 +1,103 @@
/*
Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"context"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type Manager struct {
types.ManagedObjectReference
c *vim25.Client
}
func NewManager(c *vim25.Client) *Manager {
return &Manager{*c.ServiceContent.OvfManager, c}
}
// CreateDescriptor wraps methods.CreateDescriptor
func (m *Manager) CreateDescriptor(ctx context.Context, obj mo.Reference, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
req := types.CreateDescriptor{
This: m.Reference(),
Obj: obj.Reference(),
Cdp: cdp,
}
res, err := methods.CreateDescriptor(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// CreateImportSpec wraps methods.CreateImportSpec
func (m *Manager) CreateImportSpec(ctx context.Context, ovfDescriptor string, resourcePool mo.Reference, datastore mo.Reference, cisp types.OvfCreateImportSpecParams) (*types.OvfCreateImportSpecResult, error) {
req := types.CreateImportSpec{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
ResourcePool: resourcePool.Reference(),
Datastore: datastore.Reference(),
Cisp: cisp,
}
res, err := methods.CreateImportSpec(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// ParseDescriptor wraps methods.ParseDescriptor
func (m *Manager) ParseDescriptor(ctx context.Context, ovfDescriptor string, pdp types.OvfParseDescriptorParams) (*types.OvfParseDescriptorResult, error) {
req := types.ParseDescriptor{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
Pdp: pdp,
}
res, err := methods.ParseDescriptor(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// ValidateHost wraps methods.ValidateHost
func (m *Manager) ValidateHost(ctx context.Context, ovfDescriptor string, host mo.Reference, vhp types.OvfValidateHostParams) (*types.OvfValidateHostResult, error) {
req := types.ValidateHost{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
Host: host.Reference(),
Vhp: vhp,
}
res, err := methods.ValidateHost(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}

35
vendor/github.com/vmware/govmomi/ovf/ovf.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"io"
"github.com/vmware/govmomi/vim25/xml"
)
func Unmarshal(r io.Reader) (*Envelope, error) {
var e Envelope
dec := xml.NewDecoder(r)
err := dec.Decode(&e)
if err != nil {
return nil, err
}
return &e, nil
}

1
vendor/modules.txt vendored
View File

@ -572,6 +572,7 @@ github.com/vmware/govmomi/find
github.com/vmware/govmomi/list
github.com/vmware/govmomi/nfc
github.com/vmware/govmomi/object
github.com/vmware/govmomi/ovf
github.com/vmware/govmomi/property
github.com/vmware/govmomi/session
github.com/vmware/govmomi/task

View File

@ -82,6 +82,17 @@ necessary for this build to succeed and can be found further down the page.
<%= partial "partials/helper/communicator/WinRM-not-required" %>
### Export Configuration
<%= partial "partials/builder/vsphere/common/ExportConfig" %>
### Optional:
<%= partial "partials/builder/vsphere/common/ExportConfig-not-required" %>
#### Output Configuration:
<%= partial "partials/builder/vsphere/common/OutputConfig-not-required" %>
## Working with Clusters
#### Standalone Hosts
Only use the `host` option. Optionally specify a `resource_pool`:

View File

@ -106,6 +106,17 @@ from the datastore. Example:
### Floppy Configuration
<%= partial "partials/builder/vsphere/iso/FloppyConfig-not-required" %>
### Export Configuration
<%= partial "partials/builder/vsphere/common/ExportConfig" %>
### Optional:
<%= partial "partials/builder/vsphere/common/ExportConfig-not-required" %>
#### Output Configuration:
<%= partial "partials/builder/vsphere/common/OutputConfig-not-required" %>
### Extra Configuration Parameters
<%= partial "partials/builder/vsphere/common/ConfigParamsConfig-not-required" %>

View File

@ -5,3 +5,4 @@
- `convert_to_template` (bool) - Convert VM to a template. Defaults to `false`.
- `export` (\*common.ExportConfig) - Export

View File

@ -0,0 +1,17 @@
<!-- Code generated from the comments of the ExportConfig struct in builder/vsphere/common/step_export.go; DO NOT EDIT MANUALLY -->
- `name` (string) - name of the ovf. defaults to the name of the VM
- `force` (bool) - overwrite ovf if it exists
- `images` (bool) - include iso and img image files that are attached to the VM
- `manifest` (string) - generate manifest using sha1, sha256, sha512. Defaults to 'sha256'. Use 'none' for no manifest.
- `options` ([]string) - ```json
...
"export": {
"options": ["mac"]
},
```

View File

@ -0,0 +1,21 @@
<!-- Code generated from the comments of the ExportConfig struct in builder/vsphere/common/step_export.go; DO NOT EDIT MANUALLY -->
You may optionally export an ovf from VSphere to the instance running Packer.
Example usage:
```json
...
"vm_name": "example-ubuntu",
...
"export": {
"force": true,
"output_directory": "./output_vsphere"
},
```
The above configuration would create the following files:
```
./output_vsphere/example-ubuntu-disk-0.vmdk
./output_vsphere/example-ubuntu.mf
./output_vsphere/example-ubuntu.ovf
```

View File

@ -0,0 +1,10 @@
<!-- Code generated from the comments of the OutputConfig struct in builder/vsphere/common/output_config.go; DO NOT EDIT MANUALLY -->
- `output_directory` (string) - This setting specifies the directory that
artifacts from the build, such as the virtual machine files and disks,
will be output to. The path to the directory may be relative or
absolute. If relative, the path is relative to the working directory
packer is executed from. This directory must not exist or, if
created, must be empty prior to running the builder. By default this is
"output-BUILDNAME" where "BUILDNAME" is the name of the build.

View File

@ -5,3 +5,6 @@
- `convert_to_template` (bool) - Convert VM to a template. Defaults to `false`.
- `export` (\*common.ExportConfig) - Configuration for exporting VM to an ovf file.
The VM will not be exported if no [Export Configuration](#export-configuration) is specified.