github.com/vmware/govmomi@v0.51.0/vmdk/disk_info_test.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package vmdk_test
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"os"
    11  	"path"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  
    17  	"github.com/vmware/govmomi/find"
    18  	"github.com/vmware/govmomi/object"
    19  	"github.com/vmware/govmomi/simulator"
    20  	"github.com/vmware/govmomi/vim25"
    21  	"github.com/vmware/govmomi/vim25/methods"
    22  	"github.com/vmware/govmomi/vim25/mo"
    23  	"github.com/vmware/govmomi/vim25/soap"
    24  	"github.com/vmware/govmomi/vim25/types"
    25  	"github.com/vmware/govmomi/vmdk"
    26  )
    27  
    28  func TestGetVirtualDiskInfoByUUID(t *testing.T) {
    29  
    30  	type testCase struct {
    31  		name            string
    32  		ctx             context.Context
    33  		client          *vim25.Client
    34  		mo              mo.VirtualMachine
    35  		fetchProperties bool
    36  		diskUUID        string
    37  		diskInfo        vmdk.VirtualDiskInfo
    38  		err             string
    39  	}
    40  
    41  	t.Run("w cached properties", func(t *testing.T) {
    42  
    43  		const (
    44  			deviceKey     = 1000
    45  			diskUUID      = "123"
    46  			fileName      = "[datastore] path/to.vmdk"
    47  			tenGiBInBytes = 10 * 1024 * 1024 * 1024
    48  		)
    49  
    50  		getDisk := func(backing types.BaseVirtualDeviceBackingInfo) *types.VirtualDisk {
    51  			return &types.VirtualDisk{
    52  				VirtualDevice: types.VirtualDevice{
    53  					Key:     deviceKey,
    54  					Backing: backing,
    55  				},
    56  				CapacityInBytes: tenGiBInBytes,
    57  			}
    58  		}
    59  
    60  		getDiskInfo := func() vmdk.VirtualDiskInfo {
    61  			return vmdk.VirtualDiskInfo{
    62  				CapacityInBytes: tenGiBInBytes,
    63  				DeviceKey:       deviceKey,
    64  				FileName:        fileName,
    65  				Size:            (1 * 1024 * 1024 * 1024) + 950,
    66  				UniqueSize:      (5 * 1024 * 1024) + 100,
    67  			}
    68  		}
    69  
    70  		getEncryptedDiskInfo := func(pid, kid string) vmdk.VirtualDiskInfo {
    71  			return vmdk.VirtualDiskInfo{
    72  				CapacityInBytes: tenGiBInBytes,
    73  				DeviceKey:       deviceKey,
    74  				FileName:        fileName,
    75  				Size:            (1 * 1024 * 1024 * 1024) + 950,
    76  				UniqueSize:      (5 * 1024 * 1024) + 100,
    77  				CryptoKey: vmdk.VirtualDiskCryptoKey{
    78  					KeyID:      kid,
    79  					ProviderID: pid,
    80  				},
    81  			}
    82  		}
    83  
    84  		getLayoutEx := func() *types.VirtualMachineFileLayoutEx {
    85  			return &types.VirtualMachineFileLayoutEx{
    86  				Disk: []types.VirtualMachineFileLayoutExDiskLayout{
    87  					{
    88  						Key: 1000,
    89  						Chain: []types.VirtualMachineFileLayoutExDiskUnit{
    90  							{
    91  								FileKey: []int32{
    92  									4,
    93  									5,
    94  								},
    95  							},
    96  						},
    97  					},
    98  				},
    99  				File: []types.VirtualMachineFileLayoutExFileInfo{
   100  					{
   101  						Key:        4,
   102  						Size:       1 * 1024 * 1024 * 1024, // 1 GiB
   103  						UniqueSize: 5 * 1024 * 1024,        // 500 MiB
   104  					},
   105  					{
   106  						Key:        5,
   107  						Size:       950,
   108  						UniqueSize: 100,
   109  					},
   110  				},
   111  			}
   112  		}
   113  
   114  		testCases := []testCase{
   115  			{
   116  				name: "diskUUID is empty",
   117  				err:  "diskUUID is empty",
   118  			},
   119  			{
   120  				name: "no matching disks",
   121  				mo: mo.VirtualMachine{
   122  					Config: &types.VirtualMachineConfigInfo{
   123  						Hardware: types.VirtualHardware{
   124  							Device: []types.BaseVirtualDevice{},
   125  						},
   126  					},
   127  					LayoutEx: &types.VirtualMachineFileLayoutEx{
   128  						File: []types.VirtualMachineFileLayoutExFileInfo{},
   129  						Disk: []types.VirtualMachineFileLayoutExDiskLayout{},
   130  					},
   131  				},
   132  				diskUUID: diskUUID,
   133  				err:      "disk not found with uuid \"123\"",
   134  			},
   135  			{
   136  				name: "one disk w VirtualDiskFlatVer2BackingInfo",
   137  				mo: mo.VirtualMachine{
   138  					Config: &types.VirtualMachineConfigInfo{
   139  						Hardware: types.VirtualHardware{
   140  							Device: []types.BaseVirtualDevice{
   141  								getDisk(&types.VirtualDiskFlatVer2BackingInfo{
   142  									VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
   143  										FileName: fileName,
   144  									},
   145  									Uuid: diskUUID,
   146  								}),
   147  							},
   148  						},
   149  					},
   150  					LayoutEx: getLayoutEx(),
   151  				},
   152  				diskUUID: diskUUID,
   153  				diskInfo: getDiskInfo(),
   154  			},
   155  			{
   156  				name: "one encrypted disk w VirtualDiskFlatVer2BackingInfo",
   157  				mo: mo.VirtualMachine{
   158  					Config: &types.VirtualMachineConfigInfo{
   159  						Hardware: types.VirtualHardware{
   160  							Device: []types.BaseVirtualDevice{
   161  								getDisk(&types.VirtualDiskFlatVer2BackingInfo{
   162  									VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
   163  										FileName: fileName,
   164  									},
   165  									Uuid: diskUUID,
   166  									KeyId: &types.CryptoKeyId{
   167  										KeyId: "my-key-id",
   168  										ProviderId: &types.KeyProviderId{
   169  											Id: "my-provider-id",
   170  										},
   171  									},
   172  								}),
   173  							},
   174  						},
   175  					},
   176  					LayoutEx: getLayoutEx(),
   177  				},
   178  				diskUUID: diskUUID,
   179  				diskInfo: getEncryptedDiskInfo("my-provider-id", "my-key-id"),
   180  			},
   181  			{
   182  				name: "one disk w VirtualDiskSeSparseBackingInfo",
   183  				mo: mo.VirtualMachine{
   184  					Config: &types.VirtualMachineConfigInfo{
   185  						Hardware: types.VirtualHardware{
   186  							Device: []types.BaseVirtualDevice{
   187  								getDisk(&types.VirtualDiskSeSparseBackingInfo{
   188  									VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
   189  										FileName: fileName,
   190  									},
   191  									Uuid: diskUUID,
   192  								}),
   193  							},
   194  						},
   195  					},
   196  					LayoutEx: getLayoutEx(),
   197  				},
   198  				diskUUID: diskUUID,
   199  				diskInfo: getDiskInfo(),
   200  			},
   201  			{
   202  				name: "one encrypted disk w VirtualDiskSeSparseBackingInfo",
   203  				mo: mo.VirtualMachine{
   204  					Config: &types.VirtualMachineConfigInfo{
   205  						Hardware: types.VirtualHardware{
   206  							Device: []types.BaseVirtualDevice{
   207  								getDisk(&types.VirtualDiskSeSparseBackingInfo{
   208  									VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
   209  										FileName: fileName,
   210  									},
   211  									Uuid: diskUUID,
   212  									KeyId: &types.CryptoKeyId{
   213  										KeyId: "my-key-id",
   214  										ProviderId: &types.KeyProviderId{
   215  											Id: "my-provider-id",
   216  										},
   217  									},
   218  								}),
   219  							},
   220  						},
   221  					},
   222  					LayoutEx: getLayoutEx(),
   223  				},
   224  				diskUUID: diskUUID,
   225  				diskInfo: getEncryptedDiskInfo("my-provider-id", "my-key-id"),
   226  			},
   227  			{
   228  				name: "one disk w VirtualDiskRawDiskMappingVer1BackingInfo",
   229  				mo: mo.VirtualMachine{
   230  					Config: &types.VirtualMachineConfigInfo{
   231  						Hardware: types.VirtualHardware{
   232  							Device: []types.BaseVirtualDevice{
   233  								getDisk(&types.VirtualDiskRawDiskMappingVer1BackingInfo{
   234  									VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
   235  										FileName: fileName,
   236  									},
   237  									Uuid: diskUUID,
   238  								}),
   239  							},
   240  						},
   241  					},
   242  					LayoutEx: getLayoutEx(),
   243  				},
   244  				diskUUID: diskUUID,
   245  				diskInfo: getDiskInfo(),
   246  			},
   247  			{
   248  				name: "one disk w VirtualDiskSparseVer2BackingInfo",
   249  				mo: mo.VirtualMachine{
   250  					Config: &types.VirtualMachineConfigInfo{
   251  						Hardware: types.VirtualHardware{
   252  							Device: []types.BaseVirtualDevice{
   253  								getDisk(&types.VirtualDiskSparseVer2BackingInfo{
   254  									VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
   255  										FileName: fileName,
   256  									},
   257  									Uuid: diskUUID,
   258  								}),
   259  							},
   260  						},
   261  					},
   262  					LayoutEx: getLayoutEx(),
   263  				},
   264  				diskUUID: diskUUID,
   265  				diskInfo: getDiskInfo(),
   266  			},
   267  			{
   268  				name: "one encrypted disk w VirtualDiskSparseVer2BackingInfo",
   269  				mo: mo.VirtualMachine{
   270  					Config: &types.VirtualMachineConfigInfo{
   271  						Hardware: types.VirtualHardware{
   272  							Device: []types.BaseVirtualDevice{
   273  								getDisk(&types.VirtualDiskSparseVer2BackingInfo{
   274  									VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
   275  										FileName: fileName,
   276  									},
   277  									Uuid: diskUUID,
   278  									KeyId: &types.CryptoKeyId{
   279  										KeyId: "my-key-id",
   280  										ProviderId: &types.KeyProviderId{
   281  											Id: "my-provider-id",
   282  										},
   283  									},
   284  								}),
   285  							},
   286  						},
   287  					},
   288  					LayoutEx: getLayoutEx(),
   289  				},
   290  				diskUUID: diskUUID,
   291  				diskInfo: getEncryptedDiskInfo("my-provider-id", "my-key-id"),
   292  			},
   293  			{
   294  				name: "one disk w VirtualDiskRawDiskVer2BackingInfo",
   295  				mo: mo.VirtualMachine{
   296  					Config: &types.VirtualMachineConfigInfo{
   297  						Hardware: types.VirtualHardware{
   298  							Device: []types.BaseVirtualDevice{
   299  								getDisk(&types.VirtualDiskRawDiskVer2BackingInfo{
   300  									DescriptorFileName: fileName,
   301  									Uuid:               diskUUID,
   302  								}),
   303  							},
   304  						},
   305  					},
   306  					LayoutEx: getLayoutEx(),
   307  				},
   308  				diskUUID: diskUUID,
   309  				diskInfo: getDiskInfo(),
   310  			},
   311  			{
   312  				name: "one disk w multiple chain entries",
   313  				mo: mo.VirtualMachine{
   314  					Config: &types.VirtualMachineConfigInfo{
   315  						Hardware: types.VirtualHardware{
   316  							Device: []types.BaseVirtualDevice{
   317  								getDisk(&types.VirtualDiskFlatVer2BackingInfo{
   318  									VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
   319  										FileName: fileName,
   320  									},
   321  									Uuid: diskUUID,
   322  								}),
   323  							},
   324  						},
   325  					},
   326  					LayoutEx: &types.VirtualMachineFileLayoutEx{
   327  						Disk: []types.VirtualMachineFileLayoutExDiskLayout{
   328  							{
   329  								Key: deviceKey,
   330  								Chain: []types.VirtualMachineFileLayoutExDiskUnit{
   331  									{
   332  										FileKey: []int32{
   333  											4,
   334  											5,
   335  										},
   336  									},
   337  									{
   338  										FileKey: []int32{
   339  											6,
   340  											7,
   341  										},
   342  									},
   343  									{
   344  										FileKey: []int32{
   345  											8,
   346  										},
   347  									},
   348  								},
   349  							},
   350  						},
   351  						File: []types.VirtualMachineFileLayoutExFileInfo{
   352  							{
   353  								Key:        4,
   354  								Size:       1 * 1024 * 1024 * 1024, // 1 GiB
   355  								UniqueSize: 5 * 1024 * 1024,        // 500 MiB
   356  							},
   357  							{
   358  								Key:        5,
   359  								Size:       950,
   360  								UniqueSize: 100,
   361  							},
   362  							{
   363  								Key:        6,
   364  								Size:       500,
   365  								UniqueSize: 100,
   366  							},
   367  							{
   368  								Key:        7,
   369  								Size:       500,
   370  								UniqueSize: 200,
   371  							},
   372  							{
   373  								Key:        8,
   374  								Size:       1000,
   375  								UniqueSize: 300,
   376  							},
   377  						},
   378  					},
   379  				},
   380  				diskUUID: diskUUID,
   381  				diskInfo: vmdk.VirtualDiskInfo{
   382  					CapacityInBytes: tenGiBInBytes,
   383  					DeviceKey:       deviceKey,
   384  					FileName:        fileName,
   385  					Size:            (1 * 1024 * 1024 * 1024) + 950 + 500 + 500 + 1000,
   386  					UniqueSize:      (5 * 1024 * 1024) + 100 + 100 + 200 + 300,
   387  				},
   388  			},
   389  		}
   390  
   391  		for i := range testCases {
   392  			tc := testCases[i]
   393  			t.Run(tc.name, func(t *testing.T) {
   394  				var ctx context.Context
   395  				dii, err := vmdk.GetVirtualDiskInfoByUUID(
   396  					ctx, nil, tc.mo, false, tc.diskUUID)
   397  
   398  				if tc.err != "" {
   399  					assert.EqualError(t, err, tc.err)
   400  				}
   401  				assert.Equal(t, tc.diskInfo, dii)
   402  			})
   403  		}
   404  	})
   405  
   406  	t.Run("fetch properties", func(t *testing.T) {
   407  		simulator.Test(func(ctx context.Context, c *vim25.Client) {
   408  			pc := &propertyCollectorWithFault{}
   409  			pc.Self = c.ServiceContent.PropertyCollector
   410  			simulator.Map(ctx).Put(pc)
   411  
   412  			finder := find.NewFinder(c, true)
   413  			datacenter, err := finder.DefaultDatacenter(ctx)
   414  			if err != nil {
   415  				t.Fatalf("default datacenter not found: %s", err)
   416  			}
   417  			finder.SetDatacenter(datacenter)
   418  			vmList, err := finder.VirtualMachineList(ctx, "*")
   419  			if len(vmList) == 0 {
   420  				t.Fatal("vmList == 0")
   421  			}
   422  			vm := vmList[0]
   423  
   424  			var moVM mo.VirtualMachine
   425  			if err := vm.Properties(
   426  				ctx,
   427  				vm.Reference(),
   428  				[]string{"config", "layoutEx"},
   429  				&moVM); err != nil {
   430  
   431  				t.Fatal(err)
   432  			}
   433  
   434  			devs := object.VirtualDeviceList(moVM.Config.Hardware.Device)
   435  			disks := devs.SelectByType(&types.VirtualDisk{})
   436  			if len(disks) == 0 {
   437  				t.Fatal("disks == 0")
   438  			}
   439  
   440  			var (
   441  				diskUUID     string
   442  				datastoreRef types.ManagedObjectReference
   443  				disk         = disks[0].(*types.VirtualDisk)
   444  				diskBacking  = disk.Backing
   445  				diskInfo     = vmdk.VirtualDiskInfo{
   446  					CapacityInBytes: disk.CapacityInBytes,
   447  					DeviceKey:       disk.Key,
   448  				}
   449  			)
   450  
   451  			switch tb := disk.Backing.(type) {
   452  			case *types.VirtualDiskFlatVer2BackingInfo:
   453  				diskUUID = tb.Uuid
   454  				diskInfo.FileName = tb.FileName
   455  				datastoreRef = *tb.Datastore
   456  			default:
   457  				t.Fatalf("unsupported disk backing: %T", disk.Backing)
   458  			}
   459  
   460  			datastore := object.NewDatastore(c, datastoreRef)
   461  			var moDatastore mo.Datastore
   462  			if err := datastore.Properties(
   463  				ctx,
   464  				datastore.Reference(),
   465  				[]string{"info"},
   466  				&moDatastore); err != nil {
   467  
   468  				t.Fatal(err)
   469  			}
   470  
   471  			datastorePath := moDatastore.Info.GetDatastoreInfo().Url
   472  			var diskPath object.DatastorePath
   473  			if !diskPath.FromString(diskInfo.FileName) {
   474  				t.Fatalf("invalid disk file name: %q", diskInfo.FileName)
   475  			}
   476  
   477  			const vmdkSize = 500
   478  
   479  			assert.NoError(t, os.WriteFile(
   480  				path.Join(datastorePath, diskPath.Path),
   481  				bytes.Repeat([]byte{1}, vmdkSize),
   482  				os.ModeAppend))
   483  			assert.NoError(t, vm.RefreshStorageInfo(ctx))
   484  
   485  			diskInfo.Size = vmdkSize
   486  			diskInfo.UniqueSize = vmdkSize
   487  
   488  			testCases := []testCase{
   489  				{
   490  					name:     "ctx is nil",
   491  					ctx:      nil,
   492  					client:   c,
   493  					diskUUID: diskUUID,
   494  					err:      "ctx is nil",
   495  				},
   496  				{
   497  					name:     "client is nil",
   498  					ctx:      context.Background(),
   499  					client:   nil,
   500  					diskUUID: diskUUID,
   501  					err:      "client is nil",
   502  				},
   503  				{
   504  					name:     "failed to retrieve properties",
   505  					ctx:      context.Background(),
   506  					client:   c,
   507  					diskUUID: diskUUID,
   508  					mo: mo.VirtualMachine{
   509  						ManagedEntity: mo.ManagedEntity{
   510  							ExtensibleManagedObject: mo.ExtensibleManagedObject{
   511  								Self: vm.Reference(),
   512  							},
   513  						},
   514  					},
   515  					err: "failed to retrieve properties: ServerFaultCode: InvalidArgument",
   516  				},
   517  				{
   518  					name:            "fetchProperties is false but cached properties are missing",
   519  					ctx:             context.Background(),
   520  					client:          c,
   521  					diskUUID:        diskUUID,
   522  					diskInfo:        diskInfo,
   523  					fetchProperties: false,
   524  					mo: mo.VirtualMachine{
   525  						ManagedEntity: mo.ManagedEntity{
   526  							ExtensibleManagedObject: mo.ExtensibleManagedObject{
   527  								Self: vm.Reference(),
   528  							},
   529  						},
   530  					},
   531  				},
   532  				{
   533  					name:            "fetchProperties is true and cached properties are stale",
   534  					ctx:             context.Background(),
   535  					client:          c,
   536  					diskUUID:        diskUUID,
   537  					diskInfo:        diskInfo,
   538  					fetchProperties: true,
   539  					mo: mo.VirtualMachine{
   540  						ManagedEntity: mo.ManagedEntity{
   541  							ExtensibleManagedObject: mo.ExtensibleManagedObject{
   542  								Self: vm.Reference(),
   543  							},
   544  						},
   545  						Config: &types.VirtualMachineConfigInfo{
   546  							Hardware: types.VirtualHardware{
   547  								Device: []types.BaseVirtualDevice{
   548  									&types.VirtualDisk{
   549  										VirtualDevice: types.VirtualDevice{
   550  											Key:     diskInfo.DeviceKey,
   551  											Backing: diskBacking,
   552  										},
   553  										CapacityInBytes: 500,
   554  									},
   555  								},
   556  							},
   557  						},
   558  					},
   559  				},
   560  			}
   561  
   562  			for i := range testCases {
   563  				tc := testCases[i]
   564  				t.Run(tc.name, func(t *testing.T) {
   565  					if strings.HasPrefix(tc.err, "failed to retrieve properties:") {
   566  						propertyCollectorShouldFault = true
   567  						defer func() {
   568  							propertyCollectorShouldFault = false
   569  						}()
   570  					}
   571  
   572  					dii, err := vmdk.GetVirtualDiskInfoByUUID(
   573  						tc.ctx,
   574  						tc.client,
   575  						tc.mo,
   576  						tc.fetchProperties,
   577  						tc.diskUUID)
   578  
   579  					if tc.err != "" {
   580  						assert.EqualError(t, err, tc.err)
   581  					}
   582  					assert.Equal(t, tc.diskInfo, dii)
   583  				})
   584  			}
   585  
   586  		})
   587  	})
   588  }
   589  
   590  var propertyCollectorShouldFault bool
   591  
   592  type propertyCollectorWithFault struct {
   593  	simulator.PropertyCollector
   594  }
   595  
   596  func (pc *propertyCollectorWithFault) RetrievePropertiesEx(
   597  	ctx *simulator.Context,
   598  	req *types.RetrievePropertiesEx) soap.HasFault {
   599  
   600  	if propertyCollectorShouldFault {
   601  		return &methods.RetrievePropertiesExBody{
   602  			Fault_: simulator.Fault("", &types.InvalidArgument{}),
   603  		}
   604  	}
   605  
   606  	return pc.PropertyCollector.RetrievePropertiesEx(ctx, req)
   607  }