github.com/vmware/govmomi@v0.37.1/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  	var removedContainerBacking bool
   433  	var changes []types.PropertyChange
   434  	for _, c := range spec.ExtraConfig {
   435  		val := c.GetOptionValue()
   436  		key := strings.TrimPrefix(extraConfigKey(val.Key), "SET.")
   437  		if key == val.Key {
   438  			keyIndex := -1
   439  			for i := range vm.Config.ExtraConfig {
   440  				bov := vm.Config.ExtraConfig[i]
   441  				if bov == nil {
   442  					continue
   443  				}
   444  				ov := bov.GetOptionValue()
   445  				if ov == nil {
   446  					continue
   447  				}
   448  				if ov.Key == key {
   449  					keyIndex = i
   450  					break
   451  				}
   452  			}
   453  			if keyIndex < 0 {
   454  				vm.Config.ExtraConfig = append(vm.Config.ExtraConfig, c)
   455  			} else {
   456  				if s, ok := val.Value.(string); ok && s == "" {
   457  					if key == ContainerBackingOptionKey {
   458  						removedContainerBacking = true
   459  					}
   460  					// Remove existing element
   461  					l := len(vm.Config.ExtraConfig)
   462  					vm.Config.ExtraConfig[keyIndex] = vm.Config.ExtraConfig[l-1]
   463  					vm.Config.ExtraConfig[l-1] = nil
   464  					vm.Config.ExtraConfig = vm.Config.ExtraConfig[:l-1]
   465  				} else {
   466  					// Update existing element
   467  					vm.Config.ExtraConfig[keyIndex].GetOptionValue().Value = val.Value
   468  				}
   469  			}
   470  
   471  			continue
   472  		}
   473  		changes = append(changes, types.PropertyChange{Name: key, Val: val.Value})
   474  
   475  		switch key {
   476  		case "guest.ipAddress":
   477  			if len(vm.Guest.Net) > 0 {
   478  				ip := val.Value.(string)
   479  				vm.Guest.Net[0].IpAddress = []string{ip}
   480  				changes = append(changes,
   481  					types.PropertyChange{Name: "summary." + key, Val: ip},
   482  					types.PropertyChange{Name: "guest.net", Val: vm.Guest.Net},
   483  				)
   484  			}
   485  		case "guest.hostName":
   486  			changes = append(changes,
   487  				types.PropertyChange{Name: "summary." + key, Val: val.Value},
   488  			)
   489  		}
   490  	}
   491  
   492  	// create the container backing before we publish the updates so the simVM is available before handlers
   493  	// get triggered
   494  	var fault types.BaseMethodFault
   495  	if vm.svm == nil {
   496  		vm.svm = createSimulationVM(vm)
   497  
   498  		// check to see if the VM is already powered on - if so we need to retroactively hit that path here
   499  		if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
   500  			err := vm.svm.start(ctx)
   501  			if err != nil {
   502  				// don't attempt to undo the changes already made - just return an error
   503  				// we'll retry the svm.start operation on pause/restart calls
   504  				fault = &types.VAppConfigFault{
   505  					VimFault: types.VimFault{
   506  						MethodFault: types.MethodFault{
   507  							FaultCause: &types.LocalizedMethodFault{
   508  								Fault:            &types.SystemErrorFault{Reason: err.Error()},
   509  								LocalizedMessage: err.Error()}}}}
   510  			}
   511  		}
   512  	} else if removedContainerBacking {
   513  		err := vm.svm.remove(ctx)
   514  		if err == nil {
   515  			// remove link from container to VM so callbacks no longer reflect state
   516  			vm.svm.vm = nil
   517  			// nil container backing reference to return this to a pure in-mem simulated VM
   518  			vm.svm = nil
   519  
   520  		} else {
   521  			// don't attempt to undo the changes already made - just return an error
   522  			// we'll retry the svm.start operation on pause/restart calls
   523  			fault = &types.VAppConfigFault{
   524  				VimFault: types.VimFault{
   525  					MethodFault: types.MethodFault{
   526  						FaultCause: &types.LocalizedMethodFault{
   527  							Fault:            &types.SystemErrorFault{Reason: err.Error()},
   528  							LocalizedMessage: err.Error()}}}}
   529  		}
   530  	}
   531  
   532  	if len(changes) != 0 {
   533  		Map.Update(vm, changes)
   534  	}
   535  
   536  	return fault
   537  }
   538  
   539  func validateGuestID(id string) types.BaseMethodFault {
   540  	for _, x := range GuestID {
   541  		if id == string(x) {
   542  			return nil
   543  		}
   544  	}
   545  
   546  	return &types.InvalidArgument{InvalidProperty: "configSpec.guestId"}
   547  }
   548  
   549  func (vm *VirtualMachine) configure(ctx *Context, spec *types.VirtualMachineConfigSpec) (result types.BaseMethodFault) {
   550  	defer func() {
   551  		if result == nil {
   552  			vm.updateLastModifiedAndChangeVersion(ctx)
   553  		}
   554  	}()
   555  
   556  	vm.apply(spec)
   557  
   558  	if spec.MemoryAllocation != nil {
   559  		if err := updateResourceAllocation("memory", spec.MemoryAllocation, vm.Config.MemoryAllocation); err != nil {
   560  			return err
   561  		}
   562  	}
   563  
   564  	if spec.CpuAllocation != nil {
   565  		if err := updateResourceAllocation("cpu", spec.CpuAllocation, vm.Config.CpuAllocation); err != nil {
   566  			return err
   567  		}
   568  	}
   569  
   570  	if spec.GuestId != "" {
   571  		if err := validateGuestID(spec.GuestId); err != nil {
   572  			return err
   573  		}
   574  	}
   575  
   576  	if o := spec.BootOptions; o != nil {
   577  		if isTrue(o.EfiSecureBootEnabled) && vm.Config.Firmware != string(types.GuestOsDescriptorFirmwareTypeEfi) {
   578  			return &types.InvalidVmConfig{Property: "msg.hostd.configSpec.efi"}
   579  		}
   580  	}
   581  
   582  	if spec.VAppConfig != nil {
   583  		if err := vm.updateVAppProperty(spec.VAppConfig.GetVmConfigSpec()); err != nil {
   584  			return err
   585  		}
   586  	}
   587  
   588  	return vm.configureDevices(ctx, spec)
   589  }
   590  
   591  func getVMFileType(fileName string) types.VirtualMachineFileLayoutExFileType {
   592  	var fileType types.VirtualMachineFileLayoutExFileType
   593  
   594  	fileExt := path.Ext(fileName)
   595  	fileNameNoExt := strings.TrimSuffix(fileName, fileExt)
   596  
   597  	switch fileExt {
   598  	case ".vmx":
   599  		fileType = types.VirtualMachineFileLayoutExFileTypeConfig
   600  	case ".core":
   601  		fileType = types.VirtualMachineFileLayoutExFileTypeCore
   602  	case ".vmdk":
   603  		fileType = types.VirtualMachineFileLayoutExFileTypeDiskDescriptor
   604  		if strings.HasSuffix(fileNameNoExt, "-digest") {
   605  			fileType = types.VirtualMachineFileLayoutExFileTypeDigestDescriptor
   606  		}
   607  
   608  		extentSuffixes := []string{"-flat", "-delta", "-s", "-rdm", "-rdmp"}
   609  		for _, suffix := range extentSuffixes {
   610  			if strings.HasSuffix(fileNameNoExt, suffix) {
   611  				fileType = types.VirtualMachineFileLayoutExFileTypeDiskExtent
   612  			} else if strings.HasSuffix(fileNameNoExt, "-digest"+suffix) {
   613  				fileType = types.VirtualMachineFileLayoutExFileTypeDigestExtent
   614  			}
   615  		}
   616  	case ".psf":
   617  		fileType = types.VirtualMachineFileLayoutExFileTypeDiskReplicationState
   618  	case ".vmxf":
   619  		fileType = types.VirtualMachineFileLayoutExFileTypeExtendedConfig
   620  	case ".vmft":
   621  		fileType = types.VirtualMachineFileLayoutExFileTypeFtMetadata
   622  	case ".log":
   623  		fileType = types.VirtualMachineFileLayoutExFileTypeLog
   624  	case ".nvram":
   625  		fileType = types.VirtualMachineFileLayoutExFileTypeNvram
   626  	case ".png", ".bmp":
   627  		fileType = types.VirtualMachineFileLayoutExFileTypeScreenshot
   628  	case ".vmsn":
   629  		fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotData
   630  	case ".vmsd":
   631  		fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotList
   632  	case ".xml":
   633  		if strings.HasSuffix(fileNameNoExt, "-aux") {
   634  			fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotManifestList
   635  		}
   636  	case ".stat":
   637  		fileType = types.VirtualMachineFileLayoutExFileTypeStat
   638  	case ".vmss":
   639  		fileType = types.VirtualMachineFileLayoutExFileTypeSuspend
   640  	case ".vmem":
   641  		if strings.Contains(fileNameNoExt, "Snapshot") {
   642  			fileType = types.VirtualMachineFileLayoutExFileTypeSnapshotMemory
   643  		} else {
   644  			fileType = types.VirtualMachineFileLayoutExFileTypeSuspendMemory
   645  		}
   646  	case ".vswp":
   647  		if strings.HasPrefix(fileNameNoExt, "vmx-") {
   648  			fileType = types.VirtualMachineFileLayoutExFileTypeUwswap
   649  		} else {
   650  			fileType = types.VirtualMachineFileLayoutExFileTypeSwap
   651  		}
   652  	case "":
   653  		if strings.HasPrefix(fileNameNoExt, "imcf-") {
   654  			fileType = types.VirtualMachineFileLayoutExFileTypeGuestCustomization
   655  		}
   656  	}
   657  
   658  	return fileType
   659  }
   660  
   661  func (vm *VirtualMachine) addFileLayoutEx(datastorePath object.DatastorePath, fileSize int64) int32 {
   662  	var newKey int32
   663  	for _, layoutFile := range vm.LayoutEx.File {
   664  		if layoutFile.Name == datastorePath.String() {
   665  			return layoutFile.Key
   666  		}
   667  
   668  		if layoutFile.Key >= newKey {
   669  			newKey = layoutFile.Key + 1
   670  		}
   671  	}
   672  
   673  	fileType := getVMFileType(filepath.Base(datastorePath.Path))
   674  
   675  	switch fileType {
   676  	case types.VirtualMachineFileLayoutExFileTypeNvram, types.VirtualMachineFileLayoutExFileTypeSnapshotList:
   677  		vm.addConfigLayout(datastorePath.Path)
   678  	case types.VirtualMachineFileLayoutExFileTypeLog:
   679  		vm.addLogLayout(datastorePath.Path)
   680  	case types.VirtualMachineFileLayoutExFileTypeSwap:
   681  		vm.addSwapLayout(datastorePath.String())
   682  	}
   683  
   684  	vm.LayoutEx.File = append(vm.LayoutEx.File, types.VirtualMachineFileLayoutExFileInfo{
   685  		Accessible:      types.NewBool(true),
   686  		BackingObjectId: "",
   687  		Key:             newKey,
   688  		Name:            datastorePath.String(),
   689  		Size:            fileSize,
   690  		Type:            string(fileType),
   691  		UniqueSize:      fileSize,
   692  	})
   693  
   694  	vm.LayoutEx.Timestamp = time.Now()
   695  
   696  	vm.updateStorage()
   697  
   698  	return newKey
   699  }
   700  
   701  func (vm *VirtualMachine) addConfigLayout(name string) {
   702  	for _, config := range vm.Layout.ConfigFile {
   703  		if config == name {
   704  			return
   705  		}
   706  	}
   707  
   708  	vm.Layout.ConfigFile = append(vm.Layout.ConfigFile, name)
   709  
   710  	vm.updateStorage()
   711  }
   712  
   713  func (vm *VirtualMachine) addLogLayout(name string) {
   714  	for _, log := range vm.Layout.LogFile {
   715  		if log == name {
   716  			return
   717  		}
   718  	}
   719  
   720  	vm.Layout.LogFile = append(vm.Layout.LogFile, name)
   721  
   722  	vm.updateStorage()
   723  }
   724  
   725  func (vm *VirtualMachine) addSwapLayout(name string) {
   726  	vm.Layout.SwapFile = name
   727  
   728  	vm.updateStorage()
   729  }
   730  
   731  func (vm *VirtualMachine) addSnapshotLayout(snapshot types.ManagedObjectReference, dataKey int32) {
   732  	for _, snapshotLayout := range vm.Layout.Snapshot {
   733  		if snapshotLayout.Key == snapshot {
   734  			return
   735  		}
   736  	}
   737  
   738  	var snapshotFiles []string
   739  	for _, file := range vm.LayoutEx.File {
   740  		if file.Key == dataKey || file.Type == "diskDescriptor" {
   741  			snapshotFiles = append(snapshotFiles, file.Name)
   742  		}
   743  	}
   744  
   745  	vm.Layout.Snapshot = append(vm.Layout.Snapshot, types.VirtualMachineFileLayoutSnapshotLayout{
   746  		Key:          snapshot,
   747  		SnapshotFile: snapshotFiles,
   748  	})
   749  
   750  	vm.updateStorage()
   751  }
   752  
   753  func (vm *VirtualMachine) addSnapshotLayoutEx(snapshot types.ManagedObjectReference, dataKey int32, memoryKey int32) {
   754  	for _, snapshotLayoutEx := range vm.LayoutEx.Snapshot {
   755  		if snapshotLayoutEx.Key == snapshot {
   756  			return
   757  		}
   758  	}
   759  
   760  	vm.LayoutEx.Snapshot = append(vm.LayoutEx.Snapshot, types.VirtualMachineFileLayoutExSnapshotLayout{
   761  		DataKey:   dataKey,
   762  		Disk:      vm.LayoutEx.Disk,
   763  		Key:       snapshot,
   764  		MemoryKey: memoryKey,
   765  	})
   766  
   767  	vm.LayoutEx.Timestamp = time.Now()
   768  
   769  	vm.updateStorage()
   770  }
   771  
   772  // Updates both vm.Layout.Disk and vm.LayoutEx.Disk
   773  func (vm *VirtualMachine) updateDiskLayouts() types.BaseMethodFault {
   774  	var disksLayout []types.VirtualMachineFileLayoutDiskLayout
   775  	var disksLayoutEx []types.VirtualMachineFileLayoutExDiskLayout
   776  
   777  	disks := object.VirtualDeviceList(vm.Config.Hardware.Device).SelectByType((*types.VirtualDisk)(nil))
   778  	for _, disk := range disks {
   779  		disk := disk.(*types.VirtualDisk)
   780  		diskBacking := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
   781  
   782  		diskLayout := &types.VirtualMachineFileLayoutDiskLayout{Key: disk.Key}
   783  		diskLayoutEx := &types.VirtualMachineFileLayoutExDiskLayout{Key: disk.Key}
   784  
   785  		// Iterate through disk and its parents
   786  		for {
   787  			dFileName := diskBacking.GetVirtualDeviceFileBackingInfo().FileName
   788  
   789  			var fileKeys []int32
   790  
   791  			// Add disk descriptor and extent files
   792  			for _, diskName := range vdmNames(dFileName) {
   793  				// get full path including datastore location
   794  				p, fault := parseDatastorePath(diskName)
   795  				if fault != nil {
   796  					return fault
   797  				}
   798  
   799  				datastore := vm.useDatastore(p.Datastore)
   800  				dFilePath := path.Join(datastore.Info.GetDatastoreInfo().Url, p.Path)
   801  
   802  				var fileSize int64
   803  				// If file can not be opened - fileSize will be 0
   804  				if dFileInfo, err := os.Stat(dFilePath); err == nil {
   805  					fileSize = dFileInfo.Size()
   806  				}
   807  
   808  				diskKey := vm.addFileLayoutEx(*p, fileSize)
   809  				fileKeys = append(fileKeys, diskKey)
   810  			}
   811  
   812  			diskLayout.DiskFile = append(diskLayout.DiskFile, dFileName)
   813  			diskLayoutEx.Chain = append(diskLayoutEx.Chain, types.VirtualMachineFileLayoutExDiskUnit{
   814  				FileKey: fileKeys,
   815  			})
   816  
   817  			if parent := diskBacking.Parent; parent != nil {
   818  				diskBacking = parent
   819  			} else {
   820  				break
   821  			}
   822  		}
   823  
   824  		disksLayout = append(disksLayout, *diskLayout)
   825  		disksLayoutEx = append(disksLayoutEx, *diskLayoutEx)
   826  	}
   827  
   828  	vm.Layout.Disk = disksLayout
   829  
   830  	vm.LayoutEx.Disk = disksLayoutEx
   831  	vm.LayoutEx.Timestamp = time.Now()
   832  
   833  	vm.updateStorage()
   834  
   835  	return nil
   836  }
   837  
   838  func (vm *VirtualMachine) updateStorage() types.BaseMethodFault {
   839  	// Committed - sum of Size for each file in vm.LayoutEx.File
   840  	// Unshared  - sum of Size for each disk (.vmdk) in vm.LayoutEx.File
   841  	// Uncommitted - disk capacity minus disk usage (only currently used disk)
   842  	var datastoresUsage []types.VirtualMachineUsageOnDatastore
   843  
   844  	disks := object.VirtualDeviceList(vm.Config.Hardware.Device).SelectByType((*types.VirtualDisk)(nil))
   845  
   846  	for _, file := range vm.LayoutEx.File {
   847  		p, fault := parseDatastorePath(file.Name)
   848  		if fault != nil {
   849  			return fault
   850  		}
   851  
   852  		datastore := vm.useDatastore(p.Datastore)
   853  		dsUsage := &types.VirtualMachineUsageOnDatastore{
   854  			Datastore: datastore.Self,
   855  		}
   856  
   857  		for idx, usage := range datastoresUsage {
   858  			if usage.Datastore == datastore.Self {
   859  				datastoresUsage = append(datastoresUsage[:idx], datastoresUsage[idx+1:]...)
   860  				dsUsage = &usage
   861  				break
   862  			}
   863  		}
   864  
   865  		dsUsage.Committed = file.Size
   866  
   867  		if path.Ext(file.Name) == ".vmdk" {
   868  			dsUsage.Unshared = file.Size
   869  		}
   870  
   871  		for _, disk := range disks {
   872  			disk := disk.(*types.VirtualDisk)
   873  			backing := disk.Backing.(types.BaseVirtualDeviceFileBackingInfo).GetVirtualDeviceFileBackingInfo()
   874  
   875  			if backing.FileName == file.Name {
   876  				dsUsage.Uncommitted = disk.CapacityInBytes
   877  			}
   878  		}
   879  
   880  		datastoresUsage = append(datastoresUsage, *dsUsage)
   881  	}
   882  
   883  	vm.Storage.PerDatastoreUsage = datastoresUsage
   884  	vm.Storage.Timestamp = time.Now()
   885  
   886  	storageSummary := &types.VirtualMachineStorageSummary{
   887  		Timestamp: time.Now(),
   888  	}
   889  
   890  	for _, usage := range datastoresUsage {
   891  		storageSummary.Committed += usage.Committed
   892  		storageSummary.Uncommitted += usage.Uncommitted
   893  		storageSummary.Unshared += usage.Unshared
   894  	}
   895  
   896  	vm.Summary.Storage = storageSummary
   897  
   898  	return nil
   899  }
   900  
   901  func (vm *VirtualMachine) RefreshStorageInfo(ctx *Context, req *types.RefreshStorageInfo) soap.HasFault {
   902  	body := new(methods.RefreshStorageInfoBody)
   903  
   904  	if vm.Runtime.Host == nil {
   905  		// VM not fully created
   906  		return body
   907  	}
   908  
   909  	// Validate that all files in vm.LayoutEx.File can still be found
   910  	for idx := len(vm.LayoutEx.File) - 1; idx >= 0; idx-- {
   911  		file := vm.LayoutEx.File[idx]
   912  
   913  		p, fault := parseDatastorePath(file.Name)
   914  		if fault != nil {
   915  			body.Fault_ = Fault("", fault)
   916  			return body
   917  		}
   918  
   919  		if _, err := os.Stat(p.String()); err != nil {
   920  			vm.LayoutEx.File = append(vm.LayoutEx.File[:idx], vm.LayoutEx.File[idx+1:]...)
   921  		}
   922  	}
   923  
   924  	// Directories will be used to locate VM files.
   925  	// Does not include information about virtual disk file locations.
   926  	locations := []string{
   927  		vm.Config.Files.VmPathName,
   928  		vm.Config.Files.SnapshotDirectory,
   929  		vm.Config.Files.LogDirectory,
   930  		vm.Config.Files.SuspendDirectory,
   931  		vm.Config.Files.FtMetadataDirectory,
   932  	}
   933  
   934  	for _, directory := range locations {
   935  		if directory == "" {
   936  			continue
   937  		}
   938  
   939  		p, fault := parseDatastorePath(directory)
   940  		if fault != nil {
   941  			body.Fault_ = Fault("", fault)
   942  			return body
   943  		}
   944  
   945  		datastore := vm.useDatastore(p.Datastore)
   946  		directory := path.Join(datastore.Info.GetDatastoreInfo().Url, p.Path)
   947  
   948  		if path.Ext(p.Path) == ".vmx" {
   949  			directory = path.Dir(directory) // vm.Config.Files.VmPathName can be a directory or full path to .vmx
   950  		}
   951  
   952  		if _, err := os.Stat(directory); err != nil {
   953  			// Can not access the directory
   954  			continue
   955  		}
   956  
   957  		files, err := os.ReadDir(directory)
   958  		if err != nil {
   959  			body.Fault_ = Fault("", ctx.Map.FileManager().fault(directory, err, new(types.CannotAccessFile)))
   960  			return body
   961  		}
   962  
   963  		for _, file := range files {
   964  			datastorePath := object.DatastorePath{
   965  				Datastore: p.Datastore,
   966  				Path:      strings.TrimPrefix(file.Name(), datastore.Info.GetDatastoreInfo().Url),
   967  			}
   968  			info, _ := file.Info()
   969  			vm.addFileLayoutEx(datastorePath, info.Size())
   970  		}
   971  	}
   972  
   973  	fault := vm.updateDiskLayouts()
   974  	if fault != nil {
   975  		body.Fault_ = Fault("", fault)
   976  		return body
   977  	}
   978  
   979  	vm.LayoutEx.Timestamp = time.Now()
   980  
   981  	body.Res = new(types.RefreshStorageInfoResponse)
   982  
   983  	return body
   984  }
   985  
   986  func (vm *VirtualMachine) findDatastore(name string) *Datastore {
   987  	host := Map.Get(*vm.Runtime.Host).(*HostSystem)
   988  
   989  	return Map.FindByName(name, host.Datastore).(*Datastore)
   990  }
   991  
   992  func (vm *VirtualMachine) useDatastore(name string) *Datastore {
   993  	ds := vm.findDatastore(name)
   994  	if FindReference(vm.Datastore, ds.Self) == nil {
   995  		vm.Datastore = append(vm.Datastore, ds.Self)
   996  	}
   997  
   998  	return ds
   999  }
  1000  
  1001  func (vm *VirtualMachine) vmx(spec *types.VirtualMachineConfigSpec) object.DatastorePath {
  1002  	var p object.DatastorePath
  1003  	vmx := vm.Config.Files.VmPathName
  1004  	if spec != nil {
  1005  		vmx = spec.Files.VmPathName
  1006  	}
  1007  	p.FromString(vmx)
  1008  	return p
  1009  }
  1010  
  1011  func (vm *VirtualMachine) createFile(spec string, name string, register bool) (*os.File, types.BaseMethodFault) {
  1012  	p, fault := parseDatastorePath(spec)
  1013  	if fault != nil {
  1014  		return nil, fault
  1015  	}
  1016  
  1017  	ds := vm.useDatastore(p.Datastore)
  1018  
  1019  	nhost := len(ds.Host)
  1020  	if ds.Name == "vsanDatastore" && nhost < 3 {
  1021  		fault := new(types.CannotCreateFile)
  1022  		fault.FaultMessage = []types.LocalizableMessage{
  1023  			{
  1024  				Key:     "vob.vsanprovider.object.creation.failed",
  1025  				Message: "Failed to create object.",
  1026  			},
  1027  			{
  1028  				Key:     "vob.vsan.clomd.needMoreFaultDomains2",
  1029  				Message: fmt.Sprintf("There are currently %d usable fault domains. The operation requires %d more usable fault domains.", nhost, 3-nhost),
  1030  			},
  1031  		}
  1032  		fault.File = p.Path
  1033  		return nil, fault
  1034  	}
  1035  
  1036  	file := path.Join(ds.Info.GetDatastoreInfo().Url, p.Path)
  1037  
  1038  	if name != "" {
  1039  		if path.Ext(p.Path) == ".vmx" {
  1040  			file = path.Dir(file) // vm.Config.Files.VmPathName can be a directory or full path to .vmx
  1041  		}
  1042  
  1043  		file = path.Join(file, name)
  1044  	}
  1045  
  1046  	if register {
  1047  		f, err := os.Open(filepath.Clean(file))
  1048  		if err != nil {
  1049  			log.Printf("register %s: %s", vm.Reference(), err)
  1050  			if os.IsNotExist(err) {
  1051  				return nil, &types.NotFound{}
  1052  			}
  1053  
  1054  			return nil, &types.InvalidArgument{}
  1055  		}
  1056  
  1057  		return f, nil
  1058  	}
  1059  
  1060  	_, err := os.Stat(file)
  1061  	if err == nil {
  1062  		fault := &types.FileAlreadyExists{FileFault: types.FileFault{File: file}}
  1063  		log.Printf("%T: %s", fault, file)
  1064  		return nil, fault
  1065  	}
  1066  
  1067  	// Create parent directory if needed
  1068  	dir := path.Dir(file)
  1069  	_, err = os.Stat(dir)
  1070  	if err != nil {
  1071  		if os.IsNotExist(err) {
  1072  			_ = os.Mkdir(dir, 0700)
  1073  		}
  1074  	}
  1075  
  1076  	f, err := os.Create(file)
  1077  	if err != nil {
  1078  		log.Printf("create(%s): %s", file, err)
  1079  		return nil, &types.FileFault{
  1080  			File: file,
  1081  		}
  1082  	}
  1083  
  1084  	return f, nil
  1085  }
  1086  
  1087  // Rather than keep an fd open for each VM, open/close the log for each messages.
  1088  // This is ok for now as we do not do any heavy VM logging.
  1089  func (vm *VirtualMachine) logPrintf(format string, v ...interface{}) {
  1090  	f, err := os.OpenFile(vm.log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0)
  1091  	if err != nil {
  1092  		log.Println(err)
  1093  		return
  1094  	}
  1095  	log.New(f, "vmx ", log.Flags()).Printf(format, v...)
  1096  	_ = f.Close()
  1097  }
  1098  
  1099  func (vm *VirtualMachine) create(ctx *Context, spec *types.VirtualMachineConfigSpec, register bool) types.BaseMethodFault {
  1100  	vm.apply(spec)
  1101  
  1102  	if spec.Version != "" {
  1103  		v := strings.TrimPrefix(spec.Version, "vmx-")
  1104  		_, err := strconv.Atoi(v)
  1105  		if err != nil {
  1106  			log.Printf("unsupported hardware version: %s", spec.Version)
  1107  			return new(types.NotSupported)
  1108  		}
  1109  	}
  1110  
  1111  	files := []struct {
  1112  		spec string
  1113  		name string
  1114  		use  *string
  1115  	}{
  1116  		{vm.Config.Files.VmPathName, "", nil},
  1117  		{vm.Config.Files.VmPathName, fmt.Sprintf("%s.nvram", vm.Name), nil},
  1118  		{vm.Config.Files.LogDirectory, "vmware.log", &vm.log},
  1119  	}
  1120  
  1121  	for _, file := range files {
  1122  		f, err := vm.createFile(file.spec, file.name, register)
  1123  		if err != nil {
  1124  			return err
  1125  		}
  1126  		if file.use != nil {
  1127  			*file.use = f.Name()
  1128  		}
  1129  		_ = f.Close()
  1130  	}
  1131  
  1132  	vm.logPrintf("created")
  1133  
  1134  	return vm.configureDevices(ctx, spec)
  1135  }
  1136  
  1137  var vmwOUI = net.HardwareAddr([]byte{0x0, 0xc, 0x29})
  1138  
  1139  // From http://pubs.vmware.com/vsphere-60/index.jsp?topic=%2Fcom.vmware.vsphere.networking.doc%2FGUID-DC7478FF-DC44-4625-9AD7-38208C56A552.html
  1140  // "The host generates generateMAC addresses that consists of the VMware OUI 00:0C:29 and the last three octets in hexadecimal
  1141  //
  1142  //	format of the virtual machine UUID.  The virtual machine UUID is based on a hash calculated by using the UUID of the
  1143  //	ESXi physical machine and the path to the configuration file (.vmx) of the virtual machine."
  1144  func (vm *VirtualMachine) generateMAC(unit int32) string {
  1145  	id := []byte(vm.Config.Uuid)
  1146  
  1147  	offset := len(id) - len(vmwOUI)
  1148  	key := id[offset] + byte(unit) // add device unit number, giving each VM NIC a unique MAC
  1149  	id = append([]byte{key}, id[offset+1:]...)
  1150  
  1151  	mac := append(vmwOUI, id...)
  1152  
  1153  	return mac.String()
  1154  }
  1155  
  1156  func numberToString(n int64, sep rune) string {
  1157  	buf := &bytes.Buffer{}
  1158  	if n < 0 {
  1159  		n = -n
  1160  		buf.WriteRune('-')
  1161  	}
  1162  	s := strconv.FormatInt(n, 10)
  1163  	pos := 3 - (len(s) % 3)
  1164  	for i := 0; i < len(s); i++ {
  1165  		if pos == 3 {
  1166  			if i != 0 {
  1167  				buf.WriteRune(sep)
  1168  			}
  1169  			pos = 0
  1170  		}
  1171  		pos++
  1172  		buf.WriteByte(s[i])
  1173  	}
  1174  
  1175  	return buf.String()
  1176  }
  1177  
  1178  func getDiskSize(disk *types.VirtualDisk) int64 {
  1179  	if disk.CapacityInBytes == 0 {
  1180  		return disk.CapacityInKB * 1024
  1181  	}
  1182  	return disk.CapacityInBytes
  1183  }
  1184  
  1185  func changedDiskSize(oldDisk *types.VirtualDisk, newDiskSpec *types.VirtualDisk) (int64, bool) {
  1186  	// capacity cannot be decreased
  1187  	if newDiskSpec.CapacityInBytes < oldDisk.CapacityInBytes || newDiskSpec.CapacityInKB < oldDisk.CapacityInKB {
  1188  		return 0, false
  1189  	}
  1190  
  1191  	// NOTE: capacity is ignored if specified value is same as before
  1192  	if newDiskSpec.CapacityInBytes == oldDisk.CapacityInBytes {
  1193  		return newDiskSpec.CapacityInKB * 1024, true
  1194  	}
  1195  	if newDiskSpec.CapacityInKB == oldDisk.CapacityInKB {
  1196  		return newDiskSpec.CapacityInBytes, true
  1197  	}
  1198  
  1199  	// CapacityInBytes and CapacityInKB indicate different values
  1200  	if newDiskSpec.CapacityInBytes != newDiskSpec.CapacityInKB*1024 {
  1201  		return 0, false
  1202  	}
  1203  	return newDiskSpec.CapacityInBytes, true
  1204  }
  1205  
  1206  func (vm *VirtualMachine) validateSwitchMembers(id string) types.BaseMethodFault {
  1207  	var dswitch *DistributedVirtualSwitch
  1208  
  1209  	var find func(types.ManagedObjectReference)
  1210  	find = func(child types.ManagedObjectReference) {
  1211  		s, ok := Map.Get(child).(*DistributedVirtualSwitch)
  1212  		if ok && s.Uuid == id {
  1213  			dswitch = s
  1214  			return
  1215  		}
  1216  		walk(Map.Get(child), find)
  1217  	}
  1218  	f := Map.getEntityDatacenter(vm).NetworkFolder
  1219  	walk(Map.Get(f), find) // search in NetworkFolder and any sub folders
  1220  
  1221  	if dswitch == nil {
  1222  		log.Printf("DVS %s cannot be found", id)
  1223  		return new(types.NotFound)
  1224  	}
  1225  
  1226  	h := Map.Get(*vm.Runtime.Host).(*HostSystem)
  1227  	c := hostParent(&h.HostSystem)
  1228  	isMember := func(val types.ManagedObjectReference) bool {
  1229  		for _, mem := range dswitch.Summary.HostMember {
  1230  			if mem == val {
  1231  				return true
  1232  			}
  1233  		}
  1234  		log.Printf("%s is not a member of VDS %s", h.Name, dswitch.Name)
  1235  		return false
  1236  	}
  1237  
  1238  	for _, ref := range c.Host {
  1239  		if !isMember(ref) {
  1240  			return &types.InvalidArgument{InvalidProperty: "spec.deviceChange.device.port.switchUuid"}
  1241  		}
  1242  	}
  1243  
  1244  	return nil
  1245  }
  1246  
  1247  func (vm *VirtualMachine) configureDevice(
  1248  	ctx *Context,
  1249  	devices object.VirtualDeviceList,
  1250  	spec *types.VirtualDeviceConfigSpec,
  1251  	oldDevice types.BaseVirtualDevice) types.BaseMethodFault {
  1252  
  1253  	device := spec.Device
  1254  	d := device.GetVirtualDevice()
  1255  	var controller types.BaseVirtualController
  1256  
  1257  	if d.Key <= 0 {
  1258  		// Keys can't be negative; Key 0 is reserved
  1259  		d.Key = devices.NewKey()
  1260  		d.Key *= -1
  1261  	}
  1262  
  1263  	// Choose a unique key
  1264  	for {
  1265  		if devices.FindByKey(d.Key) == nil {
  1266  			break
  1267  		}
  1268  		d.Key++
  1269  	}
  1270  
  1271  	label := devices.Name(device)
  1272  	summary := label
  1273  	dc := ctx.Map.getEntityDatacenter(ctx.Map.Get(*vm.Parent).(mo.Entity))
  1274  
  1275  	switch x := device.(type) {
  1276  	case types.BaseVirtualEthernetCard:
  1277  		controller = devices.PickController((*types.VirtualPCIController)(nil))
  1278  		var net types.ManagedObjectReference
  1279  		var name string
  1280  
  1281  		switch b := d.Backing.(type) {
  1282  		case *types.VirtualEthernetCardNetworkBackingInfo:
  1283  			name = b.DeviceName
  1284  			summary = name
  1285  			net = ctx.Map.FindByName(b.DeviceName, dc.Network).Reference()
  1286  			b.Network = &net
  1287  		case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
  1288  			summary = fmt.Sprintf("DVSwitch: %s", b.Port.SwitchUuid)
  1289  			net.Type = "DistributedVirtualPortgroup"
  1290  			net.Value = b.Port.PortgroupKey
  1291  			if err := vm.validateSwitchMembers(b.Port.SwitchUuid); err != nil {
  1292  				return err
  1293  			}
  1294  		}
  1295  
  1296  		ctx.Map.Update(vm, []types.PropertyChange{
  1297  			{Name: "summary.config.numEthernetCards", Val: vm.Summary.Config.NumEthernetCards + 1},
  1298  			{Name: "network", Val: append(vm.Network, net)},
  1299  		})
  1300  
  1301  		c := x.GetVirtualEthernetCard()
  1302  		if c.MacAddress == "" {
  1303  			if c.UnitNumber == nil {
  1304  				devices.AssignController(device, controller)
  1305  			}
  1306  			c.MacAddress = vm.generateMAC(*c.UnitNumber - 7) // Note 7 == PCI offset
  1307  		}
  1308  
  1309  		vm.Guest.Net = append(vm.Guest.Net, types.GuestNicInfo{
  1310  			Network:        name,
  1311  			IpAddress:      nil,
  1312  			MacAddress:     c.MacAddress,
  1313  			Connected:      true,
  1314  			DeviceConfigId: c.Key,
  1315  		})
  1316  
  1317  		if spec.Operation == types.VirtualDeviceConfigSpecOperationAdd {
  1318  			if c.ResourceAllocation == nil {
  1319  				c.ResourceAllocation = &types.VirtualEthernetCardResourceAllocation{
  1320  					Reservation: types.NewInt64(0),
  1321  					Share: types.SharesInfo{
  1322  						Shares: 50,
  1323  						Level:  "normal",
  1324  					},
  1325  					Limit: types.NewInt64(-1),
  1326  				}
  1327  			}
  1328  		}
  1329  	case *types.VirtualDisk:
  1330  		if oldDevice == nil {
  1331  			// NOTE: either of capacityInBytes and capacityInKB may not be specified
  1332  			x.CapacityInBytes = getDiskSize(x)
  1333  			x.CapacityInKB = getDiskSize(x) / 1024
  1334  		} else {
  1335  			if oldDisk, ok := oldDevice.(*types.VirtualDisk); ok {
  1336  				diskSize, ok := changedDiskSize(oldDisk, x)
  1337  				if !ok {
  1338  					return &types.InvalidDeviceOperation{}
  1339  				}
  1340  				x.CapacityInBytes = diskSize
  1341  				x.CapacityInKB = diskSize / 1024
  1342  			}
  1343  		}
  1344  
  1345  		summary = fmt.Sprintf("%s KB", numberToString(x.CapacityInKB, ','))
  1346  		switch b := d.Backing.(type) {
  1347  		case types.BaseVirtualDeviceFileBackingInfo:
  1348  			info := b.GetVirtualDeviceFileBackingInfo()
  1349  			var path object.DatastorePath
  1350  			path.FromString(info.FileName)
  1351  
  1352  			if path.Path == "" {
  1353  				filename, err := vm.genVmdkPath(path)
  1354  				if err != nil {
  1355  					return err
  1356  				}
  1357  
  1358  				info.FileName = filename
  1359  			}
  1360  
  1361  			err := vdmCreateVirtualDisk(spec.FileOperation, &types.CreateVirtualDisk_Task{
  1362  				Datacenter: &dc.Self,
  1363  				Name:       info.FileName,
  1364  			})
  1365  			if err != nil {
  1366  				return err
  1367  			}
  1368  
  1369  			ctx.Map.Update(vm, []types.PropertyChange{
  1370  				{Name: "summary.config.numVirtualDisks", Val: vm.Summary.Config.NumVirtualDisks + 1},
  1371  			})
  1372  
  1373  			p, _ := parseDatastorePath(info.FileName)
  1374  			ds := vm.findDatastore(p.Datastore)
  1375  			info.Datastore = &ds.Self
  1376  
  1377  			if oldDevice != nil {
  1378  				if oldDisk, ok := oldDevice.(*types.VirtualDisk); ok {
  1379  					// add previous capacity to datastore freespace
  1380  					ctx.WithLock(ds, func() {
  1381  						ds.Summary.FreeSpace += getDiskSize(oldDisk)
  1382  						ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
  1383  					})
  1384  				}
  1385  			}
  1386  
  1387  			// then subtract new capacity from datastore freespace
  1388  			// XXX: compare disk size and free space until windows stat is supported
  1389  			ctx.WithLock(ds, func() {
  1390  				ds.Summary.FreeSpace -= getDiskSize(x)
  1391  				ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
  1392  			})
  1393  
  1394  			vm.updateDiskLayouts()
  1395  
  1396  			if disk, ok := b.(*types.VirtualDiskFlatVer2BackingInfo); ok {
  1397  				// These properties default to false
  1398  				props := []**bool{
  1399  					&disk.EagerlyScrub,
  1400  					&disk.ThinProvisioned,
  1401  					&disk.WriteThrough,
  1402  					&disk.Split,
  1403  					&disk.DigestEnabled,
  1404  				}
  1405  				for _, prop := range props {
  1406  					if *prop == nil {
  1407  						*prop = types.NewBool(false)
  1408  					}
  1409  				}
  1410  				disk.Uuid = virtualDiskUUID(&dc.Self, info.FileName)
  1411  			}
  1412  		}
  1413  	case *types.VirtualCdrom:
  1414  		if b, ok := d.Backing.(types.BaseVirtualDeviceFileBackingInfo); ok {
  1415  			summary = "ISO " + b.GetVirtualDeviceFileBackingInfo().FileName
  1416  		}
  1417  	case *types.VirtualFloppy:
  1418  		if b, ok := d.Backing.(types.BaseVirtualDeviceFileBackingInfo); ok {
  1419  			summary = "Image " + b.GetVirtualDeviceFileBackingInfo().FileName
  1420  		}
  1421  	case *types.VirtualSerialPort:
  1422  		switch b := d.Backing.(type) {
  1423  		case types.BaseVirtualDeviceFileBackingInfo:
  1424  			summary = "File " + b.GetVirtualDeviceFileBackingInfo().FileName
  1425  		case *types.VirtualSerialPortURIBackingInfo:
  1426  			summary = "Remote " + b.ServiceURI
  1427  		}
  1428  	}
  1429  
  1430  	if d.UnitNumber == nil && controller != nil {
  1431  		devices.AssignController(device, controller)
  1432  	}
  1433  
  1434  	if d.DeviceInfo == nil {
  1435  		d.DeviceInfo = &types.Description{
  1436  			Label:   label,
  1437  			Summary: summary,
  1438  		}
  1439  	} else {
  1440  		info := d.DeviceInfo.GetDescription()
  1441  		if info.Label == "" {
  1442  			info.Label = label
  1443  		}
  1444  		if info.Summary == "" {
  1445  			info.Summary = summary
  1446  		}
  1447  	}
  1448  
  1449  	switch device.(type) {
  1450  	case types.BaseVirtualEthernetCard, *types.VirtualCdrom, *types.VirtualFloppy, *types.VirtualUSB, *types.VirtualSerialPort:
  1451  		if d.Connectable == nil {
  1452  			d.Connectable = &types.VirtualDeviceConnectInfo{StartConnected: true, Connected: true}
  1453  		}
  1454  	}
  1455  
  1456  	// device can be connected only if vm is powered on
  1457  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  1458  		if d.Connectable != nil {
  1459  			d.Connectable.Connected = false
  1460  		}
  1461  	}
  1462  
  1463  	return nil
  1464  }
  1465  
  1466  func (vm *VirtualMachine) removeDevice(ctx *Context, devices object.VirtualDeviceList, spec *types.VirtualDeviceConfigSpec) object.VirtualDeviceList {
  1467  	key := spec.Device.GetVirtualDevice().Key
  1468  
  1469  	for i, d := range devices {
  1470  		if d.GetVirtualDevice().Key != key {
  1471  			continue
  1472  		}
  1473  
  1474  		devices = append(devices[:i], devices[i+1:]...)
  1475  
  1476  		switch device := spec.Device.(type) {
  1477  		case *types.VirtualDisk:
  1478  			if spec.FileOperation == types.VirtualDeviceConfigSpecFileOperationDestroy {
  1479  				var file string
  1480  
  1481  				switch b := device.Backing.(type) {
  1482  				case types.BaseVirtualDeviceFileBackingInfo:
  1483  					file = b.GetVirtualDeviceFileBackingInfo().FileName
  1484  
  1485  					p, _ := parseDatastorePath(file)
  1486  					ds := vm.findDatastore(p.Datastore)
  1487  
  1488  					ctx.WithLock(ds, func() {
  1489  						ds.Summary.FreeSpace += getDiskSize(device)
  1490  						ds.Info.GetDatastoreInfo().FreeSpace = ds.Summary.FreeSpace
  1491  					})
  1492  				}
  1493  
  1494  				if file != "" {
  1495  					dc := ctx.Map.getEntityDatacenter(vm)
  1496  					dm := ctx.Map.VirtualDiskManager()
  1497  					if dc == nil {
  1498  						continue // parent was destroyed
  1499  					}
  1500  					res := dm.DeleteVirtualDiskTask(ctx, &types.DeleteVirtualDisk_Task{
  1501  						Name:       file,
  1502  						Datacenter: &dc.Self,
  1503  					})
  1504  					ctask := ctx.Map.Get(res.(*methods.DeleteVirtualDisk_TaskBody).Res.Returnval).(*Task)
  1505  					ctask.Wait()
  1506  				}
  1507  			}
  1508  			ctx.Map.Update(vm, []types.PropertyChange{
  1509  				{Name: "summary.config.numVirtualDisks", Val: vm.Summary.Config.NumVirtualDisks - 1},
  1510  			})
  1511  
  1512  			vm.updateDiskLayouts()
  1513  		case types.BaseVirtualEthernetCard:
  1514  			var net types.ManagedObjectReference
  1515  
  1516  			switch b := device.GetVirtualEthernetCard().Backing.(type) {
  1517  			case *types.VirtualEthernetCardNetworkBackingInfo:
  1518  				net = *b.Network
  1519  			case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo:
  1520  				net.Type = "DistributedVirtualPortgroup"
  1521  				net.Value = b.Port.PortgroupKey
  1522  			}
  1523  
  1524  			for j, nicInfo := range vm.Guest.Net {
  1525  				if nicInfo.DeviceConfigId == key {
  1526  					vm.Guest.Net = append(vm.Guest.Net[:j], vm.Guest.Net[j+1:]...)
  1527  					break
  1528  				}
  1529  			}
  1530  
  1531  			networks := vm.Network
  1532  			RemoveReference(&networks, net)
  1533  			ctx.Map.Update(vm, []types.PropertyChange{
  1534  				{Name: "summary.config.numEthernetCards", Val: vm.Summary.Config.NumEthernetCards - 1},
  1535  				{Name: "network", Val: networks},
  1536  			})
  1537  		}
  1538  
  1539  		break
  1540  	}
  1541  
  1542  	return devices
  1543  }
  1544  
  1545  func (vm *VirtualMachine) genVmdkPath(p object.DatastorePath) (string, types.BaseMethodFault) {
  1546  	if p.Datastore == "" {
  1547  		p.FromString(vm.Config.Files.VmPathName)
  1548  	}
  1549  	if p.Path == "" {
  1550  		p.Path = vm.Config.Name
  1551  	} else {
  1552  		p.Path = path.Dir(p.Path)
  1553  	}
  1554  	vmdir := p.String()
  1555  	index := 0
  1556  	for {
  1557  		var filename string
  1558  		if index == 0 {
  1559  			filename = fmt.Sprintf("%s.vmdk", vm.Config.Name)
  1560  		} else {
  1561  			filename = fmt.Sprintf("%s_%d.vmdk", vm.Config.Name, index)
  1562  		}
  1563  
  1564  		f, err := vm.createFile(vmdir, filename, false)
  1565  		if err != nil {
  1566  			switch err.(type) {
  1567  			case *types.FileAlreadyExists:
  1568  				index++
  1569  				continue
  1570  			default:
  1571  				return "", err
  1572  			}
  1573  		}
  1574  
  1575  		_ = f.Close()
  1576  		_ = os.Remove(f.Name())
  1577  
  1578  		return path.Join(vmdir, filename), nil
  1579  	}
  1580  }
  1581  
  1582  func (vm *VirtualMachine) configureDevices(ctx *Context, spec *types.VirtualMachineConfigSpec) types.BaseMethodFault {
  1583  	devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
  1584  
  1585  	var err types.BaseMethodFault
  1586  	for i, change := range spec.DeviceChange {
  1587  		dspec := change.GetVirtualDeviceConfigSpec()
  1588  		device := dspec.Device.GetVirtualDevice()
  1589  		invalid := &types.InvalidDeviceSpec{DeviceIndex: int32(i)}
  1590  
  1591  		switch dspec.FileOperation {
  1592  		case types.VirtualDeviceConfigSpecFileOperationCreate:
  1593  			switch dspec.Device.(type) {
  1594  			case *types.VirtualDisk:
  1595  				if device.UnitNumber == nil {
  1596  					return invalid
  1597  				}
  1598  			}
  1599  		}
  1600  
  1601  		switch dspec.Operation {
  1602  		case types.VirtualDeviceConfigSpecOperationAdd:
  1603  			if devices.FindByKey(device.Key) != nil && device.ControllerKey == 0 {
  1604  				// Note: real ESX does not allow adding base controllers (ControllerKey = 0)
  1605  				// after VM is created (returns success but device is not added).
  1606  				continue
  1607  			} else if device.UnitNumber != nil && devices.SelectByType(dspec.Device).Select(func(d types.BaseVirtualDevice) bool {
  1608  				base := d.GetVirtualDevice()
  1609  				if base.UnitNumber != nil {
  1610  					if base.ControllerKey != device.ControllerKey {
  1611  						return false
  1612  					}
  1613  					return *base.UnitNumber == *device.UnitNumber
  1614  				}
  1615  				return false
  1616  			}) != nil {
  1617  				// UnitNumber for this device type is taken
  1618  				return invalid
  1619  			}
  1620  
  1621  			key := device.Key
  1622  			err = vm.configureDevice(ctx, devices, dspec, nil)
  1623  			if err != nil {
  1624  				return err
  1625  			}
  1626  
  1627  			devices = append(devices, dspec.Device)
  1628  			if key != device.Key {
  1629  				// Update ControllerKey refs
  1630  				for i := range spec.DeviceChange {
  1631  					ckey := &spec.DeviceChange[i].GetVirtualDeviceConfigSpec().Device.GetVirtualDevice().ControllerKey
  1632  					if *ckey == key {
  1633  						*ckey = device.Key
  1634  					}
  1635  				}
  1636  			}
  1637  		case types.VirtualDeviceConfigSpecOperationEdit:
  1638  			rspec := *dspec
  1639  			oldDevice := devices.FindByKey(device.Key)
  1640  			if oldDevice == nil {
  1641  				return invalid
  1642  			}
  1643  			rspec.Device = oldDevice
  1644  			devices = vm.removeDevice(ctx, devices, &rspec)
  1645  			if device.DeviceInfo != nil {
  1646  				device.DeviceInfo.GetDescription().Summary = "" // regenerate summary
  1647  			}
  1648  
  1649  			err = vm.configureDevice(ctx, devices, dspec, oldDevice)
  1650  			if err != nil {
  1651  				return err
  1652  			}
  1653  
  1654  			devices = append(devices, dspec.Device)
  1655  		case types.VirtualDeviceConfigSpecOperationRemove:
  1656  			devices = vm.removeDevice(ctx, devices, dspec)
  1657  		}
  1658  	}
  1659  
  1660  	ctx.Map.Update(vm, []types.PropertyChange{
  1661  		{Name: "config.hardware.device", Val: []types.BaseVirtualDevice(devices)},
  1662  	})
  1663  
  1664  	err = vm.updateDiskLayouts()
  1665  	if err != nil {
  1666  		return err
  1667  	}
  1668  
  1669  	// Do this after device config, as some may apply to the devices themselves (e.g. ethernet -> guest.net)
  1670  	err = vm.applyExtraConfig(ctx, spec)
  1671  	if err != nil {
  1672  		return err
  1673  	}
  1674  
  1675  	return nil
  1676  }
  1677  
  1678  type powerVMTask struct {
  1679  	*VirtualMachine
  1680  
  1681  	state types.VirtualMachinePowerState
  1682  	ctx   *Context
  1683  }
  1684  
  1685  func (c *powerVMTask) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
  1686  	c.logPrintf("running power task: requesting %s, existing %s",
  1687  		c.state, c.VirtualMachine.Runtime.PowerState)
  1688  
  1689  	if c.VirtualMachine.Runtime.PowerState == c.state {
  1690  		return nil, &types.InvalidPowerState{
  1691  			RequestedState: c.state,
  1692  			ExistingState:  c.VirtualMachine.Runtime.PowerState,
  1693  		}
  1694  	}
  1695  
  1696  	var boot types.AnyType
  1697  	if c.state == types.VirtualMachinePowerStatePoweredOn {
  1698  		boot = time.Now()
  1699  	}
  1700  
  1701  	event := c.event()
  1702  	switch c.state {
  1703  	case types.VirtualMachinePowerStatePoweredOn:
  1704  		if c.VirtualMachine.hostInMM(c.ctx) {
  1705  			return nil, new(types.InvalidState)
  1706  		}
  1707  
  1708  		err := c.svm.start(c.ctx)
  1709  		if err != nil {
  1710  			return nil, &types.MissingPowerOnConfiguration{
  1711  				VAppConfigFault: types.VAppConfigFault{
  1712  					VimFault: types.VimFault{
  1713  						MethodFault: types.MethodFault{
  1714  							FaultCause: &types.LocalizedMethodFault{
  1715  								Fault:            &types.SystemErrorFault{Reason: err.Error()},
  1716  								LocalizedMessage: err.Error()}}}}}
  1717  		}
  1718  		c.ctx.postEvent(
  1719  			&types.VmStartingEvent{VmEvent: event},
  1720  			&types.VmPoweredOnEvent{VmEvent: event},
  1721  		)
  1722  		c.customize(c.ctx)
  1723  	case types.VirtualMachinePowerStatePoweredOff:
  1724  		c.svm.stop(c.ctx)
  1725  		c.ctx.postEvent(
  1726  			&types.VmStoppingEvent{VmEvent: event},
  1727  			&types.VmPoweredOffEvent{VmEvent: event},
  1728  		)
  1729  	case types.VirtualMachinePowerStateSuspended:
  1730  		if c.VirtualMachine.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  1731  			return nil, &types.InvalidPowerState{
  1732  				RequestedState: types.VirtualMachinePowerStatePoweredOn,
  1733  				ExistingState:  c.VirtualMachine.Runtime.PowerState,
  1734  			}
  1735  		}
  1736  
  1737  		c.svm.pause(c.ctx)
  1738  		c.ctx.postEvent(
  1739  			&types.VmSuspendingEvent{VmEvent: event},
  1740  			&types.VmSuspendedEvent{VmEvent: event},
  1741  		)
  1742  	}
  1743  
  1744  	// copy devices to prevent data race
  1745  	devices := c.VirtualMachine.cloneDevice()
  1746  	for _, d := range devices {
  1747  		conn := d.GetVirtualDevice().Connectable
  1748  		if conn == nil {
  1749  			continue
  1750  		}
  1751  
  1752  		if c.state == types.VirtualMachinePowerStatePoweredOn {
  1753  			// apply startConnected to current connection
  1754  			conn.Connected = conn.StartConnected
  1755  		} else {
  1756  			conn.Connected = false
  1757  		}
  1758  	}
  1759  
  1760  	c.ctx.Map.Update(c.VirtualMachine, []types.PropertyChange{
  1761  		{Name: "runtime.powerState", Val: c.state},
  1762  		{Name: "summary.runtime.powerState", Val: c.state},
  1763  		{Name: "summary.runtime.bootTime", Val: boot},
  1764  		{Name: "config.hardware.device", Val: devices},
  1765  	})
  1766  
  1767  	return nil, nil
  1768  }
  1769  
  1770  func (vm *VirtualMachine) PowerOnVMTask(ctx *Context, c *types.PowerOnVM_Task) soap.HasFault {
  1771  	if vm.Config.Template {
  1772  		return &methods.PowerOnVM_TaskBody{
  1773  			Fault_: Fault("cannot powerOn a template", &types.InvalidState{}),
  1774  		}
  1775  	}
  1776  
  1777  	runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOn, ctx}
  1778  	task := CreateTask(runner.Reference(), "powerOn", runner.Run)
  1779  
  1780  	return &methods.PowerOnVM_TaskBody{
  1781  		Res: &types.PowerOnVM_TaskResponse{
  1782  			Returnval: task.Run(ctx),
  1783  		},
  1784  	}
  1785  }
  1786  
  1787  func (vm *VirtualMachine) PowerOffVMTask(ctx *Context, c *types.PowerOffVM_Task) soap.HasFault {
  1788  	runner := &powerVMTask{vm, types.VirtualMachinePowerStatePoweredOff, ctx}
  1789  	task := CreateTask(runner.Reference(), "powerOff", runner.Run)
  1790  
  1791  	return &methods.PowerOffVM_TaskBody{
  1792  		Res: &types.PowerOffVM_TaskResponse{
  1793  			Returnval: task.Run(ctx),
  1794  		},
  1795  	}
  1796  }
  1797  
  1798  func (vm *VirtualMachine) SuspendVMTask(ctx *Context, req *types.SuspendVM_Task) soap.HasFault {
  1799  	runner := &powerVMTask{vm, types.VirtualMachinePowerStateSuspended, ctx}
  1800  	task := CreateTask(runner.Reference(), "suspend", runner.Run)
  1801  
  1802  	return &methods.SuspendVM_TaskBody{
  1803  		Res: &types.SuspendVM_TaskResponse{
  1804  			Returnval: task.Run(ctx),
  1805  		},
  1806  	}
  1807  }
  1808  
  1809  func (vm *VirtualMachine) ResetVMTask(ctx *Context, req *types.ResetVM_Task) soap.HasFault {
  1810  	task := CreateTask(vm, "reset", func(task *Task) (types.AnyType, types.BaseMethodFault) {
  1811  		res := vm.PowerOffVMTask(ctx, &types.PowerOffVM_Task{This: vm.Self})
  1812  		ctask := ctx.Map.Get(res.(*methods.PowerOffVM_TaskBody).Res.Returnval).(*Task)
  1813  		ctask.Wait()
  1814  		if ctask.Info.Error != nil {
  1815  			return nil, ctask.Info.Error.Fault
  1816  		}
  1817  
  1818  		res = vm.PowerOnVMTask(ctx, &types.PowerOnVM_Task{This: vm.Self})
  1819  		ctask = ctx.Map.Get(res.(*methods.PowerOnVM_TaskBody).Res.Returnval).(*Task)
  1820  		ctask.Wait()
  1821  
  1822  		return nil, nil
  1823  	})
  1824  
  1825  	return &methods.ResetVM_TaskBody{
  1826  		Res: &types.ResetVM_TaskResponse{
  1827  			Returnval: task.Run(ctx),
  1828  		},
  1829  	}
  1830  }
  1831  
  1832  func (vm *VirtualMachine) RebootGuest(ctx *Context, req *types.RebootGuest) soap.HasFault {
  1833  	body := new(methods.RebootGuestBody)
  1834  
  1835  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  1836  		body.Fault_ = Fault("", &types.InvalidPowerState{
  1837  			RequestedState: types.VirtualMachinePowerStatePoweredOn,
  1838  			ExistingState:  vm.Runtime.PowerState,
  1839  		})
  1840  		return body
  1841  	}
  1842  
  1843  	if vm.Guest.ToolsRunningStatus == string(types.VirtualMachineToolsRunningStatusGuestToolsRunning) {
  1844  		vm.svm.restart(ctx)
  1845  		body.Res = new(types.RebootGuestResponse)
  1846  	} else {
  1847  		body.Fault_ = Fault("", new(types.ToolsUnavailable))
  1848  	}
  1849  
  1850  	return body
  1851  }
  1852  
  1853  func (vm *VirtualMachine) ReconfigVMTask(ctx *Context, req *types.ReconfigVM_Task) soap.HasFault {
  1854  	task := CreateTask(vm, "reconfigVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  1855  		ctx.postEvent(&types.VmReconfiguredEvent{
  1856  			VmEvent:    vm.event(),
  1857  			ConfigSpec: req.Spec,
  1858  		})
  1859  
  1860  		if vm.Config.Template {
  1861  			expect := types.VirtualMachineConfigSpec{
  1862  				Name:       req.Spec.Name,
  1863  				Annotation: req.Spec.Annotation,
  1864  			}
  1865  			if !reflect.DeepEqual(&req.Spec, &expect) {
  1866  				log.Printf("template reconfigure only allows name and annotation change")
  1867  				return nil, new(types.NotSupported)
  1868  			}
  1869  		}
  1870  
  1871  		err := vm.configure(ctx, &req.Spec)
  1872  
  1873  		return nil, err
  1874  	})
  1875  
  1876  	return &methods.ReconfigVM_TaskBody{
  1877  		Res: &types.ReconfigVM_TaskResponse{
  1878  			Returnval: task.Run(ctx),
  1879  		},
  1880  	}
  1881  }
  1882  
  1883  func (vm *VirtualMachine) UpgradeVMTask(ctx *Context, req *types.UpgradeVM_Task) soap.HasFault {
  1884  	body := &methods.UpgradeVM_TaskBody{}
  1885  
  1886  	task := CreateTask(vm, "upgradeVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  1887  
  1888  		newInvalidStateFault := func(format string, args ...any) *types.InvalidState {
  1889  			msg := fmt.Sprintf(format, args...)
  1890  			return &types.InvalidState{
  1891  				VimFault: types.VimFault{
  1892  					MethodFault: types.MethodFault{
  1893  						FaultCause: &types.LocalizedMethodFault{
  1894  							Fault: &types.SystemErrorFault{
  1895  								Reason: msg,
  1896  							},
  1897  							LocalizedMessage: msg,
  1898  						},
  1899  					},
  1900  				},
  1901  			}
  1902  		}
  1903  
  1904  		// InvalidPowerState
  1905  		//
  1906  		// 1. Is VM's power state anything other than powered off?
  1907  		if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOff {
  1908  			return nil, &types.InvalidPowerStateFault{
  1909  				ExistingState:  vm.Runtime.PowerState,
  1910  				RequestedState: types.VirtualMachinePowerStatePoweredOff,
  1911  			}
  1912  		}
  1913  
  1914  		// InvalidState
  1915  		//
  1916  		// 1. Is host on which VM is scheduled in maintenance mode?
  1917  		// 2. Is VM a template?
  1918  		// 3. Is VM already the latest hardware version?
  1919  		var (
  1920  			ebRef                     *types.ManagedObjectReference
  1921  			latestHardwareVersion     string
  1922  			hostRef                   = vm.Runtime.Host
  1923  			supportedHardwareVersions = map[string]struct{}{}
  1924  			vmHardwareVersionString   = vm.Config.Version
  1925  		)
  1926  		if hostRef != nil {
  1927  			var hostInMaintenanceMode bool
  1928  			ctx.WithLock(*hostRef, func() {
  1929  				host := ctx.Map.Get(*hostRef).(*HostSystem)
  1930  				hostInMaintenanceMode = host.Runtime.InMaintenanceMode
  1931  				switch host.Parent.Type {
  1932  				case "ClusterComputeResource":
  1933  					obj := ctx.Map.Get(*host.Parent).(*ClusterComputeResource)
  1934  					ebRef = obj.EnvironmentBrowser
  1935  				case "ComputeResource":
  1936  					obj := ctx.Map.Get(*host.Parent).(*mo.ComputeResource)
  1937  					ebRef = obj.EnvironmentBrowser
  1938  				}
  1939  			})
  1940  			if hostInMaintenanceMode {
  1941  				return nil, newInvalidStateFault("%s in maintenance mode", hostRef.Value)
  1942  			}
  1943  		}
  1944  		if vm.Config.Template {
  1945  			return nil, newInvalidStateFault("%s is template", vm.Reference().Value)
  1946  		}
  1947  		if ebRef != nil {
  1948  			ctx.WithLock(*ebRef, func() {
  1949  				eb := ctx.Map.Get(*ebRef).(*EnvironmentBrowser)
  1950  				for i := range eb.QueryConfigOptionDescriptorResponse.Returnval {
  1951  					cod := eb.QueryConfigOptionDescriptorResponse.Returnval[i]
  1952  					for j := range cod.Host {
  1953  						if cod.Host[j].Value == hostRef.Value {
  1954  							supportedHardwareVersions[cod.Key] = struct{}{}
  1955  						}
  1956  						if latestHardwareVersion == "" {
  1957  							if def := cod.DefaultConfigOption; def != nil && *def {
  1958  								latestHardwareVersion = cod.Key
  1959  							}
  1960  						}
  1961  					}
  1962  				}
  1963  			})
  1964  		}
  1965  
  1966  		if latestHardwareVersion == "" {
  1967  			latestHardwareVersion = esx.HardwareVersion
  1968  		}
  1969  		if vmHardwareVersionString == latestHardwareVersion {
  1970  			return nil, newInvalidStateFault("%s is latest version", vm.Reference().Value)
  1971  		}
  1972  		if req.Version == "" {
  1973  			req.Version = latestHardwareVersion
  1974  		}
  1975  
  1976  		// NotSupported
  1977  		targetVersion, _ := types.ParseHardwareVersion(req.Version)
  1978  		if targetVersion.IsValid() {
  1979  			req.Version = targetVersion.String()
  1980  		}
  1981  		if _, ok := supportedHardwareVersions[req.Version]; !ok {
  1982  			msg := fmt.Sprintf("%s not supported", req.Version)
  1983  			return nil, &types.NotSupported{
  1984  				RuntimeFault: types.RuntimeFault{
  1985  					MethodFault: types.MethodFault{
  1986  						FaultCause: &types.LocalizedMethodFault{
  1987  							Fault: &types.SystemErrorFault{
  1988  								Reason: msg,
  1989  							},
  1990  							LocalizedMessage: msg,
  1991  						},
  1992  					},
  1993  				},
  1994  			}
  1995  		}
  1996  
  1997  		// AlreadyUpgraded
  1998  		vmHardwareVersion, _ := types.ParseHardwareVersion(vmHardwareVersionString)
  1999  		if targetVersion.IsValid() && vmHardwareVersion.IsValid() &&
  2000  			targetVersion <= vmHardwareVersion {
  2001  
  2002  			return nil, &types.AlreadyUpgradedFault{}
  2003  		}
  2004  
  2005  		// InvalidArgument
  2006  		if targetVersion < types.VMX3 {
  2007  			return nil, &types.InvalidArgument{}
  2008  		}
  2009  
  2010  		ctx.Map.Update(vm, []types.PropertyChange{
  2011  			{
  2012  				Name: "config.version", Val: targetVersion.String(),
  2013  			},
  2014  			{
  2015  				Name: "summary.config.hwVersion", Val: targetVersion.String(),
  2016  			},
  2017  		})
  2018  
  2019  		return nil, nil
  2020  	})
  2021  
  2022  	body.Res = &types.UpgradeVM_TaskResponse{
  2023  		Returnval: task.Run(ctx),
  2024  	}
  2025  
  2026  	return body
  2027  }
  2028  
  2029  func (vm *VirtualMachine) DestroyTask(ctx *Context, req *types.Destroy_Task) soap.HasFault {
  2030  	dc := ctx.Map.getEntityDatacenter(vm)
  2031  
  2032  	task := CreateTask(vm, "destroy", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2033  		if dc == nil {
  2034  			return nil, &types.ManagedObjectNotFound{Obj: vm.Self} // If our Parent was destroyed, so were we.
  2035  			// TODO: should this also trigger container removal?
  2036  		}
  2037  
  2038  		r := vm.UnregisterVM(ctx, &types.UnregisterVM{
  2039  			This: req.This,
  2040  		})
  2041  
  2042  		if r.Fault() != nil {
  2043  			return nil, r.Fault().VimFault().(types.BaseMethodFault)
  2044  		}
  2045  
  2046  		// Remove all devices
  2047  		devices := object.VirtualDeviceList(vm.Config.Hardware.Device)
  2048  		spec, _ := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationRemove)
  2049  		vm.configureDevices(ctx, &types.VirtualMachineConfigSpec{DeviceChange: spec})
  2050  
  2051  		// Delete VM files from the datastore (ignoring result for now)
  2052  		m := ctx.Map.FileManager()
  2053  
  2054  		_ = m.DeleteDatastoreFileTask(ctx, &types.DeleteDatastoreFile_Task{
  2055  			This:       m.Reference(),
  2056  			Name:       vm.Config.Files.LogDirectory,
  2057  			Datacenter: &dc.Self,
  2058  		})
  2059  
  2060  		err := vm.svm.remove(ctx)
  2061  		if err != nil {
  2062  			return nil, &types.RuntimeFault{
  2063  				MethodFault: types.MethodFault{
  2064  					FaultCause: &types.LocalizedMethodFault{
  2065  						Fault:            &types.SystemErrorFault{Reason: err.Error()},
  2066  						LocalizedMessage: err.Error()}}}
  2067  		}
  2068  
  2069  		return nil, nil
  2070  	})
  2071  
  2072  	return &methods.Destroy_TaskBody{
  2073  		Res: &types.Destroy_TaskResponse{
  2074  			Returnval: task.Run(ctx),
  2075  		},
  2076  	}
  2077  }
  2078  
  2079  func (vm *VirtualMachine) SetCustomValue(ctx *Context, req *types.SetCustomValue) soap.HasFault {
  2080  	return SetCustomValue(ctx, req)
  2081  }
  2082  
  2083  func (vm *VirtualMachine) UnregisterVM(ctx *Context, c *types.UnregisterVM) soap.HasFault {
  2084  	r := &methods.UnregisterVMBody{}
  2085  
  2086  	if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
  2087  		r.Fault_ = Fault("", &types.InvalidPowerState{
  2088  			RequestedState: types.VirtualMachinePowerStatePoweredOff,
  2089  			ExistingState:  vm.Runtime.PowerState,
  2090  		})
  2091  
  2092  		return r
  2093  	}
  2094  
  2095  	host := ctx.Map.Get(*vm.Runtime.Host).(*HostSystem)
  2096  	ctx.Map.RemoveReference(ctx, host, &host.Vm, vm.Self)
  2097  
  2098  	if vm.ResourcePool != nil {
  2099  		switch pool := ctx.Map.Get(*vm.ResourcePool).(type) {
  2100  		case *ResourcePool:
  2101  			ctx.Map.RemoveReference(ctx, pool, &pool.Vm, vm.Self)
  2102  		case *VirtualApp:
  2103  			ctx.Map.RemoveReference(ctx, pool, &pool.Vm, vm.Self)
  2104  		}
  2105  	}
  2106  
  2107  	for i := range vm.Datastore {
  2108  		ds := ctx.Map.Get(vm.Datastore[i]).(*Datastore)
  2109  		ctx.Map.RemoveReference(ctx, ds, &ds.Vm, vm.Self)
  2110  	}
  2111  
  2112  	ctx.postEvent(&types.VmRemovedEvent{VmEvent: vm.event()})
  2113  	if f, ok := asFolderMO(ctx.Map.getEntityParent(vm, "Folder")); ok {
  2114  		folderRemoveChild(ctx, f, c.This)
  2115  	}
  2116  
  2117  	r.Res = new(types.UnregisterVMResponse)
  2118  
  2119  	return r
  2120  }
  2121  
  2122  type vmFolder interface {
  2123  	CreateVMTask(ctx *Context, c *types.CreateVM_Task) soap.HasFault
  2124  }
  2125  
  2126  func (vm *VirtualMachine) cloneDevice() []types.BaseVirtualDevice {
  2127  	src := types.ArrayOfVirtualDevice{
  2128  		VirtualDevice: vm.Config.Hardware.Device,
  2129  	}
  2130  	dst := types.ArrayOfVirtualDevice{}
  2131  	deepCopy(src, &dst)
  2132  	return dst.VirtualDevice
  2133  }
  2134  
  2135  func (vm *VirtualMachine) CloneVMTask(ctx *Context, req *types.CloneVM_Task) soap.HasFault {
  2136  	pool := req.Spec.Location.Pool
  2137  	if pool == nil {
  2138  		if !vm.Config.Template {
  2139  			pool = vm.ResourcePool
  2140  		}
  2141  	}
  2142  
  2143  	destHost := vm.Runtime.Host
  2144  
  2145  	if req.Spec.Location.Host != nil {
  2146  		destHost = req.Spec.Location.Host
  2147  	}
  2148  
  2149  	folder, _ := asFolderMO(ctx.Map.Get(req.Folder))
  2150  	host := ctx.Map.Get(*destHost).(*HostSystem)
  2151  	event := vm.event()
  2152  
  2153  	ctx.postEvent(&types.VmBeingClonedEvent{
  2154  		VmCloneEvent: types.VmCloneEvent{
  2155  			VmEvent: event,
  2156  		},
  2157  		DestFolder: folderEventArgument(folder),
  2158  		DestName:   req.Name,
  2159  		DestHost:   *host.eventArgument(),
  2160  	})
  2161  
  2162  	vmx := vm.vmx(nil)
  2163  	vmx.Path = req.Name
  2164  	if ref := req.Spec.Location.Datastore; ref != nil {
  2165  		ds := ctx.Map.Get(*ref).(*Datastore).Name
  2166  		vmx.Datastore = ds
  2167  	}
  2168  
  2169  	task := CreateTask(vm, "cloneVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2170  		if pool == nil {
  2171  			return nil, &types.InvalidArgument{InvalidProperty: "spec.location.pool"}
  2172  		}
  2173  		if obj := ctx.Map.FindByName(req.Name, folder.ChildEntity); obj != nil {
  2174  			return nil, &types.DuplicateName{
  2175  				Name:   req.Name,
  2176  				Object: obj.Reference(),
  2177  			}
  2178  		}
  2179  		config := types.VirtualMachineConfigSpec{
  2180  			Name:    req.Name,
  2181  			Version: vm.Config.Version,
  2182  			GuestId: vm.Config.GuestId,
  2183  			Files: &types.VirtualMachineFileInfo{
  2184  				VmPathName: vmx.String(),
  2185  			},
  2186  		}
  2187  
  2188  		// Copying hardware properties
  2189  		config.NumCPUs = vm.Config.Hardware.NumCPU
  2190  		config.MemoryMB = int64(vm.Config.Hardware.MemoryMB)
  2191  		config.NumCoresPerSocket = vm.Config.Hardware.NumCoresPerSocket
  2192  		config.VirtualICH7MPresent = vm.Config.Hardware.VirtualICH7MPresent
  2193  		config.VirtualSMCPresent = vm.Config.Hardware.VirtualSMCPresent
  2194  
  2195  		defaultDevices := object.VirtualDeviceList(esx.VirtualDevice)
  2196  		devices := vm.cloneDevice()
  2197  
  2198  		for _, device := range devices {
  2199  			var fop types.VirtualDeviceConfigSpecFileOperation
  2200  
  2201  			if defaultDevices.Find(object.VirtualDeviceList(devices).Name(device)) != nil {
  2202  				// Default devices are added during CreateVMTask
  2203  				continue
  2204  			}
  2205  
  2206  			switch disk := device.(type) {
  2207  			case *types.VirtualDisk:
  2208  				// TODO: consider VirtualMachineCloneSpec.DiskMoveType
  2209  				fop = types.VirtualDeviceConfigSpecFileOperationCreate
  2210  
  2211  				// Leave FileName empty so CreateVM will just create a new one under VmPathName
  2212  				disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).FileName = ""
  2213  				disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).Parent = nil
  2214  			}
  2215  
  2216  			config.DeviceChange = append(config.DeviceChange, &types.VirtualDeviceConfigSpec{
  2217  				Operation:     types.VirtualDeviceConfigSpecOperationAdd,
  2218  				Device:        device,
  2219  				FileOperation: fop,
  2220  			})
  2221  		}
  2222  
  2223  		if dst, src := config, req.Spec.Config; src != nil {
  2224  			dst.ExtraConfig = src.ExtraConfig
  2225  			copyNonEmptyValue(&dst.Uuid, &src.Uuid)
  2226  			copyNonEmptyValue(&dst.InstanceUuid, &src.InstanceUuid)
  2227  			copyNonEmptyValue(&dst.NumCPUs, &src.NumCPUs)
  2228  			copyNonEmptyValue(&dst.MemoryMB, &src.MemoryMB)
  2229  		}
  2230  
  2231  		res := ctx.Map.Get(req.Folder).(vmFolder).CreateVMTask(ctx, &types.CreateVM_Task{
  2232  			This:   folder.Self,
  2233  			Config: config,
  2234  			Pool:   *pool,
  2235  			Host:   destHost,
  2236  		})
  2237  
  2238  		ctask := ctx.Map.Get(res.(*methods.CreateVM_TaskBody).Res.Returnval).(*Task)
  2239  		ctask.Wait()
  2240  		if ctask.Info.Error != nil {
  2241  			return nil, ctask.Info.Error.Fault
  2242  		}
  2243  
  2244  		ref := ctask.Info.Result.(types.ManagedObjectReference)
  2245  		clone := ctx.Map.Get(ref).(*VirtualMachine)
  2246  		clone.configureDevices(ctx, &types.VirtualMachineConfigSpec{DeviceChange: req.Spec.Location.DeviceChange})
  2247  		if req.Spec.Config != nil && req.Spec.Config.DeviceChange != nil {
  2248  			clone.configureDevices(ctx, &types.VirtualMachineConfigSpec{DeviceChange: req.Spec.Config.DeviceChange})
  2249  		}
  2250  		clone.DataSets = copyDataSetsForVmClone(vm.DataSets)
  2251  
  2252  		if req.Spec.Template {
  2253  			_ = clone.MarkAsTemplate(&types.MarkAsTemplate{This: clone.Self})
  2254  		}
  2255  
  2256  		ctx.postEvent(&types.VmClonedEvent{
  2257  			VmCloneEvent: types.VmCloneEvent{VmEvent: clone.event()},
  2258  			SourceVm:     *event.Vm,
  2259  		})
  2260  
  2261  		return ref, nil
  2262  	})
  2263  
  2264  	return &methods.CloneVM_TaskBody{
  2265  		Res: &types.CloneVM_TaskResponse{
  2266  			Returnval: task.Run(ctx),
  2267  		},
  2268  	}
  2269  }
  2270  
  2271  func copyNonEmptyValue[T comparable](dst, src *T) {
  2272  	if dst == nil || src == nil {
  2273  		return
  2274  	}
  2275  	var t T
  2276  	if *src == t {
  2277  		return
  2278  	}
  2279  	*dst = *src
  2280  }
  2281  
  2282  func (vm *VirtualMachine) RelocateVMTask(ctx *Context, req *types.RelocateVM_Task) soap.HasFault {
  2283  	task := CreateTask(vm, "relocateVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2284  		var changes []types.PropertyChange
  2285  
  2286  		if ref := req.Spec.Datastore; ref != nil {
  2287  			ds := ctx.Map.Get(*ref).(*Datastore)
  2288  			ctx.Map.RemoveReference(ctx, ds, &ds.Vm, *ref)
  2289  
  2290  			// TODO: migrate vm.Config.Files, vm.Summary.Config.VmPathName, vm.Layout and vm.LayoutEx
  2291  
  2292  			changes = append(changes, types.PropertyChange{Name: "datastore", Val: []types.ManagedObjectReference{*ref}})
  2293  		}
  2294  
  2295  		if ref := req.Spec.Pool; ref != nil {
  2296  			pool := ctx.Map.Get(*ref).(*ResourcePool)
  2297  			ctx.Map.RemoveReference(ctx, pool, &pool.Vm, *ref)
  2298  
  2299  			changes = append(changes, types.PropertyChange{Name: "resourcePool", Val: ref})
  2300  		}
  2301  
  2302  		if ref := req.Spec.Host; ref != nil {
  2303  			host := ctx.Map.Get(*ref).(*HostSystem)
  2304  			ctx.Map.RemoveReference(ctx, host, &host.Vm, *ref)
  2305  
  2306  			changes = append(changes,
  2307  				types.PropertyChange{Name: "runtime.host", Val: ref},
  2308  				types.PropertyChange{Name: "summary.runtime.host", Val: ref},
  2309  			)
  2310  		}
  2311  
  2312  		if ref := req.Spec.Folder; ref != nil {
  2313  			folder := ctx.Map.Get(*ref).(*Folder)
  2314  			folder.MoveIntoFolderTask(ctx, &types.MoveIntoFolder_Task{
  2315  				List: []types.ManagedObjectReference{vm.Self},
  2316  			})
  2317  		}
  2318  
  2319  		ctx.postEvent(&types.VmMigratedEvent{
  2320  			VmEvent:          vm.event(),
  2321  			SourceHost:       *ctx.Map.Get(*vm.Runtime.Host).(*HostSystem).eventArgument(),
  2322  			SourceDatacenter: datacenterEventArgument(vm),
  2323  			SourceDatastore:  ctx.Map.Get(vm.Datastore[0]).(*Datastore).eventArgument(),
  2324  		})
  2325  
  2326  		ctx.Map.Update(vm, changes)
  2327  
  2328  		return nil, nil
  2329  	})
  2330  
  2331  	return &methods.RelocateVM_TaskBody{
  2332  		Res: &types.RelocateVM_TaskResponse{
  2333  			Returnval: task.Run(ctx),
  2334  		},
  2335  	}
  2336  }
  2337  
  2338  func (vm *VirtualMachine) customize(ctx *Context) {
  2339  	if vm.imc == nil {
  2340  		return
  2341  	}
  2342  
  2343  	event := types.CustomizationEvent{VmEvent: vm.event()}
  2344  	ctx.postEvent(&types.CustomizationStartedEvent{CustomizationEvent: event})
  2345  
  2346  	changes := []types.PropertyChange{
  2347  		{Name: "config.tools.pendingCustomization", Val: ""},
  2348  	}
  2349  
  2350  	hostname := ""
  2351  	address := ""
  2352  
  2353  	switch c := vm.imc.Identity.(type) {
  2354  	case *types.CustomizationLinuxPrep:
  2355  		hostname = customizeName(vm, c.HostName)
  2356  	case *types.CustomizationSysprep:
  2357  		hostname = customizeName(vm, c.UserData.ComputerName)
  2358  	}
  2359  
  2360  	cards := object.VirtualDeviceList(vm.Config.Hardware.Device).SelectByType((*types.VirtualEthernetCard)(nil))
  2361  
  2362  	for i, s := range vm.imc.NicSettingMap {
  2363  		nic := &vm.Guest.Net[i]
  2364  		if s.MacAddress != "" {
  2365  			nic.MacAddress = strings.ToLower(s.MacAddress) // MacAddress in guest will always be lowercase
  2366  			card := cards[i].(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
  2367  			card.MacAddress = s.MacAddress // MacAddress in Virtual NIC can be any case
  2368  			card.AddressType = string(types.VirtualEthernetCardMacTypeManual)
  2369  		}
  2370  		if nic.DnsConfig == nil {
  2371  			nic.DnsConfig = new(types.NetDnsConfigInfo)
  2372  		}
  2373  		if s.Adapter.DnsDomain != "" {
  2374  			nic.DnsConfig.DomainName = s.Adapter.DnsDomain
  2375  		}
  2376  		if len(s.Adapter.DnsServerList) != 0 {
  2377  			nic.DnsConfig.IpAddress = s.Adapter.DnsServerList
  2378  		}
  2379  		if hostname != "" {
  2380  			nic.DnsConfig.HostName = hostname
  2381  		}
  2382  		if len(vm.imc.GlobalIPSettings.DnsSuffixList) != 0 {
  2383  			nic.DnsConfig.SearchDomain = vm.imc.GlobalIPSettings.DnsSuffixList
  2384  		}
  2385  		if nic.IpConfig == nil {
  2386  			nic.IpConfig = new(types.NetIpConfigInfo)
  2387  		}
  2388  
  2389  		switch ip := s.Adapter.Ip.(type) {
  2390  		case *types.CustomizationCustomIpGenerator:
  2391  		case *types.CustomizationDhcpIpGenerator:
  2392  		case *types.CustomizationFixedIp:
  2393  			if address == "" {
  2394  				address = ip.IpAddress
  2395  			}
  2396  			nic.IpAddress = []string{ip.IpAddress}
  2397  			nic.IpConfig.IpAddress = []types.NetIpConfigInfoIpAddress{{
  2398  				IpAddress: ip.IpAddress,
  2399  			}}
  2400  		case *types.CustomizationUnknownIpGenerator:
  2401  		}
  2402  	}
  2403  
  2404  	if len(vm.imc.NicSettingMap) != 0 {
  2405  		changes = append(changes, types.PropertyChange{Name: "guest.net", Val: vm.Guest.Net})
  2406  	}
  2407  	if hostname != "" {
  2408  		changes = append(changes, types.PropertyChange{Name: "guest.hostName", Val: hostname})
  2409  		changes = append(changes, types.PropertyChange{Name: "summary.guest.hostName", Val: hostname})
  2410  	}
  2411  	if address != "" {
  2412  		changes = append(changes, types.PropertyChange{Name: "guest.ipAddress", Val: address})
  2413  		changes = append(changes, types.PropertyChange{Name: "summary.guest.ipAddress", Val: address})
  2414  	}
  2415  
  2416  	vm.imc = nil
  2417  	ctx.Map.Update(vm, changes)
  2418  	ctx.postEvent(&types.CustomizationSucceeded{CustomizationEvent: event})
  2419  }
  2420  
  2421  func (vm *VirtualMachine) CustomizeVMTask(ctx *Context, req *types.CustomizeVM_Task) soap.HasFault {
  2422  	task := CreateTask(vm, "customizeVm", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2423  		if vm.hostInMM(ctx) {
  2424  			return nil, new(types.InvalidState)
  2425  		}
  2426  
  2427  		if vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn {
  2428  			return nil, &types.InvalidPowerState{
  2429  				RequestedState: types.VirtualMachinePowerStatePoweredOff,
  2430  				ExistingState:  vm.Runtime.PowerState,
  2431  			}
  2432  		}
  2433  		if vm.Config.Tools.PendingCustomization != "" {
  2434  			return nil, new(types.CustomizationPending)
  2435  		}
  2436  		if len(vm.Guest.Net) != len(req.Spec.NicSettingMap) {
  2437  			return nil, &types.NicSettingMismatch{
  2438  				NumberOfNicsInSpec: int32(len(req.Spec.NicSettingMap)),
  2439  				NumberOfNicsInVM:   int32(len(vm.Guest.Net)),
  2440  			}
  2441  		}
  2442  
  2443  		vm.imc = &req.Spec
  2444  		vm.Config.Tools.PendingCustomization = uuid.New().String()
  2445  
  2446  		return nil, nil
  2447  	})
  2448  
  2449  	return &methods.CustomizeVM_TaskBody{
  2450  		Res: &types.CustomizeVM_TaskResponse{
  2451  			Returnval: task.Run(ctx),
  2452  		},
  2453  	}
  2454  }
  2455  
  2456  func (vm *VirtualMachine) CreateSnapshotTask(ctx *Context, req *types.CreateSnapshot_Task) soap.HasFault {
  2457  	task := CreateTask(vm, "createSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2458  		var changes []types.PropertyChange
  2459  
  2460  		if vm.Snapshot == nil {
  2461  			vm.Snapshot = &types.VirtualMachineSnapshotInfo{}
  2462  		}
  2463  
  2464  		snapshot := &VirtualMachineSnapshot{}
  2465  		snapshot.Vm = vm.Reference()
  2466  		snapshot.Config = *vm.Config
  2467  		snapshot.DataSets = copyDataSetsForVmClone(vm.DataSets)
  2468  
  2469  		ctx.Map.Put(snapshot)
  2470  
  2471  		treeItem := types.VirtualMachineSnapshotTree{
  2472  			Snapshot:        snapshot.Self,
  2473  			Vm:              snapshot.Vm,
  2474  			Name:            req.Name,
  2475  			Description:     req.Description,
  2476  			Id:              atomic.AddInt32(&vm.sid, 1),
  2477  			CreateTime:      time.Now(),
  2478  			State:           vm.Runtime.PowerState,
  2479  			Quiesced:        req.Quiesce,
  2480  			BackupManifest:  "",
  2481  			ReplaySupported: types.NewBool(false),
  2482  		}
  2483  
  2484  		cur := vm.Snapshot.CurrentSnapshot
  2485  		if cur != nil {
  2486  			parent := ctx.Map.Get(*cur).(*VirtualMachineSnapshot)
  2487  			parent.ChildSnapshot = append(parent.ChildSnapshot, snapshot.Self)
  2488  
  2489  			ss := findSnapshotInTree(vm.Snapshot.RootSnapshotList, *cur)
  2490  			ss.ChildSnapshotList = append(ss.ChildSnapshotList, treeItem)
  2491  		} else {
  2492  			changes = append(changes, types.PropertyChange{
  2493  				Name: "snapshot.rootSnapshotList",
  2494  				Val:  append(vm.Snapshot.RootSnapshotList, treeItem),
  2495  			})
  2496  			changes = append(changes, types.PropertyChange{
  2497  				Name: "rootSnapshot",
  2498  				Val:  append(vm.RootSnapshot, treeItem.Snapshot),
  2499  			})
  2500  		}
  2501  
  2502  		snapshot.createSnapshotFiles()
  2503  
  2504  		changes = append(changes, types.PropertyChange{Name: "snapshot.currentSnapshot", Val: snapshot.Self})
  2505  		ctx.Map.Update(vm, changes)
  2506  
  2507  		return snapshot.Self, nil
  2508  	})
  2509  
  2510  	return &methods.CreateSnapshot_TaskBody{
  2511  		Res: &types.CreateSnapshot_TaskResponse{
  2512  			Returnval: task.Run(ctx),
  2513  		},
  2514  	}
  2515  }
  2516  
  2517  func (vm *VirtualMachine) RevertToCurrentSnapshotTask(ctx *Context, req *types.RevertToCurrentSnapshot_Task) soap.HasFault {
  2518  	body := &methods.RevertToCurrentSnapshot_TaskBody{}
  2519  
  2520  	if vm.Snapshot == nil || vm.Snapshot.CurrentSnapshot == nil {
  2521  		body.Fault_ = Fault("snapshot not found", &types.NotFound{})
  2522  
  2523  		return body
  2524  	}
  2525  	snapshot := ctx.Map.Get(*vm.Snapshot.CurrentSnapshot).(*VirtualMachineSnapshot)
  2526  
  2527  	task := CreateTask(vm, "revertSnapshot", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2528  		vm.DataSets = copyDataSetsForVmClone(snapshot.DataSets)
  2529  		return nil, nil
  2530  	})
  2531  
  2532  	body.Res = &types.RevertToCurrentSnapshot_TaskResponse{
  2533  		Returnval: task.Run(ctx),
  2534  	}
  2535  
  2536  	return body
  2537  }
  2538  
  2539  func (vm *VirtualMachine) RemoveAllSnapshotsTask(ctx *Context, req *types.RemoveAllSnapshots_Task) soap.HasFault {
  2540  	task := CreateTask(vm, "RemoveAllSnapshots", func(t *Task) (types.AnyType, types.BaseMethodFault) {
  2541  		if vm.Snapshot == nil {
  2542  			return nil, nil
  2543  		}
  2544  
  2545  		refs := allSnapshotsInTree(vm.Snapshot.RootSnapshotList)
  2546  
  2547  		ctx.Map.Update(vm, []types.PropertyChange{
  2548  			{Name: "snapshot", Val: nil},
  2549  			{Name: "rootSnapshot", Val: nil},
  2550  		})
  2551  
  2552  		for _, ref := range refs {
  2553  			ctx.Map.Get(ref).(*VirtualMachineSnapshot).removeSnapshotFiles(ctx)
  2554  			ctx.Map.Remove(ctx, ref)
  2555  		}
  2556  
  2557  		return nil, nil
  2558  	})
  2559  
  2560  	return &methods.RemoveAllSnapshots_TaskBody{
  2561  		Res: &types.RemoveAllSnapshots_TaskResponse{
  2562  			Returnval: task.Run(ctx),
  2563  		},
  2564  	}
  2565  }
  2566  
  2567  func (vm *VirtualMachine) ShutdownGuest(ctx *Context, c *types.ShutdownGuest) soap.HasFault {
  2568  	r := &methods.ShutdownGuestBody{}
  2569  
  2570  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  2571  		r.Fault_ = Fault("", &types.InvalidPowerState{
  2572  			RequestedState: types.VirtualMachinePowerStatePoweredOn,
  2573  			ExistingState:  vm.Runtime.PowerState,
  2574  		})
  2575  
  2576  		return r
  2577  	}
  2578  
  2579  	event := vm.event()
  2580  	ctx.postEvent(&types.VmGuestShutdownEvent{VmEvent: event})
  2581  
  2582  	_ = CreateTask(vm, "shutdownGuest", func(*Task) (types.AnyType, types.BaseMethodFault) {
  2583  		vm.svm.stop(ctx)
  2584  
  2585  		ctx.Map.Update(vm, []types.PropertyChange{
  2586  			{Name: "runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
  2587  			{Name: "summary.runtime.powerState", Val: types.VirtualMachinePowerStatePoweredOff},
  2588  		})
  2589  
  2590  		ctx.postEvent(&types.VmPoweredOffEvent{VmEvent: event})
  2591  
  2592  		return nil, nil
  2593  	}).Run(ctx)
  2594  
  2595  	r.Res = new(types.ShutdownGuestResponse)
  2596  
  2597  	return r
  2598  }
  2599  
  2600  func (vm *VirtualMachine) StandbyGuest(ctx *Context, c *types.StandbyGuest) soap.HasFault {
  2601  	r := &methods.StandbyGuestBody{}
  2602  
  2603  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOn {
  2604  		r.Fault_ = Fault("", &types.InvalidPowerState{
  2605  			RequestedState: types.VirtualMachinePowerStatePoweredOn,
  2606  			ExistingState:  vm.Runtime.PowerState,
  2607  		})
  2608  
  2609  		return r
  2610  	}
  2611  
  2612  	event := vm.event()
  2613  	ctx.postEvent(&types.VmGuestStandbyEvent{VmEvent: event})
  2614  
  2615  	_ = CreateTask(vm, "standbyGuest", func(*Task) (types.AnyType, types.BaseMethodFault) {
  2616  		vm.svm.pause(ctx)
  2617  
  2618  		ctx.Map.Update(vm, []types.PropertyChange{
  2619  			{Name: "runtime.powerState", Val: types.VirtualMachinePowerStateSuspended},
  2620  			{Name: "summary.runtime.powerState", Val: types.VirtualMachinePowerStateSuspended},
  2621  		})
  2622  
  2623  		ctx.postEvent(&types.VmSuspendedEvent{VmEvent: event})
  2624  
  2625  		return nil, nil
  2626  	}).Run(ctx)
  2627  
  2628  	r.Res = new(types.StandbyGuestResponse)
  2629  
  2630  	return r
  2631  }
  2632  
  2633  func (vm *VirtualMachine) MarkAsTemplate(req *types.MarkAsTemplate) soap.HasFault {
  2634  	r := &methods.MarkAsTemplateBody{}
  2635  
  2636  	if vm.Config.Template {
  2637  		r.Fault_ = Fault("", new(types.NotSupported))
  2638  		return r
  2639  	}
  2640  
  2641  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOff {
  2642  		r.Fault_ = Fault("", &types.InvalidPowerState{
  2643  			RequestedState: types.VirtualMachinePowerStatePoweredOff,
  2644  			ExistingState:  vm.Runtime.PowerState,
  2645  		})
  2646  		return r
  2647  	}
  2648  
  2649  	vm.Config.Template = true
  2650  	vm.Summary.Config.Template = true
  2651  	vm.ResourcePool = nil
  2652  
  2653  	r.Res = new(types.MarkAsTemplateResponse)
  2654  
  2655  	return r
  2656  }
  2657  
  2658  func (vm *VirtualMachine) MarkAsVirtualMachine(req *types.MarkAsVirtualMachine) soap.HasFault {
  2659  	r := &methods.MarkAsVirtualMachineBody{}
  2660  
  2661  	if !vm.Config.Template {
  2662  		r.Fault_ = Fault("", new(types.NotSupported))
  2663  		return r
  2664  	}
  2665  
  2666  	if vm.Runtime.PowerState != types.VirtualMachinePowerStatePoweredOff {
  2667  		r.Fault_ = Fault("", &types.InvalidPowerState{
  2668  			RequestedState: types.VirtualMachinePowerStatePoweredOff,
  2669  			ExistingState:  vm.Runtime.PowerState,
  2670  		})
  2671  		return r
  2672  	}
  2673  
  2674  	vm.Config.Template = false
  2675  	vm.Summary.Config.Template = false
  2676  	vm.ResourcePool = &req.Pool
  2677  	if req.Host != nil {
  2678  		vm.Runtime.Host = req.Host
  2679  	}
  2680  
  2681  	r.Res = new(types.MarkAsVirtualMachineResponse)
  2682  
  2683  	return r
  2684  }
  2685  
  2686  func findSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.VirtualMachineSnapshotTree {
  2687  	if tree == nil {
  2688  		return nil
  2689  	}
  2690  
  2691  	for i, ss := range tree {
  2692  		if ss.Snapshot == ref {
  2693  			return &tree[i]
  2694  		}
  2695  
  2696  		target := findSnapshotInTree(ss.ChildSnapshotList, ref)
  2697  		if target != nil {
  2698  			return target
  2699  		}
  2700  	}
  2701  
  2702  	return nil
  2703  }
  2704  
  2705  func findParentSnapshot(tree types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.ManagedObjectReference {
  2706  	for _, ss := range tree.ChildSnapshotList {
  2707  		if ss.Snapshot == ref {
  2708  			return &tree.Snapshot
  2709  		}
  2710  
  2711  		res := findParentSnapshot(ss, ref)
  2712  		if res != nil {
  2713  			return res
  2714  		}
  2715  	}
  2716  
  2717  	return nil
  2718  }
  2719  
  2720  func findParentSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference) *types.ManagedObjectReference {
  2721  	if tree == nil {
  2722  		return nil
  2723  	}
  2724  
  2725  	for _, ss := range tree {
  2726  		res := findParentSnapshot(ss, ref)
  2727  		if res != nil {
  2728  			return res
  2729  		}
  2730  	}
  2731  
  2732  	return nil
  2733  }
  2734  
  2735  func removeSnapshotInTree(tree []types.VirtualMachineSnapshotTree, ref types.ManagedObjectReference, removeChildren bool) []types.VirtualMachineSnapshotTree {
  2736  	if tree == nil {
  2737  		return tree
  2738  	}
  2739  
  2740  	var result []types.VirtualMachineSnapshotTree
  2741  
  2742  	for _, ss := range tree {
  2743  		if ss.Snapshot == ref {
  2744  			if !removeChildren {
  2745  				result = append(result, ss.ChildSnapshotList...)
  2746  			}
  2747  		} else {
  2748  			ss.ChildSnapshotList = removeSnapshotInTree(ss.ChildSnapshotList, ref, removeChildren)
  2749  			result = append(result, ss)
  2750  		}
  2751  	}
  2752  
  2753  	return result
  2754  }
  2755  
  2756  func allSnapshotsInTree(tree []types.VirtualMachineSnapshotTree) []types.ManagedObjectReference {
  2757  	var result []types.ManagedObjectReference
  2758  
  2759  	if tree == nil {
  2760  		return result
  2761  	}
  2762  
  2763  	for _, ss := range tree {
  2764  		result = append(result, ss.Snapshot)
  2765  		result = append(result, allSnapshotsInTree(ss.ChildSnapshotList)...)
  2766  	}
  2767  
  2768  	return result
  2769  }
  2770  
  2771  func changeTrackingSupported(spec *types.VirtualMachineConfigSpec) bool {
  2772  	for _, device := range spec.DeviceChange {
  2773  		if dev, ok := device.GetVirtualDeviceConfigSpec().Device.(*types.VirtualDisk); ok {
  2774  			switch dev.Backing.(type) {
  2775  			case *types.VirtualDiskFlatVer2BackingInfo:
  2776  				return true
  2777  			case *types.VirtualDiskSparseVer2BackingInfo:
  2778  				return true
  2779  			case *types.VirtualDiskRawDiskMappingVer1BackingInfo:
  2780  				return true
  2781  			case *types.VirtualDiskRawDiskVer2BackingInfo:
  2782  				return true
  2783  			default:
  2784  				return false
  2785  			}
  2786  		}
  2787  	}
  2788  	return false
  2789  }
  2790  
  2791  func (vm *VirtualMachine) updateLastModifiedAndChangeVersion(ctx *Context) {
  2792  	modified := time.Now()
  2793  	ctx.Map.Update(vm, []types.PropertyChange{
  2794  		{
  2795  			Name: "config.changeVersion",
  2796  			Val:  fmt.Sprintf("%d", modified.UnixNano()),
  2797  			Op:   types.PropertyChangeOpAssign,
  2798  		},
  2799  		{
  2800  			Name: "config.modified",
  2801  			Val:  modified,
  2802  			Op:   types.PropertyChangeOpAssign,
  2803  		},
  2804  	})
  2805  }