From 8ccbbb229276c62de4799810571b222d9bc046f5 Mon Sep 17 00:00:00 2001 From: Jakub Libosvar Date: Fri, 4 Feb 2022 10:15:12 -0500 Subject: [PATCH] [quota] Enable ``DbQuotaDriverNull`` as a production driver Enabled ``DbQuotaDriverNull`` as a productio quota database quota driver. This driver does not enforce any quota nor have access to the database. When using this quota driver, the API will return the default empty values expected from the ``QuotaDriverAPI`` class. Closes-bug: #1960032 Change-Id: Iafa24753e657746a8b8165b5a63c17de9a9ba791 Signed-off-by: Jakub Libosvar Co-Authored-By: Rodolfo Alonso Hernandez --- neutron/conf/quota.py | 9 ++- neutron/db/quota/api.py | 56 --------------- neutron/db/quota/driver_null.py | 71 +++++++++++++++++++ neutron/quota/__init__.py | 2 - neutron/tests/fullstack/resources/config.py | 3 + .../tests/fullstack/resources/environment.py | 5 +- neutron/tests/fullstack/test_quota.py | 51 +++++++++++++ neutron/tests/unit/api/test_extensions.py | 2 +- neutron/tests/unit/api/v2/test_base.py | 5 +- neutron/tests/unit/extensions/base.py | 3 +- .../tests/unit/extensions/test_providernet.py | 3 +- .../tests/unit/extensions/test_quotasv2.py | 23 +++--- .../unit/extensions/test_vlantransparent.py | 3 +- .../quota_null_driver-d04af65c237e4b12.yaml | 7 ++ 14 files changed, 167 insertions(+), 76 deletions(-) create mode 100644 neutron/db/quota/driver_null.py create mode 100644 neutron/tests/fullstack/test_quota.py create mode 100644 releasenotes/notes/quota_null_driver-d04af65c237e4b12.yaml 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.