From cdd998c418edc42a6a828ddef144e8408c206b7c Mon Sep 17 00:00:00 2001 From: Aarti Kriplani Date: Mon, 22 Apr 2013 12:01:27 -0500 Subject: [PATCH] Delete a quota through admin api. Added 'extended-quotas' extension that has provides ability for admins to be able to delete a non-default quota (absolute limit) for a tenant, so that tenant's quota will revert back to the configured default. Implements blueprint admin-api-for-delete-quota Change-Id: I7375420a466823b3e099aebff71a8f7d7f922afb --- .../all_extensions/extensions-get-resp.json | 8 +++++ .../all_extensions/extensions-get-resp.xml | 3 ++ etc/nova/policy.json | 1 + .../compute/contrib/extended_quotas.py | 25 ++++++++++++++ nova/api/openstack/compute/contrib/quotas.py | 18 +++++++++- .../openstack/compute/contrib/test_quotas.py | 33 ++++++++++++++++++- .../api/openstack/compute/test_extensions.py | 1 + nova/tests/fake_policy.py | 1 + .../extensions-get-resp.json.tpl | 8 +++++ .../extensions-get-resp.xml.tpl | 3 ++ nova/tests/integrated/test_api_samples.py | 16 +++++++++ 11 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 nova/api/openstack/compute/contrib/extended_quotas.py diff --git a/doc/api_samples/all_extensions/extensions-get-resp.json b/doc/api_samples/all_extensions/extensions-get-resp.json index d7c2a646c780..0b587a6b1c9d 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.json +++ b/doc/api_samples/all_extensions/extensions-get-resp.json @@ -408,6 +408,14 @@ "namespace": "http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1", "updated": "2012-03-12T00:00:00+00:00" }, + { + "alias": "os-extended-quotas", + "description": "Adds ability for admins to delete quota", + "links": [], + "name": "ExtendedQuotas", + "namespace": "http://docs.openstack.org/compute/ext/quota-delete/api/v1.1", + "updated": "2013-05-23T00:00:00+00:00" + }, { "alias": "os-quota-sets", "description": "Quotas management support.", diff --git a/doc/api_samples/all_extensions/extensions-get-resp.xml b/doc/api_samples/all_extensions/extensions-get-resp.xml index f6213a3a92eb..9924a188ae14 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.xml +++ b/doc/api_samples/all_extensions/extensions-get-resp.xml @@ -171,6 +171,9 @@ Quota classes management support. + + Adds ability for admins to delete quota. + Quotas management support. diff --git a/etc/nova/policy.json b/etc/nova/policy.json index 26a227ae201e..d6524b6a28ab 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -80,6 +80,7 @@ "compute_extension:networks_associate": "rule:admin_api", "compute_extension:quotas:show": "", "compute_extension:quotas:update": "rule:admin_api", + "compute_extension:quotas:delete": "rule:admin_api", "compute_extension:quota_classes": "", "compute_extension:rescue": "", "compute_extension:security_group_default_rules": "rule:admin_api", diff --git a/nova/api/openstack/compute/contrib/extended_quotas.py b/nova/api/openstack/compute/contrib/extended_quotas.py new file mode 100644 index 000000000000..431b95e9b321 --- /dev/null +++ b/nova/api/openstack/compute/contrib/extended_quotas.py @@ -0,0 +1,25 @@ +# Copyright 2013 Rackspace Hosting +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nova.api.openstack import extensions + + +class Extended_quotas(extensions.ExtensionDescriptor): + """Adds ability for admins to delete quota.""" + + name = "ExtendedQuotas" + alias = "os-extended-quotas" + namespace = "http://docs.openstack.org/compute/ext/quota-delete/api/v1.1" + updated = "2013-05-23T00:00:00+00:00" diff --git a/nova/api/openstack/compute/contrib/quotas.py b/nova/api/openstack/compute/contrib/quotas.py index c7fe87a1ff76..0a2453038e27 100644 --- a/nova/api/openstack/compute/contrib/quotas.py +++ b/nova/api/openstack/compute/contrib/quotas.py @@ -33,6 +33,7 @@ LOG = logging.getLogger(__name__) authorize_update = extensions.extension_authorizer('compute', 'quotas:update') authorize_show = extensions.extension_authorizer('compute', 'quotas:show') +authorize_delete = extensions.extension_authorizer('compute', 'quotas:delete') class QuotaTemplate(xmlutil.TemplateBuilder): @@ -49,6 +50,9 @@ class QuotaTemplate(xmlutil.TemplateBuilder): class QuotaSetsController(object): + def __init__(self, ext_mgr): + self.ext_mgr = ext_mgr + def _format_quota_set(self, project_id, quota_set): """Convert the quota object to a result dict.""" @@ -124,6 +128,18 @@ class QuotaSetsController(object): authorize_show(context) return self._format_quota_set(id, QUOTAS.get_defaults(context)) + def delete(self, req, id): + if self.ext_mgr.is_loaded('os-extended-quotas'): + context = req.environ['nova.context'] + authorize_delete(context) + try: + nova.context.authorize_project_context(context, id) + QUOTAS.destroy_all_by_project(context, id) + return webob.Response(status_int=202) + except exception.NotAuthorized: + raise webob.exc.HTTPForbidden() + raise webob.exc.HTTPNotFound() + class Quotas(extensions.ExtensionDescriptor): """Quotas management support.""" @@ -137,7 +153,7 @@ class Quotas(extensions.ExtensionDescriptor): resources = [] res = extensions.ResourceExtension('os-quota-sets', - QuotaSetsController(), + QuotaSetsController(self.ext_mgr), member_actions={'defaults': 'GET'}) resources.append(res) diff --git a/nova/tests/api/openstack/compute/contrib/test_quotas.py b/nova/tests/api/openstack/compute/contrib/test_quotas.py index c95c41614fbe..4fb33729462c 100644 --- a/nova/tests/api/openstack/compute/contrib/test_quotas.py +++ b/nova/tests/api/openstack/compute/contrib/test_quotas.py @@ -19,7 +19,10 @@ from lxml import etree import webob from nova.api.openstack.compute.contrib import quotas +from nova.api.openstack import extensions from nova.api.openstack import wsgi +from nova import context as context_maker +from nova import quota from nova import test from nova.tests.api.openstack import fakes @@ -37,7 +40,8 @@ class QuotaSetsTest(test.TestCase): def setUp(self): super(QuotaSetsTest, self).setUp() - self.controller = quotas.QuotaSetsController() + self.ext_mgr = self.mox.CreateMock(extensions.ExtensionManager) + self.controller = quotas.QuotaSetsController(self.ext_mgr) def test_format_quota_set(self): raw_quota_set = { @@ -201,6 +205,33 @@ class QuotaSetsTest(test.TestCase): res_dict = self.controller.update(req, 'update_me', body) self.assertEqual(res_dict, expected_resp) + def test_delete_quotas_when_extension_not_loaded(self): + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(False) + self.mox.ReplayAll() + req = fakes.HTTPRequest.blank('/v2/fake4/os-quota-sets/1234') + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, 1234) + + def test_quotas_delete_as_unauthorized_user(self): + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.ReplayAll() + req = fakes.HTTPRequest.blank('/v2/fake4/os-quota-sets/1234') + self.assertRaises(webob.exc.HTTPForbidden, self.controller.delete, + req, 1234) + + def test_quotas_delete_as_admin(self): + context = context_maker.get_admin_context() + self.req = fakes.HTTPRequest.blank('/v2/fake4/os-quota-sets/1234') + self.req.environ['nova.context'] = context + self.ext_mgr.is_loaded('os-extended-quotas').AndReturn(True) + self.mox.StubOutWithMock(quota.QUOTAS, + "destroy_all_by_project") + quota.QUOTAS.destroy_all_by_project(context, 1234) + self.mox.ReplayAll() + res = self.controller.delete(self.req, 1234) + self.mox.VerifyAll() + self.assertEqual(res.status_int, 202) + class QuotaXMLSerializerTest(test.TestCase): def setUp(self): diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py index 6e400a075cec..5c3e07a7a0db 100644 --- a/nova/tests/api/openstack/compute/test_extensions.py +++ b/nova/tests/api/openstack/compute/test_extensions.py @@ -195,6 +195,7 @@ class ExtensionControllerTest(ExtensionTestCase): "MultipleCreate", "QuotaClasses", "Quotas", + "ExtendedQuotas", "Rescue", "SchedulerHints", "SecurityGroupDefaultRules", diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index 1290ef80b7df..b30793ac40fb 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -159,6 +159,7 @@ policy_data = """ "compute_extension:os-tenant-networks": "", "compute_extension:quotas:show": "", "compute_extension:quotas:update": "", + "compute_extension:quotas:delete": "", "compute_extension:quota_classes": "", "compute_extension:rescue": "", "compute_extension:security_group_default_rules": "", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl index d559b4890f22..7a636036aca7 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl @@ -416,6 +416,14 @@ "namespace": "http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1", "updated": "%(timestamp)s" }, + { + "alias": "os-extended-quotas", + "description": "%(text)s", + "links": [], + "name": "ExtendedQuotas", + "namespace": "http://docs.openstack.org/compute/ext/quota-delete/api/v1.1", + "updated": "%(timestamp)s" + }, { "alias": "os-quota-sets", "description": "%(text)s", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl index cc9ae4c02685..ee01eb33c5db 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl @@ -156,6 +156,9 @@ %(text)s + + %(text)s + %(text)s diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index 9b678ddd3593..873719ab634e 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -2277,6 +2277,22 @@ class QuotasSampleXmlTests(QuotasSampleJsonTests): ctype = "xml" +class ExtendedQuotasSampleJsonTests(ApiSampleTestBase): + extends_name = "nova.api.openstack.compute.contrib.quotas.Quotas" + extension_name = ("nova.api.openstack.compute.contrib" + ".extended_quotas.Extended_quotas") + + def test_delete_quotas(self): + # Get api sample to delete quota. + response = self._do_delete('os-quota-sets/fake_tenant') + self.assertEqual(response.status, 202) + self.assertEqual(response.read(), '') + + +class ExtendedQuotasSampleXmlTests(ExtendedQuotasSampleJsonTests): + ctype = "xml" + + class ExtendedIpsSampleJsonTests(ServersSampleBase): extension_name = ("nova.api.openstack.compute.contrib" ".extended_ips.Extended_ips")