diff --git a/neutron/db/quota/driver.py b/neutron/db/quota/driver.py index 78e2923ad02..b9fa55e9229 100644 --- a/neutron/db/quota/driver.py +++ b/neutron/db/quota/driver.py @@ -76,9 +76,8 @@ class DbQuotaDriver(nlib_quota_api.QuotaDriverAPI): return project_quota - @staticmethod @db_api.retry_if_session_inactive() - def get_detailed_project_quotas(context, resources, project_id): + def get_detailed_project_quotas(self, context, resources, project_id): """Given a list of resources and a specific project, retrieve the detailed quotas (limit, used, reserved). :param context: The request context, for access checks. @@ -92,8 +91,7 @@ class DbQuotaDriver(nlib_quota_api.QuotaDriverAPI): project_quota_ext = {} for key, resource in resources.items(): if isinstance(resource, res.TrackedResource): - used = resource.count_used(context, project_id, - resync_usage=False) + used = self.get_resource_count(context, project_id, resource) else: # NOTE(ihrachys) .count won't use the plugin we pass, but we # pass it regardless to keep the quota driver API intact @@ -319,6 +317,11 @@ class DbQuotaDriver(nlib_quota_api.QuotaDriverAPI): return tracked_resource.count(context, None, project_id, resync_usage=False) + @staticmethod + def get_resource_count(context, project_id, tracked_resource): + return tracked_resource.count_used(context, project_id, + resync_usage=False) + def quota_limit_check(self, context, project_id, resources, deltas): # Ensure no value is less than zero unders = [key for key, val in deltas.items() if val < 0] diff --git a/neutron/db/quota/driver_nolock.py b/neutron/db/quota/driver_nolock.py index d89d73c9538..220ea78d985 100644 --- a/neutron/db/quota/driver_nolock.py +++ b/neutron/db/quota/driver_nolock.py @@ -92,6 +92,10 @@ class DbQuotaNoLockDriver(quota_driver.DbQuotaDriver): return tracked_resource.count(context, None, project_id, count_db_registers=True) + @staticmethod + def get_resource_count(context, project_id, tracked_resource): + return tracked_resource.count_db_registers(context, project_id) + @staticmethod def get_workers(): interval = quota_api.RESERVATION_EXPIRATION_TIMEOUT diff --git a/neutron/extensions/quotasv2.py b/neutron/extensions/quotasv2.py index f84eae0e37c..fa32d9286eb 100644 --- a/neutron/extensions/quotasv2.py +++ b/neutron/extensions/quotasv2.py @@ -60,9 +60,7 @@ class QuotaSetsController(wsgi.Controller): def __init__(self, plugin): self._resource_name = RESOURCE_NAME self._plugin = plugin - self._driver = importutils.import_class( - cfg.CONF.QUOTAS.quota_driver - ) + self._driver = importutils.import_class(cfg.CONF.QUOTAS.quota_driver)() self._update_extended_attributes = True def _update_attributes(self): diff --git a/neutron/quota/resource.py b/neutron/quota/resource.py index 8db626161da..258f018120d 100644 --- a/neutron/quota/resource.py +++ b/neutron/quota/resource.py @@ -333,13 +333,13 @@ class TrackedResource(BaseResource): CountableResource instances. """ if count_db_registers: - count = self._count_db_registers(context, project_id) + count = self.count_db_registers(context, project_id) else: count = self.count_used(context, project_id, resync_usage) return count + self.count_reserved(context, project_id) - def _count_db_registers(self, context, project_id): + def count_db_registers(self, context, project_id): """Return the existing resources (self._model_class) in a project. The query executed must be as fast as possible. To avoid retrieving all diff --git a/neutron/tests/unit/db/quota/test_driver_nolock.py b/neutron/tests/unit/db/quota/test_driver_nolock.py index 4ea1e8deb30..0849cd43c60 100644 --- a/neutron/tests/unit/db/quota/test_driver_nolock.py +++ b/neutron/tests/unit/db/quota/test_driver_nolock.py @@ -15,18 +15,33 @@ import itertools +import netaddr +from neutron_lib import constants +from neutron_lib import context from neutron_lib.db import api as db_api +from neutron.db import db_base_plugin_v2 as base_plugin +from neutron.db import models_v2 from neutron.db.quota import api as quota_api from neutron.db.quota import driver_nolock +from neutron.objects import network as network_obj +from neutron.objects import ports as port_obj from neutron.objects import quota as quota_obj +from neutron.objects import subnet as subnet_obj +from neutron.quota import resource as quota_resource from neutron.tests.unit.db.quota import test_driver +class FakePlugin(base_plugin.NeutronDbPluginV2, + driver_nolock.DbQuotaNoLockDriver): + """A fake plugin class containing all DB methods.""" + + class TestDbQuotaDriverNoLock(test_driver.TestDbQuotaDriver): def setUp(self): super(TestDbQuotaDriverNoLock, self).setUp() + self.plugin = FakePlugin() self.quota_driver = driver_nolock.DbQuotaNoLockDriver() @staticmethod @@ -60,3 +75,66 @@ class TestDbQuotaDriverNoLock(test_driver.TestDbQuotaDriver): self.quota_driver._remove_expired_reservations() res = quota_obj.Reservation.get_objects(self.context) self.assertEqual([], res) + + def test_get_detailed_project_quotas_resource(self): + user_ctx = context.Context(user_id=self.project_1, + tenant_id=self.project_1) + tracked_resource = quota_resource.TrackedResource( + 'network', models_v2.Network, 'quota_network') + res = {'network': tracked_resource} + self.plugin.update_quota_limit(user_ctx, self.project_1, 'network', 20) + self.quota_driver.make_reservation(user_ctx, self.project_1, res, + {'network': 5}, self.plugin) + with db_api.CONTEXT_WRITER.using(user_ctx): + network_obj.Network(user_ctx, project_id=self.project_1).create() + + detailed_quota = self.plugin.get_detailed_project_quotas( + user_ctx, res, self.project_1) + reference = {'network': {'limit': 20, 'used': 1, 'reserved': 5}} + self.assertEqual(reference, detailed_quota) + + @staticmethod + def _create_tracked_resources(): + return { + 'network': quota_resource.TrackedResource( + 'network', models_v2.Network, 'quota_network'), + 'subnet': quota_resource.TrackedResource( + 'subnet', models_v2.Subnet, 'quota_subnet'), + 'port': quota_resource.TrackedResource( + 'port', models_v2.Port, 'quota_port'), + } + + def test_get_detailed_project_quotas_multiple_resource(self): + resources = self._create_tracked_resources() + for project_id in self.projects: + user_ctx = context.Context(user_id=project_id, + tenant_id=project_id) + self.plugin.update_quota_limit( + user_ctx, project_id, 'network', 101) + self.plugin.update_quota_limit(user_ctx, project_id, 'subnet', 102) + self.plugin.update_quota_limit(user_ctx, project_id, 'port', 103) + + with db_api.CONTEXT_WRITER.using(user_ctx): + net = network_obj.Network( + user_ctx, project_id=project_id) + net.create() + subnet_obj.Subnet( + user_ctx, project_id=project_id, network_id=net.id, + ip_version=constants.IP_VERSION_4, + cidr=netaddr.IPNetwork('1.2.3.0/24')).create() + port_obj.Port( + user_ctx, project_id=project_id, + network_id=net.id, + mac_address=netaddr.EUI('ca:fe:ca:fe:ca:fe'), + admin_state_up=False, status='DOWN', device_id='', + device_owner='').create() + + reference = {'network': {'limit': 101, 'used': 1, 'reserved': 0}, + 'subnet': {'limit': 102, 'used': 1, 'reserved': 0}, + 'port': {'limit': 103, 'used': 1, 'reserved': 0}} + for project_id in self.projects: + user_ctx = context.Context(user_id=project_id, + tenant_id=project_id) + returned = self.plugin.get_detailed_project_quotas( + user_ctx, resources, project_id) + self.assertEqual(reference, returned)