From a965f42c7b4e0115d04ca9ef20b4a8e1d48f1a7e Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Mon, 15 Jan 2018 07:07:26 +0900 Subject: [PATCH] Tabbify quota default panel Part of blueprint horizon-plugin-tab-for-info-and-quotas Change-Id: I71e23835161244b2173a6444cc00d94639df2f1b --- .../dashboards/admin/defaults/tables.py | 80 +++++++++++++++---- .../dashboards/admin/defaults/tabs.py | 65 ++++++++++++--- .../dashboards/admin/defaults/tests.py | 76 ++++++++++-------- 3 files changed, 161 insertions(+), 60 deletions(-) diff --git a/openstack_dashboard/dashboards/admin/defaults/tables.py b/openstack_dashboard/dashboards/admin/defaults/tables.py index adfae7dac1..c00747b1ff 100644 --- a/openstack_dashboard/dashboards/admin/defaults/tables.py +++ b/openstack_dashboard/dashboards/admin/defaults/tables.py @@ -42,9 +42,18 @@ class UpdateDefaultQuotas(tables.LinkAction): return quotas.enabled_quotas(request) -def get_quota_name(quota): +class UpdateDefaultComputeQuotas(UpdateDefaultQuotas): + name = 'update_compute_defaults' + step = 'update_default_compute_quotas' + + +class UpdateDefaultVolumeQuotas(UpdateDefaultQuotas): + name = 'update_volume_defaults' + step = 'update_default_volume_quotas' + + +def get_compute_quota_name(quota): QUOTA_NAMES = { - # Nova 'injected_file_content_bytes': _('Injected File Content Bytes'), 'injected_file_path_bytes': _('Length of Injected File Path'), 'metadata_items': _('Metadata Items'), @@ -55,7 +64,26 @@ def get_quota_name(quota): 'key_pairs': _('Key Pairs'), 'server_group_members': _('Server Group Members'), 'server_groups': _('Server Groups'), - # Cinder + } + return QUOTA_NAMES.get(quota.name, quota.name.replace("_", " ").title()) + + +class ComputeQuotasTable(tables.DataTable): + name = tables.Column(get_compute_quota_name, verbose_name=_('Quota Name')) + limit = tables.Column("limit", verbose_name=_('Limit')) + + def get_object_id(self, obj): + return obj.name + + class Meta(object): + name = "compute_quotas" + verbose_name = _("Compute Quotas") + table_actions = (QuotaFilterAction, UpdateDefaultComputeQuotas) + multi_select = False + + +def get_volume_quota_name(quota): + QUOTA_NAMES = { 'volumes': _('Volumes'), 'snapshots': _('Volume Snapshots'), 'backups': _('Backups'), @@ -64,14 +92,6 @@ def get_quota_name(quota): 'per_volume_gigabytes': _('Per Volume Size (GiB)'), 'groups': _('Volume Groups'), 'dm-crypt': _('dm-crypt'), - # Neutron - 'network': _('Networks'), - 'subnet': _('Subnets'), - 'port': _('Ports'), - 'router': _('Routers'), - 'floatingip': _('Floating IPs'), - 'security_group': _('Security Groups'), - 'security_group_rule': _('Security Group Rules'), } QUOTA_DYNAMIC_NAMES = { @@ -89,15 +109,45 @@ def get_quota_name(quota): return QUOTA_NAMES.get(quota.name, quota.name.replace("_", " ").title()) -class QuotasTable(tables.DataTable): - name = tables.Column(get_quota_name, verbose_name=_('Quota Name')) +class VolumeQuotasTable(tables.DataTable): + name = tables.Column(get_volume_quota_name, verbose_name=_('Quota Name')) limit = tables.Column("limit", verbose_name=_('Limit')) def get_object_id(self, obj): return obj.name class Meta(object): - name = "quotas" - verbose_name = _("Quotas") + name = "volume_quotas" + verbose_name = _("Volume Quotas") table_actions = (QuotaFilterAction, UpdateDefaultQuotas) multi_select = False + + +def get_network_quota_name(quota): + QUOTA_NAMES = { + 'network': _('Networks'), + 'subnet': _('Subnets'), + 'port': _('Ports'), + 'subnetpool': _('Subnet Pool'), + 'router': _('Routers'), + 'floatingip': _('Floating IPs'), + 'security_group': _('Security Groups'), + 'security_group_rule': _('Security Group Rules'), + 'rbac_policy': _('RBAC Policies'), + 'trunk': _('Trunks'), + } + return QUOTA_NAMES.get(quota.name, quota.name.replace("_", " ").title()) + + +class NetworkQuotasTable(tables.DataTable): + name = tables.Column(get_network_quota_name, verbose_name=_('Quota Name')) + limit = tables.Column("limit", verbose_name=_('Limit')) + + def get_object_id(self, obj): + return obj.name + + class Meta(object): + name = "network_quotas" + verbose_name = _("Network Quotas") + table_actions = (QuotaFilterAction,) + multi_select = False diff --git a/openstack_dashboard/dashboards/admin/defaults/tabs.py b/openstack_dashboard/dashboards/admin/defaults/tabs.py index 832a09a0b4..dabb91424a 100644 --- a/openstack_dashboard/dashboards/admin/defaults/tabs.py +++ b/openstack_dashboard/dashboards/admin/defaults/tabs.py @@ -17,28 +17,73 @@ from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import tabs -from openstack_dashboard.usage import quotas - +from openstack_dashboard import api from openstack_dashboard.dashboards.admin.defaults import tables -class DefaultQuotasTab(tabs.TableTab): - table_classes = (tables.QuotasTable,) - name = _("Default Quotas") - slug = "quotas" +class ComputeQuotasTab(tabs.TableTab): + table_classes = (tables.ComputeQuotasTable,) + name = _("Compute Quotas") + slug = "compute_quotas" template_name = ("horizon/common/_detail_table.html") - def get_quotas_data(self): + def get_compute_quotas_data(self): request = self.tab_group.request + tenant_id = request.user.tenant_id try: - data = quotas.get_default_quota_data(request) + data = api.nova.default_quota_get(request, tenant_id) except Exception: data = [] - exceptions.handle(self.request, _('Unable to get quota info.')) + exceptions.handle(self.request, + _('Unable to get nova quota info.')) return data + def allowed(self, request): + return api.base.is_service_enabled(request, 'compute') + + +class VolumeQuotasTab(tabs.TableTab): + table_classes = (tables.VolumeQuotasTable,) + name = _("Volume Quotas") + slug = "volume_quotas" + template_name = ("horizon/common/_detail_table.html") + + def get_volume_quotas_data(self): + request = self.tab_group.request + tenant_id = request.user.tenant_id + try: + data = api.cinder.default_quota_get(request, tenant_id) + except Exception: + data = [] + exceptions.handle(self.request, + _('Unable to get cinder quota info.')) + return data + + def allowed(self, request): + return api.cinder.is_volume_service_enabled(request) + + +class NetworkQuotasTab(tabs.TableTab): + table_classes = (tables.NetworkQuotasTable,) + name = _("Network Quotas") + slug = "network_quotas" + template_name = ("horizon/common/_detail_table.html") + + def get_network_quotas_data(self): + request = self.tab_group.request + try: + data = api.neutron.default_quota_get(request) + except Exception: + data = [] + exceptions.handle(self.request, + _('Unable to get neutron quota info.')) + return data + + def allowed(self, request): + return api.base.is_service_enabled(request, 'network') + class DefaultsTabs(tabs.TabGroup): slug = "defaults" - tabs = (DefaultQuotasTab,) + tabs = (ComputeQuotasTab, VolumeQuotasTab, NetworkQuotasTab) sticky = True diff --git a/openstack_dashboard/dashboards/admin/defaults/tests.py b/openstack_dashboard/dashboards/admin/defaults/tests.py index 1175540579..d269252d3e 100644 --- a/openstack_dashboard/dashboards/admin/defaults/tests.py +++ b/openstack_dashboard/dashboards/admin/defaults/tests.py @@ -25,16 +25,6 @@ INDEX_URL = reverse('horizon:admin:defaults:index') class ServicesViewTests(test.BaseAdminViewTests): def test_index(self): - self._test_index(neutron_enabled=True) - - def test_index_with_neutron_disabled(self): - self._test_index(neutron_enabled=False) - - def test_index_with_neutron_sg_disabled(self): - self._test_index(neutron_enabled=True, - neutron_sg_enabled=False) - - def _test_index(self, neutron_enabled=True, neutron_sg_enabled=True): # Neutron does not have an API for getting default system # quotas. When not using Neutron, the floating ips quotas # should be in the list. @@ -42,27 +32,24 @@ class ServicesViewTests(test.BaseAdminViewTests): self.mox.StubOutWithMock(api.cinder, 'default_quota_get') self.mox.StubOutWithMock(api.cinder, 'is_volume_service_enabled') self.mox.StubOutWithMock(api.base, 'is_service_enabled') - if neutron_enabled: - self.mox.StubOutWithMock(api.neutron, 'is_extension_supported') - self.mox.StubOutWithMock(api.neutron, 'is_router_enabled') + self.mox.StubOutWithMock(api.neutron, 'default_quota_get') + self.mox.StubOutWithMock(quotas, 'enabled_quotas') api.cinder.is_volume_service_enabled(IsA(http.HttpRequest)) \ .MultipleTimes().AndReturn(True) api.base.is_service_enabled(IsA(http.HttpRequest), 'compute') \ .MultipleTimes().AndReturn(True) api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \ - .MultipleTimes().AndReturn(neutron_enabled) - + .MultipleTimes().AndReturn(True) + compute_quotas = [q.name for q in self.quotas.nova] + quotas.enabled_quotas(IsA(http.HttpRequest)) \ + .MultipleTimes().AndReturn(compute_quotas) api.nova.default_quota_get(IsA(http.HttpRequest), self.tenant.id).AndReturn(self.quotas.nova) api.cinder.default_quota_get(IsA(http.HttpRequest), self.tenant.id) \ .AndReturn(self.cinder_quotas.first()) - if neutron_enabled: - api.neutron.is_extension_supported( - IsA(http.HttpRequest), - 'security-group').MultipleTimes().AndReturn(neutron_sg_enabled) - api.neutron.is_router_enabled( - IsA(http.HttpRequest)).MultipleTimes().AndReturn(True) + api.neutron.default_quota_get( + IsA(http.HttpRequest)).AndReturn(self.neutron_quotas.first()) self.mox.ReplayAll() @@ -70,21 +57,40 @@ class ServicesViewTests(test.BaseAdminViewTests): self.assertTemplateUsed(res, 'admin/defaults/index.html') - quotas_tab = res.context['tab_group'].get_tab('quotas') - expected_tabs = ['', - '', - '', - '', - '', - '', - '', - '', - '', - '', - ''] + expected_data = [ + '', + '', + '', + '', + '', + '', + '', + '', + ] + self._check_quotas_data(res, 'compute_quotas', expected_data) - self.assertQuerysetEqual(quotas_tab._tables['quotas'].data, - expected_tabs, + expected_data = [ + '', + '', + '', + ] + self._check_quotas_data(res, 'volume_quotas', expected_data) + + expected_data = [ + '', + '', + '', + '', + '', + '', + '', + ] + self._check_quotas_data(res, 'network_quotas', expected_data) + + def _check_quotas_data(self, res, slug, expected_data): + quotas_tab = res.context['tab_group'].get_tab(slug) + self.assertQuerysetEqual(quotas_tab._tables[slug].data, + expected_data, ordered=False)