github.com/vmware/govmomi@v0.43.0/simulator/virtual_machine_test.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 "context" 21 "fmt" 22 "math/rand" 23 "os" 24 "reflect" 25 "testing" 26 "time" 27 28 "github.com/stretchr/testify/assert" 29 30 "github.com/vmware/govmomi" 31 "github.com/vmware/govmomi/find" 32 "github.com/vmware/govmomi/object" 33 "github.com/vmware/govmomi/property" 34 "github.com/vmware/govmomi/simulator/esx" 35 "github.com/vmware/govmomi/task" 36 "github.com/vmware/govmomi/vim25" 37 "github.com/vmware/govmomi/vim25/mo" 38 "github.com/vmware/govmomi/vim25/types" 39 ) 40 41 func TestCreateVm(t *testing.T) { 42 ctx := context.Background() 43 44 for _, model := range []*Model{ESX(), VPX()} { 45 defer model.Remove() 46 err := model.Create() 47 if err != nil { 48 t.Fatal(err) 49 } 50 51 s := model.Service.NewServer() 52 defer s.Close() 53 54 c, err := govmomi.NewClient(ctx, s.URL, true) 55 if err != nil { 56 t.Fatal(err) 57 } 58 59 p := property.DefaultCollector(c.Client) 60 61 finder := find.NewFinder(c.Client, false) 62 63 dc, err := finder.DefaultDatacenter(ctx) 64 if err != nil { 65 t.Fatal(err) 66 } 67 68 finder.SetDatacenter(dc) 69 70 folders, err := dc.Folders(ctx) 71 if err != nil { 72 t.Fatal(err) 73 } 74 75 ds, err := finder.DefaultDatastore(ctx) 76 if err != nil { 77 t.Fatal(err) 78 } 79 80 hosts, err := finder.HostSystemList(ctx, "*/*") 81 if err != nil { 82 t.Fatal(err) 83 } 84 85 nhosts := len(hosts) 86 host := hosts[rand.Intn(nhosts)] 87 pool, err := host.ResourcePool(ctx) 88 if err != nil { 89 t.Fatal(err) 90 } 91 92 if nhosts == 1 { 93 // test the default path against the ESX model 94 host = nil 95 } 96 97 vmFolder := folders.VmFolder 98 99 var vmx string 100 101 spec := types.VirtualMachineConfigSpec{ 102 // Note: real ESX allows the VM to be created without a GuestId, 103 // but will power on will fail. 104 GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest), 105 } 106 107 steps := []func(){ 108 func() { 109 spec.Name = "test" 110 vmx = fmt.Sprintf("%s/%s.vmx", spec.Name, spec.Name) 111 }, 112 func() { 113 spec.Files = &types.VirtualMachineFileInfo{ 114 VmPathName: fmt.Sprintf("[%s] %s", ds.Name(), vmx), 115 } 116 }, 117 } 118 119 // expecting CreateVM to fail until all steps are taken 120 for _, step := range steps { 121 task, cerr := vmFolder.CreateVM(ctx, spec, pool, host) 122 if cerr != nil { 123 t.Fatal(err) 124 } 125 126 cerr = task.Wait(ctx) 127 if cerr == nil { 128 t.Error("expected error") 129 } 130 131 step() 132 } 133 134 task, err := vmFolder.CreateVM(ctx, spec, pool, host) 135 if err != nil { 136 t.Fatal(err) 137 } 138 139 info, err := task.WaitForResult(ctx, nil) 140 if err != nil { 141 t.Fatal(err) 142 } 143 144 // Test that datastore files were created 145 _, err = ds.Stat(ctx, vmx) 146 if err != nil { 147 t.Fatal(err) 148 } 149 150 vm := object.NewVirtualMachine(c.Client, info.Result.(types.ManagedObjectReference)) 151 152 name, err := vm.ObjectName(ctx) 153 if err != nil { 154 t.Fatal(err) 155 } 156 157 if name != spec.Name { 158 t.Errorf("name=%s", name) 159 } 160 161 _, err = vm.Device(ctx) 162 if err != nil { 163 t.Fatal(err) 164 } 165 166 recreate := func(context.Context) (*object.Task, error) { 167 return vmFolder.CreateVM(ctx, spec, pool, nil) 168 } 169 170 ops := []struct { 171 method func(context.Context) (*object.Task, error) 172 state types.VirtualMachinePowerState 173 fail bool 174 }{ 175 // Powered off by default 176 {nil, types.VirtualMachinePowerStatePoweredOff, false}, 177 // Create with same .vmx path should fail 178 {recreate, "", true}, 179 // Off -> On == ok 180 {vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, false}, 181 // On -> On == fail 182 {vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, true}, 183 // On -> Off == ok 184 {vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, false}, 185 // Off -> Off == fail 186 {vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, true}, 187 // Off -> On == ok 188 {vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, false}, 189 // Destroy == fail (power is On) 190 {vm.Destroy, types.VirtualMachinePowerStatePoweredOn, true}, 191 // On -> Off == ok 192 {vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, false}, 193 // Off -> Reset == fail 194 {vm.Reset, types.VirtualMachinePowerStatePoweredOff, true}, 195 // Off -> On == ok 196 {vm.PowerOn, types.VirtualMachinePowerStatePoweredOn, false}, 197 // On -> Reset == ok 198 {vm.Reset, types.VirtualMachinePowerStatePoweredOn, false}, 199 // On -> Suspend == ok 200 {vm.Suspend, types.VirtualMachinePowerStateSuspended, false}, 201 // On -> Off == ok 202 {vm.PowerOff, types.VirtualMachinePowerStatePoweredOff, false}, 203 // Destroy == ok (power is Off) 204 {vm.Destroy, "", false}, 205 } 206 207 for i, op := range ops { 208 if op.method != nil { 209 task, err = op.method(ctx) 210 if err != nil { 211 t.Fatal(err) 212 } 213 214 err = task.Wait(ctx) 215 if op.fail { 216 if err == nil { 217 t.Errorf("%d: expected error", i) 218 } 219 } else { 220 if err != nil { 221 t.Errorf("%d: %s", i, err) 222 } 223 } 224 } 225 226 if len(op.state) != 0 { 227 state, err := vm.PowerState(ctx) 228 if err != nil { 229 t.Fatal(err) 230 } 231 232 if state != op.state { 233 t.Errorf("state=%s", state) 234 } 235 236 err = property.Wait(ctx, p, vm.Reference(), []string{object.PropRuntimePowerState}, func(pc []types.PropertyChange) bool { 237 for _, c := range pc { 238 switch v := c.Val.(type) { 239 case types.VirtualMachinePowerState: 240 if v != op.state { 241 t.Errorf("state=%s", v) 242 } 243 default: 244 t.Errorf("unexpected type %T", v) 245 } 246 247 } 248 return true 249 }) 250 if err != nil { 251 t.Error(err) 252 } 253 254 running, err := vm.IsToolsRunning(ctx) 255 if err != nil { 256 t.Error(err) 257 } 258 if running { 259 t.Error("tools running") 260 } 261 } 262 } 263 264 // Test that datastore files were removed 265 _, err = ds.Stat(ctx, vmx) 266 if err == nil { 267 t.Error("expected error") 268 } 269 } 270 } 271 272 func TestCreateVmWithSpecialCharaters(t *testing.T) { 273 tests := []struct { 274 name string 275 expected string 276 }{ 277 {`/`, `%2f`}, 278 {`\`, `%5c`}, 279 {`%`, `%25`}, 280 // multiple special characters 281 {`%%`, `%25%25`}, 282 // slash-separated name 283 {`foo/bar`, `foo%2fbar`}, 284 } 285 286 for _, test := range tests { 287 m := ESX() 288 289 Test(func(ctx context.Context, c *vim25.Client) { 290 finder := find.NewFinder(c, false) 291 292 dc, err := finder.DefaultDatacenter(ctx) 293 if err != nil { 294 t.Fatal(err) 295 } 296 297 finder.SetDatacenter(dc) 298 folders, err := dc.Folders(ctx) 299 if err != nil { 300 t.Fatal(err) 301 } 302 vmFolder := folders.VmFolder 303 304 ds, err := finder.DefaultDatastore(ctx) 305 if err != nil { 306 t.Fatal(err) 307 } 308 309 spec := types.VirtualMachineConfigSpec{ 310 Name: test.name, 311 Files: &types.VirtualMachineFileInfo{ 312 VmPathName: fmt.Sprintf("[%s]", ds.Name()), 313 }, 314 } 315 316 pool := object.NewResourcePool(c, esx.ResourcePool.Self) 317 318 task, err := vmFolder.CreateVM(ctx, spec, pool, nil) 319 if err != nil { 320 t.Fatal(err) 321 } 322 323 info, err := task.WaitForResult(ctx, nil) 324 if err != nil { 325 t.Fatal(err) 326 } 327 328 vm := object.NewVirtualMachine(c, info.Result.(types.ManagedObjectReference)) 329 name, err := vm.ObjectName(ctx) 330 if err != nil { 331 t.Fatal(err) 332 } 333 if name != test.expected { 334 t.Errorf("expected %s, got %s", test.expected, name) 335 } 336 }, m) 337 } 338 } 339 340 func TestCloneVm(t *testing.T) { 341 tests := []struct { 342 name string 343 vmName string 344 config types.VirtualMachineCloneSpec 345 fail bool 346 }{ 347 { 348 "clone a vm", 349 "cloned-vm", 350 types.VirtualMachineCloneSpec{ 351 Template: false, 352 PowerOn: false, 353 }, 354 false, 355 }, 356 { 357 "vm name is duplicated", 358 "DC0_H0_VM0", 359 types.VirtualMachineCloneSpec{ 360 Template: false, 361 PowerOn: false, 362 }, 363 true, 364 }, 365 } 366 367 for _, test := range tests { 368 test := test // assign to local var since loop var is reused 369 370 t.Run(test.name, func(t *testing.T) { 371 m := VPX() 372 defer m.Remove() 373 374 Test(func(ctx context.Context, c *vim25.Client) { 375 finder := find.NewFinder(c, false) 376 dc, err := finder.DefaultDatacenter(ctx) 377 if err != nil { 378 t.Fatal(err) 379 } 380 381 folders, err := dc.Folders(ctx) 382 if err != nil { 383 t.Fatal(err) 384 } 385 386 vmFolder := folders.VmFolder 387 388 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 389 vm := object.NewVirtualMachine(c, vmm.Reference()) 390 391 task, err := vm.Clone(ctx, vmFolder, test.vmName, test.config) 392 if err != nil { 393 t.Fatal(err) 394 } 395 396 err = task.Wait(ctx) 397 if test.fail { 398 if err == nil { 399 t.Errorf("%s: expected error", test.name) 400 } 401 } else { 402 if err != nil { 403 t.Errorf("%s: %s", test.name, err) 404 } 405 } 406 }, m) 407 }) 408 } 409 } 410 411 func TestReconfigVmDevice(t *testing.T) { 412 ctx := context.Background() 413 414 m := ESX() 415 defer m.Remove() 416 err := m.Create() 417 if err != nil { 418 t.Fatal(err) 419 } 420 421 s := m.Service.NewServer() 422 defer s.Close() 423 424 c, err := govmomi.NewClient(ctx, s.URL, true) 425 if err != nil { 426 t.Fatal(err) 427 } 428 429 finder := find.NewFinder(c.Client, false) 430 finder.SetDatacenter(object.NewDatacenter(c.Client, esx.Datacenter.Reference())) 431 432 vms, err := finder.VirtualMachineList(ctx, "*") 433 if err != nil { 434 t.Fatal(err) 435 } 436 437 vm := vms[0] 438 device, err := vm.Device(ctx) 439 if err != nil { 440 t.Fatal(err) 441 } 442 443 // verify default device list 444 _, err = device.FindIDEController("") 445 if err != nil { 446 t.Fatal(err) 447 } 448 449 // default list of devices + 1 NIC + 1 SCSI controller + 1 CDROM + 1 disk created by the Model 450 mdevices := len(esx.VirtualDevice) + 4 451 452 if len(device) != mdevices { 453 t.Errorf("expected %d devices, got %d", mdevices, len(device)) 454 } 455 456 d := device.FindByKey(esx.EthernetCard.Key) 457 458 err = vm.AddDevice(ctx, d) 459 if _, ok := err.(task.Error).Fault().(*types.InvalidDeviceSpec); !ok { 460 t.Fatalf("err=%v", err) 461 } 462 463 err = vm.RemoveDevice(ctx, false, d) 464 if err != nil { 465 t.Fatal(err) 466 } 467 468 device, err = vm.Device(ctx) 469 if err != nil { 470 t.Fatal(err) 471 } 472 473 if len(device) != mdevices-1 { 474 t.Error("device list mismatch") 475 } 476 477 // cover the path where the simulator assigns a UnitNumber 478 d.GetVirtualDevice().UnitNumber = nil 479 // cover the path where the simulator assigns a Key 480 d.GetVirtualDevice().Key = -1 481 482 err = vm.AddDevice(ctx, d) 483 if err != nil { 484 t.Fatal(err) 485 } 486 487 device, err = vm.Device(ctx) 488 if err != nil { 489 t.Fatal(err) 490 } 491 492 if len(device) != mdevices { 493 t.Error("device list mismatch") 494 } 495 496 disks := device.SelectByType((*types.VirtualDisk)(nil)) 497 498 for _, d := range disks { 499 disk := d.(*types.VirtualDisk) 500 info := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo) 501 502 if info.Datastore.Type == "" || info.Datastore.Value == "" { 503 t.Errorf("invalid datastore for %s", device.Name(d)) 504 } 505 506 // RemoveDevice and keep the file backing 507 if err = vm.RemoveDevice(ctx, true, d); err != nil { 508 t.Error(err) 509 } 510 511 if err = vm.AddDevice(ctx, d); err == nil { 512 t.Error("expected FileExists error") 513 } 514 515 // Need FileOperation=="" to add an existing disk, see object.VirtualMachine.configureDevice 516 disk.CapacityInKB = 0 517 disk.CapacityInBytes = 0 518 if err = vm.AddDevice(ctx, d); err != nil { 519 t.Error(err) 520 } 521 522 d.GetVirtualDevice().DeviceInfo = nil 523 if err = vm.EditDevice(ctx, d); err != nil { 524 t.Error(err) 525 } 526 527 // RemoveDevice and delete the file backing 528 if err = vm.RemoveDevice(ctx, false, d); err != nil { 529 t.Error(err) 530 } 531 532 if err = vm.AddDevice(ctx, d); err == nil { 533 t.Error("expected FileNotFound error") 534 } 535 } 536 } 537 538 func TestConnectVmDevice(t *testing.T) { 539 ctx := context.Background() 540 541 m := ESX() 542 defer m.Remove() 543 err := m.Create() 544 if err != nil { 545 t.Fatal(err) 546 } 547 548 s := m.Service.NewServer() 549 defer s.Close() 550 551 c, err := govmomi.NewClient(ctx, s.URL, true) 552 if err != nil { 553 t.Fatal(err) 554 } 555 556 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 557 vm := object.NewVirtualMachine(c.Client, vmm.Reference()) 558 559 l := object.VirtualDeviceList{} // used only for Connect/Disconnect function 560 561 tests := []struct { 562 description string 563 changePower func(context.Context) (*object.Task, error) 564 changeConnectivity func(types.BaseVirtualDevice) error 565 expectedConnected bool 566 expectedStartConnected bool 567 }{ 568 {"disconnect when vm is on", nil, l.Disconnect, false, false}, 569 {"connect when vm is on", nil, l.Connect, true, true}, 570 {"power off vm", vm.PowerOff, nil, false, true}, 571 {"disconnect when vm is off", nil, l.Disconnect, false, false}, 572 {"connect when vm is off", nil, l.Connect, false, true}, 573 {"power on vm when StartConnected is true", vm.PowerOn, nil, true, true}, 574 {"power off vm and disconnect again", vm.PowerOff, l.Disconnect, false, false}, 575 {"power on vm when StartConnected is false", vm.PowerOn, nil, false, false}, 576 } 577 578 for _, testCase := range tests { 579 testCase := testCase // assign to local var since loop var is reused 580 581 t.Run(testCase.description, func(t *testing.T) { 582 if testCase.changePower != nil { 583 task, err := testCase.changePower(ctx) 584 if err != nil { 585 t.Fatal(err) 586 } 587 588 err = task.Wait(ctx) 589 if err != nil { 590 t.Fatal(err) 591 } 592 } 593 594 if testCase.changeConnectivity != nil { 595 list, err := vm.Device(ctx) 596 if err != nil { 597 t.Fatal(err) 598 } 599 device := list.FindByKey(esx.EthernetCard.Key) 600 if device == nil { 601 t.Fatal("cloud not find EthernetCard") 602 } 603 604 err = testCase.changeConnectivity(device) 605 if err != nil { 606 t.Fatal(err) 607 } 608 err = vm.EditDevice(ctx, device) 609 if err != nil { 610 t.Fatal(err) 611 } 612 } 613 614 updatedList, err := vm.Device(ctx) 615 if err != nil { 616 t.Fatal(err) 617 } 618 updatedDevice := updatedList.FindByKey(esx.EthernetCard.Key) 619 if updatedDevice == nil { 620 t.Fatal("cloud not find EthernetCard") 621 } 622 conn := updatedDevice.GetVirtualDevice().Connectable 623 624 if conn.Connected != testCase.expectedConnected { 625 t.Errorf("unexpected Connected property. expected: %t, actual: %t", 626 testCase.expectedConnected, conn.Connected) 627 } 628 if conn.StartConnected != testCase.expectedStartConnected { 629 t.Errorf("unexpected StartConnected property. expected: %t, actual: %t", 630 testCase.expectedStartConnected, conn.StartConnected) 631 } 632 }) 633 } 634 } 635 636 func TestVAppConfigAdd(t *testing.T) { 637 ctx := context.Background() 638 639 m := ESX() 640 defer m.Remove() 641 err := m.Create() 642 if err != nil { 643 t.Fatal(err) 644 } 645 646 s := m.Service.NewServer() 647 defer s.Close() 648 649 c, err := govmomi.NewClient(ctx, s.URL, true) 650 if err != nil { 651 t.Fatal(err) 652 } 653 654 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 655 vm := object.NewVirtualMachine(c.Client, vmm.Reference()) 656 657 tests := []struct { 658 description string 659 expectedErr types.BaseMethodFault 660 spec types.VirtualMachineConfigSpec 661 existingVMConfig *types.VirtualMachineConfigSpec 662 expectedProps []types.VAppPropertyInfo 663 }{ 664 665 { 666 description: "successfully add a new property", 667 spec: types.VirtualMachineConfigSpec{ 668 VAppConfig: &types.VmConfigSpec{ 669 Property: []types.VAppPropertySpec{ 670 { 671 ArrayUpdateSpec: types.ArrayUpdateSpec{ 672 Operation: types.ArrayUpdateOperationAdd, 673 }, 674 Info: &types.VAppPropertyInfo{ 675 Key: int32(1), 676 Id: "foo-id", 677 Value: "foo-value", 678 }, 679 }, 680 }, 681 }, 682 }, 683 expectedProps: []types.VAppPropertyInfo{ 684 { 685 Key: int32(1), 686 Id: "foo-id", 687 Value: "foo-value", 688 }, 689 }, 690 }, 691 { 692 description: "return error when a property that exists is added", 693 expectedErr: new(types.InvalidArgument), 694 existingVMConfig: &types.VirtualMachineConfigSpec{ 695 VAppConfig: &types.VmConfigSpec{ 696 Property: []types.VAppPropertySpec{ 697 { 698 ArrayUpdateSpec: types.ArrayUpdateSpec{ 699 Operation: types.ArrayUpdateOperationAdd, 700 }, 701 Info: &types.VAppPropertyInfo{ 702 Key: int32(2), 703 Id: "foo-id", 704 Value: "foo-value", 705 }, 706 }, 707 }, 708 }, 709 }, 710 spec: types.VirtualMachineConfigSpec{ 711 VAppConfig: &types.VmConfigSpec{ 712 Property: []types.VAppPropertySpec{ 713 { 714 ArrayUpdateSpec: types.ArrayUpdateSpec{ 715 Operation: types.ArrayUpdateOperationAdd, 716 }, 717 Info: &types.VAppPropertyInfo{ 718 Key: int32(2), 719 }, 720 }, 721 }, 722 }, 723 }, 724 }, 725 } 726 727 for _, testCase := range tests { 728 t.Run(testCase.description, func(t *testing.T) { 729 if testCase.existingVMConfig != nil { 730 rtask, _ := vm.Reconfigure(ctx, *testCase.existingVMConfig) 731 if err := rtask.Wait(ctx); err != nil { 732 t.Errorf("Reconfigure failed during test setup. err: %v", err) 733 } 734 } 735 736 err := vmm.updateVAppProperty(testCase.spec.VAppConfig.GetVmConfigSpec()) 737 if !reflect.DeepEqual(err, testCase.expectedErr) { 738 t.Errorf("unexpected error in updating VApp property of VM. expectedErr: %v, actualErr: %v", testCase.expectedErr, err) 739 } 740 741 if testCase.expectedErr == nil { 742 props := vmm.Config.VAppConfig.GetVmConfigInfo().Property 743 // the testcase only has one VApp property, so ordering of the elements does not matter. 744 if !reflect.DeepEqual(props, testCase.expectedProps) { 745 t.Errorf("unexpected VApp properties. expected: %v, actual: %v", testCase.expectedProps, props) 746 } 747 } 748 }) 749 } 750 } 751 752 func TestVAppConfigEdit(t *testing.T) { 753 ctx := context.Background() 754 755 m := ESX() 756 defer m.Remove() 757 err := m.Create() 758 if err != nil { 759 t.Fatal(err) 760 } 761 762 s := m.Service.NewServer() 763 defer s.Close() 764 765 c, err := govmomi.NewClient(ctx, s.URL, true) 766 if err != nil { 767 t.Fatal(err) 768 } 769 770 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 771 vm := object.NewVirtualMachine(c.Client, vmm.Reference()) 772 773 tests := []struct { 774 description string 775 expectedErr types.BaseMethodFault 776 spec types.VirtualMachineConfigSpec 777 existingVMConfig *types.VirtualMachineConfigSpec 778 expectedProps []types.VAppPropertyInfo 779 }{ 780 781 { 782 description: "successfully update a property that exists", 783 existingVMConfig: &types.VirtualMachineConfigSpec{ 784 VAppConfig: &types.VmConfigSpec{ 785 Property: []types.VAppPropertySpec{ 786 { 787 ArrayUpdateSpec: types.ArrayUpdateSpec{ 788 Operation: types.ArrayUpdateOperationAdd, 789 }, 790 Info: &types.VAppPropertyInfo{ 791 Key: int32(1), 792 Id: "foo-id", 793 Value: "foo-value", 794 }, 795 }, 796 }, 797 }, 798 }, 799 spec: types.VirtualMachineConfigSpec{ 800 VAppConfig: &types.VmConfigSpec{ 801 Property: []types.VAppPropertySpec{ 802 { 803 ArrayUpdateSpec: types.ArrayUpdateSpec{ 804 Operation: types.ArrayUpdateOperationEdit, 805 }, 806 Info: &types.VAppPropertyInfo{ 807 Key: int32(1), 808 Id: "foo-id-updated", 809 Value: "foo-value-updated", 810 }, 811 }, 812 }, 813 }, 814 }, 815 expectedProps: []types.VAppPropertyInfo{ 816 { 817 Key: int32(1), 818 Id: "foo-id-updated", 819 Value: "foo-value-updated", 820 }, 821 }, 822 }, 823 { 824 description: "return error when a property that doesn't exist is updated", 825 expectedErr: new(types.InvalidArgument), 826 spec: types.VirtualMachineConfigSpec{ 827 VAppConfig: &types.VmConfigSpec{ 828 Property: []types.VAppPropertySpec{ 829 { 830 ArrayUpdateSpec: types.ArrayUpdateSpec{ 831 Operation: types.ArrayUpdateOperationEdit, 832 }, 833 Info: &types.VAppPropertyInfo{ 834 Key: int32(2), 835 }, 836 }, 837 }, 838 }, 839 }, 840 }, 841 } 842 843 for _, testCase := range tests { 844 t.Run(testCase.description, func(t *testing.T) { 845 if testCase.existingVMConfig != nil { 846 rtask, _ := vm.Reconfigure(ctx, *testCase.existingVMConfig) 847 if err := rtask.Wait(ctx); err != nil { 848 t.Errorf("Reconfigure failed during test setup. err: %v", err) 849 } 850 } 851 852 err := vmm.updateVAppProperty(testCase.spec.VAppConfig.GetVmConfigSpec()) 853 if !reflect.DeepEqual(err, testCase.expectedErr) { 854 t.Errorf("unexpected error in updating VApp property of VM. expectedErr: %v, actualErr: %v", testCase.expectedErr, err) 855 } 856 857 if testCase.expectedErr == nil { 858 props := vmm.Config.VAppConfig.GetVmConfigInfo().Property 859 // the testcase only has one VApp property, so ordering of the elements does not matter. 860 if !reflect.DeepEqual(props, testCase.expectedProps) { 861 t.Errorf("unexpected VApp properties. expected: %v, actual: %v", testCase.expectedProps, props) 862 } 863 } 864 }) 865 } 866 } 867 868 func TestVAppConfigRemove(t *testing.T) { 869 ctx := context.Background() 870 871 m := ESX() 872 defer m.Remove() 873 err := m.Create() 874 if err != nil { 875 t.Fatal(err) 876 } 877 878 s := m.Service.NewServer() 879 defer s.Close() 880 881 c, err := govmomi.NewClient(ctx, s.URL, true) 882 if err != nil { 883 t.Fatal(err) 884 } 885 886 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 887 vm := object.NewVirtualMachine(c.Client, vmm.Reference()) 888 889 tests := []struct { 890 description string 891 expectedErr types.BaseMethodFault 892 spec types.VirtualMachineConfigSpec 893 existingVMConfig *types.VirtualMachineConfigSpec 894 expectedProps []types.VAppPropertyInfo 895 }{ 896 { 897 description: "returns success when a property that exists is removed", 898 existingVMConfig: &types.VirtualMachineConfigSpec{ 899 VAppConfig: &types.VmConfigSpec{ 900 Property: []types.VAppPropertySpec{ 901 { 902 ArrayUpdateSpec: types.ArrayUpdateSpec{ 903 Operation: types.ArrayUpdateOperationAdd, 904 }, 905 Info: &types.VAppPropertyInfo{ 906 Key: int32(1), 907 }, 908 }, 909 }, 910 }, 911 }, 912 spec: types.VirtualMachineConfigSpec{ 913 VAppConfig: &types.VmConfigSpec{ 914 Property: []types.VAppPropertySpec{ 915 { 916 ArrayUpdateSpec: types.ArrayUpdateSpec{ 917 Operation: types.ArrayUpdateOperationRemove, 918 }, 919 Info: &types.VAppPropertyInfo{ 920 Key: int32(1), 921 }, 922 }, 923 }, 924 }, 925 }, 926 expectedProps: []types.VAppPropertyInfo{}, 927 }, 928 { 929 description: "return error when a property that doesn't exist is removed", 930 expectedErr: new(types.InvalidArgument), 931 spec: types.VirtualMachineConfigSpec{ 932 VAppConfig: &types.VmConfigSpec{ 933 Property: []types.VAppPropertySpec{ 934 { 935 ArrayUpdateSpec: types.ArrayUpdateSpec{ 936 Operation: types.ArrayUpdateOperationRemove, 937 }, 938 Info: &types.VAppPropertyInfo{ 939 Key: int32(2), 940 }, 941 }, 942 }, 943 }, 944 }, 945 }, 946 } 947 948 for _, testCase := range tests { 949 t.Run(testCase.description, func(t *testing.T) { 950 if testCase.existingVMConfig != nil { 951 rtask, _ := vm.Reconfigure(ctx, *testCase.existingVMConfig) 952 if err := rtask.Wait(ctx); err != nil { 953 t.Errorf("Reconfigure failed during test setup. err: %v", err) 954 } 955 } 956 957 err := vmm.updateVAppProperty(testCase.spec.VAppConfig.GetVmConfigSpec()) 958 if !reflect.DeepEqual(err, testCase.expectedErr) { 959 t.Errorf("unexpected error in updating VApp property of VM. expectedErr: %v, actualErr: %v", testCase.expectedErr, err) 960 } 961 962 if testCase.expectedErr == nil { 963 props := vmm.Config.VAppConfig.GetVmConfigInfo().Property 964 // the testcase only has one VApp property, so ordering of the elements does not matter. 965 if !reflect.DeepEqual(props, testCase.expectedProps) { 966 t.Errorf("unexpected VApp properties. expected: %v, actual: %v", testCase.expectedProps, props) 967 } 968 } 969 }) 970 } 971 } 972 973 func TestReconfigVm(t *testing.T) { 974 ctx := context.Background() 975 976 m := ESX() 977 defer m.Remove() 978 err := m.Create() 979 if err != nil { 980 t.Fatal(err) 981 } 982 983 s := m.Service.NewServer() 984 defer s.Close() 985 986 c, err := govmomi.NewClient(ctx, s.URL, true) 987 if err != nil { 988 t.Fatal(err) 989 } 990 991 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 992 vm := object.NewVirtualMachine(c.Client, vmm.Reference()) 993 994 tests := []struct { 995 fail bool 996 spec types.VirtualMachineConfigSpec 997 }{ 998 { 999 true, types.VirtualMachineConfigSpec{ 1000 CpuAllocation: &types.ResourceAllocationInfo{Reservation: types.NewInt64(-1)}, 1001 }, 1002 }, 1003 { 1004 false, types.VirtualMachineConfigSpec{ 1005 CpuAllocation: &types.ResourceAllocationInfo{Reservation: types.NewInt64(100)}, 1006 }, 1007 }, 1008 { 1009 true, types.VirtualMachineConfigSpec{ 1010 GuestId: "enoent", 1011 }, 1012 }, 1013 { 1014 false, types.VirtualMachineConfigSpec{ 1015 GuestId: string(GuestID[0]), 1016 }, 1017 }, 1018 { 1019 false, types.VirtualMachineConfigSpec{ 1020 NestedHVEnabled: types.NewBool(true), 1021 }, 1022 }, 1023 { 1024 false, types.VirtualMachineConfigSpec{ 1025 CpuHotAddEnabled: types.NewBool(true), 1026 }, 1027 }, 1028 { 1029 false, types.VirtualMachineConfigSpec{ 1030 CpuHotRemoveEnabled: types.NewBool(true), 1031 }, 1032 }, 1033 { 1034 false, types.VirtualMachineConfigSpec{ 1035 GuestAutoLockEnabled: types.NewBool(true), 1036 }, 1037 }, 1038 { 1039 false, types.VirtualMachineConfigSpec{ 1040 MemoryHotAddEnabled: types.NewBool(true), 1041 }, 1042 }, 1043 { 1044 false, types.VirtualMachineConfigSpec{ 1045 MemoryReservationLockedToMax: types.NewBool(true), 1046 }, 1047 }, 1048 { 1049 false, types.VirtualMachineConfigSpec{ 1050 MessageBusTunnelEnabled: types.NewBool(true), 1051 }, 1052 }, 1053 { 1054 false, types.VirtualMachineConfigSpec{ 1055 NpivTemporaryDisabled: types.NewBool(true), 1056 }, 1057 }, 1058 { 1059 false, types.VirtualMachineConfigSpec{ 1060 NpivOnNonRdmDisks: types.NewBool(true), 1061 }, 1062 }, 1063 { 1064 false, types.VirtualMachineConfigSpec{ 1065 ConsolePreferences: &types.VirtualMachineConsolePreferences{ 1066 PowerOnWhenOpened: types.NewBool(true), 1067 }, 1068 }, 1069 }, 1070 { 1071 false, types.VirtualMachineConfigSpec{ 1072 CpuAffinity: &types.VirtualMachineAffinityInfo{ 1073 AffinitySet: []int32{1}, 1074 }, 1075 }, 1076 }, 1077 { 1078 false, types.VirtualMachineConfigSpec{ 1079 CpuAllocation: &types.ResourceAllocationInfo{ 1080 Reservation: types.NewInt64(100), 1081 }, 1082 }, 1083 }, 1084 { 1085 false, types.VirtualMachineConfigSpec{ 1086 MemoryAffinity: &types.VirtualMachineAffinityInfo{ 1087 AffinitySet: []int32{1}, 1088 }, 1089 }, 1090 }, 1091 { 1092 false, types.VirtualMachineConfigSpec{ 1093 MemoryAllocation: &types.ResourceAllocationInfo{ 1094 Reservation: types.NewInt64(100), 1095 }, 1096 }, 1097 }, 1098 { 1099 false, types.VirtualMachineConfigSpec{ 1100 LatencySensitivity: &types.LatencySensitivity{ 1101 Sensitivity: 1, 1102 }, 1103 }, 1104 }, 1105 } 1106 1107 for i, test := range tests { 1108 rtask, _ := vm.Reconfigure(ctx, test.spec) 1109 1110 err := rtask.Wait(ctx) 1111 if test.fail { 1112 if err == nil { 1113 t.Errorf("%d: expected failure", i) 1114 } 1115 } else { 1116 if err != nil { 1117 t.Errorf("unexpected failure: %s", err) 1118 } 1119 } 1120 } 1121 1122 // Verify ReConfig actually works 1123 if *vmm.Config.NestedHVEnabled != true { 1124 t.Errorf("vm.Config.NestedHVEnabled expected true; got false") 1125 } 1126 if *vmm.Config.CpuHotAddEnabled != true { 1127 t.Errorf("vm.Config.CpuHotAddEnabled expected true; got false") 1128 } 1129 if *vmm.Config.CpuHotRemoveEnabled != true { 1130 t.Errorf("vm.Config.CpuHotRemoveEnabled expected true; got false") 1131 } 1132 if *vmm.Config.GuestAutoLockEnabled != true { 1133 t.Errorf("vm.Config.GuestAutoLockEnabled expected true; got false") 1134 } 1135 if *vmm.Config.MemoryHotAddEnabled != true { 1136 t.Errorf("vm.Config.MemoryHotAddEnabled expected true; got false") 1137 } 1138 if *vmm.Config.MemoryReservationLockedToMax != true { 1139 t.Errorf("vm.Config.MemoryReservationLockedToMax expected true; got false") 1140 } 1141 if *vmm.Config.MessageBusTunnelEnabled != true { 1142 t.Errorf("vm.Config.MessageBusTunnelEnabled expected true; got false") 1143 } 1144 if *vmm.Config.NpivTemporaryDisabled != true { 1145 t.Errorf("vm.Config.NpivTemporaryDisabled expected true; got false") 1146 } 1147 if *vmm.Config.NpivOnNonRdmDisks != true { 1148 t.Errorf("vm.Config.NpivOnNonRdmDisks expected true; got false") 1149 } 1150 if *vmm.Config.ConsolePreferences.PowerOnWhenOpened != true { 1151 t.Errorf("vm.Config.ConsolePreferences.PowerOnWhenOpened expected true; got false") 1152 } 1153 if vmm.Config.CpuAffinity.AffinitySet[0] != int32(1) { 1154 t.Errorf("vm.Config.CpuAffinity.AffinitySet[0] expected %d; got %d", 1155 1, vmm.Config.CpuAffinity.AffinitySet[0]) 1156 } 1157 if vmm.Config.MemoryAffinity.AffinitySet[0] != int32(1) { 1158 t.Errorf("vm.Config.CpuAffinity.AffinitySet[0] expected %d; got %d", 1159 1, vmm.Config.CpuAffinity.AffinitySet[0]) 1160 } 1161 if *vmm.Config.CpuAllocation.Reservation != 100 { 1162 t.Errorf("vm.Config.CpuAllocation.Reservation expected %d; got %d", 1163 100, *vmm.Config.CpuAllocation.Reservation) 1164 } 1165 if *vmm.Config.MemoryAllocation.Reservation != 100 { 1166 t.Errorf("vm.Config.MemoryAllocation.Reservation expected %d; got %d", 1167 100, *vmm.Config.MemoryAllocation.Reservation) 1168 } 1169 if vmm.Config.LatencySensitivity.Sensitivity != int32(1) { 1170 t.Errorf("vmm.Config.LatencySensitivity.Sensitivity expected %d; got %d", 1171 1, vmm.Config.LatencySensitivity.Sensitivity) 1172 } 1173 } 1174 1175 func TestCreateVmWithDevices(t *testing.T) { 1176 ctx := context.Background() 1177 1178 m := ESX() 1179 m.Datastore = 2 1180 defer m.Remove() 1181 1182 err := m.Create() 1183 if err != nil { 1184 t.Fatal(err) 1185 } 1186 1187 s := m.Service.NewServer() 1188 defer s.Close() 1189 1190 c := m.Service.client 1191 1192 folder := object.NewFolder(c, esx.Datacenter.VmFolder) 1193 pool := object.NewResourcePool(c, esx.ResourcePool.Self) 1194 1195 // different set of devices from Model.Create's 1196 var devices object.VirtualDeviceList 1197 ide, _ := devices.CreateIDEController() 1198 cdrom, _ := devices.CreateCdrom(ide.(*types.VirtualIDEController)) 1199 scsi, _ := devices.CreateSCSIController("scsi") 1200 disk := &types.VirtualDisk{ 1201 CapacityInKB: 1024, 1202 VirtualDevice: types.VirtualDevice{ 1203 Backing: new(types.VirtualDiskFlatVer2BackingInfo), // Leave fields empty to test defaults 1204 }, 1205 } 1206 disk2 := &types.VirtualDisk{ 1207 CapacityInKB: 1024, 1208 VirtualDevice: types.VirtualDevice{ 1209 Backing: &types.VirtualDiskFlatVer2BackingInfo{ 1210 VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{ 1211 FileName: "[LocalDS_0]", 1212 }, 1213 }, 1214 }, 1215 } 1216 devices = append(devices, ide, cdrom, scsi) 1217 devices.AssignController(disk, scsi.(*types.VirtualLsiLogicController)) 1218 devices = append(devices, disk) 1219 devices.AssignController(disk2, scsi.(*types.VirtualLsiLogicController)) 1220 devices = append(devices, disk2) 1221 create, _ := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd) 1222 1223 spec := types.VirtualMachineConfigSpec{ 1224 Name: "foo", 1225 GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest), 1226 DeviceChange: create, 1227 Files: &types.VirtualMachineFileInfo{ 1228 VmPathName: "[LocalDS_0]", 1229 }, 1230 } 1231 1232 ctask, _ := folder.CreateVM(ctx, spec, pool, nil) 1233 info, err := ctask.WaitForResult(ctx, nil) 1234 if err != nil { 1235 t.Fatal(err) 1236 } 1237 1238 vm := Map.Get(info.Result.(types.ManagedObjectReference)).(*VirtualMachine) 1239 1240 expect := len(esx.VirtualDevice) + len(devices) 1241 ndevice := len(vm.Config.Hardware.Device) 1242 1243 if expect != ndevice { 1244 t.Errorf("expected %d, got %d", expect, ndevice) 1245 } 1246 1247 // check number of disk and disk summary 1248 ndisk := 0 1249 for _, device := range vm.Config.Hardware.Device { 1250 disk, ok := device.(*types.VirtualDisk) 1251 if ok { 1252 ndisk++ 1253 summary := disk.DeviceInfo.GetDescription().Summary 1254 if summary != "1,024 KB" { 1255 t.Errorf("expected '1,1024 KB', got %s", summary) 1256 } 1257 } 1258 } 1259 if ndisk != 2 { 1260 t.Errorf("expected 1 disk, got %d", ndisk) 1261 } 1262 1263 // Add disk on another datastore with empty path (issue 1854) 1264 ovm := object.NewVirtualMachine(c, vm.Self) 1265 disk = &types.VirtualDisk{ 1266 CapacityInKB: 1024, 1267 VirtualDevice: types.VirtualDevice{ 1268 Backing: &types.VirtualDiskFlatVer2BackingInfo{ 1269 VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{ 1270 FileName: "[LocalDS_1]", 1271 }, 1272 }, 1273 }, 1274 } 1275 devices.AssignController(disk, scsi.(*types.VirtualLsiLogicController)) 1276 devices = nil 1277 devices = append(devices, disk) 1278 create, _ = devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd) 1279 spec = types.VirtualMachineConfigSpec{DeviceChange: create} 1280 rtask, _ := ovm.Reconfigure(ctx, spec) 1281 _, err = rtask.WaitForResult(ctx, nil) 1282 if err != nil { 1283 t.Fatal(err) 1284 } 1285 } 1286 1287 func TestAddedDiskCapacity(t *testing.T) { 1288 tests := []struct { 1289 name string 1290 capacityInBytes int64 1291 capacityInKB int64 1292 expectedCapacityInBytes int64 1293 expectedCapacityInKB int64 1294 }{ 1295 { 1296 "specify capacityInBytes", 1297 512 * 1024, 1298 0, 1299 512 * 1024, 1300 512, 1301 }, 1302 { 1303 "specify capacityInKB", 1304 0, 1305 512, 1306 512 * 1024, 1307 512, 1308 }, 1309 { 1310 "specify both", 1311 512 * 1024, 1312 512, 1313 512 * 1024, 1314 512, 1315 }, 1316 { 1317 "capacityInbytes takes precedence if two fields represents different capacity", 1318 512 * 1024, 1319 1024, 1320 512 * 1024, 1321 512, 1322 }, 1323 } 1324 1325 for _, test := range tests { 1326 test := test // assign to local var since loop var is reused 1327 t.Run(test.name, func(t *testing.T) { 1328 m := ESX() 1329 1330 Test(func(ctx context.Context, c *vim25.Client) { 1331 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 1332 vm := object.NewVirtualMachine(c, vmm.Reference()) 1333 1334 ds := Map.Any("Datastore").(*Datastore) 1335 1336 devices, err := vm.Device(ctx) 1337 if err != nil { 1338 t.Fatal(err) 1339 } 1340 1341 controller, err := devices.FindDiskController("") 1342 if err != nil { 1343 t.Fatal(err) 1344 } 1345 1346 disk := devices.CreateDisk(controller, ds.Reference(), "") 1347 disk.CapacityInBytes = test.capacityInBytes 1348 disk.CapacityInKB = test.capacityInKB 1349 1350 err = vm.AddDevice(ctx, disk) 1351 if err != nil { 1352 t.Fatal(err) 1353 } 1354 1355 newDevices, err := vm.Device(ctx) 1356 if err != nil { 1357 t.Fatal(err) 1358 } 1359 disks := newDevices.SelectByType((*types.VirtualDisk)(nil)) 1360 if len(disks) == 0 { 1361 t.Fatalf("len(disks)=%d", len(disks)) 1362 } 1363 1364 newDisk := disks[len(disks)-1].(*types.VirtualDisk) 1365 1366 if newDisk.CapacityInBytes != test.expectedCapacityInBytes { 1367 t.Errorf("CapacityInBytes expected %d, got %d", 1368 test.expectedCapacityInBytes, newDisk.CapacityInBytes) 1369 } 1370 if newDisk.CapacityInKB != test.expectedCapacityInKB { 1371 t.Errorf("CapacityInKB expected %d, got %d", 1372 test.expectedCapacityInKB, newDisk.CapacityInKB) 1373 } 1374 1375 }, m) 1376 }) 1377 } 1378 } 1379 1380 func TestEditedDiskCapacity(t *testing.T) { 1381 tests := []struct { 1382 name string 1383 capacityInBytes int64 1384 capacityInKB int64 1385 expectedCapacityInBytes int64 1386 expectedCapacityInKB int64 1387 expectedErr types.BaseMethodFault 1388 }{ 1389 { 1390 "specify same capacities as before", 1391 10 * 1024 * 1024 * 1024, // 10GB 1392 10 * 1024 * 1024, // 10GB 1393 10 * 1024 * 1024 * 1024, // 10GB 1394 10 * 1024 * 1024, // 10GB 1395 nil, 1396 }, 1397 { 1398 "increase only capacityInBytes", 1399 20 * 1024 * 1024 * 1024, // 20GB 1400 10 * 1024 * 1024, // 10GB 1401 20 * 1024 * 1024 * 1024, // 20GB 1402 20 * 1024 * 1024, // 20GB 1403 nil, 1404 }, 1405 { 1406 "increase only capacityInKB", 1407 10 * 1024 * 1024 * 1024, // 10GB 1408 20 * 1024 * 1024, // 20GB 1409 20 * 1024 * 1024 * 1024, // 20GB 1410 20 * 1024 * 1024, // 20GB 1411 nil, 1412 }, 1413 { 1414 "increase both capacityInBytes and capacityInKB", 1415 20 * 1024 * 1024 * 1024, // 20GB 1416 20 * 1024 * 1024, // 20GB 1417 20 * 1024 * 1024 * 1024, // 20GB 1418 20 * 1024 * 1024, // 20GB 1419 nil, 1420 }, 1421 { 1422 "increase both capacityInBytes and capacityInKB but value is different", 1423 20 * 1024 * 1024 * 1024, // 20GB 1424 30 * 1024 * 1024, // 30GB 1425 0, 1426 0, 1427 new(types.InvalidDeviceOperation), 1428 }, 1429 { 1430 "decrease capacity", 1431 1 * 1024 * 1024 * 1024, // 1GB 1432 1 * 1024 * 1024, // 1GB 1433 0, 1434 0, 1435 new(types.InvalidDeviceOperation), 1436 }, 1437 } 1438 1439 for _, test := range tests { 1440 test := test // assign to local var since loop var is reused 1441 t.Run(test.name, func(t *testing.T) { 1442 m := ESX() 1443 1444 Test(func(ctx context.Context, c *vim25.Client) { 1445 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 1446 vm := object.NewVirtualMachine(c, vmm.Reference()) 1447 ds := Map.Any("Datastore").(*Datastore) 1448 1449 // create a new 10GB disk 1450 devices, err := vm.Device(ctx) 1451 if err != nil { 1452 t.Fatal(err) 1453 } 1454 controller, err := devices.FindDiskController("") 1455 if err != nil { 1456 t.Fatal(err) 1457 } 1458 disk := devices.CreateDisk(controller, ds.Reference(), "") 1459 disk.CapacityInBytes = 10 * 1024 * 1024 * 1024 // 10GB 1460 err = vm.AddDevice(ctx, disk) 1461 if err != nil { 1462 t.Fatal(err) 1463 } 1464 1465 // edit its capacity 1466 addedDevices, err := vm.Device(ctx) 1467 if err != nil { 1468 t.Fatal(err) 1469 } 1470 addedDisks := addedDevices.SelectByType((*types.VirtualDisk)(nil)) 1471 if len(addedDisks) == 0 { 1472 t.Fatal("disk not found") 1473 } 1474 addedDisk := addedDisks[0].(*types.VirtualDisk) 1475 addedDisk.CapacityInBytes = test.capacityInBytes 1476 addedDisk.CapacityInKB = test.capacityInKB 1477 err = vm.EditDevice(ctx, addedDisk) 1478 1479 if test.expectedErr != nil { 1480 terr, ok := err.(task.Error) 1481 if !ok { 1482 t.Fatalf("error should be task.Error. actual: %T", err) 1483 } 1484 1485 if !reflect.DeepEqual(terr.Fault(), test.expectedErr) { 1486 t.Errorf("expectedErr: %v, actualErr: %v", test.expectedErr, terr.Fault()) 1487 } 1488 } else { 1489 // obtain the disk again 1490 editedDevices, err := vm.Device(ctx) 1491 if err != nil { 1492 t.Fatal(err) 1493 } 1494 editedDisks := editedDevices.SelectByType((*types.VirtualDisk)(nil)) 1495 if len(editedDevices) == 0 { 1496 t.Fatal("disk not found") 1497 } 1498 editedDisk := editedDisks[len(editedDisks)-1].(*types.VirtualDisk) 1499 1500 if editedDisk.CapacityInBytes != test.expectedCapacityInBytes { 1501 t.Errorf("CapacityInBytes expected %d, got %d", 1502 test.expectedCapacityInBytes, editedDisk.CapacityInBytes) 1503 } 1504 if editedDisk.CapacityInKB != test.expectedCapacityInKB { 1505 t.Errorf("CapacityInKB expected %d, got %d", 1506 test.expectedCapacityInKB, editedDisk.CapacityInKB) 1507 } 1508 } 1509 }, m) 1510 }) 1511 } 1512 } 1513 1514 func TestReconfigureDevicesDatastoreFreespace(t *testing.T) { 1515 tests := []struct { 1516 name string 1517 reconfigure func(context.Context, *object.VirtualMachine, *Datastore, object.VirtualDeviceList) error 1518 freespaceDiff int64 1519 }{ 1520 { 1521 "create a new disk", 1522 func(ctx context.Context, vm *object.VirtualMachine, ds *Datastore, l object.VirtualDeviceList) error { 1523 controller, err := l.FindDiskController("") 1524 if err != nil { 1525 return err 1526 } 1527 1528 disk := l.CreateDisk(controller, ds.Reference(), "") 1529 disk.CapacityInBytes = 10 * 1024 * 1024 * 1024 // 10GB 1530 1531 if err := vm.AddDevice(ctx, disk); err != nil { 1532 return err 1533 } 1534 return nil 1535 }, 1536 -10 * 1024 * 1024 * 1024, // -10GB 1537 }, 1538 { 1539 "edit disk size", 1540 func(ctx context.Context, vm *object.VirtualMachine, ds *Datastore, l object.VirtualDeviceList) error { 1541 disks := l.SelectByType((*types.VirtualDisk)(nil)) 1542 if len(disks) == 0 { 1543 return fmt.Errorf("disk not found") 1544 } 1545 disk := disks[len(disks)-1].(*types.VirtualDisk) 1546 1547 // specify same disk capacity 1548 if err := vm.EditDevice(ctx, disk); err != nil { 1549 return err 1550 } 1551 return nil 1552 }, 1553 0, 1554 }, 1555 { 1556 "remove a disk and its files", 1557 func(ctx context.Context, vm *object.VirtualMachine, ds *Datastore, l object.VirtualDeviceList) error { 1558 disks := l.SelectByType((*types.VirtualDisk)(nil)) 1559 if len(disks) == 0 { 1560 return fmt.Errorf("disk not found") 1561 } 1562 disk := disks[len(disks)-1].(*types.VirtualDisk) 1563 1564 if err := vm.RemoveDevice(ctx, false, disk); err != nil { 1565 return err 1566 } 1567 return nil 1568 }, 1569 10 * 1024 * 1024 * 1024, // 10GB 1570 }, 1571 { 1572 "remove a disk but keep its files", 1573 func(ctx context.Context, vm *object.VirtualMachine, ds *Datastore, l object.VirtualDeviceList) error { 1574 disks := l.SelectByType((*types.VirtualDisk)(nil)) 1575 if len(disks) == 0 { 1576 return fmt.Errorf("disk not found") 1577 } 1578 disk := disks[len(disks)-1].(*types.VirtualDisk) 1579 1580 if err := vm.RemoveDevice(ctx, true, disk); err != nil { 1581 return err 1582 } 1583 return nil 1584 }, 1585 0, 1586 }, 1587 } 1588 1589 for _, test := range tests { 1590 test := test // assign to local var since loop var is reused 1591 t.Run(test.name, func(t *testing.T) { 1592 m := ESX() 1593 1594 Test(func(ctx context.Context, c *vim25.Client) { 1595 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 1596 vm := object.NewVirtualMachine(c, vmm.Reference()) 1597 1598 ds := Map.Any("Datastore").(*Datastore) 1599 freespaceBefore := ds.Datastore.Summary.FreeSpace 1600 1601 devices, err := vm.Device(ctx) 1602 if err != nil { 1603 t.Fatal(err) 1604 } 1605 1606 err = test.reconfigure(ctx, vm, ds, devices) 1607 if err != nil { 1608 t.Fatal(err) 1609 } 1610 1611 freespaceAfter := ds.Datastore.Summary.FreeSpace 1612 1613 if freespaceAfter-freespaceBefore != test.freespaceDiff { 1614 t.Errorf("difference of freespace expected %d, got %d", 1615 test.freespaceDiff, freespaceAfter-freespaceBefore) 1616 } 1617 }, m) 1618 }) 1619 } 1620 } 1621 1622 func TestShutdownGuest(t *testing.T) { 1623 Test(func(ctx context.Context, c *vim25.Client) { 1624 vm := object.NewVirtualMachine(c, Map.Any("VirtualMachine").Reference()) 1625 1626 for _, timeout := range []bool{false, true} { 1627 if timeout { 1628 // ShutdownGuest will return right away, but powerState 1629 // is not updated until the internal task completes 1630 TaskDelay.MethodDelay = map[string]int{ 1631 "ShutdownGuest": 500, // delay 500ms 1632 "LockHandoff": 0, // don't lock vm during the delay 1633 } 1634 } 1635 1636 err := vm.ShutdownGuest(ctx) 1637 if err != nil { 1638 t.Fatal(err) 1639 } 1640 1641 wait := ctx 1642 var cancel context.CancelFunc 1643 if timeout { 1644 state, err := vm.PowerState(ctx) 1645 if err != nil { 1646 t.Fatal(err) 1647 } 1648 1649 // with the task delay, should still be on at this point 1650 if state != types.VirtualMachinePowerStatePoweredOn { 1651 t.Errorf("state=%s", state) 1652 } 1653 1654 wait, cancel = context.WithTimeout(ctx, time.Millisecond*250) // wait < task delay 1655 defer cancel() 1656 } 1657 1658 err = vm.WaitForPowerState(wait, types.VirtualMachinePowerStatePoweredOff) 1659 if timeout { 1660 if err == nil { 1661 t.Error("expected timeout") 1662 } 1663 // wait for power state to change, else next test may fail 1664 err = vm.WaitForPowerState(ctx, types.VirtualMachinePowerStatePoweredOff) 1665 if err != nil { 1666 t.Fatal(err) 1667 } 1668 1669 } else { 1670 if err != nil { 1671 t.Fatal(err) 1672 } 1673 } 1674 1675 // shutdown a poweroff vm should fail 1676 err = vm.ShutdownGuest(ctx) 1677 if err == nil { 1678 t.Error("expected error: InvalidPowerState") 1679 } 1680 1681 task, err := vm.PowerOn(ctx) 1682 if err != nil { 1683 t.Fatal(err) 1684 } 1685 err = task.Wait(ctx) 1686 if err != nil { 1687 t.Fatal(err) 1688 } 1689 } 1690 }) 1691 } 1692 1693 func TestVmSnapshot(t *testing.T) { 1694 ctx := context.Background() 1695 1696 m := ESX() 1697 defer m.Remove() 1698 err := m.Create() 1699 if err != nil { 1700 t.Fatal(err) 1701 } 1702 1703 s := m.Service.NewServer() 1704 defer s.Close() 1705 1706 c, err := govmomi.NewClient(ctx, s.URL, true) 1707 if err != nil { 1708 t.Fatal(err) 1709 } 1710 1711 simVm := Map.Any("VirtualMachine") 1712 vm := object.NewVirtualMachine(c.Client, simVm.Reference()) 1713 1714 _, err = fieldValue(reflect.ValueOf(simVm), "snapshot") 1715 if err != errEmptyField { 1716 t.Fatal("snapshot property should be 'nil' if there are no snapshots") 1717 } 1718 1719 task, err := vm.CreateSnapshot(ctx, "root", "description", true, true) 1720 if err != nil { 1721 t.Fatal(err) 1722 } 1723 1724 info, err := task.WaitForResult(ctx) 1725 if err != nil { 1726 t.Fatal(err) 1727 } 1728 1729 snapRef, ok := info.Result.(types.ManagedObjectReference) 1730 if !ok { 1731 t.Fatal("expected ManagedObjectRefrence result for CreateSnapshot") 1732 } 1733 1734 _, err = vm.FindSnapshot(ctx, snapRef.Value) 1735 if err != nil { 1736 t.Fatal(err, "snapshot should be found by result reference") 1737 } 1738 1739 _, err = fieldValue(reflect.ValueOf(simVm), "snapshot") 1740 if err == errEmptyField { 1741 t.Fatal("snapshot property should not be 'nil' if there are snapshots") 1742 } 1743 // NOTE: fieldValue cannot be used for nil check 1744 if len(simVm.(*VirtualMachine).RootSnapshot) == 0 { 1745 t.Fatal("rootSnapshot property should have elements if there are snapshots") 1746 } 1747 1748 task, err = vm.CreateSnapshot(ctx, "child", "description", true, true) 1749 if err != nil { 1750 t.Fatal(err) 1751 } 1752 1753 err = task.Wait(ctx) 1754 if err != nil { 1755 t.Fatal(err) 1756 } 1757 1758 _, err = vm.FindSnapshot(ctx, "child") 1759 if err != nil { 1760 t.Fatal(err) 1761 } 1762 1763 task, err = vm.RevertToCurrentSnapshot(ctx, true) 1764 if err != nil { 1765 t.Fatal(err) 1766 } 1767 1768 err = task.Wait(ctx) 1769 if err != nil { 1770 t.Fatal(err) 1771 } 1772 1773 task, err = vm.RevertToSnapshot(ctx, "root", true) 1774 if err != nil { 1775 t.Fatal(err) 1776 } 1777 1778 err = task.Wait(ctx) 1779 if err != nil { 1780 t.Fatal(err) 1781 } 1782 1783 task, err = vm.RemoveSnapshot(ctx, "child", false, nil) 1784 if err != nil { 1785 t.Fatal(err) 1786 } 1787 1788 err = task.Wait(ctx) 1789 if err != nil { 1790 t.Fatal(err) 1791 } 1792 1793 _, err = fieldValue(reflect.ValueOf(simVm), "snapshot") 1794 if err == errEmptyField { 1795 t.Fatal("snapshot property should not be 'nil' if there are snapshots") 1796 } 1797 // NOTE: fieldValue cannot be used for nil check 1798 if len(simVm.(*VirtualMachine).RootSnapshot) == 0 { 1799 t.Fatal("rootSnapshot property should have elements if there are snapshots") 1800 } 1801 1802 _, err = vm.FindSnapshot(ctx, "child") 1803 if err == nil { 1804 t.Fatal("child should be removed") 1805 } 1806 1807 task, err = vm.RemoveAllSnapshot(ctx, nil) 1808 if err != nil { 1809 t.Fatal(err) 1810 } 1811 1812 err = task.Wait(ctx) 1813 if err != nil { 1814 t.Fatal(err) 1815 } 1816 1817 _, err = fieldValue(reflect.ValueOf(simVm), "snapshot") 1818 if err != errEmptyField { 1819 t.Fatal("snapshot property should be 'nil' if there are no snapshots") 1820 } 1821 // NOTE: fieldValue cannot be used for nil check 1822 if len(simVm.(*VirtualMachine).RootSnapshot) != 0 { 1823 t.Fatal("rootSnapshot property should not have elements if there are no snapshots") 1824 } 1825 1826 _, err = vm.FindSnapshot(ctx, "root") 1827 if err == nil { 1828 t.Fatal("all snapshots should be removed") 1829 } 1830 } 1831 1832 func TestVmMarkAsTemplate(t *testing.T) { 1833 ctx := context.Background() 1834 1835 m := VPX() 1836 defer m.Remove() 1837 err := m.Create() 1838 if err != nil { 1839 t.Fatal(err) 1840 } 1841 1842 s := m.Service.NewServer() 1843 defer s.Close() 1844 1845 c, err := govmomi.NewClient(ctx, s.URL, true) 1846 if err != nil { 1847 t.Fatal(err) 1848 } 1849 1850 vm := object.NewVirtualMachine(c.Client, Map.Any("VirtualMachine").Reference()) 1851 1852 err = vm.MarkAsTemplate(ctx) 1853 if err == nil { 1854 t.Fatal("cannot create template for a powered on vm") 1855 } 1856 1857 task, err := vm.PowerOff(ctx) 1858 if err != nil { 1859 t.Fatal(err) 1860 } 1861 1862 task.Wait(ctx) 1863 1864 err = vm.MarkAsTemplate(ctx) 1865 if err != nil { 1866 t.Fatal(err) 1867 } 1868 1869 _, err = vm.PowerOn(ctx) 1870 if err == nil { 1871 t.Fatal("cannot PowerOn a template") 1872 } 1873 } 1874 1875 func TestVmRefreshStorageInfo(t *testing.T) { 1876 ctx := context.Background() 1877 1878 m := ESX() 1879 defer m.Remove() 1880 err := m.Create() 1881 if err != nil { 1882 t.Fatal(err) 1883 } 1884 1885 s := m.Service.NewServer() 1886 defer s.Close() 1887 1888 c, err := govmomi.NewClient(ctx, s.URL, true) 1889 if err != nil { 1890 t.Fatal(err) 1891 } 1892 1893 vmm := Map.Any("VirtualMachine").(*VirtualMachine) 1894 vm := object.NewVirtualMachine(c.Client, vmm.Reference()) 1895 1896 // take snapshot 1897 task, err := vm.CreateSnapshot(ctx, "root", "description", true, true) 1898 if err != nil { 1899 t.Fatal(err) 1900 } 1901 1902 err = task.Wait(ctx) 1903 if err != nil { 1904 t.Fatal(err) 1905 } 1906 1907 snapshot, err := vm.FindSnapshot(ctx, "root") 1908 if err != nil { 1909 t.Fatal(err) 1910 } 1911 1912 // check vm.Layout.Snapshot 1913 found := false 1914 for _, snapLayout := range vmm.Layout.Snapshot { 1915 if snapLayout.Key == *snapshot { 1916 found = true 1917 } 1918 } 1919 1920 if found == false { 1921 t.Fatal("could not find new snapshot in vm.Layout.Snapshot") 1922 } 1923 1924 // check vm.LayoutEx.Snapshot 1925 found = false 1926 for _, snapLayoutEx := range vmm.LayoutEx.Snapshot { 1927 if snapLayoutEx.Key == *snapshot { 1928 found = true 1929 } 1930 } 1931 1932 if found == false { 1933 t.Fatal("could not find new snapshot in vm.LayoutEx.Snapshot") 1934 } 1935 1936 // remove snapshot 1937 task, err = vm.RemoveAllSnapshot(ctx, nil) 1938 if err != nil { 1939 t.Fatal(err) 1940 } 1941 1942 err = task.Wait(ctx) 1943 if err != nil { 1944 t.Fatal(err) 1945 } 1946 1947 if len(vmm.Layout.Snapshot) != 0 { 1948 t.Fatal("expected vm.Layout.Snapshot to be empty") 1949 } 1950 1951 if len(vmm.LayoutEx.Snapshot) != 0 { 1952 t.Fatal("expected vm.LayoutEx.Snapshot to be empty") 1953 } 1954 1955 device, err := vm.Device(ctx) 1956 if err != nil { 1957 t.Fatal(err) 1958 } 1959 1960 disks := device.SelectByType((*types.VirtualDisk)(nil)) 1961 if len(disks) < 1 { 1962 t.Fatal("expected VM to have at least 1 disk") 1963 } 1964 1965 findDiskFile := func(vmdkName string) *types.VirtualMachineFileLayoutExFileInfo { 1966 for _, dFile := range vmm.LayoutEx.File { 1967 if dFile.Name == vmdkName { 1968 return &dFile 1969 } 1970 } 1971 1972 return nil 1973 } 1974 1975 findDsStorage := func(dsName string) *types.VirtualMachineUsageOnDatastore { 1976 host := Map.Get(*vmm.Runtime.Host).(*HostSystem) 1977 ds := Map.FindByName(dsName, host.Datastore).(*Datastore) 1978 1979 for _, dsUsage := range vmm.Storage.PerDatastoreUsage { 1980 if dsUsage.Datastore == ds.Self { 1981 return &dsUsage 1982 } 1983 } 1984 1985 return nil 1986 } 1987 1988 for _, d := range disks { 1989 disk := d.(*types.VirtualDisk) 1990 info := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo) 1991 diskLayoutCount := len(vmm.Layout.Disk) 1992 summaryStorageNew := vmm.Summary.Storage 1993 1994 p, fault := parseDatastorePath(info.FileName) 1995 if fault != nil { 1996 t.Fatalf("could not parse datastore path for disk file: %s", info.FileName) 1997 } 1998 1999 storageNew := findDsStorage(p.Datastore) 2000 if storageNew == nil { 2001 t.Fatalf("could not find vm usage on datastore: %s", p.Datastore) 2002 } 2003 2004 diskFile := findDiskFile(info.FileName) 2005 if diskFile == nil { 2006 t.Fatal("could not find disk file in vm.LayoutEx.File") 2007 } 2008 2009 // remove disk 2010 if err = vm.RemoveDevice(ctx, false, d); err != nil { 2011 t.Error(err) 2012 } 2013 2014 summaryStorageOld := summaryStorageNew 2015 summaryStorageNew = vmm.Summary.Storage 2016 2017 storageOld := storageNew 2018 storageNew = findDsStorage(p.Datastore) 2019 if storageNew == nil { 2020 t.Fatalf("could not find vm usage on datastore: %s", p.Datastore) 2021 } 2022 2023 tests := []struct { 2024 got int64 2025 expected int64 2026 }{ 2027 {int64(len(vmm.Layout.Disk)), int64(diskLayoutCount - 1)}, 2028 {summaryStorageNew.Committed, summaryStorageOld.Committed - diskFile.Size}, 2029 {summaryStorageNew.Unshared, summaryStorageOld.Unshared - diskFile.Size}, 2030 {summaryStorageNew.Uncommitted, summaryStorageOld.Uncommitted - disk.CapacityInBytes + diskFile.Size}, 2031 {storageNew.Committed, storageOld.Committed - diskFile.Size}, 2032 {storageNew.Unshared, storageOld.Unshared - diskFile.Size}, 2033 {storageNew.Uncommitted, storageOld.Uncommitted - disk.CapacityInBytes + diskFile.Size}, 2034 } 2035 2036 for _, test := range tests { 2037 if test.got != test.expected { 2038 t.Errorf("expected %d, got %d", test.expected, test.got) 2039 } 2040 } 2041 2042 // add disk 2043 disk.CapacityInBytes = 1000000000 2044 if err = vm.AddDevice(ctx, d); err != nil { 2045 t.Error(err) 2046 } 2047 2048 summaryStorageOld = summaryStorageNew 2049 summaryStorageNew = vmm.Summary.Storage 2050 2051 storageOld = storageNew 2052 storageNew = findDsStorage(p.Datastore) 2053 if storageNew == nil { 2054 t.Fatalf("could not find vm usage on datastore: %s", p.Datastore) 2055 } 2056 2057 diskFile = findDiskFile(info.FileName) 2058 if diskFile == nil { 2059 t.Fatal("could not find disk file in vm.LayoutEx.File") 2060 } 2061 2062 tests = []struct { 2063 got int64 2064 expected int64 2065 }{ 2066 {int64(len(vmm.Layout.Disk)), int64(diskLayoutCount)}, 2067 {summaryStorageNew.Committed, summaryStorageOld.Committed + diskFile.Size}, 2068 {summaryStorageNew.Unshared, summaryStorageOld.Unshared + diskFile.Size}, 2069 {summaryStorageNew.Uncommitted, summaryStorageOld.Uncommitted + disk.CapacityInBytes - diskFile.Size}, 2070 {storageNew.Committed, storageOld.Committed + diskFile.Size}, 2071 {storageNew.Unshared, storageOld.Unshared + diskFile.Size}, 2072 {storageNew.Uncommitted, storageOld.Uncommitted + disk.CapacityInBytes - diskFile.Size}, 2073 } 2074 2075 for _, test := range tests { 2076 if test.got != test.expected { 2077 t.Errorf("expected %d, got %d", test.expected, test.got) 2078 } 2079 } 2080 } 2081 2082 // manually create log file 2083 fileLayoutExCount := len(vmm.LayoutEx.File) 2084 2085 p, fault := parseDatastorePath(vmm.Config.Files.LogDirectory) 2086 if fault != nil { 2087 t.Fatalf("could not parse datastore path: %s", vmm.Config.Files.LogDirectory) 2088 } 2089 2090 f, fault := vmm.createFile(p.String(), "test.log", false) 2091 if fault != nil { 2092 t.Fatal("could not create log file") 2093 } 2094 2095 if len(vmm.LayoutEx.File) != fileLayoutExCount { 2096 t.Errorf("expected %d, got %d", fileLayoutExCount, len(vmm.LayoutEx.File)) 2097 } 2098 2099 if err = vm.RefreshStorageInfo(ctx); err != nil { 2100 t.Error(err) 2101 } 2102 2103 if len(vmm.LayoutEx.File) != fileLayoutExCount+1 { 2104 t.Errorf("expected %d, got %d", fileLayoutExCount+1, len(vmm.LayoutEx.File)) 2105 } 2106 2107 err = f.Close() 2108 if err != nil { 2109 t.Fatalf("f.Close failure: %v", err) 2110 } 2111 err = os.Remove(f.Name()) 2112 if err != nil { 2113 t.Fatalf("os.Remove(%s) failure: %v", f.Name(), err) 2114 } 2115 2116 if err = vm.RefreshStorageInfo(ctx); err != nil { 2117 t.Error(err) 2118 } 2119 2120 if len(vmm.LayoutEx.File) != fileLayoutExCount { 2121 t.Errorf("expected %d, got %d", fileLayoutExCount, len(vmm.LayoutEx.File)) 2122 } 2123 } 2124 2125 func TestApplyExtraConfig(t *testing.T) { 2126 2127 applyAndAssertExtraConfigValue := func( 2128 ctx context.Context, 2129 vm *object.VirtualMachine, 2130 val string, 2131 assertDoesNotExist bool) { 2132 2133 task, err := vm.Reconfigure(ctx, types.VirtualMachineConfigSpec{ 2134 ExtraConfig: []types.BaseOptionValue{ 2135 &types.OptionValue{ 2136 Key: "hello", 2137 Value: val, 2138 }, 2139 }, 2140 }) 2141 if err != nil { 2142 t.Fatal(err) 2143 } 2144 if err := task.Wait(ctx); err != nil { 2145 t.Fatal(err) 2146 } 2147 2148 var moVM mo.VirtualMachine 2149 if err := vm.Properties( 2150 ctx, 2151 vm.Reference(), 2152 []string{"config.extraConfig"}, 2153 &moVM); err != nil { 2154 t.Fatal(err) 2155 } 2156 if moVM.Config == nil { 2157 t.Fatal("nil config") 2158 } 2159 var found bool 2160 for i := range moVM.Config.ExtraConfig { 2161 bov := moVM.Config.ExtraConfig[i] 2162 if bov == nil { 2163 continue 2164 } 2165 ov := bov.GetOptionValue() 2166 if ov == nil { 2167 continue 2168 } 2169 if ov.Key == "hello" { 2170 if ov.Value != val { 2171 t.Fatalf("invalid ExtraConfig value: expected=%s, actual=%v", val, ov.Value) 2172 } 2173 found = true 2174 } 2175 } 2176 if !assertDoesNotExist && !found { 2177 t.Fatal("failed to apply ExtraConfig") 2178 } 2179 } 2180 2181 Test(func(ctx context.Context, c *vim25.Client) { 2182 vm := object.NewVirtualMachine(c, Map.Any("VirtualMachine").Reference()) 2183 applyAndAssertExtraConfigValue(ctx, vm, "world", false) 2184 applyAndAssertExtraConfigValue(ctx, vm, "there", false) 2185 applyAndAssertExtraConfigValue(ctx, vm, "", true) 2186 }) 2187 } 2188 2189 func TestLastModifiedAndChangeVersionAreUpdated(t *testing.T) { 2190 Test(func(ctx context.Context, c *vim25.Client) { 2191 vm := object.NewVirtualMachine(c, Map.Any("VirtualMachine").Reference()) 2192 var vmMo mo.VirtualMachine 2193 if err := vm.Properties( 2194 ctx, 2195 vm.Reference(), 2196 []string{"config.modified", "config.changeVersion"}, 2197 &vmMo); err != nil { 2198 2199 t.Fatalf("failed to fetch initial vm props: %v", err) 2200 } 2201 2202 oldModified := vmMo.Config.Modified 2203 oldChangeVersion := vmMo.Config.ChangeVersion 2204 2205 tsk, err := vm.Reconfigure(ctx, types.VirtualMachineConfigSpec{ 2206 ExtraConfig: []types.BaseOptionValue{ 2207 &types.OptionValue{ 2208 Key: "hello", 2209 Value: "world", 2210 }, 2211 }, 2212 }) 2213 if err != nil { 2214 t.Fatalf("failed to call reconfigure api: %v", err) 2215 } 2216 if err := tsk.WaitEx(ctx); err != nil { 2217 t.Fatalf("failed to reconfigure: %v", err) 2218 } 2219 2220 if err := vm.Properties( 2221 ctx, 2222 vm.Reference(), 2223 []string{"config.modified", "config.changeVersion"}, 2224 &vmMo); err != nil { 2225 2226 t.Fatalf("failed to fetch vm props after reconfigure: %v", err) 2227 } 2228 2229 newModified := vmMo.Config.Modified 2230 newChangeVersion := vmMo.Config.ChangeVersion 2231 2232 if a, e := newModified, oldModified; a == e { 2233 t.Errorf("config.modified was not updated: %v", a) 2234 } 2235 2236 if a, e := newChangeVersion, oldChangeVersion; a == e { 2237 t.Errorf("config.changeVersion was not updated: %v", a) 2238 } 2239 }) 2240 } 2241 2242 func TestUpgradeVm(t *testing.T) { 2243 2244 const ( 2245 vmx1 = "vmx-1" 2246 vmx2 = "vmx-2" 2247 vmx15 = "vmx-15" 2248 vmx17 = "vmx-17" 2249 vmx19 = "vmx-19" 2250 vmx20 = "vmx-20" 2251 vmx21 = "vmx-21" 2252 vmx22 = "vmx-22" 2253 ) 2254 2255 model := VPX() 2256 model.Autostart = false 2257 model.Cluster = 1 2258 model.ClusterHost = 1 2259 model.Host = 1 2260 2261 Test(func(ctx context.Context, c *vim25.Client) { 2262 props := []string{"config.version", "summary.config.hwVersion"} 2263 2264 vm := object.NewVirtualMachine(c, Map.Any("VirtualMachine").Reference()) 2265 vm2 := Map.Get(vm.Reference()).(*VirtualMachine) 2266 Map.WithLock(SpoofContext(), vm2.Reference(), func() { 2267 vm2.Config.Version = vmx15 2268 }) 2269 2270 host, err := vm.HostSystem(ctx) 2271 if err != nil { 2272 t.Fatalf("failed to get vm's host: %v", err) 2273 } 2274 host2 := Map.Get(host.Reference()).(*HostSystem) 2275 2276 var eb *EnvironmentBrowser 2277 { 2278 ref := Map.Get(host.Reference()).(*HostSystem).Parent 2279 switch ref.Type { 2280 case "ClusterComputeResource": 2281 obj := Map.Get(*ref).(*ClusterComputeResource) 2282 eb = Map.Get(*obj.EnvironmentBrowser).(*EnvironmentBrowser) 2283 case "ComputeResource": 2284 obj := Map.Get(*ref).(*mo.ComputeResource) 2285 eb = Map.Get(*obj.EnvironmentBrowser).(*EnvironmentBrowser) 2286 } 2287 } 2288 2289 baseline := func() { 2290 Map.WithLock(SpoofContext(), vm2.Reference(), func() { 2291 vm2.Config.Version = vmx15 2292 vm2.Config.Template = false 2293 vm2.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOff 2294 }) 2295 Map.WithLock(SpoofContext(), host2.Reference(), func() { 2296 host2.Runtime.InMaintenanceMode = false 2297 }) 2298 Map.WithLock(SpoofContext(), eb.Reference(), func() { 2299 for i := range eb.QueryConfigOptionDescriptorResponse.Returnval { 2300 cod := &eb.QueryConfigOptionDescriptorResponse.Returnval[i] 2301 hostFound := false 2302 for j := range cod.Host { 2303 if cod.Host[j].Value == host2.Reference().Value { 2304 hostFound = true 2305 break 2306 } 2307 } 2308 if !hostFound { 2309 cod.Host = append(cod.Host, host2.Reference()) 2310 } 2311 } 2312 }) 2313 } 2314 2315 t.Run("InvalidPowerState", func(t *testing.T) { 2316 baseline() 2317 Map.WithLock(SpoofContext(), vm2.Reference(), func() { 2318 vm2.Runtime.PowerState = types.VirtualMachinePowerStatePoweredOn 2319 }) 2320 2321 tsk, err := vm.UpgradeVM(ctx, vmx15) 2322 if err != nil { 2323 t.Fatalf("failed to call upgradeVm api: %v", err) 2324 } 2325 if _, err := tsk.WaitForResultEx(ctx); err == nil { 2326 t.Fatal("expected error did not occur") 2327 } else if err2, ok := err.(task.Error); !ok { 2328 t.Fatalf("unexpected error: %[1]T %+[1]v", err) 2329 } else if f := err2.Fault(); f == nil { 2330 t.Fatal("fault is nil") 2331 } else if f2, ok := f.(*types.InvalidPowerStateFault); !ok { 2332 t.Fatalf("unexpected fault: %[1]T %+[1]v", f) 2333 } else { 2334 if f2.ExistingState != types.VirtualMachinePowerStatePoweredOn { 2335 t.Errorf("unexpected existing state: %v", f2.ExistingState) 2336 } 2337 if f2.RequestedState != types.VirtualMachinePowerStatePoweredOff { 2338 t.Errorf("unexpected requested state: %v", f2.RequestedState) 2339 } 2340 } 2341 }) 2342 2343 t.Run("InvalidState", func(t *testing.T) { 2344 t.Run("MaintenanceMode", func(t *testing.T) { 2345 baseline() 2346 Map.WithLock(SpoofContext(), host2.Reference(), func() { 2347 host2.Runtime.InMaintenanceMode = true 2348 }) 2349 2350 if tsk, err := vm.UpgradeVM(ctx, vmx15); err != nil { 2351 t.Fatalf("failed to call upgradeVm api: %v", err) 2352 } else if _, err := tsk.WaitForResultEx(ctx); err == nil { 2353 t.Fatal("expected error did not occur") 2354 } else if err, ok := err.(task.Error); !ok { 2355 t.Fatalf("unexpected error: %[1]T %+[1]v", err) 2356 } else if f := err.Fault(); f == nil { 2357 t.Fatal("fault is nil") 2358 } else if f2, ok := f.(*types.InvalidState); !ok { 2359 t.Fatalf("unexpected fault: %[1]T %+[1]v", f) 2360 } else if fc := f2.FaultCause; fc == nil { 2361 t.Fatal("fault cause is nil") 2362 } else if fc.LocalizedMessage != fmt.Sprintf("%s in maintenance mode", host.Reference().Value) { 2363 t.Fatalf("unexpected error message: %s", fc.LocalizedMessage) 2364 } 2365 }) 2366 2367 t.Run("Template", func(t *testing.T) { 2368 baseline() 2369 Map.WithLock(SpoofContext(), vm2.Reference(), func() { 2370 vm2.Config.Template = true 2371 }) 2372 2373 if tsk, err := vm.UpgradeVM(ctx, vmx15); err != nil { 2374 t.Fatalf("failed to call upgradeVm api: %v", err) 2375 } else if _, err := tsk.WaitForResultEx(ctx); err == nil { 2376 t.Fatal("expected error did not occur") 2377 } else if err, ok := err.(task.Error); !ok { 2378 t.Fatalf("unexpected error: %[1]T %+[1]v", err) 2379 } else if f := err.Fault(); f == nil { 2380 t.Fatal("fault is nil") 2381 } else if f2, ok := f.(*types.InvalidState); !ok { 2382 t.Fatalf("unexpected fault: %[1]T %+[1]v", f) 2383 } else if fc := f2.FaultCause; fc == nil { 2384 t.Fatal("fault cause is nil") 2385 } else if fc.LocalizedMessage != fmt.Sprintf("%s is template", vm.Reference().Value) { 2386 t.Fatalf("unexpected error message: %s", fc.LocalizedMessage) 2387 } 2388 }) 2389 2390 t.Run("LatestHardwareVersion", func(t *testing.T) { 2391 baseline() 2392 Map.WithLock(SpoofContext(), vm.Reference(), func() { 2393 vm2.Config.Version = vmx21 2394 }) 2395 2396 if tsk, err := vm.UpgradeVM(ctx, vmx21); err != nil { 2397 t.Fatalf("failed to call upgradeVm api: %v", err) 2398 } else if _, err := tsk.WaitForResultEx(ctx); err == nil { 2399 t.Fatal("expected error did not occur") 2400 } else if err, ok := err.(task.Error); !ok { 2401 t.Fatalf("unexpected error: %[1]T %+[1]v", err) 2402 } else if f := err.Fault(); f == nil { 2403 t.Fatal("fault is nil") 2404 } else if f2, ok := f.(*types.InvalidState); !ok { 2405 t.Fatalf("unexpected fault: %[1]T %+[1]v", f) 2406 } else if fc := f2.FaultCause; fc == nil { 2407 t.Fatal("fault cause is nil") 2408 } else if fc.LocalizedMessage != fmt.Sprintf("%s is latest version", vm.Reference().Value) { 2409 t.Fatalf("unexpected error message: %s", fc.LocalizedMessage) 2410 } 2411 }) 2412 }) 2413 2414 t.Run("NotSupported", func(t *testing.T) { 2415 t.Run("AtAll", func(t *testing.T) { 2416 baseline() 2417 2418 if tsk, err := vm.UpgradeVM(ctx, vmx22); err != nil { 2419 t.Fatalf("failed to call upgradeVm api: %v", err) 2420 } else if _, err := tsk.WaitForResultEx(ctx); err == nil { 2421 t.Fatal("expected error did not occur") 2422 } else if err, ok := err.(task.Error); !ok { 2423 t.Fatalf("unexpected error: %[1]T %+[1]v", err) 2424 } else if f := err.Fault(); f == nil { 2425 t.Fatal("fault is nil") 2426 } else if f2, ok := f.(*types.NotSupported); !ok { 2427 t.Fatalf("unexpected fault: %[1]T %+[1]v", f) 2428 } else if fc := f2.FaultCause; fc == nil { 2429 t.Fatal("fault cause is nil") 2430 } else if fc.LocalizedMessage != "vmx-22 not supported" { 2431 t.Fatalf("unexpected error message: %s", fc.LocalizedMessage) 2432 } 2433 }) 2434 t.Run("OnVmHost", func(t *testing.T) { 2435 baseline() 2436 Map.WithLock(SpoofContext(), eb.Reference(), func() { 2437 for i := range eb.QueryConfigOptionDescriptorResponse.Returnval { 2438 cod := &eb.QueryConfigOptionDescriptorResponse.Returnval[i] 2439 if cod.Key == vmx17 { 2440 cod.Host = nil 2441 } 2442 } 2443 }) 2444 2445 if tsk, err := vm.UpgradeVM(ctx, vmx17); err != nil { 2446 t.Fatalf("failed to call upgradeVm api: %v", err) 2447 } else if _, err := tsk.WaitForResultEx(ctx); err == nil { 2448 t.Fatal("expected error did not occur") 2449 } else if err, ok := err.(task.Error); !ok { 2450 t.Fatalf("unexpected error: %[1]T %+[1]v", err) 2451 } else if f := err.Fault(); f == nil { 2452 t.Fatal("fault is nil") 2453 } else if f2, ok := f.(*types.NotSupported); !ok { 2454 t.Fatalf("unexpected fault: %[1]T %+[1]v", f) 2455 } else if fc := f2.FaultCause; fc == nil { 2456 t.Fatal("fault cause is nil") 2457 } else if fc.LocalizedMessage != "vmx-17 not supported" { 2458 t.Fatalf("unexpected error message: %s", fc.LocalizedMessage) 2459 } 2460 }) 2461 }) 2462 2463 t.Run("AlreadyUpgraded", func(t *testing.T) { 2464 t.Run("EqualToTargetVersion", func(t *testing.T) { 2465 baseline() 2466 if tsk, err := vm.UpgradeVM(ctx, vmx15); err != nil { 2467 t.Fatalf("failed to call upgradeVm api: %v", err) 2468 } else if _, err := tsk.WaitForResultEx(ctx); err == nil { 2469 t.Fatal("expected error did not occur") 2470 } else if err, ok := err.(task.Error); !ok { 2471 t.Fatalf("unexpected error: %[1]T %+[1]v", err) 2472 } else if f := err.Fault(); f == nil { 2473 t.Fatal("fault is nil") 2474 } else if _, ok := f.(*types.AlreadyUpgradedFault); !ok { 2475 t.Fatalf("unexpected fault: %[1]T %+[1]v", f) 2476 } 2477 }) 2478 2479 t.Run("GreaterThanTargetVersion", func(t *testing.T) { 2480 baseline() 2481 Map.WithLock(SpoofContext(), vm2.Reference(), func() { 2482 vm2.Config.Version = vmx20 2483 }) 2484 if tsk, err := vm.UpgradeVM(ctx, vmx17); err != nil { 2485 t.Fatalf("failed to call upgradeVm api: %v", err) 2486 } else if _, err := tsk.WaitForResultEx(ctx); err == nil { 2487 t.Fatal("expected error did not occur") 2488 } else if err, ok := err.(task.Error); !ok { 2489 t.Fatalf("unexpected error: %[1]T %+[1]v", err) 2490 } else if f := err.Fault(); f == nil { 2491 t.Fatal("fault is nil") 2492 } else if _, ok := f.(*types.AlreadyUpgradedFault); !ok { 2493 t.Fatalf("unexpected fault: %[1]T %+[1]v", f) 2494 } 2495 }) 2496 }) 2497 2498 t.Run("InvalidArgument", func(t *testing.T) { 2499 baseline() 2500 Map.WithLock(SpoofContext(), vm2.Reference(), func() { 2501 vm2.Config.Version = vmx1 2502 }) 2503 Map.WithLock(SpoofContext(), eb.Reference(), func() { 2504 eb.QueryConfigOptionDescriptorResponse.Returnval = append( 2505 eb.QueryConfigOptionDescriptorResponse.Returnval, 2506 types.VirtualMachineConfigOptionDescriptor{ 2507 Key: vmx2, 2508 Host: []types.ManagedObjectReference{host.Reference()}, 2509 }) 2510 }) 2511 2512 if tsk, err := vm.UpgradeVM(ctx, vmx2); err != nil { 2513 t.Fatalf("failed to call upgradeVm api: %v", err) 2514 } else if _, err := tsk.WaitForResultEx(ctx); err == nil { 2515 t.Fatal("expected error did not occur") 2516 } else if err, ok := err.(task.Error); !ok { 2517 t.Fatalf("unexpected error: %[1]T %+[1]v", err) 2518 } else if f := err.Fault(); f == nil { 2519 t.Fatal("fault is nil") 2520 } else if _, ok := f.(*types.InvalidArgument); !ok { 2521 t.Fatalf("unexpected fault: %[1]T %+[1]v", f) 2522 } 2523 }) 2524 2525 t.Run("UpgradeToLatest", func(t *testing.T) { 2526 baseline() 2527 2528 if tsk, err := vm.UpgradeVM(ctx, ""); err != nil { 2529 t.Fatalf("failed to call upgradeVm api: %v", err) 2530 } else if _, err := tsk.WaitForResultEx(ctx); err != nil { 2531 t.Fatalf("failed to upgrade vm: %v", err) 2532 } 2533 var vmMo mo.VirtualMachine 2534 if err := vm.Properties( 2535 ctx, 2536 vm.Reference(), 2537 props, 2538 &vmMo); err != nil { 2539 2540 t.Fatalf("failed to fetch vm props after upgrade: %v", err) 2541 } 2542 if v := vmMo.Config.Version; v != vmx21 { 2543 t.Fatalf("unexpected config.version %v", v) 2544 } 2545 if v := vmMo.Summary.Config.HwVersion; v != vmx21 { 2546 t.Fatalf("unexpected summary.config.hwVersion %v", v) 2547 } 2548 }) 2549 2550 t.Run("UpgradeFrom15To17", func(t *testing.T) { 2551 const targetVersion = vmx17 2552 baseline() 2553 2554 if tsk, err := vm.UpgradeVM(ctx, targetVersion); err != nil { 2555 t.Fatalf("failed to call upgradeVm api: %v", err) 2556 } else if _, err := tsk.WaitForResultEx(ctx); err != nil { 2557 t.Fatalf("failed to upgrade vm: %v", err) 2558 } 2559 var vmMo mo.VirtualMachine 2560 if err := vm.Properties( 2561 ctx, 2562 vm.Reference(), 2563 props, 2564 &vmMo); err != nil { 2565 2566 t.Fatalf("failed to fetch vm props after upgrade: %v", err) 2567 } 2568 if v := vmMo.Config.Version; v != targetVersion { 2569 t.Fatalf("unexpected config.version %v", v) 2570 } 2571 if v := vmMo.Summary.Config.HwVersion; v != targetVersion { 2572 t.Fatalf("unexpected summary.config.hwVersion %v", v) 2573 } 2574 }) 2575 2576 t.Run("UpgradeFrom17To20", func(t *testing.T) { 2577 const targetVersion = vmx20 2578 baseline() 2579 Map.WithLock(SpoofContext(), vm2.Reference(), func() { 2580 vm2.Config.Version = vmx17 2581 }) 2582 2583 if tsk, err := vm.UpgradeVM(ctx, targetVersion); err != nil { 2584 t.Fatalf("failed to call upgradeVm api: %v", err) 2585 } else if _, err := tsk.WaitForResultEx(ctx); err != nil { 2586 t.Fatalf("failed to upgrade vm: %v", err) 2587 } 2588 var vmMo mo.VirtualMachine 2589 if err := vm.Properties( 2590 ctx, 2591 vm.Reference(), 2592 props, 2593 &vmMo); err != nil { 2594 2595 t.Fatalf("failed to fetch vm props after upgrade: %v", err) 2596 } 2597 if v := vmMo.Config.Version; v != targetVersion { 2598 t.Fatalf("unexpected config.version %v", v) 2599 } 2600 if v := vmMo.Summary.Config.HwVersion; v != targetVersion { 2601 t.Fatalf("unexpected summary.config.hwVersion %v", v) 2602 } 2603 }) 2604 2605 t.Run("UpgradeFrom15To17To20", func(t *testing.T) { 2606 const ( 2607 targetVersion1 = vmx17 2608 targetVersion2 = vmx20 2609 ) 2610 baseline() 2611 2612 if tsk, err := vm.UpgradeVM(ctx, targetVersion1); err != nil { 2613 t.Fatalf("failed to call upgradeVm api first time: %v", err) 2614 } else if _, err := tsk.WaitForResultEx(ctx); err != nil { 2615 t.Fatalf("failed to upgrade vm first time: %v", err) 2616 } 2617 var vmMo mo.VirtualMachine 2618 if err := vm.Properties( 2619 ctx, 2620 vm.Reference(), 2621 props, 2622 &vmMo); err != nil { 2623 2624 t.Fatalf("failed to fetch vm props after first upgrade: %v", err) 2625 } 2626 if v := vmMo.Config.Version; v != targetVersion1 { 2627 t.Fatalf("unexpected config.version after first upgrade %v", v) 2628 } 2629 if v := vmMo.Summary.Config.HwVersion; v != targetVersion1 { 2630 t.Fatalf("unexpected summary.config.hwVersion after first upgrade %v", v) 2631 } 2632 2633 if tsk, err := vm.UpgradeVM(ctx, targetVersion2); err != nil { 2634 t.Fatalf("failed to call upgradeVm api second time: %v", err) 2635 } else if _, err := tsk.WaitForResultEx(ctx); err != nil { 2636 t.Fatalf("failed to upgrade vm second time: %v", err) 2637 } 2638 if err := vm.Properties( 2639 ctx, 2640 vm.Reference(), 2641 props, 2642 &vmMo); err != nil { 2643 2644 t.Fatalf("failed to fetch vm props after second upgrade: %v", err) 2645 } 2646 if v := vmMo.Config.Version; v != targetVersion2 { 2647 t.Fatalf("unexpected config.version after second upgrade %v", v) 2648 } 2649 if v := vmMo.Summary.Config.HwVersion; v != targetVersion2 { 2650 t.Fatalf("unexpected summary.config.hwVersion after second upgrade %v", v) 2651 } 2652 }) 2653 2654 }, model) 2655 } 2656 2657 func TestEncryptDecryptVM(t *testing.T) { 2658 2659 newTaskErrWithInvalidState := func(msg string) error { 2660 return task.Error{ 2661 LocalizedMethodFault: &types.LocalizedMethodFault{ 2662 Fault: newInvalidStateFault(msg), 2663 LocalizedMessage: "*types.InvalidState", 2664 }, 2665 } 2666 } 2667 2668 newTaskErrWithInvalidPowerState := func(cur, req types.VirtualMachinePowerState) error { 2669 return task.Error{ 2670 LocalizedMethodFault: &types.LocalizedMethodFault{ 2671 Fault: &types.InvalidPowerState{ 2672 ExistingState: cur, 2673 RequestedState: req, 2674 }, 2675 LocalizedMessage: "*types.InvalidPowerState", 2676 }, 2677 } 2678 } 2679 2680 testCases := []struct { 2681 name string 2682 initStateFn func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error 2683 configSpec types.VirtualMachineConfigSpec 2684 expectedCryptoKeyId *types.CryptoKeyId 2685 expectedErr error 2686 }{ 2687 { 2688 name: "encrypt", 2689 configSpec: types.VirtualMachineConfigSpec{ 2690 Crypto: &types.CryptoSpecEncrypt{ 2691 CryptoKeyId: types.CryptoKeyId{ 2692 KeyId: "123", 2693 ProviderId: &types.KeyProviderId{ 2694 Id: "abc", 2695 }, 2696 }, 2697 }, 2698 }, 2699 expectedCryptoKeyId: &types.CryptoKeyId{ 2700 KeyId: "123", 2701 ProviderId: &types.KeyProviderId{ 2702 Id: "abc", 2703 }, 2704 }, 2705 }, 2706 { 2707 name: "encrypt w already encrypted", 2708 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2709 Map.WithLock(ctx, vmRef, func() { 2710 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 2711 KeyId: "123", 2712 ProviderId: &types.KeyProviderId{ 2713 Id: "abc", 2714 }, 2715 } 2716 }) 2717 return nil 2718 }, 2719 configSpec: types.VirtualMachineConfigSpec{ 2720 Crypto: &types.CryptoSpecEncrypt{ 2721 CryptoKeyId: types.CryptoKeyId{ 2722 KeyId: "123", 2723 ProviderId: &types.KeyProviderId{ 2724 Id: "abc", 2725 }, 2726 }, 2727 }, 2728 }, 2729 expectedErr: newTaskErrWithInvalidState("vm is already encrypted"), 2730 }, 2731 { 2732 name: "encrypt w powered on", 2733 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2734 tsk, err := object.NewVirtualMachine(c, vmRef).PowerOn(ctx) 2735 if err != nil { 2736 return err 2737 } 2738 return tsk.Wait(ctx) 2739 }, 2740 configSpec: types.VirtualMachineConfigSpec{ 2741 Crypto: &types.CryptoSpecEncrypt{ 2742 CryptoKeyId: types.CryptoKeyId{ 2743 KeyId: "123", 2744 ProviderId: &types.KeyProviderId{ 2745 Id: "abc", 2746 }, 2747 }, 2748 }, 2749 }, 2750 expectedErr: newTaskErrWithInvalidPowerState( 2751 types.VirtualMachinePowerStatePoweredOn, 2752 types.VirtualMachinePowerStatePoweredOff, 2753 ), 2754 }, 2755 { 2756 name: "encrypt w snapshots", 2757 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2758 tsk, err := object.NewVirtualMachine(c, vmRef).CreateSnapshot( 2759 ctx, "root", "", false, false) 2760 if err != nil { 2761 return err 2762 } 2763 return tsk.Wait(ctx) 2764 }, 2765 configSpec: types.VirtualMachineConfigSpec{ 2766 Crypto: &types.CryptoSpecEncrypt{ 2767 CryptoKeyId: types.CryptoKeyId{ 2768 KeyId: "123", 2769 ProviderId: &types.KeyProviderId{ 2770 Id: "abc", 2771 }, 2772 }, 2773 }, 2774 }, 2775 expectedErr: newTaskErrWithInvalidState("vm has snapshots"), 2776 }, 2777 { 2778 name: "decrypt", 2779 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2780 Map.WithLock(ctx, vmRef, func() { 2781 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 2782 KeyId: "123", 2783 ProviderId: &types.KeyProviderId{ 2784 Id: "abc", 2785 }, 2786 } 2787 }) 2788 return nil 2789 }, 2790 configSpec: types.VirtualMachineConfigSpec{ 2791 Crypto: &types.CryptoSpecDecrypt{}, 2792 }, 2793 expectedCryptoKeyId: nil, 2794 }, 2795 { 2796 name: "decrypt w not encrypted", 2797 configSpec: types.VirtualMachineConfigSpec{ 2798 Crypto: &types.CryptoSpecDecrypt{}, 2799 }, 2800 expectedErr: newTaskErrWithInvalidState("vm is not encrypted"), 2801 }, 2802 { 2803 name: "decrypt w powered on", 2804 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2805 Map.WithLock(ctx, vmRef, func() { 2806 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 2807 KeyId: "123", 2808 ProviderId: &types.KeyProviderId{ 2809 Id: "abc", 2810 }, 2811 } 2812 }) 2813 tsk, err := object.NewVirtualMachine(c, vmRef).PowerOn(ctx) 2814 if err != nil { 2815 return err 2816 } 2817 return tsk.Wait(ctx) 2818 }, 2819 configSpec: types.VirtualMachineConfigSpec{ 2820 Crypto: &types.CryptoSpecDecrypt{}, 2821 }, 2822 expectedErr: newTaskErrWithInvalidPowerState( 2823 types.VirtualMachinePowerStatePoweredOn, 2824 types.VirtualMachinePowerStatePoweredOff, 2825 ), 2826 }, 2827 { 2828 name: "decrypt w snapshots", 2829 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2830 Map.WithLock(ctx, vmRef, func() { 2831 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 2832 KeyId: "123", 2833 ProviderId: &types.KeyProviderId{ 2834 Id: "abc", 2835 }, 2836 } 2837 }) 2838 tsk, err := object.NewVirtualMachine(c, vmRef).CreateSnapshot( 2839 ctx, "root", "", false, false) 2840 if err != nil { 2841 return err 2842 } 2843 return tsk.Wait(ctx) 2844 }, 2845 configSpec: types.VirtualMachineConfigSpec{ 2846 Crypto: &types.CryptoSpecDecrypt{}, 2847 }, 2848 expectedErr: newTaskErrWithInvalidState("vm has snapshots"), 2849 }, 2850 { 2851 name: "deep recrypt", 2852 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2853 Map.WithLock(ctx, vmRef, func() { 2854 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 2855 KeyId: "123", 2856 ProviderId: &types.KeyProviderId{ 2857 Id: "abc", 2858 }, 2859 } 2860 }) 2861 return nil 2862 }, 2863 configSpec: types.VirtualMachineConfigSpec{ 2864 Crypto: &types.CryptoSpecDeepRecrypt{ 2865 NewKeyId: types.CryptoKeyId{ 2866 KeyId: "456", 2867 ProviderId: &types.KeyProviderId{ 2868 Id: "def", 2869 }, 2870 }, 2871 }, 2872 }, 2873 expectedCryptoKeyId: &types.CryptoKeyId{ 2874 KeyId: "456", 2875 ProviderId: &types.KeyProviderId{ 2876 Id: "def", 2877 }, 2878 }, 2879 }, 2880 { 2881 name: "deep recrypt w same provider id", 2882 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2883 Map.WithLock(ctx, vmRef, func() { 2884 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 2885 KeyId: "123", 2886 ProviderId: &types.KeyProviderId{ 2887 Id: "abc", 2888 }, 2889 } 2890 }) 2891 return nil 2892 }, 2893 configSpec: types.VirtualMachineConfigSpec{ 2894 Crypto: &types.CryptoSpecDeepRecrypt{ 2895 NewKeyId: types.CryptoKeyId{ 2896 KeyId: "456", 2897 }, 2898 }, 2899 }, 2900 expectedCryptoKeyId: &types.CryptoKeyId{ 2901 KeyId: "456", 2902 ProviderId: &types.KeyProviderId{ 2903 Id: "abc", 2904 }, 2905 }, 2906 }, 2907 { 2908 name: "deep recrypt w not encrypted", 2909 configSpec: types.VirtualMachineConfigSpec{ 2910 Crypto: &types.CryptoSpecDeepRecrypt{ 2911 NewKeyId: types.CryptoKeyId{ 2912 KeyId: "456", 2913 ProviderId: &types.KeyProviderId{ 2914 Id: "def", 2915 }, 2916 }, 2917 }, 2918 }, 2919 expectedErr: newTaskErrWithInvalidState("vm is not encrypted"), 2920 }, 2921 { 2922 name: "deep recrypt w powered on", 2923 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2924 Map.WithLock(ctx, vmRef, func() { 2925 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 2926 KeyId: "123", 2927 ProviderId: &types.KeyProviderId{ 2928 Id: "abc", 2929 }, 2930 } 2931 }) 2932 tsk, err := object.NewVirtualMachine(c, vmRef).PowerOn(ctx) 2933 if err != nil { 2934 return err 2935 } 2936 return tsk.Wait(ctx) 2937 }, 2938 configSpec: types.VirtualMachineConfigSpec{ 2939 Crypto: &types.CryptoSpecDeepRecrypt{ 2940 NewKeyId: types.CryptoKeyId{ 2941 KeyId: "456", 2942 ProviderId: &types.KeyProviderId{ 2943 Id: "def", 2944 }, 2945 }, 2946 }, 2947 }, 2948 expectedErr: newTaskErrWithInvalidPowerState( 2949 types.VirtualMachinePowerStatePoweredOn, 2950 types.VirtualMachinePowerStatePoweredOff, 2951 ), 2952 }, 2953 { 2954 name: "deep recrypt w snapshots", 2955 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2956 Map.WithLock(ctx, vmRef, func() { 2957 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 2958 KeyId: "123", 2959 ProviderId: &types.KeyProviderId{ 2960 Id: "abc", 2961 }, 2962 } 2963 }) 2964 tsk, err := object.NewVirtualMachine(c, vmRef).CreateSnapshot( 2965 ctx, "root", "", false, false) 2966 if err != nil { 2967 return err 2968 } 2969 return tsk.Wait(ctx) 2970 }, 2971 configSpec: types.VirtualMachineConfigSpec{ 2972 Crypto: &types.CryptoSpecDeepRecrypt{ 2973 NewKeyId: types.CryptoKeyId{ 2974 KeyId: "456", 2975 ProviderId: &types.KeyProviderId{ 2976 Id: "def", 2977 }, 2978 }, 2979 }, 2980 }, 2981 expectedErr: newTaskErrWithInvalidState("vm has snapshots"), 2982 }, 2983 { 2984 name: "shallow recrypt", 2985 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 2986 Map.WithLock(ctx, vmRef, func() { 2987 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 2988 KeyId: "123", 2989 ProviderId: &types.KeyProviderId{ 2990 Id: "abc", 2991 }, 2992 } 2993 }) 2994 return nil 2995 }, 2996 configSpec: types.VirtualMachineConfigSpec{ 2997 Crypto: &types.CryptoSpecShallowRecrypt{ 2998 NewKeyId: types.CryptoKeyId{ 2999 KeyId: "456", 3000 ProviderId: &types.KeyProviderId{ 3001 Id: "def", 3002 }, 3003 }, 3004 }, 3005 }, 3006 expectedCryptoKeyId: &types.CryptoKeyId{ 3007 KeyId: "456", 3008 ProviderId: &types.KeyProviderId{ 3009 Id: "def", 3010 }, 3011 }, 3012 }, 3013 { 3014 name: "shallow recrypt w same provider id", 3015 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 3016 Map.WithLock(ctx, vmRef, func() { 3017 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 3018 KeyId: "123", 3019 ProviderId: &types.KeyProviderId{ 3020 Id: "abc", 3021 }, 3022 } 3023 }) 3024 return nil 3025 }, 3026 configSpec: types.VirtualMachineConfigSpec{ 3027 Crypto: &types.CryptoSpecShallowRecrypt{ 3028 NewKeyId: types.CryptoKeyId{ 3029 KeyId: "456", 3030 }, 3031 }, 3032 }, 3033 expectedCryptoKeyId: &types.CryptoKeyId{ 3034 KeyId: "456", 3035 ProviderId: &types.KeyProviderId{ 3036 Id: "abc", 3037 }, 3038 }, 3039 }, 3040 { 3041 name: "shallow recrypt w not encrypted", 3042 configSpec: types.VirtualMachineConfigSpec{ 3043 Crypto: &types.CryptoSpecShallowRecrypt{ 3044 NewKeyId: types.CryptoKeyId{ 3045 KeyId: "456", 3046 ProviderId: &types.KeyProviderId{ 3047 Id: "def", 3048 }, 3049 }, 3050 }, 3051 }, 3052 expectedErr: newTaskErrWithInvalidState("vm is not encrypted"), 3053 }, 3054 { 3055 name: "shallow recrypt w single snapshot chain", 3056 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 3057 Map.WithLock(ctx, vmRef, func() { 3058 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 3059 KeyId: "123", 3060 ProviderId: &types.KeyProviderId{ 3061 Id: "abc", 3062 }, 3063 } 3064 }) 3065 tsk, err := object.NewVirtualMachine(c, vmRef).CreateSnapshot( 3066 ctx, "root", "", false, false) 3067 if err != nil { 3068 return err 3069 } 3070 return tsk.Wait(ctx) 3071 }, 3072 configSpec: types.VirtualMachineConfigSpec{ 3073 Crypto: &types.CryptoSpecShallowRecrypt{ 3074 NewKeyId: types.CryptoKeyId{ 3075 KeyId: "456", 3076 ProviderId: &types.KeyProviderId{ 3077 Id: "def", 3078 }, 3079 }, 3080 }, 3081 }, 3082 expectedCryptoKeyId: &types.CryptoKeyId{ 3083 KeyId: "456", 3084 ProviderId: &types.KeyProviderId{ 3085 Id: "def", 3086 }, 3087 }, 3088 }, 3089 { 3090 name: "shallow recrypt w snapshot tree", 3091 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 3092 Map.WithLock(ctx, vmRef, func() { 3093 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 3094 KeyId: "123", 3095 ProviderId: &types.KeyProviderId{ 3096 Id: "abc", 3097 }, 3098 } 3099 }) 3100 vm := object.NewVirtualMachine(c, vmRef) 3101 tsk, err := vm.CreateSnapshot(ctx, "root", "", false, false) 3102 if err != nil { 3103 return err 3104 } 3105 if err := tsk.Wait(ctx); err != nil { 3106 return err 3107 } 3108 for i := 0; i < 2; i++ { 3109 tsk, err := vm.CreateSnapshot( 3110 ctx, 3111 fmt.Sprintf("snap-%d", i), 3112 "", 3113 false, 3114 false) 3115 if err != nil { 3116 return err 3117 } 3118 if err := tsk.Wait(ctx); err != nil { 3119 return err 3120 } 3121 tsk, err = vm.RevertToSnapshot(ctx, "root", true) 3122 if err != nil { 3123 return err 3124 } 3125 if err := tsk.Wait(ctx); err != nil { 3126 return err 3127 } 3128 } 3129 tsk, err = object.NewVirtualMachine(c, vmRef).PowerOn(ctx) 3130 if err != nil { 3131 return err 3132 } 3133 return tsk.Wait(ctx) 3134 }, 3135 configSpec: types.VirtualMachineConfigSpec{ 3136 Crypto: &types.CryptoSpecShallowRecrypt{ 3137 NewKeyId: types.CryptoKeyId{ 3138 KeyId: "456", 3139 ProviderId: &types.KeyProviderId{ 3140 Id: "def", 3141 }, 3142 }, 3143 }, 3144 }, 3145 expectedErr: newTaskErrWithInvalidState("vm has snapshot tree"), 3146 }, 3147 { 3148 name: "noop", 3149 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 3150 Map.WithLock(ctx, vmRef, func() { 3151 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 3152 KeyId: "123", 3153 ProviderId: &types.KeyProviderId{ 3154 Id: "abc", 3155 }, 3156 } 3157 }) 3158 return nil 3159 }, 3160 configSpec: types.VirtualMachineConfigSpec{ 3161 Crypto: &types.CryptoSpecNoOp{}, 3162 }, 3163 expectedCryptoKeyId: &types.CryptoKeyId{ 3164 KeyId: "123", 3165 ProviderId: &types.KeyProviderId{ 3166 Id: "abc", 3167 }, 3168 }, 3169 }, 3170 { 3171 name: "register", 3172 initStateFn: func(ctx *Context, c *vim25.Client, vmRef types.ManagedObjectReference) error { 3173 Map.WithLock(ctx, vmRef, func() { 3174 Map.Get(vmRef).(*VirtualMachine).Config.KeyId = &types.CryptoKeyId{ 3175 KeyId: "123", 3176 ProviderId: &types.KeyProviderId{ 3177 Id: "abc", 3178 }, 3179 } 3180 }) 3181 return nil 3182 }, 3183 configSpec: types.VirtualMachineConfigSpec{ 3184 Crypto: &types.CryptoSpecRegister{ 3185 CryptoKeyId: types.CryptoKeyId{ 3186 KeyId: "456", 3187 ProviderId: &types.KeyProviderId{ 3188 Id: "def", 3189 }, 3190 }, 3191 }, 3192 }, 3193 expectedCryptoKeyId: &types.CryptoKeyId{ 3194 KeyId: "123", 3195 ProviderId: &types.KeyProviderId{ 3196 Id: "abc", 3197 }, 3198 }, 3199 }, 3200 } 3201 3202 for i := range testCases { 3203 tc := testCases[i] 3204 t.Run(tc.name, func(t *testing.T) { 3205 3206 model := VPX() 3207 model.Autostart = false 3208 model.Cluster = 1 3209 model.ClusterHost = 1 3210 model.Host = 1 3211 3212 Test(func(ctx context.Context, c *vim25.Client) { 3213 3214 ref := Map.Any("VirtualMachine").Reference() 3215 vm := object.NewVirtualMachine(c, ref) 3216 3217 if tc.initStateFn != nil { 3218 if err := tc.initStateFn(SpoofContext(), c, ref); err != nil { 3219 t.Fatalf("initStateFn failed: %v", err) 3220 } 3221 } 3222 3223 tsk, err := vm.Reconfigure(context.TODO(), tc.configSpec) 3224 assert.NoError(t, err) 3225 3226 if a, e := tsk.Wait(context.TODO()), tc.expectedErr; e != nil { 3227 assert.Equal(t, e, a) 3228 } else { 3229 if !assert.NoError(t, a) { 3230 return 3231 } 3232 var moVM mo.VirtualMachine 3233 if err := vm.Properties(ctx, ref, []string{"config.keyId"}, &moVM); err != nil { 3234 t.Fatalf("fetching properties failed: %v", err) 3235 } 3236 if tc.expectedCryptoKeyId != nil { 3237 assert.Equal(t, tc.expectedCryptoKeyId, moVM.Config.KeyId) 3238 } else { 3239 assert.Nil(t, moVM.Config) 3240 } 3241 } 3242 }, model) 3243 }) 3244 } 3245 }