libvirt: Move some host arch checks to guest checks

We have a load of tests to enable specific behavior depending on the
guest architecture, but in many cases we're actually testing the host
architecture. This is wrong and breaks our ability to create guests with
non-host architectures plus our ability to rely on mocking of these APIs
by 'FakeLibvirt'. We fix this by switching checks against the CPU
architecture reported by libvirt's host capabilities, to checks against
the guest image metadata (via 'nova.virt.libvirt.utils.get_arch') with a
fallback to the host architecture (via 'os.uname'). This seemingly
simple change subsequently highlights *a lot* of gaps in the mocking
done by 'FakeLibvirtFixture'. Pretty much all of these stem from the
fact that by previously extracting the CPU host architecture from host
capabilities, we were in practice almost always checking against x86-64.
Now that we're doing checks against image metadata with a fallback to
'os.uname()', we suddenly need to be able to provide host and domain
capabilities for other architectures and doing additional mocks for some
of these architecture-specific code paths.

Note that this change effectively removes test coverage for the
'LibvirtDriver._has_uefi_support' helper because we now mock it in the
'FakeLibvirtFixture' to avoid checks in AArch64 tests (AArch64 defaults
to a UEFI bootloader). This is okay since we're going to remove this
helper entirely in a future change.

Also note that this _still_ isn't as complete as I'd like it to be,
owing to time constraints and the sheer depth of the problem here. The
remaining issues will be addressed in follow-ups, potentially alongside
patches to drop support for legacy architectures like MIPS and I686 (the
former has been EOL'd in favour of RISC-V as of March 2021 and both
Intel and AMD stopped manufacturing products using the latter ISA for
anything other than embedded use cases many moons ago).

Change-Id: I158dd70076ecdbef422ef08c11ce6a140c991537
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2021-03-06 14:27:06 +00:00
parent faad45b632
commit 452d2fb3a0
6 changed files with 471 additions and 155 deletions

View File

@ -3174,5 +3174,16 @@ class GenericPoisonFixture(fixtures.Fixture):
super(GenericPoisonFixture, self).setUp()
for meth, why in self.POISON_THESE:
self.useFixture(fixtures.MonkeyPatch(
meth, poison_configure(meth, why)))
# attempt to mock only if not already mocked
location, attribute = meth.rsplit('.', 1)
components = location.split('.')
try:
current = __import__(components[0], {}, {})
for component in components[1:]:
current = getattr(current, component)
if not isinstance(getattr(current, attribute), mock.Mock):
self.useFixture(fixtures.MonkeyPatch(
meth, poison_configure(meth, why)))
except ImportError:
self.useFixture(fixtures.MonkeyPatch(
meth, poison_configure(meth, why)))

View File

