From b5bd7589e6fa46139d096481e01a32a4463b896c Mon Sep 17 00:00:00 2001 From: "wei.ying" Date: Sat, 30 Sep 2017 14:10:10 +0800 Subject: [PATCH] Correct the subnets quota check in admin networks panel Currently when checking the subnets quota in admin networks table, the current tenant subnets quota is checked, while the subnet is created, using the tenant of the selected network[1], this doesn't look the same. Similarly, in the network details subnets table, the creation of subnets actions lacks quota checking. [1] https://github.com/openstack/horizon/blob/master/openstack_dashboard/dashboards/admin/networks/subnets/workflows.py#L75 Change-Id: Ifb88b97168fc4f500e4bb15658f96363ddc7651f Closes-Bug:#1719606 --- .../admin/networks/subnets/tables.py | 18 +++++ .../admin/networks/subnets/tests.py | 18 ++--- .../dashboards/admin/networks/tables.py | 17 ++++ .../dashboards/admin/networks/tests.py | 80 +++++++++---------- 4 files changed, 84 insertions(+), 49 deletions(-) diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/tables.py b/openstack_dashboard/dashboards/admin/networks/subnets/tables.py index fc7c15b8a7..75275c7898 100644 --- a/openstack_dashboard/dashboards/admin/networks/subnets/tables.py +++ b/openstack_dashboard/dashboards/admin/networks/subnets/tables.py @@ -28,6 +28,7 @@ from openstack_dashboard.dashboards.project.networks.subnets \ import tables as proj_tables from openstack_dashboard.dashboards.project.networks.subnets.tabs \ import SubnetsTab as project_tabs_subnets_tab +from openstack_dashboard.usage import quotas LOG = logging.getLogger(__name__) @@ -76,6 +77,23 @@ class CreateSubnet(proj_tables.SubnetPolicyTargetMixin, tables.LinkAction): network_id = self.table.kwargs['network_id'] return reverse(self.url, args=(network_id,)) + def allowed(self, request, datum=None): + network = self.table._get_network() + usages = quotas.tenant_quota_usages( + request, tenant_id=network.tenant_id, targets=('subnets', )) + + # when Settings.OPENSTACK_NEUTRON_NETWORK['enable_quotas'] = False + # usages["subnets'] is empty + if usages.get('subnets', {}).get('available', 1) <= 0: + if 'disabled' not in self.classes: + self.classes = [c for c in self.classes] + ['disabled'] + self.verbose_name = _('Create Subnet (Quota exceeded)') + else: + self.verbose_name = _('Create Subnet') + self.classes = [c for c in self.classes if c != 'disabled'] + + return True + class UpdateSubnet(proj_tables.SubnetPolicyTargetMixin, tables.LinkAction): name = "update" diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/tests.py b/openstack_dashboard/dashboards/admin/networks/subnets/tests.py index a16f4e9f19..28b1816f08 100644 --- a/openstack_dashboard/dashboards/admin/networks/subnets/tests.py +++ b/openstack_dashboard/dashboards/admin/networks/subnets/tests.py @@ -409,18 +409,18 @@ class NetworkSubnetTests(test.BaseAdminViewTests): def _test_network_detail_ip_availability_exception(self, mac_learning=False): - network_id = self.networks.first().id - quota_data = self.quota_usages.first() + network = self.networks.first() + quota_data = self.neutron_quota_usages.first() api.neutron.is_extension_supported( IsA(http.HttpRequest), 'network-ip-availability').AndReturn(True) api.neutron.show_network_ip_availability(IsA(http.HttpRequest), - network_id).\ + network.id).\ MultipleTimes().AndRaise(self.exceptions.neutron) - api.neutron.network_get(IsA(http.HttpRequest), network_id).\ - AndReturn(self.networks.first()) + api.neutron.network_get(IsA(http.HttpRequest), network.id).\ + MultipleTimes().AndReturn(self.networks.first()) - api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id).\ + api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network.id).\ AndReturn([self.subnets.first()]) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'mac-learning') \ @@ -432,12 +432,12 @@ class NetworkSubnetTests(test.BaseAdminViewTests): 'dhcp_agent_scheduler')\ .MultipleTimes().AndReturn(True) quotas.tenant_quota_usages( - IsA(http.HttpRequest), targets=('subnets',)) \ - .MultipleTimes().AndReturn(quota_data) + IsA(http.HttpRequest), tenant_id=network.tenant_id, + targets=('subnets',)).MultipleTimes().AndReturn(quota_data) self.mox.ReplayAll() from django.utils.http import urlunquote url = urlunquote(reverse('horizon:admin:networks:subnets_tab', - args=[network_id])) + args=[network.id])) res = self.client.get(url) self.assertTemplateUsed(res, 'horizon/common/_detail.html') subnets = res.context['subnets_table'].data diff --git a/openstack_dashboard/dashboards/admin/networks/tables.py b/openstack_dashboard/dashboards/admin/networks/tables.py index b0a649b394..50f15051bd 100644 --- a/openstack_dashboard/dashboards/admin/networks/tables.py +++ b/openstack_dashboard/dashboards/admin/networks/tables.py @@ -27,6 +27,7 @@ from openstack_dashboard import api from openstack_dashboard.dashboards.project.networks \ import tables as project_tables from openstack_dashboard import policy +from openstack_dashboard.usage import quotas LOG = logging.getLogger(__name__) @@ -82,6 +83,22 @@ class EditNetwork(policy.PolicyTargetMixin, tables.LinkAction): class CreateSubnet(project_tables.CreateSubnet): url = "horizon:admin:networks:createsubnet" + def allowed(self, request, datum=None): + usages = quotas.tenant_quota_usages( + request, tenant_id=datum.tenant_id, targets=('subnets', )) + + # when Settings.OPENSTACK_NEUTRON_NETWORK['enable_quotas'] = False + # usages["subnets'] is empty + if usages.get('subnets', {}).get('available', 1) <= 0: + if 'disabled' not in self.classes: + self.classes = [c for c in self.classes] + ['disabled'] + self.verbose_name = _('Create Subnet (Quota exceeded)') + else: + self.verbose_name = _('Create Subnet') + self.classes = [c for c in self.classes if c != 'disabled'] + + return True + DISPLAY_CHOICES = ( ("up", pgettext_lazy("Admin state of a Network", u"UP")), diff --git a/openstack_dashboard/dashboards/admin/networks/tests.py b/openstack_dashboard/dashboards/admin/networks/tests.py index a29c7f71ad..b147e72b6c 100644 --- a/openstack_dashboard/dashboards/admin/networks/tests.py +++ b/openstack_dashboard/dashboards/admin/networks/tests.py @@ -44,6 +44,9 @@ class NetworkTests(test.BaseAdminViewTests): api.keystone.tenant_list(IsA(http.HttpRequest))\ .AndReturn([tenants, False]) for network in self.networks.list(): + usage.quotas.tenant_quota_usages( + IsA(http.HttpRequest), tenant_id=network.tenant_id, + targets=('subnets', )).AndReturn(quota_data) api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest), network.id)\ .AndReturn(self.agents.list()) @@ -54,9 +57,6 @@ class NetworkTests(test.BaseAdminViewTests): api.neutron.is_extension_supported( IsA(http.HttpRequest), 'dhcp_agent_scheduler').AndReturn(True) - usage.quotas.tenant_quota_usages( - IsA(http.HttpRequest), targets=('subnets', )) \ - .MultipleTimes().AndReturn(quota_data) self.mox.ReplayAll() res = self.client.get(INDEX_URL) @@ -106,9 +106,9 @@ class NetworkTests(test.BaseAdminViewTests): 'is_extension_supported'), usage.quotas: ('tenant_quota_usages',)}) def test_network_detail_new(self, mac_learning=False): - network_id = self.networks.first().id + network = self.networks.first() quota_data = self.quota_usages.first() - api.neutron.network_get(IsA(http.HttpRequest), network_id) \ + api.neutron.network_get(IsA(http.HttpRequest), network.id) \ .MultipleTimes().AndReturn(self.networks.first()) api.neutron.is_extension_supported(IsA(http.HttpRequest), 'network-ip-availability') \ @@ -121,11 +121,11 @@ class NetworkTests(test.BaseAdminViewTests): IsA(http.HttpRequest), 'dhcp_agent_scheduler').AndReturn(True) usage.quotas.tenant_quota_usages( - IsA(http.HttpRequest), targets=('subnets',)) \ - .MultipleTimes().AndReturn(quota_data) + IsA(http.HttpRequest), tenant_id=network.tenant_id, + targets=('subnets',)).MultipleTimes().AndReturn(quota_data) self.mox.ReplayAll() url = urlunquote(reverse('horizon:admin:networks:detail', - args=[network_id])) + args=[network.id])) res = self.client.get(url) network = res.context['network'] @@ -135,15 +135,15 @@ class NetworkTests(test.BaseAdminViewTests): self.assertTemplateUsed(res, 'horizon/common/_detail.html') def _test_network_detail_subnets_tab(self, mac_learning=False): - network_id = self.networks.first().id + network = self.networks.first() ip_availability = self.ip_availability.get() quota_data = self.quota_usages.first() api.neutron.show_network_ip_availability(IsA(http.HttpRequest), - network_id).\ + network.id).\ MultipleTimes().AndReturn(ip_availability) - api.neutron.network_get(IsA(http.HttpRequest), network_id)\ - .AndReturn(self.networks.first()) - api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id)\ + api.neutron.network_get(IsA(http.HttpRequest), network.id)\ + .MultipleTimes().AndReturn(self.networks.first()) + api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network.id)\ .AndReturn([self.subnets.first()]) api.neutron.is_extension_supported( IsA(http.HttpRequest), @@ -159,11 +159,11 @@ class NetworkTests(test.BaseAdminViewTests): IsA(http.HttpRequest), 'dhcp_agent_scheduler').AndReturn(True) usage.quotas.tenant_quota_usages( - IsA(http.HttpRequest), targets=('subnets',)) \ - .MultipleTimes().AndReturn(quota_data) + IsA(http.HttpRequest), tenant_id=network.tenant_id, + targets=('subnets',)).MultipleTimes().AndReturn(quota_data) self.mox.ReplayAll() url = urlunquote(reverse('horizon:admin:networks:subnets_tab', - args=[network_id])) + args=[network.id])) res = self.client.get(url) self.assertTemplateUsed(res, 'horizon/common/_detail.html') @@ -196,8 +196,8 @@ class NetworkTests(test.BaseAdminViewTests): IsA(http.HttpRequest), 'dhcp_agent_scheduler').AndReturn(True) usage.quotas.tenant_quota_usages( - IsA(http.HttpRequest), targets=('subnets',)) \ - .MultipleTimes().AndReturn(quota_data) + IsA(http.HttpRequest), tenant_id=network.tenant_id, + targets=('subnets',)).MultipleTimes().AndReturn(quota_data) self.mox.ReplayAll() url = reverse('horizon:admin:networks:ports_tab', @@ -215,7 +215,7 @@ class NetworkTests(test.BaseAdminViewTests): 'list_dhcp_agent_hosting_networks',), usage.quotas: ('tenant_quota_usages',)}) def test_network_detail_agents_tab(self, mac_learning=False): - network_id = self.networks.first().id + network = self.networks.first() quota_data = self.quota_usages.first() api.neutron.is_extension_supported(IsA(http.HttpRequest), 'network-ip-availability') \ @@ -225,10 +225,10 @@ class NetworkTests(test.BaseAdminViewTests): 'mac-learning')\ .AndReturn(mac_learning) api.neutron.list_dhcp_agent_hosting_networks(IsA(http.HttpRequest), - network_id)\ + network.id)\ .AndReturn(self.agents.list()) - api.neutron.network_get(IsA(http.HttpRequest), network_id)\ - .AndReturn(self.networks.first()) + api.neutron.network_get(IsA(http.HttpRequest), network.id)\ + .MultipleTimes().AndReturn(self.networks.first()) api.neutron.is_extension_supported( IsA(http.HttpRequest), 'dhcp_agent_scheduler').AndReturn(True) @@ -236,10 +236,10 @@ class NetworkTests(test.BaseAdminViewTests): IsA(http.HttpRequest), 'dhcp_agent_scheduler').AndReturn(True) usage.quotas.tenant_quota_usages( - IsA(http.HttpRequest), targets=('subnets', )) \ - .MultipleTimes().AndReturn(quota_data) + IsA(http.HttpRequest), tenant_id=network.tenant_id, + targets=('subnets',)).MultipleTimes().AndReturn(quota_data) self.mox.ReplayAll() - url = reverse('horizon:admin:networks:agents_tab', args=[network_id]) + url = reverse('horizon:admin:networks:agents_tab', args=[network.id]) res = self.client.get(urlunquote(url)) self.assertTemplateUsed(res, 'horizon/common/_detail.html') @@ -318,11 +318,11 @@ class NetworkTests(test.BaseAdminViewTests): def _test_network_detail_subnets_tab_subnet_exception(self, mac_learning=False): - network_id = self.networks.first().id + network = self.networks.first() quota_data = self.quota_usages.first() - api.neutron.network_get(IsA(http.HttpRequest), network_id).\ - AndReturn(self.networks.first()) - api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id).\ + api.neutron.network_get(IsA(http.HttpRequest), network.id)\ + .MultipleTimes().AndReturn(self.networks.first()) + api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network.id).\ AndRaise(self.exceptions.neutron) api.neutron.is_extension_supported( IsA(http.HttpRequest), @@ -337,11 +337,11 @@ class NetworkTests(test.BaseAdminViewTests): IsA(http.HttpRequest), 'dhcp_agent_scheduler').AndReturn(True) usage.quotas.tenant_quota_usages( - IsA(http.HttpRequest), targets=('subnets',)) \ - .MultipleTimes().AndReturn(quota_data) + IsA(http.HttpRequest), tenant_id=network.tenant_id, + targets=('subnets',)).MultipleTimes().AndReturn(quota_data) self.mox.ReplayAll() url = urlunquote(reverse('horizon:admin:networks:subnets_tab', - args=[network_id])) + args=[network.id])) res = self.client.get(url) self.assertTemplateUsed(res, 'horizon/common/_detail.html') @@ -370,15 +370,15 @@ class NetworkTests(test.BaseAdminViewTests): def _test_network_detail_subnets_tab_port_exception(self, mac_learning=False): - network_id = self.networks.first().id + network = self.networks.first() ip_availability = self.ip_availability.get() quota_data = self.quota_usages.first() api.neutron.show_network_ip_availability(IsA(http.HttpRequest), - network_id). \ + network.id). \ MultipleTimes().AndReturn(ip_availability) - api.neutron.network_get(IsA(http.HttpRequest), network_id).\ - AndReturn(self.networks.first()) - api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id).\ + api.neutron.network_get(IsA(http.HttpRequest), network.id)\ + .MultipleTimes().AndReturn(self.networks.first()) + api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network.id).\ AndReturn([self.subnets.first()]) api.neutron.is_extension_supported( IsA(http.HttpRequest), @@ -393,11 +393,11 @@ class NetworkTests(test.BaseAdminViewTests): 'dhcp_agent_scheduler')\ .AndReturn(True) usage.quotas.tenant_quota_usages( - IsA(http.HttpRequest), targets=('subnets',)) \ - .MultipleTimes().AndReturn(quota_data) + IsA(http.HttpRequest), tenant_id=network.tenant_id, + targets=('subnets',)).MultipleTimes().AndReturn(quota_data) self.mox.ReplayAll() url = urlunquote(reverse('horizon:admin:networks:subnets_tab', - args=[network_id])) + args=[network.id])) res = self.client.get(url) self.assertTemplateUsed(res, 'horizon/common/_detail.html')