From b4abb92b815988a4700313274ac7182f374a2286 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Tue, 19 Jun 2012 14:34:03 +0100 Subject: Switch libvirt get_cpu_info method over to use config APIs The get_cpu_info method in the libvirt driver currently uses XPath queries to extract information from the capabilities XML document. Switch this over to use the new config class LibvirtConfigCaps. Also provide a test case to validate the data being returned Fixes: bug #1003373 Implements: blueprint libvirt-xml-cpu-model Change-Id: I4946a16d27f712ae2adf8441ce78e6c0bb0bb657 Signed-off-by: Daniel P. Berrange --- nova/tests/test_libvirt.py | 45 +++++++++++++++++++++++++++ nova/virt/libvirt/driver.py | 76 +++++++++++++++++++++------------------------ 2 files changed, 80 insertions(+), 41 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 2bc20d2b0..3bb377e26 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1747,6 +1747,51 @@ class LibvirtConnTestCase(test.TestCase): space = fake_libvirt_utils.get_fs_info(FLAGS.instances_path)['free'] self.assertEqual(result, space / 1024 ** 3) + def test_cpu_info(self): + conn = libvirt_driver.LibvirtDriver(True) + + def get_host_capabilities_stub(self): + cpu = config.LibvirtConfigCPU() + cpu.model = "Opteron_G4" + cpu.vendor = "AMD" + cpu.arch = "x86_64" + + cpu.cores = 2 + cpu.threads = 1 + cpu.sockets = 4 + + cpu.add_feature(config.LibvirtConfigCPUFeature("extapic")) + cpu.add_feature(config.LibvirtConfigCPUFeature("3dnow")) + + caps = config.LibvirtConfigCaps() + caps.host = config.LibvirtConfigCapsHost() + caps.host.cpu = cpu + + guest = config.LibvirtConfigGuest() + guest.ostype = "hvm" + guest.arch = "x86_64" + caps.guests.append(guest) + + guest = config.LibvirtConfigGuest() + guest.ostype = "hvm" + guest.arch = "i686" + caps.guests.append(guest) + + return caps + + self.stubs.Set(libvirt_driver.LibvirtDriver, + 'get_host_capabilities', + get_host_capabilities_stub) + + want = {"vendor": "AMD", + "features": ["extapic", "3dnow"], + "permitted_instance_types": ["x86_64", "i686"], + "model": "Opteron_G4", + "arch": "x86_64", + "topology": {"cores": 2, "threads": 1, "sockets": 4}} + got = jsonutils.loads(conn.get_cpu_info()) + self.assertEqual(want, got) + class HostStateTestCase(test.TestCase): diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 08de82d62..533597923 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -1429,6 +1429,15 @@ class LibvirtDriver(driver.ComputeDriver): config_drive, config_drive_id = self._get_config_drive_info(instance) return any((config_drive, config_drive_id)) + def get_host_capabilities(self): + """Returns an instance of config.LibvirtConfigCaps representing + the capabilities of the host""" + xmlstr = self._conn.getCapabilities() + + caps = config.LibvirtConfigCaps() + caps.parse_str(xmlstr) + return caps + def get_guest_config(self, instance, network_info, image_meta, rescue=None, block_device_info=None): """Get config data for parameters. @@ -1974,53 +1983,38 @@ class LibvirtDriver(driver.ComputeDriver): """ - xml = self._conn.getCapabilities() - xml = etree.fromstring(xml) - nodes = xml.findall('.//host/cpu') - if len(nodes) != 1: - reason = _("'' must be 1, but %d\n") % len(nodes) - reason += xml.serialize() - raise exception.InvalidCPUInfo(reason=reason) - + caps = self.get_host_capabilities() cpu_info = dict() - arch_nodes = xml.findall('.//host/cpu/arch') - if arch_nodes: - cpu_info['arch'] = arch_nodes[0].text + cpu_info['arch'] = caps.host.cpu.arch + cpu_info['model'] = caps.host.cpu.model + cpu_info['vendor'] = caps.host.cpu.vendor - model_nodes = xml.findall('.//host/cpu/model') - if model_nodes: - cpu_info['model'] = model_nodes[0].text - - vendor_nodes = xml.findall('.//host/cpu/vendor') - if vendor_nodes: - cpu_info['vendor'] = vendor_nodes[0].text - - topology_nodes = xml.findall('.//host/cpu/topology') topology = dict() - if topology_nodes: - topology_node = topology_nodes[0] - - keys = ['cores', 'sockets', 'threads'] - tkeys = topology_node.keys() - if set(tkeys) != set(keys): - ks = ', '.join(keys) - reason = _("topology (%(topology)s) must have %(ks)s") - raise exception.InvalidCPUInfo(reason=reason % locals()) - for key in keys: - topology[key] = topology_node.get(key) - - feature_nodes = xml.findall('.//host/cpu/feature') - features = list() - for nodes in feature_nodes: - features.append(nodes.get('name')) - - arch_nodes = xml.findall('.//guest/arch') - guest_cpu_arches = list(node.get('name') for node in arch_nodes) - + topology['sockets'] = caps.host.cpu.sockets + topology['cores'] = caps.host.cpu.cores + topology['threads'] = caps.host.cpu.threads cpu_info['topology'] = topology + + features = list() + for f in caps.host.cpu.features: + features.append(f.name) cpu_info['features'] = features - cpu_info['permitted_instance_types'] = guest_cpu_arches + + guest_arches = list() + for g in caps.guests: + guest_arches.append(g.arch) + cpu_info['permitted_instance_types'] = guest_arches + + # TODO(berrange): why do we bother converting the + # libvirt capabilities XML into a special JSON format ? + # The data format is different across all the drivers + # so we could just return the raw capabilties XML + # which 'compare_cpu' could use directly + # + # That said, arch_filter.py now seems to rely on + # the libvirt drivers format which suggests this + # data format needs to be standardized across drivers return jsonutils.dumps(cpu_info) def block_stats(self, instance_name, disk): -- cgit