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
This commit is contained in:
Aarti Kriplani 2013-04-22 12:01:27 -05:00
parent af7048590d
commit cdd998c418
11 changed files with 115 additions and 2 deletions

View File

@ -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.",

View File

@ -171,6 +171,9 @@
<extension alias="os-quota-class-sets" updated="2012-03-12T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1" name="QuotaClasses">
<description>Quota classes management support.</description>
</extension>
<extension alias="os-extended-quotas" updated="2013-05-23T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/quota-delete/api/v1.1" name="ExtendedQuotas">
<description>Adds ability for admins to delete quota.</description>
</extension>
<extension alias="os-quota-sets" updated="2011-08-08T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1" name="Quotas">
<description>Quotas management support.</description>
</extension>

View File

@ -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",

View File

@ -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"

View File

@ -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)

View File

@ -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):

View File

@ -195,6 +195,7 @@ class ExtensionControllerTest(ExtensionTestCase):
"MultipleCreate",
"QuotaClasses",
"Quotas",
"ExtendedQuotas",
"Rescue",
"SchedulerHints",
"SecurityGroupDefaultRules",

View File

@ -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": "",

View File

@ -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",

View File

@ -156,6 +156,9 @@
<extension alias="os-quota-class-sets" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/quota-classes-sets/api/v1.1" name="QuotaClasses">
<description>%(text)s</description>
</extension>
<extension alias="os-extended-quotas" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/quota-delete/api/v1.1" name="ExtendedQuotas">
<description>%(text)s</description>
</extension>
<extension alias="os-quota-sets" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1" name="Quotas">
<description>%(text)s</description>
</extension>

View File

@ -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")