github.com/vmware/govmomi@v0.51.0/ovf/configspec_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 ovf 6 7 import ( 8 "bytes" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 13 "github.com/vmware/govmomi/vim25/types" 14 ) 15 16 func TestEnvelopeToConfigSpec(t *testing.T) { 17 18 t.Run("Strict", func(t *testing.T) { 19 t.Run("Unsupported ResourceType", func(t *testing.T) { 20 e := testEnvelope(t, "fixtures/unsupported-resourcetype.ovf") 21 _, err := e.ToConfigSpecWithOptions(ToConfigSpecOptions{Strict: true}) 22 if assert.Error(t, err) { 23 assert.Contains(t, err.Error(), "unsupported resource type") 24 if ovfErr, ok := AsErrUnsupportedItem(err); assert.True(t, ok) { 25 assert.Equal(t, ovfErr.Index, 2) 26 assert.Equal(t, ovfErr.InstanceID, "3") 27 assert.Equal(t, ovfErr.Name, "invalidResourceType") 28 assert.Equal(t, ovfErr.ResourceType, CIMResourceType(35)) 29 assert.Equal(t, ovfErr.ResourceSubType, "") 30 } 31 } 32 }) 33 t.Run("Unsupported ResourceSubType", func(t *testing.T) { 34 e := testEnvelope(t, "fixtures/unsupported-resourcesubtype.ovf") 35 _, err := e.ToConfigSpecWithOptions(ToConfigSpecOptions{Strict: true}) 36 if assert.Error(t, err) { 37 assert.Contains(t, err.Error(), "unsupported resource subtype") 38 if ovfErr, ok := AsErrUnsupportedItem(err); assert.True(t, ok) { 39 assert.Equal(t, ovfErr.Index, 2) 40 assert.Equal(t, ovfErr.InstanceID, "3") 41 assert.Equal(t, ovfErr.Name, "invalidResourceSubType") 42 assert.Equal(t, ovfErr.ResourceType, CIMResourceType(1)) 43 assert.Equal(t, ovfErr.ResourceSubType, "invalidresourcesubtype") 44 } 45 } 46 }) 47 }) 48 49 t.Run("Photon 5", func(t *testing.T) { 50 e := testEnvelope(t, "fixtures/photon5.ovf") 51 cs, err := e.ToConfigSpec() 52 assert.NoError(t, err) 53 assert.NotEmpty(t, cs) 54 55 if testing.Verbose() { 56 var w bytes.Buffer 57 enc := types.NewJSONEncoder(&w) 58 enc.SetIndent("", " ") 59 assert.NoError(t, enc.Encode(cs)) 60 t.Logf("\n\nconfigSpec=%s\n\n", w.String()) 61 } 62 }) 63 64 t.Run("Ubuntu 24.10", func(t *testing.T) { 65 e := testEnvelope(t, "fixtures/ubuntu24.10.ovf") 66 cs, err := e.ToConfigSpec() 67 assert.NoError(t, err) 68 assert.NotEmpty(t, cs) 69 70 if testing.Verbose() { 71 var w bytes.Buffer 72 enc := types.NewJSONEncoder(&w) 73 enc.SetIndent("", " ") 74 assert.NoError(t, enc.Encode(cs)) 75 t.Logf("\n\nconfigSpec=%s\n\n", w.String()) 76 } 77 }) 78 79 t.Run("Large", func(t *testing.T) { 80 e := testEnvelope(t, "fixtures/configspec.ovf") 81 cs, err := e.ToConfigSpec() 82 assert.NoError(t, err) 83 assert.NotEmpty(t, cs) 84 85 if testing.Verbose() { 86 var w bytes.Buffer 87 enc := types.NewJSONEncoder(&w) 88 enc.SetIndent("", " ") 89 assert.NoError(t, enc.Encode(cs)) 90 t.Logf("\n\nconfigSpec=%s\n\n", w.String()) 91 } 92 93 assert.Equal(t, "haproxy", cs.Name) 94 assert.Equal(t, int32(2), cs.NumCPUs) 95 assert.Equal(t, int32(2), cs.NumCoresPerSocket) 96 assert.Equal(t, int64(4096), cs.MemoryMB) 97 assert.Equal(t, "vmx-13", cs.Version) 98 99 if assert.Len(t, cs.DeviceChange, 21) { 100 101 var scsiController1Key int32 102 if d, ok := cs.DeviceChange[0].GetVirtualDeviceConfigSpec().Device.(*types.VirtualLsiLogicController); assert.True(t, ok) { 103 scsiController1Key = d.Key 104 assert.Equal(t, int32(0), d.BusNumber) 105 if assert.NotNil(t, d.SlotInfo) { 106 si, ok := d.SlotInfo.(*types.VirtualDevicePciBusSlotInfo) 107 assert.True(t, ok) 108 assert.Equal(t, int32(128), si.PciSlotNumber) 109 } 110 } 111 112 if d, ok := cs.DeviceChange[1].GetVirtualDeviceConfigSpec().Device.(*types.VirtualDisk); assert.True(t, ok) { 113 assert.Equal(t, scsiController1Key, d.ControllerKey) 114 if assert.NotNil(t, d.UnitNumber) { 115 assert.Equal(t, int32(0), *d.UnitNumber) 116 } 117 db, ok := d.Backing.(*types.VirtualDiskFlatVer2BackingInfo) 118 assert.True(t, ok) 119 assert.Equal(t, string(types.VirtualDiskModePersistent), db.DiskMode) 120 if assert.NotNil(t, db.ThinProvisioned) { 121 assert.True(t, *db.ThinProvisioned) 122 } 123 assert.Equal(t, int64(20*1024*1024*1024), d.CapacityInBytes) 124 } 125 126 if bd, ok := cs.DeviceChange[2].GetVirtualDeviceConfigSpec().Device.(types.BaseVirtualEthernetCard); assert.True(t, ok) { 127 d := bd.GetVirtualEthernetCard() 128 if assert.NotNil(t, d.UnitNumber) { 129 assert.Equal(t, int32(2), *d.UnitNumber) 130 } 131 if assert.NotNil(t, d.Connectable) { 132 assert.True(t, d.Connectable.AllowGuestControl) 133 } 134 if assert.NotNil(t, d.WakeOnLanEnabled) { 135 assert.False(t, *d.WakeOnLanEnabled) 136 } 137 if assert.NotNil(t, d.SlotInfo) { 138 si, ok := d.SlotInfo.(*types.VirtualDevicePciBusSlotInfo) 139 assert.True(t, ok) 140 assert.Equal(t, int32(160), si.PciSlotNumber) 141 } 142 } 143 144 if bd, ok := cs.DeviceChange[3].GetVirtualDeviceConfigSpec().Device.(types.BaseVirtualEthernetCard); assert.True(t, ok) { 145 d := bd.GetVirtualEthernetCard() 146 if assert.NotNil(t, d.UnitNumber) { 147 assert.Equal(t, int32(3), *d.UnitNumber) 148 } 149 if assert.NotNil(t, d.Connectable) { 150 assert.True(t, d.Connectable.AllowGuestControl) 151 } 152 if assert.NotNil(t, d.WakeOnLanEnabled) { 153 assert.False(t, *d.WakeOnLanEnabled) 154 } 155 if assert.NotNil(t, d.SlotInfo) { 156 si, ok := d.SlotInfo.(*types.VirtualDevicePciBusSlotInfo) 157 assert.True(t, ok) 158 assert.Equal(t, int32(192), si.PciSlotNumber) 159 } 160 if assert.NotNil(t, d.UptCompatibilityEnabled) { 161 assert.False(t, *d.UptCompatibilityEnabled) 162 } 163 } 164 165 if d, ok := cs.DeviceChange[4].GetVirtualDeviceConfigSpec().Device.(*types.VirtualMachineVideoCard); assert.True(t, ok) { 166 if assert.NotNil(t, d.Enable3DSupport) { 167 assert.False(t, *d.Enable3DSupport) 168 } 169 assert.Equal(t, int64(262144), d.GraphicsMemorySizeInKB) 170 if assert.NotNil(t, d.UseAutoDetect) { 171 assert.False(t, *d.UseAutoDetect) 172 } 173 assert.Equal(t, int64(4096), d.VideoRamSizeInKB) 174 assert.Equal(t, int32(1), d.NumDisplays) 175 assert.Equal(t, "automatic", d.Use3dRenderer) 176 } 177 178 var ideControllerKey int32 179 if d, ok := cs.DeviceChange[5].GetVirtualDeviceConfigSpec().Device.(*types.VirtualIDEController); assert.True(t, ok) { 180 ideControllerKey = d.Key 181 assert.Equal(t, int32(1), d.BusNumber) 182 } 183 184 if d, ok := cs.DeviceChange[6].GetVirtualDeviceConfigSpec().Device.(*types.VirtualIDEController); assert.True(t, ok) { 185 assert.Equal(t, int32(0), d.BusNumber) 186 } 187 188 if d, ok := cs.DeviceChange[7].GetVirtualDeviceConfigSpec().Device.(*types.VirtualCdrom); assert.True(t, ok) { 189 if assert.NotNil(t, d.UnitNumber) { 190 assert.Equal(t, int32(0), *d.UnitNumber) 191 } 192 assert.Equal(t, ideControllerKey, d.ControllerKey) 193 } 194 195 var sioControllerKey int32 196 if d, ok := cs.DeviceChange[8].GetVirtualDeviceConfigSpec().Device.(*types.VirtualSIOController); assert.True(t, ok) { 197 sioControllerKey = d.Key 198 } 199 200 if d, ok := cs.DeviceChange[9].GetVirtualDeviceConfigSpec().Device.(*types.VirtualFloppy); assert.True(t, ok) { 201 if assert.NotNil(t, d.UnitNumber) { 202 assert.Equal(t, int32(0), *d.UnitNumber) 203 } 204 assert.Equal(t, sioControllerKey, d.ControllerKey) 205 } 206 207 if d, ok := cs.DeviceChange[10].GetVirtualDeviceConfigSpec().Device.(*types.VirtualMachineVMCIDevice); assert.True(t, ok) { 208 if assert.NotNil(t, d.AllowUnrestrictedCommunication) { 209 assert.False(t, *d.AllowUnrestrictedCommunication) 210 } 211 } 212 213 var scsiController2Key int32 214 if d, ok := cs.DeviceChange[11].GetVirtualDeviceConfigSpec().Device.(*types.ParaVirtualSCSIController); assert.True(t, ok) { 215 scsiController2Key = d.Key 216 assert.Equal(t, int32(1), d.BusNumber) 217 assert.Nil(t, d.SlotInfo) 218 assert.Len(t, d.Device, 1) 219 } 220 221 if d, ok := cs.DeviceChange[12].GetVirtualDeviceConfigSpec().Device.(*types.VirtualDisk); assert.True(t, ok) { 222 assert.Equal(t, scsiController2Key, d.ControllerKey) 223 if assert.NotNil(t, d.UnitNumber) { 224 assert.Equal(t, int32(0), *d.UnitNumber) 225 } 226 db, ok := d.Backing.(*types.VirtualDiskFlatVer2BackingInfo) 227 assert.True(t, ok) 228 assert.Equal(t, string(types.VirtualDiskModePersistent), db.DiskMode) 229 if assert.NotNil(t, db.ThinProvisioned) { 230 assert.True(t, *db.ThinProvisioned) 231 } 232 assert.Equal(t, int64(10*1024*1024*1024), d.CapacityInBytes) 233 } 234 235 var sataController1Key int32 236 if d, ok := cs.DeviceChange[13].GetVirtualDeviceConfigSpec().Device.(*types.VirtualAHCIController); assert.True(t, ok) { 237 sataController1Key = d.Key 238 assert.Equal(t, int32(0), d.BusNumber) 239 assert.Nil(t, d.SlotInfo) 240 } 241 242 if d, ok := cs.DeviceChange[14].GetVirtualDeviceConfigSpec().Device.(*types.VirtualDisk); assert.True(t, ok) { 243 assert.Equal(t, sataController1Key, d.ControllerKey) 244 if assert.NotNil(t, d.UnitNumber) { 245 assert.Equal(t, int32(0), *d.UnitNumber) 246 } 247 db, ok := d.Backing.(*types.VirtualDiskFlatVer2BackingInfo) 248 assert.True(t, ok) 249 assert.Equal(t, string(types.VirtualDiskModePersistent), db.DiskMode) 250 if assert.NotNil(t, db.ThinProvisioned) { 251 assert.True(t, *db.ThinProvisioned) 252 } 253 assert.Equal(t, int64(10*1024*1024*1024), d.CapacityInBytes) 254 } 255 256 var sataController2Key int32 257 if d, ok := cs.DeviceChange[15].GetVirtualDeviceConfigSpec().Device.(*types.VirtualAHCIController); assert.True(t, ok) { 258 sataController2Key = d.Key 259 assert.Equal(t, int32(1), d.BusNumber) 260 assert.Nil(t, d.SlotInfo) 261 } 262 263 if d, ok := cs.DeviceChange[16].GetVirtualDeviceConfigSpec().Device.(*types.VirtualDisk); assert.True(t, ok) { 264 assert.Equal(t, sataController2Key, d.ControllerKey) 265 if assert.NotNil(t, d.UnitNumber) { 266 assert.Equal(t, int32(0), *d.UnitNumber) 267 } 268 db, ok := d.Backing.(*types.VirtualDiskFlatVer2BackingInfo) 269 assert.True(t, ok) 270 assert.Equal(t, string(types.VirtualDiskModePersistent), db.DiskMode) 271 if assert.NotNil(t, db.ThinProvisioned) { 272 assert.True(t, *db.ThinProvisioned) 273 } 274 assert.Equal(t, int64(10*1024*1024*1024), d.CapacityInBytes) 275 } 276 277 var nvmeController1Key int32 278 if d, ok := cs.DeviceChange[17].GetVirtualDeviceConfigSpec().Device.(*types.VirtualNVMEController); assert.True(t, ok) { 279 nvmeController1Key = d.Key 280 assert.Equal(t, int32(0), d.BusNumber) 281 assert.Nil(t, d.SlotInfo) 282 } 283 284 if d, ok := cs.DeviceChange[18].GetVirtualDeviceConfigSpec().Device.(*types.VirtualDisk); assert.True(t, ok) { 285 assert.Equal(t, nvmeController1Key, d.ControllerKey) 286 if assert.NotNil(t, d.UnitNumber) { 287 assert.Equal(t, int32(0), *d.UnitNumber) 288 } 289 db, ok := d.Backing.(*types.VirtualDiskFlatVer2BackingInfo) 290 assert.True(t, ok) 291 assert.Equal(t, string(types.VirtualDiskModePersistent), db.DiskMode) 292 if assert.NotNil(t, db.ThinProvisioned) { 293 assert.True(t, *db.ThinProvisioned) 294 } 295 assert.Equal(t, int64(10*1024*1024*1024), d.CapacityInBytes) 296 } 297 298 if d, ok := cs.DeviceChange[19].GetVirtualDeviceConfigSpec().Device.(*types.VirtualUSBController); assert.True(t, ok) { 299 if assert.NotNil(t, d.AutoConnectDevices) { 300 assert.True(t, *d.AutoConnectDevices) 301 } 302 if assert.NotNil(t, d.EhciEnabled) { 303 assert.False(t, *d.EhciEnabled) 304 } 305 } 306 307 if d, ok := cs.DeviceChange[20].GetVirtualDeviceConfigSpec().Device.(*types.VirtualUSBXHCIController); assert.True(t, ok) { 308 if assert.NotNil(t, d.AutoConnectDevices) { 309 assert.True(t, *d.AutoConnectDevices) 310 } 311 } 312 } 313 314 assert.ElementsMatch(t, cs.ExtraConfig, []types.BaseOptionValue{ 315 &types.OptionValue{ 316 Key: "guest_rpc.auth.cloud-init.set", 317 Value: "FALSE", 318 }, 319 }) 320 321 if assert.NotNil(t, cs.Flags) { 322 if assert.NotNil(t, cs.Flags.VbsEnabled) { 323 assert.False(t, *cs.Flags.VbsEnabled) 324 } 325 if assert.NotNil(t, cs.Flags.VvtdEnabled) { 326 assert.False(t, *cs.Flags.VvtdEnabled) 327 } 328 } 329 330 assert.Equal(t, "bios", cs.Firmware) 331 332 if assert.NotNil(t, cs.BootOptions) { 333 if assert.NotNil(t, cs.BootOptions.EfiSecureBootEnabled) { 334 assert.False(t, *cs.BootOptions.EfiSecureBootEnabled) 335 } 336 } 337 338 if assert.NotNil(t, cs.CpuHotAddEnabled) { 339 assert.False(t, *cs.CpuHotAddEnabled) 340 } 341 if assert.NotNil(t, cs.CpuHotRemoveEnabled) { 342 assert.False(t, *cs.CpuHotRemoveEnabled) 343 } 344 if assert.NotNil(t, cs.MemoryHotAddEnabled) { 345 assert.False(t, *cs.MemoryHotAddEnabled) 346 } 347 if assert.NotNil(t, cs.NestedHVEnabled) { 348 assert.False(t, *cs.NestedHVEnabled) 349 } 350 if assert.NotNil(t, cs.VirtualICH7MPresent) { 351 assert.False(t, *cs.VirtualICH7MPresent) 352 } 353 354 assert.Equal(t, int32(1), cs.SimultaneousThreads) 355 356 if assert.NotNil(t, cs.VPMCEnabled) { 357 assert.False(t, *cs.VPMCEnabled) 358 } 359 360 if assert.NotNil(t, cs.CpuAllocation) { 361 if assert.NotNil(t, cs.CpuAllocation.Shares) { 362 assert.Equal(t, &types.SharesInfo{ 363 Shares: 2000, 364 Level: types.SharesLevelNormal, 365 }, cs.CpuAllocation.Shares) 366 } 367 } 368 369 if assert.NotNil(t, cs.PowerOpInfo) { 370 assert.Equal(t, &types.VirtualMachineDefaultPowerOpInfo{ 371 PowerOffType: "soft", 372 ResetType: "soft", 373 SuspendType: "hard", 374 StandbyAction: "checkpoint", 375 }, cs.PowerOpInfo) 376 } 377 378 if assert.NotNil(t, cs.Tools) { 379 assert.Equal(t, &types.ToolsConfigInfo{ 380 SyncTimeWithHost: types.NewBool(false), 381 SyncTimeWithHostAllowed: types.NewBool(true), 382 AfterPowerOn: types.NewBool(true), 383 AfterResume: types.NewBool(true), 384 BeforeGuestShutdown: types.NewBool(true), 385 BeforeGuestStandby: types.NewBool(true), 386 ToolsUpgradePolicy: "manual", 387 }, cs.Tools) 388 } 389 390 if va, ok := cs.VAppConfig.(*types.VAppConfigSpec); assert.True(t, ok) { 391 if assert.Len(t, va.Product, 4) { 392 assert.ElementsMatch(t, 393 []types.VAppProductSpec{ 394 { 395 ArrayUpdateSpec: types.ArrayUpdateSpec{ 396 Operation: types.ArrayUpdateOperationAdd, 397 }, 398 Info: &types.VAppProductInfo{ 399 Key: 0, 400 Name: "HAProxy for the Load Balancer API v0.2.0", 401 Vendor: "VMware Inc.", 402 Version: "v0.2.0", 403 FullVersion: "v0.2.0", 404 ProductUrl: "https://vmware.com", 405 VendorUrl: "https://vmware.com", 406 }, 407 }, 408 { 409 ArrayUpdateSpec: types.ArrayUpdateSpec{ 410 Operation: types.ArrayUpdateOperationAdd, 411 }, 412 Info: &types.VAppProductInfo{ 413 Key: 1, 414 ClassId: "appliance", 415 }, 416 }, 417 { 418 ArrayUpdateSpec: types.ArrayUpdateSpec{ 419 Operation: types.ArrayUpdateOperationAdd, 420 }, 421 Info: &types.VAppProductInfo{ 422 Key: 2, 423 ClassId: "network", 424 }, 425 }, 426 { 427 ArrayUpdateSpec: types.ArrayUpdateSpec{ 428 Operation: types.ArrayUpdateOperationAdd, 429 }, 430 Info: &types.VAppProductInfo{ 431 Key: 3, 432 ClassId: "loadbalance", 433 }, 434 }, 435 }, 436 va.Product, 437 ) 438 } 439 if assert.Len(t, va.Property, 6) { 440 assert.ElementsMatch(t, 441 []types.VAppPropertySpec{ 442 443 // default 444 { 445 ArrayUpdateSpec: types.ArrayUpdateSpec{ 446 Operation: types.ArrayUpdateOperationAdd, 447 }, 448 Info: &types.VAppPropertyInfo{ 449 Key: 0, 450 Category: "Load Balancer API", 451 Id: "BUILD_TIMESTAMP", 452 Type: "string", 453 UserConfigurable: types.NewBool(false), 454 DefaultValue: "1615488399", 455 }, 456 }, 457 { 458 ArrayUpdateSpec: types.ArrayUpdateSpec{ 459 Operation: types.ArrayUpdateOperationAdd, 460 }, 461 Info: &types.VAppPropertyInfo{ 462 Key: 1, 463 Category: "Load Balancer API", 464 Id: "BUILD_DATE", 465 Type: "string", 466 UserConfigurable: types.NewBool(false), 467 DefaultValue: "2021-03-11T18:46:39Z", 468 }, 469 }, 470 471 // appliance 472 { 473 ArrayUpdateSpec: types.ArrayUpdateSpec{ 474 Operation: types.ArrayUpdateOperationAdd, 475 }, 476 Info: &types.VAppPropertyInfo{ 477 Key: 2, 478 Category: "1. Appliance Configuration", 479 ClassId: "appliance", 480 Id: "root_pwd", 481 Label: "1.1. Root Password", 482 Type: "string", 483 UserConfigurable: types.NewBool(true), 484 Description: "The initial password of the root user. Subsequent changes of password should be performed in operating system. (6-128 characters)", 485 }, 486 }, 487 488 // network 489 { 490 ArrayUpdateSpec: types.ArrayUpdateSpec{ 491 Operation: types.ArrayUpdateOperationAdd, 492 }, 493 Info: &types.VAppPropertyInfo{ 494 Key: 3, 495 Category: "2. Network Config", 496 ClassId: "network", 497 Id: "hostname", 498 Label: "2.1. Host Name", 499 Type: "string", 500 UserConfigurable: types.NewBool(true), 501 DefaultValue: "haproxy.local", 502 Description: "The host name. A fully-qualified domain name is also supported.", 503 }, 504 }, 505 506 // loadbalance 507 { 508 ArrayUpdateSpec: types.ArrayUpdateSpec{ 509 Operation: types.ArrayUpdateOperationAdd, 510 }, 511 Info: &types.VAppPropertyInfo{ 512 Key: 4, 513 Category: "3. Load Balancing", 514 ClassId: "loadbalance", 515 Id: "service_ip_range", 516 Label: "3.1. Load Balancer IP Ranges, comma-separated in CIDR format (Eg 1.2.3.4/28,5.6.7.8/28)", 517 Type: "string", 518 UserConfigurable: types.NewBool(true), 519 Description: "The IP ranges the load balancer will use for Kubernetes Services and Control Planes. The Appliance will currently respond to ALL the IPs in these ranges whether they're assigned or not. As such, these ranges must not overlap with the IPs assigned for the appliance or any other VMs on the network.", 520 }, 521 }, 522 { 523 ArrayUpdateSpec: types.ArrayUpdateSpec{ 524 Operation: types.ArrayUpdateOperationAdd, 525 }, 526 Info: &types.VAppPropertyInfo{ 527 Key: 5, 528 Category: "3. Load Balancing", 529 ClassId: "loadbalance", 530 Id: "dataplane_port", 531 Label: "3.2. Dataplane API Management Port", 532 Type: "int", 533 UserConfigurable: types.NewBool(true), 534 DefaultValue: "5556", 535 Description: "Specifies the port on which the Dataplane API will be advertized on the Management Network.", 536 }, 537 }, 538 }, 539 va.Property, 540 ) 541 } 542 } 543 }) 544 }