146 lines
5.3 KiB
Python
146 lines
5.3 KiB
Python
from lxml.builder import E
|
|
from lxml.etree import Element, QName, SubElement, fromstring, tostring
|
|
|
|
|
|
class Constructor:
|
|
"""
|
|
The XML constructor. This class builds XML configs for libvirt.
|
|
"""
|
|
|
|
def gen_domain_xml(self, name: str, title: str, desc: str, memory: int,
|
|
vcpus: int, domain_type: str, machine: str, arch: str,
|
|
boot_order: tuple, cpu: str, mac: str) -> str:
|
|
"""
|
|
Return basic libvirt domain configuration.
|
|
"""
|
|
domain = E.domain(
|
|
E.name(name),
|
|
E.title(title),
|
|
E.description(desc),
|
|
E.metadata(),
|
|
E.memory(str(memory), unit='MB'),
|
|
E.currentMemory(str(memory), unit='MB'),
|
|
E.vcpu(str(vcpus), placement='static'),
|
|
type='kvm'
|
|
)
|
|
os = E.os(E.type(domain_type, machine=machine, arch=arch))
|
|
for dev in boot_order:
|
|
os.append(E.boot(dev=dev))
|
|
domain.append(os)
|
|
domain.append(E.features(E.acpi(), E.apic()))
|
|
domain.append(fromstring(cpu))
|
|
domain.append(E.on_poweroff('destroy'))
|
|
domain.append(E.on_reboot('restart'))
|
|
domain.append(E.on_crash('restart'))
|
|
domain.append(E.pm(
|
|
E('suspend-to-mem', enabled='no'),
|
|
E('suspend-to-disk', enabled='no'))
|
|
)
|
|
devices = E.devices()
|
|
devices.append(E.emulator('/usr/bin/qemu-system-x86_64'))
|
|
devices.append(E.interface(
|
|
E.source(network='default'),
|
|
E.mac(address=mac),
|
|
type='network')
|
|
)
|
|
devices.append(E.graphics(type='vnc', port='-1', autoport='yes'))
|
|
devices.append(E.input(type='tablet', bus='usb'))
|
|
devices.append(E.channel(
|
|
E.source(mode='bind'),
|
|
E.target(type='virtio', name='org.qemu.guest_agent.0'),
|
|
E.address(type='virtio-serial', controller='0', bus='0', port='1'),
|
|
type='unix')
|
|
)
|
|
devices.append(E.console(
|
|
E.target(type='serial', port='0'),
|
|
type='pty')
|
|
)
|
|
devices.append(E.video(
|
|
E.model(type='vga', vram='16384', heads='1', primary='yes'))
|
|
)
|
|
domain.append(devices)
|
|
return tostring(domain, encoding='unicode', pretty_print=True).strip()
|
|
|
|
def gen_volume_xml(self, dev: str, mode: str, path: str) -> str:
|
|
"""
|
|
Todo: No hardcode
|
|
https://libvirt.org/formatdomain.html#hard-drives-floppy-disks-cdroms
|
|
"""
|
|
volume = E.disk(type='file', device='disk')
|
|
volume.append(
|
|
E.driver(
|
|
name='qemu',
|
|
type='qcow2',
|
|
cache='writethrough'))
|
|
volume.append(E.source(file=path))
|
|
volume.append(E.target(dev=dev, bus='virtio'))
|
|
if mode.lower() == 'ro':
|
|
volume.append(E.readonly())
|
|
return tostring(volume, encoding='unicode', pretty_print=True).strip()
|
|
|
|
def construct_xml(self,
|
|
tag: dict,
|
|
namespace: str | None = None,
|
|
nsprefix: str | None = None,
|
|
root: Element = None) -> Element:
|
|
"""
|
|
Shortly this recursive function transforms dictonary to XML.
|
|
Return etree.Element built from dict with following structure::
|
|
|
|
{
|
|
'name': 'device', # tag name
|
|
'text': '', # optional key
|
|
'values': { # optional key, must be a dict of key-value pairs
|
|
'type': 'disk'
|
|
},
|
|
children: [] # optional key, must be a list of dicts
|
|
}
|
|
|
|
Child elements must have the same structure. Infinite `children` nesting
|
|
is allowed.
|
|
"""
|
|
use_ns = False
|
|
if isinstance(namespace, str) and isinstance(nsprefix, str):
|
|
use_ns = True
|
|
# Create element
|
|
if root is None:
|
|
if use_ns:
|
|
element = Element(QName(namespace, tag['name']),
|
|
nsmap={nsprefix: namespace})
|
|
else:
|
|
element = Element(tag['name'])
|
|
else:
|
|
if use_ns:
|
|
element = SubElement(root, QName(namespace, tag['name']))
|
|
else:
|
|
element = SubElement(root, tag['name'])
|
|
# Fill up element with content
|
|
if 'text' in tag.keys():
|
|
element.text = tag['text']
|
|
if 'values' in tag.keys():
|
|
for key in tag['values'].keys():
|
|
element.set(str(key), str(tag['values'][key]))
|
|
if 'children' in tag.keys():
|
|
for child in tag['children']:
|
|
element.append(
|
|
self.construct_xml(child,
|
|
namespace=namespace,
|
|
nsprefix=nsprefix,
|
|
root=element))
|
|
return element
|
|
|
|
def add_meta(self, xml: Element, data: dict,
|
|
namespace: str, nsprefix: str) -> None:
|
|
"""
|
|
Add metadata to domain. See:
|
|
https://libvirt.org/formatdomain.html#general-metadata
|
|
"""
|
|
metadata = metadata_old = xml.xpath('/domain/metadata')[0]
|
|
metadata.append(
|
|
self.construct_xml(
|
|
data,
|
|
namespace=namespace,
|
|
nsprefix=nsprefix,
|
|
))
|
|
xml.replace(metadata_old, metadata)
|