@ -249,8 +249,7 @@ FAKE_KVM_GUEST = """
</launchSecurity>
</domain>"""
CAPABILITIES_HOST_TEMPLATE = '''
CAPABILITIES_HOST_X86_64_TEMPLATE = """
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
@ -271,6 +270,9 @@ CAPABILITIES_HOST_TEMPLATE = '''
<feature name='acpi'/>
<feature name='ds'/>
<feature name='vme'/>
<pages unit='KiB' size='4'/>
<pages unit='KiB' size='2048'/>
<pages unit='KiB' size='1048576'/>
</cpu>
<migration_features>
<live/>
@ -283,7 +285,114 @@ CAPABILITIES_HOST_TEMPLATE = '''
<model>apparmor</model>
<doi>0</doi>
</secmodel>
</host>'''
</host>"""
# NOTE(stephenfin): This is incomplete
CAPABILITIES_HOST_I686_TEMPLATE = """
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>i686</arch>
</cpu>
<power_management/>
<iommu support='no'/>
</host>"""
CAPABILITIES_HOST_AARCH64_TEMPLATE = """
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>aarch64</arch>
<model>host</model>
<topology sockets='1' cores='48' threads='1'/>
<pages unit='KiB' size='4'/>
<pages unit='KiB' size='2048'/>
</cpu>
<power_management/>
<migration_features>
<live/>
<uri_transports>
<uri_transport>tcp</uri_transport>
<uri_transport>rdma</uri_transport>
</uri_transports>
</migration_features>
%(topology)s
<secmodel>
<model>apparmor</model>
<doi>0</doi>
</secmodel>
<secmodel>
<model>dac</model>
<doi>0</doi>
<baselabel type='kvm'>+0:+0</baselabel>
<baselabel type='qemu'>+0:+0</baselabel>
</secmodel>
</host>"""
# NOTE(stephenfin): This is incomplete
CAPABILITIES_HOST_ARMV7_TEMPLATE = """
<host>
<cpu>
<arch>armv7l</arch>
</cpu>
<power_management/>
<iommu support='no'/>
</host>"""
# NOTE(stephenfin): This is incomplete
CAPABILITIES_HOST_PPC_TEMPLATE = """
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>ppc</arch>
</cpu>
<power_management/>
<iommu support='no'/>
</host>"""
# NOTE(stephenfin): This is incomplete
CAPABILITIES_HOST_PPC64_TEMPLATE = """
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>ppc64</arch>
</cpu>
<power_management/>
<iommu support='no'/>
</host>"""
# NOTE(stephenfin): This is incomplete
CAPABILITIES_HOST_PPC64LE_TEMPLATE = """
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>ppc64le</arch>
</cpu>
<power_management/>
<iommu support='no'/>
</host>"""
# NOTE(stephenfin): This is incomplete
CAPABILITIES_HOST_S390X_TEMPLATE = """
<host>
<uuid>cef19ce0-0ca2-11df-855d-b19fbce37686</uuid>
<cpu>
<arch>s390x</arch>
</cpu>
<power_management/>
<iommu support='no'/>
</host>"""
CAPABILITIES_HOST_TEMPLATES = {
Architecture.X86_64: CAPABILITIES_HOST_X86_64_TEMPLATE,
Architecture.I686: CAPABILITIES_HOST_I686_TEMPLATE,
Architecture.AARCH64: CAPABILITIES_HOST_AARCH64_TEMPLATE,
Architecture.ARMV7: CAPABILITIES_HOST_ARMV7_TEMPLATE,
Architecture.PPC: CAPABILITIES_HOST_PPC_TEMPLATE,
Architecture.PPC64: CAPABILITIES_HOST_PPC64_TEMPLATE,
Architecture.PPC64LE: CAPABILITIES_HOST_PPC64LE_TEMPLATE,
Architecture.S390X: CAPABILITIES_HOST_S390X_TEMPLATE,
}
# NOTE(aspiers): HostTestCase has tests which assert that for any
# given (arch, domain) listed in the guest capabilities here, all
@ -500,6 +609,100 @@ CAPABILITIES_GUEST = {
</features>
</guest>''',
'aarch64': '''
<guest>
<os_type>hvm</os_type>
<arch name='aarch64'>
<wordsize>64</wordsize>
<emulator>/usr/bin/qemu-system-aarch64</emulator>
<machine maxCpus='1'>integratorcp</machine>
<machine maxCpus='2'>ast2600-evb</machine>
<machine maxCpus='1'>borzoi</machine>
<machine maxCpus='1'>spitz</machine>
<machine maxCpus='255'>virt-2.7</machine>
<machine maxCpus='2'>nuri</machine>
<machine maxCpus='2'>mcimx7d-sabre</machine>
<machine maxCpus='1'>romulus-bmc</machine>
<machine maxCpus='512'>virt-3.0</machine>
<machine maxCpus='512'>virt-5.0</machine>
<machine maxCpus='255'>virt-2.10</machine>
<machine maxCpus='255'>virt-2.8</machine>
<machine maxCpus='2'>musca-b1</machine>
<machine maxCpus='4'>realview-pbx-a9</machine>
<machine maxCpus='1'>versatileab</machine>
<machine maxCpus='1'>kzm</machine>
<machine maxCpus='2'>musca-a</machine>
<machine maxCpus='512'>virt-3.1</machine>
<machine maxCpus='1'>mcimx6ul-evk</machine>
<machine maxCpus='512'>virt-5.1</machine>
<machine canonical='virt-5.1' maxCpus='512'>virt</machine>
<machine maxCpus='2'>smdkc210</machine>
<machine maxCpus='1'>sx1</machine>
<machine maxCpus='4'>raspi2</machine>
<machine maxCpus='255'>virt-2.11</machine>
<machine maxCpus='1'>imx25-pdk</machine>
<machine maxCpus='255'>virt-2.9</machine>
<machine maxCpus='4'>orangepi-pc</machine>
<machine maxCpus='1'>z2</machine>
<machine maxCpus='1'>xilinx-zynq-a9</machine>
<machine maxCpus='6'>xlnx-zcu102</machine>
<machine maxCpus='4'>raspi3</machine>
<machine maxCpus='1'>tosa</machine>
<machine maxCpus='255'>virt-2.12</machine>
<machine maxCpus='2'>mps2-an521</machine>
<machine maxCpus='4'>sabrelite</machine>
<machine maxCpus='1'>mps2-an511</machine>
<machine maxCpus='1'>canon-a1100</machine>
<machine maxCpus='1'>realview-eb</machine>
<machine maxCpus='1'>emcraft-sf2</machine>
<machine maxCpus='1'>realview-pb-a8</machine>
<machine maxCpus='512'>sbsa-ref</machine>
<machine maxCpus='512'>virt-4.0</machine>
<machine maxCpus='1'>palmetto-bmc</machine>
<machine maxCpus='1'>sx1-v1</machine>
<machine maxCpus='1'>n810</machine>
<machine maxCpus='2'>tacoma-bmc</machine>
<machine maxCpus='1'>n800</machine>
<machine maxCpus='512'>virt-4.1</machine>
<machine maxCpus='1'>versatilepb</machine>
<machine maxCpus='1'>terrier</machine>
<machine maxCpus='1'>mainstone</machine>
<machine maxCpus='4'>realview-eb-mpcore</machine>
<machine maxCpus='512'>virt-4.2</machine>
<machine maxCpus='1'>witherspoon-bmc</machine>
<machine maxCpus='1'>swift-bmc</machine>
<machine maxCpus='4'>vexpress-a9</machine>
<machine maxCpus='4'>midway</machine>
<machine maxCpus='1'>musicpal</machine>
<machine maxCpus='1'>lm3s811evb</machine>
<machine maxCpus='1'>lm3s6965evb</machine>
<machine maxCpus='1'>microbit</machine>
<machine maxCpus='1'>mps2-an505</machine>
<machine maxCpus='1'>mps2-an385</machine>
<machine maxCpus='1'>cubieboard</machine>
<machine maxCpus='1'>verdex</machine>
<machine maxCpus='1'>netduino2</machine>
<machine maxCpus='2'>xlnx-versal-virt</machine>
<machine maxCpus='4'>vexpress-a15</machine>
<machine maxCpus='1'>sonorapass-bmc</machine>
<machine maxCpus='1'>cheetah</machine>
<machine maxCpus='255'>virt-2.6</machine>
<machine maxCpus='1'>ast2500-evb</machine>
<machine maxCpus='4'>highbank</machine>
<machine maxCpus='1'>akita</machine>
<machine maxCpus='1'>connex</machine>
<machine maxCpus='1'>netduinoplus2</machine>
<machine maxCpus='1'>collie</machine>
<domain type='qemu'/>
</arch>
<features>
<acpi default='on' toggle='yes'/>
<cpuselection/>
<deviceboot/>
<disksnapshot default='on' toggle='no'/>
</features>
</guest>''',
'armv7l': '''
<guest>
<os_type>hvm</os_type>
@ -628,20 +831,6 @@ CAPABILITIES_GUEST = {
</guest>'''
}
CAPABILITIES_TEMPLATE = (
"<capabilities>\n" +
CAPABILITIES_HOST_TEMPLATE +
CAPABILITIES_GUEST['i686'] +
CAPABILITIES_GUEST['x86_64'] +
CAPABILITIES_GUEST['armv7l'] +
CAPABILITIES_GUEST['mips'] +
CAPABILITIES_GUEST['mipsel'] +
CAPABILITIES_GUEST['sparc'] +
CAPABILITIES_GUEST['ppc'] +
"</capabilities>\n"
)
DOMCAPABILITIES_SPARC = """
<domainCapabilities>
<path>/usr/bin/qemu-system-sparc</path>
@ -719,7 +908,7 @@ DOMCAPABILITIES_SPARC = """
</domainCapabilities>
"""
DOMCAPABILITIES_ARMV7L = """
DOMCAPABILITIES_ARMV7 = """
<domainCapabilities>
<path>/usr/bin/qemu-system-arm</path>
<domain>qemu</domain>
@ -836,6 +1025,164 @@ DOMCAPABILITIES_ARMV7L = """
</domainCapabilities>
"""
DOMCAPABILITIES_AARCH64 = """
<domainCapabilities>
<path>/usr/bin/qemu-system-aarch64</path>
<domain>qemu</domain>
<machine>virt-5.1</machine>
<arch>aarch64</arch>
<vcpu max='512'/>
<iothreads supported='yes'/>
<os supported='yes'>
<enum name='firmware'>
<value>efi</value>
</enum>
<loader supported='yes'>
<value>/usr/share/AAVMF/AAVMF_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>
<value>yes</value>
</enum>
</loader>
</os>
<cpu>
<mode name='host-passthrough' supported='no'/>
<mode name='host-model' supported='no'/>
<mode name='custom' supported='yes'>
<model usable='unknown'>pxa270-c0</model>
<model usable='unknown'>cortex-a15</model>
<model usable='unknown'>pxa270-b0</model>
<model usable='unknown'>cortex-a57</model>
<model usable='unknown'>cortex-m4</model>
<model usable='unknown'>pxa270-a0</model>
<model usable='unknown'>arm1176</model>
<model usable='unknown'>pxa270-b1</model>
<model usable='unknown'>cortex-a7</model>
<model usable='unknown'>pxa270-a1</model>
<model usable='unknown'>cortex-a8</model>
<model usable='unknown'>cortex-r5</model>
<model usable='unknown'>ti925t</model>
<model usable='unknown'>cortex-r5f</model>
<model usable='unknown'>arm1026</model>
<model usable='unknown'>cortex-a9</model>
<model usable='unknown'>cortex-m7</model>
<model usable='unknown'>pxa270</model>
<model usable='unknown'>pxa260</model>
<model usable='unknown'>pxa250</model>
<model usable='unknown'>pxa270-c5</model>
<model usable='unknown'>pxa261</model>
<model usable='unknown'>pxa262</model>
<model usable='unknown'>sa1110</model>
<model usable='unknown'>sa1100</model>
<model usable='unknown'>max</model>
<model usable='unknown'>cortex-a53</model>
<model usable='unknown'>cortex-m0</model>
<model usable='unknown'>cortex-m33</model>
<model usable='unknown'>cortex-a72</model>
<model usable='unknown'>arm946</model>
<model usable='unknown'>pxa255</model>
<model usable='unknown'>arm11mpcore</model>
<model usable='unknown'>arm926</model>
<model usable='unknown'>arm1136</model>
<model usable='unknown'>arm1136-r2</model>
<model usable='unknown'>cortex-m3</model>
</mode>
</cpu>
<devices>
<disk supported='yes'>
<enum name='diskDevice'>
<value>disk</value>
<value>cdrom</value>
<value>floppy</value>
<value>lun</value>
</enum>
<enum name='bus'>
<value>fdc</value>
<value>scsi</value>
<value>virtio</value>
<value>usb</value>
<value>sata</value>
</enum>
<enum name='model'>
<value>virtio</value>
<value>virtio-transitional</value>
<value>virtio-non-transitional</value>
</enum>
</disk>
<graphics supported='yes'>
<enum name='type'>
<value>sdl</value>
<value>vnc</value>
<value>spice</value>
</enum>
</graphics>
<video supported='yes'>
<enum name='modelType'>
<value>vga</value>
<value>cirrus</value>
<value>vmvga</value>
<value>qxl</value>
<value>virtio</value>
<value>none</value>
<value>bochs</value>
<value>ramfb</value>
</enum>
</video>
<hostdev supported='yes'>
<enum name='mode'>
<value>subsystem</value>
</enum>
<enum name='startupPolicy'>
<value>default</value>
<value>mandatory</value>
<value>requisite</value>
<value>optional</value>
</enum>
<enum name='subsysType'>
<value>usb</value>
<value>pci</value>
<value>scsi</value>
</enum>
<enum name='capsType'/>
<enum name='pciBackend'/>
</hostdev>
<rng supported='yes'>
<enum name='model'>
<value>virtio</value>
<value>virtio-transitional</value>
<value>virtio-non-transitional</value>
</enum>
<enum name='backendModel'>
<value>random</value>
<value>egd</value>
<value>builtin</value>
</enum>
</rng>
</devices>
<features>
<gic supported='yes'>
<enum name='version'>
<value>2</value>
<value>3</value>
</enum>
</gic>
<vmcoreinfo supported='yes'/>
<genid supported='no'/>
<backingStoreInput supported='yes'/>
<backup supported='no'/>
<sev supported='no'/>
</features>
</domainCapabilities>
"""
DOMCAPABILITIES_PPC = """
<domainCapabilities>
<path>/usr/bin/qemu-system-ppc</path>
@ -1234,7 +1581,8 @@ DOMCAPABILITIES_I686 = """
"""
STATIC_DOMCAPABILITIES = {
Architecture.ARMV7: DOMCAPABILITIES_ARMV7L,
Architecture.ARMV7: DOMCAPABILITIES_ARMV7,
Architecture.AARCH64: DOMCAPABILITIES_AARCH64,
Architecture.SPARC: DOMCAPABILITIES_SPARC,
Architecture.PPC: DOMCAPABILITIES_PPC,
Architecture.MIPS: DOMCAPABILITIES_MIPS,

View File

@ -13,6 +13,7 @@
# under the License.
import collections
import os
import sys
import textwrap
import time
@ -1607,11 +1608,22 @@ class Connection(object):
cpu_map = [True for cpu_num in range(total_cpus)]
return (total_cpus, cpu_map, total_cpus)
def getDomainCapabilities(self, emulatorbin, arch, machine_type,
virt_type, flags):
def getDomainCapabilities(
self, emulatorbin, arch, machine_type, virt_type, flags,
):
"""Return spoofed domain capabilities."""
if arch in fake_libvirt_data.STATIC_DOMCAPABILITIES:
return fake_libvirt_data.STATIC_DOMCAPABILITIES[arch]
xml = fake_libvirt_data.STATIC_DOMCAPABILITIES[arch]
if machine_type:
# if we request a specific machine type, we should get
# information on that and not something else
tree = etree.fromstring(xml)
if machine_type not in tree.find('./machine').text:
raise Exception(
'Expected machine type of ~%s but got %s' % (
machine_type, tree.find('./machine').text,
))
return xml
if arch == 'x86_64':
aliases = {'pc': 'pc-i440fx-2.11', 'q35': 'pc-q35-2.11'}
@ -1706,11 +1718,21 @@ class Connection(object):
if isinstance(numa_topology, vconfig.LibvirtConfigCapsNUMATopology):
numa_topology = numa_topology.to_xml()
return (fake_libvirt_data.CAPABILITIES_TEMPLATE
% {'sockets': self.host_info.cpu_sockets,
'cores': self.host_info.cpu_cores,
'threads': self.host_info.cpu_threads,
'topology': numa_topology})
# we rely on os.uname() having been mocked already to ensure we have
# the correct "host" architecture
_capabilities = [
'<capabilities>\n',
fake_libvirt_data.CAPABILITIES_HOST_TEMPLATES[os.uname().machine],
] + list(fake_libvirt_data.CAPABILITIES_GUEST.values()) + [
'</capabilities>',
]
return ''.join(_capabilities) % {
'sockets': self.host_info.cpu_sockets,
'cores': self.host_info.cpu_cores,
'threads': self.host_info.cpu_threads,
'topology': numa_topology,
}
def compareCPU(self, xml, flags):
tree = etree.fromstring(xml)
@ -1941,6 +1963,14 @@ class FakeLibvirtFixture(fixtures.Fixture):
self.useFixture(
fixtures.MockPatch('os.uname', return_value=fake_uname))
# Ensure UEFI checks don't actually check the host
def fake_has_uefi_support():
return os.uname().machine == obj_fields.Architecture.AARCH64
self.useFixture(fixtures.MockPatch(
'nova.virt.libvirt.driver.LibvirtDriver._has_uefi_support',
side_effect=fake_has_uefi_support))
disable_event_thread(self)
if self.stub_os_vif:

View File

@ -5252,26 +5252,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self.assertIsInstance(cfg.devices[10],
vconfig.LibvirtConfigMemoryBalloon)
def test_has_uefi_support_not_supported_arch(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self._stub_host_capabilities_cpu_arch(fields.Architecture.ALPHA)
self.assertFalse(drvr._has_uefi_support())
@mock.patch('os.path.exists', return_value=False)
def test_has_uefi_support_with_no_loader_existed(self, mock_exist):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self.assertFalse(drvr._has_uefi_support())
@mock.patch('os.path.exists', return_value=True)
def test_has_uefi_support(self, mock_has_version):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self._stub_host_capabilities_cpu_arch(fields.Architecture.X86_64)
with mock.patch.object(drvr._host,
'has_min_version', return_value=True):
self.assertTrue(drvr._has_uefi_support())
def test_get_guest_config_with_uefi(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
@ -5290,11 +5270,11 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_support.assert_called_once_with()
self.assertEqual(cfg.os_loader_type, "pflash")
@mock.patch('os.path.exists', return_value=True)
def test_check_uefi_support_aarch64(self, mock_exist):
def test_check_uefi_support_aarch64(self):
self.mock_uname.return_value = fakelibvirt.os_uname(
'Linux', '', '5.4.0-0-generic', '', fields.Architecture.AARCH64)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self._stub_host_capabilities_cpu_arch(fields.Architecture.AARCH64)
self.assertTrue(drvr._has_uefi_support())
drvr._has_uefi_support = mock.Mock(return_value=True)
self.assertTrue(drvr._check_uefi_support(None))
def test_get_guest_config_with_block_device(self):
@ -7653,16 +7633,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
"_get_guest_storage_config")
@mock.patch.object(libvirt_driver.LibvirtDriver, "_has_numa_support")
def test_get_guest_config_armv7(self, mock_numa, mock_storage):
def get_host_capabilities_stub(self):
cpu = vconfig.LibvirtConfigGuestCPU()
cpu.arch = fields.Architecture.ARMV7
caps = vconfig.LibvirtConfigCaps()
caps.host = vconfig.LibvirtConfigCapsHost()
caps.host.cpu = cpu
return caps
self.flags(virt_type="kvm", group="libvirt")
self.mock_uname.return_value = fakelibvirt.os_uname(
'Linux', '', '5.4.0-0-generic', '', fields.Architecture.ARMV7)
@ -7674,9 +7644,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
instance_ref,
image_meta)
self.stub_out('nova.virt.libvirt.host.Host.get_capabilities',
get_host_capabilities_stub)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
cfg = drvr._get_guest_config(instance_ref,
_fake_network_info(self),
@ -7691,22 +7658,11 @@ class LibvirtConnTestCase(test.NoDBTestCase,
def test_get_guest_config_aarch64(
self, mock_path_exists, mock_numa, mock_storage,
):
def get_host_capabilities_stub(self):
cpu = vconfig.LibvirtConfigGuestCPU()
cpu.arch = fields.Architecture.AARCH64
caps = vconfig.LibvirtConfigCaps()
caps.host = vconfig.LibvirtConfigCapsHost()
caps.host.cpu = cpu
return caps
TEST_AMOUNT_OF_PCIE_SLOTS = 8
CONF.set_override("num_pcie_ports", TEST_AMOUNT_OF_PCIE_SLOTS,
group='libvirt')
self.flags(virt_type="kvm",
group="libvirt")
self.flags(virt_type="kvm", group="libvirt")
self.mock_uname.return_value = fakelibvirt.os_uname(
'Linux', '', '5.4.0-0-generic', '', fields.Architecture.AARCH64)
@ -7717,9 +7673,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
instance_ref,
image_meta)
self.stub_out('nova.virt.libvirt.host.Host.get_capabilities',
get_host_capabilities_stub)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
cfg = drvr._get_guest_config(instance_ref,
_fake_network_info(self),
@ -7749,20 +7702,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
def test_get_guest_config_aarch64_with_graphics(
self, mock_path_exists, mock_numa, mock_storage,
):
def get_host_capabilities_stub(self):
cpu = vconfig.LibvirtConfigGuestCPU()
cpu.arch = fields.Architecture.AARCH64
caps = vconfig.LibvirtConfigCaps()
caps.host = vconfig.LibvirtConfigCapsHost()
caps.host.cpu = cpu
return caps
self.mock_uname.return_value = fakelibvirt.os_uname(
'Linux', '', '5.4.0-0-generic', '', fields.Architecture.AARCH64)
self.stub_out('nova.virt.libvirt.host.Host.get_capabilities',
get_host_capabilities_stub)
self.flags(enabled=True, server_listen='10.0.0.1', group='vnc')
self.flags(virt_type='kvm', group='libvirt')
self.flags(enabled=False, group='spice')
@ -8061,27 +8002,19 @@ class LibvirtConnTestCase(test.NoDBTestCase,
fields.Architecture.AARCH64: "host-passthrough",
}
for guestarch, expect_mode in expected.items():
caps = vconfig.LibvirtConfigCaps()
caps.host = vconfig.LibvirtConfigCapsHost()
caps.host.cpu = vconfig.LibvirtConfigCPU()
caps.host.cpu.arch = guestarch
with mock.patch.object(host.Host, "get_capabilities",
return_value=caps):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
if caps.host.cpu.arch == fields.Architecture.AARCH64:
drvr._has_uefi_support = mock.Mock(return_value=True)
instance_ref = objects.Instance(**self.test_instance)
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
self.mock_uname.return_value = fakelibvirt.os_uname(
'Linux', '', '5.4.0-0-generic', '', guestarch)
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
instance_ref,
image_meta)
conf = drvr._get_guest_config(instance_ref,
_fake_network_info(self),
image_meta, disk_info)
self.assertIsInstance(conf.cpu,
vconfig.LibvirtConfigGuestCPU)
self.assertEqual(conf.cpu.mode, expect_mode)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
instance_ref = objects.Instance(**self.test_instance)
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
disk_info = blockinfo.get_disk_info(
CONF.libvirt.virt_type, instance_ref, image_meta)
conf = drvr._get_guest_config(
instance_ref, _fake_network_info(self), image_meta, disk_info)
self.assertIsInstance(conf.cpu, vconfig.LibvirtConfigGuestCPU)
self.assertEqual(conf.cpu.mode, expect_mode)
def test_get_guest_cpu_config_host_model(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
@ -8126,34 +8059,20 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self.assertEqual(conf.cpu.threads, 1)
def test_get_guest_cpu_config_qemu_custom_aarch64(self):
self.flags(cpu_mode="custom", group='libvirt',
cpu_models=["max"])
expected = {
fields.Architecture.AARCH64: "custom",
}
self.flags(cpu_mode='custom', group='libvirt', cpu_models=['max'])
self.mock_uname.return_value = fakelibvirt.os_uname(
'Linux', '', '5.4.0-0-generic', '', fields.Architecture.AARCH64)
for guestarch, expect_mode in expected.items():
caps = vconfig.LibvirtConfigCaps()
caps.host = vconfig.LibvirtConfigCapsHost()
caps.host.cpu = vconfig.LibvirtConfigCPU()
caps.host.cpu.arch = guestarch
with mock.patch.object(host.Host, "get_capabilities",
return_value=caps):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
if caps.host.cpu.arch == fields.Architecture.AARCH64:
drvr._has_uefi_support = mock.Mock(return_value=True)
instance_ref = objects.Instance(**self.test_instance)
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
instance_ref = objects.Instance(**self.test_instance)
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
instance_ref,
image_meta)
conf = drvr._get_guest_config(instance_ref,
_fake_network_info(self),
image_meta, disk_info)
self.assertIsInstance(conf.cpu,
vconfig.LibvirtConfigGuestCPU)
self.assertEqual(conf.cpu.mode, expect_mode)
disk_info = blockinfo.get_disk_info(
CONF.libvirt.virt_type, instance_ref, image_meta)
conf = drvr._get_guest_config(
instance_ref, _fake_network_info(self), image_meta, disk_info)
self.assertIsInstance(conf.cpu, vconfig.LibvirtConfigGuestCPU)
self.assertEqual(conf.cpu.mode, 'custom')
@mock.patch.object(libvirt_driver.LOG, 'warning')
def test_get_guest_cpu_config_custom_with_extra_flags(self,
@ -16885,10 +16804,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_get_domain.assert_called_once_with(instance)
mock_get_error.assert_not_called()
@mock.patch.object(libvirt_driver.LibvirtDriver, "_has_uefi_support")
@mock.patch.object(host.Host, "get_guest")
def test_undefine_domain_handles_libvirt_errors(self, mock_get,
mock_has_uefi):
def test_undefine_domain_handles_libvirt_errors(self, mock_get):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
instance = objects.Instance(**self.test_instance)
fake_guest = mock.Mock()

View File

@ -13,6 +13,7 @@
# under the License.
from lxml import etree
import mock
from oslo_utils import uuidutils
from nova.objects import fields as obj_fields
@ -274,7 +275,10 @@ class FakeLibvirtTests(test.NoDBTestCase):
def test_getCapabilities(self):
conn = self.get_openAuth_curry_func()('qemu:///system')
etree.fromstring(conn.getCapabilities())
with mock.patch('os.uname') as mock_uname:
mock_uname.return_value = libvirt.os_uname(
'Linux', '', '5.10.13-200-generic', '', 'x86_64')
etree.fromstring(conn.getCapabilities())
def test_getDomainCapabilities(self):
conn = self.get_openAuth_curry_func()('qemu:///system')

View File

@ -4703,16 +4703,23 @@ class LibvirtDriver(driver.ComputeDriver):
return self.cpu_models_mapping.get(model.lower())
def _get_guest_cpu_model_config(self, flavor=None):
# TODO(stephenfin): Libvirt exposes information about possible CPU models
# via 'getDomainCapabilities' and we should use it
def _get_guest_cpu_model_config(self, flavor=None, arch=None):
mode = CONF.libvirt.cpu_mode
models = [self._get_cpu_model_mapping(model)
for model in CONF.libvirt.cpu_models]
extra_flags = set([flag.lower() for flag in
CONF.libvirt.cpu_model_extra_flags])
if (CONF.libvirt.virt_type == "kvm" or
CONF.libvirt.virt_type == "qemu"):
if not arch:
caps = self._host.get_capabilities()
arch = caps.host.cpu.arch
if (
CONF.libvirt.virt_type == "kvm" or
CONF.libvirt.virt_type == "qemu"
):
if mode is None:
# AArch64 lacks 'host-model' support because neither libvirt
# nor QEMU are able to tell what the host CPU model exactly is.
@ -4722,7 +4729,7 @@ class LibvirtDriver(driver.ComputeDriver):
# Also worth noting: 'host-passthrough' mode will completely
# break live migration, *unless* all the Compute nodes (running
# libvirtd) have *identical* CPUs.
if caps.host.cpu.arch == fields.Architecture.AARCH64:
if arch == fields.Architecture.AARCH64:
mode = "host-passthrough"
LOG.info('CPU mode "host-passthrough" was chosen. Live '
'migration can break unless all compute nodes '
@ -4735,7 +4742,7 @@ class LibvirtDriver(driver.ComputeDriver):
# On AArch64 platform the return of _get_cpu_model_mapping will not
# return the default CPU model.
if mode == "custom":
if caps.host.cpu.arch == fields.Architecture.AARCH64:
if arch == fields.Architecture.AARCH64:
if not models:
models = ['max']
@ -4801,7 +4808,8 @@ class LibvirtDriver(driver.ComputeDriver):
def _get_guest_cpu_config(self, flavor, image_meta,
guest_cpu_numa_config, instance_numa_topology):
cpu = self._get_guest_cpu_model_config(flavor)
arch = libvirt_utils.get_arch(image_meta)
cpu = self._get_guest_cpu_model_config(flavor, arch)
if cpu is None:
return None
@ -5878,16 +5886,14 @@ class LibvirtDriver(driver.ComputeDriver):
flavor: 'objects.Flavor',
) -> None:
if CONF.libvirt.virt_type in ("kvm", "qemu"):
caps = self._host.get_capabilities()
if caps.host.cpu.arch in (
fields.Architecture.I686, fields.Architecture.X86_64,
):
arch = libvirt_utils.get_arch(image_meta)
if arch in (fields.Architecture.I686, fields.Architecture.X86_64):
guest.sysinfo = self._get_guest_config_sysinfo(instance)
guest.os_smbios = vconfig.LibvirtConfigGuestSMBIOS()
hw_firmware_type = image_meta.properties.get('hw_firmware_type')
if caps.host.cpu.arch == fields.Architecture.AARCH64:
if arch == fields.Architecture.AARCH64:
if not hw_firmware_type:
hw_firmware_type = fields.FirmwareType.UEFI
@ -5899,7 +5905,7 @@ class LibvirtDriver(driver.ComputeDriver):
"functional testing and therefore "
"considered experimental.")
uefi_logged = True
for lpath in DEFAULT_UEFI_LOADER_PATH[caps.host.cpu.arch]:
for lpath in DEFAULT_UEFI_LOADER_PATH[arch]:
if os.path.exists(lpath):
guest.os_loader = lpath
guest.os_loader_type = "pflash"