From 29030ec179c38f6160693a382ac3ff1a33a25837 Mon Sep 17 00:00:00 2001 From: Leander Bessa Beernaert Date: Fri, 22 Jun 2012 12:08:08 +0100 Subject: [PATCH] Adds diagnostics command for the libvirt driver. This code provides an implementation of the get_diagnostics API for the libvirt driver. This API is invoked by the 'nova diagnostic' command. There is no existing formal specification for what data items to expose from the 'get_diagnostics' API, so the XenAPI driver is used as an informal guide. The Xen implementation currently produces the following output: +----------------+-----------------+ | Property | Value | +----------------+-----------------+ | cpu0 | 4.3627 | | memory | 1171088064.0000 | | memory_target | 1171088064.0000 | | vbd_xvda_read | 0.0 | | vbd_xvda_write | 0.0 | | vif_0_rx | 3223.6870 | | vif_0_tx | 0.0 | | vif_1_rx | 104.4955 | | vif_1_tx | 0.0 | +----------------+-----------------+ The new libvirt implementation will return a similar set of stats for guest CPUs, memory, disks and network interfaces, resulting in the following output: +------------------+------------+ | Property | Value | +------------------+------------+ | cpu0_time | 2870000000 | | memory | 524288 | | vda_errors | -1 | | vda_read | 262144 | | vda_read_req | 112 | | vda_write | 5606400 | | vda_write_req | 376 | | vnet0_rx | 63343 | | vnet0_rx_drop | 0 | | vnet0_rx_errors | 0 | | vnet0_rx_packets | 431 | | vnet0_tx | 4905 | | vnet0_tx_drop | 0 | | vnet0_tx_errors | 0 | | vnet0_tx_packets | 45 | +------------------+------------+ The implementation has only been tested with the libvirt KVM driver. In a future a formal specification for the required data format should be written and all virt drivers made to comply. Finally, the fakelibvirt driver has been updated in order to support the methods required by the get_diagnostics method. For those interested, below is the formula to extract the CPU usage based on the CPU time. I've added it here for reference since it's not used at the moment and it took quite a while to locate it in the libvirt documentation. usage = 100 * (current_cpu_time - last_cpu_time) / (measurement_interval * (10 ** 9)) Fixes bug 986200. Change-Id: I92da392f43d0840ed31cfd88f54570ea5830bb4a --- nova/tests/fakelibvirt.py | 13 ++ nova/tests/test_libvirt.py | 390 ++++++++++++++++++++++++++++++++ nova/tests/test_virt_drivers.py | 2 +- 3 files changed, 404 insertions(+), 1 deletion(-) diff --git a/nova/tests/fakelibvirt.py b/nova/tests/fakelibvirt.py index ab5d4073..4e337aa5 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 86128626..a506b0ad 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 @@ -1919,6 +1920,395 @@ class LibvirtConnTestCase(test.TestCase): got = jsonutils.loads(conn.get_cpu_info()) self.assertEqual(want, got) + def test_diagnostic_vcpus_exception(self): + xml = """ + + + + + + + + + + + + + + + + + + """ + + 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 = """ + + + + + + + + + + + + + + + + + + """ + + 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 = """ + + + + + + + + + + + + + + + + + + """ + + 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 = """ + + + + + + + + + + + + + + + + + + """ + + 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 = """ + + + + + + + + + + + + + + + + + + """ + + 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 e95d15ae..bc89a24e 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):