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 }