diff options
author | Mike Hagedorn <mike.hagedorn@hp.com> | 2015-08-06 15:43:16 -0400 |
---|---|---|
committer | Matthias Runge <mrunge@redhat.com> | 2015-09-28 11:32:56 +0200 |
commit | b8e1ce89041dfe6547e875571eb987dd9eae3a03 (patch) | |
tree | 2c0ec7ca85ee941eeaf46bd2eb510d86a8b1ad57 | |
parent | b55acddc1590095973f7814eb9d06ea7d5b784f7 (diff) | |
download | horizon-b8e1ce89041dfe6547e875571eb987dd9eae3a03.tar.gz |
Neutron Quota Settings Flag Disables Neutron GUI
if OPENSTACK_NEUTRON_NETWORK["enable_quotas"] = False buttons will not appear
and exceptions are thrown. This is because a dict is searched for a key
("available") which wont be there if its disabled. This generates a
KeyError at runtime.
As currently written enable_quatas -> False essentially disables Neutron usage.
This is not a desirable outcome.
This patch shows Neutron related buttons and ignores the quota settings,
which seems more in line with the notion of disabling quotas.
Closes-Bug:#1482354
Change-Id: I9485ddeece74d87317c2587498f5f79d4ffdb66e
(cherry picked from commit 5ba219acbfddb9b9308a75da361fda915ba61f37)
6 files changed, 141 insertions, 33 deletions
diff --git a/openstack_dashboard/dashboards/project/network_topology/views.py b/openstack_dashboard/dashboards/project/network_topology/views.py index 23b50b8c5..36327638c 100644 --- a/openstack_dashboard/dashboards/project/network_topology/views.py +++ b/openstack_dashboard/dashboards/project/network_topology/views.py @@ -111,7 +111,7 @@ class NetworkTopologyView(views.HorizonTemplateView): def _quota_exceeded(self, quota): usages = quotas.tenant_quota_usages(self.request) - available = usages[quota]['available'] + available = usages.get(quota, {}).get('available', 1) return available <= 0 def get_context_data(self, **kwargs): diff --git a/openstack_dashboard/dashboards/project/networks/tables.py b/openstack_dashboard/dashboards/project/networks/tables.py index e13e3c452..637d0e9d1 100644 --- a/openstack_dashboard/dashboards/project/networks/tables.py +++ b/openstack_dashboard/dashboards/project/networks/tables.py @@ -92,7 +92,9 @@ class CreateNetwork(tables.LinkAction): def allowed(self, request, datum=None): usages = quotas.tenant_quota_usages(request) - if usages['networks']['available'] <= 0: + # when Settings.OPENSTACK_NEUTRON_NETWORK['enable_quotas'] = False + # usages["networks"] is empty + if usages.get('networks', {}).get('available', 1) <= 0: if "disabled" not in self.classes: self.classes = [c for c in self.classes] + ["disabled"] self.verbose_name = _("Create Network (Quota exceeded)") @@ -125,7 +127,9 @@ class CreateSubnet(policy.PolicyTargetMixin, CheckNetworkEditable, def allowed(self, request, datum=None): usages = quotas.tenant_quota_usages(request) - if usages['subnets']['available'] <= 0: + # 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 = _('Add Subnet (Quota exceeded)') diff --git a/openstack_dashboard/dashboards/project/networks/tests.py b/openstack_dashboard/dashboards/project/networks/tests.py index c56eebcd8..39bc86496 100644 --- a/openstack_dashboard/dashboards/project/networks/tests.py +++ b/openstack_dashboard/dashboards/project/networks/tests.py @@ -29,6 +29,7 @@ from openstack_dashboard.dashboards.project.networks import workflows from openstack_dashboard.test import helpers as test from openstack_dashboard.usage import quotas + INDEX_URL = reverse('horizon:project:networks:index') @@ -125,9 +126,7 @@ class NetworkTests(test.TestCase): @test.create_stubs({api.neutron: ('network_list',), quotas: ('tenant_quota_usages',)}) def test_index_network_list_exception(self): - quota_data = self.quota_usages.first() - quota_data['networks']['available'] = 5 - quota_data['subnets']['available'] = 5 + quota_data = self.neutron_quota_usages.first() api.neutron.network_list( IsA(http.HttpRequest), tenant_id=self.tenant.id, @@ -160,8 +159,7 @@ class NetworkTests(test.TestCase): self._test_network_detail(mac_learning=True) def _test_network_detail(self, mac_learning=False): - quota_data = self.quota_usages.first() - quota_data['subnets']['available'] = 5 + quota_data = self.neutron_quota_usages.first() network_id = self.networks.first().id api.neutron.network_get(IsA(http.HttpRequest), network_id)\ .AndReturn(self.networks.first()) @@ -235,7 +233,7 @@ class NetworkTests(test.TestCase): def _test_network_detail_subnet_exception(self, mac_learning=False): network_id = self.networks.first().id - quota_data = self.quota_usages.first() + quota_data = self.neutron_quota_usages.first() quota_data['networks']['available'] = 5 quota_data['subnets']['available'] = 5 api.neutron.network_get(IsA(http.HttpRequest), network_id).\ @@ -280,7 +278,7 @@ class NetworkTests(test.TestCase): def _test_network_detail_port_exception(self, mac_learning=False): network_id = self.networks.first().id - quota_data = self.quota_usages.first() + quota_data = self.neutron_quota_usages.first() quota_data['subnets']['available'] = 5 api.neutron.network_get(IsA(http.HttpRequest), network_id).\ AndReturn(self.networks.first()) @@ -1659,9 +1657,41 @@ class NetworkPortTests(test.TestCase): class NetworkViewTests(test.TestCase): + def _test_create_button_shown_when_quota_disabled( + self, expected_string): + # if quota_data doesnt contain a networks|subnets|routers key or + # these keys are empty dicts, its disabled + quota_data = self.neutron_quota_usages.first() + + quota_data['networks'].pop('available') + quota_data['subnets'].pop('available') + + api.neutron.network_list( + IsA(http.HttpRequest), + tenant_id=self.tenant.id, + shared=False).AndReturn(self.networks.list()) + api.neutron.network_list( + IsA(http.HttpRequest), + shared=True).AndReturn([]) + quotas.tenant_quota_usages( + IsA(http.HttpRequest)) \ + .MultipleTimes().AndReturn(quota_data) + + self.mox.ReplayAll() + + res = self.client.get(INDEX_URL) + self.assertTemplateUsed(res, 'project/networks/index.html') + + networks = res.context['networks_table'].data + self.assertItemsEqual(networks, self.networks.list()) + self.assertContains(res, expected_string, True, html=True, + msg_prefix="The enabled create button not shown") + def _test_create_button_disabled_when_quota_exceeded( self, expected_string, network_quota=5, subnet_quota=5): - quota_data = self.quota_usages.first() + + quota_data = self.neutron_quota_usages.first() + quota_data['networks']['available'] = network_quota quota_data['subnets']['available'] = subnet_quota @@ -1683,8 +1713,7 @@ class NetworkViewTests(test.TestCase): networks = res.context['networks_table'].data self.assertItemsEqual(networks, self.networks.list()) - - self.assertContains(res, expected_string, html=True, + self.assertContains(res, expected_string, True, html=True, msg_prefix="The create button is not disabled") @test.create_stubs({api.neutron: ('network_list',), @@ -1700,27 +1729,56 @@ class NetworkViewTests(test.TestCase): "id='networks__action_create'>" \ "<span class='fa fa-plus'></span>%s</a>" \ % (url, link_name, " ".join(classes), link_name) - self._test_create_button_disabled_when_quota_exceeded(expected_string, - network_quota=0) + network_quota=0 + ) @test.create_stubs({api.neutron: ('network_list',), quotas: ('tenant_quota_usages',)}) def test_subnet_create_button_disabled_when_quota_exceeded_index(self): network_id = self.networks.first().id - create_link = networks_tables.CreateSubnet() url = reverse(create_link.get_link_url(), args=[network_id]) classes = (list(create_link.get_default_classes()) + list(create_link.classes)) link_name = "%s (%s)" % (unicode(create_link.verbose_name), "Quota exceeded") - expected_string = "<a href='%s' class='%s disabled' "\ + expected_string = "<a href='%s' class='%s disabled' " \ + "id='networks__row_%s__action_subnet'>%s</a>" \ + % (url, " ".join(classes), network_id, link_name) + self._test_create_button_disabled_when_quota_exceeded(expected_string, + subnet_quota=0 + ) + + @test.create_stubs({api.neutron: ('network_list',), + quotas: ('tenant_quota_usages',)}) + def test_network_create_button_shown_when_quota_disabled_index(self): + # if quota_data doesnt contain a networks["available"] key its disabled + create_link = networks_tables.CreateNetwork() + url = create_link.get_link_url() + classes = (list(create_link.get_default_classes()) + + list(create_link.classes)) + link_name = "%s" % (unicode(create_link.verbose_name),) + expected_string = "<a href='%s' title='%s' class='%s' "\ + "id='networks__action_create'>" \ + "<span class='fa fa-plus'></span>%s</a>" \ + % (url, link_name, " ".join(classes), link_name) + self._test_create_button_shown_when_quota_disabled(expected_string) + + @test.create_stubs({api.neutron: ('network_list',), + quotas: ('tenant_quota_usages',)}) + def test_subnet_create_button_shown_when_quota_disabled_index(self): + # if quota_data doesnt contain a subnets["available"] key, its disabled + network_id = self.networks.first().id + create_link = networks_tables.CreateSubnet() + url = reverse(create_link.get_link_url(), args=[network_id]) + classes = (list(create_link.get_default_classes()) + + list(create_link.classes)) + link_name = "%s" % (unicode(create_link.verbose_name),) + expected_string = "<a href='%s' class='%s' "\ "id='networks__row_%s__action_subnet'>%s</a>" \ % (url, " ".join(classes), network_id, link_name) - - self._test_create_button_disabled_when_quota_exceeded(expected_string, - subnet_quota=0) + self._test_create_button_shown_when_quota_disabled(expected_string) @test.create_stubs({api.neutron: ('network_get', 'subnet_list', @@ -1729,7 +1787,7 @@ class NetworkViewTests(test.TestCase): quotas: ('tenant_quota_usages',)}) def test_subnet_create_button_disabled_when_quota_exceeded_detail(self): network_id = self.networks.first().id - quota_data = self.quota_usages.first() + quota_data = self.neutron_quota_usages.first() quota_data['subnets']['available'] = 0 api.neutron.network_get( diff --git a/openstack_dashboard/dashboards/project/routers/tables.py b/openstack_dashboard/dashboards/project/routers/tables.py index 3ae157cd4..351abe43d 100644 --- a/openstack_dashboard/dashboards/project/routers/tables.py +++ b/openstack_dashboard/dashboards/project/routers/tables.py @@ -90,7 +90,9 @@ class CreateRouter(tables.LinkAction): def allowed(self, request, datum=None): usages = quotas.tenant_quota_usages(request) - if usages['routers']['available'] <= 0: + # when Settings.OPENSTACK_NEUTRON_NETWORK['enable_quotas'] = False + # usages['routers'] is empty + if usages.get('routers', {}).get('available', 1) <= 0: if "disabled" not in self.classes: self.classes = [c for c in self.classes] + ["disabled"] self.verbose_name = _("Create Router (Quota exceeded)") diff --git a/openstack_dashboard/dashboards/project/routers/tests.py b/openstack_dashboard/dashboards/project/routers/tests.py index 586da2756..95687c080 100644 --- a/openstack_dashboard/dashboards/project/routers/tests.py +++ b/openstack_dashboard/dashboards/project/routers/tests.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. import copy +import six from django.core.urlresolvers import reverse from django import http @@ -81,8 +82,7 @@ class RouterTests(RouterMixin, test.TestCase): @test.create_stubs({api.neutron: ('router_list', 'network_list'), quotas: ('tenant_quota_usages',)}) def test_index(self): - quota_data = self.quota_usages.first() - quota_data['routers']['available'] = 5 + quota_data = self.neutron_quota_usages.first() api.neutron.router_list( IsA(http.HttpRequest), tenant_id=self.tenant.id, @@ -102,8 +102,7 @@ class RouterTests(RouterMixin, test.TestCase): @test.create_stubs({api.neutron: ('router_list', 'network_list'), quotas: ('tenant_quota_usages',)}) def test_index_router_list_exception(self): - quota_data = self.quota_usages.first() - quota_data['routers']['available'] = 5 + quota_data = self.neutron_quota_usages.first() api.neutron.router_list( IsA(http.HttpRequest), tenant_id=self.tenant.id, @@ -124,8 +123,7 @@ class RouterTests(RouterMixin, test.TestCase): quotas: ('tenant_quota_usages',)}) def test_set_external_network_empty(self): router = self.routers.first() - quota_data = self.quota_usages.first() - quota_data['routers']['available'] = 5 + quota_data = self.neutron_quota_usages.first() api.neutron.router_list( IsA(http.HttpRequest), tenant_id=self.tenant.id, @@ -170,8 +168,7 @@ class RouterTests(RouterMixin, test.TestCase): quotas: ('tenant_quota_usages',)}) def test_router_delete(self): router = self.routers.first() - quota_data = self.quota_usages.first() - quota_data['routers']['available'] = 5 + quota_data = self.neutron_quota_usages.first() api.neutron.router_list( IsA(http.HttpRequest), tenant_id=self.tenant.id, @@ -211,8 +208,7 @@ class RouterTests(RouterMixin, test.TestCase): def test_router_with_interface_delete(self): router = self.routers.first() ports = self.ports.list() - quota_data = self.quota_usages.first() - quota_data['routers']['available'] = 5 + quota_data = self.neutron_quota_usages.first() api.neutron.router_list( IsA(http.HttpRequest), tenant_id=self.tenant.id, @@ -819,7 +815,7 @@ class RouterViewTests(RouterMixin, test.TestCase): @test.create_stubs({api.neutron: ('router_list', 'network_list'), quotas: ('tenant_quota_usages',)}) def test_create_button_disabled_when_quota_exceeded(self): - quota_data = self.quota_usages.first() + quota_data = self.neutron_quota_usages.first() quota_data['routers']['available'] = 0 api.neutron.router_list( IsA(http.HttpRequest), @@ -850,3 +846,37 @@ class RouterViewTests(RouterMixin, test.TestCase): % (url, link_name, " ".join(classes), link_name) self.assertContains(res, expected_string, html=True, msg_prefix="The create button is not disabled") + + @test.create_stubs({api.neutron: ('router_list', 'network_list'), + quotas: ('tenant_quota_usages',)}) + def test_create_button_shown_when_quota_disabled(self): + quota_data = self.neutron_quota_usages.first() + quota_data['routers'].pop('available') + api.neutron.router_list( + IsA(http.HttpRequest), + tenant_id=self.tenant.id, + search_opts=None).AndReturn(self.routers.list()) + quotas.tenant_quota_usages( + IsA(http.HttpRequest)) \ + .MultipleTimes().AndReturn(quota_data) + + self._mock_external_network_list() + self.mox.ReplayAll() + + res = self.client.get(self.INDEX_URL) + self.assertTemplateUsed(res, 'project/routers/index.html') + + routers = res.context['Routers_table'].data + self.assertItemsEqual(routers, self.routers.list()) + + create_link = tables.CreateRouter() + url = create_link.get_link_url() + classes = (list(create_link.get_default_classes()) + + list(create_link.classes)) + link_name = "%s" % (six.text_type(create_link.verbose_name)) + expected_string = "<a href='%s' title='%s' class='%s' "\ + "id='Routers__action_create'>" \ + "<span class='fa fa-plus'></span>%s</a>" \ + % (url, link_name, " ".join(classes), link_name) + self.assertContains(res, expected_string, html=True, + msg_prefix="The create button is not displayed") diff --git a/openstack_dashboard/test/test_data/neutron_data.py b/openstack_dashboard/test/test_data/neutron_data.py index 0bc646e60..0bea29497 100644 --- a/openstack_dashboard/test/test_data/neutron_data.py +++ b/openstack_dashboard/test/test_data/neutron_data.py @@ -21,6 +21,7 @@ from openstack_dashboard.api import lbaas from openstack_dashboard.api import neutron from openstack_dashboard.api import vpn from openstack_dashboard.test.test_data import utils +from openstack_dashboard.usage import quotas as usage_quotas def data(TEST): @@ -40,6 +41,7 @@ def data(TEST): TEST.members = utils.TestDataContainer() TEST.monitors = utils.TestDataContainer() TEST.neutron_quotas = utils.TestDataContainer() + TEST.neutron_quota_usages = utils.TestDataContainer() TEST.net_profiles = utils.TestDataContainer() TEST.policy_profiles = utils.TestDataContainer() TEST.network_profile_binding = utils.TestDataContainer() @@ -652,6 +654,18 @@ def data(TEST): } TEST.neutron_quotas.add(base.QuotaSet(quota_data)) + # Quota Usages + quota_usage_data = {'networks': {'used': 0, 'quota': 5}, + 'subnets': {'used': 0, 'quota': 5}, + 'routers': {'used': 0, 'quota': 5}, + } + quota_usage = usage_quotas.QuotaUsage() + for k, v in quota_usage_data.items(): + quota_usage.add_quota(base.Quota(k, v['quota'])) + quota_usage.tally(k, v['used']) + + TEST.neutron_quota_usages.add(quota_usage) + # Extensions. extension_1 = {"name": "security-group", "alias": "security-group", |