packer-cn/builder/vsphere/driver/vm.go

1073 lines
26 KiB
Go
Raw Normal View History

package driver
import (
2018-10-31 17:42:24 -04:00
"context"
"errors"
"fmt"
"log"
"net"
"reflect"
"strings"
2018-10-31 17:42:24 -04:00
"time"
"github.com/hashicorp/packer/packer"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vapi/vcenter"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
2017-08-23 20:06:50 -04:00
type VirtualMachine struct {
vm *object.VirtualMachine
driver *Driver
}
2017-08-23 20:06:50 -04:00
type CloneConfig struct {
Name string
Folder string
Cluster string
Host string
ResourcePool string
Datastore string
LinkedClone bool
Network string
Annotation string
VAppProperties map[string]string
2017-08-23 20:06:50 -04:00
}
type HardwareConfig struct {
CPUs int32
2018-12-18 12:51:56 -05:00
CpuCores int32
CPUReservation int64
CPULimit int64
RAM int64
RAMReservation int64
RAMReserveAll bool
NestedHV bool
CpuHotAddEnabled bool
MemoryHotAddEnabled bool
2018-12-17 07:49:04 -05:00
VideoRAM int64
VGPUProfile string
Firmware string
ForceBIOSSetup bool
2017-08-23 20:06:50 -04:00
}
type NIC struct {
Network string // "" for default network
NetworkCard string // example: vmxnet3
MacAddress string // set mac if want specific address
Passthrough *bool // direct path i/o
}
type CreateConfig struct {
DiskControllerType []string // example: "scsi", "pvscsi", "lsilogic"
2018-02-26 19:22:52 -05:00
Annotation string
Name string
Folder string
2018-05-05 17:41:14 -04:00
Cluster string
2018-02-26 19:22:52 -05:00
Host string
ResourcePool string
Datastore string
GuestOS string // example: otherGuest
NICs []NIC
USBController []string
Version uint // example: 10
Storage []Disk
}
type Disk struct {
DiskSize int64
DiskEagerlyScrub bool
DiskThinProvisioned bool
ControllerIndex int
}
2017-08-23 20:06:50 -04:00
func (d *Driver) NewVM(ref *types.ManagedObjectReference) *VirtualMachine {
return &VirtualMachine{
vm: object.NewVirtualMachine(d.client.Client, *ref),
driver: d,
}
}
func (d *Driver) FindVM(name string) (*VirtualMachine, error) {
vm, err := d.finder.VirtualMachine(d.ctx, name)
if err != nil {
return nil, err
}
return &VirtualMachine{
vm: vm,
2017-08-23 20:06:50 -04:00
driver: d,
}, nil
}
func (d *Driver) PreCleanVM(ui packer.Ui, vmPath string, force bool) error {
vm, err := d.FindVM(vmPath)
if err != nil {
if _, ok := err.(*find.NotFoundError); !ok {
return fmt.Errorf("error looking up old vm: %v", err)
}
}
if force && vm != nil {
ui.Say(fmt.Sprintf("the vm/template %s already exists, but deleting it due to -force flag", vmPath))
// power off just in case it is still on
vm.PowerOff()
err := vm.Destroy()
if err != nil {
return fmt.Errorf("error destroying %s: %v", vmPath, err)
}
}
if !force && vm != nil {
return fmt.Errorf("%s already exists, you can use -force flag to destroy it: %v", vmPath, err)
}
return nil
}
func (d *Driver) CreateVM(config *CreateConfig) (*VirtualMachine, error) {
2018-05-05 17:41:14 -04:00
createSpec := types.VirtualMachineConfigSpec{
Name: config.Name,
Annotation: config.Annotation,
GuestId: config.GuestOS,
}
if config.Version != 0 {
createSpec.Version = fmt.Sprintf("%s%d", "vmx-", config.Version)
}
folder, err := d.FindFolder(config.Folder)
if err != nil {
return nil, err
}
2018-03-15 17:53:25 -04:00
resourcePool, err := d.FindResourcePool(config.Cluster, config.Host, config.ResourcePool)
if err != nil {
return nil, err
}
2018-03-15 17:53:25 -04:00
var host *object.HostSystem
if config.Cluster != "" && config.Host != "" {
2018-03-15 17:53:25 -04:00
h, err := d.FindHost(config.Host)
if err != nil {
return nil, err
}
host = h.host
}
2018-03-20 19:03:47 -04:00
datastore, err := d.FindDatastore(config.Datastore, config.Host)
if err != nil {
return nil, err
}
devices := object.VirtualDeviceList{}
devices, err = addDisk(d, devices, config)
if err != nil {
return nil, err
}
2018-01-30 15:15:30 -05:00
devices, err = addNetwork(d, devices, config)
if err != nil {
return nil, err
}
t := true
for _, usbType := range config.USBController {
var usb types.BaseVirtualDevice
switch usbType {
// handle "true" and "1" for backwards compatibility
case "usb", "true", "1":
usb = &types.VirtualUSBController{
EhciEnabled: &t,
}
case "xhci":
usb = new(types.VirtualUSBXHCIController)
default:
continue
2018-02-26 19:22:52 -05:00
}
2018-02-26 19:22:52 -05:00
devices = append(devices, usb)
}
createSpec.DeviceChange, err = devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
if err != nil {
return nil, err
}
createSpec.Files = &types.VirtualMachineFileInfo{
VmPathName: fmt.Sprintf("[%s]", datastore.Name()),
}
2018-03-15 17:53:25 -04:00
task, err := folder.folder.CreateVM(d.ctx, createSpec, resourcePool.pool, host)
if err != nil {
return nil, err
}
taskInfo, err := task.WaitForResult(d.ctx, nil)
if err != nil {
return nil, err
}
vmRef, ok := taskInfo.Result.(types.ManagedObjectReference)
if !ok {
return nil, fmt.Errorf("something went wrong when creating the VM")
}
return d.NewVM(&vmRef), nil
}
2017-08-23 20:06:50 -04:00
func (vm *VirtualMachine) Info(params ...string) (*mo.VirtualMachine, error) {
var p []string
if len(params) == 0 {
p = []string{"*"}
} else {
p = params
}
2017-08-23 20:06:50 -04:00
var info mo.VirtualMachine
err := vm.vm.Properties(vm.driver.ctx, vm.vm.Reference(), p, &info)
if err != nil {
return nil, err
}
return &info, nil
}
func (vm *VirtualMachine) Devices() (object.VirtualDeviceList, error) {
vmInfo, err := vm.Info("config.hardware.device")
if err != nil {
return nil, err
}
return vmInfo.Config.Hardware.Device, nil
}
2019-07-08 10:48:37 -04:00
func (vm *VirtualMachine) Clone(ctx context.Context, config *CloneConfig) (*VirtualMachine, error) {
folder, err := vm.driver.FindFolder(config.Folder)
if err != nil {
return nil, err
}
2017-08-23 20:06:50 -04:00
var relocateSpec types.VirtualMachineRelocateSpec
2019-07-08 10:48:37 -04:00
pool, err := vm.driver.FindResourcePool(config.Cluster, config.Host, config.ResourcePool)
2017-08-23 20:06:50 -04:00
if err != nil {
return nil, err
}
poolRef := pool.pool.Reference()
relocateSpec.Pool = &poolRef
2019-07-08 10:48:37 -04:00
datastore, err := vm.driver.FindDatastore(config.Datastore, config.Host)
if err != nil {
return nil, err
2017-08-23 20:06:50 -04:00
}
datastoreRef := datastore.ds.Reference()
relocateSpec.Datastore = &datastoreRef
2017-08-23 20:06:50 -04:00
var cloneSpec types.VirtualMachineCloneSpec
cloneSpec.Location = relocateSpec
cloneSpec.PowerOn = false
if config.LinkedClone == true {
cloneSpec.Location.DiskMoveType = "createNewChildDiskBacking"
2019-07-08 10:48:37 -04:00
tpl, err := vm.Info("snapshot")
2017-08-23 20:06:50 -04:00
if err != nil {
return nil, err
}
if tpl.Snapshot == nil {
err = errors.New("`linked_clone=true`, but template has no snapshots")
return nil, err
}
cloneSpec.Snapshot = tpl.Snapshot.CurrentSnapshot
}
var configSpec types.VirtualMachineConfigSpec
cloneSpec.Config = &configSpec
2018-11-08 11:50:52 -05:00
if config.Annotation != "" {
configSpec.Annotation = config.Annotation
}
if config.Network != "" {
2019-07-08 10:48:37 -04:00
net, err := vm.driver.FindNetwork(config.Network)
if err != nil {
return nil, err
}
2019-06-13 09:56:14 -04:00
backing, err := net.network.EthernetCardBackingInfo(ctx)
if err != nil {
return nil, err
}
2019-07-08 10:48:37 -04:00
devices, err := vm.vm.Device(ctx)
if err != nil {
return nil, err
}
adapter, err := findNetworkAdapter(devices)
if err != nil {
return nil, err
}
adapter.GetVirtualEthernetCard().Backing = backing
config := &types.VirtualDeviceConfigSpec{
Device: adapter.(types.BaseVirtualDevice),
Operation: types.VirtualDeviceConfigSpecOperationEdit,
}
configSpec.DeviceChange = append(configSpec.DeviceChange, config)
2018-11-08 11:50:52 -05:00
}
vAppConfig, err := vm.updateVAppConfig(ctx, config.VAppProperties)
if err != nil {
return nil, err
}
configSpec.VAppConfig = vAppConfig
2019-07-08 10:48:37 -04:00
task, err := vm.vm.Clone(vm.driver.ctx, folder.folder, config.Name, cloneSpec)
2017-08-23 20:06:50 -04:00
if err != nil {
return nil, err
}
2018-05-16 09:05:08 -04:00
info, err := task.WaitForResult(ctx, nil)
2017-08-23 20:06:50 -04:00
if err != nil {
2018-05-16 09:05:08 -04:00
if ctx.Err() == context.Canceled {
err = task.Cancel(context.TODO())
return nil, err
}
2017-08-23 20:06:50 -04:00
return nil, err
}
vmRef, ok := info.Result.(types.ManagedObjectReference)
if !ok {
return nil, fmt.Errorf("something went wrong when cloning the VM")
}
2019-07-08 10:48:37 -04:00
created := vm.driver.NewVM(&vmRef)
return created, nil
2017-08-23 20:06:50 -04:00
}
func (vm *VirtualMachine) updateVAppConfig(ctx context.Context, newProps map[string]string) (*types.VmConfigSpec, error) {
if len(newProps) == 0 {
return nil, nil
}
vProps, _ := vm.Properties(ctx)
if vProps.Config.VAppConfig == nil {
return nil, fmt.Errorf("this VM lacks a vApp configuration and cannot have vApp properties set on it")
}
allProperties := vProps.Config.VAppConfig.GetVmConfigInfo().Property
var props []types.VAppPropertySpec
for _, p := range allProperties {
userValue, setByUser := newProps[p.Id]
if !setByUser {
continue
}
if *p.UserConfigurable == false {
return nil, fmt.Errorf("vApp property with userConfigurable=false specified in vapp.properties: %+v", reflect.ValueOf(newProps).MapKeys())
}
prop := types.VAppPropertySpec{
ArrayUpdateSpec: types.ArrayUpdateSpec{
Operation: types.ArrayUpdateOperationEdit,
},
Info: &types.VAppPropertyInfo{
Key: p.Key,
Id: p.Id,
Value: userValue,
UserConfigurable: p.UserConfigurable,
},
}
props = append(props, prop)
delete(newProps, p.Id)
}
if len(newProps) > 0 {
return nil, fmt.Errorf("unsupported vApp properties in vapp.properties: %+v", reflect.ValueOf(newProps).MapKeys())
}
return &types.VmConfigSpec{
Property: props,
}, nil
}
func (vm *VirtualMachine) AddPublicKeys(ctx context.Context, publicKeys string) error {
newProps := map[string]string{"public-keys": publicKeys}
config, err := vm.updateVAppConfig(ctx, newProps)
if err != nil {
return fmt.Errorf("not possible to save temporary public key: %s", err.Error())
}
confSpec := types.VirtualMachineConfigSpec{VAppConfig: config}
task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec)
if err != nil {
return err
}
_, err = task.WaitForResult(vm.driver.ctx, nil)
return err
}
func (vm *VirtualMachine) Properties(ctx context.Context) (*mo.VirtualMachine, error) {
log.Printf("fetching properties for VM %q", vm.vm.InventoryPath)
var props mo.VirtualMachine
if err := vm.vm.Properties(ctx, vm.vm.Reference(), nil, &props); err != nil {
return nil, err
}
return &props, nil
}
2017-08-23 20:06:50 -04:00
func (vm *VirtualMachine) Destroy() error {
task, err := vm.vm.Destroy(vm.driver.ctx)
if err != nil {
return err
}
_, err = task.WaitForResult(vm.driver.ctx, nil)
return err
}
func (vm *VirtualMachine) Configure(config *HardwareConfig) error {
2018-05-05 17:41:14 -04:00
var confSpec types.VirtualMachineConfigSpec
confSpec.NumCPUs = config.CPUs
2018-12-18 12:51:56 -05:00
confSpec.NumCoresPerSocket = config.CpuCores
2018-05-05 17:41:14 -04:00
confSpec.MemoryMB = config.RAM
2017-08-23 20:06:50 -04:00
2018-05-05 17:41:14 -04:00
var cpuSpec types.ResourceAllocationInfo
2018-05-12 07:19:17 -04:00
cpuSpec.Reservation = &config.CPUReservation
2018-10-16 11:59:51 -04:00
if config.CPULimit != 0 {
cpuSpec.Limit = &config.CPULimit
}
2018-05-05 17:41:14 -04:00
confSpec.CpuAllocation = &cpuSpec
2017-12-01 06:09:47 -05:00
2018-05-05 17:41:14 -04:00
var ramSpec types.ResourceAllocationInfo
2018-05-12 07:19:17 -04:00
ramSpec.Reservation = &config.RAMReservation
2018-05-05 17:41:14 -04:00
confSpec.MemoryAllocation = &ramSpec
2017-12-01 06:09:47 -05:00
2018-05-05 17:41:14 -04:00
confSpec.MemoryReservationLockedToMax = &config.RAMReserveAll
confSpec.NestedHVEnabled = &config.NestedHV
2017-12-01 06:09:47 -05:00
2018-05-05 17:41:14 -04:00
confSpec.CpuHotAddEnabled = &config.CpuHotAddEnabled
confSpec.MemoryHotAddEnabled = &config.MemoryHotAddEnabled
2018-12-17 07:49:04 -05:00
if config.VideoRAM != 0 {
devices, err := vm.vm.Device(vm.driver.ctx)
if err != nil {
return err
}
l := devices.SelectByType((*types.VirtualMachineVideoCard)(nil))
if len(l) != 1 {
return err
}
card := l[0].(*types.VirtualMachineVideoCard)
card.VideoRamSizeInKB = config.VideoRAM
spec := &types.VirtualDeviceConfigSpec{
Device: card,
Operation: types.VirtualDeviceConfigSpecOperationEdit,
}
confSpec.DeviceChange = append(confSpec.DeviceChange, spec)
}
if config.VGPUProfile != "" {
devices, err := vm.vm.Device(vm.driver.ctx)
if err != nil {
return err
}
pciDevices := devices.SelectByType((*types.VirtualPCIPassthrough)(nil))
vGPUDevices := pciDevices.SelectByBackingInfo((*types.VirtualPCIPassthroughVmiopBackingInfo)(nil))
var operation types.VirtualDeviceConfigSpecOperation
if len(vGPUDevices) > 1 {
return err
} else if len(pciDevices) == 1 {
operation = types.VirtualDeviceConfigSpecOperationEdit
} else if len(pciDevices) == 0 {
operation = types.VirtualDeviceConfigSpecOperationAdd
}
vGPUProfile := newVGPUProfile(config.VGPUProfile)
spec := &types.VirtualDeviceConfigSpec{
Device: &vGPUProfile,
Operation: operation,
}
log.Printf("Adding vGPU device with profile '%s'", config.VGPUProfile)
confSpec.DeviceChange = append(confSpec.DeviceChange, spec)
}
2018-12-17 07:49:04 -05:00
efiSecureBootEnabled := false
firmware := config.Firmware
if firmware == "efi-secure" {
firmware = "efi"
efiSecureBootEnabled = true
}
confSpec.Firmware = firmware
confSpec.BootOptions = &types.VirtualMachineBootOptions{
EnterBIOSSetup: types.NewBool(config.ForceBIOSSetup),
EfiSecureBootEnabled: types.NewBool(efiSecureBootEnabled),
}
2018-05-05 17:41:14 -04:00
task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec)
if err != nil {
return err
}
_, err = task.WaitForResult(vm.driver.ctx, nil)
return err
}
2020-07-29 03:09:58 -04:00
func (vm *VirtualMachine) Customize(spec types.CustomizationSpec) error {
task, err := vm.vm.Customize(vm.driver.ctx, spec)
if err != nil {
return err
}
return task.Wait(vm.driver.ctx)
}
2018-05-05 17:41:14 -04:00
func (vm *VirtualMachine) ResizeDisk(diskSize int64) error {
var confSpec types.VirtualMachineConfigSpec
devices, err := vm.vm.Device(vm.driver.ctx)
if err != nil {
return err
}
disk, err := findDisk(devices)
if err != nil {
return err
}
disk.CapacityInKB = diskSize * 1024
confSpec.DeviceChange = []types.BaseVirtualDeviceConfigSpec{
&types.VirtualDeviceConfigSpec{
Device: disk,
Operation: types.VirtualDeviceConfigSpecOperationEdit,
},
2017-12-01 06:09:47 -05:00
}
2017-08-23 20:06:50 -04:00
task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec)
if err != nil {
return err
}
2017-08-23 20:06:50 -04:00
_, err = task.WaitForResult(vm.driver.ctx, nil)
return err
}
2017-12-01 06:09:47 -05:00
func findDisk(devices object.VirtualDeviceList) (*types.VirtualDisk, error) {
var disks []*types.VirtualDisk
for _, device := range devices {
switch d := device.(type) {
case *types.VirtualDisk:
disks = append(disks, d)
}
}
switch len(disks) {
case 0:
return nil, errors.New("VM has no disks")
case 1:
return disks[0], nil
}
return nil, errors.New("VM has multiple disks")
}
2017-08-23 20:06:50 -04:00
func (vm *VirtualMachine) PowerOn() error {
task, err := vm.vm.PowerOn(vm.driver.ctx)
if err != nil {
return err
}
_, err = task.WaitForResult(vm.driver.ctx, nil)
return err
}
func (vm *VirtualMachine) WaitForIP(ctx context.Context, ipNet *net.IPNet) (string, error) {
netIP, err := vm.vm.WaitForNetIP(ctx, false)
if err != nil {
return "", err
}
for _, ips := range netIP {
for _, ip := range ips {
parseIP := net.ParseIP(ip)
if ipNet != nil && !ipNet.Contains(parseIP) {
// ip address is not in range
continue
}
// default to an ipv4 addresses if no ipNet is defined
if ipNet == nil && parseIP.To4() == nil {
continue
}
return ip, nil
}
}
return "", fmt.Errorf("unable to find an IP")
2017-08-23 20:06:50 -04:00
}
func (vm *VirtualMachine) PowerOff() error {
state, err := vm.vm.PowerState(vm.driver.ctx)
if err != nil {
return err
}
if state == types.VirtualMachinePowerStatePoweredOff {
return nil
}
task, err := vm.vm.PowerOff(vm.driver.ctx)
if err != nil {
return err
}
_, err = task.WaitForResult(vm.driver.ctx, nil)
return err
}
func (vm *VirtualMachine) IsPoweredOff() (bool, error) {
state, err := vm.vm.PowerState(vm.driver.ctx)
if err != nil {
return false, err
}
return state == types.VirtualMachinePowerStatePoweredOff, nil
}
2017-08-23 20:06:50 -04:00
func (vm *VirtualMachine) StartShutdown() error {
err := vm.vm.ShutdownGuest(vm.driver.ctx)
return err
}
2018-05-16 09:05:08 -04:00
func (vm *VirtualMachine) WaitForShutdown(ctx context.Context, timeout time.Duration) error {
2017-08-23 20:06:50 -04:00
shutdownTimer := time.After(timeout)
for {
off, err := vm.IsPoweredOff()
2017-08-23 20:06:50 -04:00
if err != nil {
return err
}
if off {
2017-08-23 20:06:50 -04:00
break
}
select {
case <-shutdownTimer:
err := errors.New("Timeout while waiting for machine to shut down.")
return err
2018-05-16 09:05:08 -04:00
case <-ctx.Done():
return nil
2017-08-23 20:06:50 -04:00
default:
time.Sleep(1 * time.Second)
}
}
return nil
}
func (vm *VirtualMachine) CreateSnapshot(name string) error {
task, err := vm.vm.CreateSnapshot(vm.driver.ctx, name, "", false, false)
if err != nil {
return err
}
_, err = task.WaitForResult(vm.driver.ctx, nil)
return err
}
func (vm *VirtualMachine) ConvertToTemplate() error {
return vm.vm.MarkAsTemplate(vm.driver.ctx)
}
func (vm *VirtualMachine) ImportOvfToContentLibrary(ovf vcenter.OVF) error {
l, err := vm.driver.FindContentLibrary(ovf.Target.LibraryID)
if err != nil {
return err
}
if l.library.Type != "LOCAL" {
return fmt.Errorf("can not deploy a VM to the content library %s of type %s; "+
"the content library must be of type LOCAL", ovf.Target.LibraryID, l.library.Type)
}
item, err := vm.driver.FindContentLibraryItem(l.library.ID, ovf.Spec.Name)
if err != nil {
return err
}
if item != nil {
// Updates existing library item
ovf.Target.LibraryItemID = item.ID
}
ovf.Target.LibraryID = l.library.ID
ovf.Source.Value = vm.vm.Reference().Value
ovf.Source.Type = "VirtualMachine"
vcm := vcenter.NewManager(vm.driver.restClient)
_, err = vcm.CreateOVF(vm.driver.ctx, ovf)
return err
}
func (vm *VirtualMachine) ImportToContentLibrary(template vcenter.Template) error {
l, err := vm.driver.FindContentLibrary(template.Library)
if err != nil {
return err
}
if l.library.Type != "LOCAL" {
return fmt.Errorf("can not deploy a VM to the content library %s of type %s; "+
"the content library must be of type LOCAL", template.Library, l.library.Type)
}
template.Library = l.library.ID
template.SourceVM = vm.vm.Reference().Value
if template.Placement.Cluster != "" {
c, err := vm.driver.FindCluster(template.Placement.Cluster)
if err != nil {
return err
}
template.Placement.Cluster = c.cluster.Reference().Value
}
if template.Placement.Folder != "" {
f, err := vm.driver.FindFolder(template.Placement.Folder)
if err != nil {
return err
}
template.Placement.Folder = f.folder.Reference().Value
}
if template.Placement.Host != "" {
h, err := vm.driver.FindHost(template.Placement.Host)
if err != nil {
return err
}
template.Placement.Host = h.host.Reference().Value
}
if template.Placement.ResourcePool != "" {
rp, err := vm.driver.FindResourcePool(template.Placement.Cluster, template.Placement.Host, template.Placement.ResourcePool)
if err != nil {
return err
}
template.Placement.ResourcePool = rp.pool.Reference().Value
}
if template.VMHomeStorage != nil {
d, err := vm.driver.FindDatastore(template.VMHomeStorage.Datastore, template.Placement.Host)
if err != nil {
return err
}
template.VMHomeStorage.Datastore = d.ds.Reference().Value
}
vcm := vcenter.NewManager(vm.driver.restClient)
_, err = vcm.CreateTemplate(vm.driver.ctx, template)
return err
}
func (vm *VirtualMachine) GetDir() (string, error) {
vmInfo, err := vm.Info("name", "layoutEx.file")
if err != nil {
return "", err
}
vmxName := fmt.Sprintf("/%s.vmx", vmInfo.Name)
for _, file := range vmInfo.LayoutEx.File {
if strings.HasSuffix(file.Name, vmxName) {
return RemoveDatastorePrefix(file.Name[:len(file.Name)-len(vmxName)]), nil
}
}
return "", fmt.Errorf("cannot find '%s'", vmxName)
}
func addDisk(_ *Driver, devices object.VirtualDeviceList, config *CreateConfig) (object.VirtualDeviceList, error) {
if len(config.Storage) == 0 {
return nil, errors.New("no storage devices have been defined")
}
if len(config.DiskControllerType) == 0 {
return nil, errors.New("no controllers have been defined")
}
var controllers []types.BaseVirtualController
for _, controllerType := range config.DiskControllerType {
device, err := devices.CreateSCSIController(controllerType)
if err != nil {
return nil, err
}
devices = append(devices, device)
controller, err := devices.FindDiskController(devices.Name(device))
if err != nil {
return nil, err
}
controllers = append(controllers, controller)
}
for _, dc := range config.Storage {
disk := &types.VirtualDisk{
VirtualDevice: types.VirtualDevice{
Key: devices.NewKey(),
Backing: &types.VirtualDiskFlatVer2BackingInfo{
DiskMode: string(types.VirtualDiskModePersistent),
ThinProvisioned: types.NewBool(dc.DiskThinProvisioned),
EagerlyScrub: types.NewBool(dc.DiskEagerlyScrub),
},
},
CapacityInKB: dc.DiskSize * 1024,
}
devices.AssignController(disk, controllers[dc.ControllerIndex])
devices = append(devices, disk)
}
return devices, nil
}
2018-01-30 15:15:30 -05:00
func addNetwork(d *Driver, devices object.VirtualDeviceList, config *CreateConfig) (object.VirtualDeviceList, error) {
if len(config.NICs) == 0 {
return nil, errors.New("no network adapters have been defined")
}
for _, nic := range config.NICs {
network, err := findNetwork(nic.Network, config.Host, d)
if err != nil {
return nil, err
2018-10-31 16:51:20 -04:00
}
backing, err := network.EthernetCardBackingInfo(d.ctx)
2018-10-31 16:51:20 -04:00
if err != nil {
return nil, err
}
device, err := object.EthernetCardTypes().CreateEthernetCard(nic.NetworkCard, backing)
2018-10-31 16:51:20 -04:00
if err != nil {
return nil, err
}
card := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
if nic.MacAddress != "" {
card.AddressType = string(types.VirtualEthernetCardMacTypeManual)
card.MacAddress = nic.MacAddress
}
card.UptCompatibilityEnabled = nic.Passthrough
devices = append(devices, device)
}
return devices, nil
}
func findNetwork(network string, host string, d *Driver) (object.NetworkReference, error) {
if network != "" {
var err error
networks, err := d.FindNetworks(network)
if err != nil {
return nil, err
}
if len(networks) == 1 {
return networks[0].network, nil
}
// If there are multiple networks then try to match the host
if host != "" {
h, err := d.FindHost(host)
if err != nil {
return nil, &MultipleNetworkFoundError{network, fmt.Sprintf("unable to match a network to the host %s: %s", host, err.Error())}
}
for _, n := range networks {
info, err := n.Info("host")
if err != nil {
continue
}
for _, host := range info.Host {
if h.host.Reference().Value == host.Reference().Value {
return n.network, nil
}
}
}
return nil, &MultipleNetworkFoundError{network, fmt.Sprintf("unable to match a network to the host %s", host)}
}
return nil, &MultipleNetworkFoundError{network, "please provide a host to match or the network full path"}
}
if host != "" {
h, err := d.FindHost(host)
if err != nil {
return nil, err
}
i, err := h.Info("network")
if err != nil {
return nil, err
}
if len(i.Network) > 1 {
return nil, fmt.Errorf("Host has multiple networks. Specify it explicitly")
}
return object.NewNetwork(d.client.Client, i.Network[0]), nil
}
return nil, fmt.Errorf("Couldn't find network; 'host' and 'network' not specified. At least one of the two must be specified.")
}
func newVGPUProfile(vGPUProfile string) types.VirtualPCIPassthrough {
return types.VirtualPCIPassthrough{
VirtualDevice: types.VirtualDevice{
DeviceInfo: &types.Description{
Summary: "",
Label: fmt.Sprintf("New vGPU %v PCI device", vGPUProfile),
},
Backing: &types.VirtualPCIPassthroughVmiopBackingInfo{
Vgpu: vGPUProfile,
},
},
}
}
func (vm *VirtualMachine) AddCdrom(controllerType string, isoPath string) error {
devices, err := vm.vm.Device(vm.driver.ctx)
if err != nil {
return err
}
var controller *types.VirtualController
if controllerType == "sata" {
c, err := vm.FindSATAController()
if err != nil {
return err
}
controller = c.GetVirtualController()
} else {
c, err := devices.FindIDEController("")
if err != nil {
return err
}
controller = c.GetVirtualController()
}
cdrom, err := vm.CreateCdrom(controller)
if err != nil {
return err
}
if isoPath != "" {
devices.InsertIso(cdrom, isoPath)
}
log.Printf("Creating CD-ROM on controller '%v' with iso '%v'", controller, isoPath)
return vm.addDevice(cdrom)
}
func (vm *VirtualMachine) AddFloppy(imgPath string) error {
devices, err := vm.vm.Device(vm.driver.ctx)
if err != nil {
return err
}
floppy, err := devices.CreateFloppy()
if err != nil {
return err
}
if imgPath != "" {
floppy = devices.InsertImg(floppy, imgPath)
}
return vm.addDevice(floppy)
}
2018-01-30 12:25:05 -05:00
func (vm *VirtualMachine) SetBootOrder(order []string) error {
devices, err := vm.vm.Device(vm.driver.ctx)
if err != nil {
return err
}
bootOptions := types.VirtualMachineBootOptions{
BootOrder: devices.BootOrder(order),
}
return vm.vm.SetBootOptions(vm.driver.ctx, &bootOptions)
}
func (vm *VirtualMachine) RemoveDevice(keepFiles bool, device ...types.BaseVirtualDevice) error {
return vm.vm.RemoveDevice(vm.driver.ctx, keepFiles, device...)
}
func (vm *VirtualMachine) addDevice(device types.BaseVirtualDevice) error {
newDevices := object.VirtualDeviceList{device}
confSpec := types.VirtualMachineConfigSpec{}
var err error
confSpec.DeviceChange, err = newDevices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
if err != nil {
return err
}
task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec)
if err != nil {
return err
}
_, err = task.WaitForResult(vm.driver.ctx, nil)
return err
}
func (vm *VirtualMachine) AddConfigParams(params map[string]string, info *types.ToolsConfigInfo) error {
2018-02-26 16:18:06 -05:00
var confSpec types.VirtualMachineConfigSpec
2020-07-02 12:33:28 -04:00
var ov []types.BaseOptionValue
for k, v := range params {
o := types.OptionValue{
Key: k,
Value: v,
2018-02-26 16:18:06 -05:00
}
2020-07-02 12:33:28 -04:00
ov = append(ov, &o)
}
2020-07-02 12:33:28 -04:00
confSpec.ExtraConfig = ov
2020-07-02 12:33:28 -04:00
confSpec.Tools = info
2018-02-26 16:18:06 -05:00
2020-07-02 12:33:28 -04:00
if len(confSpec.ExtraConfig) > 0 || confSpec.Tools != nil {
task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec)
if err != nil {
return err
}
_, err = task.WaitForResult(vm.driver.ctx, nil)
2020-07-02 12:55:10 -04:00
return err
2018-02-26 16:18:06 -05:00
}
2020-07-02 12:55:10 -04:00
return nil
2018-02-26 16:18:06 -05:00
}
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 {
return nil, errors.New("no network adapter device found")
}
return c[0].(types.BaseVirtualEthernetCard), nil
}