diff --git a/etc/neutron.conf b/etc/neutron.conf index f5a6da62767..ca3baa9cf32 100755 --- a/etc/neutron.conf +++ b/etc/neutron.conf @@ -593,7 +593,7 @@ [quotas] # Default driver to use for quota checks -# quota_driver = neutron.db.quota_db.DbQuotaDriver +# quota_driver = neutron.db.quota.driver.DbQuotaDriver # Resource name(s) that are supported in quota features # This option is deprecated for removal in the M release, please refrain from using it diff --git a/neutron/db/quota/driver.py b/neutron/db/quota/driver.py new file mode 100644 index 00000000000..cf6031ae2d8 --- /dev/null +++ b/neutron/db/quota/driver.py @@ -0,0 +1,151 @@ +# Copyright 2011 OpenStack Foundation. +# 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 neutron.common import exceptions +from neutron.db.quota import models as quota_models + + +class DbQuotaDriver(object): + """Driver to perform necessary checks to enforce quotas and obtain quota + information. + + The default driver utilizes the local database. + """ + + @staticmethod + def get_tenant_quotas(context, resources, tenant_id): + """Given a list of resources, retrieve the quotas for the given + tenant. + + :param context: The request context, for access checks. + :param resources: A dictionary of the registered resource keys. + :param tenant_id: The ID of the tenant to return quotas for. + :return dict: from resource name to dict of name and limit + """ + + # init with defaults + tenant_quota = dict((key, resource.default) + for key, resource in resources.items()) + + # update with tenant specific limits + q_qry = context.session.query(quota_models.Quota).filter_by( + tenant_id=tenant_id) + tenant_quota.update((q['resource'], q['limit']) for q in q_qry) + + return tenant_quota + + @staticmethod + def delete_tenant_quota(context, tenant_id): + """Delete the quota entries for a given tenant_id. + + Atfer deletion, this tenant will use default quota values in conf. + """ + with context.session.begin(): + tenant_quotas = context.session.query(quota_models.Quota) + tenant_quotas = tenant_quotas.filter_by(tenant_id=tenant_id) + tenant_quotas.delete() + + @staticmethod + 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 tenant_id:, resourcekey1: + resourcekey2: ... + """ + tenant_default = dict((key, resource.default) + for key, resource in resources.items()) + + all_tenant_quotas = {} + + for quota in context.session.query(quota_models.Quota): + tenant_id = quota['tenant_id'] + + # avoid setdefault() because only want to copy when actually req'd + tenant_quota = all_tenant_quotas.get(tenant_id) + if tenant_quota is None: + tenant_quota = tenant_default.copy() + tenant_quota['tenant_id'] = tenant_id + all_tenant_quotas[tenant_id] = tenant_quota + + tenant_quota[quota['resource']] = quota['limit'] + + return list(all_tenant_quotas.values()) + + @staticmethod + def update_quota_limit(context, tenant_id, resource, limit): + with context.session.begin(): + tenant_quota = context.session.query(quota_models.Quota).filter_by( + tenant_id=tenant_id, resource=resource).first() + + if tenant_quota: + tenant_quota.update({'limit': limit}) + else: + tenant_quota = quota_models.Quota(tenant_id=tenant_id, + resource=resource, + limit=limit) + context.session.add(tenant_quota) + + def _get_quotas(self, context, tenant_id, resources): + """Retrieves the quotas for specific resources. + + 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 tenant_id: the tenant_id to check quota. + :param resources: A dictionary of the registered resources. + """ + # Grab and return the quotas (without usages) + quotas = DbQuotaDriver.get_tenant_quotas( + context, resources, tenant_id) + + return dict((k, v) for k, v in quotas.items()) + + 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 the 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, tenant_id, 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)) diff --git a/neutron/db/quota_db.py b/neutron/db/quota_db.py index cf6031ae2d8..1ce75aeef78 100644 --- a/neutron/db/quota_db.py +++ b/neutron/db/quota_db.py @@ -13,139 +13,10 @@ # License for the specific language governing permissions and limitations # under the License. -from neutron.common import exceptions -from neutron.db.quota import models as quota_models +import sys +from neutron.db.quota import driver # noqa -class DbQuotaDriver(object): - """Driver to perform necessary checks to enforce quotas and obtain quota - information. - - The default driver utilizes the local database. - """ - - @staticmethod - def get_tenant_quotas(context, resources, tenant_id): - """Given a list of resources, retrieve the quotas for the given - tenant. - - :param context: The request context, for access checks. - :param resources: A dictionary of the registered resource keys. - :param tenant_id: The ID of the tenant to return quotas for. - :return dict: from resource name to dict of name and limit - """ - - # init with defaults - tenant_quota = dict((key, resource.default) - for key, resource in resources.items()) - - # update with tenant specific limits - q_qry = context.session.query(quota_models.Quota).filter_by( - tenant_id=tenant_id) - tenant_quota.update((q['resource'], q['limit']) for q in q_qry) - - return tenant_quota - - @staticmethod - def delete_tenant_quota(context, tenant_id): - """Delete the quota entries for a given tenant_id. - - Atfer deletion, this tenant will use default quota values in conf. - """ - with context.session.begin(): - tenant_quotas = context.session.query(quota_models.Quota) - tenant_quotas = tenant_quotas.filter_by(tenant_id=tenant_id) - tenant_quotas.delete() - - @staticmethod - 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 tenant_id:, resourcekey1: - resourcekey2: ... - """ - tenant_default = dict((key, resource.default) - for key, resource in resources.items()) - - all_tenant_quotas = {} - - for quota in context.session.query(quota_models.Quota): - tenant_id = quota['tenant_id'] - - # avoid setdefault() because only want to copy when actually req'd - tenant_quota = all_tenant_quotas.get(tenant_id) - if tenant_quota is None: - tenant_quota = tenant_default.copy() - tenant_quota['tenant_id'] = tenant_id - all_tenant_quotas[tenant_id] = tenant_quota - - tenant_quota[quota['resource']] = quota['limit'] - - return list(all_tenant_quotas.values()) - - @staticmethod - def update_quota_limit(context, tenant_id, resource, limit): - with context.session.begin(): - tenant_quota = context.session.query(quota_models.Quota).filter_by( - tenant_id=tenant_id, resource=resource).first() - - if tenant_quota: - tenant_quota.update({'limit': limit}) - else: - tenant_quota = quota_models.Quota(tenant_id=tenant_id, - resource=resource, - limit=limit) - context.session.add(tenant_quota) - - def _get_quotas(self, context, tenant_id, resources): - """Retrieves the quotas for specific resources. - - 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 tenant_id: the tenant_id to check quota. - :param resources: A dictionary of the registered resources. - """ - # Grab and return the quotas (without usages) - quotas = DbQuotaDriver.get_tenant_quotas( - context, resources, tenant_id) - - return dict((k, v) for k, v in quotas.items()) - - 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 the 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, tenant_id, 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)) +# This module has been preserved for backward compatibility, and will be +# deprecated in the future +sys.modules[__name__] = sys.modules['neutron.db.quota.driver'] diff --git a/neutron/extensions/quotasv2.py b/neutron/extensions/quotasv2.py index cfe94c05229..a47a1adb98c 100644 --- a/neutron/extensions/quotasv2.py +++ b/neutron/extensions/quotasv2.py @@ -31,7 +31,7 @@ from neutron import wsgi RESOURCE_NAME = 'quota' RESOURCE_COLLECTION = RESOURCE_NAME + "s" QUOTAS = quota.QUOTAS -DB_QUOTA_DRIVER = 'neutron.db.quota_db.DbQuotaDriver' +DB_QUOTA_DRIVER = 'neutron.db.quota.driver.DbQuotaDriver' EXTENDED_ATTRIBUTES_2_0 = { RESOURCE_COLLECTION: {} } diff --git a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py b/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py index 63d37b79c85..31953df53db 100644 --- a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py +++ b/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py @@ -32,7 +32,7 @@ from neutron.db import agentschedulers_db from neutron.db import db_base_plugin_v2 from neutron.db import external_net_db from neutron.db import portbindings_db -from neutron.db import quota_db +from neutron.db.quota import driver from neutron.extensions import portbindings from neutron.extensions import providernet from neutron.i18n import _LW @@ -59,7 +59,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, n1kv_db_v2.PolicyProfile_db_mixin, network_db_v2.Credential_db_mixin, agentschedulers_db.DhcpAgentSchedulerDbMixin, - quota_db.DbQuotaDriver): + driver.DbQuotaDriver): """ Implement the Neutron abstractions using Cisco Nexus1000V. diff --git a/neutron/plugins/ibm/sdnve_neutron_plugin.py b/neutron/plugins/ibm/sdnve_neutron_plugin.py index 2c272250e91..ac4ae1a3bc6 100644 --- a/neutron/plugins/ibm/sdnve_neutron_plugin.py +++ b/neutron/plugins/ibm/sdnve_neutron_plugin.py @@ -31,7 +31,6 @@ from neutron.db import db_base_plugin_v2 from neutron.db import external_net_db from neutron.db import l3_gwmode_db from neutron.db import portbindings_db -from neutron.db import quota_db # noqa from neutron.extensions import portbindings from neutron.i18n import _LE, _LI, _LW from neutron.plugins.ibm.common import config # noqa diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index de1ee1199a5..9a1d5a84eac 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -54,7 +54,7 @@ from neutron.db import external_net_db from neutron.db import extradhcpopt_db from neutron.db import models_v2 from neutron.db import netmtu_db -from neutron.db import quota_db # noqa +from neutron.db.quota import driver # noqa from neutron.db import securitygroups_rpc_base as sg_db_rpc from neutron.db import vlantransparent_db from neutron.extensions import allowedaddresspairs as addr_pair diff --git a/neutron/plugins/oneconvergence/plugin.py b/neutron/plugins/oneconvergence/plugin.py index f0295cb7701..d3150f7ea70 100644 --- a/neutron/plugins/oneconvergence/plugin.py +++ b/neutron/plugins/oneconvergence/plugin.py @@ -39,7 +39,6 @@ from neutron.db import extraroute_db from neutron.db import l3_agentschedulers_db from neutron.db import l3_gwmode_db from neutron.db import portbindings_base -from neutron.db import quota_db # noqa from neutron.db import securitygroups_rpc_base as sg_db_rpc from neutron.extensions import portbindings from neutron.i18n import _LE diff --git a/neutron/quota.py b/neutron/quota/__init__.py similarity index 98% rename from neutron/quota.py rename to neutron/quota/__init__.py index e99a01ecdde..f71b14aabe1 100644 --- a/neutron/quota.py +++ b/neutron/quota/__init__.py @@ -28,8 +28,8 @@ from neutron.i18n import _LI, _LW LOG = logging.getLogger(__name__) -QUOTA_DB_MODULE = 'neutron.db.quota_db' -QUOTA_DB_DRIVER = 'neutron.db.quota_db.DbQuotaDriver' +QUOTA_DB_MODULE = 'neutron.db.quota.driver' +QUOTA_DB_DRIVER = '%s.DbQuotaDriver' % QUOTA_DB_MODULE QUOTA_CONF_DRIVER = 'neutron.quota.ConfDriver' default_quota_items = ['network', 'subnet', 'port'] @@ -226,8 +226,8 @@ class QuotaEngine(object): versionutils.report_deprecated_feature( LOG, _LW("The quota driver neutron.quota.ConfDriver is " "deprecated as of Liberty. " - "neutron.db.quota_db.DbQuotaDriver should be " - "used in its place")) + "neutron.db.quota.driver.DbQuotaDriver should " + "be used in its place")) self._driver = _driver_class LOG.info(_LI('Loaded quota_driver: %s.'), _driver_class) return self._driver diff --git a/neutron/tests/api/admin/test_quotas.py b/neutron/tests/api/admin/test_quotas.py index 0dfe7987584..f29d438c787 100644 --- a/neutron/tests/api/admin/test_quotas.py +++ b/neutron/tests/api/admin/test_quotas.py @@ -35,7 +35,7 @@ class QuotasTest(base.BaseAdminNetworkTest): It is also assumed that the per-tenant quota extension API is configured in /etc/neutron/neutron.conf as follows: - quota_driver = neutron.db.quota_db.DbQuotaDriver + quota_driver = neutron.db.driver.DbQuotaDriver """ @classmethod diff --git a/neutron/tests/unit/db/test_quota_db.py b/neutron/tests/unit/db/quota/test_driver.py similarity index 97% rename from neutron/tests/unit/db/test_quota_db.py rename to neutron/tests/unit/db/quota/test_driver.py index b6ba3f52016..31a741721ce 100644 --- a/neutron/tests/unit/db/test_quota_db.py +++ b/neutron/tests/unit/db/quota/test_driver.py @@ -16,11 +16,11 @@ from neutron.common import exceptions from neutron import context from neutron.db import db_base_plugin_v2 as base_plugin -from neutron.db import quota_db +from neutron.db.quota import driver from neutron.tests.unit import testlib_api -class FakePlugin(base_plugin.NeutronDbPluginV2, quota_db.DbQuotaDriver): +class FakePlugin(base_plugin.NeutronDbPluginV2, driver.DbQuotaDriver): """A fake plugin class containing all DB methods.""" diff --git a/neutron/tests/unit/extensions/test_quotasv2.py b/neutron/tests/unit/extensions/test_quotasv2.py index 6f8fd6b0a2a..bf1ac304cae 100644 --- a/neutron/tests/unit/extensions/test_quotasv2.py +++ b/neutron/tests/unit/extensions/test_quotasv2.py @@ -27,7 +27,7 @@ from neutron.common import config from neutron.common import constants from neutron.common import exceptions from neutron import context -from neutron.db import quota_db +from neutron.db.quota import driver from neutron import quota from neutron.tests import base from neutron.tests import tools @@ -95,7 +95,7 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase): def setUp(self): cfg.CONF.set_override( 'quota_driver', - 'neutron.db.quota_db.DbQuotaDriver', + 'neutron.db.quota.driver.DbQuotaDriver', group='QUOTAS') super(QuotaExtensionDbTestCase, self).setUp() @@ -404,25 +404,25 @@ class QuotaExtensionCfgTestCase(QuotaExtensionTestCase): class TestDbQuotaDriver(base.BaseTestCase): - """Test for neutron.db.quota_db.DbQuotaDriver.""" + """Test for neutron.db.quota.driver.DbQuotaDriver.""" def test_get_tenant_quotas_arg(self): - """Call neutron.db.quota_db.DbQuotaDriver._get_quotas.""" + """Call neutron.db.quota.driver.DbQuotaDriver._get_quotas.""" - driver = quota_db.DbQuotaDriver() + quota_driver = driver.DbQuotaDriver() ctx = context.Context('', 'bar') foo_quotas = {'network': 5} default_quotas = {'network': 10} target_tenant = 'foo' - with mock.patch.object(quota_db.DbQuotaDriver, + with mock.patch.object(driver.DbQuotaDriver, 'get_tenant_quotas', return_value=foo_quotas) as get_tenant_quotas: - quotas = driver._get_quotas(ctx, - target_tenant, - default_quotas) + quotas = quota_driver._get_quotas(ctx, + target_tenant, + default_quotas) self.assertEqual(quotas, foo_quotas) get_tenant_quotas.assert_called_once_with(ctx, @@ -441,17 +441,17 @@ class TestQuotaDriverLoad(base.BaseTestCase): cfg.CONF.set_override('quota_driver', cfg_driver, group='QUOTAS') with mock.patch.dict(sys.modules, {}): if (not with_quota_db_module and - 'neutron.db.quota_db' in sys.modules): - del sys.modules['neutron.db.quota_db'] + 'neutron.db.quota.driver' in sys.modules): + del sys.modules['neutron.db.quota.driver'] driver = quota.QUOTAS.get_driver() self.assertEqual(loaded_driver, driver.__class__.__name__) def test_quota_db_driver_with_quotas_table(self): - self._test_quota_driver('neutron.db.quota_db.DbQuotaDriver', + self._test_quota_driver('neutron.db.quota.driver.DbQuotaDriver', 'DbQuotaDriver', True) def test_quota_db_driver_fallback_conf_driver(self): - self._test_quota_driver('neutron.db.quota_db.DbQuotaDriver', + self._test_quota_driver('neutron.db.quota.driver.DbQuotaDriver', 'ConfDriver', False) def test_quota_conf_driver(self): diff --git a/tox.ini b/tox.ini index e9223ef5f89..1f274f99c08 100644 --- a/tox.ini +++ b/tox.ini @@ -160,7 +160,7 @@ commands = python -m testtools.run \ neutron.tests.unit.db.test_l3_hamode_db \ neutron.tests.unit.db.test_migration \ neutron.tests.unit.db.test_agents_db \ - neutron.tests.unit.db.test_quota_db \ + neutron.tests.unit.db.quota.test_driver \ neutron.tests.unit.db.test_dvr_mac_db \ neutron.tests.unit.debug.test_commands \ neutron.tests.unit.tests.test_post_mortem_debug \