diff --git a/neutron/conf/quota.py b/neutron/conf/quota.py index 535950a1262..4a124e93b90 100644 --- a/neutron/conf/quota.py +++ b/neutron/conf/quota.py @@ -19,8 +19,13 @@ from oslo_config import cfg from neutron._i18n import _ -QUOTA_DB_MODULE = 'neutron.db.quota.driver_nolock' -QUOTA_DB_DRIVER = QUOTA_DB_MODULE + '.DbQuotaNoLockDriver' +QUOTA_DB_DIRECTORY = 'neutron.db.quota.' +QUOTA_DB_DRIVER_LEGACY = QUOTA_DB_DIRECTORY + 'driver.DbQuotaDriver' +QUOTA_DB_DRIVER_NO_LOCK = (QUOTA_DB_DIRECTORY + + 'driver_nolock.DbQuotaNoLockDriver') +QUOTA_DB_DRIVER_NULL = QUOTA_DB_DIRECTORY + 'driver_null.DbQuotaDriverNull' + +QUOTA_DB_DRIVER = QUOTA_DB_DRIVER_NO_LOCK QUOTAS_CFG_GROUP = 'QUOTAS' DEFAULT_QUOTA = -1 diff --git a/neutron/db/quota/api.py b/neutron/db/quota/api.py index 0026caa744b..dd192a6143d 100644 --- a/neutron/db/quota/api.py +++ b/neutron/db/quota/api.py @@ -16,7 +16,6 @@ import collections import datetime from neutron_lib.db import api as db_api -from neutron_lib.db import quota_api as nlib_quota_api from oslo_db import exception as db_exc from neutron.common import utils @@ -249,58 +248,3 @@ def remove_expired_reservations(context, project_id=None, timeout=None): expiring_time -= datetime.timedelta(seconds=timeout) return quota_obj.Reservation.delete_expired(context, expiring_time, project_id) - - -class NullQuotaDriver(nlib_quota_api.QuotaDriverAPI): - - @staticmethod - def get_default_quotas(context, resources, project_id): - pass - - @staticmethod - def get_project_quotas(context, resources, project_id): - pass - - @staticmethod - def get_detailed_project_quotas(context, resources, project_id): - pass - - @staticmethod - def delete_project_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 - - @staticmethod - def get_resource_usage(context, project_id, resources, resource_name): - pass - - @staticmethod - def quota_limit_check(context, project_id, resources, deltas): - pass - - @staticmethod - def get_workers(): - return [] diff --git a/neutron/db/quota/driver_null.py b/neutron/db/quota/driver_null.py new file mode 100644 index 00000000000..8acea69de15 --- /dev/null +++ b/neutron/db/quota/driver_null.py @@ -0,0 +1,71 @@ +# Copyright (c) 2022 Red Hat, Inc. +# 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_lib.db import quota_api as nlib_quota_api + + +class DbQuotaDriverNull(nlib_quota_api.QuotaDriverAPI): + + @staticmethod + def get_default_quotas(context, resources, project_id): + return {} + + @staticmethod + def get_project_quotas(context, resources, project_id): + return {} + + @staticmethod + def get_detailed_project_quotas(context, resources, project_id): + return {} + + @staticmethod + def delete_project_quota(context, project_id): + pass + + @staticmethod + def get_all_quotas(context, resources): + return [] + + @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 + + @staticmethod + def get_resource_usage(context, project_id, resources, resource_name): + return 0 + + @staticmethod + def quota_limit_check(context, project_id, resources, deltas): + pass + + @staticmethod + def get_workers(): + return [] diff --git a/neutron/quota/__init__.py b/neutron/quota/__init__.py index efe6f0a4e8d..2ad86232bbc 100644 --- a/neutron/quota/__init__.py +++ b/neutron/quota/__init__.py @@ -21,8 +21,6 @@ from neutron.conf import quota from neutron.quota import resource_registry LOG = logging.getLogger(__name__) -QUOTA_DB_MODULE = quota.QUOTA_DB_MODULE -QUOTA_DB_DRIVER = quota.QUOTA_DB_DRIVER # Register the configuration options diff --git a/neutron/tests/fullstack/resources/config.py b/neutron/tests/fullstack/resources/config.py index 77c70ba0996..42285e31607 100644 --- a/neutron/tests/fullstack/resources/config.py +++ b/neutron/tests/fullstack/resources/config.py @@ -94,6 +94,9 @@ class NeutronConfigFixture(ConfigFixture): 'report_interval': str(env_desc.agent_down_time // 2), 'log_agent_heartbeats': 'True', }, + 'quotas': { + 'quota_driver': env_desc.quota_driver + }, }) if use_local_apipaste: diff --git a/neutron/tests/fullstack/resources/environment.py b/neutron/tests/fullstack/resources/environment.py index 5c7136695a5..ce8c7a6495a 100644 --- a/neutron/tests/fullstack/resources/environment.py +++ b/neutron/tests/fullstack/resources/environment.py @@ -19,6 +19,7 @@ from oslo_config import cfg from neutron.agent.linux import ip_lib from neutron.common import utils as common_utils +from neutron.conf import quota as quota_conf from neutron.plugins.ml2.drivers.linuxbridge.agent import \ linuxbridge_neutron_agent as lb_agent from neutron.tests.common.exclusive_resources import ip_address @@ -42,7 +43,8 @@ class EnvironmentDescription(object): has_placement=False, placement_port=None, dhcp_scheduler_class=None, ml2_extension_drivers=None, api_workers=1, - enable_traditional_dhcp=True, local_ip_ext=False): + enable_traditional_dhcp=True, local_ip_ext=False, + quota_driver=quota_conf.QUOTA_DB_DRIVER): self.network_type = network_type self.l2_pop = l2_pop self.qos = qos @@ -69,6 +71,7 @@ class EnvironmentDescription(object): self.local_ip_ext = local_ip_ext if self.local_ip_ext: self.service_plugins += ',local_ip' + self.quota_driver = quota_driver @property def tunneling_enabled(self): diff --git a/neutron/tests/fullstack/test_quota.py b/neutron/tests/fullstack/test_quota.py new file mode 100644 index 00000000000..456fa9ad672 --- /dev/null +++ b/neutron/tests/fullstack/test_quota.py @@ -0,0 +1,51 @@ +# Copyright (c) 2022 Red Hat, Inc. +# 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 oslo_utils import uuidutils + +from neutron.conf import quota as quota_conf +from neutron.tests.fullstack import base +from neutron.tests.fullstack.resources import environment +from neutron.tests.unit import testlib_api + +load_tests = testlib_api.module_load_tests + + +class TestQuota(base.BaseFullStackTestCase): + + scenarios = [ + ('DbQuotaDriver', + {'quota_driver': quota_conf.QUOTA_DB_DRIVER_LEGACY}), + ('DbQuotaNoLockDriver', + {'quota_driver': quota_conf.QUOTA_DB_DRIVER_NO_LOCK}), + ('DbQuotaDriverNull', + {'quota_driver': quota_conf.QUOTA_DB_DRIVER_NULL}), + ] + + def setUp(self, *args): + host_descriptions = [environment.HostDescription()] + env = environment.Environment(environment.EnvironmentDescription( + quota_driver=self.quota_driver), host_descriptions) + super().setUp(env) + self.tenant_id = uuidutils.generate_uuid() + + def test_create_network_and_port(self): + network = self.safe_client.create_network(self.tenant_id) + self.safe_client.create_subnet(self.tenant_id, network['id'], + '20.0.0.0/24') + port = self.safe_client.create_port(self.tenant_id, network['id']) + port_id = port['id'] + port = self.safe_client.client.list_ports(id=port_id)['ports'][0] + self.assertEqual(port_id, port['id']) diff --git a/neutron/tests/unit/api/test_extensions.py b/neutron/tests/unit/api/test_extensions.py index 56bd169ceb0..f542028349c 100644 --- a/neutron/tests/unit/api/test_extensions.py +++ b/neutron/tests/unit/api/test_extensions.py @@ -1033,7 +1033,7 @@ class ExtensionExtendedAttributeTestCase(base.BaseTestCase): quota.QUOTAS._driver = None cfg.CONF.set_override('quota_driver', - 'neutron.db.quota.api.NullQuotaDriver', + 'neutron.db.quota.driver_null.DbQuotaDriverNull', 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 99aeb01b47b..4d182cd9842 100644 --- a/neutron/tests/unit/api/v2/test_base.py +++ b/neutron/tests/unit/api/v2/test_base.py @@ -40,6 +40,7 @@ from neutron.api import api_common from neutron.api import extensions from neutron.api.v2 import base as v2_base from neutron.api.v2 import router +from neutron.conf import quota as quota_conf from neutron import policy from neutron import quota from neutron.tests import base @@ -49,7 +50,7 @@ from neutron.tests.unit import testlib_api EXTDIR = os.path.join(base.ROOTDIR, 'unit/extensions') -NULL_QUOTA_DRIVER = 'neutron.db.quota.api.NullQuotaDriver' +NULL_QUOTA_DRIVER = 'neutron.db.quota.driver_null.DbQuotaDriverNull' _uuid = uuidutils.generate_uuid @@ -98,7 +99,7 @@ class APIv2TestBase(base.BaseTestCase): self.api = webtest.TestApp(api) quota.QUOTAS._driver = None - cfg.CONF.set_override('quota_driver', quota.QUOTA_DB_DRIVER, + cfg.CONF.set_override('quota_driver', quota_conf.QUOTA_DB_DRIVER, group='QUOTAS') # APIRouter initialization resets policy module, re-initializing it diff --git a/neutron/tests/unit/extensions/base.py b/neutron/tests/unit/extensions/base.py index dc8c0e17c23..b54365a00c7 100644 --- a/neutron/tests/unit/extensions/base.py +++ b/neutron/tests/unit/extensions/base.py @@ -25,6 +25,7 @@ from webob import exc import webtest from neutron.api import extensions +from neutron.conf import quota as quota_conf from neutron import manager from neutron import quota from neutron.tests.unit.api import test_extensions @@ -83,7 +84,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', quota.QUOTA_DB_DRIVER, + cfg.CONF.set_override('quota_driver', quota_conf.QUOTA_DB_DRIVER, group='QUOTAS') setattr(instance, 'path_prefix', resource_prefix) diff --git a/neutron/tests/unit/extensions/test_providernet.py b/neutron/tests/unit/extensions/test_providernet.py index 1f9f68b3525..61e1422aa2d 100644 --- a/neutron/tests/unit/extensions/test_providernet.py +++ b/neutron/tests/unit/extensions/test_providernet.py @@ -27,6 +27,7 @@ import webtest from neutron.api import extensions from neutron.api.v2 import router +from neutron.conf import quota as quota_conf from neutron.extensions import providernet as pnet from neutron import quota from neutron.tests import tools @@ -80,7 +81,7 @@ class ProvidernetExtensionTestCase(testlib_api.WebTestCase): self.api = webtest.TestApp(router.APIRouter()) quota.QUOTAS._driver = None - cfg.CONF.set_override('quota_driver', quota.QUOTA_DB_DRIVER, + cfg.CONF.set_override('quota_driver', quota_conf.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 c70ba5bb2b2..fab52f8be60 100644 --- a/neutron/tests/unit/extensions/test_quotasv2.py +++ b/neutron/tests/unit/extensions/test_quotasv2.py @@ -31,6 +31,7 @@ from neutron.common import config from neutron.conf import quota as qconf from neutron.db.quota import driver from neutron.db.quota import driver_nolock +from neutron.db.quota import driver_null from neutron import quota from neutron.quota import resource_registry from neutron.tests import base @@ -88,7 +89,7 @@ class QuotaExtensionDbTestCase(QuotaExtensionTestCase): def setUp(self): cfg.CONF.set_override( - 'quota_driver', quota.QUOTA_DB_DRIVER, group='QUOTAS') + 'quota_driver', qconf.QUOTA_DB_DRIVER, group='QUOTAS') super(QuotaExtensionDbTestCase, self).setUp() def test_quotas_loaded_right(self): @@ -443,7 +444,7 @@ class QuotaExtensionCfgTestCase(QuotaExtensionTestCase): def setUp(self): cfg.CONF.set_override( - 'quota_driver', quota.QUOTA_DB_DRIVER, group='QUOTAS') + 'quota_driver', qconf.QUOTA_DB_DRIVER, group='QUOTAS') super(QuotaExtensionCfgTestCase, self).setUp() def test_quotas_default_values(self): @@ -524,20 +525,24 @@ class TestDbQuotaDriver(base.BaseTestCase): class TestQuotaDriverLoad(base.BaseTestCase): - def _test_quota_driver(self, cfg_driver, loaded_driver, - with_quota_db_module=True): + MODULE_CLASS = [ + (qconf.QUOTA_DB_DRIVER_LEGACY, driver.DbQuotaDriver), + (qconf.QUOTA_DB_DRIVER_NO_LOCK, driver_nolock.DbQuotaNoLockDriver), + (qconf.QUOTA_DB_DRIVER_NULL, driver_null.DbQuotaDriverNull), + ] + + def _test_quota_driver(self, module, cfg_driver, loaded_driver): quota.QUOTAS._driver = None cfg.CONF.set_override('quota_driver', cfg_driver, group='QUOTAS') with mock.patch.dict(sys.modules, {}): - if (not with_quota_db_module and - quota.QUOTA_DB_MODULE in sys.modules): + if module in sys.modules: del sys.modules[quota.QUOTA_DB_MODULE] driver = quota.QUOTAS.get_driver() self.assertEqual(loaded_driver, driver.__class__.__name__) def test_quota_driver_load(self): - for klass in (driver.DbQuotaDriver, - driver_nolock.DbQuotaNoLockDriver): + for module, klass in self.MODULE_CLASS: self._test_quota_driver( + module, '.'.join([klass.__module__, klass.__name__]), - klass.__name__, True) + klass.__name__) diff --git a/neutron/tests/unit/extensions/test_vlantransparent.py b/neutron/tests/unit/extensions/test_vlantransparent.py index 53806ec0c01..e2beb38f23c 100644 --- a/neutron/tests/unit/extensions/test_vlantransparent.py +++ b/neutron/tests/unit/extensions/test_vlantransparent.py @@ -17,6 +17,7 @@ from neutron_lib.db import api as db_api from oslo_config import cfg from webob import exc as web_exc +from neutron.conf import quota as quota_conf from neutron.db import db_base_plugin_v2 from neutron.db import vlantransparent_db as vlt_db from neutron.extensions import vlantransparent as vlt @@ -72,7 +73,7 @@ class VlanTransparentExtensionTestCase(test_db_base_plugin_v2.TestNetworksV2): ext_mgr=ext_mgr) quota.QUOTAS._driver = None - cfg.CONF.set_override('quota_driver', quota.QUOTA_DB_DRIVER, + cfg.CONF.set_override('quota_driver', quota_conf.QUOTA_DB_DRIVER, group='QUOTAS') def test_network_create_with_vlan_transparent_attr(self): diff --git a/releasenotes/notes/quota_null_driver-d04af65c237e4b12.yaml b/releasenotes/notes/quota_null_driver-d04af65c237e4b12.yaml new file mode 100644 index 00000000000..9edbe5498d8 --- /dev/null +++ b/releasenotes/notes/quota_null_driver-d04af65c237e4b12.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Enabled ``DbQuotaDriverNull`` as production ready database quota driver. + This driver does not have access to the database and will return empty + values to the request queries. This driver can be used to override the + Neutron quota engine.