diff --git a/doc/source/admin/ops-quotas.rst b/doc/source/admin/ops-quotas.rst index 45b811cd7a6..c6803036805 100644 --- a/doc/source/admin/ops-quotas.rst +++ b/doc/source/admin/ops-quotas.rst @@ -38,7 +38,7 @@ default quota values: quota_port = 50 # default driver to use for quota checks - quota_driver = neutron.quota.ConfDriver + quota_driver = neutron.quota.DbQuotaNoLockDriver OpenStack Networking also supports quotas for L3 resources: router and floating IP. Add these lines to the diff --git a/doc/source/contributor/internals/quota.rst b/doc/source/contributor/internals/quota.rst index 92cd68c5d0b..2d94942940b 100644 --- a/doc/source/contributor/internals/quota.rst +++ b/doc/source/contributor/internals/quota.rst @@ -63,10 +63,9 @@ three quota drivers: * neutron.db.quota.driver.DbQuotaDriver * neutron.db.quota.driver_nolock.DbQuotaNoLockDriver (default) - * neutron.quota.ConfDriver -The latter driver is however deprecated. The ``DbQuotaNoLockDriver`` is the -default quota driver, defined in the configuration option ``quota_driver``. +The ``DbQuotaNoLockDriver`` is the default quota driver, defined in the +configuration option ``quota_driver``. The Quota API extension handles quota management, whereas the Quota Engine component handles quota enforcement. This API extension is loaded like any diff --git a/neutron/api/v2/base.py b/neutron/api/v2/base.py index 7c4793463d3..6c54b77d4b3 100644 --- a/neutron/api/v2/base.py +++ b/neutron/api/v2/base.py @@ -489,7 +489,8 @@ class Controller(object): tenant, {self._resource: delta}, self._plugin) - reservations.append(reservation) + if reservation: + reservations.append(reservation) except exceptions.QuotaResourceUnknown as e: # We don't want to quota this resource LOG.debug(e) diff --git a/neutron/conf/quota.py b/neutron/conf/quota.py index 4a3a96cca20..535950a1262 100644 --- a/neutron/conf/quota.py +++ b/neutron/conf/quota.py @@ -21,7 +21,6 @@ from neutron._i18n import _ QUOTA_DB_MODULE = 'neutron.db.quota.driver_nolock' QUOTA_DB_DRIVER = QUOTA_DB_MODULE + '.DbQuotaNoLockDriver' -QUOTA_CONF_DRIVER = 'neutron.quota.ConfDriver' QUOTAS_CFG_GROUP = 'QUOTAS' DEFAULT_QUOTA = -1 diff --git a/neutron/db/quota/api.py b/neutron/db/quota/api.py index 89ef9ae7832..33c15f8b2ae 100644 --- a/neutron/db/quota/api.py +++ b/neutron/db/quota/api.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import abc import collections import datetime @@ -237,3 +238,170 @@ def get_reservations_for_resources(context, tenant_id, resources, @db_api.CONTEXT_WRITER def remove_expired_reservations(context, tenant_id=None): return quota_obj.Reservation.delete_expired(context, utcnow(), tenant_id) + + +class QuotaDriverAPI(object, metaclass=abc.ABCMeta): + + @staticmethod + @abc.abstractmethod + def get_default_quotas(context, resources, project_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 project_id: The ID of the project to return default quotas for. + :return: dict from resource name to dict of name and limit + """ + + @staticmethod + @abc.abstractmethod + def get_tenant_quotas(context, resources, project_id): + """Retrieve the quotas for the given list of resources and project + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resource keys. + :param project_id: The ID of the project to return quotas for. + :return: dict from resource name to dict of name and limit + """ + + @staticmethod + @abc.abstractmethod + def get_detailed_tenant_quotas(context, resources, project_id): + """Retrieve detailed quotas for the given list of resources and project + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resource keys. + :param project_id: The ID of the project to return quotas for. + :return dict: mapping resource name in dict to its corresponding limit + used and reserved. Reserved currently returns default + value of 0 + """ + + @staticmethod + @abc.abstractmethod + def delete_tenant_quota(context, project_id): + """Delete the quota entries for a given project_id. + + After deletion, this tenant will use default quota values in conf. + Raise a "not found" error if the quota for the given tenant was + never defined. + + :param context: The request context, for access checks. + :param project_id: The ID of the project to return quotas for. + """ + + @staticmethod + @abc.abstractmethod + def get_all_quotas(context, resources): + """Given a list of resources, retrieve the quotas for the all tenants. + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resource keys. + :return: quotas list of dict of project_id:, resourcekey1: + resourcekey2: ... + """ + + @staticmethod + @abc.abstractmethod + def update_quota_limit(context, project_id, resource, limit): + """Update the quota limit for a resource in a project + + :param context: The request context, for access checks. + :param project_id: The ID of the project to update the quota. + :param resource: the resource to update the quota. + :param limit: new resource quota limit. + """ + + @staticmethod + @abc.abstractmethod + def make_reservation(context, project_id, resources, deltas, plugin): + """Make multiple resource reservations for a given project + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resource keys. + :param project_id: The ID of the project to make the reservations for. + :return: ``ReservationInfo`` object. + """ + + @staticmethod + @abc.abstractmethod + def commit_reservation(context, reservation_id): + """Commit a reservation register + + :param context: The request context, for access checks. + :param reservation_id: ID of the reservation register to commit. + """ + + @staticmethod + @abc.abstractmethod + def cancel_reservation(context, reservation_id): + """Cancel a reservation register + + :param context: The request context, for access checks. + :param reservation_id: ID of the reservation register to cancel. + """ + + @staticmethod + @abc.abstractmethod + def limit_check(context, project_id, resources, values): + """Check simple quota limits. + + For limits--those quotas for which there is no usage + synchronization function--this method checks that a set of + proposed values are permitted by the limit restriction. + + If any of the proposed values is over the defined quota, an + OverQuota exception will be raised with the sorted list of the + resources which are too high. Otherwise, the method returns + nothing. + + :param context: The request context, for access checks. + :param project_id: The ID of the project to make the reservations for. + :param resources: A dictionary of the registered resource. + :param values: A dictionary of the values to check against the + quota. + """ + + +class NullQuotaDriver(QuotaDriverAPI): + + @staticmethod + def get_default_quotas(context, resources, project_id): + pass + + @staticmethod + def get_tenant_quotas(context, resources, project_id): + pass + + @staticmethod + def get_detailed_tenant_quotas(context, resources, project_id): + pass + + @staticmethod + def delete_tenant_quota(context, project_id): + pass + + @staticmethod + def get_all_quotas(context, resources): + pass + + @staticmethod + def update_quota_limit(context, project_id, resource, limit): + pass + + @staticmethod + def make_reservation(context, project_id, resources, deltas, plugin): + pass + + @staticmethod + def commit_reservation(context, reservation_id): + pass + + @staticmethod + def cancel_reservation(context, reservation_id): + pass + + @staticmethod + def limit_check(context, project_id, resources, values): + pass diff --git a/neutron/db/quota/driver.py b/neutron/db/quota/driver.py index bd35ccecadd..6ace65ce4a3 100644 --- a/neutron/db/quota/driver.py +++ b/neutron/db/quota/driver.py @@ -27,7 +27,7 @@ from neutron.quota import resource as res LOG = log.getLogger(__name__) -class DbQuotaDriver(object): +class DbQuotaDriver(quota_api.QuotaDriverAPI): """Driver to perform necessary checks to enforce quotas and obtain quota information. diff --git a/neutron/db/securitygroups_db.py b/neutron/db/securitygroups_db.py index 746898519f4..964c1ef48d1 100644 --- a/neutron/db/securitygroups_db.py +++ b/neutron/db/securitygroups_db.py @@ -137,8 +137,9 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase, sg.rules.append(egress_rule) sg.obj_reset_changes(['rules']) - quota.QUOTAS.commit_reservation(context, - reservation.reservation_id) + if reservation: + quota.QUOTAS.commit_reservation(context, + reservation.reservation_id) # fetch sg from db to load the sg rules with sg model. # NOTE(slaweq): With new system/project scopes it may happen that diff --git a/neutron/pecan_wsgi/hooks/quota_enforcement.py b/neutron/pecan_wsgi/hooks/quota_enforcement.py index 0f42b49ffba..0267145c0f8 100644 --- a/neutron/pecan_wsgi/hooks/quota_enforcement.py +++ b/neutron/pecan_wsgi/hooks/quota_enforcement.py @@ -53,7 +53,8 @@ class QuotaEnforcementHook(hooks.PecanHook): LOG.debug("Made reservation on behalf of %(tenant_id)s " "for: %(delta)s", {'tenant_id': tenant_id, 'delta': {resource: delta}}) - reservations.append(reservation) + if reservation: + reservations.append(reservation) except exceptions.QuotaResourceUnknown as e: # Quotas cannot be enforced on this resource LOG.debug(e) diff --git a/neutron/quota/__init__.py b/neutron/quota/__init__.py index ed0ca8f1079..3a1198a1482 100644 --- a/neutron/quota/__init__.py +++ b/neutron/quota/__init__.py @@ -12,141 +12,23 @@ # License for the specific language governing permissions and limitations # under the License. -"""Quotas for instances, volumes, and floating ips.""" - -import sys - from neutron_lib import exceptions from oslo_config import cfg from oslo_log import log as logging -from oslo_log import versionutils from oslo_utils import importutils -import webob -from neutron._i18n import _ from neutron.conf import quota -from neutron.db.quota import api as quota_api from neutron.quota import resource_registry LOG = logging.getLogger(__name__) QUOTA_DB_MODULE = quota.QUOTA_DB_MODULE QUOTA_DB_DRIVER = quota.QUOTA_DB_DRIVER -QUOTA_CONF_DRIVER = quota.QUOTA_CONF_DRIVER # Register the configuration options quota.register_quota_opts(quota.core_quota_opts) -class ConfDriver(object): - """Configuration driver. - - Driver to perform necessary checks to enforce quotas and obtain - quota information. The default driver utilizes the default values - in neutron.conf. - """ - - def _get_quotas(self, context, resources): - """Get quotas. - - A helper method which retrieves the quotas for the specific - resources identified by keys, and which apply to the current - context. - - :param context: The request context, for access checks. - :param resources: A dictionary of the registered resources. - """ - - quotas = {} - for resource in resources.values(): - quotas[resource.name] = resource.default - return quotas - - def limit_check(self, context, tenant_id, - resources, values): - """Check simple quota limits. - - For limits--those quotas for which there is no usage - synchronization function--this method checks that a set of - proposed values are permitted by the limit restriction. - - If any of the proposed values is over the defined quota, an - OverQuota exception will be raised with the sorted list of the - resources which are too high. Otherwise, the method returns - nothing. - - :param context: The request context, for access checks. - :param tenant_id: The tenant_id to check quota. - :param resources: A dictionary of the registered resources. - :param values: A dictionary of the values to check against the - quota. - """ - # Ensure no value is less than zero - unders = [key for key, val in values.items() if val < 0] - if unders: - raise exceptions.InvalidQuotaValue(unders=sorted(unders)) - - # Get the applicable quotas - quotas = self._get_quotas(context, resources) - - # Check the quotas and construct a list of the resources that - # would be put over limit by the desired values - overs = [key for key, val in values.items() - if quotas[key] >= 0 and quotas[key] < val] - if overs: - raise exceptions.OverQuota(overs=sorted(overs), quotas=quotas, - usages={}) - - @staticmethod - def get_tenant_quotas(context, resources, tenant_id): - quotas = {} - sub_resources = dict((k, v) for k, v in resources.items()) - for resource in sub_resources.values(): - quotas[resource.name] = resource.default - return quotas - - @staticmethod - def get_all_quotas(context, resources): - return [] - - @staticmethod - def delete_tenant_quota(context, tenant_id): - msg = _('Access to this resource was denied.') - raise webob.exc.HTTPForbidden(msg) - - @staticmethod - def update_quota_limit(context, tenant_id, resource, limit): - msg = _('Access to this resource was denied.') - raise webob.exc.HTTPForbidden(msg) - - def make_reservation(self, context, tenant_id, resources, deltas, plugin): - """This driver does not support reservations. - - This routine is provided for backward compatibility purposes with - the API controllers which have now been adapted to make reservations - rather than counting resources and checking limits - as this - routine ultimately does. - """ - for resource in deltas.keys(): - count = QUOTAS.count(context, resource, plugin, tenant_id) - total_use = deltas.get(resource, 0) + count - deltas[resource] = total_use - - self.limit_check( - context, - tenant_id, - resource_registry.get_all_resources(), - deltas) - # return a fake reservation - the REST controller expects it - return quota_api.ReservationInfo('fake', None, None, None) - - def commit_reservation(self, context, reservation_id): - """This is a noop as this driver does not support reservations.""" - - def cancel_reservation(self, context, reservation_id): - """This is a noop as this driver does not support reservations.""" - - class QuotaEngine(object): """Represent the set of recognized quotas.""" @@ -167,20 +49,8 @@ class QuotaEngine(object): if self._driver is None: _driver_class = (self._driver_class or cfg.CONF.QUOTAS.quota_driver) - if (_driver_class == QUOTA_DB_DRIVER and - QUOTA_DB_MODULE not in sys.modules): - # If quotas table is not loaded, force config quota driver. - _driver_class = QUOTA_CONF_DRIVER - LOG.info("ConfDriver is used as quota_driver because the " - "loaded plugin does not support 'quotas' table.") if isinstance(_driver_class, str): _driver_class = importutils.import_object(_driver_class) - if isinstance(_driver_class, ConfDriver): - versionutils.report_deprecated_feature( - LOG, ("The quota driver neutron.quota.ConfDriver is " - "deprecated as of Liberty. " - "neutron.db.quota.driver.DbQuotaDriver should " - "be used in its place")) self._driver = _driver_class LOG.info('Loaded quota_driver: %s.', _driver_class) return self._driver diff --git a/neutron/tests/unit/api/test_extensions.py b/neutron/tests/unit/api/test_extensions.py index c7b1951ec8e..56bd169ceb0 100644 --- a/neutron/tests/unit/api/test_extensions.py +++ b/neutron/tests/unit/api/test_extensions.py @@ -1032,7 +1032,8 @@ class ExtensionExtendedAttributeTestCase(base.BaseTestCase): self.agentscheduler_dbMinxin = directory.get_plugin() quota.QUOTAS._driver = None - cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', + cfg.CONF.set_override('quota_driver', + 'neutron.db.quota.api.NullQuotaDriver', group='QUOTAS') def _do_request(self, method, path, data=None, params=None, action=None): diff --git a/neutron/tests/unit/api/v2/test_base.py b/neutron/tests/unit/api/v2/test_base.py index da02937bd33..acac8e8f350 100644 --- a/neutron/tests/unit/api/v2/test_base.py +++ b/neutron/tests/unit/api/v2/test_base.py @@ -42,7 +42,6 @@ from neutron.api.v2 import base as v2_base from neutron.api.v2 import router from neutron import policy from neutron import quota -from neutron.quota import resource_registry from neutron.tests import base from neutron.tests import tools from neutron.tests.unit import dummy_plugin @@ -50,6 +49,7 @@ from neutron.tests.unit import testlib_api EXTDIR = os.path.join(base.ROOTDIR, 'unit/extensions') +NULL_QUOTA_DRIVER = 'neutron.db.quota.api.NullQuotaDriver' _uuid = uuidutils.generate_uuid @@ -98,7 +98,7 @@ class APIv2TestBase(base.BaseTestCase): self.api = webtest.TestApp(api) quota.QUOTAS._driver = None - cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', + cfg.CONF.set_override('quota_driver', quota.QUOTA_DB_DRIVER, group='QUOTAS') # APIRouter initialization resets policy module, re-initializing it @@ -1302,6 +1302,9 @@ class NotificationTest(APIv2TestBase): def setUp(self): super(NotificationTest, self).setUp() fake_notifier.reset() + quota.QUOTAS._driver = None + cfg.CONF.set_override('quota_driver', NULL_QUOTA_DRIVER, + group='QUOTAS') def _resource_op_notifier(self, opname, resource, expected_errors=False): initial_input = {resource: {'name': 'myname'}} @@ -1354,9 +1357,10 @@ class NotificationTest(APIv2TestBase): class RegistryNotificationTest(APIv2TestBase): def setUp(self): - # This test does not have database support so tracking cannot be used - cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') super(RegistryNotificationTest, self).setUp() + quota.QUOTAS._driver = None + cfg.CONF.set_override('quota_driver', NULL_QUOTA_DRIVER, + group='QUOTAS') def _test_registry_notify(self, opname, resource, initial_input=None): instance = self.plugin.return_value @@ -1400,69 +1404,28 @@ class RegistryNotificationTest(APIv2TestBase): class QuotaTest(APIv2TestBase): + """This class checks the quota enforcement API, regardless of the driver""" - def setUp(self): - # This test does not have database support so tracking cannot be used - cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') - super(QuotaTest, self).setUp() - # Use mock to let the API use a different QuotaEngine instance for - # unit test in this class. This will ensure resource are registered - # again and instantiated with neutron.quota.resource.CountableResource - replacement_registry = resource_registry.ResourceRegistry() - registry_patcher = mock.patch('neutron.quota.resource_registry.' - 'ResourceRegistry.get_instance') - mock_registry = registry_patcher.start().return_value - mock_registry.get_resource = replacement_registry.get_resource - mock_registry.resources = replacement_registry.resources - # Register a resource - replacement_registry.register_resource_by_name('network') - - def test_create_network_quota(self): - cfg.CONF.set_override('quota_network', 1, group='QUOTAS') + def test_create_network_quota_exceeded(self): initial_input = {'network': {'name': 'net1', 'tenant_id': _uuid()}} - full_input = {'network': {'admin_state_up': True, 'subnets': []}} - full_input['network'].update(initial_input['network']) - - instance = self.plugin.return_value - instance.get_networks_count.return_value = 1 - res = self.api.post_json( - _get_path('networks'), initial_input, expect_errors=True) - instance.get_networks_count.assert_called_with(mock.ANY, - filters=mock.ANY) - self.assertIn("Quota exceeded for resources", - res.json['NeutronError']['message']) - - def test_create_network_quota_no_counts(self): - cfg.CONF.set_override('quota_network', 1, group='QUOTAS') - initial_input = {'network': {'name': 'net1', 'tenant_id': _uuid()}} - full_input = {'network': {'admin_state_up': True, 'subnets': []}} - full_input['network'].update(initial_input['network']) - - instance = self.plugin.return_value - instance.get_networks_count.side_effect = ( - NotImplementedError()) - instance.get_networks.return_value = ["foo"] - res = self.api.post_json( - _get_path('networks'), initial_input, expect_errors=True) - instance.get_networks_count.assert_called_with(mock.ANY, - filters=mock.ANY) + with mock.patch.object(quota.QUOTAS, 'make_reservation', + side_effect=n_exc.OverQuota(overs='network')): + res = self.api.post_json( + _get_path('networks'), initial_input, expect_errors=True) self.assertIn("Quota exceeded for resources", res.json['NeutronError']['message']) def test_create_network_quota_without_limit(self): - cfg.CONF.set_override('quota_network', -1, group='QUOTAS') initial_input = {'network': {'name': 'net1', 'tenant_id': _uuid()}} - instance = self.plugin.return_value - instance.get_networks_count.return_value = 3 - res = self.api.post_json( - _get_path('networks'), initial_input) + with mock.patch.object(quota.QUOTAS, 'make_reservation'), \ + mock.patch.object(quota.QUOTAS, 'commit_reservation'): + res = self.api.post_json( + _get_path('networks'), initial_input) self.assertEqual(exc.HTTPCreated.code, res.status_int) class ExtensionTestCase(base.BaseTestCase): def setUp(self): - # This test does not have database support so tracking cannot be used - cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') super(ExtensionTestCase, self).setUp() plugin = 'neutron.neutron_plugin_base_v2.NeutronPluginBaseV2' # Ensure existing ExtensionManager is not used @@ -1487,7 +1450,7 @@ class ExtensionTestCase(base.BaseTestCase): self.api = webtest.TestApp(api) quota.QUOTAS._driver = None - cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', + cfg.CONF.set_override('quota_driver', NULL_QUOTA_DRIVER, group='QUOTAS') def test_extended_create(self): diff --git a/neutron/tests/unit/db/test_db_base_plugin_v2.py b/neutron/tests/unit/db/test_db_base_plugin_v2.py index 9002dbec9c2..30679c225c4 100644 --- a/neutron/tests/unit/db/test_db_base_plugin_v2.py +++ b/neutron/tests/unit/db/test_db_base_plugin_v2.py @@ -63,6 +63,8 @@ from neutron.ipam import exceptions as ipam_exc from neutron.objects import network as network_obj from neutron.objects import router as l3_obj from neutron import policy +from neutron import quota +from neutron.quota import resource_registry from neutron.tests import base from neutron.tests.unit.api import test_extensions from neutron.tests.unit import testlib_api @@ -104,17 +106,26 @@ def _get_create_db_method(resource): return 'create_%s' % resource +def _set_temporary_quota(resource, default_value): + quota_name = uuidutils.generate_uuid(dashed=False) + opt = cfg.IntOpt(quota_name, default=default_value) + cfg.CONF.register_opt(opt, group='QUOTAS') + resources = resource_registry.ResourceRegistry.get_instance().resources + resources[resource].flag = quota_name + + class NeutronDbPluginV2TestCase(testlib_api.WebTestCase): fmt = 'json' resource_prefix_map = {} block_dhcp_notifier = True + quota_db_driver = quota_conf.QUOTA_DB_DRIVER def setUp(self, plugin=None, service_plugins=None, ext_mgr=None): + quota.QUOTAS._driver = None quota_conf.register_quota_opts(quota_conf.core_quota_opts, cfg.CONF) - cfg.CONF.set_override( - 'quota_driver', 'neutron.db.quota.driver.DbQuotaDriver', - group=quota_conf.QUOTAS_CFG_GROUP) + cfg.CONF.set_override('quota_driver', self.quota_db_driver, + group=quota_conf.QUOTAS_CFG_GROUP) super(NeutronDbPluginV2TestCase, self).setUp() cfg.CONF.set_override('notify_nova_on_port_status_changes', False) cfg.CONF.set_override('allow_overlapping_ips', True) @@ -146,6 +157,7 @@ class NeutronDbPluginV2TestCase(testlib_api.WebTestCase): cfg.CONF.set_override('base_mac', "12:34:56:78:00:00") cfg.CONF.set_override('max_dns_nameservers', 2) cfg.CONF.set_override('max_subnet_host_routes', 2) + resource_registry.ResourceRegistry._instance = None self.api = router.APIRouter() # Set the default status self.net_create_status = 'ACTIVE' @@ -1694,8 +1706,8 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s res['port']['fixed_ips']) def test_create_ports_native_quotas(self): - quota = 1 - cfg.CONF.set_override('quota_port', quota, group='QUOTAS') + self._tenant_id = uuidutils.generate_uuid() + _set_temporary_quota('port', 1) with self.network() as network: res = self._create_port(self.fmt, network['network']['id']) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) @@ -1706,7 +1718,8 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk port create") quota = 4 - cfg.CONF.set_override('quota_port', quota, group='QUOTAS') + _set_temporary_quota('port', quota) + self._tenant_id = uuidutils.generate_uuid() with self.network() as network: res = self._create_port_bulk(self.fmt, quota + 1, network['network']['id'], @@ -2861,7 +2874,8 @@ class TestNetworksV2(NeutronDbPluginV2TestCase): def test_create_networks_native_quotas(self): quota = 1 - cfg.CONF.set_override('quota_network', quota, group='QUOTAS') + _set_temporary_quota('network', quota) + self._tenant_id = uuidutils.generate_uuid() res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) self.assertEqual(webob.exc.HTTPCreated.code, res.status_int) @@ -2873,7 +2887,8 @@ class TestNetworksV2(NeutronDbPluginV2TestCase): 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') + _set_temporary_quota('network', quota) + self._tenant_id = uuidutils.generate_uuid() res = self._create_network_bulk(self.fmt, quota + 1, 'test', True) self._validate_behavior_on_bulk_failure( res, 'networks', @@ -2883,7 +2898,8 @@ class TestNetworksV2(NeutronDbPluginV2TestCase): 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') + _set_temporary_quota('network', quota) + self._tenant_id = uuidutils.generate_uuid() networks = [{'network': {'name': 'n1', 'tenant_id': self._tenant_id}}, {'network': {'name': 'n2', @@ -2900,7 +2916,8 @@ class TestNetworksV2(NeutronDbPluginV2TestCase): 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') + _set_temporary_quota('network', quota) + self._tenant_id = uuidutils.generate_uuid() networks = [{'network': {'name': 'n1', 'tenant_id': self._tenant_id}}, {'network': {'name': 'n2', @@ -5120,7 +5137,8 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): def test_create_subnets_native_quotas(self): quota = 1 - cfg.CONF.set_override('quota_subnet', quota, group='QUOTAS') + _set_temporary_quota('subnet', quota) + self._tenant_id = uuidutils.generate_uuid() with self.network() as network: res = self._create_subnet( self.fmt, network['network']['id'], '10.0.0.0/24', @@ -5135,7 +5153,8 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk subnet create") quota = 4 - cfg.CONF.set_override('quota_subnet', quota, group='QUOTAS') + _set_temporary_quota('subnet', quota) + self._tenant_id = uuidutils.generate_uuid() with self.network() as network: res = self._create_subnet_bulk(self.fmt, quota + 1, network['network']['id'], diff --git a/neutron/tests/unit/extensions/base.py b/neutron/tests/unit/extensions/base.py index f5335e85ec4..dc8c0e17c23 100644 --- a/neutron/tests/unit/extensions/base.py +++ b/neutron/tests/unit/extensions/base.py @@ -83,7 +83,7 @@ class ExtensionTestCase(testlib_api.WebTestCase): setattr(instance, native_sorting_attr_name, True) if use_quota: quota.QUOTAS._driver = None - cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', + cfg.CONF.set_override('quota_driver', quota.QUOTA_DB_DRIVER, group='QUOTAS') setattr(instance, 'path_prefix', resource_prefix) diff --git a/neutron/tests/unit/extensions/test_l3.py b/neutron/tests/unit/extensions/test_l3.py index dac9fc141de..e76c137f1ce 100644 --- a/neutron/tests/unit/extensions/test_l3.py +++ b/neutron/tests/unit/extensions/test_l3.py @@ -751,7 +751,7 @@ class L3NatTestCaseBase(L3NatTestCaseMixin): def test_create_routers_native_quotas(self): tenant_id = _uuid() quota = 1 - cfg.CONF.set_override('quota_router', quota, group='QUOTAS') + test_db_base_plugin_v2._set_temporary_quota('router', quota) res = self._create_router(self.fmt, tenant_id) self.assertEqual(exc.HTTPCreated.code, res.status_int) res = self._create_router(self.fmt, tenant_id) @@ -3394,7 +3394,8 @@ class L3NatTestCaseBase(L3NatTestCaseMixin): def test_create_floatingips_native_quotas(self): quota = 1 - cfg.CONF.set_override('quota_floatingip', quota, group='QUOTAS') + test_db_base_plugin_v2._set_temporary_quota('floatingip', quota) + self._tenant_id = uuidutils.generate_uuid() with self.subnet() as public_sub: self._set_net_external(public_sub['subnet']['network_id']) res = self._create_floatingip( diff --git a/neutron/tests/unit/extensions/test_providernet.py b/neutron/tests/unit/extensions/test_providernet.py index 33ac310d554..1f9f68b3525 100644 --- a/neutron/tests/unit/extensions/test_providernet.py +++ b/neutron/tests/unit/extensions/test_providernet.py @@ -80,7 +80,7 @@ class ProvidernetExtensionTestCase(testlib_api.WebTestCase): self.api = webtest.TestApp(router.APIRouter()) quota.QUOTAS._driver = None - cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', + cfg.CONF.set_override('quota_driver', quota.QUOTA_DB_DRIVER, group='QUOTAS') def _prepare_net_data(self): diff --git a/neutron/tests/unit/extensions/test_quotasv2.py b/neutron/tests/unit/extensions/test_quotasv2.py index d37945a4445..881d0cd2115 100644 --- a/neutron/tests/unit/extensions/test_quotasv2.py +++ b/neutron/tests/unit/extensions/test_quotasv2.py @@ -88,9 +88,7 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase): def setUp(self): cfg.CONF.set_override( - 'quota_driver', - 'neutron.db.quota.driver.DbQuotaDriver', - group='QUOTAS') + 'quota_driver', quota.QUOTA_DB_DRIVER, group='QUOTAS') super(QuotaExtensionDbTestCase, self).setUp() def test_quotas_loaded_right(self): @@ -423,9 +421,7 @@ class QuotaExtensionCfgTestCase(QuotaExtensionTestCase): def setUp(self): cfg.CONF.set_override( - 'quota_driver', - 'neutron.quota.ConfDriver', - group='QUOTAS') + 'quota_driver', quota.QUOTA_DB_DRIVER, group='QUOTAS') super(QuotaExtensionCfgTestCase, self).setUp() def test_quotas_default_values(self): @@ -466,7 +462,7 @@ class QuotaExtensionCfgTestCase(QuotaExtensionTestCase): res = self.api.put(_get_path('quotas', id=tenant_id, fmt=self.fmt), self.serialize(quotas), expect_errors=True) - self.assertEqual(403, res.status_int) + self.assertEqual(200, res.status_int) def test_delete_quotas_forbidden(self): tenant_id = 'tenant_id1' @@ -518,11 +514,8 @@ class TestQuotaDriverLoad(base.BaseTestCase): self.assertEqual(loaded_driver, driver.__class__.__name__) def test_quota_driver_load(self): - for klass in (quota.ConfDriver, driver.DbQuotaDriver, + for klass in (driver.DbQuotaDriver, driver_nolock.DbQuotaNoLockDriver): self._test_quota_driver( '.'.join([klass.__module__, klass.__name__]), klass.__name__, True) - - def test_quota_driver_fallback_conf_driver(self): - self._test_quota_driver(quota.QUOTA_DB_DRIVER, 'ConfDriver', False) diff --git a/neutron/tests/unit/extensions/test_securitygroup.py b/neutron/tests/unit/extensions/test_securitygroup.py index 2ada04da99a..c41903a107f 100644 --- a/neutron/tests/unit/extensions/test_securitygroup.py +++ b/neutron/tests/unit/extensions/test_securitygroup.py @@ -1977,8 +1977,8 @@ class TestSecurityGroups(SecurityGroupDBTestCase): sgr = self._list('security-group-rules').get( 'security_group_rules') quota = len(sgr) + 1 - cfg.CONF.set_override( - 'quota_security_group_rule', quota, group='QUOTAS') + test_db_base_plugin_v2._set_temporary_quota('security_group_rule', + quota) security_group_id = sg['security_group']['id'] rule = self._build_security_group_rule( diff --git a/neutron/tests/unit/extensions/test_vlantransparent.py b/neutron/tests/unit/extensions/test_vlantransparent.py index 761918cc82a..53806ec0c01 100644 --- a/neutron/tests/unit/extensions/test_vlantransparent.py +++ b/neutron/tests/unit/extensions/test_vlantransparent.py @@ -72,7 +72,7 @@ class VlanTransparentExtensionTestCase(test_db_base_plugin_v2.TestNetworksV2): ext_mgr=ext_mgr) quota.QUOTAS._driver = None - cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', + cfg.CONF.set_override('quota_driver', quota.QUOTA_DB_DRIVER, group='QUOTAS') def test_network_create_with_vlan_transparent_attr(self): diff --git a/neutron/tests/unit/plugins/ml2/test_tracked_resources.py b/neutron/tests/unit/plugins/ml2/test_tracked_resources.py index 28ab68220b5..912b0dc143c 100644 --- a/neutron/tests/unit/plugins/ml2/test_tracked_resources.py +++ b/neutron/tests/unit/plugins/ml2/test_tracked_resources.py @@ -16,8 +16,11 @@ from neutron_lib import context from neutron_lib import fixture from oslo_utils import uuidutils +from neutron.conf import quota as quota_conf from neutron.db.quota import api as quota_db_api +from neutron import quota from neutron.tests.unit.api import test_extensions +from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.extensions import test_l3 from neutron.tests.unit.extensions import test_securitygroup from neutron.tests.unit.plugins.ml2 import base as ml2_base @@ -38,9 +41,17 @@ class BaseTestTrackedResources(test_plugin.Ml2PluginV2TestCase, def setUp(self): self.ctx = context.get_admin_context() + self.addCleanup(self._cleanup) + test_db_base_plugin_v2.NeutronDbPluginV2TestCase.quota_db_driver = ( + 'neutron.db.quota.driver.DbQuotaDriver') super(BaseTestTrackedResources, self).setUp() self._tenant_id = uuidutils.generate_uuid() + @staticmethod + def _cleanup(): + test_db_base_plugin_v2.NeutronDbPluginV2TestCase.quota_db_driver = ( + quota_conf.QUOTA_DB_DRIVER) + def _test_init(self, resource_name): quota_db_api.set_quota_usage( self.ctx, resource_name, self._tenant_id) @@ -167,10 +178,19 @@ class TestL3ResourcesEventHandler(BaseTestEventHandler, test_l3.L3NatTestCaseMixin): def setUp(self): + self.addCleanup(self._cleanup) + test_db_base_plugin_v2.NeutronDbPluginV2TestCase.quota_db_driver = ( + 'neutron.db.quota.driver.DbQuotaDriver') super(TestL3ResourcesEventHandler, self).setUp() self.useFixture(fixture.APIDefinitionFixture()) ext_mgr = test_l3.L3TestExtensionManager() self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) + quota.QUOTAS._driver = None + + @staticmethod + def _cleanup(): + test_db_base_plugin_v2.NeutronDbPluginV2TestCase.quota_db_driver = ( + quota_conf.QUOTA_DB_DRIVER) def test_create_delete_floating_ip_triggers_event(self): net = self._make_network('json', 'meh', True)