diff --git a/nova/conf/libvirt.py b/nova/conf/libvirt.py index 632bba40fb4e..e32452c4cea6 100644 --- a/nova/conf/libvirt.py +++ b/nova/conf/libvirt.py @@ -1479,6 +1479,9 @@ Related options: ] libvirt_cpu_mgmt_opts = [ + cfg.BoolOpt('cpu_power_management', + default=False, + help='Use libvirt to manage CPU cores performance.'), cfg.StrOpt('cpu_power_governor_low', default='powersave', help='Governor to use in order ' diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 139299e1facf..04c80d662b16 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -9254,6 +9254,34 @@ class LibvirtConnTestCase(test.NoDBTestCase, drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) self.assertRaises(exception.Invalid, drvr._get_pcpu_available) + @mock.patch('nova.virt.libvirt.host.Host.get_available_cpus', + return_value=set([0, 1, 2, 3])) + def test_get_pcpu_available_for_power_mgmt(self, get_available_cpus): + """Test what happens when the '[compute] cpu_dedicated_set' config + option is set and power management is defined. + """ + self.flags(vcpu_pin_set=None) + self.flags(cpu_dedicated_set='2-3', cpu_shared_set=None, + group='compute') + self.flags(cpu_power_management=True, group='libvirt') + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + pcpus = drvr._get_pcpu_available() + self.assertEqual(set([2, 3]), pcpus) + + @mock.patch('nova.virt.libvirt.host.Host.get_available_cpus', + return_value=set([4, 5])) + def test_get_pcpu_available__cpu_dedicated_set_invalid_for_pm(self, + get_available_cpus): + """Test what happens when the '[compute] cpu_dedicated_set' config + option is set but it's invalid with power management set. + """ + self.flags(vcpu_pin_set=None) + self.flags(cpu_dedicated_set='4-6', cpu_shared_set=None, + group='compute') + self.flags(cpu_power_management=True, group='libvirt') + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + self.assertRaises(exception.Invalid, drvr._get_pcpu_available) + @mock.patch('nova.virt.libvirt.host.Host.get_online_cpus', return_value=set([0, 1, 2, 3])) def test_get_vcpu_available(self, get_online_cpus): diff --git a/nova/tests/unit/virt/libvirt/test_host.py b/nova/tests/unit/virt/libvirt/test_host.py index 3afd6c139dfd..631b10d81a50 100644 --- a/nova/tests/unit/virt/libvirt/test_host.py +++ b/nova/tests/unit/virt/libvirt/test_host.py @@ -1052,6 +1052,12 @@ Active: 8381604 kB 'iowait': 6121490000000}, stats) + @mock.patch.object(fakelibvirt.virConnect, "getCPUMap") + def test_get_available_cpus(self, mock_map): + mock_map.return_value = (4, [True, True, False, False], None) + result = self.host.get_available_cpus() + self.assertEqual(result, {0, 1, 2, 3}) + @mock.patch.object(fakelibvirt.virConnect, "defineXML") def test_write_instance_config(self, mock_defineXML): fake_dom_xml = """ diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index b9a389910dc0..905ec0f306a2 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -7750,15 +7750,18 @@ class LibvirtDriver(driver.ComputeDriver): if not CONF.compute.cpu_dedicated_set: return set() - online_cpus = self._host.get_online_cpus() + if CONF.libvirt.cpu_power_management: + available_cpus = self._host.get_available_cpus() + else: + available_cpus = self._host.get_online_cpus() dedicated_cpus = hardware.get_cpu_dedicated_set() - if not dedicated_cpus.issubset(online_cpus): + if not dedicated_cpus.issubset(available_cpus): msg = _("Invalid '[compute] cpu_dedicated_set' config: one or " - "more of the configured CPUs is not online. Online " - "cpuset(s): %(online)s, configured cpuset(s): %(req)s") + "more of the configured CPUs is not available. Available " + "cpuset(s): %(available)s, configured cpuset(s): %(req)s") raise exception.Invalid(msg % { - 'online': sorted(online_cpus), + 'available': sorted(available_cpus), 'req': sorted(dedicated_cpus)}) return dedicated_cpus diff --git a/nova/virt/libvirt/host.py b/nova/virt/libvirt/host.py index b98670240113..9658a5791df1 100644 --- a/nova/virt/libvirt/host.py +++ b/nova/virt/libvirt/host.py @@ -740,6 +740,14 @@ class Host(object): return doms + def get_available_cpus(self): + """Get the set of CPUs that exist on the host. + + :returns: set of CPUs, raises libvirtError on error + """ + cpus, cpu_map, online = self.get_connection().getCPUMap() + return {cpu for cpu in range(cpus)} + def get_online_cpus(self): """Get the set of CPUs that are online on the host