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

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