Merge "libvirt: Parse the 'os' element from domainCapabilities"

This commit is contained in:
Zuul 2021-03-04 19:42:53 +00:00 committed by Gerrit Code Review
commit 3386c71fbd
5 changed files with 372 additions and 15 deletions

View File

@ -659,6 +659,10 @@ DOMCAPABILITIES_SPARC = """
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>yes</value>
<value>no</value>
</enum>
</loader>
</os>
<cpu>
@ -732,6 +736,10 @@ DOMCAPABILITIES_ARMV7L = """
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>yes</value>
<value>no</value>
</enum>
</loader>
</os>
<cpu>
@ -845,6 +853,10 @@ DOMCAPABILITIES_PPC = """
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>yes</value>
<value>no</value>
</enum>
</loader>
</os>
<cpu>
@ -924,6 +936,10 @@ DOMCAPABILITIES_MIPS = """
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>yes</value>
<value>no</value>
</enum>
</loader>
</os>
<cpu>
@ -1005,6 +1021,10 @@ DOMCAPABILITIES_MIPSEL = """
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>yes</value>
<value>no</value>
</enum>
</loader>
</os>
<cpu>
@ -1089,6 +1109,10 @@ DOMCAPABILITIES_I686 = """
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>yes</value>
<value>no</value>
</enum>
</loader>
</os>
<cpu>
@ -1231,9 +1255,12 @@ DOMCAPABILITIES_X86_64_TEMPLATE = """
<arch>x86_64</arch>
<vcpu max='255'/>
<os supported='yes'>
<enum name='firmware'>
<value>efi</value>
</enum>
<loader supported='yes'>
<value>/usr/share/qemu/ovmf-x86_64-ms-4m-code.bin</value>
<value>/usr/share/qemu/ovmf-x86_64-ms-code.bin</value>
<value>/usr/share/edk2/ovmf/OVMF_CODE.fd</value>
<value>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</value>
<enum name='type'>
<value>rom</value>
<value>pflash</value>
@ -1242,6 +1269,10 @@ DOMCAPABILITIES_X86_64_TEMPLATE = """
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>yes</value>
<value>no</value>
</enum>
</loader>
</os>
<cpu>

View File

