diff options
author | Jenkins <jenkins@review.openstack.org> | 2012-07-12 10:46:12 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2012-07-12 10:46:12 +0000 |
commit | c2d33008f040fae7971646ad01a5d4e48a79c480 (patch) | |
tree | 60e8deb358a0e6b8759a0c2b2294813f0390dbd0 | |
parent | 7d00b664a4d74333f7cd9736ce6bb613ccddc814 (diff) | |
parent | ad54ed53cf6a475ad0f8042f8b95454a8c0b35a4 (diff) | |
download | nova-c2d33008f040fae7971646ad01a5d4e48a79c480.tar.gz nova-c2d33008f040fae7971646ad01a5d4e48a79c480.tar.xz nova-c2d33008f040fae7971646ad01a5d4e48a79c480.zip |
Merge "Adds diagnostics command for the libvirt driver."
-rw-r--r-- | nova/tests/fakelibvirt.py | 13 | ||||
-rw-r--r-- | nova/tests/test_libvirt.py | 390 | ||||
-rw-r--r-- | nova/tests/test_virt_drivers.py | 2 | ||||
-rw-r--r-- | nova/virt/libvirt/driver.py | 75 |
4 files changed, 479 insertions, 1 deletions
diff --git a/nova/tests/fakelibvirt.py b/nova/tests/fakelibvirt.py index ab5d4073c..4e337aa51 100644 --- a/nova/tests/fakelibvirt.py +++ b/nova/tests/fakelibvirt.py @@ -424,6 +424,19 @@ class Domain(object): self._snapshots[name] = snapshot return snapshot + def vcpus(self): + vcpus = ([], []) + for i in range(0, self._def['vcpu']): + vcpus[0].append((i, 1, 120405L, i)) + vcpus[1].append((True, True, True, True)) + return vcpus + + def memoryStats(self): + return {} + + def maxMemory(self): + return self._def['memory'] + class DomainSnapshot(object): def __init__(self, name, domain): diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 357ab0493..7c2fee245 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # # Copyright 2010 OpenStack LLC +# Copyright 2012 University Of Minho # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -1965,6 +1966,395 @@ class LibvirtConnTestCase(test.TestCase): got = jsonutils.loads(conn.get_cpu_info()) self.assertEqual(want, got) + def test_diagnostic_vcpus_exception(self): + xml = """ + <domain type='kvm'> + <devices> + <disk type='file'> + <source file='filename'/> + <target dev='vda' bus='virtio'/> + </disk> + <disk type='block'> + <source dev='/path/to/dev/1'/> + <target dev='vdb' bus='virtio'/> + </disk> + <interface type='network'> + <mac address='52:54:00:a4:38:38'/> + <source network='default'/> + <target dev='vnet0'/> + </interface> + </devices> + </domain> + """ + + class DiagFakeDomain(FakeVirtDomain): + + def __init__(self): + super(DiagFakeDomain, self).__init__(fake_xml=xml) + + def vcpus(self): + raise libvirt.libvirtError('vcpus missing') + + def blockStats(self, path): + return (169L, 688640L, 0L, 0L, -1L) + + def interfaceStats(self, path): + return (4408L, 82L, 0L, 0L, 0L, 0L, 0L, 0L) + + def memoryStats(self): + return {'actual': 220160L, 'rss': 200164L} + + def maxMemory(self): + return 280160L + + def fake_lookup_name(name): + return DiagFakeDomain() + + self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn') + libvirt_driver.LibvirtDriver._conn.lookupByName = fake_lookup_name + + conn = libvirt_driver.LibvirtDriver(False) + actual = conn.get_diagnostics({"name": "testvirt"}) + expect = {'vda_read': 688640L, + 'vda_read_req': 169L, + 'vda_write': 0L, + 'vda_write_req': 0L, + 'vda_errors': -1L, + 'vdb_read': 688640L, + 'vdb_read_req': 169L, + 'vdb_write': 0L, + 'vdb_write_req': 0L, + 'vdb_errors': -1L, + 'memory': 280160L, + 'memory-actual': 220160L, + 'memory-rss': 200164L, + 'vnet0_rx': 4408L, + 'vnet0_rx_drop': 0L, + 'vnet0_rx_errors': 0L, + 'vnet0_rx_packets': 82L, + 'vnet0_tx': 0L, + 'vnet0_tx_drop': 0L, + 'vnet0_tx_errors': 0L, + 'vnet0_tx_packets': 0L, + } + self.assertEqual(actual, expect) + + def test_diagnostic_blockstats_exception(self): + xml = """ + <domain type='kvm'> + <devices> + <disk type='file'> + <source file='filename'/> + <target dev='vda' bus='virtio'/> + </disk> + <disk type='block'> + <source dev='/path/to/dev/1'/> + <target dev='vdb' bus='virtio'/> + </disk> + <interface type='network'> + <mac address='52:54:00:a4:38:38'/> + <source network='default'/> + <target dev='vnet0'/> + </interface> + </devices> + </domain> + """ + + class DiagFakeDomain(FakeVirtDomain): + + def __init__(self): + super(DiagFakeDomain, self).__init__(fake_xml=xml) + + def vcpus(self): + return ([(0, 1, 15340000000L, 0), + (1, 1, 1640000000L, 0), + (2, 1, 3040000000L, 0), + (3, 1, 1420000000L, 0)], + [(True, False), + (True, False), + (True, False), + (True, False)]) + + def blockStats(self, path): + raise libvirt.libvirtError('blockStats missing') + + def interfaceStats(self, path): + return (4408L, 82L, 0L, 0L, 0L, 0L, 0L, 0L) + + def memoryStats(self): + return {'actual': 220160L, 'rss': 200164L} + + def maxMemory(self): + return 280160L + + def fake_lookup_name(name): + return DiagFakeDomain() + + self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn') + libvirt_driver.LibvirtDriver._conn.lookupByName = fake_lookup_name + + conn = libvirt_driver.LibvirtDriver(False) + actual = conn.get_diagnostics({"name": "testvirt"}) + expect = {'cpu0_time': 15340000000L, + 'cpu1_time': 1640000000L, + 'cpu2_time': 3040000000L, + 'cpu3_time': 1420000000L, + 'memory': 280160L, + 'memory-actual': 220160L, + 'memory-rss': 200164L, + 'vnet0_rx': 4408L, + 'vnet0_rx_drop': 0L, + 'vnet0_rx_errors': 0L, + 'vnet0_rx_packets': 82L, + 'vnet0_tx': 0L, + 'vnet0_tx_drop': 0L, + 'vnet0_tx_errors': 0L, + 'vnet0_tx_packets': 0L, + } + self.assertEqual(actual, expect) + + def test_diagnostic_interfacestats_exception(self): + xml = """ + <domain type='kvm'> + <devices> + <disk type='file'> + <source file='filename'/> + <target dev='vda' bus='virtio'/> + </disk> + <disk type='block'> + <source dev='/path/to/dev/1'/> + <target dev='vdb' bus='virtio'/> + </disk> + <interface type='network'> + <mac address='52:54:00:a4:38:38'/> + <source network='default'/> + <target dev='vnet0'/> + </interface> + </devices> + </domain> + """ + + class DiagFakeDomain(FakeVirtDomain): + + def __init__(self): + super(DiagFakeDomain, self).__init__(fake_xml=xml) + + def vcpus(self): + return ([(0, 1, 15340000000L, 0), + (1, 1, 1640000000L, 0), + (2, 1, 3040000000L, 0), + (3, 1, 1420000000L, 0)], + [(True, False), + (True, False), + (True, False), + (True, False)]) + + def blockStats(self, path): + return (169L, 688640L, 0L, 0L, -1L) + + def interfaceStats(self, path): + raise libvirt.libvirtError('interfaceStat missing') + + def memoryStats(self): + return {'actual': 220160L, 'rss': 200164L} + + def maxMemory(self): + return 280160L + + def fake_lookup_name(name): + return DiagFakeDomain() + + self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn') + libvirt_driver.LibvirtDriver._conn.lookupByName = fake_lookup_name + + conn = libvirt_driver.LibvirtDriver(False) + actual = conn.get_diagnostics({"name": "testvirt"}) + expect = {'cpu0_time': 15340000000L, + 'cpu1_time': 1640000000L, + 'cpu2_time': 3040000000L, + 'cpu3_time': 1420000000L, + 'vda_read': 688640L, + 'vda_read_req': 169L, + 'vda_write': 0L, + 'vda_write_req': 0L, + 'vda_errors': -1L, + 'vdb_read': 688640L, + 'vdb_read_req': 169L, + 'vdb_write': 0L, + 'vdb_write_req': 0L, + 'vdb_errors': -1L, + 'memory': 280160L, + 'memory-actual': 220160L, + 'memory-rss': 200164L, + } + self.assertEqual(actual, expect) + + def test_diagnostic_memorystats_exception(self): + xml = """ + <domain type='kvm'> + <devices> + <disk type='file'> + <source file='filename'/> + <target dev='vda' bus='virtio'/> + </disk> + <disk type='block'> + <source dev='/path/to/dev/1'/> + <target dev='vdb' bus='virtio'/> + </disk> + <interface type='network'> + <mac address='52:54:00:a4:38:38'/> + <source network='default'/> + <target dev='vnet0'/> + </interface> + </devices> + </domain> + """ + + class DiagFakeDomain(FakeVirtDomain): + + def __init__(self): + super(DiagFakeDomain, self).__init__(fake_xml=xml) + + def vcpus(self): + return ([(0, 1, 15340000000L, 0), + (1, 1, 1640000000L, 0), + (2, 1, 3040000000L, 0), + (3, 1, 1420000000L, 0)], + [(True, False), + (True, False), + (True, False), + (True, False)]) + + def blockStats(self, path): + return (169L, 688640L, 0L, 0L, -1L) + + def interfaceStats(self, path): + return (4408L, 82L, 0L, 0L, 0L, 0L, 0L, 0L) + + def memoryStats(self): + raise libvirt.libvirtError('memoryStats missing') + + def maxMemory(self): + return 280160L + + def fake_lookup_name(name): + return DiagFakeDomain() + + self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn') + libvirt_driver.LibvirtDriver._conn.lookupByName = fake_lookup_name + + conn = libvirt_driver.LibvirtDriver(False) + actual = conn.get_diagnostics({"name": "testvirt"}) + expect = {'cpu0_time': 15340000000L, + 'cpu1_time': 1640000000L, + 'cpu2_time': 3040000000L, + 'cpu3_time': 1420000000L, + 'vda_read': 688640L, + 'vda_read_req': 169L, + 'vda_write': 0L, + 'vda_write_req': 0L, + 'vda_errors': -1L, + 'vdb_read': 688640L, + 'vdb_read_req': 169L, + 'vdb_write': 0L, + 'vdb_write_req': 0L, + 'vdb_errors': -1L, + 'memory': 280160L, + 'vnet0_rx': 4408L, + 'vnet0_rx_drop': 0L, + 'vnet0_rx_errors': 0L, + 'vnet0_rx_packets': 82L, + 'vnet0_tx': 0L, + 'vnet0_tx_drop': 0L, + 'vnet0_tx_errors': 0L, + 'vnet0_tx_packets': 0L, + } + self.assertEqual(actual, expect) + + def test_diagnostic_full(self): + xml = """ + <domain type='kvm'> + <devices> + <disk type='file'> + <source file='filename'/> + <target dev='vda' bus='virtio'/> + </disk> + <disk type='block'> + <source dev='/path/to/dev/1'/> + <target dev='vdb' bus='virtio'/> + </disk> + <interface type='network'> + <mac address='52:54:00:a4:38:38'/> + <source network='default'/> + <target dev='vnet0'/> + </interface> + </devices> + </domain> + """ + + class DiagFakeDomain(FakeVirtDomain): + + def __init__(self): + super(DiagFakeDomain, self).__init__(fake_xml=xml) + + def vcpus(self): + return ([(0, 1, 15340000000L, 0), + (1, 1, 1640000000L, 0), + (2, 1, 3040000000L, 0), + (3, 1, 1420000000L, 0)], + [(True, False), + (True, False), + (True, False), + (True, False)]) + + def blockStats(self, path): + return (169L, 688640L, 0L, 0L, -1L) + + def interfaceStats(self, path): + return (4408L, 82L, 0L, 0L, 0L, 0L, 0L, 0L) + + def memoryStats(self): + return {'actual': 220160L, 'rss': 200164L} + + def maxMemory(self): + return 280160L + + def fake_lookup_name(name): + return DiagFakeDomain() + + self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn') + libvirt_driver.LibvirtDriver._conn.lookupByName = fake_lookup_name + + conn = libvirt_driver.LibvirtDriver(False) + actual = conn.get_diagnostics({"name": "testvirt"}) + expect = {'cpu0_time': 15340000000L, + 'cpu1_time': 1640000000L, + 'cpu2_time': 3040000000L, + 'cpu3_time': 1420000000L, + 'vda_read': 688640L, + 'vda_read_req': 169L, + 'vda_write': 0L, + 'vda_write_req': 0L, + 'vda_errors': -1L, + 'vdb_read': 688640L, + 'vdb_read_req': 169L, + 'vdb_write': 0L, + 'vdb_write_req': 0L, + 'vdb_errors': -1L, + 'memory': 280160L, + 'memory-actual': 220160L, + 'memory-rss': 200164L, + 'vnet0_rx': 4408L, + 'vnet0_rx_drop': 0L, + 'vnet0_rx_errors': 0L, + 'vnet0_rx_packets': 82L, + 'vnet0_tx': 0L, + 'vnet0_tx_drop': 0L, + 'vnet0_tx_errors': 0L, + 'vnet0_tx_packets': 0L, + } + self.assertEqual(actual, expect) + class HostStateTestCase(test.TestCase): diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py index e95d15ae5..bc89a24e8 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -402,7 +402,7 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): @catch_notimplementederror def test_get_diagnostics(self): instance_ref, network_info = self._get_running_instance() - self.connection.get_diagnostics(instance_ref['name']) + self.connection.get_diagnostics(instance_ref) @catch_notimplementederror def test_block_stats(self): diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 0f62a9c52..94564a622 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -5,6 +5,7 @@ # All Rights Reserved. # Copyright (c) 2010 Citrix Systems, Inc. # Copyright (c) 2011 Piston Cloud Computing, Inc +# Copyright (c) 2012 University Of Minho # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -2723,6 +2724,80 @@ class LibvirtDriver(driver.ComputeDriver): """Confirms a resize, destroying the source VM""" self._cleanup_resize(instance) + def get_diagnostics(self, instance): + def get_io_devices(xml_doc): + """ get the list of io devices from the + xml document.""" + result = {"volumes": [], "ifaces": []} + try: + doc = etree.fromstring(xml_doc) + except Exception: + return result + blocks = [('./devices/disk', 'volumes'), + ('./devices/interface', 'ifaces')] + for block, key in blocks: + section = doc.findall(block) + for node in section: + for child in node.getchildren(): + if child.tag == 'target' and child.get('dev'): + result[key].append(child.get('dev')) + return result + + domain = self._lookup_by_name(instance['name']) + output = {} + # get cpu time, might launch an exception if the method + # is not supported by the underlying hypervisor being + # used by libvirt + try: + cputime = domain.vcpus()[0] + for i in range(len(cputime)): + output["cpu" + str(i) + "_time"] = cputime[i][2] + except libvirt.libvirtError: + pass + # get io status + xml = domain.XMLDesc(0) + dom_io = get_io_devices(xml) + for disk in dom_io["volumes"]: + try: + # blockStats might launch an exception if the method + # is not supported by the underlying hypervisor being + # used by libvirt + stats = domain.blockStats(disk) + output[disk + "_read_req"] = stats[0] + output[disk + "_read"] = stats[1] + output[disk + "_write_req"] = stats[2] + output[disk + "_write"] = stats[3] + output[disk + "_errors"] = stats[4] + except libvirt.libvirtError: + pass + for interface in dom_io["ifaces"]: + try: + # interfaceStats might launch an exception if the method + # is not supported by the underlying hypervisor being + # used by libvirt + stats = domain.interfaceStats(interface) + output[interface + "_rx"] = stats[0] + output[interface + "_rx_packets"] = stats[1] + output[interface + "_rx_errors"] = stats[2] + output[interface + "_rx_drop"] = stats[3] + output[interface + "_tx"] = stats[4] + output[interface + "_tx_packets"] = stats[5] + output[interface + "_tx_errors"] = stats[6] + output[interface + "_tx_drop"] = stats[7] + except libvirt.libvirtError: + pass + output["memory"] = domain.maxMemory() + # memoryStats might launch an exception if the method + # is not supported by the underlying hypervisor being + # used by libvirt + try: + mem = domain.memoryStats() + for key in mem.keys(): + output["memory-" + key] = mem[key] + except libvirt.libvirtError: + pass + return output + class HostState(object): """Manages information about the compute node through libvirt""" |