Browse Source

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

changes/93/671793/24
Zuul 1 week ago
parent
commit
93f4cdb713
2 changed files with 137 additions and 30 deletions
  1. 64
    11
      nova/tests/unit/virt/libvirt/test_driver.py
  2. 73
    19
      nova/virt/libvirt/driver.py

+ 64
- 11
nova/tests/unit/virt/libvirt/test_driver.py View File

@@ -1292,6 +1292,70 @@ class LibvirtConnTestCase(test.NoDBTestCase,
1292 1292
         self.assertRaises(exception.InternalError,
1293 1293
                           drvr._check_file_backed_memory_support)
1294 1294
 
1295
+    def test__check_cpu_compatibility_start_ok(self):
1296
+        self.flags(cpu_mode="custom",
1297
+                   cpu_models=["Penryn"],
1298
+                   group="libvirt")
1299
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
1300
+        drvr.init_host("dummyhost")
1301
+
1302
+    def test__check_cpu_compatibility_none_models(self):
1303
+        self.flags(cpu_mode="custom",
1304
+                   cpu_models=[],
1305
+                   group="libvirt")
1306
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
1307
+        self.assertRaises(exception.Invalid, drvr.init_host, "dummyhost")
1308
+
1309
+    def test__check_cpu_compatibility_none_mode(self):
1310
+        self.flags(cpu_mode="none",
1311
+                   cpu_models=["Penryn"],
1312
+                   group="libvirt")
1313
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
1314
+        self.assertRaises(exception.Invalid, drvr.init_host, "dummyhost")
1315
+
1316
+    @mock.patch('nova.virt.libvirt.host.libvirt.Connection.compareCPU')
1317
+    def test__check_cpu_compatibility_advance_model(self, mocked_compare):
1318
+        mocked_compare.side_effect = (2, 0)
1319
+        self.flags(cpu_mode="custom",
1320
+                   cpu_models=["qemu64", "Broadwell-noTSX"],
1321
+                   group="libvirt")
1322
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
1323
+        self.assertRaises(exception.InvalidCPUInfo,
1324
+                          drvr.init_host, "dummyhost")
1325
+
1326
+    @mock.patch('nova.virt.libvirt.host.libvirt.Connection.compareCPU')
1327
+    def test__check_cpu_compatibility_advance_flag(self, mocked_compare):
1328
+        mocked_compare.side_effect = (2, 0)
1329
+        self.flags(cpu_mode="custom",
1330
+                   cpu_models=["qemu64"],
1331
+                   cpu_model_extra_flags = ["avx", "avx2"],
1332
+                   group="libvirt")
1333
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
1334
+        self.assertRaises(exception.InvalidCPUInfo,
1335
+                          drvr.init_host, "dummyhost")
1336
+
1337
+    @mock.patch('nova.virt.libvirt.host.libvirt.Connection.compareCPU')
1338
+    def test__check_cpu_compatibility_wrong_flag(self, mocked_compare):
1339
+        mocked_compare.side_effect = (2, 0)
1340
+        self.flags(cpu_mode="custom",
1341
+                   cpu_models=["Broadwell-noTSX"],
1342
+                   cpu_model_extra_flags = ["a v x"],
1343
+                   group="libvirt")
1344
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
1345
+        self.assertRaises(exception.InvalidCPUInfo,
1346
+                          drvr.init_host, "dummyhost")
1347
+
1348
+    def test__check_cpu_compatibility_invalid_virt_type(self):
1349
+        """Test getting CPU traits when using a virt_type that doesn't support
1350
+        the feature, only kvm and qemu supports reporting CPU traits.
1351
+        """
1352
+        self.flags(cpu_mode='custom',
1353
+                   cpu_models=['IvyBridge'],
1354
+                   virt_type='lxc',
1355
+                   group='libvirt')
1356
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
1357
+        self.assertRaises(exception.Invalid, drvr.init_host, "dummyhost")
1358
+
1295 1359
     def _do_test_parse_migration_flags(self, lm_expected=None,
1296 1360
                                        bm_expected=None):
1297 1361
         drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
@@ -22436,17 +22500,6 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
22436 22500
 
22437 22501
         self.assertTraitsEqual([], self.drvr._get_cpu_feature_traits())
22438 22502
 
22439
-    def test_cpu_traits_with_invalid_virt_type(self):
22440
-        """Test getting CPU traits when using a virt_type that doesn't support
22441
-        the feature, only kvm and qemu supports reporting CPU traits.
22442
-        """
22443
-        self.flags(cpu_mode='custom',
22444
-                   cpu_models=['IvyBridge'],
22445
-                   virt_type='lxc',
22446
-                   group='libvirt'
22447
-                   )
22448
-        self.assertRaises(exception.Invalid, self.drvr._get_cpu_feature_traits)
22449
-
22450 22503
     @mock.patch('nova.virt.libvirt.host.libvirt.Connection.getCapabilities')
22451 22504
     @mock.patch('nova.virt.libvirt.utils.cpu_features_to_traits')
22452 22505
     def test_cpu_traits_with_mode_passthrough_and_extra_flags(

+ 73
- 19
nova/virt/libvirt/driver.py View File

@@ -649,6 +649,64 @@ class LibvirtDriver(driver.ComputeDriver):
649 649
         if self._host.has_min_version(MIN_LIBVIRT_MDEV_SUPPORT):
650 650
             self._recreate_assigned_mediated_devices()
651 651
 
652
+        self._check_cpu_compatibility()
653
+
654
+    def _check_cpu_compatibility(self):
655
+        mode = CONF.libvirt.cpu_mode
656
+        models = CONF.libvirt.cpu_models
657
+
658
+        if (CONF.libvirt.virt_type not in ("kvm", "qemu") and
659
+                mode not in (None, 'none')):
660
+            msg = _("Config requested an explicit CPU model, but "
661
+                    "the current libvirt hypervisor '%s' does not "
662
+                    "support selecting CPU models") % CONF.libvirt.virt_type
663
+            raise exception.Invalid(msg)
664
+
665
+        if mode != "custom":
666
+            if not models:
667
+                return
668
+            msg = _("The cpu_models option is not required when "
669
+                    "cpu_mode!=custom")
670
+            raise exception.Invalid(msg)
671
+
672
+        if not models:
673
+            msg = _("The cpu_models option is required when cpu_mode=custom")
674
+            raise exception.Invalid(msg)
675
+
676
+        cpu = vconfig.LibvirtConfigGuestCPU()
677
+        for model in models:
678
+            cpu.model = self._get_cpu_model_mapping(model)
679
+            if not cpu.model:
680
+                msg = (_("Configured CPU model: %(model)s is not correct, "
681
+                         "or your host CPU arch does not suuport this "
682
+                         "model. Please correct your config and try "
683
+                         "again.") % {'model': model})
684
+                raise exception.InvalidCPUInfo(msg)
685
+            try:
686
+                self._compare_cpu(cpu, self._get_cpu_info(), None)
687
+            except exception.InvalidCPUInfo as e:
688
+                msg = (_("Configured CPU model: %(model)s is not "
689
+                         "compatible with host CPU. Please correct your "
690
+                         "config and try again. %(e)s") % {
691
+                            'model': model, 'e': e})
692
+                raise exception.InvalidCPUInfo(msg)
693
+
694
+        # Use guest CPU model to check the compatibility between guest CPU and
695
+        # configured extra_flags
696
+        cpu = vconfig.LibvirtConfigGuestCPU()
697
+        cpu.model = self._host.get_capabilities().host.cpu.arch
698
+        for flag in set(x.lower() for x in CONF.libvirt.cpu_model_extra_flags):
699
+            cpu.add_feature(vconfig.LibvirtConfigCPUFeature(flag))
700
+            try:
701
+                self._compare_cpu(cpu, self._get_cpu_info(), None)
702
+            except (exception.InvalidCPUInfo,
703
+                    exception.MigrationPreCheckError) as e:
704
+                msg = (_("Configured extra flag: %(flag)s it not correct, or "
705
+                         "the host CPU does not support this flag. Please "
706
+                         "correct the config and try again. %(e)s") % {
707
+                            'flag': flag, 'e': e})
708
+                raise exception.InvalidCPUInfo(msg)
709
+
652 710
     @staticmethod
653 711
     def _is_existing_mdev(uuid):
654 712
         # FIXME(sbauza): Some kernel can have a uevent race meaning that the
@@ -3965,11 +4023,21 @@ class LibvirtDriver(driver.ComputeDriver):
3965 4023
             mount.get_manager().host_down()
3966 4024
 
3967 4025
     def _get_cpu_model_mapping(self, model):
4026
+        """Get the CPU model mapping
4027
+
4028
+        The CPU models which admin configured are case-insensitive, libvirt is
4029
+        case-sensitive, therefore build a mapping to get the correct CPU model
4030
+        name.
4031
+
4032
+        :param model: Case-insensitive CPU model name.
4033
+        :return: Case-sensitive CPU model name, or None(Only when configured
4034
+                 CPU model name not correct)
4035
+        """
3968 4036
         if not self.cpu_models_mapping:
3969 4037
             cpu_models = self._host.get_cpu_model_names()
3970 4038
             for cpu_model in cpu_models:
3971 4039
                 self.cpu_models_mapping[cpu_model.lower()] = cpu_model
3972
-        return self.cpu_models_mapping.get(model.lower())
4040
+        return self.cpu_models_mapping.get(model.lower(), None)
3973 4041
 
3974 4042
     def _get_guest_cpu_model_config(self, flavor=None):
3975 4043
         mode = CONF.libvirt.cpu_mode
@@ -4004,23 +4072,6 @@ class LibvirtDriver(driver.ComputeDriver):
4004 4072
             if mode is None or mode == "none":
4005 4073
                 return None
4006 4074
 
4007
-        if ((CONF.libvirt.virt_type != "kvm" and
4008
-             CONF.libvirt.virt_type != "qemu")):
4009
-            msg = _("Config requested an explicit CPU model, but "
4010
-                    "the current libvirt hypervisor '%s' does not "
4011
-                    "support selecting CPU models") % CONF.libvirt.virt_type
4012
-            raise exception.Invalid(msg)
4013
-
4014
-        if mode == "custom" and not models:
4015
-            msg = _("Config requested custom CPU models, but no "
4016
-                    "model names was provided")
4017
-            raise exception.Invalid(msg)
4018
-
4019
-        if mode != "custom" and models:
4020
-            msg = _("CPU model names should not be set when a "
4021
-                    "host CPU model is requested")
4022
-            raise exception.Invalid(msg)
4023
-
4024 4075
         cpu = vconfig.LibvirtConfigGuestCPU()
4025 4076
         cpu.mode = mode
4026 4077
         cpu.model = models[0] if models else None
@@ -7643,7 +7694,8 @@ class LibvirtDriver(driver.ComputeDriver):
7643 7694
     def _compare_cpu(self, guest_cpu, host_cpu_str, instance):
7644 7695
         """Check the host is compatible with the requested CPU
7645 7696
 
7646
-        :param guest_cpu: nova.objects.VirtCPUModel or None
7697
+        :param guest_cpu: nova.objects.VirtCPUModel
7698
+            or nova.virt.libvirt.vconfig.LibvirtConfigGuestCPU or None.
7647 7699
         :param host_cpu_str: JSON from _get_cpu_info() method
7648 7700
 
7649 7701
         If the 'guest_cpu' parameter is not None, this will be
@@ -7677,6 +7729,8 @@ class LibvirtDriver(driver.ComputeDriver):
7677 7729
             cpu.threads = info['topology']['threads']
7678 7730
             for f in info['features']:
7679 7731
                 cpu.add_feature(vconfig.LibvirtConfigCPUFeature(f))
7732
+        elif isinstance(guest_cpu, vconfig.LibvirtConfigGuestCPU):
7733
+            cpu = guest_cpu
7680 7734
         else:
7681 7735
             cpu = self._vcpu_model_to_cpu_config(guest_cpu)
7682 7736
 

Loading…
Cancel
Save