Skip to content

Commit 1e1e404

Browse files
committed
libvirt: At start-up rework compareCPU() usage with a workaround
In this patch: - Remove the first compareCPU() call (called via the wrapper _compare_cpu()) in _check_cpu_compatibility(), and let libvirt handle it. (QEMU >=2.9 and libvirt >= 4.4.0 are the mininum required versions, and upstream Nova satisfies them by a good margin.) - Validate the user-configured CPU models from _get_cpu_model_mapping(). And take into account all the CPU flags before calling _compare_cpu(). (Suggested-by: Sean Mooney -- thanks!) - Add a workaround to allow skipping the remaining compareCPU() call in _check_cpu_compatibility() as a potential future-proof (because we cannot test all possible CPU models and hardware). Unlike the removed first call, this call takes into account the extra CPU flags provided by the user into account when evaluating guest CPU model compatibility. As a follow up comes the patch[1] that replaces the older libvirt CPU API with the newer one. [1] https://review.opendev.org/c/openstack/nova/+/869950 -- libvirt: Replace usage of compareCPU() with compareHypervisorCPU() Change-Id: I8ef9db851b37c5249d2efbe09a15a1ddbae8205d Signed-off-by: Kashyap Chamarthy <[email protected]> (cherry picked from commit 9caaaf1)
1 parent 9bca7f3 commit 1e1e404

File tree

3 files changed

+46
-30
lines changed

3 files changed

+46
-30
lines changed

