From 4ae1ad5e2bc60c2911fda9bdf38044251e91b6ab Mon Sep 17 00:00:00 2001 From: Nikola Dipanov Date: Wed, 16 Jul 2014 17:00:50 +0200 Subject: [PATCH] libvirt: Add method for getting host NUMA topology We can get this information from the libvirt capabilities XML now, so we add a method that returns it in the form of virt.hardware.VirtNUMATopology objects. We will exclude any CPUs not found in the CONF.vcpu_pin_set. Change-Id: Ibfc60bc71e49579bc5ab4996b752ed7a49830b6a Blueprint: virt-driver-numa-placement --- nova/tests/virt/libvirt/test_driver.py | 67 ++++++++++++++++++++++++++ nova/virt/libvirt/driver.py | 19 ++++++++ 2 files changed, 86 insertions(+) diff --git a/nova/tests/virt/libvirt/test_driver.py b/nova/tests/virt/libvirt/test_driver.py index 1d49afebe87d..f59faf026ceb 100644 --- a/nova/tests/virt/libvirt/test_driver.py +++ b/nova/tests/virt/libvirt/test_driver.py @@ -78,6 +78,7 @@ from nova.virt import driver from nova.virt import event as virtevent from nova.virt import fake from nova.virt import firewall as base_firewall +from nova.virt import hardware from nova.virt import images from nova.virt.libvirt import blockinfo from nova.virt.libvirt import config as vconfig @@ -6899,6 +6900,72 @@ class LibvirtConnTestCase(test.TestCase, if key not in ['phys_function', 'virt_functions', 'label']: self.assertEqual(actctualvfs[0][key], expectvfs[1][key]) + def test_get_host_numa_topology(self): + caps = vconfig.LibvirtConfigCaps() + caps.host = vconfig.LibvirtConfigCapsHost() + caps.host.topology = vconfig.LibvirtConfigCapsNUMATopology() + + cell_0 = vconfig.LibvirtConfigCapsNUMACell() + cell_0.id = 0 + cell_0.memory = 512 + cpu_0_0 = vconfig.LibvirtConfigCapsNUMACPU() + cpu_0_0.id = 0 + cpu_0_0.socket_id = 0 + cpu_0_0.core_id = 0 + cpu_0_0.sibling = 0 + cpu_0_1 = vconfig.LibvirtConfigCapsNUMACPU() + cpu_0_1.id = 1 + cpu_0_1.socket_id = 0 + cpu_0_1.core_id = 1 + cpu_0_1.sibling = 1 + cell_0.cpus = [cpu_0_0, cpu_0_1] + + cell_1 = vconfig.LibvirtConfigCapsNUMACell() + cell_1.id = 1 + cell_1.memory = 512 + cpu_1_0 = vconfig.LibvirtConfigCapsNUMACPU() + cpu_1_0.id = 2 + cpu_1_0.socket_id = 1 + cpu_1_0.core_id = 0 + cpu_1_0.sibling = 2 + cpu_1_1 = vconfig.LibvirtConfigCapsNUMACPU() + cpu_1_1.id = 3 + cpu_1_1.socket_id = 1 + cpu_1_1.core_id = 1 + cpu_1_1.sibling = 3 + cell_1.cpus = [cpu_1_0, cpu_1_1] + + caps.host.topology.cells = [cell_0, cell_1] + + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + expected_topo_dict = {'cells': [ + {'cpus': '0,1', 'cpu_usage': 0, + 'mem': {'total': 512, 'used': 0}, + 'id': 0}, + {'cpus': '3', 'cpu_usage': 0, + 'mem': {'total': 512, 'used': 0}, + 'id': 1}]} + with contextlib.nested( + mock.patch.object( + conn, '_get_host_capabilities', return_value=caps), + mock.patch.object( + hardware, 'get_vcpu_pin_set', return_value=set([0, 1, 3])) + ): + got_topo = conn._get_host_numa_topology() + got_topo_dict = got_topo._to_dict() + self.assertThat( + expected_topo_dict, matchers.DictMatches(got_topo_dict)) + + def test_get_host_numa_topology_empty(self): + caps = vconfig.LibvirtConfigCaps() + caps.host = vconfig.LibvirtConfigCapsHost() + caps.host.topology = None + + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + with mock.patch.object( + conn, '_get_host_capabilities', return_value=caps): + self.assertIsNone(conn._get_host_numa_topology()) + def test_diagnostic_vcpus_exception(self): xml = """ diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index e2454efe9c1e..1d226e3b16fb 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -4427,6 +4427,25 @@ class LibvirtDriver(driver.ComputeDriver): return jsonutils.dumps(pci_info) + def _get_host_numa_topology(self): + caps = self._get_host_capabilities() + topology = caps.host.topology + + if topology is None or not topology.cells: + return + + topology = hardware.VirtNUMAHostTopology( + cells=[hardware.VirtNUMATopologyCellUsage( + cell.id, set(cpu.id for cpu in cell.cpus), + cell.memory) + for cell in topology.cells]) + + allowed_cpus = hardware.get_vcpu_pin_set() + if allowed_cpus: + for cell in topology.cells: + cell.cpuset &= allowed_cpus + return topology + def get_all_volume_usage(self, context, compute_host_bdms): """Return usage info for volumes attached to vms on a given host.