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:
parent
f80e5f989d
commit
ac7a0e8409
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue