From 2a998267e008bf135ebf943bbebac11c3f109b80 Mon Sep 17 00:00:00 2001 From: Gary Kotton Date: Sat, 12 Oct 2013 11:10:17 -0700 Subject: [PATCH] VMware: add support for VM diagnostics The drivers now return diagnostics for a requested VM. Implements blueprint vmware-vm-diagnostics VMware specific keys will be namespaced. DocImpact Change-Id: I08f199f7e3b4edafe60ce73d2fc833f8c5a5574e --- nova/tests/virt/vmwareapi/test_vmwareapi.py | 27 ++++++++++------- nova/virt/vmwareapi/fake.py | 32 +++++++++++++++++++++ nova/virt/vmwareapi/vim.py | 26 +++++++++++++++++ nova/virt/vmwareapi/vmops.py | 31 ++++++++++++++++++-- 4 files changed, 104 insertions(+), 12 deletions(-) diff --git a/nova/tests/virt/vmwareapi/test_vmwareapi.py b/nova/tests/virt/vmwareapi/test_vmwareapi.py index 2dbe7ec7c9c2..45b5f2bcfbae 100644 --- a/nova/tests/virt/vmwareapi/test_vmwareapi.py +++ b/nova/tests/virt/vmwareapi/test_vmwareapi.py @@ -653,9 +653,23 @@ class VMwareAPIVMTestCase(test.NoDBTestCase): instance=None) def test_get_diagnostics(self): - # Simply tests that the VMwareESXDriver doesn't implement the - # get_diagnostics API. - self.assertRaises(NotImplementedError, self.conn.get_diagnostics, None) + self._create_vm() + expected = {'memoryReservation': 0, 'suspendInterval': 0, + 'maxCpuUsage': 2000, 'toolsInstallerMounted': False, + 'consumedOverheadMemory': 20, 'numEthernetCards': 1, + 'numCpu': 1, 'featureRequirement': [{'key': 'cpuid.AES'}], + 'memoryOverhead': 21417984, + 'guestMemoryUsage': 0, 'connectionState': 'connected', + 'memorySizeMB': 512, 'balloonedMemory': 0, + 'vmPathName': 'fake_path', 'template': False, + 'overallCpuUsage': 0, 'powerState': 'poweredOn', + 'cpuReservation': 0, 'overallCpuDemand': 0, + 'numVirtualDisks': 1, 'hostMemoryUsage': 141} + expected = dict([('vmware:' + k, v) for k, v in expected.items()]) + self.assertThat( + self.conn.get_diagnostics({'name': 1, 'uuid': self.uuid, + 'node': self.instance_node}), + matchers.DictMatches(expected)) def test_get_console_output(self): self._create_instance_in_the_db() @@ -1190,13 +1204,6 @@ class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase): self.conn.unplug_vifs, instance=self.instance, network_info=None) - def test_get_diagnostics(self): - # Tests that the VMwareVCDriver doesn't implement get_diagnostics. - self._create_instance_in_the_db() - self.assertRaises(NotImplementedError, - self.conn.get_diagnostics, - self.instance) - def test_migrate_disk_and_power_off(self): def fake_update_instance_progress(context, instance, step, total_steps): diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index 642f936ae486..bde919d53292 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -325,6 +325,38 @@ class VirtualMachine(ManagedObject): self.set("config.extraConfig", kwargs.get("extra_config", None)) self.set('runtime.host', kwargs.get("runtime_host", None)) self.device = kwargs.get("virtual_device") + # Sample of diagnostics data is below. + config = [ + ('template', False), + ('vmPathName', 'fake_path'), + ('memorySizeMB', 512), + ('cpuReservation', 0), + ('memoryReservation', 0), + ('numCpu', 1), + ('numEthernetCards', 1), + ('numVirtualDisks', 1)] + self.set("summary.config", config) + + quickStats = [ + ('overallCpuUsage', 0), + ('overallCpuDemand', 0), + ('guestMemoryUsage', 0), + ('hostMemoryUsage', 141), + ('balloonedMemory', 0), + ('consumedOverheadMemory', 20)] + self.set("summary.quickStats", quickStats) + + key1 = {'key': 'cpuid.AES'} + key2 = {'key': 'cpuid.AVX'} + runtime = [ + ('connectionState', 'connected'), + ('powerState', 'poweredOn'), + ('toolsInstallerMounted', False), + ('suspendInterval', 0), + ('memoryOverhead', 21417984), + ('maxCpuUsage', 2000), + ('featureRequirement', [key1, key2])] + self.set("summary.runtime", runtime) def reconfig(self, factory, val): """ diff --git a/nova/virt/vmwareapi/vim.py b/nova/virt/vmwareapi/vim.py index 1ed2d10576e4..d2143936e639 100644 --- a/nova/virt/vmwareapi/vim.py +++ b/nova/virt/vmwareapi/vim.py @@ -51,6 +51,32 @@ def get_moref(value, type): return moref +def object_to_dict(obj, list_depth=1): + """Convert Suds object into serializable format. + + The calling function can limit the amount of list entries that + are converted. + """ + d = {} + for k, v in suds.sudsobject.asdict(obj).iteritems(): + if hasattr(v, '__keylist__'): + d[k] = object_to_dict(v, list_depth=list_depth) + elif isinstance(v, list): + d[k] = [] + used = 0 + for item in v: + used = used + 1 + if used > list_depth: + break + if hasattr(item, '__keylist__'): + d[k].append(object_to_dict(item, list_depth=list_depth)) + else: + d[k].append(item) + else: + d[k] = v + return d + + class VIMMessagePlugin(suds.plugin.MessagePlugin): def addAttributeForValue(self, node): # suds does not handle AnyType properly. diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 8217247428c4..d84b5fd09a30 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -46,6 +46,7 @@ from nova import utils from nova.virt import configdrive from nova.virt import driver from nova.virt.vmwareapi import vif as vmwarevif +from nova.virt.vmwareapi import vim from nova.virt.vmwareapi import vim_util from nova.virt.vmwareapi import vm_util from nova.virt.vmwareapi import vmware_images @@ -1300,10 +1301,36 @@ class VMwareVMOps(object): 'num_cpu': int(query['summary.config.numCpu']), 'cpu_time': 0} + def _get_diagnostic_from_object_properties(self, props, wanted_props): + diagnostics = {} + while props: + for elem in props.objects: + for prop in elem.propSet: + if prop.name in wanted_props: + prop_dict = vim.object_to_dict(prop.val, list_depth=1) + diagnostics.update(prop_dict) + token = vm_util._get_token(props) + if not token: + break + + props = self._session._call_method(vim_util, + "continue_to_get_objects", + token) + return diagnostics + def get_diagnostics(self, instance): """Return data about VM diagnostics.""" - msg = _("get_diagnostics not implemented for vmwareapi") - raise NotImplementedError(msg) + vm_ref = vm_util.get_vm_ref(self._session, instance) + lst_properties = ["summary.config", + "summary.quickStats", + "summary.runtime"] + vm_props = self._session._call_method(vim_util, + "get_object_properties", None, vm_ref, "VirtualMachine", + lst_properties) + data = self._get_diagnostic_from_object_properties(vm_props, + set(lst_properties)) + # Add a namespace to all of the diagnostsics + return dict([('vmware:' + k, v) for k, v in data.items()]) def get_console_output(self, instance): """Return snapshot of console."""