github.com/vmware/govmomi@v0.51.0/simulator/virtual_machine.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package simulator
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"log"
    12  	"net"
    13  	"os"
    14  	"path"
    15  	"path/filepath"
    16  	"reflect"
    17  	"slices"
    18  	"strconv"
    19  	"strings"
    20  	"sync/atomic"
    21  	"time"
    22  
    23  	"github.com/google/uuid"
    24  
    25  	"github.com/vmware/govmomi/internal"
    26  	"github.com/vmware/govmomi/object"
    27  	"github.com/vmware/govmomi/simulator/esx"
    28  	"github.com/vmware/govmomi/units"
    29  	"github.com/vmware/govmomi/vim25/methods"
    30  	"github.com/vmware/govmomi/vim25/mo"
    31  	"github.com/vmware/govmomi/vim25/soap"
    32  	"github.com/vmware/govmomi/vim25/types"
    33  )
    34  
    35  type VirtualMachine struct {
    36  	mo.VirtualMachine
    37  	DataSets map[string]*DataSet
    38  
    39  	log string
    40  	sid int32
    41  	svm *simVM
    42  	uid uuid.UUID
    43  	imc *types.CustomizationSpec
    44  }
    45  
    46  func asVirtualMachineMO(obj mo.Reference) (*mo.VirtualMachine, bool) {
    47  	vm, ok := getManagedObject(obj).Addr().Interface().(*mo.VirtualMachine)
    48  	return vm, ok
    49  }
    50  
    51  func NewVirtualMachine(ctx *Context, parent types.ManagedObjectReference, spec *types.VirtualMachineConfigSpec) (*VirtualMachine, types.BaseMethodFault) {
    52  	vm := &VirtualMachine{}
    53  	vm.Parent = &parent
    54  	ctx.Map.reference(vm)
    55  
    56  	folder := ctx.Map.Get(parent)
    57  
    58  	if spec.Name == "" {
    59  		return vm, &types.InvalidVmConfig{Property: "configSpec.name"}
    60  	}
    61  
    62  	if spec.Files == nil || spec.Files.VmPathName == "" {
    63  		return vm, &types.InvalidVmConfig{Property: "configSpec.files.vmPathName"}
    64  	}
    65  
    66  	rspec := types.DefaultResourceConfigSpec()
    67  	vm.Guest = &types.GuestInfo{}
    68  	vm.Config = &types.VirtualMachineConfigInfo{
    69  		ExtraConfig:        []types.BaseOptionValue{&types.OptionValue{Key: "govcsim", Value: "TRUE"}},
    70  		Tools:              &types.ToolsConfigInfo{},
    71  		MemoryAllocation:   &rspec.MemoryAllocation,
    72  		CpuAllocation:      &rspec.CpuAllocation,
    73  		LatencySensitivity: &types.LatencySensitivity{Level: types.LatencySensitivitySensitivityLevelNormal},
    74  		BootOptions:        &types.VirtualMachineBootOptions{},
    75  		CreateDate:         types.NewTime(time.Now()),
    76  	}
    77  	vm.Layout = &types.VirtualMachineFileLayout{}
    78  	vm.LayoutEx = &types.VirtualMachineFileLayoutEx{
    79  		Timestamp: time.Now(),
    80  	}
    81  	vm.Snapshot = nil // intentionally set to nil until a snapshot is created
    82  	vm.Storage = &types.VirtualMachineStorageInfo{
    83  		Timestamp: time.Now(),
    84  	}
    85  	vm.Summary.Guest = &types.VirtualMachineGuestSummary{}
    86  	vm.Summary.Vm = &vm.Self
    87  	vm.Summary.Storage = &types.VirtualMachineStorageSummary{
    88  		Timestamp: time.Now(),
    89  	}
    90  
    91  	vmx := vm.vmx(spec)
    92  	if vmx.Path == "" {
    93  		// Append VM Name as the directory name if not specified
    94  		vmx.Path = spec.Name
    95  	}
    96  
    97  	dc := ctx.Map.getEntityDatacenter(folder.(mo.Entity))
    98  	ds := ctx.Map.FindByName(vmx.Datastore, dc.Datastore).(*Datastore)
    99  	dir := ds.resolve(ctx, vmx.Path)
   100  
   101  	if path.Ext(vmx.Path) == ".vmx" {
   102  		dir = path.Dir(dir)
   103  		// Ignore error here, deferring to createFile
   104  		_ = os.Mkdir(dir, 0700)
   105  	} else {
   106  		// Create VM directory, renaming if already exists
   107  		name := dir
   108  
   109  		for i := 0; i < 1024; /* just in case */ i++ {
   110  			err := os.Mkdir(name, 0700)
   111  			if err != nil {
   112  				if os.IsExist(err) {
   113  					name = fmt.Sprintf("%s (%d)", dir, i)
   114  					continue
   115  				}
   116  				return nil, &types.FileFault{File: name}
   117  			}
   118  			break
   119  		}
   120  		vmx.Path = path.Join(path.Base(name), spec.Name+".vmx")
   121  	}
   122  
   123  	spec.Files.VmPathName = vmx.String()
   124  
   125  	dsPath := path.Dir(spec.Files.VmPathName)
   126  	vm.uid = internal.OID(spec.Files.VmPathName)
   127  
   128  	defaults := types.VirtualMachineConfigSpec{
   129  		NumCPUs:           1,
   130  		NumCoresPerSocket: 1,
   131  		MemoryMB:          32,
   132  		Uuid:              vm.uid.String(),
   133  		InstanceUuid:      newUUID(strings.ToUpper(spec.Files.VmPathName)),
   134  		Version:           esx.HardwareVersion,
   135  		Firmware:          string(types.GuestOsDescriptorFirmwareTypeBios),
   136  		VAppConfig:        spec.VAppConfig,
   137  		Files: &types.VirtualMachineFileInfo{
   138  			SnapshotDirectory: dsPath,
   139  			SuspendDirectory:  dsPath,
   140  			LogDirectory:      dsPath,
   141  		},
   142  	}
   143  
   144  	// Add the default devices
   145  	defaults.DeviceChange, _ = object.VirtualDeviceList(esx.VirtualDevice).ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
   146  
   147  	err := vm.configure(ctx, &defaults)
   148  	if err != nil {
   149  		return vm, err
   150  	}
   151  
   152  	vm.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff
   153  	vm.Runtime.ConnectionState = types.VirtualMachineConnectionStateConnected
   154  	vm.Summary.Runtime = vm.Runtime
   155  
   156  	vm.Capability.ChangeTrackingSupported = types.NewBool(changeTrackingSupported(spec))
   157  
   158  	vm.Summary.QuickStats.GuestHeartbeatStatus = types.ManagedEntityStatusGray
   159  	vm.Summary.OverallStatus = types.ManagedEntityStatusGreen
   160  	vm.ConfigStatus = types.ManagedEntityStatusGreen
   161  	vm.DataSets = make(map[string]*DataSet)
   162  
   163  	// put vm in the folder only if no errors occurred
   164  	f, _ := asFolderMO(folder)
   165  	folderPutChild(ctx, f, vm)
   166  
   167  	return vm, nil
   168  }
   169  
   170  func (o *VirtualMachine) RenameTask(ctx *Context, r *types.Rename_Task) soap.HasFault {
   171  	return RenameTask(ctx, o, r)
   172  }
   173  
   174  func (*VirtualMachine) Reload(*types.Reload) soap.HasFault {
   175  	return &methods.ReloadBody{Res: new(types.ReloadResponse)}
   176  }
   177  
   178  func (vm *VirtualMachine) event(ctx *Context) types.VmEvent {
   179  	host := ctx.Map.Get(*vm.Runtime.Host).(*HostSystem)
   180  
   181  	return types.VmEvent{
   182  		Event: types.Event{
   183  			Datacenter:      datacenterEventArgument(ctx, host),
   184  			ComputeResource: host.eventArgumentParent(ctx),
   185  			Host:            host.eventArgument(),
   186  			Ds:              ctx.Map.Get(vm.Datastore[0]).(*Datastore).eventArgument(),
   187  			Vm: &types.VmEventArgument{
   188  				EntityEventArgument: types.EntityEventArgument{Name: vm.Name},
   189  				Vm:                  vm.Self,
   190  			},
   191  		},
   192  	}
   193  }
   194  
   195  func (vm *VirtualMachine) hostInMM(ctx *Context) bool {
   196  	return ctx.Map.Get(*vm.Runtime.Host).(*HostSystem).Runtime.InMaintenanceMode
   197  }
   198  
   199  func (vm *VirtualMachine) apply(spec *types.VirtualMachineConfigSpec) {
   200  	if spec.Files == nil {
   201  		spec.Files = new(types.VirtualMachineFileInfo)
   202  	}
   203  
   204  	apply := []struct {
   205  		src string
   206  		dst *string
   207  	}{
   208  		{spec.AlternateGuestName, &vm.Config.AlternateGuestName},
   209  		{spec.Annotation, &vm.Config.Annotation},
   210  		{spec.Firmware, &vm.Config.Firmware},
   211  		{spec.InstanceUuid, &vm.Config.InstanceUuid},
   212  		{spec.LocationId, &vm.Config.LocationId},
   213  		{spec.NpivWorldWideNameType, &vm.Config.NpivWorldWideNameType},
   214  		{spec.Name, &vm.Name},
   215  		{spec.Name, &vm.Config.Name},
   216  		{spec.Name, &vm.Summary.Config.Name},
   217  		{spec.GuestId, &vm.Config.GuestId},
   218  		{spec.GuestId, &vm.Config.GuestFullName},
   219  		{spec.GuestId, &vm.Summary.Guest.GuestId},
   220  		{spec.GuestId, &vm.Summary.Config.GuestId},
   221  		{spec.GuestId, &vm.Summary.Config.GuestFullName},
   222  		{spec.Uuid, &vm.Config.Uuid},
   223  		{spec.Uuid, &vm.Summary.Config.Uuid},
   224  		{spec.InstanceUuid, &vm.Config.InstanceUuid},
   225  		{spec.InstanceUuid, &vm.Summary.Config.InstanceUuid},
   226  		{spec.Version, &vm.Config.Version},
   227  		{spec.Version, &vm.Summary.Config.HwVersion},
   228  		{spec.Files.VmPathName, &vm.Config.Files.VmPathName},
   229  		{spec.Files.VmPathName, &vm.Summary.Config.VmPathName},
   230  		{spec.Files.SnapshotDirectory, &vm.Config.Files.SnapshotDirectory},
   231  		{spec.Files.SuspendDirectory, &vm.Config.Files.SuspendDirectory},
   232  		{spec.Files.LogDirectory, &vm.Config.Files.LogDirectory},
   233  		{spec.FtEncryptionMode, &vm.Config.FtEncryptionMode},
   234  		{spec.MigrateEncryption, &vm.Config.MigrateEncryption},
   235  	}
   236  
   237  	for _, f := range apply {
   238  		if f.src != "" {
   239  			*f.dst = f.src
   240  		}
   241  	}
   242  
   243  	applyb := []struct {
   244  		src *bool
   245  		dst **bool
   246  	}{
   247  		{spec.NestedHVEnabled, &vm.Config.NestedHVEnabled},
   248  		{spec.CpuHotAddEnabled, &vm.Config.CpuHotAddEnabled},
   249  		{spec.CpuHotRemoveEnabled, &vm.Config.CpuHotRemoveEnabled},
   250  		{spec.GuestAutoLockEnabled, &vm.Config.GuestAutoLockEnabled},
   251  		{spec.MemoryHotAddEnabled, &vm.Config.MemoryHotAddEnabled},
   252  		{spec.MemoryReservationLockedToMax, &vm.Config.MemoryReservationLockedToMax},
   253  		{spec.MessageBusTunnelEnabled, &vm.Config.MessageBusTunnelEnabled},
   254  		{spec.NpivTemporaryDisabled, &vm.Config.NpivTemporaryDisabled},
   255  		{spec.NpivOnNonRdmDisks, &vm.Config.NpivOnNonRdmDisks},
   256  		{spec.ChangeTrackingEnabled, &vm.Config.ChangeTrackingEnabled},
   257  	}
   258  
   259  	for _, f := range applyb {
   260  		if f.src != nil {
   261  			*f.dst = f.src
   262  		}
   263  	}
   264  
   265  	if spec.Flags != nil {
   266  		vm.Config.Flags = *spec.Flags
   267  	}
   268  
   269  	if spec.LatencySensitivity != nil {
   270  		vm.Config.LatencySensitivity = spec.LatencySensitivity
   271  	}
   272  
   273  	if spec.ManagedBy != nil {
   274  		if spec.ManagedBy.ExtensionKey == "" {
   275  			spec.ManagedBy = nil
   276  		}
   277  		vm.Config.ManagedBy = spec.ManagedBy
   278  		vm.Summary.Config.ManagedBy = spec.ManagedBy
   279  	}
   280  
   281  	if spec.BootOptions != nil {
   282  		vm.Config.BootOptions = spec.BootOptions
   283  	}
   284  
   285  	if spec.RepConfig != nil {
   286  		vm.Config.RepConfig = spec.RepConfig
   287  	}
   288  
   289  	if spec.Tools != nil {
   290  		vm.Config.Tools = spec.Tools
   291  	}
   292  
   293  	if spec.ConsolePreferences != nil {
   294  		vm.Config.ConsolePreferences = spec.ConsolePreferences
   295  	}
   296  
   297  	if spec.CpuAffinity != nil {
   298  		vm.Config.CpuAffinity = spec.CpuAffinity
   299  	}
   300  
   301  	if spec.CpuAllocation != nil {
   302  		vm.Config.CpuAllocation = spec.CpuAllocation
   303  	}
   304  
   305  	if spec.MemoryAffinity != nil {
   306  		vm.Config.MemoryAffinity = spec.MemoryAffinity
   307  	}
   308  
   309  	if spec.MemoryAllocation != nil {
   310  		vm.Config.MemoryAllocation = spec.MemoryAllocation
   311  	}
   312  
   313  	if spec.LatencySensitivity != nil {
   314  		vm.Config.LatencySensitivity = spec.LatencySensitivity
   315  	}
   316  
   317  	if spec.MemoryMB != 0 {
   318  		vm.Config.Hardware.MemoryMB = int32(spec.MemoryMB)
   319  		vm.Summary.Config.MemorySizeMB = vm.Config.Hardware.MemoryMB
   320  	}
   321  
   322  	if spec.NumCPUs != 0 {
   323  		vm.Config.Hardware.NumCPU = spec.NumCPUs
   324  		vm.Summary.Config.NumCpu = vm.Config.Hardware.NumCPU
   325  	}
   326  
   327  	if spec.NumCoresPerSocket != 0 {
   328  		vm.Config.Hardware.NumCoresPerSocket = spec.NumCoresPerSocket
   329  	}
   330  
   331  	if spec.GuestId != "" {
   332  		vm.Guest.GuestFamily = guestFamily(spec.GuestId)
   333  	}
   334  
   335  	vm.Config.Modified = time.Now()
   336  }
   337  
   338  // updateVAppProperty updates the simulator VM with the specified VApp properties.
   339  func (vm *VirtualMachine) updateVAppProperty(spec *types.VmConfigSpec) types.BaseMethodFault {
   340  	if vm.Config.VAppConfig == nil {
   341  		vm.Config.VAppConfig = &types.VmConfigInfo{}
   342  	}
   343  
   344  	info := vm.Config.VAppConfig.GetVmConfigInfo()
   345  	propertyInfo := info.Property
   346  	productInfo := info.Product
   347  
   348  	for _, prop := range spec.Property {
   349  		var foundIndex int
   350  		exists := false
   351  		// Check if the specified property exists or not. This helps rejecting invalid
   352  		// operations (e.g., Adding a VApp property that already exists)
   353  		for i, p := range propertyInfo {
   354  			if p.Key == prop.Info.Key {
   355  				exists = true
   356  				foundIndex = i
   357  				break
   358  			}
   359  		}
   360  
   361  		switch prop.Operation {
   362  		case types.ArrayUpdateOperationAdd:
   363  			if exists {
   364  				return new(types.InvalidArgument)
   365  			}
   366  			propertyInfo = append(propertyInfo, *prop.Info)
   367  		case types.ArrayUpdateOperationEdit:
   368  			if !exists {
   369  				return new(types.InvalidArgument)
   370  			}
   371  			propertyInfo[foundIndex] = *prop.Info
   372  		case types.ArrayUpdateOperationRemove:
   373  			if !exists {
   374  				return new(types.InvalidArgument)
   375  			}
   376  			propertyInfo = append(propertyInfo[:foundIndex], propertyInfo[foundIndex+1:]...)
   377  		}
   378  	}
   379  
   380  	for _, prod := range spec.Product {
   381  		var foundIndex int
   382  		exists := false
   383  		// Check if the specified product exists or not. This helps rejecting invalid
   384  		// operations (e.g., Adding a VApp product that already exists)
   385  		for i, p := range productInfo {
   386  			if p.Key == prod.Info.Key {
   387  				exists = true
   388  				foundIndex = i
   389  				break
   390  			}
   391  		}
   392  
   393  		switch prod.Operation {
   394  		case types.ArrayUpdateOperationAdd:
   395  			if exists {
   396  				return new(types.InvalidArgument)
   397  			}
   398  			productInfo = append(productInfo, *prod.Info)
   399  		case types.ArrayUpdateOperationEdit:
   400  			if !exists {
   401  				return new(types.InvalidArgument)
   402  			}
   403  			productInfo[foundIndex] = *prod.Info
   404  		case types.ArrayUpdateOperationRemove:
   405  			if !exists {
   406  				return new(types.InvalidArgument)
   407  			}
   408  			productInfo = append(productInfo[:foundIndex], productInfo[foundIndex+1:]...)
   409  		}
   410  	}
   411  
   412  	info.Product = productInfo
   413  	info.Property = propertyInfo
   414  
   415  	return nil
   416  }
   417  
   418  var extraConfigAlias = map[string]string{
   419  	"ip0": "SET.guest.ipAddress",
   420  }
   421  
   422  func extraConfigKey(key string) string {
   423  	if k, ok := extraConfigAlias[key]; ok {
   424  		return k
   425  	}
   426  	return key
   427  }
   428  
   429  func (vm *VirtualMachine) applyExtraConfig(ctx *Context, spec *types.VirtualMachineConfigSpec) types.BaseMethodFault {
   430  	if len(spec.ExtraConfig) == 0 {
   431  		return nil
   432  	}
   433  	var removedContainerBacking bool
   434  	var changes []types.PropertyChange
   435  	field := mo.Field{Path: "config.extraConfig"}
   436  
   437  	for _, c := range spec.ExtraConfig {
   438  		val := c.GetOptionValue()
   439  		key := strings.TrimPrefix(extraConfigKey(val.Key), "SET.")
   440  		if key == val.Key {
   441  			field.Key = key
   442  			op := types.PropertyChangeOpAssign
   443  			keyIndex := -1
   444  			for i := range vm.Config.ExtraConfig {
   445  				bov := vm.Config.ExtraConfig[i]
   446  				if bov == nil {
   447  					continue
   448  				}
   449  				ov := bov.GetOptionValue()
   450  				if ov == nil {
   451  					continue
   452  				}
   453  				if ov.Key == key {
   454  					keyIndex = i
   455  					break
   456  				}
   457  			}
   458  			if keyIndex < 0 {
   459  				op = types.PropertyChangeOpAdd
   460  				vm.Config.ExtraConfig = append(vm.Config.ExtraConfig, c)
   461  			} else {
   462  				if s, ok := val.Value.(string); ok && s == "" {
   463  					op = types.PropertyChangeOpRemove
   464  					if key == ContainerBackingOptionKey {
   465  						removedContainerBacking = true
   466  					}
   467  					// Remove existing element
   468  					vm.Config.ExtraConfig = append(
   469  						vm.Config.ExtraConfig[:keyIndex],
   470  						vm.Config.ExtraConfig[keyIndex+1:]...)
   471  					val = nil
   472  				} else {
   473  					// Update existing element
   474  					vm.Config.ExtraConfig[keyIndex] = val
   475  				}
   476  			}
   477  
   478  			changes = append(changes, types.PropertyChange{Name: field.String(), Val: val, Op: op})
   479  			continue
   480  		}
   481  		changes = append(changes, types.PropertyChange{Name: key, Val: val.Value})
   482  
   483  		switch key {
   484  		case "guest.ipAddress":
   485  			if len(vm.Guest.Net) > 0 {
   486  				ip := val.Value.(string)
   487  				vm.Guest.Net[0].IpAddress = []string{ip}
   488  				changes = append(changes,
   489  					types.PropertyChange{Name: "summary." + key, Val: ip},
   490  					types.PropertyChange{Name: "guest.net", Val: vm.Guest.Net},
   491  				)
   492  			}
   493  		case "guest.hostName":
   494  			changes = append(changes,
   495  				types.PropertyChange{Name: "summary." + key, Val: val.Value},
   496  			)
   497  		}
   498  	}
   499  
   500  	// create the container backing before we publish the updates so the simVM is available before handlers
   501  	// get triggered
   502  	var fault types.BaseMethodFault
   503  	if vm.svm == nil {
   504  		vm.svm = createSimulationVM(vm)
   505  
   506  		// check to see if the VM is already powered on - if so we need to retroactively hit that path here
   507  		if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
   508  			err := vm.svm.start(ctx)
   509  			if err != nil {
   510  				// don't attempt to undo the changes already made - just return an error
   511  				// we'll retry the svm.start operation on pause/restart calls
   512  				fault = &types.VAppConfigFault{
   513  					VimFault: types.VimFault{
   514  						MethodFault: types.MethodFault{
   515  							FaultCause: &types.LocalizedMethodFault{
   516  								Fault:            &types.SystemErrorFault{Reason: err.Error()},
   517  								LocalizedMessage: err.Error()}}}}
   518  			}
   519  		}
   520  	} else if removedContainerBacking {
   521  		err := vm.svm.remove(ctx)
   522  		if err == nil {
   523  			// remove link from container to VM so callbacks no longer reflect state
   524  			vm.svm.vm = nil
   525  			// nil container backing reference to return this to a pure in-mem simulated VM
   526  			vm.svm = nil
   527  
   528  		} else {
   529  			// don't attempt to undo the changes already made - just return an error
   530  			// we'll retry the svm.start operation on pause/restart calls
   531  			fault = &types.VAppConfigFault{
   532  				VimFault: types.VimFault{
   533  					MethodFault: types.MethodFault{
   534  						FaultCause: &types.LocalizedMethodFault{
   535  							Fault:            &types.SystemErrorFault{Reason: err.Error()},
   536  							LocalizedMessage: err.Error()}}}}
   537  		}
   538  	}
   539  
   540  	change := types.PropertyChange{Name: field.Path, Val: vm.Config.ExtraConfig}
   541  	ctx.Update(vm, append(changes, change))
   542  
   543  	return fault
   544  }
   545  
   546  func validateGuestID(id string) types.BaseMethodFault {
   547  	for _, x := range GuestID {
   548  		if id == string(x) {
   549  			return nil
   550  		}
   551  	}
   552  
   553  	return &types.InvalidArgument{InvalidProperty: "configSpec.guestId"}
   554  }
   555  
   556  func (vm *VirtualMachine) configure(ctx *Context, spec *types.VirtualMachineConfigSpec) (result types.BaseMethodFault) {
   557  	defer func() {
   558  		if result == nil {
   559  			vm.updateLastModifiedAndChangeVersion(ctx)
   560  		}
   561  	}()
   562  
   563  	vm.apply(spec)
   564  
   565  	if spec.MemoryAllocation != nil {
   566  		if err := updateResourceAllocation("memory", spec.MemoryAllocation, vm.Config.MemoryAllocation); err != nil {
   567  			return err
   568  		}
   569  	}
   570  
   571  	if spec.CpuAllocation != nil {
   572  		if err := updateResourceAllocation("cpu", spec.CpuAllocation, vm.Config.CpuAllocation); err != nil {
   573  			return err
   574  		}
   575  	}
   576  
   577  	if spec.GuestId != "" {
   578  		if err := validateGuestID(spec.GuestId); err != nil {
   579  			return err
   580  		}
   581  	}
   582  
   583  	if o := spec.BootOptions; o != nil {
   584  		if isTrue(o.EfiSecureBootEnabled) && vm.Config.Firmware != string(types.GuestOsDescriptorFirmwareTypeEfi) {
   585  			return &types.InvalidVmConfig{Property: "msg.hostd.configSpec.efi"}
   586  		}
   587  	}
   588  
   589  	if spec.VAppConfig != nil {
   590  		if err := vm.updateVAppProperty(spec.VAppConfig.GetVmConfigSpec()); err != nil {
   591  			return err
   592  		}
   593  	}
   594  
   595  	if spec.Crypto != nil {
   596  		if err := vm.updateCrypto(ctx, spec.Crypto); err != nil {
   597  			return err
   598  		}
   599  	}
   600  
   601  	return vm.configureDevices(ctx, spec)
   602  }
   603  
   604  func getVMFileType(fileName string) types.VirtualMachineFileLayoutExFileType {
   605  	var fileType types.VirtualMachineFileLayoutExFileType
   606  
   607  	fileExt := path.Ext(fileName)
   608  	fileNameNoExt := strings.TrimSuffix(fileName, fileExt)
   609  
   610  	switch fileExt {
   611  	case ".vmx":
   612  		fileType = types.VirtualMachineFileLayoutExFileTypeConfig
   613  	case ".core":
   614  		fileType = types.VirtualMachineFileLayoutExFileTypeCore
   615  	case ".vmdk":
   616  		fileType = types.VirtualMachineFileLayoutExFileTypeDiskDescriptor
   617  		if strings.HasSuffix(fileNameNoExt, "-digest") {
   618  			fileType = types.VirtualMachineFileLayoutExFileTypeDigestDescriptor
   619  		}
   620  
   621  		extentSuffixes := []string{"-flat", "-delta", "-s", "-rdm", "-rdmp"}
   622  		for _, suffix := range extentSuffixes {
   623  			if strings.HasSuffix(fileNameNoExt, suffix) {
   624  				fileType = types.VirtualMachineFileLayoutExFileTypeDiskExtent
   625  			} else if strings.HasSuffix(fileNameNoExt, "-digest"+suffix) {
   626  				fileType = types.VirtualMachineFileLayoutExFileTypeDigestExtent
   627  			}
   628  		}
   629  	case ".psf":
   630  		fileType = types.VirtualMachineFileLayoutExFileTypeDiskReplicationState
   631  	case ".vmxf":
   632  		fileType = types.VirtualMachineFileLayoutExFileTypeExtendedConfig
   633  	case ".vmft":
   634  		fileType = types.VirtualMachineFileLayoutExFileTypeFtMetadata
   635  	case ".log":
   636  		fileType = types.VirtualMachineFileLayoutExFileTypeLog
   637  	case ".nvram":
   638  		fileType = types.VirtualMachineFileLayoutExFileTypeNvram
   639  	case ".png", ".bmp":
   640  		fileType = types.VirtualMachineFileLayoutExFileTypeScreenshot
   641  	case ".vmsn":
   642  		fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotData
   643  	case ".vmsd":
   644  		fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotList
   645  	case ".xml":
   646  		if strings.HasSuffix(fileNameNoExt, "-aux") {
   647  			fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotManifestList
   648  		}
   649  	case ".stat":
   650  		fileType = types.VirtualMachineFileLayoutExFileTypeStat
   651  	case ".vmss":
   652  		fileType = types.VirtualMachineFileLayoutExFileTypeSuspend
   653  	case ".vmem":
   654  		if strings.Contains(fileNameNoExt, "Snapshot") {
   655  			fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotMemory
   656  		} else {
   657  			fileType = types.VirtualMachineFileLayoutExFileTypeSuspendMemory
   658  		}
   659  	case ".vswp":
   660  		if strings.HasPrefix(fileNameNoExt, "vmx-") {
   661  			fileType = types.VirtualMachineFileLayoutExFileTypeUwswap
   662  		} else {
   663  			fileType = types.VirtualMachineFileLayoutExFileTypeSwap
   664  		}
   665  	case "":
   666  		if strings.HasPrefix(fileNameNoExt, "imcf-") {
   667  			fileType = types.VirtualMachineFileLayoutExFileTypeGuestCustomization
   668  		}
   669  	}
   670  
   671  	return fileType
   672  }
   673  
   674  func (vm *VirtualMachine) addFileLayoutEx(ctx *Context, datastorePath object.DatastorePath, fileSize int64) int32 {
   675  	var newKey int32
   676  	for _, layoutFile := range vm.LayoutEx.File {
   677  		if layoutFile.Name == datastorePath.String() {
   678  			return layoutFile.Key
   679  		}
   680  
   681  		if layoutFile.Key >= newKey {
   682  			newKey = layoutFile.Key + 1
   683  		}
   684  	}
   685  
   686  	fileType := getVMFileType(filepath.Base(datastorePath.Path))
   687  
   688  	switch fileType {
   689  	case types.VirtualMachineFileLayoutExFileTypeNvram, types.VirtualMachineFileLayoutExFileTypeSnapshotList:
   690  		if !slices.Contains(vm.Layout.ConfigFile, datastorePath.Path) {
   691  			vm.Layout.ConfigFile = append(vm.Layout.ConfigFile, datastorePath.Path)
   692  		}
   693  	case types.VirtualMachineFileLayoutExFileTypeLog:
   694  		if !slices.Contains(vm.Layout.LogFile, datastorePath.Path) {
   695  			vm.Layout.LogFile = append(vm.Layout.LogFile, datastorePath.Path)
   696  		}
   697  	case types.VirtualMachineFileLayoutExFileTypeSwap:
   698  		vm.Layout.SwapFile = datastorePath.String()
   699  	}
   700  
   701  	vm.LayoutEx.File = append(vm.LayoutEx.File, types.VirtualMachineFileLayoutExFileInfo{
   702  		Accessible:      types.NewBool(true),
   703  		BackingObjectId: "",
   704  		Key:             newKey,
   705  		Name:            datastorePath.String(),
   706  		Size:            fileSize,
   707  		Type:            string(fileType),
   708  		UniqueSize:      fileSize,
   709  	})
   710  
   711  	vm.LayoutEx.Timestamp = time.Now()
   712  
   713  	vm.updateStorage(ctx)
   714  
   715  	return newKey
   716  }
   717  
   718  func (vm *VirtualMachine) addSnapshotLayout(ctx *Context, snapshot types.ManagedObjectReference, dataKey int32) {
   719  	for _, snapshotLayout := range vm.Layout.Snapshot {
   720  		if snapshotLayout.Key == snapshot {
   721  			return
   722  		}
   723  	}
   724  
   725  	var snapshotFiles []string
   726  	for _, file := range vm.LayoutEx.File {
   727  		if file.Key == dataKey || file.Type == "diskDescriptor" {
   728  			snapshotFiles = append(snapshotFiles, file.Name)
   729  		}
   730  	}
   731  
   732  	vm.Layout.Snapshot = append(vm.Layout.Snapshot, types.VirtualMachineFileLayoutSnapshotLayout{
   733  		Key:          snapshot,
   734  		SnapshotFile: snapshotFiles,
   735  	})
   736  }
   737  
   738  func (vm *VirtualMachine) addSnapshotLayoutEx(ctx *Context, snapshot types.ManagedObjectReference, dataKey int32, memoryKey int32) {
   739  	for _, snapshotLayoutEx := range vm.LayoutEx.Snapshot {
   740  		if snapshotLayoutEx.Key == snapshot {
   741  			return
   742  		}
   743  	}
   744  
   745  	vm.LayoutEx.Snapshot = append(vm.LayoutEx.Snapshot, types.VirtualMachineFileLayoutExSnapshotLayout{
   746  		DataKey:   dataKey,
   747  		Disk:      vm.LayoutEx.Disk,
   748  		Key:       snapshot,
   749  		MemoryKey: memoryKey,
   750  	})
   751  
   752  	vm.LayoutEx.Timestamp = time.Now()
   753  
   754  	vm.updateStorage(ctx)
   755  }
   756  
   757  // Updates both vm.Layout.Disk and vm.LayoutEx.Disk
   758  func (vm *VirtualMachine) updateDiskLayouts(ctx *Context) types.BaseMethodFault {
   759  	var disksLayout []types.VirtualMachineFileLayoutDiskLayout
   760  	var disksLayoutEx []types.VirtualMachineFileLayoutExDiskLayout
   761  
   762  	disks := object.VirtualDeviceList(vm.Config.Hardware.Device).SelectByType((*types.VirtualDisk)(nil))
   763  	for _, disk := range disks {
   764  		disk := disk.(*types.VirtualDisk)
   765  		diskBacking := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
   766  
   767  		diskLayout := &types.VirtualMachineFileLayoutDiskLayout{Key: disk.Key}
   768  		diskLayoutEx := &types.VirtualMachineFileLayoutExDiskLayout{Key: disk.Key}
   769  
   770  		// Iterate through disk and its parents
   771  		for {
   772  			dFileName := diskBacking.GetVirtualDeviceFileBackingInfo().FileName
   773  
   774  			var fileKeys []int32
   775  
   776  			// Add disk descriptor and extent files
   777  			for _, diskName := range vdmNames(dFileName) {
   778  				// get full path including datastore location
   779  				p, fault := parseDatastorePath(diskName)
   780  				if fault != nil {
   781  					return fault
   782  				}
   783  
   784  				datastore := vm.useDatastore(ctx, p.Datastore)
   785  				dFilePath := datastore.resolve(ctx, p.Path)
   786  
   787  				var fileSize int64
   788  				// If file can not be opened - fileSize will be 0
   789  				if dFileInfo, err := os.Stat(dFilePath); err == nil {
   790  					fileSize = dFileInfo.Size()
   791  				}
   792  
   793  				diskKey := vm.addFileLayoutEx(ctx, *p, fileSize)
   794  				fileKeys = append(fileKeys, diskKey)
   795  			}
   796  
   797  			diskLayout.DiskFile = append(diskLayout.DiskFile, dFileName)
   798  			diskLayoutEx.Chain = append(diskLayoutEx.Chain, types.VirtualMachineFileLayoutExDiskUnit{
   799  				FileKey: fileKeys,
   800  			})
   801  
   802  			if parent := diskBacking.Parent; parent != nil {
   803  				diskBacking = parent
   804  			} else {
   805  				break
   806  			}
   807  		}
   808  
   809  		disksLayout = append(disksLayout, *diskLayout)
   810  		disksLayoutEx = append(disksLayoutEx, *diskLayoutEx)
   811  	}
   812  
   813  	vm.Layout.Disk = disksLayout
   814  
   815  	vm.LayoutEx.Disk = disksLayoutEx
   816  	vm.LayoutEx.Timestamp = time.Now()
   817  
   818  	vm.updateStorage(ctx)
   819  
   820  	return nil
   821  }
   822  
   823  func (vm *VirtualMachine) updateStorage(ctx *Context) types.BaseMethodFault {
   824  	// Committed - sum of Size for each file in vm.LayoutEx.File
   825  	// Unshared  - sum of Size for each disk (.vmdk) in vm.LayoutEx.File
   826  	// Uncommitted - disk capacity minus disk usage (only currently used disk)
   827  	var datastoresUsage []types.VirtualMachineUsageOnDatastore
   828  
   829  	disks := object.VirtualDeviceList(vm.Config.Hardware.Device).SelectByType((*types.VirtualDisk)(nil))
   830  
   831  	for _, file := range vm.LayoutEx.File {
   832  		p, fault := parseDatastorePath(file.Name)
   833  		if fault != nil {
   834  			return fault
   835  		}
   836  
   837  		datastore := vm.useDatastore(ctx, p.Datastore)
   838  		dsUsage := &types.VirtualMachineUsageOnDatastore{
   839  			Datastore: datastore.Self,
   840  		}
   841  
   842  		for idx, usage := range datastoresUsage {
   843  			if usage.Datastore == datastore.Self {
   844  				datastoresUsage = append(datastoresUsage[:idx], datastoresUsage[idx+1:]...)
   845  				dsUsage = &usage
   846  				break
   847  			}
   848  		}
   849  
   850  		dsUsage.Committed += file.Size
   851  
   852  		if path.Ext(file.Name) == ".vmdk" {
   853  			dsUsage.Unshared += file.Size
   854  		}
   855  
   856  		for _, disk := range disks {
   857  			disk := disk.(*types.VirtualDisk)
   858  			backing := disk.Backing.(types.BaseVirtualDeviceFileBackingInfo).GetVirtualDeviceFileBackingInfo()
   859  
   860  			if backing.FileName == file.Name {
   861  				dsUsage.Uncommitted += disk.CapacityInBytes
   862  			}
   863  		}
   864  
   865  		datastoresUsage = append(datastoresUsage, *dsUsage)
   866  	}
   867  
   868  	vm.Storage.PerDatastoreUsage = datastoresUsage
   869  	vm.Storage.Timestamp = time.Now()
   870  
   871  	storageSummary := &types.VirtualMachineStorageSummary{
   872  		Timestamp: time.Now(),
   873  	}
   874  
   875  	for i, usage := range datastoresUsage {
   876  		datastoresUsage[i].Uncommitted -= usage.Committed
   877  		storageSummary.Committed += usage.Committed
   878  		storageSummary.Uncommitted += usage.Uncommitted
   879  		storageSummary.Unshared += usage.Unshared
   880  	}
   881  
   882  	vm.Summary.Storage = storageSummary
   883  
   884  	return nil
   885  }
   886  
   887  func (vm *VirtualMachine) RefreshStorageInfo(ctx *Context, req *types.RefreshStorageInfo) soap.HasFault {
   888  	body := new(methods.RefreshStorageInfoBody)
   889  
   890  	if vm.Runtime.Host == nil {
   891  		// VM not fully created
   892  		return body
   893  	}
   894  
   895  	// Validate that all files in vm.LayoutEx.File can still be found
   896  	for idx := len(vm.LayoutEx.File) - 1; idx >= 0; idx-- {
   897  		file := vm.LayoutEx.File[idx]
   898  
   899  		p, fault := parseDatastorePath(file.Name)
   900  		if fault != nil {
   901  			body.Fault_ = Fault("", fault)
   902  			return body
   903  		}
   904  
   905  		if _, err := os.Stat(p.String()); err != nil {
   906  			vm.LayoutEx.File = append(vm.LayoutEx.File[:idx], vm.LayoutEx.File[idx+1:]...)
   907  		}
   908  	}
   909  
   910  	vmPathName := vm.Config.Files.VmPathName
   911  	// vm.Config.Files.VmPathName can be a directory or full path to .vmx
   912  	if path.Ext(vmPathName) == ".vmx" {
   913  		vmPathName = path.Dir(vmPathName)
   914  	}
   915  
   916  	// Directories will be used to locate VM files.
   917  	// Does not include information about virtual disk file locations.
   918  	locations := []string{
   919  		vmPathName,
   920  		vm.Config.Files.SnapshotDirectory,
   921  		vm.Config.Files.LogDirectory,
   922  		vm.Config.Files.SuspendDirectory,
   923  		vm.Config.Files.FtMetadataDirectory,
   924  	}
   925  
   926  	for _, directory := range slices.Compact(locations) {
   927  		if directory == "" {
   928  			continue
   929  		}
   930  
   931  		p, fault := parseDatastorePath(directory)
   932  		if fault != nil {
   933  			body.Fault_ = Fault("", fault)
   934  			return body
   935  		}
   936  
   937  		datastore := vm.useDatastore(ctx, p.Datastore)
   938  		directory := datastore.resolve(ctx, p.Path)
   939  
   940  		if _, err := os.Stat(directory); err != nil {
   941  			// Can not access the directory
   942  			continue
   943  		}
   944  
   945  		files, err := os.ReadDir(directory)
   946  		if err != nil {
   947  			body.Fault_ = Fault("", ctx.Map.FileManager().fault(directory, err, new(types.CannotAccessFile)))
   948  			return body
   949  		}
   950  
   951  		for _, file := range files {
   952  			datastorePath := object.DatastorePath{
   953  				Datastore: p.Datastore,
   954  				Path:      path.Join(p.Path, file.Name()),
   955  			}
   956  			info, _ := file.Info()
   957  			vm.addFileLayoutEx(ctx, datastorePath, info.Size())
   958  		}
   959  	}
   960  
   961  	fault := vm.updateDiskLayouts(ctx)
   962  	if fault != nil {
   963  		body.Fault_ = Fault("", fault)
   964  		return body
   965  	}
   966  
   967  	vm.LayoutEx.Timestamp = time.Now()
   968  
   969  	body.Res = new(types.RefreshStorageInfoResponse)
   970  
   971  	return body
   972  }
   973  
   974  func (vm *VirtualMachine) findDatastore(ctx *Context, name string) *Datastore {
   975  	host := ctx.Map.Get(*vm.Runtime.Host).(*HostSystem)
   976  
   977  	return ctx.Map.FindByName(name, host.Datastore).(*Datastore)
   978  }
   979  
   980  func (vm *VirtualMachine) useDatastore(ctx *Context, name string) *Datastore {
   981  	ds := vm.findDatastore(ctx, name)
   982  	if FindReference(vm.Datastore, ds.Self) == nil {
   983  		vm.Datastore = append(vm.Datastore, ds.Self)
   984  	}
   985  
   986  	return ds
   987  }
   988  
   989  func (vm *VirtualMachine) vmx(spec *types.VirtualMachineConfigSpec) object.DatastorePath {
   990  	var p object.DatastorePath
   991  	vmx := vm.Config.Files.VmPathName
   992  	if spec != nil {
   993  		vmx = spec.Files.VmPathName
   994  	}
   995  	p.FromString(vmx)
   996  	return p
   997  }
   998  
   999  func (vm *VirtualMachine) createFile(ctx *Context, spec string, name string, register bool) (*os.File, types.BaseMethodFault) {
  1000  	p, fault := parseDatastorePath(spec)
  1001  	if fault != nil {
  1002  		return nil, fault
  1003  	}
  1004  
  1005  	ds := vm.useDatastore(ctx, p.Datastore)
  1006  
  1007  	nhost := len(ds.Host)
  1008  	if internal.IsDatastoreVSAN(ds.Datastore) && nhost < 3 {
  1009  		fault := new(types.CannotCreateFile)
  1010  		fault.FaultMessage = []types.LocalizableMessage{
  1011  			{
  1012  				Key:     "vob.vsanprovider.object.creation.failed",
  1013  				Message: "Failed to create object.",
  1014  			},
  1015  			{
  1016  				Key:     "vob.vsan.clomd.needMoreFaultDomains2",
  1017  				Message: fmt.Sprintf("There are currently %d usable fault domains. The operation requires %d more usable fault domains.", nhost, 3-nhost),
  1018  			},
  1019  		}
  1020  		fault.File = p.Path
  1021  		return nil, fault
  1022  	}
  1023  
  1024  	file := ds.resolve(ctx, p.Path)
  1025  
  1026  	if name != "" {
  1027  		if path.Ext(p.Path) == ".vmx" {
  1028  			file = path.Dir(file) // vm.Config.Files.VmPathName can be a directory or full path to .vmx
  1029  		}
  1030  
  1031  		file = path.Join(file, name)
  1032  	}
  1033  
  1034  	if register {
  1035  		f, err := os.Open(filepath.Clean(file))
  1036  		if err != nil {
  1037  			log.Printf("register %s: %s", vm.Reference(), err)
  1038  			if os.IsNotExist(err) {
  1039  				return nil, &types.NotFound{}
  1040  			}
  1041  
  1042  			return nil, &types.InvalidArgument{}
  1043  		}
  1044  
  1045  		return f, nil
  1046  	}
  1047  
  1048  	_, err := os.Stat(file)
  1049  	if err == nil {
  1050  		fault := &types.FileAlreadyExists{FileFault: types.FileFault{File: file}}
  1051  		log.Printf("%T: %s", fault, file)
  1052  		return nil, fault
  1053  	}
  1054  
  1055  	// Create parent directory if needed
  1056  	dir := path.Dir(file)
  1057  	_, err = os.Stat(dir)
  1058  	if err != nil {
  1059  		if os.IsNotExist(err) {
  1060  			_ = os.Mkdir(dir, 0700)
  1061  		}
  1062  	}
  1063  
  1064  	f, err := os.Create(file)
  1065  	if err != nil {
  1066  		log.Printf("create(%s): %s", file, err)
  1067  		return nil, &types.FileFault{
  1068  			File: file,
  1069  		}
  1070  	}
  1071  
  1072  	return f, nil
  1073  }
  1074  
  1075  // Rather than keep an fd open for each VM, open/close the log for each messages.
  1076  // This is ok for now as we do not do any heavy VM logging.
  1077  func (vm *VirtualMachine) logPrintf(format string, v ...any) {
  1078  	f, err := os.OpenFile(vm.log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0)
  1079  	if err != nil {
  1080  		log.Println(err)
  1081  		return
  1082  	}
  1083  	log.New(f, "vmx ", log.Flags()).Printf(format, v...)
  1084  	_ = f.Close()
  1085  }
  1086  
  1087  func (vm *VirtualMachine) create(ctx *Context, spec *types.VirtualMachineConfigSpec, register bool) types.BaseMethodFault {
  1088  	vm.apply(spec)
  1089  
  1090  	if spec.Version != "" {
  1091  		v := strings.TrimPrefix(spec.Version, "vmx-")
  1092  		_, err := strconv.Atoi(v)
  1093  		if err != nil {
  1094  			log.Printf("unsupported hardware version: %s", spec.Version)
  1095  			return new(types.NotSupported)
  1096  		}
  1097  	}
  1098  
  1099  	files := []struct {
  1100  		spec string
  1101  		name string
  1102  		use  *string
  1103  	}{
  1104  		{vm.Config.Files.VmPathName, "", nil},
  1105  		{vm.Config.Files.VmPathName, fmt.Sprintf("%s.nvram", vm.Name), nil},
  1106  		{vm.Config.Files.LogDirectory, "vmware.log", &vm.log},
  1107  	}
  1108  
  1109  	for _, file := range files {
  1110  		f, err := vm.createFile(ctx, file.spec, file.name, register)
  1111  		if err != nil {
  1112  			return err
  1113  		}
  1114  		if file.use != nil {
  1115  			*file.use = f.Name()
  1116  		}
  1117  		_ = f.Close()
  1118  	}
  1119  
  1120  	vm.logPrintf("created")
  1121  
  1122  	return vm.configureDevices(ctx, spec)
  1123  }
  1124  
  1125  var vmwOUI = net.HardwareAddr([]byte{0x0, 0xc, 0x29})
  1126  
  1127  // From https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/8-0/vsphere-networking-8-0/mac-addresses/mac-address-generation-on-esxi-hosts.html
  1128  // > The host generates generateMAC addresses that consists of the VMware OUI 00:0C:29 and the last three octets in hexadecimal
  1129  // > format of the virtual machine UUID.  The virtual machine UUID is based on a hash calculated by using the UUID of the
  1130  // > ESXi physical machine and the path to the configuration file (.vmx) of the virtual machine.
  1131  func (vm *VirtualMachine) generateMAC(unit int32) string {
  1132  	id := []byte(vm.Config.Uuid)
  1133  
  1134  	offset := len(id) - len(vmwOUI)
  1135  	key := id[offset] + byte(unit) // add device unit number, giving each VM NIC a unique MAC
  1136  	id = append([]byte{key}, id[offset+1:]...)
  1137  
  1138  	mac := append(vmwOUI, id...)
  1139  
  1140  	return mac.String()
  1141  }
  1142  
  1143  func numberToString(n int64, sep rune) string {
  1144  	buf := &bytes.Buffer{}
  1145  	if n < 0 {
  1146  		n = -n
  1147  		buf.WriteRune('-')
  1148  	}
  1149  	s := strconv.FormatInt(n, 10)
  1150  	pos := 3 - (len(s) % 3)
  1151  	for i := 0; i < len(s); i++ {
  1152  		if pos == 3 {
  1153  			if i != 0 {
  1154  				buf.WriteRune(sep)
  1155  			}
  1156  			pos = 0
  1157  		}
  1158  		pos++
  1159  		buf.WriteByte(s[i])
  1160  	}
  1161  
  1162  	return buf.String()
  1163  }
  1164  
  1165  func getDiskSize(disk *types.VirtualDisk) int64 {
  1166  	if disk.CapacityInBytes == 0 {
  1167  		return disk.CapacityInKB * 1024
  1168  	}
  1169  	return disk.CapacityInBytes
  1170  }
  1171  
  1172  func changedDiskSize(oldDisk *types.VirtualDisk, newDiskSpec *types.VirtualDisk) (int64, bool) {
  1173  	// capacity cannot be decreased
  1174  	if newDiskSpec.CapacityInBytes > 0 && newDiskSpec.CapacityInBytes < oldDisk.CapacityInBytes {
  1175  		return 0, false
  1176  	}
  1177  	if newDiskSpec.CapacityInKB > 0 && newDiskSpec.CapacityInKB < oldDisk.CapacityInKB {
  1178  		return 0, false
  1179  	}
  1180  
  1181  	// NOTE: capacity is ignored if specified value is same as before
  1182  	if newDiskSpec.CapacityInBytes == oldDisk.CapacityInBytes {
  1183  		return newDiskSpec.CapacityInKB * 1024, true
  1184  	}
  1185  	if newDiskSpec.CapacityInKB == oldDisk.CapacityInKB {
  1186  		return newDiskSpec.CapacityInBytes, true
  1187  	}
  1188  
  1189  	// if both set, CapacityInBytes and CapacityInKB must be the same
  1190  	if newDiskSpec.CapacityInBytes > 0 && newDiskSpec.CapacityInKB > 0 {
  1191  		if newDiskSpec.CapacityInBytes != newDiskSpec.CapacityInKB*1024 {
  1192  			return 0, false
  1193  		}
  1194  	}
  1195  
  1196  	return newDiskSpec.CapacityInBytes, true
  1197  }
  1198  
  1199  func (vm *VirtualMachine) validateSwitchMembers(ctx *Context, id string) types.BaseMethodFault {
  1200  	var dswitch *DistributedVirtualSwitch
  1201  
  1202  	var find func(types.ManagedObjectReference)
  1203  	find = func(child types.ManagedObjectReference) {
  1204  		s, ok := ctx.Map.Get(child).(*DistributedVirtualSwitch)
  1205  		if ok && s.Uuid == id {
  1206  			dswitch = s
  1207  			return
  1208  		}
  1209  		walk(ctx.Map.Get(child), find)
  1210  	}
  1211  	f := ctx.Map.getEntityDatacenter(vm).NetworkFolder
  1212  	walk(ctx.Map.Get(f), find) // search in NetworkFolder and any sub folders
  1213  
  1214  	if dswitch == nil {
  1215  		log.Printf("DVS %s cannot be found", id)
  1216  		return new(types.NotFound)
  1217  	}
  1218  
  1219  	h := ctx.Map.Get(*vm.Runtime.Host).(*HostSystem)
  1220  	c := hostParent(ctx, &h.HostSystem)
  1221  	isMember := func(val types.ManagedObjectReference) bool {
  1222  		for _, mem := range dswitch.Summary.HostMember {
  1223  			if mem == val {
  1224  				return true
  1225  			}
  1226  		}
  1227  		log.Printf("%s is not a member of VDS %s", h.Name, dswitch.Name)
  1228  		return false
  1229  	}
  1230  
  1231  	for _, ref := range c.Host {
  1232  		if !isMember(ref) {
  1233  			return &types.InvalidArgument{InvalidProperty: "spec.deviceChange.device.port.switchUuid"}
  1234  		}
  1235  	}
  1236  
  1237  	return nil
  1238  }
  1239  
  1240  func (vm *VirtualMachine) configureDevice(
  1241  	ctx *Context,
  1242  	devices object.VirtualDeviceList,
  1243  	spec *types.VirtualDeviceConfigSpec,
  1244  	oldDevice types.BaseVirtualDevice) types.BaseMethodFault {
  1245  
  1246  	device := spec.Device
  1247  	d := device.GetVirtualDevice()
  1248  	var controller types.BaseVirtualController
  1249  
  1250  	key := d.Key
  1251  	if d.Key <= 0 {
  1252  		// Keys can't be negative; Key 0 is reserved
  1253  		d.Key = devices.NewKey()
  1254  		d.Key *= -1
  1255  	}
  1256  
  1257  	// Update device controller's key reference
  1258  	if key != d.Key {
  1259  		if device := devices.FindByKey(d.ControllerKey); device != nil {
  1260  			if c, ok := device.(types.BaseVirtualController); ok {
  1261  				c := c.GetVirtualController()
  1262  				for i := range c.Device {
  1263  					if c.Device[i] == key {
  1264  						c.Device[i] = d.Key
  1265  						break
  1266  					}
  1267  				}
  1268  			}
  1269  		}
  1270  	}
  1271  
  1272  	// Choose a unique key
  1273  	for {
  1274  		if devices.FindByKey(d.Key) == nil {
  1275  			break
  1276  		}
  1277  		d.Key++
  1278  	}
  1279  
  1280  	label := devices.Name(device)
  1281  	summary := label
  1282  	dc := ctx.Map.getEntityDatacenter(ctx.Map.Get(*vm.Parent).(mo.Entity))
  1283  
  1284  	switch x := device.(type) {
  1285  	case types.BaseVirtualEthernetCard:
  1286  		controller = devices.PickController((*types.VirtualPCIController)(nil))
  1287  		var net types.ManagedObjectReference
  1288  		var name string
  1289  
  1290  		switch b := d.Backing.(type) {
  1291  		case *types.VirtualEthernetCardNetworkBackingInfo:
  1292  			name = b.DeviceName
  1293  			summary = name
  1294  			net = ctx.Map.FindByName(b.DeviceName, dc.Network).Reference()
  1295  			b.Network = &net
  1296  		case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
  1297  			summary = fmt.Sprintf("DVSwitch: %s", b.Port.SwitchUuid)
  1298  			net.Type = "DistributedVirtualPortgroup"
  1299  			net.Value = b.Port.PortgroupKey
  1300  			if err := vm.validateSwitchMembers(ctx, b.Port.SwitchUuid); err != nil {
  1301  				return err
  1302  			}
  1303  		}
  1304  
  1305  		ctx.Update(vm, []types.PropertyChange{
  1306  			{Name: "summary.config.numEthernetCards", Val: vm.Summary.Config.NumEthernetCards + 1},
  1307  			{Name: "network", Val: append(vm.Network, net)},
  1308  		})
  1309  
  1310  		c := x.GetVirtualEthernetCard()
  1311  		if c.MacAddress == "" {
  1312  			if c.UnitNumber == nil {
  1313  				devices.AssignController(device, controller)
  1314  			}
  1315  			c.MacAddress = vm.generateMAC(*c.UnitNumber - 7) // Note 7 == PCI offset
  1316  		}
  1317  
  1318  		vm.Guest.Net = append(vm.Guest.Net, types.GuestNicInfo{
  1319  			Network:        name,
  1320  			IpAddress:      nil,
  1321  			MacAddress:     c.MacAddress,
  1322  			Connected:      true,
  1323  			DeviceConfigId: c.Key,
  1324  		})
  1325  
  1326  		if spec.Operation == types.VirtualDeviceConfigSpecOperationAdd {
  1327  			if c.ResourceAllocation == nil {
  1328  				c.ResourceAllocation = &types.VirtualEthernetCardResourceAllocation{
  1329  					Reservation: types.NewInt64(0),
  1330  					Share: types.SharesInfo{
  1331  						Shares: 50,
  1332  						Level:  "normal",
  1333  					},
  1334  					Limit: types.NewInt64(-1),
  1335  				}
  1336  			}
  1337  		}
  1338  	case *types.VirtualDisk:
  1339  		if oldDevice == nil {
  1340  			// NOTE: either of capacityInBytes and capacityInKB may not be specified
  1341  			x.CapacityInBytes = getDiskSize(x)
  1342  			x.CapacityInKB = getDiskSize(x) / 1024
  1343  		} else {
  1344  			if oldDisk, ok := oldDevice.(*types.VirtualDisk); ok {
  1345  				diskSize, ok := changedDiskSize(oldDisk, x)
  1346  				if !ok {
  1347  					return &types.InvalidDeviceOperation{}
  1348  				}
  1349  				x.CapacityInBytes = diskSize
  1350  				x.CapacityInKB = diskSize / 1024
  1351  			}
  1352  		}
  1353  
  1354  		switch b := d.Backing.(type) {
  1355  		case *types.VirtualDiskSparseVer2BackingInfo:
  1356  			// Sparse disk creation not supported in ESX
  1357  			return &types.DeviceUnsupportedForVmPlatform{
  1358  				InvalidDeviceSpec: types.InvalidDeviceSpec{
  1359  					InvalidVmConfig: types.InvalidVmConfig{Property: "VirtualDeviceSpec.device.backing"},
  1360  				},
  1361  			}
  1362  		case types.BaseVirtualDeviceFileBackingInfo:
  1363  			parent := ""
  1364  
  1365  			switch backing := d.Backing.(type) {
  1366  			case *types.VirtualDiskFlatVer2BackingInfo:
  1367  				if backing.Parent != nil {
  1368  					parent = backing.Parent.FileName
  1369  				}
  1370  			case *types.VirtualDiskSeSparseBackingInfo:
  1371  				if backing.Parent != nil {
  1372  					parent = backing.Parent.FileName
  1373  				}
  1374  			case *types.VirtualDiskSparseVer2BackingInfo:
  1375  				if backing.Parent != nil {
  1376  					parent = backing.Parent.FileName
  1377  				}
  1378  			}
  1379  
  1380  			if parent != "" {
  1381  				desc, _, err := ctx.Map.FileManager().DiskDescriptor(ctx, &dc.Self, parent)
  1382  				if err != nil {
  1383  					return &types.InvalidDeviceSpec{
  1384  						InvalidVmConfig: types.InvalidVmConfig{
  1385  							Property: "virtualDeviceSpec.device.backing.parent.fileName",
  1386  						},
  1387  					}
  1388  				}
  1389  
  1390  				// Disk Capacity is always same as the parent's
  1391  				x.CapacityInBytes = int64(desc.Capacity())
  1392  				x.CapacityInKB = x.CapacityInBytes / 1024
  1393  			}
  1394  
  1395  			summary = fmt.Sprintf("%s KB", numberToString(x.CapacityInKB, ','))
  1396  
  1397  			info := b.GetVirtualDeviceFileBackingInfo()
  1398  			var path object.DatastorePath
  1399  			path.FromString(info.FileName)
  1400  
  1401  			if path.Path == "" {
  1402  				filename, err := vm.genVmdkPath(ctx, path)
  1403  				if err != nil {
  1404  					return err
  1405  				}
  1406  
  1407  				info.FileName = filename
  1408  			}
  1409  
  1410  			err := vdmCreateVirtualDisk(ctx, spec.FileOperation, &types.CreateVirtualDisk_Task{
  1411  				Datacenter: &dc.Self,
  1412  				Name:       info.FileName,
  1413  				Spec:       &types.FileBackedVirtualDiskSpec{CapacityKb: x.CapacityInKB},
  1414  			})
  1415  			if err != nil {
  1416  				return err
  1417  			}
  1418  
  1419  			ctx.Update(vm, []types.PropertyChange{
  1420  				{Name: "summary.config.numVirtualDisks", Val: vm.Summary.Config.NumVirtualDisks + 1},
  1421  			})
  1422  
  1423  			p, _ := parseDatastorePath(info.FileName)
  1424  			ds := vm.findDatastore(ctx, p.Datastore)
  1425  			info.Datastore = &ds.Self
  1426  
  1427  			if oldDevice != nil {
  1428  				if oldDisk, ok := oldDevice.(*types.VirtualDisk); ok {
  1429  					// add previous capacity to datastore freespace
  1430  					ctx.WithLock(ds, func() {
  1431  						ds.Summary.FreeSpace += getDiskSize(oldDisk)
  1432  						ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
  1433  					})
  1434  				}
  1435  			}
  1436  
  1437  			// then subtract new capacity from datastore freespace
  1438  			// XXX: compare disk size and free space until windows stat is supported
  1439  			ctx.WithLock(ds, func() {
  1440  				ds.Summary.FreeSpace -= getDiskSize(x)
  1441  				ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
  1442  			})
  1443  
  1444  			vm.updateDiskLayouts(ctx)
  1445  
  1446  			if disk, ok := b.(*types.VirtualDiskFlatVer2BackingInfo); ok {
  1447  				// These properties default to false
  1448  				props := []**bool{
  1449  					&disk.EagerlyScrub,
  1450  					&disk.ThinProvisioned,
  1451  					&disk.WriteThrough,
  1452  					&disk.Split,
  1453  					&disk.DigestEnabled,
  1454  				}
  1455  				for _, prop := range props {
  1456  					if *prop == nil {
  1457  						*prop = types.NewBool(false)
  1458  					}
  1459  				}
  1460  				disk.Uuid = virtualDiskUUID(&dc.Self, info.FileName)
  1461  			}
  1462  		}
  1463  	case *types.VirtualCdrom:
  1464  		if b, ok := d.Backing.(types.BaseVirtualDeviceFileBackingInfo); ok {
  1465  			summary = "ISO " + b.GetVirtualDeviceFileBackingInfo().FileName
  1466  		}
  1467  	case *types.VirtualFloppy:
  1468  		if b, ok := d.Backing.(types.BaseVirtualDeviceFileBackingInfo); ok {
  1469  			summary = "Image " + b.GetVirtualDeviceFileBackingInfo().FileName
  1470  		}
  1471  	case *types.VirtualSerialPort:
  1472  		switch b := d.Backing.(type) {
  1473  		case types.BaseVirtualDeviceFileBackingInfo:
  1474  			summary = "File " + b.GetVirtualDeviceFileBackingInfo().FileName
  1475  		case *types.VirtualSerialPortURIBackingInfo:
  1476  			summary = "Remote " + b.ServiceURI
  1477  		}
  1478  	}
  1479  
  1480  	if d.UnitNumber == nil && controller != nil {
  1481  		devices.AssignController(device, controller)
  1482  	}
  1483  
  1484  	if d.DeviceInfo == nil {
  1485  		d.DeviceInfo = &types.Description{
  1486  			Label:   label,
  1487  			Summary: summary,
  1488  		}
  1489  	} else {
  1490  		info := d.DeviceInfo.GetDescription()
  1491  		if info.Label == "" {
  1492  			info.Label = label
  1493  		}
  1494  		if info.Summary == "" {
  1495  			info.Summary = summary
  1496  		}
  1497  	}
  1498  
  1499  	switch device.(type) {
  1500  	case types.BaseVirtualEthernetCard, *types.VirtualCdrom, *types.VirtualFloppy, *types.VirtualUSB, *types.VirtualSerialPort:
  1501  		if d.Connectable == nil {
  1502  			d.Connectable = &types.VirtualDeviceConnectInfo{StartConnected: true, Connected: true}
  1503  		}
  1504  	}
  1505  
  1506  	// device can be connected only if vm is powered on
  1507  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  1508  		if d.Connectable != nil {
  1509  			d.Connectable.Connected = false
  1510  		}
  1511  	}
  1512  
  1513  	return nil
  1514  }
  1515  
  1516  func (vm *VirtualMachine) removeDevice(ctx *Context, devices object.VirtualDeviceList, spec *types.VirtualDeviceConfigSpec) object.VirtualDeviceList {
  1517  	key := spec.Device.GetVirtualDevice().Key
  1518  
  1519  	for i, d := range devices {
  1520  		if d.GetVirtualDevice().Key != key {
  1521  			continue
  1522  		}
  1523  
  1524  		devices = append(devices[:i], devices[i+1:]...)
  1525  
  1526  		switch device := spec.Device.(type) {
  1527  		case *types.VirtualDisk:
  1528  			if spec.FileOperation == types.VirtualDeviceConfigSpecFileOperationDestroy {
  1529  				var file string
  1530  
  1531  				switch b := device.Backing.(type) {
  1532  				case types.BaseVirtualDeviceFileBackingInfo:
  1533  					file = b.GetVirtualDeviceFileBackingInfo().FileName
  1534  
  1535  					p, _ := parseDatastorePath(file)
  1536  					ds := vm.findDatastore(ctx, p.Datastore)
  1537  
  1538  					ctx.WithLock(ds, func() {
  1539  						ds.Summary.FreeSpace += getDiskSize(device)
  1540  						ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
  1541  					})
  1542  				}
  1543  
  1544  				if file != "" {
  1545  					dc := ctx.Map.getEntityDatacenter(vm)
  1546  					dm := ctx.Map.VirtualDiskManager()
  1547  					if dc == nil {
  1548  						continue // parent was destroyed
  1549  					}
  1550  					res := dm.DeleteVirtualDiskTask(ctx, &types.DeleteVirtualDisk_Task{
  1551  						Name:       file,
  1552  						Datacenter: &dc.Self,
  1553  					})
  1554  					ctask := ctx.Map.Get(res.(*methods.DeleteVirtualDisk_TaskBody).Res.Returnval).(*Task)
  1555  					ctask.Wait()
  1556  				}
  1557  			}
  1558  			ctx.Update(vm, []types.PropertyChange{
  1559  				{Name: "summary.config.numVirtualDisks", Val: vm.Summary.Config.NumVirtualDisks - 1},
  1560  			})
  1561  
  1562  			vm.RefreshStorageInfo(ctx, nil)
  1563  		case types.BaseVirtualEthernetCard:
  1564  			var net types.ManagedObjectReference
  1565  
  1566  			switch b := device.GetVirtualEthernetCard().Backing.(type) {
  1567  			case *types.VirtualEthernetCardNetworkBackingInfo:
  1568  				net = *b.Network
  1569  			case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
  1570  				net.Type = "DistributedVirtualPortgroup"
  1571  				net.Value = b.Port.PortgroupKey
  1572  			}
  1573  
  1574  			for j, nicInfo := range vm.Guest.Net {
  1575  				if nicInfo.DeviceConfigId == key {
  1576  					vm.Guest.Net = append(vm.Guest.Net[:j], vm.Guest.Net[j+1:]...)
  1577  					break
  1578  				}
  1579  			}
  1580  
  1581  			networks := vm.Network
  1582  			RemoveReference(&networks, net)
  1583  			ctx.Update(vm, []types.PropertyChange{
  1584  				{Name: "summary.config.numEthernetCards", Val: vm.Summary.Config.NumEthernetCards - 1},
  1585  				{Name: "network", Val: networks},
  1586  			})
  1587  		}
  1588  
  1589  		break
  1590  	}
  1591  
  1592  	return devices
  1593  }
  1594  
  1595  func (vm *VirtualMachine) genVmdkPath(ctx *Context, p object.DatastorePath) (string, types.BaseMethodFault) {
  1596  	if p.Datastore == "" {
  1597  		p.FromString(vm.Config.Files.VmPathName)
  1598  	}
  1599  	if p.Path == "" {
  1600  		p.Path = vm.Config.Name
  1601  	} else {
  1602  		p.Path = path.Dir(p.Path)
  1603  	}
  1604  	vmdir := p.String()
  1605  	index := 0
  1606  	for {
  1607  		var filename string
  1608  		if index == 0 {
  1609  			filename = fmt.Sprintf("%s.vmdk", vm.Config.Name)
  1610  		} else {
  1611  			filename = fmt.Sprintf("%s_%d.vmdk", vm.Config.Name, index)
  1612  		}
  1613  
  1614  		f, err := vm.createFile(ctx, vmdir, filename, false)
  1615  		if err != nil {
  1616  			switch err.(type) {
  1617  			case *types.FileAlreadyExists:
  1618  				index++
  1619  				continue
  1620  			default:
  1621  				return "", err
  1622  			}
  1623  		}
  1624  
  1625  		_ = f.Close()
  1626  		_ = os.Remove(f.Name())
  1627  
  1628  		return path.Join(vmdir, filename), nil
  1629  	}
  1630  }
  1631  
  1632  // Encrypt requires powered off VM with no snapshots.
  1633  // Decrypt requires powered off VM.
  1634  // Deep recrypt requires powered off VM with no snapshots.
  1635  // Shallow recrypt works with VMs in any power state and even if snapshots are
  1636  // present as long as it is a single chain and not a tree.
  1637  func (vm *VirtualMachine) updateCrypto(
  1638  	ctx *Context,
  1639  	spec types.BaseCryptoSpec) types.BaseMethodFault {
  1640  
  1641  	const configKeyId = "config.keyId"
  1642  
  1643  	assertEncrypted := func() types.BaseMethodFault {
  1644  		if vm.Config.KeyId == nil {
  1645  			return newInvalidStateFault("vm is not encrypted")
  1646  		}
  1647  		return nil
  1648  	}
  1649  
  1650  	assertPoweredOff := func() types.BaseMethodFault {
  1651  		if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOff {
  1652  			return &types.InvalidPowerState{
  1653  				ExistingState:  vm.Runtime.PowerState,
  1654  				RequestedState: types.VirtualMachinePowerStatePoweredOff,
  1655  			}
  1656  		}
  1657  		return nil
  1658  	}
  1659  
  1660  	assertNoSnapshots := func(allowSingleChain bool) types.BaseMethodFault {
  1661  		hasSnapshots := vm.Snapshot != nil && vm.Snapshot.CurrentSnapshot != nil
  1662  		if !hasSnapshots {
  1663  			return nil
  1664  		}
  1665  		if !allowSingleChain {
  1666  			return newInvalidStateFault("vm has snapshots")
  1667  		}
  1668  		type node = types.VirtualMachineSnapshotTree
  1669  		var isTreeFn func(nodes []node) types.BaseMethodFault
  1670  		isTreeFn = func(nodes []node) types.BaseMethodFault {
  1671  			switch len(nodes) {
  1672  			case 0:
  1673  				return nil
  1674  			case 1:
  1675  				return isTreeFn(nodes[0].ChildSnapshotList)
  1676  			default:
  1677  				return newInvalidStateFault("vm has snapshot tree")
  1678  			}
  1679  		}
  1680  		return isTreeFn(vm.Snapshot.RootSnapshotList)
  1681  	}
  1682  
  1683  	doRecrypt := func(newKeyID types.CryptoKeyId) types.BaseMethodFault {
  1684  		if err := assertEncrypted(); err != nil {
  1685  			return err
  1686  		}
  1687  
  1688  		var providerID *types.KeyProviderId
  1689  		if pid := newKeyID.ProviderId; pid != nil {
  1690  			providerID = &types.KeyProviderId{
  1691  				Id: pid.Id,
  1692  			}
  1693  		}
  1694  
  1695  		keyID := newKeyID.KeyId
  1696  		if providerID == nil {
  1697  			if p, k := getDefaultProvider(ctx, vm, true); p != "" && k != "" {
  1698  				providerID = &types.KeyProviderId{
  1699  					Id: p,
  1700  				}
  1701  				keyID = k
  1702  			}
  1703  		} else if keyID == "" {
  1704  			keyID = generateKeyForProvider(ctx, providerID.Id)
  1705  		}
  1706  
  1707  		ctx.Update(vm, []types.PropertyChange{
  1708  			{
  1709  				Name: configKeyId,
  1710  				Op:   types.PropertyChangeOpAssign,
  1711  				Val: &types.CryptoKeyId{
  1712  					KeyId:      keyID,
  1713  					ProviderId: providerID,
  1714  				},
  1715  			},
  1716  		})
  1717  		return nil
  1718  	}
  1719  
  1720  	switch tspec := spec.(type) {
  1721  	case *types.CryptoSpecDecrypt:
  1722  		if err := assertPoweredOff(); err != nil {
  1723  			return err
  1724  		}
  1725  		if err := assertNoSnapshots(false); err != nil {
  1726  			return err
  1727  		}
  1728  		if err := assertEncrypted(); err != nil {
  1729  			return err
  1730  		}
  1731  		ctx.Update(vm, []types.PropertyChange{
  1732  			{
  1733  				Name: configKeyId,
  1734  				Op:   types.PropertyChangeOpRemove,
  1735  				Val:  nil,
  1736  			},
  1737  		})
  1738  
  1739  	case *types.CryptoSpecDeepRecrypt:
  1740  		if err := assertPoweredOff(); err != nil {
  1741  			return err
  1742  		}
  1743  		if err := assertNoSnapshots(false); err != nil {
  1744  			return err
  1745  		}
  1746  		return doRecrypt(tspec.NewKeyId)
  1747  
  1748  	case *types.CryptoSpecShallowRecrypt:
  1749  		if err := assertNoSnapshots(true); err != nil {
  1750  			return err
  1751  		}
  1752  		return doRecrypt(tspec.NewKeyId)
  1753  
  1754  	case *types.CryptoSpecEncrypt:
  1755  		if err := assertPoweredOff(); err != nil {
  1756  			return err
  1757  		}
  1758  		if err := assertNoSnapshots(false); err != nil {
  1759  			return err
  1760  		}
  1761  		if vm.Config.KeyId != nil {
  1762  			return newInvalidStateFault("vm is already encrypted")
  1763  		}
  1764  
  1765  		var providerID *types.KeyProviderId
  1766  		if pid := tspec.CryptoKeyId.ProviderId; pid != nil {
  1767  			providerID = &types.KeyProviderId{
  1768  				Id: pid.Id,
  1769  			}
  1770  		}
  1771  
  1772  		keyID := tspec.CryptoKeyId.KeyId
  1773  		if providerID == nil {
  1774  			if p, k := getDefaultProvider(ctx, vm, true); p != "" && k != "" {
  1775  				providerID = &types.KeyProviderId{
  1776  					Id: p,
  1777  				}
  1778  				keyID = k
  1779  			}
  1780  		} else if keyID == "" {
  1781  			keyID = generateKeyForProvider(ctx, providerID.Id)
  1782  		}
  1783  
  1784  		ctx.Update(vm, []types.PropertyChange{
  1785  			{
  1786  				Name: configKeyId,
  1787  				Op:   types.PropertyChangeOpAssign,
  1788  				Val: &types.CryptoKeyId{
  1789  					KeyId:      keyID,
  1790  					ProviderId: providerID,
  1791  				},
  1792  			},
  1793  		})
  1794  
  1795  	case *types.CryptoSpecNoOp,
  1796  		*types.CryptoSpecRegister:
  1797  
  1798  		// No-op
  1799  	}
  1800  
  1801  	return nil
  1802  }
  1803  
  1804  func (vm *VirtualMachine) configureDevices(ctx *Context, spec *types.VirtualMachineConfigSpec) types.BaseMethodFault {
  1805  	var changes []types.PropertyChange
  1806  	field := mo.Field{Path: "config.hardware.device"}
  1807  	devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
  1808  
  1809  	var err types.BaseMethodFault
  1810  	for i, change := range spec.DeviceChange {
  1811  		dspec := change.GetVirtualDeviceConfigSpec()
  1812  		device := dspec.Device.GetVirtualDevice()
  1813  		invalid := &types.InvalidDeviceSpec{DeviceIndex: int32(i)}
  1814  		change := types.PropertyChange{}
  1815  
  1816  		switch dspec.FileOperation {
  1817  		case types.VirtualDeviceConfigSpecFileOperationCreate:
  1818  			switch dspec.Device.(type) {
  1819  			case *types.VirtualDisk:
  1820  				if device.UnitNumber == nil {
  1821  					return invalid
  1822  				}
  1823  			}
  1824  		}
  1825  
  1826  		switch dspec.Operation {
  1827  		case types.VirtualDeviceConfigSpecOperationAdd:
  1828  			change.Op = types.PropertyChangeOpAdd
  1829  
  1830  			if devices.FindByKey(device.Key) != nil && device.ControllerKey == 0 {
  1831  				// Note: real ESX does not allow adding base controllers (ControllerKey = 0)
  1832  				// after VM is created (returns success but device is not added).
  1833  				continue
  1834  			} else if device.UnitNumber != nil && devices.SelectByType(dspec.Device).Select(func(d types.BaseVirtualDevice) bool {
  1835  				base := d.GetVirtualDevice()
  1836  				if base.UnitNumber != nil {
  1837  					if base.ControllerKey != device.ControllerKey {
  1838  						return false
  1839  					}
  1840  					return *base.UnitNumber == *device.UnitNumber
  1841  				}
  1842  				return false
  1843  			}) != nil {
  1844  				// UnitNumber for this device type is taken
  1845  				return invalid
  1846  			}
  1847  
  1848  			key := device.Key
  1849  			err = vm.configureDevice(ctx, devices, dspec, nil)
  1850  			if err != nil {
  1851  				return err
  1852  			}
  1853  
  1854  			devices = append(devices, dspec.Device)
  1855  			change.Val = dspec.Device
  1856  			if key != device.Key {
  1857  				// Update ControllerKey refs
  1858  				for i := range spec.DeviceChange {
  1859  					ckey := &spec.DeviceChange[i].GetVirtualDeviceConfigSpec().Device.GetVirtualDevice().ControllerKey
  1860  					if *ckey == key {
  1861  						*ckey = device.Key
  1862  					}
  1863  				}
  1864  			}
  1865  		case types.VirtualDeviceConfigSpecOperationEdit:
  1866  			rspec := *dspec
  1867  			oldDevice := devices.FindByKey(device.Key)
  1868  			if oldDevice == nil {
  1869  				return invalid
  1870  			}
  1871  			rspec.Device = oldDevice
  1872  			devices = vm.removeDevice(ctx, devices, &rspec)
  1873  			if device.DeviceInfo != nil {
  1874  				device.DeviceInfo.GetDescription().Summary = "" // regenerate summary
  1875  			}
  1876  
  1877  			err = vm.configureDevice(ctx, devices, dspec, oldDevice)
  1878  			if err != nil {
  1879  				return err
  1880  			}
  1881  
  1882  			devices = append(devices, dspec.Device)
  1883  			change.Val = dspec.Device
  1884  		case types.VirtualDeviceConfigSpecOperationRemove:
  1885  			change.Op = types.PropertyChangeOpRemove
  1886  
  1887  			devices = vm.removeDevice(ctx, devices, dspec)
  1888  		}
  1889  
  1890  		field.Key = device.Key
  1891  		change.Name = field.String()
  1892  		changes = append(changes, change)
  1893  	}
  1894  
  1895  	if len(changes) != 0 {
  1896  		change := types.PropertyChange{Name: field.Path, Val: []types.BaseVirtualDevice(devices)}
  1897  		ctx.Update(vm, append(changes, change))
  1898  	}
  1899  
  1900  	err = vm.updateDiskLayouts(ctx)
  1901  	if err != nil {
  1902  		return err
  1903  	}
  1904  
  1905  	// Do this after device config, as some may apply to the devices themselves (e.g. ethernet -> guest.net)
  1906  	err = vm.applyExtraConfig(ctx, spec)
  1907  	if err != nil {
  1908  		return err
  1909  	}
  1910  
  1911  	return nil
  1912  }
  1913  
  1914  type powerVMTask struct {
  1915  	*VirtualMachine
  1916  
  1917  	state types.VirtualMachinePowerState
  1918  	ctx   *Context
  1919  }
  1920  
  1921  func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
  1922  	c.logPrintf("running power task: requesting %s, existing %s",
  1923  		c.state, c.VirtualMachine.Runtime.PowerState)
  1924  
  1925  	if c.VirtualMachine.Runtime.PowerState == c.state {
  1926  		return nil, &types.InvalidPowerState{
  1927  			RequestedState: c.state,
  1928  			ExistingState:  c.VirtualMachine.Runtime.PowerState,
  1929  		}
  1930  	}
  1931  
  1932  	var boot types.AnyType
  1933  	if c.state == types.VirtualMachinePowerStatePoweredOn {
  1934  		boot = time.Now()
  1935  	}
  1936  
  1937  	event := c.event(c.ctx)
  1938  	switch c.state {
  1939  	case types.VirtualMachinePowerStatePoweredOn:
  1940  		if c.VirtualMachine.hostInMM(c.ctx) {
  1941  			return nil, new(types.InvalidState)
  1942  		}
  1943  
  1944  		err := c.svm.start(c.ctx)
  1945  		if err != nil {
  1946  			return nil, &types.MissingPowerOnConfiguration{
  1947  				VAppConfigFault: types.VAppConfigFault{
  1948  					VimFault: types.VimFault{
  1949  						MethodFault: types.MethodFault{
  1950  							FaultCause: &types.LocalizedMethodFault{
  1951  								Fault:            &types.SystemErrorFault{Reason: err.Error()},
  1952  								LocalizedMessage: err.Error()}}}}}
  1953  		}
  1954  		c.ctx.postEvent(
  1955  			&types.VmStartingEvent{VmEvent: event},
  1956  			&types.VmPoweredOnEvent{VmEvent: event},
  1957  		)
  1958  		c.customize(c.ctx)
  1959  	case types.VirtualMachinePowerStatePoweredOff:
  1960  		c.svm.stop(c.ctx)
  1961  		c.ctx.postEvent(
  1962  			&types.VmStoppingEvent{VmEvent: event},
  1963  			&types.VmPoweredOffEvent{VmEvent: event},
  1964  		)
  1965  	case types.VirtualMachinePowerStateSuspended:
  1966  		if c.VirtualMachine.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  1967  			return nil, &types.InvalidPowerState{
  1968  				RequestedState: types.VirtualMachinePowerStatePoweredOn,
  1969  				ExistingState:  c.VirtualMachine.Runtime.PowerState,
  1970  			}
  1971  		}
  1972  
  1973  		c.svm.pause(c.ctx)
  1974  		c.ctx.postEvent(
  1975  			&types.VmSuspendingEvent{VmEvent: event},
  1976  			&types.VmSuspendedEvent{VmEvent: event},
  1977  		)
  1978  	}
  1979  
  1980  	// copy devices to prevent data race
  1981  	devices := c.VirtualMachine.cloneDevice()
  1982  	for _, d := range devices {
  1983  		conn := d.GetVirtualDevice().Connectable
  1984  		if conn == nil {
  1985  			continue
  1986  		}
  1987  
  1988  		if c.state == types.VirtualMachinePowerStatePoweredOn {
  1989  			// apply startConnected to current connection
  1990  			conn.Connected = conn.StartConnected
  1991  		} else {
  1992  			conn.Connected = false
  1993  		}
  1994  	}
  1995  
  1996  	c.ctx.Update(c.VirtualMachine, []types.PropertyChange{
  1997  		{Name: "runtime.powerState", Val: c.state},
  1998  		{Name: "summary.runtime.powerState", Val: c.state},
  1999  		{Name: "summary.runtime.bootTime", Val: boot},
  2000  		{Name: "config.hardware.device", Val: devices},
  2001  	})
  2002  
  2003  	return nil, nil
  2004  }
  2005  
  2006  func (vm *VirtualMachine) PowerOnVMTask(ctx *Context, c *types.PowerOnVM_Task) soap.HasFault {
  2007  	if vm.Config.Template {
  2008  		return &methods.PowerOnVM_TaskBody{
  2009  			Fault_: Fault("cannot powerOn a template", &types.InvalidState{}),
  2010  		}
  2011  	}
  2012  
  2013  	runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOn, ctx}
  2014  	task := CreateTask(runner.Reference(), "powerOn", runner.Run)
  2015  
  2016  	return &methods.PowerOnVM_TaskBody{
  2017  		Res: &types.PowerOnVM_TaskResponse{
  2018  			Returnval: task.Run(ctx),
  2019  		},
  2020  	}
  2021  }
  2022  
  2023  func (vm *VirtualMachine) PowerOffVMTask(ctx *Context, c *types.PowerOffVM_Task) soap.HasFault {
  2024  	runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOff, ctx}
  2025  	task := CreateTask(runner.Reference(), "powerOff", runner.Run)
  2026  
  2027  	return &methods.PowerOffVM_TaskBody{
  2028  		Res: &types.PowerOffVM_TaskResponse{
  2029  			Returnval: task.Run(ctx),
  2030  		},
  2031  	}
  2032  }
  2033  
  2034  func (vm *VirtualMachine) SuspendVMTask(ctx *Context, req *types.SuspendVM_Task) soap.HasFault {
  2035  	runner := &powerVMTask{vm, types.VirtualMachinePowerStateSuspended, ctx}
  2036  	task := CreateTask(runner.Reference(), "suspend", runner.Run)
  2037  
  2038  	return &methods.SuspendVM_TaskBody{
  2039  		Res: &types.SuspendVM_TaskResponse{
  2040  			Returnval: task.Run(ctx),
  2041  		},
  2042  	}
  2043  }
  2044  
  2045  func (vm *VirtualMachine) ResetVMTask(ctx *Context, req *types.ResetVM_Task) soap.HasFault {
  2046  	task := CreateTask(vm, "reset", func(task *Task) (types.AnyType, types.BaseMethodFault) {
  2047  		res := vm.PowerOffVMTask(ctx, &types.PowerOffVM_Task{This: vm.Self})
  2048  		ctask := ctx.Map.Get(res.(*methods.PowerOffVM_TaskBody).Res.Returnval).(*Task)
  2049  		ctask.Wait()
  2050  		if ctask.Info.Error != nil {
  2051  			return nil, ctask.Info.Error.Fault
  2052  		}
  2053  
  2054  		res = vm.PowerOnVMTask(ctx, &types.PowerOnVM_Task{This: vm.Self})
  2055  		ctask = ctx.Map.Get(res.(*methods.PowerOnVM_TaskBody).Res.Returnval).(*Task)
  2056  		ctask.Wait()
  2057  
  2058  		return nil, nil
  2059  	})
  2060  
  2061  	return &methods.ResetVM_TaskBody{
  2062  		Res: &types.ResetVM_TaskResponse{
  2063  			Returnval: task.Run(ctx),
  2064  		},
  2065  	}
  2066  }
  2067  
  2068  func (vm *VirtualMachine) RebootGuest(ctx *Context, req *types.RebootGuest) soap.HasFault {
  2069  	body := new(methods.RebootGuestBody)
  2070  
  2071  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  2072  		body.Fault_ = Fault("", &types.InvalidPowerState{
  2073  			RequestedState: types.VirtualMachinePowerStatePoweredOn,
  2074  			ExistingState:  vm.Runtime.PowerState,
  2075  		})
  2076  		return body
  2077  	}
  2078  
  2079  	if vm.Guest.ToolsRunningStatus == string(types.VirtualMachineToolsRunningStatusGuestToolsRunning) {
  2080  		vm.svm.restart(ctx)
  2081  		body.Res = new(types.RebootGuestResponse)
  2082  	} else {
  2083  		body.Fault_ = Fault("", new(types.ToolsUnavailable))
  2084  	}
  2085  
  2086  	return body
  2087  }
  2088  
  2089  func (vm *VirtualMachine) ReconfigVMTask(ctx *Context, req *types.ReconfigVM_Task) soap.HasFault {
  2090  	task := CreateTask(vm, "reconfigVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2091  		ctx.postEvent(&types.VmReconfiguredEvent{
  2092  			VmEvent:    vm.event(ctx),
  2093  			ConfigSpec: req.Spec,
  2094  		})
  2095  
  2096  		if vm.Config.Template {
  2097  			expect := types.VirtualMachineConfigSpec{
  2098  				Name:       req.Spec.Name,
  2099  				Annotation: req.Spec.Annotation,
  2100  			}
  2101  			if !reflect.DeepEqual(&req.Spec, &expect) {
  2102  				log.Printf("template reconfigure only allows name and annotation change")
  2103  				return nil, new(types.NotSupported)
  2104  			}
  2105  		}
  2106  
  2107  		err := vm.configure(ctx, &req.Spec)
  2108  
  2109  		return nil, err
  2110  	})
  2111  
  2112  	return &methods.ReconfigVM_TaskBody{
  2113  		Res: &types.ReconfigVM_TaskResponse{
  2114  			Returnval: task.Run(ctx),
  2115  		},
  2116  	}
  2117  }
  2118  
  2119  func (vm *VirtualMachine) UpgradeVMTask(ctx *Context, req *types.UpgradeVM_Task) soap.HasFault {
  2120  	body := &methods.UpgradeVM_TaskBody{}
  2121  
  2122  	task := CreateTask(vm, "upgradeVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2123  
  2124  		// InvalidPowerState
  2125  		//
  2126  		// 1. Is VM's power state anything other than powered off?
  2127  		if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOff {
  2128  			return nil, &types.InvalidPowerStateFault{
  2129  				ExistingState:  vm.Runtime.PowerState,
  2130  				RequestedState: types.VirtualMachinePowerStatePoweredOff,
  2131  			}
  2132  		}
  2133  
  2134  		// InvalidState
  2135  		//
  2136  		// 1. Is host on which VM is scheduled in maintenance mode?
  2137  		// 2. Is VM a template?
  2138  		// 3. Is VM already the latest hardware version?
  2139  		var (
  2140  			ebRef                     *types.ManagedObjectReference
  2141  			latestHardwareVersion     string
  2142  			hostRef                   = vm.Runtime.Host
  2143  			supportedHardwareVersions = map[string]struct{}{}
  2144  			vmHardwareVersionString   = vm.Config.Version
  2145  		)
  2146  		if hostRef != nil {
  2147  			var hostInMaintenanceMode bool
  2148  			ctx.WithLock(*hostRef, func() {
  2149  				host := ctx.Map.Get(*hostRef).(*HostSystem)
  2150  				hostInMaintenanceMode = host.Runtime.InMaintenanceMode
  2151  				switch host.Parent.Type {
  2152  				case "ClusterComputeResource":
  2153  					obj := ctx.Map.Get(*host.Parent).(*ClusterComputeResource)
  2154  					ebRef = obj.EnvironmentBrowser
  2155  				case "ComputeResource":
  2156  					obj := ctx.Map.Get(*host.Parent).(*mo.ComputeResource)
  2157  					ebRef = obj.EnvironmentBrowser
  2158  				}
  2159  			})
  2160  			if hostInMaintenanceMode {
  2161  				return nil, newInvalidStateFault("%s in maintenance mode", hostRef.Value)
  2162  			}
  2163  		}
  2164  		if vm.Config.Template {
  2165  			return nil, newInvalidStateFault("%s is template", vm.Reference().Value)
  2166  		}
  2167  		if ebRef != nil {
  2168  			ctx.WithLock(*ebRef, func() {
  2169  				eb := ctx.Map.Get(*ebRef).(*EnvironmentBrowser)
  2170  				for i := range eb.QueryConfigOptionDescriptorResponse.Returnval {
  2171  					cod := eb.QueryConfigOptionDescriptorResponse.Returnval[i]
  2172  					for j := range cod.Host {
  2173  						if cod.Host[j].Value == hostRef.Value {
  2174  							supportedHardwareVersions[cod.Key] = struct{}{}
  2175  						}
  2176  						if latestHardwareVersion == "" {
  2177  							if def := cod.DefaultConfigOption; def != nil && *def {
  2178  								latestHardwareVersion = cod.Key
  2179  							}
  2180  						}
  2181  					}
  2182  				}
  2183  			})
  2184  		}
  2185  
  2186  		if latestHardwareVersion == "" {
  2187  			latestHardwareVersion = esx.HardwareVersion
  2188  		}
  2189  		if vmHardwareVersionString == latestHardwareVersion {
  2190  			return nil, newInvalidStateFault("%s is latest version", vm.Reference().Value)
  2191  		}
  2192  		if req.Version == "" {
  2193  			req.Version = latestHardwareVersion
  2194  		}
  2195  
  2196  		// NotSupported
  2197  		targetVersion, _ := types.ParseHardwareVersion(req.Version)
  2198  		if targetVersion.IsValid() {
  2199  			req.Version = targetVersion.String()
  2200  		}
  2201  		if _, ok := supportedHardwareVersions[req.Version]; !ok {
  2202  			msg := fmt.Sprintf("%s not supported", req.Version)
  2203  			return nil, &types.NotSupported{
  2204  				RuntimeFault: types.RuntimeFault{
  2205  					MethodFault: types.MethodFault{
  2206  						FaultCause: &types.LocalizedMethodFault{
  2207  							Fault: &types.SystemErrorFault{
  2208  								Reason: msg,
  2209  							},
  2210  							LocalizedMessage: msg,
  2211  						},
  2212  					},
  2213  				},
  2214  			}
  2215  		}
  2216  
  2217  		// AlreadyUpgraded
  2218  		vmHardwareVersion, _ := types.ParseHardwareVersion(vmHardwareVersionString)
  2219  		if targetVersion.IsValid() && vmHardwareVersion.IsValid() &&
  2220  			targetVersion <= vmHardwareVersion {
  2221  
  2222  			return nil, &types.AlreadyUpgradedFault{}
  2223  		}
  2224  
  2225  		// InvalidArgument
  2226  		if targetVersion < types.VMX3 {
  2227  			return nil, &types.InvalidArgument{}
  2228  		}
  2229  
  2230  		ctx.Update(vm, []types.PropertyChange{
  2231  			{
  2232  				Name: "config.version", Val: targetVersion.String(),
  2233  			},
  2234  			{
  2235  				Name: "summary.config.hwVersion", Val: targetVersion.String(),
  2236  			},
  2237  		})
  2238  
  2239  		return nil, nil
  2240  	})
  2241  
  2242  	body.Res = &types.UpgradeVM_TaskResponse{
  2243  		Returnval: task.Run(ctx),
  2244  	}
  2245  
  2246  	return body
  2247  }
  2248  
  2249  func (vm *VirtualMachine) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
  2250  	dc := ctx.Map.getEntityDatacenter(vm)
  2251  
  2252  	task := CreateTask(vm, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2253  		if dc == nil {
  2254  			return nil, &types.ManagedObjectNotFound{Obj: vm.Self} // If our Parent was destroyed, so were we.
  2255  			// TODO: should this also trigger container removal?
  2256  		}
  2257  
  2258  		r := vm.UnregisterVM(ctx, &types.UnregisterVM{
  2259  			This: req.This,
  2260  		})
  2261  
  2262  		if r.Fault() != nil {
  2263  			return nil, r.Fault().VimFault().(types.BaseMethodFault)
  2264  		}
  2265  
  2266  		// Remove all devices
  2267  		devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
  2268  		spec, _ := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationRemove)
  2269  		vm.configureDevices(ctx, &types.VirtualMachineConfigSpec{DeviceChange: spec})
  2270  
  2271  		// Delete VM files from the datastore (ignoring result for now)
  2272  		m := ctx.Map.FileManager()
  2273  
  2274  		_ = m.DeleteDatastoreFileTask(ctx, &types.DeleteDatastoreFile_Task{
  2275  			This:       m.Reference(),
  2276  			Name:       vm.Config.Files.LogDirectory,
  2277  			Datacenter: &dc.Self,
  2278  		})
  2279  
  2280  		err := vm.svm.remove(ctx)
  2281  		if err != nil {
  2282  			return nil, &types.RuntimeFault{
  2283  				MethodFault: types.MethodFault{
  2284  					FaultCause: &types.LocalizedMethodFault{
  2285  						Fault:            &types.SystemErrorFault{Reason: err.Error()},
  2286  						LocalizedMessage: err.Error()}}}
  2287  		}
  2288  
  2289  		return nil, nil
  2290  	})
  2291  
  2292  	return &methods.Destroy_TaskBody{
  2293  		Res: &types.Destroy_TaskResponse{
  2294  			Returnval: task.Run(ctx),
  2295  		},
  2296  	}
  2297  }
  2298  
  2299  func (vm *VirtualMachine) SetCustomValue(ctx *Context, req *types.SetCustomValue) soap.HasFault {
  2300  	return SetCustomValue(ctx, req)
  2301  }
  2302  
  2303  func (vm *VirtualMachine) UnregisterVM(ctx *Context, c *types.UnregisterVM) soap.HasFault {
  2304  	r := &methods.UnregisterVMBody{}
  2305  
  2306  	if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
  2307  		r.Fault_ = Fault("", &types.InvalidPowerState{
  2308  			RequestedState: types.VirtualMachinePowerStatePoweredOff,
  2309  			ExistingState:  vm.Runtime.PowerState,
  2310  		})
  2311  
  2312  		return r
  2313  	}
  2314  
  2315  	host := ctx.Map.Get(*vm.Runtime.Host).(*HostSystem)
  2316  	ctx.Map.RemoveReference(ctx, host, &host.Vm, vm.Self)
  2317  
  2318  	if vm.ResourcePool != nil {
  2319  		switch pool := ctx.Map.Get(*vm.ResourcePool).(type) {
  2320  		case *ResourcePool:
  2321  			ctx.Map.RemoveReference(ctx, pool, &pool.Vm, vm.Self)
  2322  		case *VirtualApp:
  2323  			ctx.Map.RemoveReference(ctx, pool, &pool.Vm, vm.Self)
  2324  		}
  2325  	}
  2326  
  2327  	for i := range vm.Datastore {
  2328  		ds := ctx.Map.Get(vm.Datastore[i]).(*Datastore)
  2329  		ctx.Map.RemoveReference(ctx, ds, &ds.Vm, vm.Self)
  2330  	}
  2331  
  2332  	ctx.postEvent(&types.VmRemovedEvent{VmEvent: vm.event(ctx)})
  2333  	if f, ok := asFolderMO(ctx.Map.getEntityParent(vm, "Folder")); ok {
  2334  		folderRemoveChild(ctx, f, c.This)
  2335  	}
  2336  
  2337  	r.Res = new(types.UnregisterVMResponse)
  2338  
  2339  	return r
  2340  }
  2341  
  2342  type vmFolder interface {
  2343  	CreateVMTask(ctx *Context, c *types.CreateVM_Task) soap.HasFault
  2344  }
  2345  
  2346  func (vm *VirtualMachine) cloneDevice() []types.BaseVirtualDevice {
  2347  	src := types.ArrayOfVirtualDevice{
  2348  		VirtualDevice: vm.Config.Hardware.Device,
  2349  	}
  2350  	dst := types.ArrayOfVirtualDevice{}
  2351  	deepCopy(src, &dst)
  2352  	return dst.VirtualDevice
  2353  }
  2354  
  2355  func (vm *VirtualMachine) worldID() int {
  2356  	return int(binary.BigEndian.Uint32(vm.uid[0:4]))
  2357  }
  2358  
  2359  func (vm *VirtualMachine) CloneVMTask(ctx *Context, req *types.CloneVM_Task) soap.HasFault {
  2360  	pool := req.Spec.Location.Pool
  2361  	if pool == nil {
  2362  		if !vm.Config.Template {
  2363  			pool = vm.ResourcePool
  2364  		}
  2365  	}
  2366  
  2367  	destHost := vm.Runtime.Host
  2368  
  2369  	if req.Spec.Location.Host != nil {
  2370  		destHost = req.Spec.Location.Host
  2371  	}
  2372  
  2373  	folder, _ := asFolderMO(ctx.Map.Get(req.Folder))
  2374  	host := ctx.Map.Get(*destHost).(*HostSystem)
  2375  	event := vm.event(ctx)
  2376  
  2377  	ctx.postEvent(&types.VmBeingClonedEvent{
  2378  		VmCloneEvent: types.VmCloneEvent{
  2379  			VmEvent: event,
  2380  		},
  2381  		DestFolder: folderEventArgument(folder),
  2382  		DestName:   req.Name,
  2383  		DestHost:   *host.eventArgument(),
  2384  	})
  2385  
  2386  	vmx := vm.vmx(nil)
  2387  	vmx.Path = req.Name
  2388  	if ref := req.Spec.Location.Datastore; ref != nil {
  2389  		ds := ctx.Map.Get(*ref).(*Datastore).Name
  2390  		vmx.Datastore = ds
  2391  	}
  2392  
  2393  	task := CreateTask(vm, "cloneVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2394  		if pool == nil {
  2395  			return nil, &types.InvalidArgument{InvalidProperty: "spec.location.pool"}
  2396  		}
  2397  		if obj := ctx.Map.FindByName(req.Name, folder.ChildEntity); obj != nil {
  2398  			return nil, &types.DuplicateName{
  2399  				Name:   req.Name,
  2400  				Object: obj.Reference(),
  2401  			}
  2402  		}
  2403  		config := types.VirtualMachineConfigSpec{
  2404  			Name:    req.Name,
  2405  			Version: vm.Config.Version,
  2406  			GuestId: vm.Config.GuestId,
  2407  			Files: &types.VirtualMachineFileInfo{
  2408  				VmPathName: vmx.String(),
  2409  			},
  2410  		}
  2411  
  2412  		// Copying hardware properties
  2413  		config.NumCPUs = vm.Config.Hardware.NumCPU
  2414  		config.MemoryMB = int64(vm.Config.Hardware.MemoryMB)
  2415  		config.NumCoresPerSocket = vm.Config.Hardware.NumCoresPerSocket
  2416  		config.VirtualICH7MPresent = vm.Config.Hardware.VirtualICH7MPresent
  2417  		config.VirtualSMCPresent = vm.Config.Hardware.VirtualSMCPresent
  2418  
  2419  		defaultDevices := object.VirtualDeviceList(esx.VirtualDevice)
  2420  		devices := vm.cloneDevice()
  2421  
  2422  		for _, device := range devices {
  2423  			var fop types.VirtualDeviceConfigSpecFileOperation
  2424  
  2425  			if defaultDevices.Find(object.VirtualDeviceList(devices).Name(device)) != nil {
  2426  				// Default devices are added during CreateVMTask
  2427  				continue
  2428  			}
  2429  
  2430  			switch disk := device.(type) {
  2431  			case *types.VirtualDisk:
  2432  				// TODO: consider VirtualMachineCloneSpec.DiskMoveType
  2433  				fop = types.VirtualDeviceConfigSpecFileOperationCreate
  2434  
  2435  				// Leave FileName empty so CreateVM will just create a new one under VmPathName
  2436  				disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).FileName = ""
  2437  				disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).Parent = nil
  2438  			}
  2439  
  2440  			config.DeviceChange = append(config.DeviceChange, &types.VirtualDeviceConfigSpec{
  2441  				Operation:     types.VirtualDeviceConfigSpecOperationAdd,
  2442  				Device:        device,
  2443  				FileOperation: fop,
  2444  			})
  2445  		}
  2446  
  2447  		if dst, src := config, req.Spec.Config; src != nil {
  2448  			dst.ExtraConfig = src.ExtraConfig
  2449  			copyNonEmptyValue(&dst.Uuid, &src.Uuid)
  2450  			copyNonEmptyValue(&dst.InstanceUuid, &src.InstanceUuid)
  2451  			copyNonEmptyValue(&dst.NumCPUs, &src.NumCPUs)
  2452  			copyNonEmptyValue(&dst.MemoryMB, &src.MemoryMB)
  2453  		}
  2454  
  2455  		res := ctx.Map.Get(req.Folder).(vmFolder).CreateVMTask(ctx, &types.CreateVM_Task{
  2456  			This:   folder.Self,
  2457  			Config: config,
  2458  			Pool:   *pool,
  2459  			Host:   destHost,
  2460  		})
  2461  
  2462  		ctask := ctx.Map.Get(res.(*methods.CreateVM_TaskBody).Res.Returnval).(*Task)
  2463  		ctask.Wait()
  2464  		if ctask.Info.Error != nil {
  2465  			return nil, ctask.Info.Error.Fault
  2466  		}
  2467  
  2468  		ref := ctask.Info.Result.(types.ManagedObjectReference)
  2469  		clone := ctx.Map.Get(ref).(*VirtualMachine)
  2470  		clone.configureDevices(ctx, &types.VirtualMachineConfigSpec{DeviceChange: req.Spec.Location.DeviceChange})
  2471  		if req.Spec.Config != nil && req.Spec.Config.DeviceChange != nil {
  2472  			clone.configureDevices(ctx, &types.VirtualMachineConfigSpec{DeviceChange: req.Spec.Config.DeviceChange})
  2473  		}
  2474  		clone.DataSets = copyDataSetsForVmClone(vm.DataSets)
  2475  
  2476  		if req.Spec.Template {
  2477  			_ = clone.MarkAsTemplate(&types.MarkAsTemplate{This: clone.Self})
  2478  		}
  2479  
  2480  		ctx.postEvent(&types.VmClonedEvent{
  2481  			VmCloneEvent: types.VmCloneEvent{VmEvent: clone.event(ctx)},
  2482  			SourceVm:     *event.Vm,
  2483  		})
  2484  
  2485  		return ref, nil
  2486  	})
  2487  
  2488  	return &methods.CloneVM_TaskBody{
  2489  		Res: &types.CloneVM_TaskResponse{
  2490  			Returnval: task.Run(ctx),
  2491  		},
  2492  	}
  2493  }
  2494  
  2495  func copyNonEmptyValue[T comparable](dst, src *T) {
  2496  	if dst == nil || src == nil {
  2497  		return
  2498  	}
  2499  	var t T
  2500  	if *src == t {
  2501  		return
  2502  	}
  2503  	*dst = *src
  2504  }
  2505  
  2506  func (vm *VirtualMachine) RelocateVMTask(ctx *Context, req *types.RelocateVM_Task) soap.HasFault {
  2507  	task := CreateTask(vm, "relocateVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2508  		var changes []types.PropertyChange
  2509  
  2510  		if ref := req.Spec.Datastore; ref != nil {
  2511  			ds := ctx.Map.Get(*ref).(*Datastore)
  2512  			ctx.Map.RemoveReference(ctx, ds, &ds.Vm, *ref)
  2513  
  2514  			// TODO: migrate vm.Config.Files, vm.Summary.Config.VmPathName, vm.Layout and vm.LayoutEx
  2515  
  2516  			changes = append(changes, types.PropertyChange{Name: "datastore", Val: []types.ManagedObjectReference{*ref}})
  2517  		}
  2518  
  2519  		if ref := req.Spec.Pool; ref != nil {
  2520  			pool := ctx.Map.Get(*ref).(*ResourcePool)
  2521  			ctx.Map.RemoveReference(ctx, pool, &pool.Vm, *ref)
  2522  
  2523  			changes = append(changes, types.PropertyChange{Name: "resourcePool", Val: ref})
  2524  		}
  2525  
  2526  		if ref := req.Spec.Host; ref != nil {
  2527  			host := ctx.Map.Get(*ref).(*HostSystem)
  2528  			ctx.Map.RemoveReference(ctx, host, &host.Vm, *ref)
  2529  
  2530  			changes = append(changes,
  2531  				types.PropertyChange{Name: "runtime.host", Val: ref},
  2532  				types.PropertyChange{Name: "summary.runtime.host", Val: ref},
  2533  			)
  2534  		}
  2535  
  2536  		if ref := req.Spec.Folder; ref != nil {
  2537  			folder := ctx.Map.Get(*ref).(*Folder)
  2538  			ctx.WithLock(folder, func() {
  2539  				res := folder.MoveIntoFolderTask(ctx, &types.MoveIntoFolder_Task{
  2540  					List: []types.ManagedObjectReference{vm.Self},
  2541  				}).(*methods.MoveIntoFolder_TaskBody).Res
  2542  				// Wait for task to complete while we hold the Folder lock
  2543  				ctx.Map.Get(res.Returnval).(*Task).Wait()
  2544  			})
  2545  		}
  2546  
  2547  		cspec := &types.VirtualMachineConfigSpec{DeviceChange: req.Spec.DeviceChange}
  2548  		if err := vm.configureDevices(ctx, cspec); err != nil {
  2549  			return nil, err
  2550  		}
  2551  
  2552  		ctx.postEvent(&types.VmMigratedEvent{
  2553  			VmEvent:          vm.event(ctx),
  2554  			SourceHost:       *ctx.Map.Get(*vm.Runtime.Host).(*HostSystem).eventArgument(),
  2555  			SourceDatacenter: datacenterEventArgument(ctx, vm),
  2556  			SourceDatastore:  ctx.Map.Get(vm.Datastore[0]).(*Datastore).eventArgument(),
  2557  		})
  2558  
  2559  		ctx.Update(vm, changes)
  2560  
  2561  		return nil, nil
  2562  	})
  2563  
  2564  	return &methods.RelocateVM_TaskBody{
  2565  		Res: &types.RelocateVM_TaskResponse{
  2566  			Returnval: task.Run(ctx),
  2567  		},
  2568  	}
  2569  }
  2570  
  2571  func (vm *VirtualMachine) customize(ctx *Context) {
  2572  	if vm.imc == nil {
  2573  		return
  2574  	}
  2575  
  2576  	event := types.CustomizationEvent{VmEvent: vm.event(ctx)}
  2577  	ctx.postEvent(&types.CustomizationStartedEvent{CustomizationEvent: event})
  2578  
  2579  	changes := []types.PropertyChange{
  2580  		{Name: "config.tools.pendingCustomization", Val: ""},
  2581  	}
  2582  
  2583  	hostname := ""
  2584  	address := ""
  2585  
  2586  	switch c := vm.imc.Identity.(type) {
  2587  	case *types.CustomizationLinuxPrep:
  2588  		hostname = customizeName(vm, c.HostName)
  2589  	case *types.CustomizationSysprep:
  2590  		hostname = customizeName(vm, c.UserData.ComputerName)
  2591  	}
  2592  
  2593  	cards := object.VirtualDeviceList(vm.Config.Hardware.Device).SelectByType((*types.VirtualEthernetCard)(nil))
  2594  
  2595  	for i, s := range vm.imc.NicSettingMap {
  2596  		nic := &vm.Guest.Net[i]
  2597  		if s.MacAddress != "" {
  2598  			nic.MacAddress = strings.ToLower(s.MacAddress) // MacAddress in guest will always be lowercase
  2599  			card := cards[i].(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
  2600  			card.MacAddress = s.MacAddress // MacAddress in Virtual NIC can be any case
  2601  			card.AddressType = string(types.VirtualEthernetCardMacTypeManual)
  2602  		}
  2603  		if nic.DnsConfig == nil {
  2604  			nic.DnsConfig = new(types.NetDnsConfigInfo)
  2605  		}
  2606  		if s.Adapter.DnsDomain != "" {
  2607  			nic.DnsConfig.DomainName = s.Adapter.DnsDomain
  2608  		}
  2609  		if len(s.Adapter.DnsServerList) != 0 {
  2610  			nic.DnsConfig.IpAddress = s.Adapter.DnsServerList
  2611  		}
  2612  		if hostname != "" {
  2613  			nic.DnsConfig.HostName = hostname
  2614  		}
  2615  		if len(vm.imc.GlobalIPSettings.DnsSuffixList) != 0 {
  2616  			nic.DnsConfig.SearchDomain = vm.imc.GlobalIPSettings.DnsSuffixList
  2617  		}
  2618  		if nic.IpConfig == nil {
  2619  			nic.IpConfig = new(types.NetIpConfigInfo)
  2620  		}
  2621  
  2622  		switch ip := s.Adapter.Ip.(type) {
  2623  		case *types.CustomizationCustomIpGenerator:
  2624  		case *types.CustomizationDhcpIpGenerator:
  2625  		case *types.CustomizationFixedIp:
  2626  			if address == "" {
  2627  				address = ip.IpAddress
  2628  			}
  2629  			nic.IpAddress = []string{ip.IpAddress}
  2630  			nic.IpConfig.IpAddress = []types.NetIpConfigInfoIpAddress{{
  2631  				IpAddress: ip.IpAddress,
  2632  			}}
  2633  		case *types.CustomizationUnknownIpGenerator:
  2634  		}
  2635  	}
  2636  
  2637  	if len(vm.imc.NicSettingMap) != 0 {
  2638  		changes = append(changes, types.PropertyChange{Name: "guest.net", Val: vm.Guest.Net})
  2639  	}
  2640  	if hostname != "" {
  2641  		changes = append(changes, types.PropertyChange{Name: "guest.hostName", Val: hostname})
  2642  		changes = append(changes, types.PropertyChange{Name: "summary.guest.hostName", Val: hostname})
  2643  	}
  2644  	if address != "" {
  2645  		changes = append(changes, types.PropertyChange{Name: "guest.ipAddress", Val: address})
  2646  		changes = append(changes, types.PropertyChange{Name: "summary.guest.ipAddress", Val: address})
  2647  	}
  2648  
  2649  	vm.imc = nil
  2650  	ctx.Update(vm, changes)
  2651  	ctx.postEvent(&types.CustomizationSucceeded{CustomizationEvent: event})
  2652  }
  2653  
  2654  func (vm *VirtualMachine) CustomizeVMTask(ctx *Context, req *types.CustomizeVM_Task) soap.HasFault {
  2655  	task := CreateTask(vm, "customizeVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2656  		if vm.hostInMM(ctx) {
  2657  			return nil, new(types.InvalidState)
  2658  		}
  2659  
  2660  		if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
  2661  			return nil, &types.InvalidPowerState{
  2662  				RequestedState: types.VirtualMachinePowerStatePoweredOff,
  2663  				ExistingState:  vm.Runtime.PowerState,
  2664  			}
  2665  		}
  2666  		if vm.Config.Tools.PendingCustomization != "" {
  2667  			return nil, new(types.CustomizationPending)
  2668  		}
  2669  		if len(vm.Guest.Net) != len(req.Spec.NicSettingMap) {
  2670  			return nil, &types.NicSettingMismatch{
  2671  				NumberOfNicsInSpec: int32(len(req.Spec.NicSettingMap)),
  2672  				NumberOfNicsInVM:   int32(len(vm.Guest.Net)),
  2673  			}
  2674  		}
  2675  
  2676  		vm.imc = &req.Spec
  2677  		vm.Config.Tools.PendingCustomization = uuid.New().String()
  2678  
  2679  		return nil, nil
  2680  	})
  2681  
  2682  	return &methods.CustomizeVM_TaskBody{
  2683  		Res: &types.CustomizeVM_TaskResponse{
  2684  			Returnval: task.Run(ctx),
  2685  		},
  2686  	}
  2687  }
  2688  
  2689  func (vm *VirtualMachine) CreateSnapshotTask(ctx *Context, req *types.CreateSnapshot_Task) soap.HasFault {
  2690  	body := &methods.CreateSnapshot_TaskBody{}
  2691  
  2692  	r := &types.CreateSnapshotEx_Task{
  2693  		Name:        req.Name,
  2694  		Description: req.Description,
  2695  		Memory:      req.Memory,
  2696  	}
  2697  
  2698  	if req.Quiesce {
  2699  		r.QuiesceSpec = &types.VirtualMachineGuestQuiesceSpec{}
  2700  	}
  2701  
  2702  	res := vm.CreateSnapshotExTask(ctx, r)
  2703  
  2704  	if res.Fault() != nil {
  2705  		body.Fault_ = res.Fault()
  2706  	} else {
  2707  		body.Res = &types.CreateSnapshot_TaskResponse{
  2708  			Returnval: res.(*methods.CreateSnapshotEx_TaskBody).Res.Returnval,
  2709  		}
  2710  	}
  2711  
  2712  	return body
  2713  }
  2714  
  2715  func (vm *VirtualMachine) CreateSnapshotExTask(ctx *Context, req *types.CreateSnapshotEx_Task) soap.HasFault {
  2716  	task := CreateTask(vm, "createSnapshotEx", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2717  		var changes []types.PropertyChange
  2718  
  2719  		if vm.Snapshot == nil {
  2720  			vm.Snapshot = &types.VirtualMachineSnapshotInfo{}
  2721  		}
  2722  
  2723  		snapshot := &VirtualMachineSnapshot{}
  2724  		snapshot.Vm = vm.Reference()
  2725  		snapshot.Config = *vm.Config
  2726  		snapshot.DataSets = copyDataSetsForVmClone(vm.DataSets)
  2727  
  2728  		ctx.Map.Put(snapshot)
  2729  
  2730  		quiesced := false
  2731  		if req.QuiesceSpec != nil {
  2732  			quiesced = true
  2733  		}
  2734  
  2735  		treeItem := types.VirtualMachineSnapshotTree{
  2736  			Snapshot:        snapshot.Self,
  2737  			Vm:              snapshot.Vm,
  2738  			Name:            req.Name,
  2739  			Description:     req.Description,
  2740  			Id:              atomic.AddInt32(&vm.sid, 1),
  2741  			CreateTime:      time.Now(),
  2742  			State:           vm.Runtime.PowerState,
  2743  			Quiesced:        quiesced,
  2744  			BackupManifest:  "",
  2745  			ReplaySupported: types.NewBool(false),
  2746  		}
  2747  
  2748  		cur := vm.Snapshot.CurrentSnapshot
  2749  		if cur != nil {
  2750  			parent := ctx.Map.Get(*cur).(*VirtualMachineSnapshot)
  2751  			parent.ChildSnapshot = append(parent.ChildSnapshot, snapshot.Self)
  2752  
  2753  			ss := findSnapshotInTree(vm.Snapshot.RootSnapshotList, *cur)
  2754  			ss.ChildSnapshotList = append(ss.ChildSnapshotList, treeItem)
  2755  		} else {
  2756  			changes = append(changes, types.PropertyChange{
  2757  				Name: "snapshot.rootSnapshotList",
  2758  				Val:  append(vm.Snapshot.RootSnapshotList, treeItem),
  2759  			})
  2760  			changes = append(changes, types.PropertyChange{
  2761  				Name: "rootSnapshot",
  2762  				Val:  append(vm.RootSnapshot, treeItem.Snapshot),
  2763  			})
  2764  		}
  2765  
  2766  		snapshot.createSnapshotFiles(ctx)
  2767  
  2768  		changes = append(changes, types.PropertyChange{Name: "snapshot.currentSnapshot", Val: snapshot.Self})
  2769  		ctx.Update(vm, changes)
  2770  
  2771  		return snapshot.Self, nil
  2772  	})
  2773  
  2774  	return &methods.CreateSnapshotEx_TaskBody{
  2775  		Res: &types.CreateSnapshotEx_TaskResponse{
  2776  			Returnval: task.Run(ctx),
  2777  		},
  2778  	}
  2779  }
  2780  
  2781  func (vm *VirtualMachine) RevertToCurrentSnapshotTask(ctx *Context, req *types.RevertToCurrentSnapshot_Task) soap.HasFault {
  2782  	body := &methods.RevertToCurrentSnapshot_TaskBody{}
  2783  
  2784  	if vm.Snapshot == nil || vm.Snapshot.CurrentSnapshot == nil {
  2785  		body.Fault_ = Fault("snapshot not found", &types.NotFound{})
  2786  
  2787  		return body
  2788  	}
  2789  	snapshot := ctx.Map.Get(*vm.Snapshot.CurrentSnapshot).(*VirtualMachineSnapshot)
  2790  
  2791  	task := CreateTask(vm, "revertSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2792  		vm.DataSets = copyDataSetsForVmClone(snapshot.DataSets)
  2793  		return nil, nil
  2794  	})
  2795  
  2796  	body.Res = &types.RevertToCurrentSnapshot_TaskResponse{
  2797  		Returnval: task.Run(ctx),
  2798  	}
  2799  
  2800  	return body
  2801  }
  2802  
  2803  func (vm *VirtualMachine) RemoveAllSnapshotsTask(ctx *Context, req *types.RemoveAllSnapshots_Task) soap.HasFault {
  2804  	task := CreateTask(vm, "RemoveAllSnapshots", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2805  		if vm.Snapshot == nil {
  2806  			return nil, nil
  2807  		}
  2808  
  2809  		refs := allSnapshotsInTree(vm.Snapshot.RootSnapshotList)
  2810  
  2811  		ctx.Update(vm, []types.PropertyChange{
  2812  			{Name: "snapshot", Val: nil},
  2813  			{Name: "rootSnapshot", Val: nil},
  2814  		})
  2815  
  2816  		for _, ref := range refs {
  2817  			ctx.Map.Get(ref).(*VirtualMachineSnapshot).removeSnapshotFiles(ctx)
  2818  			ctx.Map.Remove(ctx, ref)
  2819  		}
  2820  
  2821  		return nil, nil
  2822  	})
  2823  
  2824  	return &methods.RemoveAllSnapshots_TaskBody{
  2825  		Res: &types.RemoveAllSnapshots_TaskResponse{
  2826  			Returnval: task.Run(ctx),
  2827  		},
  2828  	}
  2829  }
  2830  
  2831  func (vm *VirtualMachine) fcd(ctx *Context, ds types.ManagedObjectReference, id types.ID) *VStorageObject {
  2832  	m := ctx.Map.VStorageObjectManager()
  2833  	if ds.Value != "" {
  2834  		return m.objects[ds][id]
  2835  	}
  2836  	for _, set := range m.objects {
  2837  		for key, val := range set {
  2838  			if key == id {
  2839  				return val
  2840  			}
  2841  		}
  2842  	}
  2843  	return nil
  2844  }
  2845  
  2846  func (vm *VirtualMachine) AttachDiskTask(ctx *Context, req *types.AttachDisk_Task) soap.HasFault {
  2847  	task := CreateTask(vm, "attachDisk", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2848  		fcd := vm.fcd(ctx, req.Datastore, req.DiskId)
  2849  		if fcd == nil {
  2850  			return nil, new(types.InvalidArgument)
  2851  		}
  2852  
  2853  		fcd.Config.ConsumerId = []types.ID{{Id: vm.Config.Uuid}}
  2854  
  2855  		// TODO: add device
  2856  
  2857  		return nil, nil
  2858  	})
  2859  
  2860  	return &methods.AttachDisk_TaskBody{
  2861  		Res: &types.AttachDisk_TaskResponse{
  2862  			Returnval: task.Run(ctx),
  2863  		},
  2864  	}
  2865  }
  2866  
  2867  func (vm *VirtualMachine) DetachDiskTask(ctx *Context, req *types.DetachDisk_Task) soap.HasFault {
  2868  	task := CreateTask(vm, "detachDisk", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2869  		fcd := vm.fcd(ctx, types.ManagedObjectReference{}, req.DiskId)
  2870  		if fcd == nil {
  2871  			return nil, new(types.InvalidArgument)
  2872  		}
  2873  
  2874  		fcd.Config.ConsumerId = nil
  2875  
  2876  		// TODO: remove device
  2877  
  2878  		return nil, nil
  2879  	})
  2880  
  2881  	return &methods.DetachDisk_TaskBody{
  2882  		Res: &types.DetachDisk_TaskResponse{
  2883  			Returnval: task.Run(ctx),
  2884  		},
  2885  	}
  2886  }
  2887  
  2888  func (vm *VirtualMachine) PromoteDisksTask(ctx *Context, req *types.PromoteDisks_Task) soap.HasFault {
  2889  	task := CreateTask(vm, "promoteDisks", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2890  		devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
  2891  		devices = devices.SelectByType((*types.VirtualDisk)(nil))
  2892  		var cap int64
  2893  
  2894  		for i := range req.Disks {
  2895  			d := devices.FindByKey(req.Disks[i].Key)
  2896  			if d == nil {
  2897  				return nil, &types.InvalidArgument{InvalidProperty: "disks"}
  2898  			}
  2899  
  2900  			disk := d.(*types.VirtualDisk)
  2901  
  2902  			switch backing := disk.Backing.(type) {
  2903  			case *types.VirtualDiskFlatVer2BackingInfo:
  2904  				if backing.Parent != nil {
  2905  					cap += disk.CapacityInBytes
  2906  					if req.Unlink {
  2907  						backing.Parent = nil
  2908  					}
  2909  				}
  2910  			case *types.VirtualDiskSeSparseBackingInfo:
  2911  				if backing.Parent != nil {
  2912  					cap += disk.CapacityInBytes
  2913  					if req.Unlink {
  2914  						backing.Parent = nil
  2915  					}
  2916  				}
  2917  			case *types.VirtualDiskSparseVer2BackingInfo:
  2918  				if backing.Parent != nil {
  2919  					cap += disk.CapacityInBytes
  2920  					if req.Unlink {
  2921  						backing.Parent = nil
  2922  					}
  2923  				}
  2924  			}
  2925  		}
  2926  
  2927  		// Built-in default delay. `simulator.TaskDelay` can be used to add additional time
  2928  		// Translates to roughly 1s per 1GB
  2929  		sleep := time.Duration(cap/units.MB) * time.Millisecond
  2930  		if sleep > 0 {
  2931  			log.Printf("%s: sleep %s for %s", t.Info.DescriptionId, sleep, units.ByteSize(cap))
  2932  			time.Sleep(sleep)
  2933  		}
  2934  
  2935  		return nil, nil
  2936  	})
  2937  
  2938  	return &methods.PromoteDisks_TaskBody{
  2939  		Res: &types.PromoteDisks_TaskResponse{
  2940  			Returnval: task.Run(ctx),
  2941  		},
  2942  	}
  2943  }
  2944  
  2945  func (vm *VirtualMachine) ShutdownGuest(ctx *Context, c *types.ShutdownGuest) soap.HasFault {
  2946  	r := &methods.ShutdownGuestBody{}
  2947  
  2948  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  2949  		r.Fault_ = Fault("", &types.InvalidPowerState{
  2950  			RequestedState: types.VirtualMachinePowerStatePoweredOn,
  2951  			ExistingState:  vm.Runtime.PowerState,
  2952  		})
  2953  
  2954  		return r
  2955  	}
  2956  
  2957  	event := vm.event(ctx)
  2958  	ctx.postEvent(&types.VmGuestShutdownEvent{VmEvent: event})
  2959  
  2960  	_ = CreateTask(vm, "shutdownGuest", func(*Task) (types.AnyType, types.BaseMethodFault) {
  2961  		vm.svm.stop(ctx)
  2962  
  2963  		ctx.Update(vm, []types.PropertyChange{
  2964  			{Name: "runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
  2965  			{Name: "summary.runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
  2966  		})
  2967  
  2968  		ctx.postEvent(&types.VmPoweredOffEvent{VmEvent: event})
  2969  
  2970  		return nil, nil
  2971  	}).Run(ctx)
  2972  
  2973  	r.Res = new(types.ShutdownGuestResponse)
  2974  
  2975  	return r
  2976  }
  2977  
  2978  func (vm *VirtualMachine) StandbyGuest(ctx *Context, c *types.StandbyGuest) soap.HasFault {
  2979  	r := &methods.StandbyGuestBody{}
  2980  
  2981  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  2982  		r.Fault_ = Fault("", &types.InvalidPowerState{
  2983  			RequestedState: types.VirtualMachinePowerStatePoweredOn,
  2984  			ExistingState:  vm.Runtime.PowerState,
  2985  		})
  2986  
  2987  		return r
  2988  	}
  2989  
  2990  	event := vm.event(ctx)
  2991  	ctx.postEvent(&types.VmGuestStandbyEvent{VmEvent: event})
  2992  
  2993  	_ = CreateTask(vm, "standbyGuest", func(*Task) (types.AnyType, types.BaseMethodFault) {
  2994  		vm.svm.pause(ctx)
  2995  
  2996  		ctx.Update(vm, []types.PropertyChange{
  2997  			{Name: "runtime.powerState", Val: types.VirtualMachinePowerStateSuspended},
  2998  			{Name: "summary.runtime.powerState", Val: types.VirtualMachinePowerStateSuspended},
  2999  		})
  3000  
  3001  		ctx.postEvent(&types.VmSuspendedEvent{VmEvent: event})
  3002  
  3003  		return nil, nil
  3004  	}).Run(ctx)
  3005  
  3006  	r.Res = new(types.StandbyGuestResponse)
  3007  
  3008  	return r
  3009  }
  3010  
  3011  func (vm *VirtualMachine) MarkAsTemplate(req *types.MarkAsTemplate) soap.HasFault {
  3012  	r := &methods.MarkAsTemplateBody{}
  3013  
  3014  	if vm.Config.Template {
  3015  		r.Fault_ = Fault("", new(types.NotSupported))
  3016  		return r
  3017  	}
  3018  
  3019  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOff {
  3020  		r.Fault_ = Fault("", &types.InvalidPowerState{
  3021  			RequestedState: types.VirtualMachinePowerStatePoweredOff,
  3022  			ExistingState:  vm.Runtime.PowerState,
  3023  		})
  3024  		return r
  3025  	}
  3026  
  3027  	vm.Config.Template = true
  3028  	vm.Summary.Config.Template = true
  3029  	vm.ResourcePool = nil
  3030  
  3031  	r.Res = new(types.MarkAsTemplateResponse)
  3032  
  3033  	return r
  3034  }
  3035  
  3036  func (vm *VirtualMachine) MarkAsVirtualMachine(req *types.MarkAsVirtualMachine) soap.HasFault {
  3037  	r := &methods.MarkAsVirtualMachineBody{}
  3038  
  3039  	if !vm.Config.Template {
  3040  		r.Fault_ = Fault("", new(types.NotSupported))
  3041  		return r
  3042  	}
  3043  
  3044  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOff {
  3045  		r.Fault_ = Fault("", &types.InvalidPowerState{
  3046  			RequestedState: types.VirtualMachinePowerStatePoweredOff,
  3047  			ExistingState:  vm.Runtime.PowerState,
  3048  		})
  3049  		return r
  3050  	}
  3051  
  3052  	vm.Config.Template = false
  3053  	vm.Summary.Config.Template = false
  3054  	vm.ResourcePool = &req.Pool
  3055  	if req.Host != nil {
  3056  		vm.Runtime.Host = req.Host
  3057  	}
  3058  
  3059  	r.Res = new(types.MarkAsVirtualMachineResponse)
  3060  
  3061  	return r
  3062  }
  3063  
  3064  func findSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.VirtualMachineSnapshotTree {
  3065  	if tree == nil {
  3066  		return nil
  3067  	}
  3068  
  3069  	for i, ss := range tree {
  3070  		if ss.Snapshot == ref {
  3071  			return &tree[i]
  3072  		}
  3073  
  3074  		target := findSnapshotInTree(ss.ChildSnapshotList, ref)
  3075  		if target != nil {
  3076  			return target
  3077  		}
  3078  	}
  3079  
  3080  	return nil
  3081  }
  3082  
  3083  func findParentSnapshot(tree types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.ManagedObjectReference {
  3084  	for _, ss := range tree.ChildSnapshotList {
  3085  		if ss.Snapshot == ref {
  3086  			return &tree.Snapshot
  3087  		}
  3088  
  3089  		res := findParentSnapshot(ss, ref)
  3090  		if res != nil {
  3091  			return res
  3092  		}
  3093  	}
  3094  
  3095  	return nil
  3096  }
  3097  
  3098  func findParentSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.ManagedObjectReference {
  3099  	if tree == nil {
  3100  		return nil
  3101  	}
  3102  
  3103  	for _, ss := range tree {
  3104  		res := findParentSnapshot(ss, ref)
  3105  		if res != nil {
  3106  			return res
  3107  		}
  3108  	}
  3109  
  3110  	return nil
  3111  }
  3112  
  3113  func removeSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference, removeChildren bool) []types.VirtualMachineSnapshotTree {
  3114  	if tree == nil {
  3115  		return tree
  3116  	}
  3117  
  3118  	var result []types.VirtualMachineSnapshotTree
  3119  
  3120  	for _, ss := range tree {
  3121  		if ss.Snapshot == ref {
  3122  			if !removeChildren {
  3123  				result = append(result, ss.ChildSnapshotList...)
  3124  			}
  3125  		} else {
  3126  			ss.ChildSnapshotList = removeSnapshotInTree(ss.ChildSnapshotList, ref, removeChildren)
  3127  			result = append(result, ss)
  3128  		}
  3129  	}
  3130  
  3131  	return result
  3132  }
  3133  
  3134  func allSnapshotsInTree(tree []types.VirtualMachineSnapshotTree) []types.ManagedObjectReference {
  3135  	var result []types.ManagedObjectReference
  3136  
  3137  	if tree == nil {
  3138  		return result
  3139  	}
  3140  
  3141  	for _, ss := range tree {
  3142  		result = append(result, ss.Snapshot)
  3143  		result = append(result, allSnapshotsInTree(ss.ChildSnapshotList)...)
  3144  	}
  3145  
  3146  	return result
  3147  }
  3148  
  3149  func changeTrackingSupported(spec *types.VirtualMachineConfigSpec) bool {
  3150  	for _, device := range spec.DeviceChange {
  3151  		if dev, ok := device.GetVirtualDeviceConfigSpec().Device.(*types.VirtualDisk); ok {
  3152  			switch dev.Backing.(type) {
  3153  			case *types.VirtualDiskFlatVer2BackingInfo:
  3154  				return true
  3155  			case *types.VirtualDiskSparseVer2BackingInfo:
  3156  				return true
  3157  			case *types.VirtualDiskRawDiskMappingVer1BackingInfo:
  3158  				return true
  3159  			case *types.VirtualDiskRawDiskVer2BackingInfo:
  3160  				return true
  3161  			default:
  3162  				return false
  3163  			}
  3164  		}
  3165  	}
  3166  	return false
  3167  }
  3168  
  3169  func (vm *VirtualMachine) updateLastModifiedAndChangeVersion(ctx *Context) {
  3170  	modified := time.Now()
  3171  	ctx.Update(vm, []types.PropertyChange{
  3172  		{
  3173  			Name: "config.changeVersion",
  3174  			Val:  fmt.Sprintf("%d", modified.UnixNano()),
  3175  			Op:   types.PropertyChangeOpAssign,
  3176  		},
  3177  		{
  3178  			Name: "config.modified",
  3179  			Val:  modified,
  3180  			Op:   types.PropertyChangeOpAssign,
  3181  		},
  3182  	})
  3183  }