From a0a036d7e667ada52f9e5cb9c70be651ea7de488 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Fri, 31 Jan 2020 14:34:48 +1300 Subject: [PATCH] Delete quota API Change-Id: If3d938833bf1c519cd454221c1a8b7658a26440b --- aodh/api/controllers/v2/quotas.py | 7 ++++ aodh/api/policies.py | 11 ++++++ aodh/storage/base.py | 5 +++ aodh/storage/impl_sqlalchemy.py | 5 +++ aodh/tests/functional/api/v2/test_quotas.py | 40 +++++++++++++++++++++ 5 files changed, 68 insertions(+) diff --git a/aodh/api/controllers/v2/quotas.py b/aodh/api/controllers/v2/quotas.py index 4586ec3fa..f5a30ef02 100644 --- a/aodh/api/controllers/v2/quotas.py +++ b/aodh/api/controllers/v2/quotas.py @@ -82,3 +82,10 @@ class QuotasController(rest.RestController): quotas = [Quota.from_db_model(i) for i in db_quotas] return Quotas(project_id=project_id, quotas=quotas) + + @wsme_pecan.wsexpose(None, str, status_code=204) + def delete(self, project_id): + """Delete quotas for the given project.""" + rbac.enforce('delete_quotas', pecan.request.headers, + pecan.request.enforcer, {}) + pecan.request.storage.delete_quotas(project_id) diff --git a/aodh/api/policies.py b/aodh/api/policies.py index 419775b51..dc9983267 100644 --- a/aodh/api/policies.py +++ b/aodh/api/policies.py @@ -166,6 +166,17 @@ rules = [ 'method': 'POST' } ] + ), + policy.DocumentedRuleDefault( + name="telemetry:delete_quotas", + check_str=RULE_CONTEXT_IS_ADMIN, + description='Delete resources quotas for project.', + operations=[ + { + 'path': '/v2/quotas/{project_id}', + 'method': 'DELETE' + } + ] ) ] diff --git a/aodh/storage/base.py b/aodh/storage/base.py index c4a4afb56..fdf224592 100644 --- a/aodh/storage/base.py +++ b/aodh/storage/base.py @@ -215,3 +215,8 @@ class Connection(object): """Set resource quota for the given user.""" raise aodh.NotImplementedError('Setting resource quota not ' 'implemented') + + @staticmethod + def delete_quotas(project_id): + raise aodh.NotImplementedError('Deleting resource quota not ' + 'implemented') diff --git a/aodh/storage/impl_sqlalchemy.py b/aodh/storage/impl_sqlalchemy.py index 7fdf1fa06..613ce35bd 100644 --- a/aodh/storage/impl_sqlalchemy.py +++ b/aodh/storage/impl_sqlalchemy.py @@ -475,3 +475,8 @@ class Connection(base.Connection): filters = {'project_id': project_id} query = session.query(models.Quota).filter_by(**filters) return self._retrieve_quotas(query) + + def delete_quotas(self, project_id): + filters = {'project_id': project_id} + session = self._engine_facade.get_session() + session.query(models.Quota).filter_by(**filters).delete() diff --git a/aodh/tests/functional/api/v2/test_quotas.py b/aodh/tests/functional/api/v2/test_quotas.py index eec8f0d95..b855268b8 100644 --- a/aodh/tests/functional/api/v2/test_quotas.py +++ b/aodh/tests/functional/api/v2/test_quotas.py @@ -190,3 +190,43 @@ class TestQuotas(v2.FunctionalTest): self.assertIn('Value should be one of', resp.json['error_message']['faultstring']) + + def test_delete_project_quota_by_admin(self): + auth_headers = copy.copy(self.auth_headers) + auth_headers['X-Roles'] = 'admin' + + self.post_json( + '/quotas', + { + "project_id": self.other_project, + "quotas": [ + { + "resource": "alarms", + "limit": 30 + } + ] + }, + headers=auth_headers, + status=201 + ) + + resp = self.get_json('/quotas?project_id=%s' % self.other_project, + headers=auth_headers, + status=200) + self.assert_single_item(resp['quotas'], resource='alarms', + limit=30) + + self.delete('/quotas/%s' % self.other_project, headers=auth_headers, + status=204) + + resp = self.get_json('/quotas?project_id=%s' % self.other_project, + headers=auth_headers, + status=200) + self.assert_multiple_items(resp['quotas'], 0, resource='alarms', + limit=30) + + def test_delete_project_quota_by_user_failed(self): + self.delete('/quotas/%s' % self.other_project, + headers=self.auth_headers, + expect_errors=True, + status=403)