stages: add ovf stage
This stage generates an ovf descriptor and a manifest intended for vSphere. The resulting artifacts can be tarred together with the vmdk into an ova.
This commit is contained in:
parent
cfed69adca
commit
bae4f77661
4 changed files with 977 additions and 1 deletions
154
stages/org.osbuild.ovf
Executable file
154
stages/org.osbuild.ovf
Executable file
|
|
@ -0,0 +1,154 @@
|
|||
#!/usr/bin/python3
|
||||
"""
|
||||
Create OVF descriptor and manifest
|
||||
|
||||
Generates a OVF descriptor (xml) for a vmdk intended for vSphere.
|
||||
|
||||
The OVF descriptor has minimal virtual hardware and no network.
|
||||
Hardware and network can be configured during or after importing
|
||||
into vSphere.
|
||||
|
||||
Buildhost commands used: `qemu-img`.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import osbuild.api
|
||||
from osbuild.util import checksum
|
||||
|
||||
SCHEMA_2 = """
|
||||
"options": {
|
||||
"additionalProperties": false,
|
||||
"required": ["vmdk"],
|
||||
"properties": {
|
||||
"vmdk": {
|
||||
"description": "The vmdk image filename present in the root of the tree",
|
||||
"type": "string",
|
||||
"pattern": "[a-zA-Z0-9+_.-]+.vmdk"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
OVF_TEMPLATE = """<?xml version="1.0"?>
|
||||
<Envelope xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<References>
|
||||
<File ovf:href="{image_name}" ovf:id="file1" ovf:size="{vmdk_size}"/>
|
||||
</References>
|
||||
<DiskSection>
|
||||
<Info>Virtual disk information</Info>
|
||||
<Disk ovf:capacity="{vmdk_capacity}" ovf:capacityAllocationUnits="byte" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="{vmdk_size}"/>
|
||||
</DiskSection>
|
||||
<NetworkSection>
|
||||
<Info>The list of logical networks</Info>
|
||||
</NetworkSection>
|
||||
<VirtualSystem ovf:id="image">
|
||||
<Info>A virtual machine</Info>
|
||||
<Name>VM</Name>
|
||||
<OperatingSystemSection ovf:id="100" vmw:osType="other26xLinux64Guest">
|
||||
<Info>The kind of installed guest operating system</Info>
|
||||
</OperatingSystemSection>
|
||||
<VirtualHardwareSection ovf:transport="com.vmware.guestInfo">
|
||||
<Info>Virtual hardware requirements</Info>
|
||||
<System>
|
||||
<vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
|
||||
<vssd:InstanceID>0</vssd:InstanceID>
|
||||
<vssd:VirtualSystemIdentifier>image</vssd:VirtualSystemIdentifier>
|
||||
<vssd:VirtualSystemType>vmx-15</vssd:VirtualSystemType>
|
||||
</System>
|
||||
<Item>
|
||||
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
|
||||
<rasd:Description>Number of Virtual CPUs</rasd:Description>
|
||||
<rasd:ElementName>2 virtual CPU(s)</rasd:ElementName>
|
||||
<rasd:InstanceID>1</rasd:InstanceID>
|
||||
<rasd:ResourceType>3</rasd:ResourceType>
|
||||
<rasd:VirtualQuantity>2</rasd:VirtualQuantity>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
|
||||
<rasd:Description>Memory Size</rasd:Description>
|
||||
<rasd:ElementName>4096 MB of memory</rasd:ElementName>
|
||||
<rasd:InstanceID>2</rasd:InstanceID>
|
||||
<rasd:ResourceType>4</rasd:ResourceType>
|
||||
<rasd:VirtualQuantity>4096</rasd:VirtualQuantity>
|
||||
</Item>
|
||||
<Item ovf:required="false">
|
||||
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
|
||||
<rasd:ElementName>VirtualVMCIDevice</rasd:ElementName>
|
||||
<rasd:InstanceID>7</rasd:InstanceID>
|
||||
<rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
|
||||
<rasd:ResourceType>1</rasd:ResourceType>
|
||||
<vmw:Config ovf:required="false" vmw:key="allowUnrestrictedCommunication" vmw:value="false"/>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:Address>0</rasd:Address>
|
||||
<rasd:Description>SCSI Controller</rasd:Description>
|
||||
<rasd:ElementName>SCSI Controller 0</rasd:ElementName>
|
||||
<rasd:InstanceID>3</rasd:InstanceID>
|
||||
<rasd:ResourceSubType>VirtualSCSI</rasd:ResourceSubType>
|
||||
<rasd:ResourceType>6</rasd:ResourceType>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:Address>0</rasd:Address>
|
||||
<rasd:Description>IDE Controller</rasd:Description>
|
||||
<rasd:ElementName>VirtualIDEController 0</rasd:ElementName>
|
||||
<rasd:InstanceID>4</rasd:InstanceID>
|
||||
<rasd:ResourceType>5</rasd:ResourceType>
|
||||
</Item>
|
||||
<Item>
|
||||
<rasd:AddressOnParent>0</rasd:AddressOnParent>
|
||||
<rasd:ElementName>Hard disk 0</rasd:ElementName>
|
||||
<rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
|
||||
<rasd:InstanceID>5</rasd:InstanceID>
|
||||
<rasd:Parent>3</rasd:Parent>
|
||||
<rasd:ResourceType>17</rasd:ResourceType>
|
||||
<vmw:Config ovf:required="false" vmw:key="backing.writeThrough" vmw:value="false"/>
|
||||
</Item>
|
||||
<vmw:Config ovf:required="false" vmw:key="bootOptions.efiSecureBootEnabled" vmw:value="false"/>
|
||||
<vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="efi"/>
|
||||
</VirtualHardwareSection>
|
||||
</VirtualSystem>
|
||||
</Envelope>
|
||||
"""
|
||||
|
||||
|
||||
def virtual_size(vmdk):
|
||||
cmd = ["qemu-img", "info", "--output=json", vmdk]
|
||||
res = subprocess.run(cmd, check=True, capture_output=True)
|
||||
if res.returncode != 0:
|
||||
raise RuntimeError("Unable to determine vmdk size")
|
||||
return json.loads(res.stdout)["virtual-size"]
|
||||
|
||||
|
||||
def write_template(vmdk):
|
||||
dirname, basename = os.path.split(vmdk)
|
||||
ovf_data = OVF_TEMPLATE.format(vmdk_size=os.stat(vmdk).st_size,
|
||||
vmdk_capacity=virtual_size(vmdk), image_name=basename)
|
||||
ovf = f"{os.path.join(dirname, basename.removesuffix('.vmdk'))}.ovf"
|
||||
with open(ovf, "w", encoding="utf8") as f:
|
||||
f.write(ovf_data)
|
||||
return ovf
|
||||
|
||||
|
||||
def write_manifest(vmdk, ovf):
|
||||
dirname, basename = os.path.split(vmdk)
|
||||
mf = f"{os.path.join(dirname, basename.removesuffix('.vmdk'))}.mf"
|
||||
with open(mf, "w", encoding="utf8") as f:
|
||||
f.write(f"SHA256({os.path.basename(ovf)})= {checksum.hexdigest_file(ovf, 'sha256')}\n")
|
||||
f.write(f"SHA256({basename})= {checksum.hexdigest_file(vmdk, 'sha256')}\n")
|
||||
|
||||
|
||||
def main(options, tree):
|
||||
vmdk = os.path.join(tree, options["vmdk"])
|
||||
ovf = write_template(vmdk)
|
||||
write_manifest(vmdk, ovf)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = osbuild.api.arguments()
|
||||
r = main(args["options"], args["tree"])
|
||||
sys.exit(r)
|
||||
Loading…
Add table
Add a link
Reference in a new issue