2017-08-23 15:40:57 -04:00
package driver
import (
2018-10-31 17:42:24 -04:00
"context"
2017-11-14 16:19:32 -05:00
"errors"
"fmt"
2019-07-16 16:23:17 -04:00
"log"
2020-06-04 01:35:29 -04:00
"net"
2020-07-08 04:33:45 -04:00
"reflect"
2018-02-01 06:47:09 -05:00
"strings"
2018-10-31 17:42:24 -04:00
"time"
2020-02-14 11:42:29 -05:00
2020-04-23 08:07:07 -04:00
"github.com/hashicorp/packer/packer"
"github.com/vmware/govmomi/find"
2020-03-19 13:51:43 -04:00
"github.com/vmware/govmomi/nfc"
2020-02-14 11:42:29 -05:00
"github.com/vmware/govmomi/object"
2020-04-23 08:07:07 -04:00
"github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/property"
2020-07-10 05:01:10 -04:00
"github.com/vmware/govmomi/vapi/vcenter"
2020-02-14 11:42:29 -05:00
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
2017-08-23 15:40:57 -04:00
)
2017-08-23 20:06:50 -04:00
type VirtualMachine struct {
vm * object . VirtualMachine
driver * Driver
2017-08-23 15:40:57 -04:00
}
2017-08-23 20:06:50 -04:00
type CloneConfig struct {
2020-07-08 04:33:45 -04:00
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 {
2018-03-23 04:56:03 -04:00
CPUs int32
2018-12-18 12:51:56 -05:00
CpuCores int32
2018-03-23 04:56:03 -04:00
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
2020-03-24 10:28:04 -04:00
VGPUProfile string
2020-04-07 15:45:03 -04:00
Firmware string
2020-07-10 05:38:36 -04:00
ForceBIOSSetup bool
2017-08-23 20:06:50 -04:00
}
2020-02-14 11:51:57 -05: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
}
2018-01-24 08:35:04 -05:00
type CreateConfig struct {
2020-07-10 06:25:46 -04:00
DiskControllerType [ ] string // example: "scsi", "pvscsi", "lsilogic"
2018-01-24 08:35:04 -05:00
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
2020-02-14 11:51:57 -05:00
NICs [ ] NIC
2020-07-13 20:25:56 -04:00
USBController [ ] string
2020-07-10 05:38:36 -04:00
Version uint // example: 10
2020-02-21 10:47:46 -05:00
Storage [ ] Disk
}
type Disk struct {
DiskSize int64
DiskEagerlyScrub bool
DiskThinProvisioned bool
2020-07-10 06:25:46 -04:00
ControllerIndex int
2018-01-24 08:35:04 -05:00
}
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 {
2017-08-24 16:18:14 -04:00
vm : vm ,
2017-08-23 20:06:50 -04:00
driver : d ,
} , nil
}
2020-04-23 08:07:07 -04:00
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
}
2018-01-24 08:35:04 -05:00
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 )
}
2018-01-24 08:35:04 -05:00
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 )
2018-01-24 08:35:04 -05:00
if err != nil {
return nil , err
}
2018-03-15 17:53:25 -04:00
var host * object . HostSystem
2018-10-31 08:35:39 -04:00
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-01-24 08:35:04 -05:00
}
2018-03-20 19:03:47 -04:00
datastore , err := d . FindDatastore ( config . Datastore , config . Host )
2018-01-24 08:35:04 -05:00
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 )
2018-01-24 08:35:04 -05:00
if err != nil {
return nil , err
}
2020-07-13 20:25:56 -04:00
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
}
2020-07-13 20:25:56 -04:00
2018-02-26 19:22:52 -05:00
devices = append ( devices , usb )
}
2018-01-24 08:35:04 -05:00
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 )
2018-01-24 08:35:04 -05:00
if err != nil {
return nil , err
}
taskInfo , err := task . WaitForResult ( d . ctx , nil )
if err != nil {
return nil , err
}
2020-06-03 11:37:30 -04:00
vmRef , ok := taskInfo . Result . ( types . ManagedObjectReference )
if ! ok {
return nil , fmt . Errorf ( "something went wrong when creating the VM" )
}
2018-01-24 08:35:04 -05:00
return d . NewVM ( & vmRef ) , nil
}
2017-08-23 20:06:50 -04:00
func ( vm * VirtualMachine ) Info ( params ... string ) ( * mo . VirtualMachine , error ) {
2017-08-23 15:40:57 -04:00
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
}
2018-01-31 06:31:11 -05:00
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 )
2017-08-23 15:40:57 -04:00
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 )
2018-01-24 08:35:04 -05:00
if err != nil {
2017-11-14 16:19:32 -05:00
return nil , err
2017-08-23 20:06:50 -04:00
}
2018-01-24 08:35:04 -05: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
}
2019-03-13 16:07:30 -04:00
var configSpec types . VirtualMachineConfigSpec
cloneSpec . Config = & configSpec
2018-11-08 11:50:52 -05:00
if config . Annotation != "" {
configSpec . Annotation = config . Annotation
2019-03-13 16:07:30 -04:00
}
if config . Network != "" {
2019-07-08 10:48:37 -04:00
net , err := vm . driver . FindNetwork ( config . Network )
2019-03-13 16:07:30 -04:00
if err != nil {
return nil , err
}
2019-06-13 09:56:14 -04:00
backing , err := net . network . EthernetCardBackingInfo ( ctx )
2019-03-13 16:07:30 -04:00
if err != nil {
return nil , err
}
2019-07-08 10:48:37 -04:00
devices , err := vm . vm . Device ( ctx )
2019-03-13 16:07:30 -04:00
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
}
2020-07-08 04:33:45 -04: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
}
2020-06-03 11:37:30 -04:00
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
}
2020-07-08 04:33:45 -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 )
}
2020-03-24 10:28:04 -04:00
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
2020-07-10 05:38:36 -04: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 ) ,
2020-04-07 15:45:03 -04:00
}
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
}
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
}
2018-01-24 08:35:04 -05:00
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
}
2020-06-04 01:35:29 -04:00
func ( vm * VirtualMachine ) WaitForIP ( ctx context . Context , ipNet * net . IPNet ) ( string , error ) {
2020-06-17 17:40:21 -04:00
netIP , err := vm . vm . WaitForNetIP ( ctx , false )
if err != nil {
return "" , err
}
2020-06-04 01:35:29 -04:00
2020-06-17 17:40:21 -04:00
for _ , ips := range netIP {
for _ , ip := range ips {
parseIP := net . ParseIP ( ip )
if ipNet != nil && ! ipNet . Contains ( parseIP ) {
// ip address is not in range
2020-06-04 01:35:29 -04:00
continue
}
2020-06-17 17:40:21 -04:00
// default to an ipv4 addresses if no ipNet is defined
if ipNet == nil && parseIP . To4 ( ) == nil {
2020-06-04 01:35:29 -04:00
continue
}
2020-06-17 17:40:21 -04:00
return ip , nil
2020-06-04 01:35:29 -04:00
}
}
2020-06-17 17:40:21 -04:00
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
}
2020-04-20 13:56:53 -04:00
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 {
2020-04-20 13:56:53 -04:00
off , err := vm . IsPoweredOff ( )
2017-08-23 20:06:50 -04:00
if err != nil {
return err
}
2020-04-20 13:56:53 -04:00
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 {
2017-10-25 16:20:59 -04:00
return vm . vm . MarkAsTemplate ( vm . driver . ctx )
2017-08-23 15:40:57 -04:00
}
2018-01-24 08:35:04 -05:00
2020-07-10 05:01:10 -04:00
func ( vm * VirtualMachine ) ImportToContentLibrary ( template vcenter . Template ) error {
template . SourceVM = vm . vm . Reference ( ) . Value
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
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
}
2018-02-01 06:47:09 -05:00
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 ) {
2018-02-01 07:48:30 -05:00
return RemoveDatastorePrefix ( file . Name [ : len ( file . Name ) - len ( vmxName ) ] ) , nil
2018-02-01 06:47:09 -05:00
}
}
return "" , fmt . Errorf ( "cannot find '%s'" , vmxName )
}
func addDisk ( _ * Driver , devices object . VirtualDeviceList , config * CreateConfig ) ( object . VirtualDeviceList , error ) {
2020-02-21 10:47:46 -05:00
if len ( config . Storage ) == 0 {
return nil , errors . New ( "no storage devices have been defined" )
}
2020-07-10 06:25:46 -04:00
if len ( config . DiskControllerType ) == 0 {
return nil , errors . New ( "no controllers have been defined" )
2018-01-24 08:35:04 -05:00
}
2020-07-10 06:25:46 -04:00
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 )
2018-01-24 08:35:04 -05:00
}
2020-02-21 10:47:46 -05:00
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 ) ,
} ,
2018-01-24 08:35:04 -05:00
} ,
2020-02-21 10:47:46 -05:00
CapacityInKB : dc . DiskSize * 1024 ,
}
2018-01-24 08:35:04 -05:00
2020-07-10 06:25:46 -04:00
devices . AssignController ( disk , controllers [ dc . ControllerIndex ] )
2020-02-21 10:47:46 -05:00
devices = append ( devices , disk )
}
2018-01-24 08:35:04 -05:00
return devices , nil
}
2018-01-30 15:15:30 -05:00
func addNetwork ( d * Driver , devices object . VirtualDeviceList , config * CreateConfig ) ( object . VirtualDeviceList , error ) {
2020-02-14 11:51:57 -05:00
if len ( config . NICs ) == 0 {
return nil , errors . New ( "no network adapters have been defined" )
}
for _ , nic := range config . NICs {
2020-06-05 05:01:58 -04:00
network , err := findNetwork ( nic . Network , config . Host , d )
if err != nil {
return nil , err
2018-10-31 16:51:20 -04:00
}
2020-02-14 11:51:57 -05:00
backing , err := network . EthernetCardBackingInfo ( d . ctx )
2018-10-31 16:51:20 -04:00
if err != nil {
return nil , err
}
2020-02-14 11:51:57 -05:00
device , err := object . EthernetCardTypes ( ) . CreateEthernetCard ( nic . NetworkCard , backing )
2018-10-31 16:51:20 -04:00
if err != nil {
return nil , err
}
2018-01-24 08:35:04 -05:00
2020-02-14 11:51:57 -05:00
card := device . ( types . BaseVirtualEthernetCard ) . GetVirtualEthernetCard ( )
if nic . MacAddress != "" {
card . AddressType = string ( types . VirtualEthernetCardMacTypeManual )
card . MacAddress = nic . MacAddress
}
card . UptCompatibilityEnabled = nic . Passthrough
2018-01-24 08:35:04 -05:00
2020-02-14 11:51:57 -05:00
devices = append ( devices , device )
2018-01-24 08:35:04 -05:00
}
2020-02-14 11:51:57 -05:00
return devices , nil
2018-01-24 08:35:04 -05:00
}
2020-06-05 05:01:58 -04:00
func findNetwork ( network string , host string , d * Driver ) ( object . NetworkReference , error ) {
if network != "" {
var err error
2020-07-09 09:59:15 -04:00
networks , err := d . FindNetworks ( network )
2020-06-05 05:01:58 -04:00
if err != nil {
return nil , err
}
2020-07-09 09:59:15 -04:00
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" }
2020-06-05 05:01:58 -04:00
}
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." )
}
2020-03-24 10:28:04 -04:00
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 ,
} ,
} ,
}
}
2018-10-29 20:18:57 -04:00
func ( vm * VirtualMachine ) AddCdrom ( controllerType string , isoPath string ) error {
2018-01-24 08:35:04 -05:00
devices , err := vm . vm . Device ( vm . driver . ctx )
if err != nil {
return err
}
2018-10-29 20:18:57 -04:00
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 ( )
2018-01-24 08:35:04 -05:00
}
2018-10-29 20:18:57 -04:00
cdrom , err := vm . CreateCdrom ( controller )
2018-01-24 08:35:04 -05:00
if err != nil {
return err
}
if isoPath != "" {
2018-10-29 20:18:57 -04:00
devices . InsertIso ( cdrom , isoPath )
2018-01-24 08:35:04 -05:00
}
2019-07-16 16:23:17 -04:00
log . Printf ( "Creating CD-ROM on controller '%v' with iso '%v'" , controller , isoPath )
2018-01-30 11:45:56 -05:00
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 )
}
2018-02-02 13:04:32 -05:00
func ( vm * VirtualMachine ) RemoveDevice ( keepFiles bool , device ... types . BaseVirtualDevice ) error {
return vm . vm . RemoveDevice ( vm . driver . ctx , keepFiles , device ... )
}
2018-01-30 11:45:56 -05:00
func ( vm * VirtualMachine ) addDevice ( device types . BaseVirtualDevice ) error {
newDevices := object . VirtualDeviceList { device }
2018-01-24 08:35:04 -05:00
confSpec := types . VirtualMachineConfigSpec { }
2018-01-30 11:45:56 -05:00
var err error
2018-01-24 08:35:04 -05:00
confSpec . DeviceChange , err = newDevices . ConfigSpec ( types . VirtualDeviceConfigSpecOperationAdd )
2018-01-30 11:45:56 -05:00
if err != nil {
return err
}
2018-01-24 08:35:04 -05: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-01 12:53:56 -04:00
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-01 12:53:56 -04:00
}
2020-07-02 12:33:28 -04:00
confSpec . ExtraConfig = ov
2020-07-01 12:53:56 -04:00
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
}
2019-03-13 16:07:30 -04:00
2020-03-19 13:51:43 -04: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
}
2019-03-13 16:07:30 -04:00
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
}