From 09d978994325b28c76050f112af3ee66b84a5e1f Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Mon, 23 Jul 2012 16:01:47 -0500 Subject: Add call to get hypervisor statistics Adds an admin API call to retrieve the compute node statistics for an entire nova instance. Counts up all hypervisors and sums all their values (vcpus, vcpus_used, etc.). Change-Id: I36272656bb417c1549133fd2963bac54db0e86c6 --- nova/api/openstack/compute/contrib/hypervisors.py | 30 ++++++++- nova/db/api.py | 5 ++ nova/db/sqlalchemy/api.py | 24 +++++++ .../openstack/compute/contrib/test_hypervisors.py | 74 +++++++++++++++++++++- 4 files changed, 130 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/compute/contrib/hypervisors.py b/nova/api/openstack/compute/contrib/hypervisors.py index 49b050871..33aaa014b 100644 --- a/nova/api/openstack/compute/contrib/hypervisors.py +++ b/nova/api/openstack/compute/contrib/hypervisors.py @@ -104,6 +104,26 @@ class HypervisorServersTemplate(xmlutil.TemplateBuilder): return xmlutil.MasterTemplate(root, 1) +class HypervisorStatisticsTemplate(xmlutil.TemplateBuilder): + def construct(self): + root = xmlutil.TemplateElement('hypervisor_statistics', + selector='hypervisor_statistics') + root.set('count') + root.set('vcpus') + root.set('memory_mb') + root.set('local_gb') + root.set('vcpus_used') + root.set('memory_mb_used') + root.set('local_gb_used') + root.set('free_ram_mb') + root.set('free_disk_gb') + root.set('current_workload') + root.set('running_vms') + root.set('disk_available_least') + + return xmlutil.MasterTemplate(root, 1) + + class HypervisorsController(object): """The Hypervisors API controller for the OpenStack API.""" @@ -211,6 +231,13 @@ class HypervisorsController(object): msg = _("No hypervisor matching '%s' could be found.") % id raise webob.exc.HTTPNotFound(explanation=msg) + @wsgi.serializers(xml=HypervisorStatisticsTemplate) + def statistics(self, req): + context = req.environ['nova.context'] + authorize(context) + stats = db.compute_node_statistics(context) + return dict(hypervisor_statistics=stats) + class Hypervisors(extensions.ExtensionDescriptor): """Admin-only hypervisor administration""" @@ -223,7 +250,8 @@ class Hypervisors(extensions.ExtensionDescriptor): def get_resources(self): resources = [extensions.ResourceExtension('os-hypervisors', HypervisorsController(), - collection_actions={'detail': 'GET'}, + collection_actions={'detail': 'GET', + 'statistics': 'GET'}, member_actions={'uptime': 'GET', 'search': 'GET', 'servers': 'GET'})] diff --git a/nova/db/api.py b/nova/db/api.py index be1a20a10..231df02f2 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -232,6 +232,11 @@ def compute_node_utilization_set(context, host, free_ram_mb=None, return IMPL.compute_node_utilization_set(context, host, free_ram_mb, free_disk_gb, work, vms) + +def compute_node_statistics(context): + return IMPL.compute_node_statistics(context) + + ################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index f3bcb5c1e..946cac780 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -630,6 +630,30 @@ def compute_node_utilization_set(context, host, free_ram_mb=None, return compute_node +def compute_node_statistics(context): + """Compute statistics over all compute nodes.""" + result = model_query(context, + func.count(models.ComputeNode.id), + func.sum(models.ComputeNode.vcpus), + func.sum(models.ComputeNode.memory_mb), + func.sum(models.ComputeNode.local_gb), + func.sum(models.ComputeNode.vcpus_used), + func.sum(models.ComputeNode.memory_mb_used), + func.sum(models.ComputeNode.local_gb_used), + func.sum(models.ComputeNode.free_ram_mb), + func.sum(models.ComputeNode.free_disk_gb), + func.sum(models.ComputeNode.current_workload), + func.sum(models.ComputeNode.running_vms), + func.sum(models.ComputeNode.disk_available_least), + read_deleted="no").first() + + # Build a dict of the info--making no assumptions about result + fields = ('count', 'vcpus', 'memory_mb', 'local_gb', 'vcpus_used', + 'memory_mb_used', 'local_gb_used', 'free_ram_mb', 'free_disk_gb', + 'current_workload', 'running_vms', 'disk_available_least') + return dict((field, result[idx] or 0) for idx, field in enumerate(fields)) + + ################### diff --git a/nova/tests/api/openstack/compute/contrib/test_hypervisors.py b/nova/tests/api/openstack/compute/contrib/test_hypervisors.py index 6a9a1fc25..740477ca3 100644 --- a/nova/tests/api/openstack/compute/contrib/test_hypervisors.py +++ b/nova/tests/api/openstack/compute/contrib/test_hypervisors.py @@ -94,6 +94,32 @@ def fake_compute_node_get(context, compute_id): raise exception.ComputeHostNotFound +def fake_compute_node_statistics(context): + result = dict( + count=0, + vcpus=0, + memory_mb=0, + local_gb=0, + vcpus_used=0, + memory_mb_used=0, + local_gb_used=0, + free_ram_mb=0, + free_disk_gb=0, + current_workload=0, + running_vms=0, + disk_available_least=0, + ) + + for hyper in TEST_HYPERS: + for key in result: + if key == 'count': + result[key] += 1 + else: + result[key] += hyper[key] + + return result + + def fake_instance_get_all_by_host(context, host): results = [] for inst in TEST_SERVERS: @@ -113,6 +139,8 @@ class HypervisorsTest(test.TestCase): fake_compute_node_search_by_hypervisor) self.stubs.Set(db, 'compute_node_get', fake_compute_node_get) + self.stubs.Set(db, 'compute_node_statistics', + fake_compute_node_statistics) self.stubs.Set(db, 'instance_get_all_by_host', fake_instance_get_all_by_host) @@ -285,11 +313,27 @@ class HypervisorsTest(test.TestCase): dict(name="inst2", uuid="uuid2"), dict(name="inst4", uuid="uuid4")])])) + def test_statistics(self): + req = fakes.HTTPRequest.blank('/v2/fake/os-hypervisors/statistics') + result = self.controller.statistics(req) + + self.assertEqual(result, dict(hypervisor_statistics=dict( + count=2, + vcpus=8, + memory_mb=20 * 1024, + local_gb=500, + vcpus_used=4, + memory_mb_used=10 * 1024, + local_gb_used=250, + free_ram_mb=10 * 1024, + free_disk_gb=250, + current_workload=4, + running_vms=4, + disk_available_least=200))) + class HypervisorsSerializersTest(test.TestCase): def compare_to_exemplar(self, exemplar, hyper): - self.assertEqual('hypervisor', hyper.tag) - # Check attributes for key, value in exemplar.items(): if key in ('service', 'servers'): @@ -332,6 +376,7 @@ class HypervisorsSerializersTest(test.TestCase): self.assertEqual('hypervisors', tree.tag) self.assertEqual(len(exemplar['hypervisors']), len(tree)) for idx, hyper in enumerate(tree): + self.assertEqual('hypervisor', hyper.tag) self.compare_to_exemplar(exemplar['hypervisors'][idx], hyper) def test_detail_serializer(self): @@ -377,6 +422,7 @@ class HypervisorsSerializersTest(test.TestCase): self.assertEqual('hypervisors', tree.tag) self.assertEqual(len(exemplar['hypervisors']), len(tree)) for idx, hyper in enumerate(tree): + self.assertEqual('hypervisor', hyper.tag) self.compare_to_exemplar(exemplar['hypervisors'][idx], hyper) def test_show_serializer(self): @@ -402,6 +448,7 @@ class HypervisorsSerializersTest(test.TestCase): text = serializer.serialize(exemplar) tree = etree.fromstring(text) + self.assertEqual('hypervisor', tree.tag) self.compare_to_exemplar(exemplar['hypervisor'], tree) def test_uptime_serializer(self): @@ -413,6 +460,7 @@ class HypervisorsSerializersTest(test.TestCase): text = serializer.serialize(exemplar) tree = etree.fromstring(text) + self.assertEqual('hypervisor', tree.tag) self.compare_to_exemplar(exemplar['hypervisor'], tree) def test_servers_serializer(self): @@ -438,4 +486,26 @@ class HypervisorsSerializersTest(test.TestCase): self.assertEqual('hypervisors', tree.tag) self.assertEqual(len(exemplar['hypervisors']), len(tree)) for idx, hyper in enumerate(tree): + self.assertEqual('hypervisor', hyper.tag) self.compare_to_exemplar(exemplar['hypervisors'][idx], hyper) + + def test_statistics_serializer(self): + serializer = hypervisors.HypervisorStatisticsTemplate() + exemplar = dict(hypervisor_statistics=dict( + count=2, + vcpus=8, + memory_mb=20 * 1024, + local_gb=500, + vcpus_used=4, + memory_mb_used=10 * 1024, + local_gb_used=250, + free_ram_mb=10 * 1024, + free_disk_gb=250, + current_workload=4, + running_vms=4, + disk_available_least=200)) + text = serializer.serialize(exemplar) + tree = etree.fromstring(text) + + self.assertEqual('hypervisor_statistics', tree.tag) + self.compare_to_exemplar(exemplar['hypervisor_statistics'], tree) -- cgit