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