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 }