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

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