summaryrefslogtreecommitdiff
path: root/nova/virt/libvirt/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'nova/virt/libvirt/config.py')
-rw-r--r--nova/virt/libvirt/config.py320
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):