From 24baea50675cd30758478b8f2e93460399ec786d Mon Sep 17 00:00:00 2001 From: Gary Kotton Date: Wed, 26 Dec 2012 07:24:37 +0000 Subject: [PATCH] Ensure bulk creations have quota validations Fixes bug 1093749 The patch set also returns a proper error instead of a internal server error when the quotas are reached. Change-Id: Ifc74ffa8b54faa70f5558bf5263830f5e71f58ae --- quantum/api/v2/base.py | 14 ++++++- quantum/tests/unit/test_db_plugin.py | 63 +++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/quantum/api/v2/base.py b/quantum/api/v2/base.py index ad3caa588..c70509577 100644 --- a/quantum/api/v2/base.py +++ b/quantum/api/v2/base.py @@ -49,6 +49,7 @@ FAULT_MAP = {exceptions.NotFound: webob.exc.HTTPNotFound, AttributeError: webob.exc.HTTPBadRequest, ValueError: webob.exc.HTTPBadRequest, exceptions.IpAddressGenerationFailure: webob.exc.HTTPConflict, + exceptions.OverQuota: webob.exc.HTTPConflict, } QUOTAS = quota.QUOTAS @@ -293,8 +294,11 @@ class Controller(object): if self._collection in body: # Have to account for bulk create items = body[self._collection] + deltas = {} + bulk = True else: items = [body] + bulk = False for item in items: self._validate_network_tenant_ownership(request, item[self._resource]) @@ -303,10 +307,16 @@ class Controller(object): item[self._resource], plugin=self._plugin) try: + tenant_id = item[self._resource]['tenant_id'] count = QUOTAS.count(request.context, self._resource, self._plugin, self._collection, - item[self._resource]['tenant_id']) - kwargs = {self._resource: count + 1} + tenant_id) + if bulk: + delta = deltas.get(tenant_id, 0) + 1 + deltas[tenant_id] = delta + else: + delta = 1 + kwargs = {self._resource: count + delta} except exceptions.QuotaResourceUnknown as e: # We don't want to quota this resource LOG.debug(e) diff --git a/quantum/tests/unit/test_db_plugin.py b/quantum/tests/unit/test_db_plugin.py index 65e0823c6..284fd2285 100644 --- a/quantum/tests/unit/test_db_plugin.py +++ b/quantum/tests/unit/test_db_plugin.py @@ -172,6 +172,21 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase): data = self._deserializers[ctype].deserialize(response.body)['body'] return data + def _create_bulk_from_list(self, fmt, resource, objects, **kwargs): + """ Creates a bulk request from a list of objects """ + collection = "%ss" % resource + req_data = {collection: objects} + req = self.new_create_request(collection, req_data, fmt) + if ('set_context' in kwargs and + kwargs['set_context'] is True and + 'tenant_id' in kwargs): + # create a specific auth context for this request + req.environ['quantum.context'] = context.Context( + '', kwargs['tenant_id']) + elif 'context' in kwargs: + req.environ['quantum.context'] = kwargs['context'] + return req.get_response(self.api) + def _create_bulk(self, fmt, number, resource, data, name='test', **kwargs): """ Creates a bulk request for any kind of resource """ objects = [] @@ -386,8 +401,8 @@ class QuantumDbPluginV2TestCase(unittest2.TestCase): patched_plugin.side_effect = second_call return orig(*args, **kwargs) - def _validate_behavior_on_bulk_failure(self, res, collection): - self.assertEqual(res.status_int, 400) + def _validate_behavior_on_bulk_failure(self, res, collection, errcode=400): + self.assertEqual(res.status_int, errcode) req = self.new_list_request(collection) res = req.get_response(self.api) self.assertEqual(res.status_int, 200) @@ -1621,6 +1636,50 @@ class TestNetworksV2(QuantumDbPluginV2TestCase): res = self._create_network_bulk('json', 2, 'test', True) self._validate_behavior_on_bulk_success(res, 'networks') + def test_create_networks_bulk_native_quotas(self): + if self._skip_native_bulk: + self.skipTest("Plugin does not support native bulk network create") + quota = 4 + cfg.CONF.set_override('quota_network', quota, group='QUOTAS') + res = self._create_network_bulk('json', quota + 1, 'test', True) + self._validate_behavior_on_bulk_failure(res, 'networks', errcode=409) + + def test_create_networks_bulk_tenants_and_quotas(self): + if self._skip_native_bulk: + self.skipTest("Plugin does not support native bulk network create") + quota = 2 + cfg.CONF.set_override('quota_network', quota, group='QUOTAS') + networks = [{'network': {'name': 'n1', + 'tenant_id': self._tenant_id}}, + {'network': {'name': 'n2', + 'tenant_id': self._tenant_id}}, + {'network': {'name': 'n1', + 'tenant_id': 't1'}}, + {'network': {'name': 'n2', + 'tenant_id': 't1'}}] + + res = self._create_bulk_from_list('json', 'network', networks) + self.assertEqual(res.status_int, 201) + + def test_create_networks_bulk_tenants_and_quotas_fail(self): + if self._skip_native_bulk: + self.skipTest("Plugin does not support native bulk network create") + quota = 2 + cfg.CONF.set_override('quota_network', quota, group='QUOTAS') + networks = [{'network': {'name': 'n1', + 'tenant_id': self._tenant_id}}, + {'network': {'name': 'n2', + 'tenant_id': self._tenant_id}}, + {'network': {'name': 'n1', + 'tenant_id': 't1'}}, + {'network': {'name': 'n3', + 'tenant_id': self._tenant_id}}, + {'network': {'name': 'n2', + 'tenant_id': 't1'}}] + + res = self._create_bulk_from_list('json', 'network', networks) + self.assertEqual(res.status_int, 409) + def test_create_networks_bulk_emulated(self): real_has_attr = hasattr -- 2.45.2