@ -3798,7 +3798,7 @@ class LibvirtConfigGuestVPMEMTest(LibvirtConfigBaseTest):
</memory>""")
class LibvirtConfigVideoModelsTests(LibvirtConfigBaseTest):
class LibvirtConfigDomainCapsVideoModelsTests(LibvirtConfigBaseTest):
def test_parse_video_model(self):
@ -3822,7 +3822,7 @@ class LibvirtConfigVideoModelsTests(LibvirtConfigBaseTest):
self.assertNotIn('gop', obj.models)
class LibvirtConfigDiskBusesTests(LibvirtConfigBaseTest):
class LibvirtConfigDomainCapsDiskBusesTests(LibvirtConfigBaseTest):
def test_parse_disk_buses(self):
@ -3927,6 +3927,50 @@ class LibvirtConfigDomainCapsDevicesTests(LibvirtConfigBaseTest):
obj.video, config.LibvirtConfigDomainCapsVideoModels)
class LibvirtConfigDomainCapsOSTests(LibvirtConfigBaseTest):
def test_parse_domain_caps_os(self):
xml = """
<os supported="yes">
<enum name="firmware">
<value>efi</value>
</enum>
<loader supported="yes">
<value>/usr/share/edk2/ovmf/OVMF_CODE.fd</value>
<value>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</value>
<enum name="type">
<value>rom</value>
<value>pflash</value>
</enum>
<enum name="readonly">
<value>yes</value>
<value>no</value>
</enum>
<enum name="secure">
<value>no</value>
<value>yes</value>
</enum>
</loader>
</os>
"""
obj = config.LibvirtConfigDomainCapsOS()
obj.parse_str(xml)
self.assertTrue(obj.supported)
self.assertTrue(obj.loader_supported)
self.assertTrue(obj.uefi_autoconfig_supported)
self.assertEqual(
[
'/usr/share/edk2/ovmf/OVMF_CODE.fd',
'/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd',
],
obj.loader_paths,
)
self.assertTrue(obj.uefi_supported)
self.assertTrue(obj.secure_boot_supported)
class LibvirtConfigTPMTest(LibvirtConfigBaseTest):
def test_config_tpm_tis_1_2(self):

View File

@ -1259,6 +1259,189 @@ cg /cgroup/memory cg opt1,opt2 0 0
"""
self.assertFalse(self.host.has_hyperthreading)
@mock.patch(
'nova.virt.libvirt.host.libvirt.Connection.getDomainCapabilities')
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities')
def test_supports_uefi__false(self, mock_caps, mock_domcaps):
mock_caps.return_value = """
<capabilities>
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>x86_64</arch>
<vendor>Intel</vendor>
</cpu>
</host>
<guest>
<os_type>hvm</os_type>
<arch name='x86_64'/>
</guest>
</capabilities>
"""
mock_domcaps.return_value = """
<domainCapabilities>
<machine>pc-q35-5.1</machine>
<arch>x86_64</arch>
<os supported='yes'>
<enum name='firmware'>
<value>bios</value>
</enum>
<loader supported='yes'>
<enum name='type'>
<value>rom</value>
</enum>
<enum name='readonly'>
<value>yes</value>
</enum>
<enum name='secure'>
<value>no</value>
</enum>
</loader>
</os>
</domainCapabilities>
"""
self.assertFalse(self.host.supports_uefi)
@mock.patch(
'nova.virt.libvirt.host.libvirt.Connection.getDomainCapabilities')
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities')
def test_supports_uefi__true(self, mock_caps, mock_domcaps):
mock_caps.return_value = """
<capabilities>
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>x86_64</arch>
<vendor>Intel</vendor>
</cpu>
</host>
<guest>
<os_type>hvm</os_type>
<arch name='x86_64'/>
</guest>
</capabilities>
"""
mock_domcaps.return_value = """
<domainCapabilities>
<machine>pc-q35-5.1</machine>
<arch>x86_64</arch>
<os supported='yes'>
<enum name='firmware'>
<value>efi</value>
</enum>
<loader supported='yes'>
<value>/usr/share/edk2/ovmf/OVMF_CODE.fd</value>
<enum name='type'>
<value>rom</value>
<value>pflash</value>
</enum>
<enum name='readonly'>
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>no</value>
</enum>
</loader>
</os>
</domainCapabilities>
"""
self.assertTrue(self.host.supports_uefi)
@mock.patch(
'nova.virt.libvirt.host.libvirt.Connection.getDomainCapabilities')
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities')
def test_supports_secure_boot__false(self, mock_caps, mock_domcaps):
mock_caps.return_value = """
<capabilities>
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>x86_64</arch>
<vendor>Intel</vendor>
</cpu>
</host>
<guest>
<os_type>hvm</os_type>
<arch name='x86_64'/>
</guest>
</capabilities>
"""
mock_domcaps.return_value = """
<domainCapabilities>
<machine>pc-q35-5.1</machine>
<arch>x86_64</arch>
<os supported='yes'>
<enum name='firmware'>
<value>efi</value>
</enum>
<loader supported='yes'>
<value>/usr/share/edk2/ovmf/OVMF_CODE.fd</value>
<enum name='type'>
<value>rom</value>
<value>pflash</value>
</enum>
<enum name='readonly'>
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>no</value>
</enum>
</loader>
</os>
</domainCapabilities>
"""
self.assertFalse(self.host.supports_secure_boot)
@mock.patch(
'nova.virt.libvirt.host.libvirt.Connection.getDomainCapabilities')
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities')
def test_supports_secure_boot__true(self, mock_caps, mock_domcaps):
mock_caps.return_value = """
<capabilities>
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>x86_64</arch>
<vendor>Intel</vendor>
</cpu>
</host>
<guest>
<os_type>hvm</os_type>
<arch name='x86_64'/>
</guest>
</capabilities>
"""
mock_domcaps.return_value = """
<domainCapabilities>
<machine>pc-q35-5.1</machine>
<arch>x86_64</arch>
<os supported='yes'>
<enum name='firmware'>
<value>efi</value>
</enum>
<loader supported='yes'>
<value>/usr/share/edk2/ovmf/OVMF_CODE.fd</value>
<value>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</value>
<enum name='type'>
<value>rom</value>
<value>pflash</value>
</enum>
<enum name='readonly'>
<value>yes</value>
<value>no</value>
</enum>
<enum name='secure'>
<value>yes</value>
<value>no</value>
</enum>
</loader>
</os>
</domainCapabilities>
"""
self.assertTrue(self.host.supports_secure_boot)
vc = fakelibvirt.virConnect
@ -1318,7 +1501,6 @@ class TestLibvirtSEVUnsupported(TestLibvirtSEV):
</capabilities>'''
with mock.patch.object(fakelibvirt.virConnect, 'getCapabilities',
return_value=fake_caps_xml):
self.host._set_amd_sev_support()
self.assertFalse(self.host.supports_amd_sev)

View File

@ -122,6 +122,7 @@ class LibvirtConfigDomainCaps(LibvirtConfigObject):
self._machine = None
self._alias = None
self._devices = None
self._os = None
def parse_dom(self, xmldoc):
super(LibvirtConfigDomainCaps, self).parse_dom(xmldoc)
@ -137,6 +138,10 @@ class LibvirtConfigDomainCaps(LibvirtConfigObject):
devices = LibvirtConfigDomainCapsDevices()
devices.parse_dom(c)
self._devices = devices
elif c.tag == "os":
os = LibvirtConfigDomainCapsOS()
os.parse_dom(c)
self._os = os
@property
def features(self):
@ -166,6 +171,10 @@ class LibvirtConfigDomainCaps(LibvirtConfigObject):
return []
return self._devices
@property
def os(self):
return self._os
class LibvirtConfigDomainCapsVideoModels(LibvirtConfigObject):
@ -287,6 +296,50 @@ class LibvirtConfigDomainCapsFeatureSev(LibvirtConfigObject):
self.cbitpos = int(c.text)
class LibvirtConfigDomainCapsOS(LibvirtConfigObject):
def __init__(self, **kwargs):
super().__init__(root_name='os', **kwargs)
self.supported = False
self.loader_supported = None
self.uefi_autoconfig_supported = None
self.loader_paths = []
self.uefi_supported = None
self.secure_boot_supported = None
def parse_dom(self, xmldoc):
super().parse_dom(xmldoc)
self.supported = xmldoc.get('supported')
for c in xmldoc:
self.loader_supported = c.get('supported')
if c.tag == 'enum':
if c.get('name') == 'firmware':
# theoretically we can also do autoconfiguration of BIOS
# but it's unlikely that anything supports this yet
self.uefi_autoconfig_supported = 'efi' in [
child.text for child in c if child.tag == 'value'
]
elif c.tag == 'loader':
for c2 in c:
if c2.tag == 'value':
self.loader_paths.append(c2.text)
elif c2.tag == 'enum':
if c2.get('name') == 'type':
self.uefi_supported = 'pflash' in [
val.text for val in c2 if val.tag == 'value'
]
elif c2.get('name') == 'secure':
# we might want to ensure 'no' is also supported,
# but a platform that only supports SB sounds odd
self.secure_boot_supported = 'yes' in [
val.text for val in c2 if val.tag == 'value'
]
class LibvirtConfigCapsNUMATopology(LibvirtConfigObject):
def __init__(self, **kwargs):

View File

@ -118,10 +118,12 @@ class Host(object):
self._libvirt_proxy_classes = self._get_libvirt_proxy_classes(libvirt)
self._libvirt_proxy = self._wrap_libvirt_proxy(libvirt)
# AMD SEV is conditional on support in the hardware, kernel,
# qemu, and libvirt. This is determined on demand and
# memoized by the supports_amd_sev property below.
# A number of features are conditional on support in the hardware,
# kernel, QEMU, and/or libvirt. These are determined on demand and
# memoized by various properties below
self._supports_amd_sev = None
self._supports_uefi: ty.Optional[bool] = None
self._supports_secure_boot: ty.Optional[bool] = None
self._has_hyperthreading = None
@ -838,13 +840,9 @@ class Host(object):
_domain_caps.
:returns: a nested dict of dicts which maps architectures to
machine types to instances of config.LibvirtConfigDomainCaps
representing the domain capabilities of the host for that arch
and machine type:
{ arch:
{ machine_type: LibvirtConfigDomainCaps }
}
machine types to instances of config.LibvirtConfigDomainCaps
representing the domain capabilities of the host for that arch and
machine type: ``{arch: machine_type: LibvirtConfigDomainCaps}{``
"""
if self._domain_caps:
return self._domain_caps
@ -1263,6 +1261,55 @@ class Host(object):
return self._has_hyperthreading
@property
def supports_uefi(self) -> bool:
"""Returns whether the host supports UEFI guests."""
if self._supports_uefi is not None:
return self._supports_uefi
# we only check the host architecture since nova doesn't support
# non-host architectures currently
arch = self.get_capabilities().host.cpu.arch
domain_caps = self.get_domain_capabilities()
for machine_type in domain_caps[arch]:
LOG.debug("Checking UEFI support for host arch (%s)", arch)
_domain_caps = domain_caps[arch][machine_type]
if _domain_caps.os.uefi_supported:
LOG.info('UEFI support detected')
self._supports_uefi = True
return True
LOG.debug('No UEFI support detected')
self._supports_uefi = False
return False
@property
def supports_secure_boot(self) -> bool:
"""Determine if the host supports Secure Boot for guests."""
if self._supports_secure_boot is not None:
return self._supports_secure_boot
# we only check the host architecture since nova doesn't support
# non-host architectures currently
arch = self.get_capabilities().host.cpu.arch
domain_caps = self.get_domain_capabilities()
for machine_type in domain_caps[arch]:
LOG.debug(
"Checking secure boot support for host arch (%s)",
arch,
)
_domain_caps = domain_caps[arch][machine_type]
if _domain_caps.os.secure_boot_supported:
LOG.info('Secure Boot support detected')
self._supports_secure_boot = True
return True
LOG.debug('No Secure Boot support detected')
self._supports_secure_boot = False
return False
def _kernel_supports_amd_sev(self):
if not os.path.exists(SEV_KERNEL_PARAM_FILE):
LOG.debug("%s does not exist", SEV_KERNEL_PARAM_FILE)