Add compatibility checks for CPU mode and CPU models and extra flags

Add new method '_check_cpu_compability' in driver. It is used to check
if the configuration of cpu_mode/cpu_models/cpu_model_extra_flags is
correct and compatible with guest CPU.

The method will be called when the service(use libvirt driver) start, if
the configuration is not correct, an exception will be raised.

Change-Id: Ib37ff6fc2e1f9de0e60adca54e87c74f45f12ffa
Implements: blueprint cpu-model-selection
This commit is contained in:
ya.wang 2019-07-11 16:28:11 +08:00
parent f80e5f989d
commit ac7a0e8409
2 changed files with 137 additions and 30 deletions

View File

@ -1288,6 +1288,70 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self.assertRaises(exception.InternalError,
drvr._check_file_backed_memory_support)
def test__check_cpu_compatibility_start_ok(self):
self.flags(cpu_mode="custom",
cpu_models=["Penryn"],
group="libvirt")
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr.init_host("dummyhost")
def test__check_cpu_compatibility_none_models(self):
self.flags(cpu_mode="custom",
cpu_models=[],
group="libvirt")
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self.assertRaises(exception.Invalid, drvr.init_host, "dummyhost")
def test__check_cpu_compatibility_none_mode(self):
self.flags(cpu_mode="none",
cpu_models=["Penryn"],
group="libvirt")
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self.assertRaises(exception.Invalid, drvr.init_host, "dummyhost")
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.compareCPU')
def test__check_cpu_compatibility_advance_model(self, mocked_compare):
mocked_compare.side_effect = (2, 0)
self.flags(cpu_mode="custom",
cpu_models=["qemu64", "Broadwell-noTSX"],
group="libvirt")
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self.assertRaises(exception.InvalidCPUInfo,
drvr.init_host, "dummyhost")
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.compareCPU')
def test__check_cpu_compatibility_advance_flag(self, mocked_compare):
mocked_compare.side_effect = (2, 0)
self.flags(cpu_mode="custom",
cpu_models=["qemu64"],
cpu_model_extra_flags = ["avx", "avx2"],
group="libvirt")
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self.assertRaises(exception.InvalidCPUInfo,
drvr.init_host, "dummyhost")
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.compareCPU')
def test__check_cpu_compatibility_wrong_flag(self, mocked_compare):
mocked_compare.side_effect = (2, 0)
self.flags(cpu_mode="custom",
cpu_models=["Broadwell-noTSX"],
cpu_model_extra_flags = ["a v x"],
group="libvirt")
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self.assertRaises(exception.InvalidCPUInfo,
drvr.init_host, "dummyhost")
def test__check_cpu_compatibility_invalid_virt_type(self):
"""Test getting CPU traits when using a virt_type that doesn't support
the feature, only kvm and qemu supports reporting CPU traits.
"""
self.flags(cpu_mode='custom',
cpu_models=['IvyBridge'],
virt_type='lxc',
group='libvirt')
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
self.assertRaises(exception.Invalid, drvr.init_host, "dummyhost")
def _do_test_parse_migration_flags(self, lm_expected=None,
bm_expected=None):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
@ -22232,17 +22296,6 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
self.assertTraitsEqual([], self.drvr._get_cpu_feature_traits())
def test_cpu_traits_with_invalid_virt_type(self):
"""Test getting CPU traits when using a virt_type that doesn't support
the feature, only kvm and qemu supports reporting CPU traits.
"""
self.flags(cpu_mode='custom',
cpu_models=['IvyBridge'],
virt_type='lxc',
group='libvirt'
)
self.assertRaises(exception.Invalid, self.drvr._get_cpu_feature_traits)
@mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities')
@mock.patch('nova.virt.libvirt.utils.cpu_features_to_traits')
def test_cpu_traits_with_mode_passthrough_and_extra_flags(

View File

@ -648,6 +648,64 @@ class LibvirtDriver(driver.ComputeDriver):
if self._host.has_min_version(MIN_LIBVIRT_MDEV_SUPPORT):
self._recreate_assigned_mediated_devices()
self._check_cpu_compatibility()
def _check_cpu_compatibility(self):
mode = CONF.libvirt.cpu_mode
models = CONF.libvirt.cpu_models
if (CONF.libvirt.virt_type not in ("kvm", "qemu") and
mode not in (None, 'none')):
msg = _("Config requested an explicit CPU model, but "
"the current libvirt hypervisor '%s' does not "
"support selecting CPU models") % CONF.libvirt.virt_type
raise exception.Invalid(msg)
if mode != "custom":
if not models:
return
msg = _("The cpu_models option is not required when "
"cpu_mode!=custom")
raise exception.Invalid(msg)
if not models:
msg = _("The cpu_models option is required when cpu_mode=custom")
raise exception.Invalid(msg)
cpu = vconfig.LibvirtConfigGuestCPU()
for model in models:
cpu.model = self._get_cpu_model_mapping(model)
if not cpu.model:
msg = (_("Configured CPU model: %(model)s is not correct, "
"or your host CPU arch does not suuport this "
"model. Please correct your config and try "
"again.") % {'model': model})
raise exception.InvalidCPUInfo(msg)
try:
self._compare_cpu(cpu, self._get_cpu_info(), None)
except exception.InvalidCPUInfo as e:
msg = (_("Configured CPU model: %(model)s is not "
"compatible with host CPU. Please correct your "
"config and try again. %(e)s") % {
'model': model, 'e': e})
raise exception.InvalidCPUInfo(msg)
# Use guest CPU model to check the compatibility between guest CPU and
# configured extra_flags
cpu = vconfig.LibvirtConfigGuestCPU()
cpu.model = self._host.get_capabilities().host.cpu.arch
for flag in set(x.lower() for x in CONF.libvirt.cpu_model_extra_flags):
cpu.add_feature(vconfig.LibvirtConfigCPUFeature(flag))
try:
self._compare_cpu(cpu, self._get_cpu_info(), None)
except (exception.InvalidCPUInfo,
exception.MigrationPreCheckError) as e:
msg = (_("Configured extra flag: %(flag)s it not correct, or "
"the host CPU does not support this flag. Please "
"correct the config and try again. %(e)s") % {
'flag': flag, 'e': e})
raise exception.InvalidCPUInfo(msg)
@staticmethod
def _is_existing_mdev(uuid):
# FIXME(sbauza): Some kernel can have a uevent race meaning that the
@ -3964,11 +4022,21 @@ class LibvirtDriver(driver.ComputeDriver):
mount.get_manager().host_down()
def _get_cpu_model_mapping(self, model):
"""Get the CPU model mapping
The CPU models which admin configured are case-insensitive, libvirt is
case-sensitive, therefore build a mapping to get the correct CPU model
name.
:param model: Case-insensitive CPU model name.
:return: Case-sensitive CPU model name, or None(Only when configured
CPU model name not correct)
"""
if not self.cpu_models_mapping:
cpu_models = self._host.get_cpu_model_names()
for cpu_model in cpu_models:
self.cpu_models_mapping[cpu_model.lower()] = cpu_model
return self.cpu_models_mapping.get(model.lower())
return self.cpu_models_mapping.get(model.lower(), None)
def _get_guest_cpu_model_config(self, flavor=None):
mode = CONF.libvirt.cpu_mode
@ -4003,23 +4071,6 @@ class LibvirtDriver(driver.ComputeDriver):
if mode is None or mode == "none":
return None
if ((CONF.libvirt.virt_type != "kvm" and
CONF.libvirt.virt_type != "qemu")):
msg = _("Config requested an explicit CPU model, but "
"the current libvirt hypervisor '%s' does not "
"support selecting CPU models") % CONF.libvirt.virt_type
raise exception.Invalid(msg)
if mode == "custom" and not models:
msg = _("Config requested custom CPU models, but no "
"model names was provided")
raise exception.Invalid(msg)
if mode != "custom" and models:
msg = _("CPU model names should not be set when a "
"host CPU model is requested")
raise exception.Invalid(msg)
cpu = vconfig.LibvirtConfigGuestCPU()
cpu.mode = mode
cpu.model = models[0] if models else None
@ -7511,7 +7562,8 @@ class LibvirtDriver(driver.ComputeDriver):
def _compare_cpu(self, guest_cpu, host_cpu_str, instance):
"""Check the host is compatible with the requested CPU
:param guest_cpu: nova.objects.VirtCPUModel or None
:param guest_cpu: nova.objects.VirtCPUModel
or nova.virt.libvirt.vconfig.LibvirtConfigGuestCPU or None.
:param host_cpu_str: JSON from _get_cpu_info() method
If the 'guest_cpu' parameter is not None, this will be
@ -7545,6 +7597,8 @@ class LibvirtDriver(driver.ComputeDriver):
cpu.threads = info['topology']['threads']
for f in info['features']:
cpu.add_feature(vconfig.LibvirtConfigCPUFeature(f))
elif isinstance(guest_cpu, vconfig.LibvirtConfigGuestCPU):
cpu = guest_cpu
else:
cpu = self._vcpu_model_to_cpu_config(guest_cpu)