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
This commit is contained in:
@@ -17,6 +17,9 @@
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "horizon/common/_usage_summary.html" %}
|
||||
{{ table.render }}
|
||||
|
||||
{% if simple_tenant_usage_enabled %}
|
||||
{% include "horizon/common/_usage_summary.html" %}
|
||||
{{ table.render }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@@ -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,
|
||||
'<td class="sortable normal_column">test_tenant'
|
||||
'</td>'
|
||||
'<td class="sortable normal_column">%s</td>'
|
||||
'<td class="sortable normal_column">%s</td>'
|
||||
'<td class="sortable normal_column">%s</td>'
|
||||
'<td class="sortable normal_column">%.2f</td>'
|
||||
'<td class="sortable normal_column">%.2f</td>' %
|
||||
(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 = '<td class="sortable normal_column">test_tenant</td>' \
|
||||
'<td class="sortable normal_column">%s</td>' \
|
||||
'<td class="sortable normal_column">%s</td>' \
|
||||
'<td class="sortable normal_column">%s</td>' \
|
||||
'<td class="sortable normal_column">%.2f</td>' \
|
||||
'<td class="sortable normal_column">%.2f</td>' % \
|
||||
(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)
|
||||
|
@@ -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 %}
|
||||
|
@@ -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 %}
|
||||
|
@@ -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)
|
||||
|
@@ -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.
|
||||
|
@@ -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):
|
||||
|
Reference in New Issue
Block a user