github.com/vmware/govmomi@v0.43.0/vim25/types/json_test.go (about) 1 /* 2 Copyright (c) 2022-2023 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 types 18 19 import ( 20 "bytes" 21 "os" 22 "reflect" 23 "strings" 24 "testing" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/stretchr/testify/assert" 29 ) 30 31 var serializationTests = []struct { 32 name string 33 file string 34 data interface{} 35 goType reflect.Type 36 expDecErr string 37 }{ 38 { 39 name: "vminfo", 40 file: "./testdata/vminfo.json", 41 data: &vmInfoObjForTests, 42 goType: reflect.TypeOf(VirtualMachineConfigInfo{}), 43 }, 44 { 45 name: "retrieveResult", 46 file: "./testdata/retrieveResult.json", 47 data: &retrieveResultForTests, 48 goType: reflect.TypeOf(RetrieveResult{}), 49 }, 50 { 51 name: "vminfo-invalid-type-name-value", 52 file: "./testdata/vminfo-invalid-type-name-value.json", 53 data: &vmInfoObjForTests, 54 goType: reflect.TypeOf(VirtualMachineConfigInfo{}), 55 expDecErr: `json: cannot unmarshal bool into Go struct field VirtualMachineConfigInfo.extraConfig of type string`, 56 }, 57 } 58 59 func TestSerialization(t *testing.T) { 60 for _, test := range serializationTests { 61 t.Run(test.name+" Decode", func(t *testing.T) { 62 f, err := os.Open(test.file) 63 if err != nil { 64 t.Fatal(err) 65 } 66 defer f.Close() 67 68 dec := NewJSONDecoder(f) 69 70 ee := test.expDecErr 71 data := reflect.New(test.goType).Interface() 72 if err := dec.Decode(data); err != nil { 73 if ee != err.Error() { 74 t.Errorf("expected error mismatch: e=%v, a=%v", ee, err) 75 } else if ee == "" { 76 t.Errorf("unexpected error: %v", err) 77 } 78 } else if ee != "" { 79 t.Errorf("expected error did not occur: %v", ee) 80 } else { 81 a, e := data, test.data 82 if diff := cmp.Diff(a, e); diff != "" { 83 t.Errorf("mismatched %v: %s", test.name, diff) 84 } 85 } 86 }) 87 88 t.Run(test.name+" Encode", func(t *testing.T) { 89 if test.expDecErr != "" { 90 t.Skip("skipping due to expected decode error") 91 } 92 93 expJSON, err := os.ReadFile(test.file) 94 if err != nil { 95 t.Fatal(err) 96 } 97 98 var w bytes.Buffer 99 _ = w 100 enc := NewJSONEncoder(&w) 101 102 if err := enc.Encode(test.data); err != nil { 103 t.Fatal(err) 104 } 105 106 expected, actual := string(expJSON), w.String() 107 assert.JSONEq(t, expected, actual) 108 }) 109 } 110 111 t.Run("ConfigSpec", func(t *testing.T) { 112 t.Run("Encode", func(t *testing.T) { 113 114 var testCases = []struct { 115 name string 116 data any 117 expected string 118 expectPanic bool 119 }{ 120 { 121 name: "nil ConfigSpec", 122 data: (*VirtualMachineConfigSpec)(nil), 123 expected: "null", 124 }, 125 { 126 name: "ConfigSpec with nil OptionValue value", 127 data: &VirtualMachineConfigSpec{ 128 ExtraConfig: []BaseOptionValue{ 129 &OptionValue{ 130 Key: "key1", 131 Value: nil, 132 }, 133 }, 134 }, 135 expected: `{"_typeName":"VirtualMachineConfigSpec","extraConfig":[{"_typeName":"OptionValue","key":"key1","value":null}]}`, 136 }, 137 { 138 name: "ConfigSpec with nil OptionValue interface value", 139 data: &VirtualMachineConfigSpec{ 140 ExtraConfig: []BaseOptionValue{ 141 &OptionValue{ 142 Key: "key1", 143 Value: (any)(nil), 144 }, 145 }, 146 }, 147 expected: `{"_typeName":"VirtualMachineConfigSpec","extraConfig":[{"_typeName":"OptionValue","key":"key1","value":null}]}`, 148 }, 149 { 150 name: "ConfigSpec with nil element in OptionValues", 151 data: &VirtualMachineConfigSpec{ 152 ExtraConfig: []BaseOptionValue{ 153 (*OptionValue)(nil), 154 }, 155 }, 156 expected: `{"_typeName":"VirtualMachineConfigSpec","extraConfig":[null]}`, 157 }, 158 { 159 name: "ConfigSpec with nil ToolsConfigInfo", 160 data: &VirtualMachineConfigSpec{ 161 Tools: (*ToolsConfigInfo)(nil), 162 }, 163 expected: `{"_typeName":"VirtualMachineConfigSpec"}`, 164 }, 165 { 166 name: "ConfigSpec with nil vAppConfig", 167 data: &VirtualMachineConfigSpec{ 168 VAppConfig: nil, 169 }, 170 expected: `{"_typeName":"VirtualMachineConfigSpec"}`, 171 }, 172 { 173 name: "ConfigSpec with nil pointer vAppConfig ", 174 data: &VirtualMachineConfigSpec{ 175 VAppConfig: (*VmConfigSpec)(nil), 176 }, 177 expected: `{"_typeName":"VirtualMachineConfigSpec","vAppConfig":null}`, 178 }, 179 } 180 181 for i := range testCases { 182 tc := testCases[i] 183 t.Run(tc.name, func(t *testing.T) { 184 var w bytes.Buffer 185 enc := NewJSONEncoder(&w) 186 187 var panicErr any 188 189 func() { 190 defer func() { 191 panicErr = recover() 192 }() 193 if err := enc.Encode(tc.data); err != nil { 194 t.Fatal(err) 195 } 196 }() 197 198 if tc.expectPanic && panicErr == nil { 199 t.Fatalf("did not panic, w=%v", w.String()) 200 } else if tc.expectPanic && panicErr != nil { 201 t.Logf("expected panic occurred: %v\n", panicErr) 202 } else if !tc.expectPanic && panicErr != nil { 203 t.Fatalf("unexpected panic occurred: %v\n", panicErr) 204 } else if a, e := w.String(), tc.expected+"\n"; a != e { 205 t.Fatalf("act=%s != exp=%s", a, e) 206 } else { 207 t.Log(a) 208 } 209 }) 210 } 211 }) 212 }) 213 } 214 215 func TestOptionValueSerialization(t *testing.T) { 216 tv, e := time.Parse(time.RFC3339Nano, "2022-12-12T11:48:35.473645Z") 217 if e != nil { 218 t.Log("Cannot parse test timestamp. This is coding error.") 219 t.Fail() 220 return 221 } 222 options := []struct { 223 name string 224 wire string 225 binding OptionValue 226 }{ 227 { 228 name: "boolean", 229 wire: `{"_typeName": "OptionValue","key": "option1", 230 "value": {"_typeName": "boolean","_value": true} 231 }`, 232 binding: OptionValue{Key: "option1", Value: true}, 233 }, 234 { 235 name: "byte", 236 wire: `{"_typeName": "OptionValue","key": "option1", 237 "value": {"_typeName": "byte","_value": 16} 238 }`, 239 binding: OptionValue{Key: "option1", Value: uint8(16)}, 240 }, 241 { 242 name: "short", 243 wire: `{"_typeName": "OptionValue","key": "option1", 244 "value": {"_typeName": "short","_value": 300} 245 }`, 246 binding: OptionValue{Key: "option1", Value: int16(300)}, 247 }, 248 { 249 name: "int", 250 wire: `{"_typeName": "OptionValue","key": "option1", 251 "value": {"_typeName": "int","_value": 300}}`, 252 binding: OptionValue{Key: "option1", Value: int32(300)}, 253 }, 254 { 255 name: "long", 256 wire: `{"_typeName": "OptionValue","key": "option1", 257 "value": {"_typeName": "long","_value": 300}}`, 258 binding: OptionValue{Key: "option1", Value: int64(300)}, 259 }, 260 { 261 name: "float", 262 wire: `{"_typeName": "OptionValue","key": "option1", 263 "value": {"_typeName": "float","_value": 30.5}}`, 264 binding: OptionValue{Key: "option1", Value: float32(30.5)}, 265 }, 266 { 267 name: "double", 268 wire: `{"_typeName": "OptionValue","key": "option1", 269 "value": {"_typeName": "double","_value": 12.2}}`, 270 binding: OptionValue{Key: "option1", Value: float64(12.2)}, 271 }, 272 { 273 name: "string", 274 wire: `{"_typeName": "OptionValue","key": "option1", 275 "value": {"_typeName": "string","_value": "test"}}`, 276 binding: OptionValue{Key: "option1", Value: "test"}, 277 }, 278 { 279 name: "dateTime", // time.Time 280 wire: `{"_typeName": "OptionValue","key": "option1", 281 "value": {"_typeName": "dateTime","_value": "2022-12-12T11:48:35.473645Z"}}`, 282 binding: OptionValue{Key: "option1", Value: tv}, 283 }, 284 { 285 name: "binary", // []byte 286 wire: `{"_typeName": "OptionValue","key": "option1", 287 "value": {"_typeName": "binary","_value": "SGVsbG8="}}`, 288 binding: OptionValue{Key: "option1", Value: []byte("Hello")}, 289 }, 290 // during serialization we have no way to guess that a string is to be 291 // converted to uri. Using net.URL solves this. It is a breaking change. 292 // See https://github.com/vmware/govmomi/pull/3123 293 // { 294 // name: "anyURI", // string 295 // wire: `{"_typeName": "OptionValue","key": "option1", 296 // "value": {"_typeName": "anyURI","_value": "http://hello"}}`, 297 // binding: OptionValue{Key: "option1", Value: "test"}, 298 // }, 299 { 300 name: "enum", 301 wire: `{"_typeName": "OptionValue","key": "option1", 302 "value": {"_typeName": "CustomizationNetBIOSMode","_value": "enableNetBIOS"}}`, 303 binding: OptionValue{Key: "option1", Value: CustomizationNetBIOSModeEnableNetBIOS}, 304 }, 305 // There is no ArrayOfCustomizationNetBIOSMode type emitted i.e. no enum 306 // array types are emitted in govmomi. 307 // We can process these in the serialization logic i.e. discover or 308 // prepend the "ArrayOf" prefix 309 // { 310 // name: "array of enum", 311 // wire: `{ 312 // "_typeName": "OptionValue", 313 // "key": "option1", 314 // "value": {"_typeName": "ArrayOfCustomizationNetBIOSMode", 315 // "_value": ["enableNetBIOS"]}}`, 316 // binding: OptionValue{Key: "option1", 317 // Value: []CustomizationNetBIOSMode{ 318 // CustomizationNetBIOSModeEnableNetBIOS 319 // }}, 320 // }, 321 322 // array of struct is weird. Do we want to unmarshal this as 323 // []ClusterHostRecommendation directly? Why do we want to use 324 // ArrayOfClusterHostRecommendation wrapper? 325 // if SOAP does it then I guess back compat is a big reason. 326 { 327 name: "array of struct", 328 wire: `{"_typeName": "OptionValue","key": "option1", 329 "value": {"_typeName": "ArrayOfClusterHostRecommendation","_value": [ 330 { 331 "_typeName":"ClusterHostRecommendation", 332 "host": { 333 "_typeName": "ManagedObjectReference", 334 "type": "HostSystem", 335 "value": "host-42" 336 }, 337 "rating":42 338 }]}}`, 339 binding: OptionValue{ 340 Key: "option1", 341 Value: ArrayOfClusterHostRecommendation{ 342 ClusterHostRecommendation: []ClusterHostRecommendation{ 343 { 344 Host: ManagedObjectReference{ 345 Type: "HostSystem", 346 Value: "host-42", 347 }, 348 Rating: 42, 349 }, 350 }, 351 }, 352 }, 353 }, 354 } 355 356 for _, opt := range options { 357 t.Run("Serialize "+opt.name, func(t *testing.T) { 358 r := strings.NewReader(opt.wire) 359 dec := NewJSONDecoder(r) 360 v := OptionValue{} 361 e := dec.Decode(&v) 362 if e != nil { 363 assert.Fail(t, "Cannot read json", "json %v err %v", opt.wire, e) 364 return 365 } 366 assert.Equal(t, opt.binding, v) 367 }) 368 369 t.Run("De-serialize "+opt.name, func(t *testing.T) { 370 var w bytes.Buffer 371 enc := NewJSONEncoder(&w) 372 enc.Encode(opt.binding) 373 s := w.String() 374 assert.JSONEq(t, opt.wire, s) 375 }) 376 } 377 } 378 379 var vmInfoObjForTests = VirtualMachineConfigInfo{ 380 ChangeVersion: "2022-12-12T11:48:35.473645Z", 381 Modified: mustParseTime(time.RFC3339, "1970-01-01T00:00:00Z"), 382 Name: "test", 383 GuestFullName: "VMware Photon OS (64-bit)", 384 Version: "vmx-20", 385 Uuid: "422ca90b-853b-1101-3350-759f747730cc", 386 CreateDate: addrOfMustParseTime(time.RFC3339, "2022-12-12T11:47:24.685785Z"), 387 InstanceUuid: "502cc2a5-1f06-2890-6d70-ba2c55c5c2b7", 388 NpivTemporaryDisabled: NewBool(true), 389 LocationId: "Earth", 390 Template: false, 391 GuestId: "vmwarePhoton64Guest", 392 AlternateGuestName: "", 393 Annotation: "Hello, world.", 394 Files: VirtualMachineFileInfo{ 395 VmPathName: "[datastore1] test/test.vmx", 396 SnapshotDirectory: "[datastore1] test/", 397 SuspendDirectory: "[datastore1] test/", 398 LogDirectory: "[datastore1] test/", 399 }, 400 Tools: &ToolsConfigInfo{ 401 ToolsVersion: 1, 402 AfterPowerOn: NewBool(true), 403 AfterResume: NewBool(true), 404 BeforeGuestStandby: NewBool(true), 405 BeforeGuestShutdown: NewBool(true), 406 BeforeGuestReboot: nil, 407 ToolsUpgradePolicy: "manual", 408 SyncTimeWithHostAllowed: NewBool(true), 409 SyncTimeWithHost: NewBool(false), 410 LastInstallInfo: &ToolsConfigInfoToolsLastInstallInfo{ 411 Counter: 0, 412 }, 413 }, 414 Flags: VirtualMachineFlagInfo{ 415 EnableLogging: NewBool(true), 416 UseToe: NewBool(false), 417 RunWithDebugInfo: NewBool(false), 418 MonitorType: "release", 419 HtSharing: "any", 420 SnapshotDisabled: NewBool(false), 421 SnapshotLocked: NewBool(false), 422 DiskUuidEnabled: NewBool(false), 423 SnapshotPowerOffBehavior: "powerOff", 424 RecordReplayEnabled: NewBool(false), 425 FaultToleranceType: "unset", 426 CbrcCacheEnabled: NewBool(false), 427 VvtdEnabled: NewBool(false), 428 VbsEnabled: NewBool(false), 429 }, 430 DefaultPowerOps: VirtualMachineDefaultPowerOpInfo{ 431 PowerOffType: "soft", 432 SuspendType: "hard", 433 ResetType: "soft", 434 DefaultPowerOffType: "soft", 435 DefaultSuspendType: "hard", 436 DefaultResetType: "soft", 437 StandbyAction: "checkpoint", 438 }, 439 RebootPowerOff: NewBool(false), 440 Hardware: VirtualHardware{ 441 NumCPU: 1, 442 NumCoresPerSocket: 1, 443 AutoCoresPerSocket: NewBool(true), 444 MemoryMB: 2048, 445 VirtualICH7MPresent: NewBool(false), 446 VirtualSMCPresent: NewBool(false), 447 Device: []BaseVirtualDevice{ 448 &VirtualIDEController{ 449 VirtualController: VirtualController{ 450 VirtualDevice: VirtualDevice{ 451 Key: 200, 452 DeviceInfo: &Description{ 453 Label: "IDE 0", 454 Summary: "IDE 0", 455 }, 456 }, 457 BusNumber: 0, 458 }, 459 }, 460 &VirtualIDEController{ 461 VirtualController: VirtualController{ 462 VirtualDevice: VirtualDevice{ 463 Key: 201, 464 DeviceInfo: &Description{ 465 Label: "IDE 1", 466 Summary: "IDE 1", 467 }, 468 }, 469 BusNumber: 1, 470 }, 471 }, 472 &VirtualPS2Controller{ 473 VirtualController: VirtualController{ 474 VirtualDevice: VirtualDevice{ 475 Key: 300, 476 DeviceInfo: &Description{ 477 Label: "PS2 controller 0", 478 Summary: "PS2 controller 0", 479 }, 480 }, 481 BusNumber: 0, 482 Device: []int32{600, 700}, 483 }, 484 }, 485 &VirtualPCIController{ 486 VirtualController: VirtualController{ 487 VirtualDevice: VirtualDevice{ 488 Key: 100, 489 DeviceInfo: &Description{ 490 Label: "PCI controller 0", 491 Summary: "PCI controller 0", 492 }, 493 }, 494 BusNumber: 0, 495 Device: []int32{500, 12000, 14000, 1000, 15000, 4000}, 496 }, 497 }, 498 &VirtualSIOController{ 499 VirtualController: VirtualController{ 500 VirtualDevice: VirtualDevice{ 501 Key: 400, 502 DeviceInfo: &Description{ 503 Label: "SIO controller 0", 504 Summary: "SIO controller 0", 505 }, 506 }, 507 BusNumber: 0, 508 }, 509 }, 510 &VirtualKeyboard{ 511 VirtualDevice: VirtualDevice{ 512 Key: 600, 513 DeviceInfo: &Description{ 514 Label: "Keyboard", 515 Summary: "Keyboard", 516 }, 517 ControllerKey: 300, 518 UnitNumber: NewInt32(0), 519 }, 520 }, 521 &VirtualPointingDevice{ 522 VirtualDevice: VirtualDevice{ 523 Key: 700, 524 DeviceInfo: &Description{Label: "Pointing device", Summary: "Pointing device; Device"}, 525 Backing: &VirtualPointingDeviceDeviceBackingInfo{ 526 VirtualDeviceDeviceBackingInfo: VirtualDeviceDeviceBackingInfo{ 527 UseAutoDetect: NewBool(false), 528 }, 529 HostPointingDevice: "autodetect", 530 }, 531 ControllerKey: 300, 532 UnitNumber: NewInt32(1), 533 }, 534 }, 535 &VirtualMachineVideoCard{ 536 VirtualDevice: VirtualDevice{ 537 Key: 500, 538 DeviceInfo: &Description{Label: "Video card ", Summary: "Video card"}, 539 ControllerKey: 100, 540 UnitNumber: NewInt32(0), 541 }, 542 VideoRamSizeInKB: 4096, 543 NumDisplays: 1, 544 UseAutoDetect: NewBool(false), 545 Enable3DSupport: NewBool(false), 546 Use3dRenderer: "automatic", 547 GraphicsMemorySizeInKB: 262144, 548 }, 549 &VirtualMachineVMCIDevice{ 550 VirtualDevice: VirtualDevice{ 551 Key: 12000, 552 DeviceInfo: &Description{ 553 Label: "VMCI device", 554 Summary: "Device on the virtual machine PCI " + 555 "bus that provides support for the " + 556 "virtual machine communication interface", 557 }, 558 ControllerKey: 100, 559 UnitNumber: NewInt32(17), 560 }, 561 Id: -1, 562 AllowUnrestrictedCommunication: NewBool(false), 563 FilterEnable: NewBool(true), 564 }, 565 &ParaVirtualSCSIController{ 566 VirtualSCSIController: VirtualSCSIController{ 567 VirtualController: VirtualController{ 568 VirtualDevice: VirtualDevice{ 569 Key: 1000, 570 DeviceInfo: &Description{ 571 Label: "SCSI controller 0", 572 Summary: "VMware paravirtual SCSI", 573 }, 574 ControllerKey: 100, 575 UnitNumber: NewInt32(3), 576 }, 577 Device: []int32{2000}, 578 }, 579 HotAddRemove: NewBool(true), 580 SharedBus: "noSharing", 581 ScsiCtlrUnitNumber: 7, 582 }, 583 }, 584 &VirtualAHCIController{ 585 VirtualSATAController: VirtualSATAController{ 586 VirtualController: VirtualController{ 587 VirtualDevice: VirtualDevice{ 588 Key: 15000, 589 DeviceInfo: &Description{ 590 Label: "SATA controller 0", 591 Summary: "AHCI", 592 }, 593 ControllerKey: 100, 594 UnitNumber: NewInt32(24), 595 }, 596 Device: []int32{16000}, 597 }, 598 }, 599 }, 600 &VirtualCdrom{ 601 VirtualDevice: VirtualDevice{ 602 Key: 16000, 603 DeviceInfo: &Description{ 604 Label: "CD/DVD drive 1", 605 Summary: "Remote device", 606 }, 607 Backing: &VirtualCdromRemotePassthroughBackingInfo{ 608 VirtualDeviceRemoteDeviceBackingInfo: VirtualDeviceRemoteDeviceBackingInfo{ 609 UseAutoDetect: NewBool(false), 610 }, 611 }, 612 Connectable: &VirtualDeviceConnectInfo{AllowGuestControl: true, Status: "untried"}, 613 ControllerKey: 15000, 614 UnitNumber: NewInt32(0), 615 }, 616 }, 617 &VirtualDisk{ 618 VirtualDevice: VirtualDevice{ 619 Key: 2000, 620 DeviceInfo: &Description{ 621 Label: "Hard disk 1", 622 Summary: "4,194,304 KB", 623 }, 624 Backing: &VirtualDiskFlatVer2BackingInfo{ 625 VirtualDeviceFileBackingInfo: VirtualDeviceFileBackingInfo{ 626 BackingObjectId: "1", 627 FileName: "[datastore1] test/test.vmdk", 628 Datastore: &ManagedObjectReference{ 629 Type: "Datastore", 630 Value: "datastore-21", 631 }, 632 }, 633 DiskMode: "persistent", 634 Split: NewBool(false), 635 WriteThrough: NewBool(false), 636 ThinProvisioned: NewBool(false), 637 EagerlyScrub: NewBool(false), 638 Uuid: "6000C298-df15-fe89-ddcb-8ea33329595d", 639 ContentId: "e4e1a794c6307ce7906a3973fffffffe", 640 ChangeId: "", 641 Parent: nil, 642 DeltaDiskFormat: "", 643 DigestEnabled: NewBool(false), 644 DeltaGrainSize: 0, 645 DeltaDiskFormatVariant: "", 646 Sharing: "sharingNone", 647 KeyId: nil, 648 }, 649 ControllerKey: 1000, 650 UnitNumber: NewInt32(0), 651 }, 652 CapacityInKB: 4194304, 653 CapacityInBytes: 4294967296, 654 Shares: &SharesInfo{Shares: 1000, Level: "normal"}, 655 StorageIOAllocation: &StorageIOAllocationInfo{ 656 Limit: NewInt64(-1), 657 Shares: &SharesInfo{Shares: 1000, Level: "normal"}, 658 Reservation: NewInt32(0), 659 }, 660 DiskObjectId: "1-2000", 661 NativeUnmanagedLinkedClone: NewBool(false), 662 }, 663 &VirtualVmxnet3{ 664 VirtualVmxnet: VirtualVmxnet{ 665 VirtualEthernetCard: VirtualEthernetCard{ 666 VirtualDevice: VirtualDevice{ 667 Key: 4000, 668 DeviceInfo: &Description{ 669 Label: "Network adapter 1", 670 Summary: "VM Network", 671 }, 672 Backing: &VirtualEthernetCardNetworkBackingInfo{ 673 VirtualDeviceDeviceBackingInfo: VirtualDeviceDeviceBackingInfo{ 674 DeviceName: "VM Network", 675 UseAutoDetect: NewBool(false), 676 }, 677 Network: &ManagedObjectReference{ 678 Type: "Network", 679 Value: "network-27", 680 }, 681 }, 682 Connectable: &VirtualDeviceConnectInfo{ 683 MigrateConnect: "unset", 684 StartConnected: true, 685 Status: "untried", 686 }, 687 ControllerKey: 100, 688 UnitNumber: NewInt32(7), 689 }, 690 AddressType: "assigned", 691 MacAddress: "00:50:56:ac:4d:ed", 692 WakeOnLanEnabled: NewBool(true), 693 ResourceAllocation: &VirtualEthernetCardResourceAllocation{ 694 Reservation: NewInt64(0), 695 Share: SharesInfo{ 696 Shares: 50, 697 Level: "normal", 698 }, 699 Limit: NewInt64(-1), 700 }, 701 UptCompatibilityEnabled: NewBool(true), 702 }, 703 }, 704 Uptv2Enabled: NewBool(false), 705 }, 706 &VirtualUSBXHCIController{ 707 VirtualController: VirtualController{ 708 VirtualDevice: VirtualDevice{ 709 Key: 14000, 710 DeviceInfo: &Description{ 711 Label: "USB xHCI controller ", 712 Summary: "USB xHCI controller", 713 }, 714 SlotInfo: &VirtualDevicePciBusSlotInfo{ 715 PciSlotNumber: -1, 716 }, 717 ControllerKey: 100, 718 UnitNumber: NewInt32(23), 719 }, 720 }, 721 722 AutoConnectDevices: NewBool(false), 723 }, 724 }, 725 MotherboardLayout: "i440bxHostBridge", 726 SimultaneousThreads: 1, 727 }, 728 CpuAllocation: &ResourceAllocationInfo{ 729 Reservation: NewInt64(0), 730 ExpandableReservation: NewBool(false), 731 Limit: NewInt64(-1), 732 Shares: &SharesInfo{ 733 Shares: 1000, 734 Level: SharesLevelNormal, 735 }, 736 }, 737 MemoryAllocation: &ResourceAllocationInfo{ 738 Reservation: NewInt64(0), 739 ExpandableReservation: NewBool(false), 740 Limit: NewInt64(-1), 741 Shares: &SharesInfo{ 742 Shares: 20480, 743 Level: SharesLevelNormal, 744 }, 745 }, 746 LatencySensitivity: &LatencySensitivity{ 747 Level: LatencySensitivitySensitivityLevelNormal, 748 }, 749 MemoryHotAddEnabled: NewBool(false), 750 CpuHotAddEnabled: NewBool(false), 751 CpuHotRemoveEnabled: NewBool(false), 752 ExtraConfig: []BaseOptionValue{ 753 &OptionValue{Key: "nvram", Value: "test.nvram"}, 754 &OptionValue{Key: "svga.present", Value: "TRUE"}, 755 &OptionValue{Key: "pciBridge0.present", Value: "TRUE"}, 756 &OptionValue{Key: "pciBridge4.present", Value: "TRUE"}, 757 &OptionValue{Key: "pciBridge4.virtualDev", Value: "pcieRootPort"}, 758 &OptionValue{Key: "pciBridge4.functions", Value: "8"}, 759 &OptionValue{Key: "pciBridge5.present", Value: "TRUE"}, 760 &OptionValue{Key: "pciBridge5.virtualDev", Value: "pcieRootPort"}, 761 &OptionValue{Key: "pciBridge5.functions", Value: "8"}, 762 &OptionValue{Key: "pciBridge6.present", Value: "TRUE"}, 763 &OptionValue{Key: "pciBridge6.virtualDev", Value: "pcieRootPort"}, 764 &OptionValue{Key: "pciBridge6.functions", Value: "8"}, 765 &OptionValue{Key: "pciBridge7.present", Value: "TRUE"}, 766 &OptionValue{Key: "pciBridge7.virtualDev", Value: "pcieRootPort"}, 767 &OptionValue{Key: "pciBridge7.functions", Value: "8"}, 768 &OptionValue{Key: "hpet0.present", Value: "TRUE"}, 769 &OptionValue{Key: "RemoteDisplay.maxConnections", Value: "-1"}, 770 &OptionValue{Key: "sched.cpu.latencySensitivity", Value: "normal"}, 771 &OptionValue{Key: "vmware.tools.internalversion", Value: "0"}, 772 &OptionValue{Key: "vmware.tools.requiredversion", Value: "12352"}, 773 &OptionValue{Key: "migrate.hostLogState", Value: "none"}, 774 &OptionValue{Key: "migrate.migrationId", Value: "0"}, 775 &OptionValue{Key: "migrate.hostLog", Value: "test-36f94569.hlog"}, 776 &OptionValue{ 777 Key: "viv.moid", 778 Value: "c5b34aa9-d962-4a74-b7d2-b83ec683ba1b:vm-28:lIgQ2t7v24n2nl3N7K3m6IHW2OoPF4CFrJd5N+Tdfio=", 779 }, 780 }, 781 DatastoreUrl: []VirtualMachineConfigInfoDatastoreUrlPair{ 782 { 783 Name: "datastore1", 784 Url: "/vmfs/volumes/63970ed8-4abddd2a-62d7-02003f49c37d", 785 }, 786 }, 787 SwapPlacement: "inherit", 788 BootOptions: &VirtualMachineBootOptions{ 789 EnterBIOSSetup: NewBool(false), 790 EfiSecureBootEnabled: NewBool(false), 791 BootDelay: 1, 792 BootRetryEnabled: NewBool(false), 793 BootRetryDelay: 10000, 794 NetworkBootProtocol: "ipv4", 795 }, 796 FtInfo: nil, 797 RepConfig: nil, 798 VAppConfig: nil, 799 VAssertsEnabled: NewBool(false), 800 ChangeTrackingEnabled: NewBool(false), 801 Firmware: "bios", 802 MaxMksConnections: -1, 803 GuestAutoLockEnabled: NewBool(true), 804 ManagedBy: nil, 805 MemoryReservationLockedToMax: NewBool(false), 806 InitialOverhead: &VirtualMachineConfigInfoOverheadInfo{ 807 InitialMemoryReservation: 214446080, 808 InitialSwapReservation: 2541883392, 809 }, 810 NestedHVEnabled: NewBool(false), 811 VPMCEnabled: NewBool(false), 812 ScheduledHardwareUpgradeInfo: &ScheduledHardwareUpgradeInfo{ 813 UpgradePolicy: "never", 814 ScheduledHardwareUpgradeStatus: "none", 815 }, 816 ForkConfigInfo: nil, 817 VFlashCacheReservation: 0, 818 VmxConfigChecksum: []uint8{ 819 0x69, 0xf7, 0xa7, 0x9e, 820 0xd1, 0xc2, 0x21, 0x4b, 821 0x6c, 0x20, 0x77, 0x0a, 822 0x94, 0x94, 0x99, 0xee, 823 0x17, 0x5d, 0xdd, 0xa3, 824 }, 825 MessageBusTunnelEnabled: NewBool(false), 826 GuestIntegrityInfo: &VirtualMachineGuestIntegrityInfo{ 827 Enabled: NewBool(false), 828 }, 829 MigrateEncryption: "opportunistic", 830 SgxInfo: &VirtualMachineSgxInfo{ 831 FlcMode: "unlocked", 832 RequireAttestation: NewBool(false), 833 }, 834 ContentLibItemInfo: nil, 835 FtEncryptionMode: "ftEncryptionOpportunistic", 836 GuestMonitoringModeInfo: &VirtualMachineGuestMonitoringModeInfo{}, 837 SevEnabled: NewBool(false), 838 NumaInfo: &VirtualMachineVirtualNumaInfo{ 839 AutoCoresPerNumaNode: NewBool(true), 840 VnumaOnCpuHotaddExposed: NewBool(false), 841 }, 842 PmemFailoverEnabled: NewBool(false), 843 VmxStatsCollectionEnabled: NewBool(true), 844 VmOpNotificationToAppEnabled: NewBool(false), 845 VmOpNotificationTimeout: -1, 846 DeviceSwap: &VirtualMachineVirtualDeviceSwap{ 847 LsiToPvscsi: &VirtualMachineVirtualDeviceSwapDeviceSwapInfo{ 848 Enabled: NewBool(true), 849 Applicable: NewBool(false), 850 Status: "none", 851 }, 852 }, 853 Pmem: nil, 854 DeviceGroups: &VirtualMachineVirtualDeviceGroups{}, 855 } 856 857 var retrieveResultForTests = RetrieveResult{ 858 Token: "", 859 Objects: []ObjectContent{ 860 861 { 862 863 DynamicData: DynamicData{}, 864 Obj: ManagedObjectReference{ 865 866 Type: "Folder", 867 Value: "group-d1", 868 }, 869 PropSet: []DynamicProperty{ 870 { 871 872 Name: "alarmActionsEnabled", 873 Val: true, 874 }, 875 { 876 877 Name: "availableField", 878 Val: ArrayOfCustomFieldDef{ 879 880 CustomFieldDef: []CustomFieldDef{}, 881 }, 882 }, 883 884 { 885 886 Name: "childEntity", 887 Val: ArrayOfManagedObjectReference{ 888 ManagedObjectReference: []ManagedObjectReference{}, 889 }, 890 }, 891 { 892 Name: "childType", 893 Val: ArrayOfString{ 894 String: []string{ 895 "Folder", 896 "Datacenter"}, 897 }, 898 }, 899 { 900 Name: "configIssue", 901 Val: ArrayOfEvent{ 902 Event: []BaseEvent{}, 903 }, 904 }, 905 { 906 Name: "configStatus", 907 Val: ManagedEntityStatusGray}, 908 { 909 Name: "customValue", 910 Val: ArrayOfCustomFieldValue{ 911 CustomFieldValue: []BaseCustomFieldValue{}, 912 }, 913 }, 914 { 915 Name: "declaredAlarmState", 916 Val: ArrayOfAlarmState{ 917 AlarmState: []AlarmState{ 918 { 919 Key: "alarm-328.group-d1", 920 Entity: ManagedObjectReference{ 921 Type: "Folder", 922 Value: "group-d1"}, 923 Alarm: ManagedObjectReference{ 924 Type: "Alarm", 925 Value: "alarm-328"}, 926 OverallStatus: "gray", 927 Time: time.Date(2023, time.January, 14, 8, 57, 35, 279575000, time.UTC), 928 Acknowledged: NewBool(false), 929 }, 930 { 931 Key: "alarm-327.group-d1", 932 Entity: ManagedObjectReference{ 933 Type: "Folder", 934 Value: "group-d1"}, 935 Alarm: ManagedObjectReference{ 936 Type: "Alarm", 937 Value: "alarm-327"}, 938 OverallStatus: "green", 939 Time: time.Date(2023, time.January, 14, 8, 56, 40, 83607000, time.UTC), 940 Acknowledged: NewBool(false), 941 EventKey: 756, 942 }, 943 { 944 DynamicData: DynamicData{}, 945 Key: "alarm-326.group-d1", 946 Entity: ManagedObjectReference{ 947 Type: "Folder", 948 Value: "group-d1"}, 949 Alarm: ManagedObjectReference{ 950 Type: "Alarm", 951 Value: "alarm-326"}, 952 OverallStatus: "green", 953 Time: time.Date(2023, 954 time.January, 955 14, 956 8, 957 56, 958 35, 959 82616000, 960 time.UTC), 961 Acknowledged: NewBool(false), 962 EventKey: 751, 963 }, 964 }, 965 }, 966 }, 967 { 968 Name: "disabledMethod", 969 Val: ArrayOfString{ 970 String: []string{}, 971 }, 972 }, 973 { 974 Name: "effectiveRole", 975 Val: ArrayOfInt{ 976 Int: []int32{-1}, 977 }, 978 }, 979 { 980 Name: "name", 981 Val: "Datacenters"}, 982 { 983 Name: "overallStatus", 984 Val: ManagedEntityStatusGray}, 985 { 986 Name: "permission", 987 Val: ArrayOfPermission{ 988 Permission: []Permission{ 989 { 990 Entity: &ManagedObjectReference{ 991 Value: "group-d1", 992 Type: "Folder", 993 }, 994 Principal: "VSPHERE.LOCAL\\vmware-vsm-2bd917c6-e084-4d1f-988d-a68f7525cc94", 995 Group: false, 996 RoleId: 1034, 997 Propagate: true}, 998 { 999 Entity: &ManagedObjectReference{ 1000 Value: "group-d1", 1001 Type: "Folder", 1002 }, 1003 Principal: "VSPHERE.LOCAL\\topologysvc-2bd917c6-e084-4d1f-988d-a68f7525cc94", 1004 Group: false, 1005 RoleId: 1024, 1006 Propagate: true}, 1007 { 1008 Entity: &ManagedObjectReference{ 1009 Value: "group-d1", 1010 Type: "Folder", 1011 }, 1012 Principal: "VSPHERE.LOCAL\\vpxd-extension-2bd917c6-e084-4d1f-988d-a68f7525cc94", 1013 Group: false, 1014 RoleId: -1, 1015 Propagate: true}, 1016 }, 1017 }, 1018 }, 1019 { 1020 Name: "recentTask", 1021 Val: ArrayOfManagedObjectReference{ 1022 ManagedObjectReference: []ManagedObjectReference{ 1023 { 1024 Type: "Task", 1025 Value: "task-186"}, 1026 { 1027 Type: "Task", 1028 Value: "task-187"}, 1029 { 1030 Type: "Task", 1031 Value: "task-188"}, 1032 }, 1033 }, 1034 }, 1035 { 1036 Name: "tag", 1037 Val: ArrayOfTag{ 1038 Tag: []Tag{}, 1039 }, 1040 }, 1041 { 1042 Name: "triggeredAlarmState", 1043 Val: ArrayOfAlarmState{ 1044 AlarmState: []AlarmState{}, 1045 }, 1046 }, 1047 { 1048 Name: "value", 1049 Val: ArrayOfCustomFieldValue{ 1050 CustomFieldValue: []BaseCustomFieldValue{}, 1051 }, 1052 }, 1053 }, 1054 MissingSet: nil, 1055 }, 1056 }, 1057 } 1058 1059 func mustParseTime(layout, value string) time.Time { 1060 t, err := time.Parse(layout, value) 1061 if err != nil { 1062 panic(err) 1063 } 1064 return t 1065 } 1066 1067 func addrOfMustParseTime(layout, value string) *time.Time { 1068 t := mustParseTime(layout, value) 1069 return &t 1070 }