diff options
Diffstat (limited to 'nova/virt/libvirt/config.py')
-rw-r--r-- | nova/virt/libvirt/config.py | 320 |
1 files changed, 297 insertions, 23 deletions
diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py index 7129933f34..231283b8dd 100644 --- a/nova/virt/libvirt/config.py +++ b/nova/virt/libvirt/config.py @@ -24,6 +24,7 @@ helpers for populating up config object instances. """ import time +import typing as ty from collections import OrderedDict from lxml import etree @@ -32,6 +33,7 @@ from oslo_utils import units from nova import exception from nova.i18n import _ +from nova.objects import fields from nova.pci import utils as pci_utils from nova.virt import hardware @@ -45,9 +47,12 @@ class LibvirtConfigObject(object): def __init__(self, **kwargs): super(LibvirtConfigObject, self).__init__() - self.root_name = kwargs.get("root_name") - self.ns_prefix = kwargs.get('ns_prefix') - self.ns_uri = kwargs.get('ns_uri') + self.root_name = kwargs.pop("root_name") + self.ns_prefix = kwargs.pop("ns_prefix", None) + self.ns_uri = kwargs.pop("ns_uri", None) + + # handle programmer error + assert not kwargs def _new_node(self, node_name, **kwargs): if self.ns_uri is None: @@ -63,9 +68,6 @@ class LibvirtConfigObject(object): child.text = str(value) return child - def get_yes_no_str(self, value): - return 'yes' if value else 'no' - def format_dom(self): return self._new_node(self.root_name) @@ -84,6 +86,25 @@ class LibvirtConfigObject(object): pretty_print=pretty_print) return xml_str + @classmethod + def parse_on_off_str(self, value: ty.Optional[str]) -> bool: + if value is not None and value not in ('on', 'off'): + msg = _( + "Element should contain either 'on' or 'off'; " + "found: '%(value)s'" + ) + raise exception.InvalidInput(msg % {'value': value}) + + return value == 'on' + + @classmethod + def get_yes_no_str(self, value: bool) -> str: + return 'yes' if value else 'no' + + @classmethod + def get_on_off_str(self, value: bool) -> str: + return 'on' if value else 'off' + def __repr__(self): return self.to_xml(pretty_print=False) @@ -1532,7 +1553,8 @@ class LibvirtConfigGuestFilesys(LibvirtConfigGuestDevice): class LibvirtConfigGuestDiskEncryptionSecret(LibvirtConfigObject): def __init__(self, **kwargs): - super(LibvirtConfigGuestDiskEncryptionSecret, self).__init__(**kwargs) + super(LibvirtConfigGuestDiskEncryptionSecret, self).__init__( + root_name='diskencryptionsecret', **kwargs) self.type = None self.uuid = None @@ -1552,7 +1574,8 @@ class LibvirtConfigGuestDiskEncryption(LibvirtConfigObject): """ def __init__(self, **kwargs): - super(LibvirtConfigGuestDiskEncryption, self).__init__(**kwargs) + super(LibvirtConfigGuestDiskEncryption, self).__init__( + root_name='diskencryption', **kwargs) self.format = None self.secret = None @@ -1575,7 +1598,8 @@ class LibvirtConfigGuestDiskEncryption(LibvirtConfigObject): class LibvirtConfigGuestDiskMirror(LibvirtConfigObject): def __init__(self, **kwargs): - super(LibvirtConfigGuestDiskMirror, self).__init__(**kwargs) + super(LibvirtConfigGuestDiskMirror, self).__init__( + root_name='diskmirror', **kwargs) self.ready = None def parse_dom(self, xmldoc): @@ -1585,6 +1609,8 @@ class LibvirtConfigGuestDiskMirror(LibvirtConfigObject): class LibvirtConfigGuestIDMap(LibvirtConfigObject): def __init__(self, **kwargs): + if 'root_name' not in kwargs: + kwargs['root_name'] = 'id' super(LibvirtConfigGuestIDMap, self).__init__(**kwargs) self.start = 0 self.target = 0 @@ -1912,6 +1938,8 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice): if self.net_type == 'direct': self.source_dev = c.get('dev') self.source_mode = c.get('mode', 'private') + elif self.net_type == 'vdpa': + self.source_dev = c.get('dev') elif self.net_type == 'vhostuser': self.vhostuser_type = c.get('type') self.vhostuser_mode = c.get('mode') @@ -2019,6 +2047,12 @@ class LibvirtConfigGuestGraphics(LibvirtConfigGuestDevice): self.keymap = None self.listen = None + self.image_compression = None + self.jpeg_compression = None + self.zlib_compression = None + self.playback_compression = None + self.streaming_mode = None + def format_dom(self): dev = super(LibvirtConfigGuestGraphics, self).format_dom() @@ -2029,6 +2063,24 @@ class LibvirtConfigGuestGraphics(LibvirtConfigGuestDevice): if self.listen: dev.set("listen", self.listen) + if self.type == "spice": + if self.image_compression is not None: + dev.append(etree.Element( + 'image', compression=self.image_compression)) + if self.jpeg_compression is not None: + dev.append(etree.Element( + 'jpeg', compression=self.jpeg_compression)) + if self.zlib_compression is not None: + dev.append(etree.Element( + 'zlib', compression=self.zlib_compression)) + if self.playback_compression is not None: + dev.append(etree.Element( + 'playback', compression=self.get_on_off_str( + self.playback_compression))) + if self.streaming_mode is not None: + dev.append(etree.Element( + 'streaming', mode=self.streaming_mode)) + return dev @@ -2168,13 +2220,14 @@ class LibvirtConfigGuestPCIeRootPortController(LibvirtConfigGuestController): class LibvirtConfigGuestHostdev(LibvirtConfigGuestDevice): def __init__(self, **kwargs): - super(LibvirtConfigGuestHostdev, self).\ - __init__(root_name="hostdev", **kwargs) - self.mode = kwargs.get('mode') - self.type = kwargs.get('type') + super(LibvirtConfigGuestHostdev, self).__init__( + root_name="hostdev", **kwargs, + ) + self.mode = None + self.type = None # managed attribute is only used by PCI devices but mediated devices # need to say managed=no - self.managed = kwargs.get('managed', 'yes') + self.managed = "yes" def format_dom(self): dev = super(LibvirtConfigGuestHostdev, self).format_dom() @@ -2194,8 +2247,11 @@ class LibvirtConfigGuestHostdev(LibvirtConfigGuestDevice): class LibvirtConfigGuestHostdevPCI(LibvirtConfigGuestHostdev): def __init__(self, **kwargs): super(LibvirtConfigGuestHostdevPCI, self).\ - __init__(mode='subsystem', type='pci', - **kwargs) + __init__(**kwargs) + + self.mode = 'subsystem' + self.type = 'pci' + # These are returned from libvirt as hexadecimal strings with 0x prefix # even if they have a different meaningful range: domain 16 bit, # bus 8 bit, slot 5 bit, and function 3 bit @@ -2252,10 +2308,14 @@ class LibvirtConfigGuestHostdevPCI(LibvirtConfigGuestHostdev): class LibvirtConfigGuestHostdevMDEV(LibvirtConfigGuestHostdev): def __init__(self, **kwargs): - super(LibvirtConfigGuestHostdevMDEV, self).__init__( - mode='subsystem', type='mdev', managed='no', **kwargs) + super(LibvirtConfigGuestHostdevMDEV, self).__init__(**kwargs) + + self.mode = 'subsystem' + self.type = 'mdev' + self.managed = 'no' + # model attribute is only supported by mediated devices - self.model = kwargs.get('model', 'vfio-pci') + self.model = 'vfio-pci' self.uuid = None def format_dom(self): @@ -2688,6 +2748,19 @@ class LibvirtConfigGuestFeatureKvmHidden(LibvirtConfigGuestFeature): return root +class LibvirtConfigGuestFeatureSMM(LibvirtConfigGuestFeature): + + def __init__(self, **kwargs): + super(LibvirtConfigGuestFeatureSMM, self).__init__("smm", **kwargs) + + def format_dom(self): + root = super(LibvirtConfigGuestFeatureSMM, self).format_dom() + + root.append(etree.Element("smm", state="on")) + + return root + + class LibvirtConfigGuestFeaturePMU(LibvirtConfigGuestFeature): def __init__(self, state, **kwargs): @@ -2704,6 +2777,18 @@ class LibvirtConfigGuestFeaturePMU(LibvirtConfigGuestFeature): return root +class LibvirtConfigGuestFeatureIOAPIC(LibvirtConfigGuestFeature): + + def __init__(self, **kwargs): + super().__init__("ioapic", **kwargs) + self.driver = "qemu" + + def format_dom(self): + root = super().format_dom() + root.set('driver', self.driver) + return root + + class LibvirtConfigGuestFeatureHyperV(LibvirtConfigGuestFeature): # QEMU requires at least this value to be set @@ -2719,6 +2804,15 @@ class LibvirtConfigGuestFeatureHyperV(LibvirtConfigGuestFeature): self.vapic = False self.spinlocks = False self.spinlock_retries = self.MIN_SPINLOCK_RETRIES + self.vpindex = False + self.runtime = False + self.synic = False + self.reset = False + self.frequencies = False + self.reenlightenment = False + self.tlbflush = False + self.ipi = False + self.evmcs = False self.vendorid_spoof = False self.vendorid = self.SPOOFED_VENDOR_ID @@ -2735,6 +2829,24 @@ class LibvirtConfigGuestFeatureHyperV(LibvirtConfigGuestFeature): if self.vendorid_spoof: root.append(etree.Element("vendor_id", state="on", value=self.vendorid)) + if self.vpindex: + root.append(etree.Element('vpindex', state='on')) + if self.runtime: + root.append(etree.Element('runtime', state='on')) + if self.synic: + root.append(etree.Element('synic', state='on')) + if self.reset: + root.append(etree.Element('reset', state='on')) + if self.frequencies: + root.append(etree.Element('frequencies', state='on')) + if self.reenlightenment: + root.append(etree.Element('reenlightenment', state='on')) + if self.tlbflush: + root.append(etree.Element('tlbflush', state='on')) + if self.ipi: + root.append(etree.Element('ipi', state='on')) + if self.evmcs: + root.append(etree.Element('evmcs', state='on')) return root @@ -2810,6 +2922,7 @@ class LibvirtConfigGuest(LibvirtConfigObject): self.os_init_path = None self.os_boot_dev = [] self.os_smbios = None + self.os_arch = None self.os_mach_type = None self.os_bootmenu = False self.devices = [] @@ -2852,6 +2965,8 @@ class LibvirtConfigGuest(LibvirtConfigObject): os.set("firmware", self.os_firmware) type_node = self._text_node("type", self.os_type) + if self.os_arch is not None: + type_node.set("arch", self.os_arch) if self.os_mach_type is not None: type_node.set("machine", self.os_mach_type) os.append(type_node) @@ -3029,6 +3144,7 @@ class LibvirtConfigGuest(LibvirtConfigObject): # LibvirtConfigGuestGidMap # LibvirtConfigGuestCPU # LibvirtConfigGuestVPMEM + # LibvirtConfigGuestIOMMU for c in xmldoc: if c.tag == 'devices': for d in c: @@ -3056,6 +3172,10 @@ class LibvirtConfigGuest(LibvirtConfigObject): obj = LibvirtConfigGuestVPMEM() obj.parse_dom(d) self.devices.append(obj) + elif d.tag == 'iommu': + obj = LibvirtConfigGuestIOMMU() + obj.parse_dom(d) + self.devices.append(obj) if c.tag == 'idmap': for idmap in c: obj = None @@ -3080,7 +3200,10 @@ class LibvirtConfigGuest(LibvirtConfigObject): else: self._parse_basic_props(c) - def add_device(self, dev): + def add_feature(self, dev: LibvirtConfigGuestFeature) -> None: + self.features.append(dev) + + def add_device(self, dev: LibvirtConfigGuestDevice) -> None: self.devices.append(dev) def add_perf_event(self, event): @@ -3130,6 +3253,7 @@ class LibvirtConfigNodeDevice(LibvirtConfigObject): self.pci_capability = None self.mdev_information = None self.vdpa_capability = None + self.vpd_capability = None def parse_dom(self, xmldoc): super(LibvirtConfigNodeDevice, self).parse_dom(xmldoc) @@ -3183,6 +3307,7 @@ class LibvirtConfigNodeDevicePciCap(LibvirtConfigObject): self.numa_node = None self.fun_capability = [] self.mdev_capability = [] + self.vpd_capability = None self.interface = None self.address = None self.link_state = None @@ -3225,6 +3350,10 @@ class LibvirtConfigNodeDevicePciCap(LibvirtConfigObject): mdevcap = LibvirtConfigNodeDeviceMdevCapableSubFunctionCap() mdevcap.parse_dom(c) self.mdev_capability.append(mdevcap) + elif c.tag == "capability" and c.get('type') in ('vpd',): + vpdcap = LibvirtConfigNodeDeviceVpdCap() + vpdcap.parse_dom(c) + self.vpd_capability = vpdcap def pci_address(self): return "%04x:%02x:%02x.%01x" % ( @@ -3277,6 +3406,7 @@ class LibvirtConfigNodeDeviceMdevInformation(LibvirtConfigObject): root_name="capability", **kwargs) self.type = None self.iommu_group = None + self.uuid = None def parse_dom(self, xmldoc): super(LibvirtConfigNodeDeviceMdevInformation, @@ -3286,6 +3416,103 @@ class LibvirtConfigNodeDeviceMdevInformation(LibvirtConfigObject): self.type = c.get('id') if c.tag == "iommuGroup": self.iommu_group = int(c.get('number')) + if c.tag == "uuid": + self.uuid = c.text + + +class LibvirtConfigNodeDeviceVpdCap(LibvirtConfigObject): + + def __init__(self, **kwargs): + super().__init__( + root_name="capability", **kwargs) + self._card_name = None + self._change_level = None + self._manufacture_id = None + self._part_number = None + self._serial_number = None + self._asset_tag = None + self._ro_vendor_fields = {} + self._rw_vendor_fields = {} + self._rw_system_fields = {} + + @staticmethod + def _process_custom_field(fields_dict, field_element): + index = field_element.get('index') + if index: + fields_dict[index] = field_element.text + + def _parse_ro_fields(self, fields_element): + for e in fields_element: + if e.tag == 'change_level': + self._change_level = e.text + elif e.tag == 'manufacture_id': + self._manufacture_id = e.text + elif e.tag == 'part_number': + self._part_number = e.text + elif e.tag == 'serial_number': + self._serial_number = e.text + elif e.tag == 'vendor_field': + self._process_custom_field(self._ro_vendor_fields, e) + + def _parse_rw_fields(self, fields_element): + for e in fields_element: + if e.tag == 'asset_tag': + self._asset_tag = e.text + elif e.tag == 'vendor_field': + self._process_custom_field(self._rw_vendor_fields, e) + elif e.tag == 'system_field': + self._process_custom_field(self._rw_system_fields, e) + + def parse_dom(self, xmldoc): + super(LibvirtConfigNodeDeviceVpdCap, self).parse_dom(xmldoc) + for c in xmldoc: + if c.tag == "name": + self._card_name = c.text + if c.tag == "fields": + access = c.get('access') + if access: + if access == 'readonly': + self._parse_ro_fields(c) + elif access == 'readwrite': + self._parse_rw_fields(c) + else: + continue + + @property + def card_name(self): + return self._card_name + + @property + def change_level(self): + return self._change_level + + @property + def manufacture_id(self): + return self._manufacture_id + + @property + def part_number(self): + return self._part_number + + @property + def card_serial_number(self): + return self._serial_number + + @property + def asset_tag(self): + return self._asset_tag + + @property + def ro_vendor_fields(self): + return self._ro_vendor_fields + + @property + def rw_vendor_fields(self): + return self._rw_vendor_fields + + @property + def rw_system_fields(self): + return self._rw_system_fields class LibvirtConfigGuestRng(LibvirtConfigGuestDevice): @@ -3468,11 +3695,11 @@ class LibvirtConfigGuestVPMEM(LibvirtConfigGuestDevice): self.model = "nvdimm" self.access = "shared" - self.source_path = kwargs.get("devpath", "") - self.align_size = kwargs.get("align_kb", 0) + self.source_path = "" + self.align_size = 0 self.pmem = True - self.target_size = kwargs.get("size_kb", 0) + self.target_size = 0 self.target_node = 0 self.label_size = 2 * units.Ki @@ -3518,6 +3745,53 @@ class LibvirtConfigGuestVPMEM(LibvirtConfigGuestDevice): self.target_size = sub.text +class LibvirtConfigGuestIOMMU(LibvirtConfigGuestDevice): + """https://libvirt.org/formatdomain.html#iommu-devices""" + + def __init__(self, **kwargs): + super().__init__(root_name="iommu", **kwargs) + + self.model: str = fields.VIOMMUModel.AUTO + self.interrupt_remapping: bool = False + self.caching_mode: bool = False + self.eim: bool = False + self.iotlb: bool = False + + def format_dom(self): + iommu = super().format_dom() + iommu.set("model", self.model) + + driver = etree.Element("driver") + driver.set("intremap", self.get_on_off_str(self.interrupt_remapping)) + driver.set("caching_mode", self.get_on_off_str(self.caching_mode)) + + # Set aw_bits to None when the Libvirt version not satisfy + # MIN_LIBVIRT_VIOMMU_AW_BITS in driver. When it's None, means it's not + # supported to have aw_bits. + if hasattr(self, "aw_bits"): + driver.set("aw_bits", str(self.aw_bits)) + driver.set("eim", self.get_on_off_str(self.eim)) + driver.set("iotlb", self.get_on_off_str(self.iotlb)) + iommu.append(driver) + + return iommu + + def parse_dom(self, xmldoc): + super().parse_dom(xmldoc) + self.model = xmldoc.get("model") + + driver = xmldoc.find("./driver") + if driver: + self.interrupt_remapping = self.parse_on_off_str( + driver.get("intremap")) + self.caching_mode = self.parse_on_off_str( + driver.get("caching_mode")) + if driver.get("aw_bits") is not None: + self.aw_bits = int(driver.get("aw_bits")) + self.iotlb = self.parse_on_off_str(driver.get("iotlb")) + self.eim = self.parse_on_off_str(driver.get("eim")) + + class LibvirtConfigGuestMetaNovaPorts(LibvirtConfigObject): def __init__(self, ports=None): |