github.com/vmware/govmomi@v0.51.0/ovf/configspec.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 "errors" 9 "fmt" 10 "math" 11 "strconv" 12 "strings" 13 14 "github.com/vmware/govmomi/object" 15 "github.com/vmware/govmomi/vim25/types" 16 ) 17 18 const ( 19 ResourceSubTypeSoundCardSB16 = "vmware.soundcard.sb16" 20 ResourceSubTypeSoundCardEnsoniq1371 = "vmware.soundcard.ensoniq1371" 21 ResourceSubTypeSoundCardHDAudio = "vmware.soundcard.hdaudio" 22 ResourceSubTypePCIController = "vmware.pcicontroller" 23 ResourceSubTypePS2Controller = "vmware.ps2controller" 24 ResourceSubTypeSIOController = "vmware.siocontroller" 25 ResourceSubTypeKeyboard = "vmware.keyboard" 26 ResourceSubTypePointingDevice = "vmware.pointingdevice" 27 ResourceSubTypeVMCI = "vmware.vmci" 28 ResourceSubTypeUSBEHCI = "vmware.usb.ehci" /* USB 2.0 */ 29 ResourceSubTypeUSBXHCI = "vmware.usb.xhci" /* USB 3.0 */ 30 ResourceSubTypeCdromISO = "vmware.cdrom.iso" 31 ResourceSubTypeCDROMRemotePassthrough = "vmware.cdrom.remotepassthrough" 32 ResourceSubTypeCDROMRemoteATAPI = "vmware.cdrom.remoteatapi" 33 ResourceSubTypeCDROMPassthrough = "vmware.cdrom.passthrough" 34 ResourceSubTypeCDROMATAPI = "vmware.cdrom.atapi" 35 ResourceSubTypeFloppyDevice = "vmware.floppy.device" 36 ResourceSubTypeFloppyImage = "vmware.floppy.image" 37 ResourceSubTypeFloppyRemoveDevice = "vmware.floppy.remotedevice" 38 ResourceSubTypeSCSIPassthrough = "vmware.scsi.passthrough" 39 ResourceSubTypeParallelPortDevice = "vmware.parallelport.device" 40 ResourceSubTypeParallelPortFile = "vmware.parallelport.file" 41 ResourceSubTypeSerialPortDevice = "vmware.serialport.device" 42 ResourceSubTypeSerialPortFile = "vmware.serialport.file" 43 ResourceSubTypeSerialPortPipe = "vmware.serialport.pipe" 44 ResourceSubTypeSerialPortURI = "vmware.serialport.uri" 45 ResourceSubTypeSerialPortThinPrint = "vmware.serialport.thinprint" 46 ResourceSubTypeSATAAHCI = "vmware.sata.ahci" 47 ResourceSubTypeSATAAHCIAlter = "ahci" 48 ResourceSubTypeNVMEController = "vmware.nvme.controller" 49 ResourceSubTypeNVDIMMController = "vmware.nvdimm.controller" 50 ResourceSubTypeNVDIMMDevice = "vmware.nvdimm.device" 51 ResourceSubTypePCIPassthrough = "vmware.pci.passthrough" 52 ResourceSubTypePCIPassthroughDVX = "vmware.pci.passthrough-dvx" 53 ResourceSubTypePCIPassthroughAH = "vmware.pci.passthrough-ah" 54 ResourceSubTypePCIPassthroughVMIOP = "vmware.pci.passthrough-vmiop" 55 ResourceSubTypePrecisionClock = "vmware.precisionclock" 56 ResourceSubTypeWatchdogTimer = "vmware.watchdogtimer" 57 ResourceSubTypeVTPM = "vmware.vtpm" 58 ) 59 60 var errUnsupportedResourceSubtype = errors.New("unsupported resource subtype") 61 62 // ErrUnsupportedItem is returned by Envelope.ToConfigSpec when there is an 63 // invalid item configuration. 64 type ErrUnsupportedItem struct { 65 Name string 66 Index int 67 InstanceID string 68 ResourceType CIMResourceType 69 ResourceSubType string 70 LocalizedMessage string 71 } 72 73 func (e ErrUnsupportedItem) Error() string { 74 msg := fmt.Sprintf( 75 "unsupported item name=%q, index=%d, instanceID=%q", 76 e.Name, e.Index, e.InstanceID) 77 if e.ResourceType > 0 { 78 msg = fmt.Sprintf("%s, resourceType=%d", msg, e.ResourceType) 79 } 80 if e.ResourceSubType != "" { 81 msg = fmt.Sprintf("%s, resourceSubType=%s", msg, e.ResourceSubType) 82 } 83 if e.LocalizedMessage != "" { 84 msg = fmt.Sprintf("%s, msg=%q", msg, e.LocalizedMessage) 85 } 86 return msg 87 } 88 89 // AsErrUnsupportedItem returns any possible wrapped ErrUnsupportedItem error 90 // from the provided error. 91 func AsErrUnsupportedItem(in error) (ErrUnsupportedItem, bool) { 92 var out ErrUnsupportedItem 93 if errors.As(in, &out) { 94 return out, true 95 } 96 return ErrUnsupportedItem{}, false 97 } 98 99 func errUnsupportedItem( 100 index int, 101 item itemElement, 102 inner error, 103 args ...any) error { 104 105 err := ErrUnsupportedItem{ 106 Name: item.ElementName, 107 InstanceID: item.InstanceID, 108 Index: index, 109 ResourceSubType: item.resourceSubType, 110 } 111 112 if item.ResourceType != nil { 113 err.ResourceType = *item.ResourceType 114 } 115 116 if len(args) == 1 { 117 err.LocalizedMessage = args[0].(string) 118 } else if len(args) > 1 { 119 err.LocalizedMessage = fmt.Sprintf(args[0].(string), args[1:]...) 120 } 121 122 if inner != nil { 123 return fmt.Errorf("%w, %w", err, inner) 124 } 125 126 return err 127 } 128 129 type itemElement struct { 130 ResourceAllocationSettingData 131 132 resourceSubType string 133 } 134 135 type configSpec = types.VirtualMachineConfigSpec 136 137 // ToConfigSpecOptions influence the behavior of the ToConfigSpecWithOptions 138 // function. 139 type ToConfigSpecOptions struct { 140 141 // Strict indicates that an error should be returned on Item elements in 142 // a VirtualHardware section that have an unknown ResourceType, i.e. a value 143 // that falls outside the range of the enum CIMResourceType. 144 Strict bool 145 } 146 147 // ToConfigSpec calls ToConfigSpecWithOptions with an empty ToConfigSpecOptions 148 // object. 149 func (e Envelope) ToConfigSpec() (types.VirtualMachineConfigSpec, error) { 150 return e.ToConfigSpecWithOptions(ToConfigSpecOptions{}) 151 } 152 153 // ToConfigSpecWithOptions transforms the envelope into a ConfigSpec that may be 154 // used to create a new virtual machine. 155 // Please note, at this time: 156 // - Only a single VirtualSystem is supported. The VirtualSystemCollection 157 // section is ignored. 158 // - Only the first VirtualHardware section is supported. 159 // - Only the default deployment option configuration is considered. Elements 160 // part of a non-default configuration are ignored. 161 // - Disks must specify zero or one HostResource elements. 162 // - Many, many more constraints... 163 func (e Envelope) ToConfigSpecWithOptions( 164 opts ToConfigSpecOptions) (types.VirtualMachineConfigSpec, error) { 165 166 vs := e.VirtualSystem 167 if vs == nil { 168 return configSpec{}, errors.New("no VirtualSystem") 169 } 170 171 // Determine if there is a default configuration. 172 var defaultConfigName string 173 if do := e.DeploymentOption; do != nil { 174 for _, c := range do.Configuration { 175 if d := c.Default; d != nil && *d { 176 defaultConfigName = c.ID 177 break 178 } 179 } 180 } 181 182 dst := configSpec{ 183 Files: &types.VirtualMachineFileInfo{}, 184 Name: vs.ID, 185 } 186 187 // Set the guest ID. 188 if os := vs.OperatingSystem; os != nil && os.OSType != nil { 189 dst.GuestId = *os.OSType 190 } 191 192 // Parse the hardware. 193 if err := e.toHardware(&dst, defaultConfigName, vs, opts); err != nil { 194 return configSpec{}, err 195 } 196 197 // Parse the vApp config. 198 if err := e.toVAppConfig(&dst, defaultConfigName, vs); err != nil { 199 return configSpec{}, err 200 } 201 202 return dst, nil 203 } 204 205 func (e Envelope) toHardware( 206 dst *configSpec, 207 configName string, 208 vs *VirtualSystem, 209 opts ToConfigSpecOptions) error { 210 211 var hw VirtualHardwareSection 212 if len(vs.VirtualHardware) == 0 { 213 return errors.New("no VirtualHardware") 214 } 215 hw = vs.VirtualHardware[0] 216 217 // Set the hardware version. 218 if vmx := hw.System.VirtualSystemType; vmx != nil { 219 dst.Version = *vmx 220 } 221 222 // Parse the config 223 e.toConfig(dst, hw) 224 225 // Parse the extra config. 226 e.toExtraConfig(dst, hw) 227 228 var ( 229 devices object.VirtualDeviceList 230 resources = map[string]types.BaseVirtualDevice{} 231 ) 232 233 for index := range hw.Item { 234 item := itemElement{ 235 ResourceAllocationSettingData: hw.Item[index], 236 } 237 238 if c := item.Configuration; c != nil { 239 if *c != configName { 240 // Skip items that do not belong to the provided config. 241 continue 242 } 243 } 244 245 if item.ResourceType == nil { 246 return errUnsupportedItem(index, item, nil, "nil ResourceType") 247 } 248 249 // Get the resource sub type, if any. 250 if rst := item.ResourceSubType; rst != nil { 251 item.resourceSubType = strings.ToLower(*rst) 252 } 253 254 var ( 255 d types.BaseVirtualDevice 256 err error 257 ) 258 259 switch *item.ResourceType { 260 261 case Other: // 1 262 d, err = e.toOther(item, devices, resources) 263 264 case ComputerSystem: // 2 265 // TODO(akutz) 266 267 case Processor: // 3 268 if item.VirtualQuantity == nil { 269 return errUnsupportedItem( 270 index, item, nil, "nil VirtualQuantity") 271 } 272 dst.NumCPUs = int32(*item.VirtualQuantity) 273 if cps := item.CoresPerSocket; cps != nil { 274 dst.NumCoresPerSocket = cps.Value 275 } 276 277 case Memory: // 4 278 if item.VirtualQuantity == nil { 279 return errUnsupportedItem( 280 index, item, nil, "nil VirtualQuantity") 281 } 282 dst.MemoryMB = int64(*item.VirtualQuantity) 283 284 case IdeController: // 5 285 d, err = e.toIDEController(item, devices, resources) 286 287 case ParallelScsiHba: // 6 288 d, err = e.toSCSIController(item, devices, resources) 289 290 case FcHba: // 7 291 // TODO(akutz) 292 293 case IScsiHba: // 8 294 // TODO(akutz) 295 296 case IbHba: // 9 297 // TODO(akutz) 298 299 case EthernetAdapter: // 10 300 d, err = e.toNetworkInterface(item, devices, resources) 301 302 case OtherNetwork: // 11 303 // TODO(akutz) 304 305 case IoSlot: // 12 306 // TODO(akutz) 307 308 case IoDevice: // 13 309 // TODO(akutz) 310 311 case FloppyDrive: // 14 312 if devices.PickController((*types.VirtualSIOController)(nil)) == nil { 313 c := &types.VirtualSIOController{} 314 c.Key = devices.NewKey() 315 devices = append(devices, c) 316 } 317 d, err = e.toFloppyDrive(item, devices, resources) 318 319 case CdDrive, DvdDrive: // 15, 16 320 d, err = e.toCDOrDVDDrive(item, devices, resources) 321 322 case DiskDrive: // 17 323 d, err = e.toVirtualDisk(item, devices, resources) 324 325 case TapeDrive: // 18 326 // TODO(akutz) 327 328 case StorageExtent: // 19 329 // TODO(akutz) 330 331 case OtherStorage: // 20 332 d, err = e.toOtherStorage(item, devices, resources) 333 334 case SerialPort: // 21 335 // TODO(akutz) 336 337 case ParallelPort: // 22 338 // TODO(akutz) 339 340 case UsbController: // 23 341 d, err = e.toUSB(item, devices, resources) 342 343 case Graphics: // 24 344 d, err = e.toVideoCard(item, devices, resources) 345 346 case Ieee1394: // 25 347 // TODO(akutz) 348 349 case PartitionableUnit: // 26 350 // TODO(akutz) 351 352 case BasePartitionable: // 27 353 // TODO(akutz) 354 355 case PowerSupply: // 28 356 // TODO(akutz) 357 358 case CoolingDevice: // 29 359 // TODO(akutz) 360 361 case EthernetSwitchPort: // 30 362 // TODO(akutz) 363 364 case LogicalDisk: // 31 365 // TODO(akutz) 366 367 case StorageVolume: // 32 368 // TODO(akutz) 369 370 case EthernetConnection: // 33 371 // TODO(akutz) 372 373 default: 374 if opts.Strict { 375 return errUnsupportedItem( 376 index, item, nil, "unsupported resource type") 377 } 378 } 379 380 if err != nil { 381 if err == errUnsupportedResourceSubtype { 382 if !opts.Strict { 383 continue 384 } 385 } 386 return errUnsupportedItem(index, item, err) 387 } 388 389 if d != nil { 390 setConnectable(d, item) 391 if err := e.setUnitNumber(item, d); err != nil { 392 return errUnsupportedItem(index, item, err) 393 } 394 if err := e.setPCISlotNumber(item, d); err != nil { 395 return errUnsupportedItem(index, item, err) 396 } 397 devices = append(devices, d) 398 } 399 } 400 401 // Add the devices to the ConfigSpec. 402 dst.DeviceChange, _ = devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd) 403 404 return nil 405 } 406 407 func (e Envelope) setUnitNumber( 408 item itemElement, 409 d types.BaseVirtualDevice) error { 410 411 if item.AddressOnParent == nil || *item.AddressOnParent == "" { 412 return nil 413 } 414 415 unitNumber, err := strconv.ParseInt(*item.AddressOnParent, 10, 32) 416 if err != nil { 417 return fmt.Errorf("invalid AddressOnParent=%q", *item.AddressOnParent) 418 } 419 420 d.GetVirtualDevice().UnitNumber = types.NewInt32(int32(unitNumber)) 421 return nil 422 } 423 424 func (e Envelope) setBusNumber( 425 item itemElement, 426 d types.BaseVirtualDevice) error { 427 428 if item.Address == nil || *item.Address == "" { 429 return nil 430 } 431 432 c, ok := d.(types.BaseVirtualController) 433 if !ok { 434 return fmt.Errorf("expectedType=%s, actualType=%T", 435 "types.BaseVirtualController", d) 436 } 437 438 busNumber, err := strconv.ParseInt(*item.Address, 10, 32) 439 if err != nil { 440 return fmt.Errorf("invalid Address=%q", *item.Address) 441 } 442 443 c.GetVirtualController().BusNumber = int32(busNumber) 444 return nil 445 } 446 447 func (e Envelope) setPCISlotNumber( 448 item itemElement, 449 d types.BaseVirtualDevice) error { 450 451 var pciSlotNumber int32 = -1 452 453 for i := range item.Config { 454 c := item.Config[i] 455 if c.Key == "slotInfo.pciSlotNumber" { 456 if c.Value != "" { 457 v, err := strconv.ParseInt(c.Value, 10, 32) 458 if err != nil { 459 return fmt.Errorf("invalid pci slot number %s", c.Value) 460 } 461 pciSlotNumber = int32(v) 462 } 463 break 464 } 465 } 466 467 if pciSlotNumber >= 0 { 468 vd := d.GetVirtualDevice() 469 if vd.SlotInfo == nil { 470 vd.SlotInfo = &types.VirtualDevicePciBusSlotInfo{} 471 } 472 si, ok := vd.SlotInfo.(*types.VirtualDevicePciBusSlotInfo) 473 if !ok { 474 return fmt.Errorf("expectedType=%s, actualType=%T", 475 "*types.VirtualDevicePciBusSlotInfo", vd.SlotInfo) 476 } 477 si.PciSlotNumber = pciSlotNumber 478 } 479 480 return nil 481 } 482 483 func (e Envelope) ovfDisk(diskID string) *VirtualDiskDesc { 484 for _, disk := range e.Disk.Disks { 485 if strings.HasSuffix(diskID, disk.DiskID) { 486 return &disk 487 } 488 } 489 return nil 490 } 491 492 func (e Envelope) toVirtualDisk( 493 item itemElement, 494 devices object.VirtualDeviceList, 495 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 496 497 if item.Parent == nil { 498 return nil, fmt.Errorf("missing Parent") 499 } 500 501 r, ok := resources[*item.Parent] 502 if !ok { 503 return nil, nil 504 } 505 506 c, ok := r.(types.BaseVirtualController) 507 if !ok { 508 return nil, fmt.Errorf("expectedType=%s, actualType=%T", 509 "types.BaseVirtualController", r) 510 } 511 512 d := devices.CreateDisk(c, types.ManagedObjectReference{}, "") 513 514 d.VirtualDevice.DeviceInfo = &types.Description{ 515 Label: item.ElementName, 516 } 517 518 // Find the disk's capacity. 519 var capacityInBytes uint64 520 switch len(item.HostResource) { 521 522 case 0: 523 var allocUnitsSz string 524 if item.AllocationUnits != nil { 525 allocUnitsSz = *item.AllocationUnits 526 } 527 capacityInBytes = uint64(ParseCapacityAllocationUnits(allocUnitsSz)) 528 if r := item.VirtualQuantity; r != nil { 529 capacityInBytes *= uint64(*r) 530 } 531 532 case 1: 533 diskID := item.HostResource[0] 534 dd := e.ovfDisk(diskID) 535 if dd == nil { 536 return nil, fmt.Errorf("missing diskID %q", diskID) 537 } 538 539 var allocUnitsSz string 540 if dd.CapacityAllocationUnits != nil { 541 allocUnitsSz = *dd.CapacityAllocationUnits 542 } 543 capacityInBytes = uint64(ParseCapacityAllocationUnits(allocUnitsSz)) 544 if capSz := dd.Capacity; capSz != "" { 545 cap, err := strconv.ParseUint(dd.Capacity, 10, 64) 546 if err != nil { 547 return nil, fmt.Errorf("disk=%s has invalid capacity=%q", 548 diskID, capSz) 549 } 550 capacityInBytes *= cap 551 } 552 553 default: 554 return nil, fmt.Errorf("multiple HostResource elements") 555 } 556 557 if capacityInBytes > math.MaxInt64 { 558 return nil, fmt.Errorf( 559 "capacityInBytes=%d exceeds math.MaxInt64", capacityInBytes) 560 } 561 562 d.CapacityInBytes = int64(capacityInBytes) 563 564 return d, nil 565 } 566 567 func (e Envelope) toCDOrDVDDrive( 568 item itemElement, 569 devices object.VirtualDeviceList, 570 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 571 572 if item.Parent == nil { 573 return nil, fmt.Errorf("missing Parent") 574 } 575 576 r, ok := resources[*item.Parent] 577 if !ok { 578 return nil, nil // Parent is unsupported 579 } 580 581 c, ok := r.(types.BaseVirtualController) 582 if !ok { 583 return nil, fmt.Errorf("expectedType=%s, actualType=%T", 584 "*types.VirtualIDEController", r) 585 } 586 587 d, err := devices.CreateCdrom(c) 588 if err != nil { 589 return nil, err 590 } 591 592 return d, nil 593 } 594 595 func (e Envelope) toSCSIController( 596 item itemElement, 597 devices object.VirtualDeviceList, 598 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 599 600 d, err := devices.CreateSCSIController(item.resourceSubType) 601 if err != nil { 602 return nil, err 603 } 604 if err := e.setBusNumber(item, d); err != nil { 605 return nil, err 606 } 607 resources[item.InstanceID] = d 608 609 return d, nil 610 } 611 612 func (e Envelope) toIDEController( 613 item itemElement, 614 devices object.VirtualDeviceList, 615 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 616 617 d, err := devices.CreateIDEController() 618 if err != nil { 619 return nil, err 620 } 621 if err := e.setBusNumber(item, d); err != nil { 622 return nil, err 623 } 624 resources[item.InstanceID] = d 625 return d, nil 626 } 627 628 func (e Envelope) toOtherStorage( 629 item itemElement, 630 devices object.VirtualDeviceList, 631 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 632 633 switch item.resourceSubType { 634 case ResourceSubTypeSATAAHCI, ResourceSubTypeSATAAHCIAlter: 635 return e.toSATAController(item, devices, resources) 636 case ResourceSubTypeNVMEController: 637 return e.toNVMEController(item, devices, resources) 638 } 639 return nil, errUnsupportedResourceSubtype 640 } 641 642 func (e Envelope) toSATAController( 643 item itemElement, 644 devices object.VirtualDeviceList, 645 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 646 647 d, err := devices.CreateSATAController() 648 if err != nil { 649 return nil, err 650 } 651 if err := e.setBusNumber(item, d); err != nil { 652 return nil, err 653 } 654 resources[item.InstanceID] = d 655 return d, nil 656 } 657 658 func (e Envelope) toNVMEController( 659 item itemElement, 660 devices object.VirtualDeviceList, 661 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 662 663 d, err := devices.CreateNVMEController() 664 if err != nil { 665 return nil, err 666 } 667 if err := e.setBusNumber(item, d); err != nil { 668 return nil, err 669 } 670 resources[item.InstanceID] = d 671 return d, nil 672 } 673 674 func (e Envelope) toUSB( 675 item itemElement, 676 devices object.VirtualDeviceList, 677 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 678 679 var d types.BaseVirtualDevice 680 681 vc := types.VirtualController{ 682 VirtualDevice: types.VirtualDevice{ 683 Key: devices.NewKey(), 684 }, 685 } 686 687 switch item.resourceSubType { 688 case ResourceSubTypeUSBEHCI: 689 c := &types.VirtualUSBController{VirtualController: vc} 690 for i := range item.Config { 691 ic := item.Config[i] 692 switch ic.Key { 693 case "autoConnectDevices": 694 c.AutoConnectDevices = szToBoolPtr(ic.Value) 695 case "ehciEnabled": 696 c.EhciEnabled = szToBoolPtr(ic.Value) 697 } 698 } 699 d = c 700 case ResourceSubTypeUSBXHCI: 701 c := &types.VirtualUSBXHCIController{VirtualController: vc} 702 for i := range item.Config { 703 ic := item.Config[i] 704 switch ic.Key { 705 case "autoConnectDevices": 706 c.AutoConnectDevices = szToBoolPtr(ic.Value) 707 } 708 } 709 d = c 710 default: 711 return nil, errUnsupportedResourceSubtype 712 } 713 714 if err := e.setBusNumber(item, d); err != nil { 715 return nil, err 716 } 717 718 resources[item.InstanceID] = d 719 return d, nil 720 } 721 722 func (e Envelope) toNetworkInterface( 723 item itemElement, 724 devices object.VirtualDeviceList, 725 _ map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 726 727 d, err := devices.CreateEthernetCard(item.resourceSubType, nil) 728 if err != nil { 729 return nil, err 730 } 731 732 nic := d.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard() 733 734 for i := range item.Config { 735 c := item.Config[i] 736 switch c.Key { 737 case "wakeOnLanEnabled": 738 nic.WakeOnLanEnabled = szToBoolPtr(c.Value) 739 case "uptCompatibilityEnabled": 740 nic.UptCompatibilityEnabled = szToBoolPtr(c.Value) 741 } 742 } 743 744 return d, nil 745 } 746 747 func (e Envelope) toFloppyDrive( 748 item itemElement, 749 devices object.VirtualDeviceList, 750 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 751 752 d, err := devices.CreateFloppy() 753 if err != nil { 754 return nil, err 755 } 756 resources[item.InstanceID] = d 757 758 return d, nil 759 } 760 761 func (e Envelope) toVideoCard( 762 item itemElement, 763 devices object.VirtualDeviceList, 764 _ map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 765 766 d := &types.VirtualMachineVideoCard{ 767 VirtualDevice: types.VirtualDevice{ 768 Key: devices.NewKey(), 769 }, 770 } 771 772 for i := range item.Config { 773 c := item.Config[i] 774 switch c.Key { 775 case "enable3DSupport": 776 d.Enable3DSupport = szToBoolPtr(c.Value) 777 case "graphicsMemorySizeInKB": 778 v, err := strconv.ParseInt(c.Value, 10, 64) 779 if err != nil { 780 return nil, fmt.Errorf("invalid %q=%s", c.Key, c.Value) 781 } 782 d.GraphicsMemorySizeInKB = v 783 case "useAutoDetect": 784 d.UseAutoDetect = szToBoolPtr(c.Value) 785 case "videoRamSizeInKB": 786 v, err := strconv.ParseInt(c.Value, 10, 64) 787 if err != nil { 788 return nil, fmt.Errorf("invalid %q=%s", c.Key, c.Value) 789 } 790 d.VideoRamSizeInKB = v 791 case "numDisplays": 792 v, err := strconv.ParseInt(c.Value, 10, 32) 793 if err != nil { 794 return nil, fmt.Errorf("invalid %q=%s", c.Key, c.Value) 795 } 796 d.NumDisplays = int32(v) 797 case "use3dRenderer": 798 d.Use3dRenderer = c.Value 799 } 800 } 801 802 return d, nil 803 } 804 805 func (e Envelope) toOther( 806 item itemElement, 807 devices object.VirtualDeviceList, 808 resources map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 809 810 switch item.resourceSubType { 811 case ResourceSubTypeVMCI: 812 return e.toVMCI(item, devices, resources) 813 } 814 return nil, errUnsupportedResourceSubtype 815 } 816 817 func (e Envelope) toVMCI( 818 item itemElement, 819 devices object.VirtualDeviceList, 820 _ map[string]types.BaseVirtualDevice) (types.BaseVirtualDevice, error) { 821 822 d := &types.VirtualMachineVMCIDevice{ 823 VirtualDevice: types.VirtualDevice{ 824 Key: devices.NewKey(), 825 }, 826 } 827 828 for i := range item.Config { 829 c := item.Config[i] 830 switch c.Key { 831 case "allowUnrestrictedCommunication": 832 d.AllowUnrestrictedCommunication = szToBoolPtr(c.Value) 833 } 834 } 835 836 return d, nil 837 } 838 839 func (e Envelope) toConfig( 840 dst *configSpec, 841 hw VirtualHardwareSection) { 842 843 for i := range hw.Config { 844 c := hw.Config[i] 845 switch c.Key { 846 case "cpuHotAddEnabled": 847 dst.CpuHotAddEnabled = szToBoolPtr(c.Value) 848 case "cpuHotRemoveEnabled": 849 dst.CpuHotRemoveEnabled = szToBoolPtr(c.Value) 850 case "bootOptions.efiSecureBootEnabled": 851 initBootOptions(dst) 852 dst.BootOptions.EfiSecureBootEnabled = szToBoolPtr(c.Value) 853 case "firmware": 854 dst.Firmware = c.Value 855 case "flags.vbsEnabled": 856 initFlags(dst) 857 dst.Flags.VbsEnabled = szToBoolPtr(c.Value) 858 case "flags.vvtdEnabled": 859 initFlags(dst) 860 dst.Flags.VvtdEnabled = szToBoolPtr(c.Value) 861 case "memoryHotAddEnabled": 862 dst.MemoryHotAddEnabled = szToBoolPtr(c.Value) 863 case "nestedHVEnabled": 864 dst.NestedHVEnabled = szToBoolPtr(c.Value) 865 case "virtualICH7MPresent": 866 dst.VirtualICH7MPresent = szToBoolPtr(c.Value) 867 case "virtualSMCPresent": 868 dst.VirtualSMCPresent = szToBoolPtr(c.Value) 869 case "cpuAllocation.shares.shares": 870 initCPUAllocationShares(dst) 871 dst.CpuAllocation.Shares.Shares = szToInt32(c.Value) 872 case "cpuAllocation.shares.level": 873 initCPUAllocationShares(dst) 874 dst.CpuAllocation.Shares.Level = types.SharesLevel(c.Value) 875 case "simultaneousThreads": 876 dst.SimultaneousThreads = szToInt32(c.Value) 877 case "tools.syncTimeWithHost": 878 initToolsConfig(dst) 879 dst.Tools.SyncTimeWithHost = szToBoolPtr(c.Value) 880 case "tools.syncTimeWithHostAllowed": 881 initToolsConfig(dst) 882 dst.Tools.SyncTimeWithHostAllowed = szToBoolPtr(c.Value) 883 case "tools.afterPowerOn": 884 initToolsConfig(dst) 885 dst.Tools.AfterPowerOn = szToBoolPtr(c.Value) 886 case "tools.afterResume": 887 initToolsConfig(dst) 888 dst.Tools.AfterResume = szToBoolPtr(c.Value) 889 case "tools.beforeGuestShutdown": 890 initToolsConfig(dst) 891 dst.Tools.BeforeGuestShutdown = szToBoolPtr(c.Value) 892 case "tools.beforeGuestStandby": 893 initToolsConfig(dst) 894 dst.Tools.BeforeGuestStandby = szToBoolPtr(c.Value) 895 case "tools.toolsUpgradePolicy": 896 initToolsConfig(dst) 897 dst.Tools.ToolsUpgradePolicy = c.Value 898 case "powerOpInfo.powerOffType": 899 initPowerOpInfo(dst) 900 dst.PowerOpInfo.PowerOffType = c.Value 901 case "powerOpInfo.resetType": 902 initPowerOpInfo(dst) 903 dst.PowerOpInfo.ResetType = c.Value 904 case "powerOpInfo.suspendType": 905 initPowerOpInfo(dst) 906 dst.PowerOpInfo.SuspendType = c.Value 907 case "powerOpInfo.standbyAction": 908 initPowerOpInfo(dst) 909 dst.PowerOpInfo.StandbyAction = c.Value 910 case "vPMCEnabled": 911 dst.VPMCEnabled = szToBoolPtr(c.Value) 912 } 913 } 914 } 915 916 func (e Envelope) toExtraConfig( 917 dst *configSpec, 918 hw VirtualHardwareSection) { 919 920 var newEC object.OptionValueList 921 for i := range hw.ExtraConfig { 922 newEC = append(newEC, &types.OptionValue{ 923 Key: hw.ExtraConfig[i].Key, 924 Value: hw.ExtraConfig[i].Value, 925 }) 926 } 927 dst.ExtraConfig = newEC.Join(dst.ExtraConfig...) 928 } 929 930 func initToolsConfig(dst *configSpec) { 931 if dst.Tools == nil { 932 dst.Tools = &types.ToolsConfigInfo{} 933 } 934 } 935 936 func initPowerOpInfo(dst *configSpec) { 937 if dst.PowerOpInfo == nil { 938 dst.PowerOpInfo = &types.VirtualMachineDefaultPowerOpInfo{} 939 } 940 } 941 942 func initCPUAllocation(dst *configSpec) { 943 if dst.CpuAllocation == nil { 944 dst.CpuAllocation = &types.ResourceAllocationInfo{} 945 } 946 } 947 948 func initCPUAllocationShares(dst *configSpec) { 949 initCPUAllocation(dst) 950 if dst.CpuAllocation.Shares == nil { 951 dst.CpuAllocation.Shares = &types.SharesInfo{} 952 } 953 } 954 955 func initFlags(dst *configSpec) { 956 if dst.Flags == nil { 957 dst.Flags = &types.VirtualMachineFlagInfo{} 958 } 959 } 960 961 func initBootOptions(dst *configSpec) { 962 if dst.BootOptions == nil { 963 dst.BootOptions = &types.VirtualMachineBootOptions{} 964 } 965 } 966 967 func setConnectable(dst types.BaseVirtualDevice, src itemElement) { 968 969 d := dst.GetVirtualDevice() 970 for i := range src.Config { 971 c := src.Config[i] 972 switch c.Key { 973 case "connectable.allowGuestControl": 974 if d.Connectable == nil { 975 d.Connectable = &types.VirtualDeviceConnectInfo{} 976 } 977 d.Connectable.AllowGuestControl = szToBool(c.Value) 978 } 979 } 980 } 981 982 func szToBoolPtr(s string) *bool { 983 if s == "" { 984 return nil 985 } 986 b, _ := strconv.ParseBool(s) 987 return &b 988 } 989 990 func szToBool(s string) bool { 991 b, _ := strconv.ParseBool(s) 992 return b 993 } 994 995 func szToInt32(s string) int32 { 996 v, _ := strconv.ParseInt(s, 10, 32) 997 return int32(v) 998 } 999 1000 func deref[T any](pT *T) T { 1001 var t T 1002 if pT != nil { 1003 t = *pT 1004 } 1005 return t 1006 } 1007 1008 func (e Envelope) toVAppConfig( 1009 dst *configSpec, 1010 configName string, 1011 vs *VirtualSystem) error { 1012 1013 if len(vs.Product) == 0 { 1014 return nil 1015 } 1016 1017 vapp := &types.VAppConfigSpec{} 1018 1019 index := 0 1020 for i, product := range vs.Product { 1021 vapp.Product = append(vapp.Product, types.VAppProductSpec{ 1022 ArrayUpdateSpec: types.ArrayUpdateSpec{ 1023 Operation: types.ArrayUpdateOperationAdd, 1024 }, 1025 Info: &types.VAppProductInfo{ 1026 Key: int32(i), 1027 ClassId: deref(product.Class), 1028 InstanceId: deref(product.Instance), 1029 Name: product.Product, 1030 Vendor: product.Vendor, 1031 Version: product.Version, 1032 FullVersion: product.FullVersion, 1033 ProductUrl: product.ProductURL, 1034 VendorUrl: product.VendorURL, 1035 AppUrl: product.AppURL, 1036 }, 1037 }) 1038 1039 for _, p := range product.Property { 1040 if p.Configuration != nil && *p.Configuration != configName { 1041 // Skip properties that are not part of the provided 1042 // configuration. 1043 continue 1044 } 1045 vapp.Property = append(vapp.Property, types.VAppPropertySpec{ 1046 ArrayUpdateSpec: types.ArrayUpdateSpec{ 1047 Operation: types.ArrayUpdateOperationAdd, 1048 }, 1049 Info: &types.VAppPropertyInfo{ 1050 Key: int32(index), 1051 ClassId: deref(product.Class), 1052 InstanceId: deref(product.Instance), 1053 Id: p.Key, 1054 Category: product.Category, 1055 Label: deref(p.Label), 1056 Type: p.Type, 1057 UserConfigurable: p.UserConfigurable, 1058 DefaultValue: deref(p.Default), 1059 Value: "", 1060 Description: deref(p.Description), 1061 }, 1062 }) 1063 index++ 1064 } 1065 } 1066 1067 dst.VAppConfig = vapp 1068 return nil 1069 }