From d43e49ae465c501ff453364a7c7cbe5f0ec8a0f0 Mon Sep 17 00:00:00 2001 From: Romain Hardouin Date: Mon, 30 Sep 2013 16:12:12 +0200 Subject: [PATCH] Handle the case no SimpleTenantUsage Nova extension When the SimpleTenantUsage Nova extension is not enabled, panels that include usages produce an error and the usage table is useless. Three panels use the SimpleTenantUsage extension: - openstack_dashboard.dashboards.admin.overview - openstack_dashboard.dashboards.admin.projects - openstack_dashboard.dashboards.project.overview This fix avoids errors and does not show the usage table in such a case. A context variable 'simple_tenant_usage_enabled' is now available in templates rendered by usage.UsageView subclasses. Unit tests now mock nova 'extension_supported' API. Change-Id: Ib306846bf6c947572ba0e7c773125d03b3dbf68b Closes-Bug: #1211470 --- .../overview/templates/overview/usage.html | 7 +- .../dashboards/admin/overview/tests.py | 128 +++++++++++------ .../projects/templates/projects/usage.html | 6 +- .../overview/templates/overview/usage.html | 7 +- .../dashboards/project/overview/tests.py | 135 +++++++++++++----- openstack_dashboard/usage/base.py | 3 + openstack_dashboard/usage/views.py | 3 + 7 files changed, 198 insertions(+), 91 deletions(-) diff --git a/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.html b/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.html index 7f53c2a9d1..7ce6c3ff29 100644 --- a/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.html +++ b/openstack_dashboard/dashboards/admin/overview/templates/overview/usage.html @@ -17,6 +17,9 @@ {% endif %} - {% include "horizon/common/_usage_summary.html" %} - {{ table.render }} + + {% if simple_tenant_usage_enabled %} + {% include "horizon/common/_usage_summary.html" %} + {{ table.render }} + {% endif %} {% endblock %} diff --git a/openstack_dashboard/dashboards/admin/overview/tests.py b/openstack_dashboard/dashboards/admin/overview/tests.py index bdf3c80837..d21a3beefd 100644 --- a/openstack_dashboard/dashboards/admin/overview/tests.py +++ b/openstack_dashboard/dashboards/admin/overview/tests.py @@ -38,24 +38,44 @@ INDEX_URL = reverse('horizon:project:overview:index') class UsageViewTests(test.BaseAdminViewTests): - @test.create_stubs({api.nova: ('usage_list', 'tenant_absolute_limits', ), - api.keystone: ('tenant_list',), - api.neutron: ('is_extension_supported',), - api.network: ('tenant_floating_ip_list', - 'security_group_list')}) + def _stub_nova_api_calls(self, nova_stu_enabled): + self.mox.StubOutWithMock(api.nova, 'usage_list') + self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') + self.mox.StubOutWithMock(api.nova, 'extension_supported') + self.mox.StubOutWithMock(api.keystone, 'tenant_list') + self.mox.StubOutWithMock(api.neutron, 'is_extension_supported') + self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list') + self.mox.StubOutWithMock(api.network, 'security_group_list') + + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(nova_stu_enabled) + def test_usage(self): + self._test_usage(nova_stu_enabled=True) + + def test_usage_disabled(self): + self._test_usage(nova_stu_enabled=False) + + def _test_usage(self, nova_stu_enabled=True): + self._stub_nova_api_calls(nova_stu_enabled) + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(nova_stu_enabled) now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) api.keystone.tenant_list(IsA(http.HttpRequest)) \ .AndReturn([self.tenants.list(), False]) - api.nova.usage_list(IsA(http.HttpRequest), - datetime.datetime(now.year, - now.month, - now.day, 0, 0, 0, 0), - datetime.datetime(now.year, - now.month, - now.day, 23, 59, 59, 0)) \ - .AndReturn([usage_obj]) + + if nova_stu_enabled: + api.nova.usage_list(IsA(http.HttpRequest), + datetime.datetime(now.year, + now.month, + now.day, 0, 0, 0, 0), + datetime.datetime(now.year, + now.month, + now.day, 23, 59, 59, 0)) \ + .AndReturn([usage_obj]) api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \ .AndReturn(self.limits['absolute']) api.neutron.is_extension_supported(IsA(http.HttpRequest), @@ -69,38 +89,50 @@ class UsageViewTests(test.BaseAdminViewTests): res = self.client.get(reverse('horizon:admin:overview:index')) self.assertTemplateUsed(res, 'admin/overview/usage.html') self.assertTrue(isinstance(res.context['usage'], usage.GlobalUsage)) - self.assertContains(res, - 'test_tenant' - '' - '%s' - '%s' - '%s' - '%.2f' - '%.2f' % - (usage_obj.vcpus, - usage_obj.disk_gb_hours, - sizeformat.mbformat(usage_obj.memory_mb), - usage_obj.vcpu_hours, - usage_obj.total_local_gb_usage)) + self.assertEqual(nova_stu_enabled, + res.context['simple_tenant_usage_enabled']) + + usage_table = 'test_tenant' \ + '%s' \ + '%s' \ + '%s' \ + '%.2f' \ + '%.2f' % \ + (usage_obj.vcpus, + usage_obj.disk_gb_hours, + sizeformat.mbformat(usage_obj.memory_mb), + usage_obj.vcpu_hours, + usage_obj.total_local_gb_usage) + + if nova_stu_enabled: + self.assertContains(res, usage_table) + else: + self.assertNotContains(res, usage_table) - @test.create_stubs({api.nova: ('usage_list', 'tenant_absolute_limits', ), - api.keystone: ('tenant_list',), - api.neutron: ('is_extension_supported',), - api.network: ('tenant_floating_ip_list', - 'security_group_list')}) def test_usage_csv(self): + self._test_usage_csv(nova_stu_enabled=True) + + def test_usage_csv_disabled(self): + self._test_usage_csv(nova_stu_enabled=False) + + def _test_usage_csv(self, nova_stu_enabled=True): + self._stub_nova_api_calls(nova_stu_enabled) + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(nova_stu_enabled) now = timezone.now() usage_obj = [api.nova.NovaUsage(u) for u in self.usages.list()] api.keystone.tenant_list(IsA(http.HttpRequest)) \ .AndReturn([self.tenants.list(), False]) - api.nova.usage_list(IsA(http.HttpRequest), - datetime.datetime(now.year, - now.month, - now.day, 0, 0, 0, 0), - datetime.datetime(now.year, - now.month, - now.day, 23, 59, 59, 0)) \ - .AndReturn(usage_obj) + if nova_stu_enabled: + api.nova.usage_list(IsA(http.HttpRequest), + datetime.datetime(now.year, + now.month, + now.day, 0, 0, 0, 0), + datetime.datetime(now.year, + now.month, + now.day, 23, 59, 59, 0)) \ + .AndReturn(usage_obj) api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ .AndReturn(self.limits['absolute']) api.neutron.is_extension_supported(IsA(http.HttpRequest), @@ -116,11 +148,13 @@ class UsageViewTests(test.BaseAdminViewTests): self.assertTemplateUsed(res, 'admin/overview/usage.csv') self.assertTrue(isinstance(res.context['usage'], usage.GlobalUsage)) hdr = 'Project Name,VCPUs,Ram (MB),Disk (GB),Usage (Hours)' - self.assertContains(res, '%s\r\n' % (hdr)) - for obj in usage_obj: - row = u'{0},{1},{2},{3},{4:.2f}\r\n'.format(obj.project_name, - obj.vcpus, - obj.memory_mb, - obj.disk_gb_hours, - obj.vcpu_hours) - self.assertContains(res, row) + self.assertContains(res, '%s\r\n' % hdr) + + if nova_stu_enabled: + for obj in usage_obj: + row = u'{0},{1},{2},{3},{4:.2f}\r\n'.format(obj.project_name, + obj.vcpus, + obj.memory_mb, + obj.disk_gb_hours, + obj.vcpu_hours) + self.assertContains(res, row) diff --git a/openstack_dashboard/dashboards/admin/projects/templates/projects/usage.html b/openstack_dashboard/dashboards/admin/projects/templates/projects/usage.html index 0a71273d38..767df022eb 100644 --- a/openstack_dashboard/dashboards/admin/projects/templates/projects/usage.html +++ b/openstack_dashboard/dashboards/admin/projects/templates/projects/usage.html @@ -7,6 +7,8 @@ {% endblock %} {% block main %} - {% include "horizon/common/_usage_summary.html" %} - {{ table.render }} + {% if simple_tenant_usage_enabled %} + {% include "horizon/common/_usage_summary.html" %} + {{ table.render }} + {% endif %} {% endblock %} diff --git a/openstack_dashboard/dashboards/project/overview/templates/overview/usage.html b/openstack_dashboard/dashboards/project/overview/templates/overview/usage.html index 3651499a1f..ee804f2131 100644 --- a/openstack_dashboard/dashboards/project/overview/templates/overview/usage.html +++ b/openstack_dashboard/dashboards/project/overview/templates/overview/usage.html @@ -8,6 +8,9 @@ {% block main %} {% include "horizon/common/_limit_summary.html" %} - {% include "horizon/common/_usage_summary.html" %} - {{ table.render }} + + {% if simple_tenant_usage_enabled %} + {% include "horizon/common/_usage_summary.html" %} + {{ table.render }} + {% endif %} {% endblock %} diff --git a/openstack_dashboard/dashboards/project/overview/tests.py b/openstack_dashboard/dashboards/project/overview/tests.py index 8fbfa84cb6..cbb4469b35 100644 --- a/openstack_dashboard/dashboards/project/overview/tests.py +++ b/openstack_dashboard/dashboards/project/overview/tests.py @@ -36,6 +36,15 @@ INDEX_URL = reverse('horizon:project:overview:index') class UsageViewTests(test.TestCase): + + def _stub_nova_api_calls(self, nova_stu_enabled=True): + self.mox.StubOutWithMock(api.nova, 'usage_get') + self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') + self.mox.StubOutWithMock(api.nova, 'extension_supported') + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(nova_stu_enabled) + def _stub_neutron_api_calls(self, neutron_sg_enabled=True): self.mox.StubOutWithMock(api.neutron, 'is_extension_supported') self.mox.StubOutWithMock(api.network, 'tenant_floating_ip_list') @@ -51,20 +60,30 @@ class UsageViewTests(test.TestCase): .AndReturn(self.q_secgroups.list()) def test_usage(self): + self._test_usage(nova_stu_enabled=True) + + def test_usage_disabled(self): + self._test_usage(nova_stu_enabled=False) + + def _test_usage(self, nova_stu_enabled): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') - api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, - datetime.datetime(now.year, - now.month, - now.day, 0, 0, 0, 0), - datetime.datetime(now.year, - now.month, - now.day, 23, 59, 59, 0)) \ - .AndReturn(usage_obj) + self._stub_nova_api_calls(nova_stu_enabled) + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(nova_stu_enabled) + + if nova_stu_enabled: + api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, + datetime.datetime(now.year, + now.month, + now.day, 0, 0, 0, 0), + datetime.datetime(now.year, + now.month, + now.day, 23, 59, 59, 0)) \ + .AndReturn(usage_obj) api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \ - .AndReturn(self.limits['absolute']) + .AndReturn(self.limits['absolute']) self._stub_neutron_api_calls() self.mox.ReplayAll() @@ -72,23 +91,38 @@ class UsageViewTests(test.TestCase): usages = res.context['usage'] self.assertTemplateUsed(res, 'project/overview/usage.html') self.assertTrue(isinstance(usages, usage.ProjectUsage)) - self.assertContains(res, 'form-horizontal') + self.assertEqual(nova_stu_enabled, + res.context['simple_tenant_usage_enabled']) + if nova_stu_enabled: + self.assertContains(res, 'form-horizontal') + else: + self.assertNotContains(res, 'form-horizontal') self.assertEqual(usages.limits['maxTotalFloatingIps'], float("inf")) def test_usage_nova_network(self): + self._test_usage_nova_network(nova_stu_enabled=True) + + def test_usage_nova_network_disabled(self): + self._test_usage_nova_network(nova_stu_enabled=False) + + def _test_usage_nova_network(self, nova_stu_enabled): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') self.mox.StubOutWithMock(api.base, 'is_service_enabled') - api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, - datetime.datetime(now.year, - now.month, - now.day, 0, 0, 0, 0), - datetime.datetime(now.year, - now.month, - now.day, 23, 59, 59, 0)) \ - .AndReturn(usage_obj) + self._stub_nova_api_calls(nova_stu_enabled) + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(nova_stu_enabled) + + if nova_stu_enabled: + api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, + datetime.datetime(now.year, + now.month, + now.day, 0, 0, 0, 0), + datetime.datetime(now.year, + now.month, + now.day, 23, 59, 59, 0)) \ + .AndReturn(usage_obj) api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \ .AndReturn(self.limits['absolute']) api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \ @@ -99,14 +133,21 @@ class UsageViewTests(test.TestCase): usages = res.context['usage'] self.assertTemplateUsed(res, 'project/overview/usage.html') self.assertTrue(isinstance(usages, usage.ProjectUsage)) - self.assertContains(res, 'form-horizontal') + self.assertEqual(nova_stu_enabled, + res.context['simple_tenant_usage_enabled']) + if nova_stu_enabled: + self.assertContains(res, 'form-horizontal') + else: + self.assertNotContains(res, 'form-horizontal') self.assertEqual(usages.limits['maxTotalFloatingIps'], 10) def test_unauthorized(self): exc = self.exceptions.nova_unauthorized now = timezone.now() - self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') + self._stub_nova_api_calls() + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(True) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, datetime.datetime(now.year, now.month, @@ -127,15 +168,25 @@ class UsageViewTests(test.TestCase): self.assertContains(res, 'Unauthorized:') def test_usage_csv(self): + self._test_usage_csv(nova_stu_enabled=True) + + def test_usage_csv_disabled(self): + self._test_usage_csv(nova_stu_enabled=False) + + def _test_usage_csv(self, nova_stu_enabled=True): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') + self._stub_nova_api_calls(nova_stu_enabled) + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(nova_stu_enabled) start = datetime.datetime(now.year, now.month, now.day, 0, 0, 0, 0) end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0) - api.nova.usage_get(IsA(http.HttpRequest), - self.tenant.id, - start, end).AndReturn(usage_obj) + + if nova_stu_enabled: + api.nova.usage_get(IsA(http.HttpRequest), + self.tenant.id, + start, end).AndReturn(usage_obj) api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\ .AndReturn(self.limits['absolute']) self._stub_neutron_api_calls() @@ -147,10 +198,12 @@ class UsageViewTests(test.TestCase): def test_usage_exception_usage(self): now = timezone.now() - self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') start = datetime.datetime(now.year, now.month, now.day, 0, 0, 0, 0) end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0) + self._stub_nova_api_calls() + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(True) api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id, start, end).AndRaise(self.exceptions.nova) @@ -166,8 +219,10 @@ class UsageViewTests(test.TestCase): def test_usage_exception_quota(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') + self._stub_nova_api_calls() + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(True) start = datetime.datetime(now.year, now.month, now.day, 0, 0, 0, 0) end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0) api.nova.usage_get(IsA(http.HttpRequest), @@ -185,8 +240,10 @@ class UsageViewTests(test.TestCase): def test_usage_default_tenant(self): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') + self._stub_nova_api_calls() + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(True) start = datetime.datetime(now.year, now.month, now.day, 0, 0, 0, 0) end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0) api.nova.usage_get(IsA(http.HttpRequest), @@ -212,8 +269,10 @@ class UsageViewTests(test.TestCase): def _test_usage_with_neutron(self, neutron_sg_enabled=True): now = timezone.now() usage_obj = api.nova.NovaUsage(self.usages.first()) - self.mox.StubOutWithMock(api.nova, 'usage_get') - self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits') + self._stub_nova_api_calls() + api.nova.extension_supported( + 'SimpleTenantUsage', IsA(http.HttpRequest)) \ + .AndReturn(True) self.mox.StubOutWithMock(api.neutron, 'tenant_quota_get') start = datetime.datetime(now.year, now.month, now.day, 0, 0, 0, 0) end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0) diff --git a/openstack_dashboard/usage/base.py b/openstack_dashboard/usage/base.py index 6e4de540df..cb838839d7 100644 --- a/openstack_dashboard/usage/base.py +++ b/openstack_dashboard/usage/base.py @@ -181,6 +181,9 @@ class BaseUsage(object): raise NotImplementedError("You must define a get_usage_list method.") def summarize(self, start, end): + if not api.nova.extension_supported('SimpleTenantUsage', self.request): + return + if start <= end and start <= self.today: # The API can't handle timezone aware datetime, so convert back # to naive UTC just for this last step. diff --git a/openstack_dashboard/usage/views.py b/openstack_dashboard/usage/views.py index a10bb5756d..51c29028fe 100644 --- a/openstack_dashboard/usage/views.py +++ b/openstack_dashboard/usage/views.py @@ -1,4 +1,5 @@ from horizon import tables +from openstack_dashboard import api from openstack_dashboard.usage import base @@ -35,6 +36,8 @@ class UsageView(tables.DataTableView): context['table'].kwargs['usage'] = self.usage context['form'] = self.usage.form context['usage'] = self.usage + context['simple_tenant_usage_enabled'] = \ + api.nova.extension_supported('SimpleTenantUsage', self.request) return context def render_to_response(self, context, **response_kwargs):