From 0cf3789014ccccacb435d3a5c3d74afd304c7b42 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Thu, 27 Oct 2011 20:35:48 -0400 Subject: Added code to libvirt backend to report state info. Also renamed property variable in Xen code to make it consistent with nova conventions. Implements blueprint kvm-report-capabilities Change-Id: I7953e857d9b8ce4b410c31b82cead7aaa3fb911f --- nova/tests/test_libvirt.py | 62 +++++++++++++++++++++++++++++++++++++++++ nova/virt/libvirt/connection.py | 58 +++++++++++++++++++++++++++++++++++--- nova/virt/xenapi_conn.py | 6 ++-- 3 files changed, 119 insertions(+), 7 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index b7864c9dd..d4db164c9 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1076,6 +1076,68 @@ class LibvirtConnTestCase(test.TestCase): self.assertRaises(NotImplementedError, compute_driver.reboot, *args) +class HostStateTestCase(test.TestCase): + + cpu_info = '{"vendor": "Intel", "model": "pentium", "arch": "i686", '\ + '"features": ["ssse3", "monitor", "pni", "sse2", "sse", "fxsr", '\ + '"clflush", "pse36", "pat", "cmov", "mca", "pge", "mtrr", "sep", '\ + '"apic"], "topology": {"cores": "1", "threads": "1", "sockets": "1"}}' + + class FakeConnection(object): + """Fake connection object""" + + def get_vcpu_total(self): + return 1 + + def get_vcpu_used(self): + return 0 + + def get_cpu_info(self): + return HostStateTestCase.cpu_info + + def get_local_gb_total(self): + return 100 + + def get_local_gb_used(self): + return 20 + + def get_memory_mb_total(self): + return 497 + + def get_memory_mb_used(self): + return 88 + + def get_hypervisor_type(self): + return 'QEMU' + + def get_hypervisor_version(self): + return 13091 + + def test_update_status(self): + self.mox.StubOutWithMock(connection, 'get_connection') + connection.get_connection(True).AndReturn(self.FakeConnection()) + + self.mox.ReplayAll() + hs = connection.HostState(True) + stats = hs._stats + self.assertEquals(stats["vcpus"], 1) + self.assertEquals(stats["vcpus_used"], 0) + self.assertEquals(stats["cpu_info"], \ + {"vendor": "Intel", "model": "pentium", "arch": "i686", + "features": ["ssse3", "monitor", "pni", "sse2", "sse", "fxsr", + "clflush", "pse36", "pat", "cmov", "mca", "pge", + "mtrr", "sep", "apic"], + "topology": {"cores": "1", "threads": "1", "sockets": "1"} + }) + self.assertEquals(stats["disk_total"], 100) + self.assertEquals(stats["disk_used"], 20) + self.assertEquals(stats["disk_available"], 80) + self.assertEquals(stats["host_memory_total"], 497) + self.assertEquals(stats["host_memory_free"], 409) + self.assertEquals(stats["hypervisor_type"], 'QEMU') + self.assertEquals(stats["hypervisor_version"], 13091) + + class NWFilterFakes: def __init__(self): self.filters = {} diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 3e39482d8..20bd83722 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -196,6 +196,12 @@ class LibvirtConnection(driver.ComputeDriver): driver_class = utils.import_class(driver) self.volume_drivers[driver_type] = driver_class(self) + @property + def host_state(self): + if not self._host_state: + self._host_state = HostState(self._session) + return self._host_state + def init_host(self, host): # NOTE(nsokolov): moved instance restarting to ComputeManager pass @@ -1930,12 +1936,18 @@ class LibvirtConnection(driver.ComputeDriver): network_info=network_info) def update_host_status(self): - """See xenapi_conn.py implementation.""" - pass + """Retrieve status info from libvirt. + + Query libvirt to get the state of the compute node, such + as memory and disk usage. + """ + return self.host_state.update_status() def get_host_stats(self, refresh=False): - """See xenapi_conn.py implementation.""" - pass + """Return the current state of the host. + + If 'refresh' is True, run update the stats first.""" + return self.host_state.get_host_stats(refresh=refresh) def host_power_action(self, host, action): """Reboots, shuts down or powers up the host.""" @@ -1944,3 +1956,41 @@ class LibvirtConnection(driver.ComputeDriver): def set_host_enabled(self, host, enabled): """Sets the specified host's ability to accept new instances.""" pass + + +class HostState(object): + """Manages information about the compute node through libvirt""" + def __init__(self, read_only): + super(HostState, self).__init__() + self.read_only = read_only + self._stats = {} + self.connection = None + self.update_status() + + def get_host_stats(self, refresh=False): + """Return the current state of the host. + + If 'refresh' is True, run update the stats first.""" + if refresh: + self.update_status() + return self._stats + + def update_status(self): + """Retrieve status info from libvirt.""" + LOG.debug(_("Updating host stats")) + if self.connection is None: + self.connection = get_connection(self.read_only) + data = {} + data["vcpus"] = self.connection.get_vcpu_total() + data["vcpus_used"] = self.connection.get_vcpu_used() + data["cpu_info"] = utils.loads(self.connection.get_cpu_info()) + data["disk_total"] = self.connection.get_local_gb_total() + data["disk_used"] = self.connection.get_local_gb_used() + data["disk_available"] = data["disk_total"] - data["disk_used"] + data["host_memory_total"] = self.connection.get_memory_mb_total() + data["host_memory_free"] = data["host_memory_total"] - \ + self.connection.get_memory_mb_used() + data["hypervisor_type"] = self.connection.get_hypervisor_type() + data["hypervisor_version"] = self.connection.get_hypervisor_version() + + self._stats = data diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 0aa8d45a5..2dc57c5ae 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -174,7 +174,7 @@ class XenAPIConnection(driver.ComputeDriver): self._host_state = None @property - def HostState(self): + def host_state(self): if not self._host_state: self._host_state = HostState(self._session) return self._host_state @@ -377,12 +377,12 @@ class XenAPIConnection(driver.ComputeDriver): def update_host_status(self): """Update the status info of the host, and return those values to the calling program.""" - return self.HostState.update_status() + return self.host_state.update_status() def get_host_stats(self, refresh=False): """Return the current state of the host. If 'refresh' is True, run the update first.""" - return self.HostState.get_host_stats(refresh=refresh) + return self.host_state.get_host_stats(refresh=refresh) def host_power_action(self, host, action): """The only valid values for 'action' on XenServer are 'reboot' or -- cgit