github.com/vmware/govmomi@v0.43.0/simulator/virtual_machine_test.go (about)

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