Merge "Add compatibility checks for CPU mode and CPU models and extra flags"
This commit is contained in:
commit
93f4cdb713
|
@ -1292,6 +1292,70 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||||
self.assertRaises(exception.InternalError,
|
self.assertRaises(exception.InternalError,
|
||||||
drvr._check_file_backed_memory_support)
|
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,
|
def _do_test_parse_migration_flags(self, lm_expected=None,
|
||||||
bm_expected=None):
|
bm_expected=None):
|
||||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||||
|
@ -22436,17 +22500,6 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
|
||||||
|
|
||||||
self.assertTraitsEqual([], self.drvr._get_cpu_feature_traits())
|
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.host.libvirt.Connection.getCapabilities')
|
||||||
@mock.patch('nova.virt.libvirt.utils.cpu_features_to_traits')
|
@mock.patch('nova.virt.libvirt.utils.cpu_features_to_traits')
|
||||||
def test_cpu_traits_with_mode_passthrough_and_extra_flags(
|
def test_cpu_traits_with_mode_passthrough_and_extra_flags(
|
||||||
|
|
|
@ -649,6 +649,64 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
if self._host.has_min_version(MIN_LIBVIRT_MDEV_SUPPORT):
|
if self._host.has_min_version(MIN_LIBVIRT_MDEV_SUPPORT):
|
||||||
self._recreate_assigned_mediated_devices()
|
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
|
@staticmethod
|
||||||
def _is_existing_mdev(uuid):
|
def _is_existing_mdev(uuid):
|
||||||
# FIXME(sbauza): Some kernel can have a uevent race meaning that the
|
# 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()
|
mount.get_manager().host_down()
|
||||||
|
|
||||||
def _get_cpu_model_mapping(self, model):
|
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:
|
if not self.cpu_models_mapping:
|
||||||
cpu_models = self._host.get_cpu_model_names()
|
cpu_models = self._host.get_cpu_model_names()
|
||||||
for cpu_model in cpu_models:
|
for cpu_model in cpu_models:
|
||||||
self.cpu_models_mapping[cpu_model.lower()] = cpu_model
|
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):
|
def _get_guest_cpu_model_config(self, flavor=None):
|
||||||
mode = CONF.libvirt.cpu_mode
|
mode = CONF.libvirt.cpu_mode
|
||||||
|
@ -4004,23 +4072,6 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
if mode is None or mode == "none":
|
if mode is None or mode == "none":
|
||||||
return 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 = vconfig.LibvirtConfigGuestCPU()
|
||||||
cpu.mode = mode
|
cpu.mode = mode
|
||||||
cpu.model = models[0] if models else None
|
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):
|
def _compare_cpu(self, guest_cpu, host_cpu_str, instance):
|
||||||
"""Check the host is compatible with the requested CPU
|
"""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
|
:param host_cpu_str: JSON from _get_cpu_info() method
|
||||||
|
|
||||||
If the 'guest_cpu' parameter is not None, this will be
|
If the 'guest_cpu' parameter is not None, this will be
|
||||||
|
@ -7677,6 +7729,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
cpu.threads = info['topology']['threads']
|
cpu.threads = info['topology']['threads']
|
||||||
for f in info['features']:
|
for f in info['features']:
|
||||||
cpu.add_feature(vconfig.LibvirtConfigCPUFeature(f))
|
cpu.add_feature(vconfig.LibvirtConfigCPUFeature(f))
|
||||||
|
elif isinstance(guest_cpu, vconfig.LibvirtConfigGuestCPU):
|
||||||
|
cpu = guest_cpu
|
||||||
else:
|
else:
|
||||||
cpu = self._vcpu_model_to_cpu_config(guest_cpu)
|
cpu = self._vcpu_model_to_cpu_config(guest_cpu)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue