From f5a2ee300d109ac9d403ee1f39d6e056ac925133 Mon Sep 17 00:00:00 2001 From: Abhishek Raut Date: Thu, 7 Apr 2016 13:59:15 -0700 Subject: [PATCH] Add API to retrieve default quotas Currently there is no support to retrieve default quotas set for all projects. This patch adds a new API function to get default quotas. GET /v2.0/quotas//default DocImpact: Document new API to used to retrieve default quotas APIImpact: New Read-only API to retrieve default quotas Change-Id: If40a44348e305da444acd6196d2e0c04202b8f7a Closes-Bug: #1204956 --- neutron/db/quota/driver.py | 16 ++++++++ neutron/extensions/quotasv2.py | 13 +++++++ neutron/tests/unit/db/quota/test_driver.py | 7 ++++ .../tests/unit/extensions/test_quotasv2.py | 39 +++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/neutron/db/quota/driver.py b/neutron/db/quota/driver.py index fba93406a71..9200e1a7657 100644 --- a/neutron/db/quota/driver.py +++ b/neutron/db/quota/driver.py @@ -33,6 +33,22 @@ class DbQuotaDriver(object): The default driver utilizes the local database. """ + @staticmethod + def get_default_quotas(context, resources, tenant_id): + """Given a list of resources, retrieve the default quotas set for + a tenant. + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resource keys. + :param tenant_id: The ID of the tenant to return default quotas for. + :return dict: from resource name to dict of name and limit + """ + # Currently the tenant_id parameter is unused, since all tenants + # share the same default values. This may change in the future so + # we include tenant-id to remain backwards compatible. + return dict((key, resource.default) + for key, resource in resources.items()) + @staticmethod def get_tenant_quotas(context, resources, tenant_id): """Given a list of resources, retrieve the quotas for the given diff --git a/neutron/extensions/quotasv2.py b/neutron/extensions/quotasv2.py index 99197c6e6e8..11ff8520c9f 100644 --- a/neutron/extensions/quotasv2.py +++ b/neutron/extensions/quotasv2.py @@ -31,6 +31,8 @@ from neutron import quota from neutron.quota import resource_registry from neutron import wsgi + +DEFAULT_QUOTAS_ACTION = 'default' RESOURCE_NAME = 'quota' RESOURCE_COLLECTION = RESOURCE_NAME + "s" QUOTAS = quota.QUOTAS @@ -67,6 +69,16 @@ class QuotaSetsController(wsgi.Controller): resource_registry.get_all_resources(), tenant_id) + def default(self, request, id): + if id != request.context.tenant_id: + self._check_admin(request.context, + reason=_("Only admin is authorized " + "to access quotas for another tenant")) + return {self._resource_name: self._driver.get_default_quotas( + context=request.context, + resources=resource_registry.get_all_resources(), + tenant_id=id)} + def create(self, request, body=None): msg = _('POST requests are not supported on this resource.') raise webob.exc.HTTPNotImplemented(msg) @@ -144,6 +156,7 @@ class Quotasv2(extensions.ExtensionDescriptor): return [extensions.ResourceExtension( Quotasv2.get_alias(), controller, + member_actions={DEFAULT_QUOTAS_ACTION: 'GET'}, collection_actions={'tenant': 'GET'})] @classmethod diff --git a/neutron/tests/unit/db/quota/test_driver.py b/neutron/tests/unit/db/quota/test_driver.py index 030f1b19ff8..6f4041d4b36 100644 --- a/neutron/tests/unit/db/quota/test_driver.py +++ b/neutron/tests/unit/db/quota/test_driver.py @@ -76,6 +76,13 @@ class TestDbQuotaDriver(testlib_api.SqlTestCase): quotas = self.plugin.get_tenant_quotas(self.context, defaults, PROJECT) self.assertEqual(4, quotas[RESOURCE]) + def test_get_default_quotas(self): + defaults = {RESOURCE: TestResource(RESOURCE, 4)} + user_ctx = context.Context(user_id=PROJECT, tenant_id=PROJECT) + self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) + quotas = self.plugin.get_default_quotas(user_ctx, defaults, PROJECT) + self.assertEqual(4, quotas[RESOURCE]) + def test_get_tenant_quotas(self): user_ctx = context.Context(user_id=PROJECT, tenant_id=PROJECT) self.plugin.update_quota_limit(self.context, PROJECT, RESOURCE, 2) diff --git a/neutron/tests/unit/extensions/test_quotasv2.py b/neutron/tests/unit/extensions/test_quotasv2.py index 124ad7507c6..6b81492e3ca 100644 --- a/neutron/tests/unit/extensions/test_quotasv2.py +++ b/neutron/tests/unit/extensions/test_quotasv2.py @@ -35,6 +35,7 @@ from neutron.tests import tools from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit import testlib_api +DEFAULT_QUOTAS_ACTION = 'default' TARGET_PLUGIN = 'neutron.plugins.ml2.plugin.Ml2Plugin' _get_path = test_base._get_path @@ -126,6 +127,44 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase): 'port': -1, 'extra1': -1}) + def test_show_default_quotas_with_admin(self): + tenant_id = 'tenant_id1' + env = {'neutron.context': context.Context('', tenant_id + '2', + is_admin=True)} + res = self.api.get(_get_path('quotas', id=tenant_id, + action=DEFAULT_QUOTAS_ACTION, + fmt=self.fmt), + extra_environ=env) + self.assertEqual(200, res.status_int) + quota = self.deserialize(res) + self.assertEqual(10, quota['quota']['network']) + self.assertEqual(10, quota['quota']['subnet']) + self.assertEqual(50, quota['quota']['port']) + + def test_show_default_quotas_with_owner_tenant(self): + tenant_id = 'tenant_id1' + env = {'neutron.context': context.Context('', tenant_id, + is_admin=False)} + res = self.api.get(_get_path('quotas', id=tenant_id, + action=DEFAULT_QUOTAS_ACTION, + fmt=self.fmt), + extra_environ=env) + self.assertEqual(200, res.status_int) + quota = self.deserialize(res) + self.assertEqual(10, quota['quota']['network']) + self.assertEqual(10, quota['quota']['subnet']) + self.assertEqual(50, quota['quota']['port']) + + def test_show_default_quotas_without_admin_forbidden_returns_403(self): + tenant_id = 'tenant_id1' + env = {'neutron.context': context.Context('', tenant_id + '2', + is_admin=False)} + res = self.api.get(_get_path('quotas', id=tenant_id, + action=DEFAULT_QUOTAS_ACTION, + fmt=self.fmt), + extra_environ=env, expect_errors=True) + self.assertEqual(403, res.status_int) + def test_show_quotas_with_admin(self): tenant_id = 'tenant_id1' env = {'neutron.context': context.Context('', tenant_id + '2',