feat: VM provisioning, and reboot_host on ESXi
create_vm_from_cloud_init takes the same qcow2/raw cloud images netOrk offers for Proxmox. ESXi can neither boot nor download them, so the driver does both: download with checksum check and one retry, convert with qemu-img to a streamOptimized VMDK (cached by URL), import through a minimal OVF descriptor over NFC, pin requested MACs, grow the disk, and attach a NoCloud seed ISO placed next to the VM's files. NoCloud rather than guestinfo because it needs nothing in the guest; the user-data installs open-vm-tools, which the driver declares as its guest agent. A failure after the import removes the VM again. Placement is a pure decision over inventory rows: a connected host outside maintenance mode that sees the datastore and every port group, with the resource pool and VM folder found by walking up to the datacenter -- one path for a standalone host and for a vCenter. Two faults vcsim surfaced and the tests now pin: a chunked upload body next to a Content-Length is refused with 500, so the disk goes up as a sized file object that also reports lease progress; and a device edit replaces the device as sent, so disk and NIC edits start from the live objects, backing included (vcsim panicked on a disk without one). destroy_vm, get_vm_status, get_network_targets (port groups with their fixed VLAN) and get_image_storages complete the contract. reboot_host on ESXi uses RebootHost_Task and refuses outside maintenance mode: force=True would cut power to running VMs. Tested against vcsim in ESXi and vCenter mode, end to end.
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
"""A minimal OVF 1.0 descriptor for importing one converted cloud image.
|
||||
|
||||
vSphere builds the VM from this (``OvfManager.CreateImportSpec``) and then
|
||||
takes the disk contents over NFC. The descriptor only has to describe what
|
||||
the image does not: CPU, memory, one disk and the NICs.
|
||||
|
||||
Devices are the ones VMware recommends for a 64-bit Linux guest -- PVSCSI and
|
||||
VMXNET3. Both drivers are in the mainline kernel; whether every distribution's
|
||||
*cloud* kernel carries them is one of the things #305 checks on real hardware.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from xml.sax.saxutils import escape, quoteattr
|
||||
|
||||
_HW_VERSION = "vmx-13" # ESXi 6.5 and later
|
||||
_GUEST_OS = "otherLinux64Guest"
|
||||
_STREAM_OPTIMIZED = "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"
|
||||
|
||||
|
||||
def _item(**elements: str) -> str:
|
||||
# CIM requires the rasd elements in alphabetical order.
|
||||
body = "".join(f"<rasd:{k}>{escape(v)}</rasd:{k}>" for k, v in sorted(elements.items()))
|
||||
return f"<Item>{body}</Item>"
|
||||
|
||||
|
||||
def _nic_items(nic_count: int, first_instance: int) -> str:
|
||||
return "".join(
|
||||
_item(
|
||||
AutomaticAllocation="true",
|
||||
Connection=f"net{i}",
|
||||
ElementName=f"Network adapter {i + 1}",
|
||||
InstanceID=str(first_instance + i),
|
||||
ResourceSubType="VmxNet3",
|
||||
ResourceType="10",
|
||||
)
|
||||
for i in range(nic_count)
|
||||
)
|
||||
|
||||
|
||||
def ovf_descriptor(
|
||||
name: str,
|
||||
*,
|
||||
cpu: int,
|
||||
memory_mb: int,
|
||||
capacity_bytes: int,
|
||||
nic_count: int,
|
||||
firmware: str = "bios",
|
||||
) -> str:
|
||||
networks = "".join(
|
||||
f'<Network ovf:name="net{i}"><Description>net{i}</Description></Network>'
|
||||
for i in range(nic_count)
|
||||
)
|
||||
hardware = "".join(
|
||||
[
|
||||
_item(
|
||||
AllocationUnits="hertz * 10^6",
|
||||
ElementName=f"{cpu} virtual CPU(s)",
|
||||
InstanceID="1",
|
||||
ResourceType="3",
|
||||
VirtualQuantity=str(cpu),
|
||||
),
|
||||
_item(
|
||||
AllocationUnits="byte * 2^20",
|
||||
ElementName=f"{memory_mb} MB of memory",
|
||||
InstanceID="2",
|
||||
ResourceType="4",
|
||||
VirtualQuantity=str(memory_mb),
|
||||
),
|
||||
_item(
|
||||
Address="0",
|
||||
ElementName="SCSI controller 0",
|
||||
InstanceID="3",
|
||||
ResourceSubType="VirtualSCSI",
|
||||
ResourceType="6",
|
||||
),
|
||||
_item(
|
||||
AddressOnParent="0",
|
||||
ElementName="Hard disk 1",
|
||||
HostResource="ovf:/disk/vmdisk1",
|
||||
InstanceID="4",
|
||||
Parent="3",
|
||||
ResourceType="17",
|
||||
),
|
||||
_nic_items(nic_count, first_instance=5),
|
||||
]
|
||||
)
|
||||
firmware_config = (
|
||||
'<vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="efi"/>'
|
||||
if firmware == "efi"
|
||||
else ""
|
||||
)
|
||||
return (
|
||||
'<?xml version="1.0" encoding="UTF-8"?>'
|
||||
'<Envelope xmlns="http://schemas.dmtf.org/ovf/envelope/1"'
|
||||
' xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"'
|
||||
' xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"'
|
||||
' xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"'
|
||||
' xmlns:vmw="http://www.vmware.com/schema/ovf">'
|
||||
'<References><File ovf:href="disk.vmdk" ovf:id="file1"/></References>'
|
||||
"<DiskSection><Info>Virtual disks</Info>"
|
||||
f'<Disk ovf:capacity="{capacity_bytes}" ovf:capacityAllocationUnits="byte"'
|
||||
f' ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="{_STREAM_OPTIMIZED}"/>'
|
||||
"</DiskSection>"
|
||||
f"<NetworkSection><Info>Networks</Info>{networks}</NetworkSection>"
|
||||
f"<VirtualSystem ovf:id={quoteattr(name)}>"
|
||||
"<Info>A virtual machine</Info>"
|
||||
f"<Name>{escape(name)}</Name>"
|
||||
f'<OperatingSystemSection ovf:id="101" vmw:osType="{_GUEST_OS}"><Info>Guest OS</Info>'
|
||||
"</OperatingSystemSection>"
|
||||
"<VirtualHardwareSection><Info>Virtual hardware</Info>"
|
||||
"<System><vssd:ElementName>Virtual Hardware Family</vssd:ElementName>"
|
||||
"<vssd:InstanceID>0</vssd:InstanceID>"
|
||||
f"<vssd:VirtualSystemIdentifier>{escape(name)}</vssd:VirtualSystemIdentifier>"
|
||||
f"<vssd:VirtualSystemType>{_HW_VERSION}</vssd:VirtualSystemType></System>"
|
||||
f"{hardware}{firmware_config}"
|
||||
"</VirtualHardwareSection>"
|
||||
"</VirtualSystem>"
|
||||
"</Envelope>"
|
||||
)
|
||||
Reference in New Issue
Block a user