nova/conf/workarounds.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,13 @@
410410
4.4.0, libvirt will do the correct thing with respect to checking CPU
411411
compatibility on the destination host during live migration.
412412
"""),
413+
cfg.BoolOpt('skip_cpu_compare_at_startup',
414+
default=False,
415+
help="""
416+
This will skip the CPU comparison call at the startup of Compute
417+
service and lets libvirt handle it.
418+
"""),
419+
413420
cfg.BoolOpt(
414421
'skip_hypervisor_version_check_on_lm',
415422
default=False,

nova/tests/unit/virt/libvirt/test_driver.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,22 @@ def test__check_cpu_compatibility_advance_model(self, mocked_compare):
13301330
self.assertRaises(exception.InvalidCPUInfo,
13311331
drvr.init_host, "dummyhost")
13321332

1333+
@mock.patch.object(libvirt_driver.LibvirtDriver,
1334+
'_register_all_undefined_instance_details',
1335+
new=mock.Mock())
1336+
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.compareCPU')
1337+
def test__check_cpu_compatibility_skip_compare_at_init(
1338+
self, mocked_compare
1339+
):
1340+
self.flags(group='workarounds', skip_cpu_compare_at_startup=True)
1341+
self.flags(cpu_mode="custom",
1342+
cpu_models=["Icelake-Server-noTSX"],
1343+
cpu_model_extra_flags = ["-mpx"],
1344+
group="libvirt")
1345+
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
1346+
drvr.init_host("dummyhost")
1347+
mocked_compare.assert_not_called()
1348+
13331349
@mock.patch.object(libvirt_driver.LibvirtDriver,
13341350
'_register_all_undefined_instance_details',
13351351
new=mock.Mock())
@@ -1343,7 +1359,7 @@ def test__check_cpu_compatibility_with_flag(self):
13431359

13441360
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.compareCPU')
13451361
def test__check_cpu_compatibility_advance_flag(self, mocked_compare):
1346-
mocked_compare.side_effect = (2, 0)
1362+
mocked_compare.side_effect = (-1, 0)
13471363
self.flags(cpu_mode="custom",
13481364
cpu_models=["qemu64"],
13491365
cpu_model_extra_flags = ["avx", "avx2"],
@@ -1356,7 +1372,7 @@ def test__check_cpu_compatibility_advance_flag(self, mocked_compare):
13561372
def test__check_cpu_compatibility_wrong_flag(self, mocked_compare):
13571373
# here, and in the surrounding similar tests, the non-zero error
13581374
# code in the compareCPU() side effect indicates error
1359-
mocked_compare.side_effect = (2, 0)
1375+
mocked_compare.side_effect = (-1, 0)
13601376
self.flags(cpu_mode="custom",
13611377
cpu_models=["Broadwell-noTSX"],
13621378
cpu_model_extra_flags = ["a v x"],
@@ -1369,7 +1385,7 @@ def test__check_cpu_compatibility_wrong_flag(self, mocked_compare):
13691385
def test__check_cpu_compatibility_enabled_and_disabled_flags(
13701386
self, mocked_compare
13711387
):
1372-
mocked_compare.side_effect = (2, 0)
1388+
mocked_compare.side_effect = (-1, 0)
13731389
self.flags(
13741390
cpu_mode="custom",
13751391
cpu_models=["Cascadelake-Server"],

nova/virt/libvirt/driver.py

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -984,33 +984,26 @@ def _check_cpu_compatibility(self):
984984
msg = _("The cpu_models option is required when cpu_mode=custom")
985985
raise exception.Invalid(msg)
986986

987-
cpu = vconfig.LibvirtConfigGuestCPU()
988-
for model in models:
989-
cpu.model = self._get_cpu_model_mapping(model)
990-
try:
991-
self._compare_cpu(cpu, self._get_cpu_info(), None)
992-
except exception.InvalidCPUInfo as e:
993-
msg = (_("Configured CPU model: %(model)s is not "
994-
"compatible with host CPU. Please correct your "
995-
"config and try again. %(e)s") % {
996-
'model': model, 'e': e})
997-
raise exception.InvalidCPUInfo(msg)
998-
999-
# Use guest CPU model to check the compatibility between guest CPU and
1000-
# configured extra_flags
1001-
cpu = vconfig.LibvirtConfigGuestCPU()
1002-
cpu.model = self._host.get_capabilities().host.cpu.model
1003-
for flag in set(x.lower() for x in CONF.libvirt.cpu_model_extra_flags):
1004-
cpu_feature = self._prepare_cpu_flag(flag)
1005-
cpu.add_feature(cpu_feature)
1006-
try:
1007-
self._compare_cpu(cpu, self._get_cpu_info(), None)
1008-
except exception.InvalidCPUInfo as e:
1009-
msg = (_("Configured extra flag: %(flag)s it not correct, or "
1010-
"the host CPU does not support this flag. Please "
1011-
"correct the config and try again. %(e)s") % {
1012-
'flag': flag, 'e': e})
1013-
raise exception.InvalidCPUInfo(msg)
987+
if not CONF.workarounds.skip_cpu_compare_at_startup:
988+
# Use guest CPU model to check the compatibility between
989+
# guest CPU and configured extra_flags
990+
for model in models:
991+
cpu = vconfig.LibvirtConfigGuestCPU()
992+
cpu.model = self._get_cpu_model_mapping(model)
993+
for flag in set(x.lower() for
994+
x in CONF.libvirt.cpu_model_extra_flags):
995+
cpu_feature = self._prepare_cpu_flag(flag)
996+
cpu.add_feature(cpu_feature)
997+
try:
998+
self._compare_cpu(cpu, self._get_cpu_info(), None)
999+
except exception.InvalidCPUInfo as e:
1000+
msg = (_("Configured CPU model: %(model)s "
1001+
"and CPU Flags %(flags)s ar not "
1002+
"compatible with host CPU. Please correct your "
1003+
"config and try again. %(e)s") % {
1004+
'model': model, 'e': e,
1005+
'flags': CONF.libvirt.cpu_model_extra_flags})
1006+
raise exception.InvalidCPUInfo(msg)
10141007

10151008
def _check_vtpm_support(self) -> None:
10161009
# TODO(efried): A key manager must be configured to create/retrieve

0 commit comments

Comments
 (0)