// © Broadcom. All Rights Reserved. // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. // SPDX-License-Identifier: Apache-2.0 package object import ( "context" "errors" "fmt" "net" "path" "strings" "github.com/vmware/govmomi/nfc" "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" ) const ( PropRuntimePowerState = "summary.runtime.powerState" PropConfigTemplate = "summary.config.template" ) type VirtualMachine struct { Common } // extractDiskLayoutFiles is a helper function used to extract file keys for // all disk files attached to the virtual machine at the current point of // running. func extractDiskLayoutFiles(diskLayoutList []types.VirtualMachineFileLayoutExDiskLayout) []int { var result []int for _, layoutExDisk := range diskLayoutList { for _, link := range layoutExDisk.Chain { for i := range link.FileKey { // diskDescriptor, diskExtent pairs result = append(result, int(link.FileKey[i])) } } } return result } // removeKey is a helper function for removing a specific file key from a list // of keys associated with disks attached to a virtual machine. func removeKey(l *[]int, key int) { for i, k := range *l { if k == key { *l = append((*l)[:i], (*l)[i+1:]...) break } } } func NewVirtualMachine(c *vim25.Client, ref types.ManagedObjectReference) *VirtualMachine { return &VirtualMachine{ Common: NewCommon(c, ref), } } func (v VirtualMachine) PowerState(ctx context.Context) (types.VirtualMachinePowerState, error) { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{PropRuntimePowerState}, &o) if err != nil { return "", err } return o.Summary.Runtime.PowerState, nil } func (v VirtualMachine) IsTemplate(ctx context.Context) (bool, error) { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{PropConfigTemplate}, &o) if err != nil { return false, err } return o.Summary.Config.Template, nil } func (v VirtualMachine) PowerOn(ctx context.Context) (*Task, error) { req := types.PowerOnVM_Task{ This: v.Reference(), } res, err := methods.PowerOnVM_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) PowerOff(ctx context.Context) (*Task, error) { req := types.PowerOffVM_Task{ This: v.Reference(), } res, err := methods.PowerOffVM_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) PutUsbScanCodes(ctx context.Context, spec types.UsbScanCodeSpec) (int32, error) { req := types.PutUsbScanCodes{ This: v.Reference(), Spec: spec, } res, err := methods.PutUsbScanCodes(ctx, v.c, &req) if err != nil { return 0, err } return res.Returnval, nil } func (v VirtualMachine) Reset(ctx context.Context) (*Task, error) { req := types.ResetVM_Task{ This: v.Reference(), } res, err := methods.ResetVM_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) Suspend(ctx context.Context) (*Task, error) { req := types.SuspendVM_Task{ This: v.Reference(), } res, err := methods.SuspendVM_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) ShutdownGuest(ctx context.Context) error { req := types.ShutdownGuest{ This: v.Reference(), } _, err := methods.ShutdownGuest(ctx, v.c, &req) return err } func (v VirtualMachine) StandbyGuest(ctx context.Context) error { req := types.StandbyGuest{ This: v.Reference(), } _, err := methods.StandbyGuest(ctx, v.c, &req) return err } func (v VirtualMachine) RebootGuest(ctx context.Context) error { req := types.RebootGuest{ This: v.Reference(), } _, err := methods.RebootGuest(ctx, v.c, &req) return err } func (v VirtualMachine) Destroy(ctx context.Context) (*Task, error) { req := types.Destroy_Task{ This: v.Reference(), } res, err := methods.Destroy_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) Clone(ctx context.Context, folder *Folder, name string, config types.VirtualMachineCloneSpec) (*Task, error) { req := types.CloneVM_Task{ This: v.Reference(), Folder: folder.Reference(), Name: name, Spec: config, } res, err := methods.CloneVM_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) InstantClone(ctx context.Context, config types.VirtualMachineInstantCloneSpec) (*Task, error) { req := types.InstantClone_Task{ This: v.Reference(), Spec: config, } res, err := methods.InstantClone_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) Customize(ctx context.Context, spec types.CustomizationSpec) (*Task, error) { req := types.CustomizeVM_Task{ This: v.Reference(), Spec: spec, } res, err := methods.CustomizeVM_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) Relocate(ctx context.Context, config types.VirtualMachineRelocateSpec, priority types.VirtualMachineMovePriority) (*Task, error) { req := types.RelocateVM_Task{ This: v.Reference(), Spec: config, Priority: priority, } res, err := methods.RelocateVM_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) Reconfigure(ctx context.Context, config types.VirtualMachineConfigSpec) (*Task, error) { req := types.ReconfigVM_Task{ This: v.Reference(), Spec: config, } res, err := methods.ReconfigVM_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) RefreshStorageInfo(ctx context.Context) error { req := types.RefreshStorageInfo{ This: v.Reference(), } _, err := methods.RefreshStorageInfo(ctx, v.c, &req) return err } // WaitForIP waits for the VM guest.ipAddress property to report an IP address. // Waits for an IPv4 address if the v4 param is true. func (v VirtualMachine) WaitForIP(ctx context.Context, v4 ...bool) (string, error) { var ip string p := property.DefaultCollector(v.c) err := property.Wait(ctx, p, v.Reference(), []string{"guest.ipAddress"}, func(pc []types.PropertyChange) bool { for _, c := range pc { if c.Name != "guest.ipAddress" { continue } if c.Op != types.PropertyChangeOpAssign { continue } if c.Val == nil { continue } ip = c.Val.(string) if len(v4) == 1 && v4[0] { if net.ParseIP(ip).To4() == nil { return false } } return true } return false }) if err != nil { return "", err } return ip, nil } // WaitForNetIP waits for the VM guest.net property to report an IP address for all VM NICs. // Only consider IPv4 addresses if the v4 param is true. // By default, wait for all NICs to get an IP address, unless 1 or more device is given. // A device can be specified by the MAC address or the device name, e.g. "ethernet-0". // Returns a map with MAC address as the key and IP address list as the value. func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool, device ...string) (map[string][]string, error) { macs := make(map[string][]string) eths := make(map[string]string) p := property.DefaultCollector(v.c) // Wait for all NICs to have a MacAddress, which may not be generated yet. err := property.Wait(ctx, p, v.Reference(), []string{"config.hardware.device"}, func(pc []types.PropertyChange) bool { for _, c := range pc { if c.Op != types.PropertyChangeOpAssign { continue } devices := VirtualDeviceList(c.Val.(types.ArrayOfVirtualDevice).VirtualDevice) for _, d := range devices { if nic, ok := d.(types.BaseVirtualEthernetCard); ok { // Convert to lower so that e.g. 00:50:56:83:3A:5D is treated the // same as 00:50:56:83:3a:5d mac := strings.ToLower(nic.GetVirtualEthernetCard().MacAddress) if mac == "" { return false } macs[mac] = nil eths[devices.Name(d)] = mac } } } return true }) if err != nil { return nil, err } if len(device) != 0 { // Only wait for specific NIC(s) macs = make(map[string][]string) for _, mac := range device { if eth, ok := eths[mac]; ok { mac = eth // device name, e.g. "ethernet-0" } macs[mac] = nil } } err = property.Wait(ctx, p, v.Reference(), []string{"guest.net"}, func(pc []types.PropertyChange) bool { for _, c := range pc { if c.Op != types.PropertyChangeOpAssign { continue } nics := c.Val.(types.ArrayOfGuestNicInfo).GuestNicInfo for _, nic := range nics { // Convert to lower so that e.g. 00:50:56:83:3A:5D is treated the // same as 00:50:56:83:3a:5d mac := strings.ToLower(nic.MacAddress) if mac == "" || nic.IpConfig == nil { continue } for _, ip := range nic.IpConfig.IpAddress { if _, ok := macs[mac]; !ok { continue // Ignore any that don't correspond to a VM device } if v4 && net.ParseIP(ip.IpAddress).To4() == nil { continue // Ignore non IPv4 address } macs[mac] = append(macs[mac], ip.IpAddress) } } } for _, ips := range macs { if len(ips) == 0 { return false } } return true }) if err != nil { return nil, err } return macs, nil } // Device returns the VirtualMachine's config.hardware.device property. func (v VirtualMachine) Device(ctx context.Context) (VirtualDeviceList, error) { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{"config.hardware.device", "summary.runtime.connectionState"}, &o) if err != nil { return nil, err } // Quoting the SDK doc: // The virtual machine configuration is not guaranteed to be available. // For example, the configuration information would be unavailable if the server // is unable to access the virtual machine files on disk, and is often also unavailable // during the initial phases of virtual machine creation. if o.Config == nil { return nil, fmt.Errorf("%s Config is not available, connectionState=%s", v.Reference(), o.Summary.Runtime.ConnectionState) } return VirtualDeviceList(o.Config.Hardware.Device), nil } func (v VirtualMachine) EnvironmentBrowser(ctx context.Context) (*EnvironmentBrowser, error) { var vm mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{"environmentBrowser"}, &vm) if err != nil { return nil, err } return NewEnvironmentBrowser(v.c, vm.EnvironmentBrowser), nil } func (v VirtualMachine) HostSystem(ctx context.Context) (*HostSystem, error) { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{"summary.runtime.host"}, &o) if err != nil { return nil, err } host := o.Summary.Runtime.Host if host == nil { return nil, errors.New("VM doesn't have a HostSystem") } return NewHostSystem(v.c, *host), nil } func (v VirtualMachine) ResourcePool(ctx context.Context) (*ResourcePool, error) { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{"resourcePool"}, &o) if err != nil { return nil, err } rp := o.ResourcePool if rp == nil { return nil, errors.New("VM doesn't have a resourcePool") } return NewResourcePool(v.c, *rp), nil } func diskFileOperation(op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, device types.BaseVirtualDevice) types.VirtualDeviceConfigSpecFileOperation { if disk, ok := device.(*types.VirtualDisk); ok { // Special case to attach an existing disk if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 && disk.CapacityInBytes == 0 { childDisk := false if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok { childDisk = b.Parent != nil } if !childDisk { fop = "" // existing disk } } return fop } return "" } func (v VirtualMachine) configureDevice(ctx context.Context, profile []types.BaseVirtualMachineProfileSpec, op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, devices ...types.BaseVirtualDevice) error { spec := types.VirtualMachineConfigSpec{} for _, device := range devices { config := &types.VirtualDeviceConfigSpec{ Device: device, Operation: op, FileOperation: diskFileOperation(op, fop, device), Profile: profile, } spec.DeviceChange = append(spec.DeviceChange, config) } task, err := v.Reconfigure(ctx, spec) if err != nil { return err } return task.Wait(ctx) } // AddDevice adds the given devices to the VirtualMachine func (v VirtualMachine) AddDevice(ctx context.Context, device ...types.BaseVirtualDevice) error { return v.AddDeviceWithProfile(ctx, nil, device...) } // AddDeviceWithProfile adds the given devices to the VirtualMachine with the given profile func (v VirtualMachine) AddDeviceWithProfile(ctx context.Context, profile []types.BaseVirtualMachineProfileSpec, device ...types.BaseVirtualDevice) error { return v.configureDevice(ctx, profile, types.VirtualDeviceConfigSpecOperationAdd, types.VirtualDeviceConfigSpecFileOperationCreate, device...) } // EditDevice edits the given (existing) devices on the VirtualMachine func (v VirtualMachine) EditDevice(ctx context.Context, device ...types.BaseVirtualDevice) error { return v.EditDeviceWithProfile(ctx, nil, device...) } // EditDeviceWithProfile edits the given (existing) devices on the VirtualMachine with the given profile func (v VirtualMachine) EditDeviceWithProfile(ctx context.Context, profile []types.BaseVirtualMachineProfileSpec, device ...types.BaseVirtualDevice) error { return v.configureDevice(ctx, profile, types.VirtualDeviceConfigSpecOperationEdit, types.VirtualDeviceConfigSpecFileOperationReplace, device...) } // RemoveDevice removes the given devices on the VirtualMachine func (v VirtualMachine) RemoveDevice(ctx context.Context, keepFiles bool, device ...types.BaseVirtualDevice) error { fop := types.VirtualDeviceConfigSpecFileOperationDestroy if keepFiles { fop = "" } return v.configureDevice(ctx, nil, types.VirtualDeviceConfigSpecOperationRemove, fop, device...) } // AttachDisk attaches the given disk to the VirtualMachine func (v VirtualMachine) AttachDisk(ctx context.Context, id string, datastore *Datastore, controllerKey int32, unitNumber *int32) error { req := types.AttachDisk_Task{ This: v.Reference(), DiskId: types.ID{Id: id}, Datastore: datastore.Reference(), ControllerKey: controllerKey, UnitNumber: unitNumber, } res, err := methods.AttachDisk_Task(ctx, v.c, &req) if err != nil { return err } task := NewTask(v.c, res.Returnval) return task.Wait(ctx) } // DetachDisk detaches the given disk from the VirtualMachine func (v VirtualMachine) DetachDisk(ctx context.Context, id string) error { req := types.DetachDisk_Task{ This: v.Reference(), DiskId: types.ID{Id: id}, } res, err := methods.DetachDisk_Task(ctx, v.c, &req) if err != nil { return err } task := NewTask(v.c, res.Returnval) return task.Wait(ctx) } // BootOptions returns the VirtualMachine's config.bootOptions property. func (v VirtualMachine) BootOptions(ctx context.Context) (*types.VirtualMachineBootOptions, error) { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{"config.bootOptions"}, &o) if err != nil { return nil, err } return o.Config.BootOptions, nil } // SetBootOptions reconfigures the VirtualMachine with the given options. func (v VirtualMachine) SetBootOptions(ctx context.Context, options *types.VirtualMachineBootOptions) error { spec := types.VirtualMachineConfigSpec{} spec.BootOptions = options task, err := v.Reconfigure(ctx, spec) if err != nil { return err } return task.Wait(ctx) } // Answer answers a pending question. func (v VirtualMachine) Answer(ctx context.Context, id, answer string) error { req := types.AnswerVM{ This: v.Reference(), QuestionId: id, AnswerChoice: answer, } _, err := methods.AnswerVM(ctx, v.c, &req) if err != nil { return err } return nil } func (v VirtualMachine) AcquireTicket(ctx context.Context, kind string) (*types.VirtualMachineTicket, error) { req := types.AcquireTicket{ This: v.Reference(), TicketType: kind, } res, err := methods.AcquireTicket(ctx, v.c, &req) if err != nil { return nil, err } return &res.Returnval, nil } // CreateSnapshot creates a new snapshot of a virtual machine. func (v VirtualMachine) CreateSnapshot(ctx context.Context, name string, description string, memory bool, quiesce bool) (*Task, error) { req := types.CreateSnapshot_Task{ This: v.Reference(), Name: name, Description: description, Memory: memory, Quiesce: quiesce, } res, err := methods.CreateSnapshot_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } // CreateSnapshotEx creates a new snapshot of a virtual machine. func (v VirtualMachine) CreateSnapshotEx(ctx context.Context, name string, description string, memory bool, quiesceSpec types.BaseVirtualMachineGuestQuiesceSpec) (*Task, error) { req := types.CreateSnapshotEx_Task{ This: v.Reference(), Name: name, Description: description, Memory: memory, QuiesceSpec: quiesceSpec, } res, err := methods.CreateSnapshotEx_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } // RemoveAllSnapshot removes all snapshots of a virtual machine func (v VirtualMachine) RemoveAllSnapshot(ctx context.Context, consolidate *bool) (*Task, error) { req := types.RemoveAllSnapshots_Task{ This: v.Reference(), Consolidate: consolidate, } res, err := methods.RemoveAllSnapshots_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } type snapshotMap map[string][]types.ManagedObjectReference func (m snapshotMap) add(parent string, tree []types.VirtualMachineSnapshotTree) { for i, st := range tree { sname := st.Name names := []string{sname, st.Snapshot.Value} if parent != "" { sname = path.Join(parent, sname) // Add full path as an option to resolve duplicate names names = append(names, sname) } for _, name := range names { m[name] = append(m[name], tree[i].Snapshot) } m.add(sname, st.ChildSnapshotList) } } // SnapshotSize calculates the size of a given snapshot in bytes. If the // snapshot is current, disk files not associated with any parent snapshot are // included in size calculations. This allows for measuring and including the // growth from the last fixed snapshot to the present state. func SnapshotSize(info types.ManagedObjectReference, parent *types.ManagedObjectReference, vmlayout *types.VirtualMachineFileLayoutEx, isCurrent bool) int { var fileKeyList []int var parentFiles []int var allSnapshotFiles []int diskFiles := extractDiskLayoutFiles(vmlayout.Disk) for _, layout := range vmlayout.Snapshot { diskLayout := extractDiskLayoutFiles(layout.Disk) allSnapshotFiles = append(allSnapshotFiles, diskLayout...) if layout.Key.Value == info.Value { fileKeyList = append(fileKeyList, int(layout.DataKey)) // The .vmsn file fileKeyList = append(fileKeyList, diskLayout...) // The .vmdk files } else if parent != nil && layout.Key.Value == parent.Value { parentFiles = append(parentFiles, diskLayout...) } } for _, parentFile := range parentFiles { removeKey(&fileKeyList, parentFile) } for _, file := range allSnapshotFiles { removeKey(&diskFiles, file) } fileKeyMap := make(map[int]types.VirtualMachineFileLayoutExFileInfo) for _, file := range vmlayout.File { fileKeyMap[int(file.Key)] = file } size := 0 for _, fileKey := range fileKeyList { file := fileKeyMap[fileKey] if parent != nil || (file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskDescriptor) && file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskExtent)) { size += int(file.Size) } } if isCurrent { for _, diskFile := range diskFiles { file := fileKeyMap[diskFile] size += int(file.Size) } } return size } // FindSnapshot supports snapshot lookup by name, where name can be: // 1) snapshot ManagedObjectReference.Value (unique) // 2) snapshot name (may not be unique) // 3) snapshot tree path (may not be unique) func (v VirtualMachine) FindSnapshot(ctx context.Context, name string) (*types.ManagedObjectReference, error) { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{"snapshot"}, &o) if err != nil { return nil, err } if o.Snapshot == nil || len(o.Snapshot.RootSnapshotList) == 0 { return nil, errors.New("no snapshots for this VM") } m := make(snapshotMap) m.add("", o.Snapshot.RootSnapshotList) s := m[name] switch len(s) { case 0: return nil, fmt.Errorf("snapshot %q not found", name) case 1: return &s[0], nil default: return nil, fmt.Errorf("%q resolves to %d snapshots", name, len(s)) } } // RemoveSnapshot removes a named snapshot func (v VirtualMachine) RemoveSnapshot(ctx context.Context, name string, removeChildren bool, consolidate *bool) (*Task, error) { snapshot, err := v.FindSnapshot(ctx, name) if err != nil { return nil, err } req := types.RemoveSnapshot_Task{ This: snapshot.Reference(), RemoveChildren: removeChildren, Consolidate: consolidate, } res, err := methods.RemoveSnapshot_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } // RevertToCurrentSnapshot reverts to the current snapshot func (v VirtualMachine) RevertToCurrentSnapshot(ctx context.Context, suppressPowerOn bool) (*Task, error) { req := types.RevertToCurrentSnapshot_Task{ This: v.Reference(), SuppressPowerOn: types.NewBool(suppressPowerOn), } res, err := methods.RevertToCurrentSnapshot_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } // RevertToSnapshot reverts to a named snapshot func (v VirtualMachine) RevertToSnapshot(ctx context.Context, name string, suppressPowerOn bool) (*Task, error) { snapshot, err := v.FindSnapshot(ctx, name) if err != nil { return nil, err } req := types.RevertToSnapshot_Task{ This: snapshot.Reference(), SuppressPowerOn: types.NewBool(suppressPowerOn), } res, err := methods.RevertToSnapshot_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } // IsToolsRunning returns true if VMware Tools is currently running in the guest OS, and false otherwise. func (v VirtualMachine) IsToolsRunning(ctx context.Context) (bool, error) { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{"guest.toolsRunningStatus"}, &o) if err != nil { return false, err } return o.Guest.ToolsRunningStatus == string(types.VirtualMachineToolsRunningStatusGuestToolsRunning), nil } // Wait for the VirtualMachine to change to the desired power state. func (v VirtualMachine) WaitForPowerState(ctx context.Context, state types.VirtualMachinePowerState) error { p := property.DefaultCollector(v.c) err := property.Wait(ctx, p, v.Reference(), []string{PropRuntimePowerState}, func(pc []types.PropertyChange) bool { for _, c := range pc { if c.Name != PropRuntimePowerState { continue } if c.Val == nil { continue } ps := c.Val.(types.VirtualMachinePowerState) if ps == state { return true } } return false }) return err } func (v VirtualMachine) MarkAsTemplate(ctx context.Context) error { req := types.MarkAsTemplate{ This: v.Reference(), } _, err := methods.MarkAsTemplate(ctx, v.c, &req) if err != nil { return err } return nil } func (v VirtualMachine) MarkAsVirtualMachine(ctx context.Context, pool ResourcePool, host *HostSystem) error { req := types.MarkAsVirtualMachine{ This: v.Reference(), Pool: pool.Reference(), } if host != nil { ref := host.Reference() req.Host = &ref } _, err := methods.MarkAsVirtualMachine(ctx, v.c, &req) if err != nil { return err } return nil } func (v VirtualMachine) Migrate(ctx context.Context, pool *ResourcePool, host *HostSystem, priority types.VirtualMachineMovePriority, state types.VirtualMachinePowerState) (*Task, error) { req := types.MigrateVM_Task{ This: v.Reference(), Priority: priority, State: state, } if pool != nil { ref := pool.Reference() req.Pool = &ref } if host != nil { ref := host.Reference() req.Host = &ref } res, err := methods.MigrateVM_Task(ctx, v.c, &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) Unregister(ctx context.Context) error { req := types.UnregisterVM{ This: v.Reference(), } _, err := methods.UnregisterVM(ctx, v.Client(), &req) return err } func (v VirtualMachine) MountToolsInstaller(ctx context.Context) error { req := types.MountToolsInstaller{ This: v.Reference(), } _, err := methods.MountToolsInstaller(ctx, v.Client(), &req) return err } func (v VirtualMachine) UnmountToolsInstaller(ctx context.Context) error { req := types.UnmountToolsInstaller{ This: v.Reference(), } _, err := methods.UnmountToolsInstaller(ctx, v.Client(), &req) return err } func (v VirtualMachine) UpgradeTools(ctx context.Context, options string) (*Task, error) { req := types.UpgradeTools_Task{ This: v.Reference(), InstallerOptions: options, } res, err := methods.UpgradeTools_Task(ctx, v.Client(), &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } func (v VirtualMachine) Export(ctx context.Context) (*nfc.Lease, error) { req := types.ExportVm{ This: v.Reference(), } res, err := methods.ExportVm(ctx, v.Client(), &req) if err != nil { return nil, err } return nfc.NewLease(v.c, res.Returnval), nil } func (v VirtualMachine) UpgradeVM(ctx context.Context, version string) (*Task, error) { req := types.UpgradeVM_Task{ This: v.Reference(), Version: version, } res, err := methods.UpgradeVM_Task(ctx, v.Client(), &req) if err != nil { return nil, err } return NewTask(v.c, res.Returnval), nil } // UUID is a helper to get the UUID of the VirtualMachine managed object. // This method returns an empty string if an error occurs when retrieving UUID from the VirtualMachine object. func (v VirtualMachine) UUID(ctx context.Context) string { var o mo.VirtualMachine err := v.Properties(ctx, v.Reference(), []string{"config.uuid"}, &o) if err != nil { return "" } if o.Config != nil { return o.Config.Uuid } return "" } func (v VirtualMachine) QueryChangedDiskAreas(ctx context.Context, baseSnapshot, curSnapshot *types.ManagedObjectReference, disk *types.VirtualDisk, offset int64) (types.DiskChangeInfo, error) { var noChange types.DiskChangeInfo var err error if offset > disk.CapacityInBytes { return noChange, fmt.Errorf("offset is greater than the disk size (%#x and %#x)", offset, disk.CapacityInBytes) } else if offset == disk.CapacityInBytes { return types.DiskChangeInfo{StartOffset: offset, Length: 0}, nil } var b mo.VirtualMachineSnapshot err = v.Properties(ctx, baseSnapshot.Reference(), []string{"config.hardware"}, &b) if err != nil { return noChange, fmt.Errorf("failed to fetch config.hardware of snapshot %s: %s", baseSnapshot, err) } var changeId *string for _, vd := range b.Config.Hardware.Device { d := vd.GetVirtualDevice() if d.Key != disk.Key { continue } // As per VDDK programming guide, these are the four types of disks // that support CBT, see "Gathering Changed Block Information". if b, ok := d.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok { changeId = &b.ChangeId break } if b, ok := d.Backing.(*types.VirtualDiskSparseVer2BackingInfo); ok { changeId = &b.ChangeId break } if b, ok := d.Backing.(*types.VirtualDiskRawDiskMappingVer1BackingInfo); ok { changeId = &b.ChangeId break } if b, ok := d.Backing.(*types.VirtualDiskRawDiskVer2BackingInfo); ok { changeId = &b.ChangeId break } return noChange, fmt.Errorf("disk %d has backing info without .ChangeId: %t", disk.Key, d.Backing) } if changeId == nil || *changeId == "" { return noChange, fmt.Errorf("CBT is not enabled on disk %d", disk.Key) } req := types.QueryChangedDiskAreas{ This: v.Reference(), Snapshot: curSnapshot, DeviceKey: disk.Key, StartOffset: offset, ChangeId: *changeId, } res, err := methods.QueryChangedDiskAreas(ctx, v.Client(), &req) if err != nil { return noChange, err } return res.Returnval, nil } // ExportSnapshot exports all VMDK-files up to (but not including) a specified snapshot. This // is useful when exporting a running VM. func (v *VirtualMachine) ExportSnapshot(ctx context.Context, snapshot *types.ManagedObjectReference) (*nfc.Lease, error) { req := types.ExportSnapshot{ This: *snapshot, } resp, err := methods.ExportSnapshot(ctx, v.Client(), &req) if err != nil { return nil, err } return nfc.NewLease(v.c, resp.Returnval), nil } func (v *VirtualMachine) PromoteDisks(ctx context.Context, unlink bool, disks []types.VirtualDisk) (*Task, error) { req := types.PromoteDisks_Task{ This: v.Reference(), Unlink: unlink, Disks: disks, } res, err := methods.PromoteDisks_Task(ctx, v.Client(), &req) if err != nil { return nil, err } return NewTask(v.Client(), res.Returnval), nil }