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

This commit is contained in:
Zuul 2019-09-11 23:15:22 +00:00 committed by Gerrit Code Review
commit 93f4cdb713
2 changed files with 137 additions and 30 deletions

View File

@ -1292,6 +1292,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)
@ -22436,17 +22500,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

@ -649,6 +649,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
@ -3965,11 +4023,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
@ -4004,23 +4072,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
@ -7643,7 +7694,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
@ -7677,6 +7729,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)