upd
This commit is contained in:
212
node_agent/utils/__old_xml.py
Normal file
212
node_agent/utils/__old_xml.py
Normal file
@ -0,0 +1,212 @@
|
||||
from pathlib import Path
|
||||
|
||||
from lxml.builder import E
|
||||
from lxml.etree import Element, QName, SubElement, tostring, fromstring
|
||||
|
||||
|
||||
XPATH_DOM_NAME = '/domain/name'
|
||||
XPATH_DOM_TITLE = '/domain/title'
|
||||
XPATH_DOM_DESCRIPTION = '/domain/description'
|
||||
XPATH_DOM_METADATA = '/domain/metadata'
|
||||
XPATH_DOM_MEMORY = '/domain/memory'
|
||||
XPATH_DOM_CURRENT_MEMORY = '/domain/currentMemory'
|
||||
XPATH_DOM_VCPU = '/domain/vcpu'
|
||||
XPATH_DOM_OS = '/domian/os'
|
||||
XPATH_DOM_CPU = '/domain/cpu'
|
||||
|
||||
|
||||
class Reader:
|
||||
|
||||
def __init__(xml: str):
|
||||
self.xml = xml
|
||||
self.el = fromstring(self.xml)
|
||||
|
||||
def get_domcaps_machine(self):
|
||||
return self.el.xpath('/domainCapabilities/machine')[0].text
|
||||
|
||||
def get_domcaps_cpus(self):
|
||||
# mode can be: custom, host-model, host-passthrough
|
||||
return self.el.xpath('/domainCapabilities/cpu/mode[@name="custom"]')[0]
|
||||
|
||||
|
||||
class Constructor:
|
||||
"""
|
||||
The XML constructor. This class builds XML configs for libvirt.
|
||||
Features:
|
||||
- Generate basic virtual machine XML. See gen_domain_xml()
|
||||
- Generate virtual disk XML. See gen_volume_xml()
|
||||
- Add arbitrary metadata to XML from special structured dict
|
||||
"""
|
||||
|
||||
def __init__(self, xml: str | None = None):
|
||||
self.xml_string = xml
|
||||
self.xml = None
|
||||
|
||||
@property
|
||||
def domain_xml(self):
|
||||
return self.xml
|
||||
|
||||
def gen_domain_xml(self,
|
||||
name: str,
|
||||
title: str,
|
||||
vcpus: int,
|
||||
vcpu_vendor: str,
|
||||
vcpu_model: str,
|
||||
mac_addr: str,
|
||||
memory: int,
|
||||
volume: Path,
|
||||
vcpu_features: dict | None = None,
|
||||
desc: str = "") -> None:
|
||||
"""
|
||||
Generate default domain XML configuration for virtual machines.
|
||||
See https://lxml.de/tutorial.html#the-e-factory for details.
|
||||
"""
|
||||
domain_xml = 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'),
|
||||
E.os(
|
||||
E.type('hvm', arch='x86_64'),
|
||||
E.boot(dev='cdrom'),
|
||||
E.boot(dev='hd'),
|
||||
),
|
||||
E.features(
|
||||
E.acpi(),
|
||||
E.apic(),
|
||||
),
|
||||
E.cpu(
|
||||
E.vendor(vcpu_vendor),
|
||||
E.model(vcpu_model, fallback='forbid'),
|
||||
E.topology(sockets='1', dies='1', cores=str(vcpus),
|
||||
threads='1'),
|
||||
mode='custom',
|
||||
match='exact',
|
||||
check='partial',
|
||||
),
|
||||
E.on_poweroff('destroy'),
|
||||
E.on_reboot('restart'),
|
||||
E.on_crash('restart'),
|
||||
E.pm(
|
||||
E('suspend-to-mem', enabled='no'),
|
||||
E('suspend-to-disk', enabled='no'),
|
||||
),
|
||||
E.devices(
|
||||
E.emulator('/usr/bin/qemu-system-x86_64'),
|
||||
E.disk(
|
||||
E.driver(name='qemu', type='qcow2', cache='writethrough'),
|
||||
E.source(file=volume),
|
||||
E.target(dev='vda', bus='virtio'),
|
||||
type='file',
|
||||
device='disk',
|
||||
),
|
||||
E.interface(
|
||||
E.source(network='default'),
|
||||
E.mac(address=mac_addr),
|
||||
type='network',
|
||||
),
|
||||
E.graphics(
|
||||
E.listen(type='address'),
|
||||
type='vnc', port='-1', autoport='yes'
|
||||
),
|
||||
E.video(
|
||||
E.model(type='vga', vram='16384', heads='1', primary='yes'),
|
||||
E.address(type='pci', domain='0x0000', bus='0x00',
|
||||
slot='0x02', function='0x0'),
|
||||
),
|
||||
),
|
||||
type='kvm',
|
||||
)
|
||||
return self.to_string(domain_xml)
|
||||
|
||||
def gen_volume_xml(self,
|
||||
device_name: str,
|
||||
file: Path,
|
||||
bus: str = 'virtio',
|
||||
cache: str = 'writethrough',
|
||||
disktype: str = 'file'):
|
||||
disk_xml = E.disk(E.driver(name='qemu', type='qcow2', cache=cache),
|
||||
E.source(file=file),
|
||||
E.target(dev=device_name, bus=bus),
|
||||
type=disktype,
|
||||
device='disk')
|
||||
return self.to_string(disk_xml)
|
||||
|
||||
def add_volume(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def add_meta(self, data: dict, namespace: str, nsprefix: str) -> None:
|
||||
"""
|
||||
Add metadata to domain. See:
|
||||
https://libvirt.org/formatdomain.html#general-metadata
|
||||
"""
|
||||
metadata = metadata_old = self.xml.xpath('/domain/metadata')[0]
|
||||
metadata.append(
|
||||
self.construct_xml(
|
||||
data,
|
||||
namespace=namespace,
|
||||
nsprefix=nsprefix,
|
||||
))
|
||||
self.xml.replace(metadata_old, metadata)
|
||||
|
||||
def remove_meta(self, namespace: str):
|
||||
"""Remove metadata by namespace."""
|
||||
raise NotImplementedError()
|
||||
|
||||
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 to_string(self):
|
||||
return (tostring(self.xml, pretty_print=True,
|
||||
encoding='utf-8').decode().strip())
|
@ -1,54 +1,21 @@
|
||||
from pathlib import Path
|
||||
|
||||
from lxml.builder import E
|
||||
from lxml.etree import Element, QName, SubElement, tostring
|
||||
|
||||
from .mac import random_mac
|
||||
from lxml.etree import Element, QName, SubElement, tostring, fromstring
|
||||
|
||||
|
||||
XPATH_DOMAIN_NAME = '/domain/name'
|
||||
XPATH_DOMAIN_TITLE = '/domain/title'
|
||||
XPATH_DOMAIN_DESCRIPTION = '/domain/description'
|
||||
XPATH_DOMAIN_METADATA = '/domain/metadata'
|
||||
XPATH_DOMAIN_MEMORY = '/domain/memory'
|
||||
XPATH_DOMAIN_CURRENT_MEMORY = '/domain/currentMemory'
|
||||
XPATH_DOMAIN_VCPU = '/domain/vcpu'
|
||||
XPATH_DOMAIN_OS = '/domian/os'
|
||||
XPATH_DOMAIN_CPU = '/domain/cpu'
|
||||
|
||||
|
||||
class XMLConstructor:
|
||||
class Constructor:
|
||||
"""
|
||||
The XML constructor. This class builds XML configs for libvirtd.
|
||||
Features:
|
||||
- Generate basic virtual machine XML. See gen_domain_xml()
|
||||
- Generate virtual disk XML. See gen_volume_xml()
|
||||
- Add arbitrary metadata to XML from special structured dict
|
||||
The XML constructor. This class builds XML configs for libvirt.
|
||||
"""
|
||||
|
||||
def __init__(self, xml: str | None = None):
|
||||
self.xml_string = xml
|
||||
self.xml = None
|
||||
|
||||
@property
|
||||
def domain_xml(self):
|
||||
return self.xml
|
||||
|
||||
def gen_domain_xml(self,
|
||||
name: str,
|
||||
title: str,
|
||||
vcpus: int,
|
||||
vcpu_vendor: str,
|
||||
vcpu_model: str,
|
||||
memory: int,
|
||||
volume: Path,
|
||||
vcpu_features: dict | None = None,
|
||||
desc: str = "") -> None:
|
||||
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:
|
||||
"""
|
||||
Generate default domain XML configuration for virtual machines.
|
||||
See https://lxml.de/tutorial.html#the-e-factory for details.
|
||||
Return basic libvirt domain configuration.
|
||||
"""
|
||||
self.xml = E.domain(
|
||||
domain = E.domain(
|
||||
E.name(name),
|
||||
E.title(title),
|
||||
E.description(desc),
|
||||
@ -56,142 +23,55 @@ class XMLConstructor:
|
||||
E.memory(str(memory), unit='MB'),
|
||||
E.currentMemory(str(memory), unit='MB'),
|
||||
E.vcpu(str(vcpus), placement='static'),
|
||||
E.os(
|
||||
E.type('hvm', arch='x86_64'),
|
||||
E.boot(dev='cdrom'),
|
||||
E.boot(dev='hd'),
|
||||
),
|
||||
E.features(
|
||||
E.acpi(),
|
||||
E.apic(),
|
||||
),
|
||||
E.cpu(
|
||||
E.vendor(vcpu_vendor),
|
||||
E.model(vcpu_model, fallback='forbid'),
|
||||
E.topology(sockets='1', dies='1', cores=str(vcpus),
|
||||
threads='1'),
|
||||
mode='custom',
|
||||
match='exact',
|
||||
check='partial',
|
||||
),
|
||||
E.on_poweroff('destroy'),
|
||||
E.on_reboot('restart'),
|
||||
E.on_crash('restart'),
|
||||
E.pm(
|
||||
E('suspend-to-mem', enabled='no'),
|
||||
E('suspend-to-disk', enabled='no'),
|
||||
),
|
||||
E.devices(
|
||||
E.emulator('/usr/bin/qemu-system-x86_64'),
|
||||
E.disk(
|
||||
E.driver(name='qemu', type='qcow2', cache='writethrough'),
|
||||
E.source(file=volume),
|
||||
E.target(dev='vda', bus='virtio'),
|
||||
type='file',
|
||||
device='disk',
|
||||
),
|
||||
E.interface(
|
||||
E.source(network='default'),
|
||||
E.mac(address=random_mac()),
|
||||
type='network',
|
||||
),
|
||||
E.graphics(
|
||||
E.listen(type='address'),
|
||||
type='vnc', port='-1', autoport='yes'
|
||||
),
|
||||
E.video(
|
||||
E.model(type='vga', vram='16384', heads='1', primary='yes'),
|
||||
E.address(type='pci', domain='0x0000', bus='0x00',
|
||||
slot='0x02', function='0x0'),
|
||||
),
|
||||
),
|
||||
type='kvm',
|
||||
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,
|
||||
device_name: str,
|
||||
file: Path,
|
||||
bus: str = 'virtio',
|
||||
cache: str = 'writethrough',
|
||||
disktype: str = 'file'):
|
||||
return E.disk(E.driver(name='qemu', type='qcow2', cache=cache),
|
||||
E.source(file=file),
|
||||
E.target(dev=device_name, bus=bus),
|
||||
type=disktype,
|
||||
device='disk')
|
||||
|
||||
def add_volume(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def add_meta(self, data: dict, namespace: str, nsprefix: str) -> None:
|
||||
def gen_volume_xml(self, dev: str, mode: str) -> str:
|
||||
"""
|
||||
Add metadata to domain. See:
|
||||
https://libvirt.org/formatdomain.html#general-metadata
|
||||
Todo: No hardcode
|
||||
https://libvirt.org/formatdomain.html#hard-drives-floppy-disks-cdroms
|
||||
"""
|
||||
metadata = metadata_old = self.xml.xpath('/domain/metadata')[0]
|
||||
metadata.append(
|
||||
self.construct_xml(
|
||||
data,
|
||||
namespace=namespace,
|
||||
nsprefix=nsprefix,
|
||||
))
|
||||
self.xml.replace(metadata_old, metadata)
|
||||
|
||||
def remove_meta(self, namespace: str):
|
||||
"""Remove metadata by namespace."""
|
||||
raise NotImplementedError()
|
||||
|
||||
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': 'devices', # 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 to_string(self):
|
||||
return (tostring(self.xml, pretty_print=True,
|
||||
encoding='utf-8').decode().strip())
|
||||
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()
|
||||
|
Reference in New Issue
Block a user