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