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  }