Implement specific tracked resource count method per quota driver
This patch implements a new method specific for each quota driver class. This method, "get_resource_count", returns the current number of resources created in a project of a tracked resource. A tracked resource is an instance of ``neutron.quota.resource.TrackedResource``. This method does not count the current reservations, just the actual resources created. This new method, "get_resource_count", will be added to the abstract class ``neutron_lib.db.quota_api.QuotaDriverAPI``. This patch also fixes ``TestDbQuotaDriverNoLock``, that was using a plugin inheriting from ``DbQuotaDriver`` instead of ``DbQuotaNoLockDriver``. Closes-Bug: #1982962 Change-Id: I2707506468cb60d93a4459ea364f1e79faa83838
This commit is contained in:
parent
1b9e9a6c2c
commit
bd60f0833b
@ -76,9 +76,8 @@ class DbQuotaDriver(nlib_quota_api.QuotaDriverAPI):
|
|||||||
|
|
||||||
return project_quota
|
return project_quota
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@db_api.retry_if_session_inactive()
|
@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
|
"""Given a list of resources and a specific project, retrieve
|
||||||
the detailed quotas (limit, used, reserved).
|
the detailed quotas (limit, used, reserved).
|
||||||
:param context: The request context, for access checks.
|
:param context: The request context, for access checks.
|
||||||
@ -92,8 +91,7 @@ class DbQuotaDriver(nlib_quota_api.QuotaDriverAPI):
|
|||||||
project_quota_ext = {}
|
project_quota_ext = {}
|
||||||
for key, resource in resources.items():
|
for key, resource in resources.items():
|
||||||
if isinstance(resource, res.TrackedResource):
|
if isinstance(resource, res.TrackedResource):
|
||||||
used = resource.count_used(context, project_id,
|
used = self.get_resource_count(context, project_id, resource)
|
||||||
resync_usage=False)
|
|
||||||
else:
|
else:
|
||||||
# NOTE(ihrachys) .count won't use the plugin we pass, but we
|
# NOTE(ihrachys) .count won't use the plugin we pass, but we
|
||||||
# pass it regardless to keep the quota driver API intact
|
# 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,
|
return tracked_resource.count(context, None, project_id,
|
||||||
resync_usage=False)
|
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):
|
def quota_limit_check(self, context, project_id, resources, deltas):
|
||||||
# Ensure no value is less than zero
|
# Ensure no value is less than zero
|
||||||
unders = [key for key, val in deltas.items() if val < 0]
|
unders = [key for key, val in deltas.items() if val < 0]
|
||||||
|
@ -92,6 +92,10 @@ class DbQuotaNoLockDriver(quota_driver.DbQuotaDriver):
|
|||||||
return tracked_resource.count(context, None, project_id,
|
return tracked_resource.count(context, None, project_id,
|
||||||
count_db_registers=True)
|
count_db_registers=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_resource_count(context, project_id, tracked_resource):
|
||||||
|
return tracked_resource.count_db_registers(context, project_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_workers():
|
def get_workers():
|
||||||
interval = quota_api.RESERVATION_EXPIRATION_TIMEOUT
|
interval = quota_api.RESERVATION_EXPIRATION_TIMEOUT
|
||||||
|
@ -62,6 +62,10 @@ class DbQuotaDriverNull(nlib_quota_api.QuotaDriverAPI):
|
|||||||
def get_resource_usage(context, project_id, resources, resource_name):
|
def get_resource_usage(context, project_id, resources, resource_name):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_resource_count(context, project_id, tracked_resource):
|
||||||
|
return 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def quota_limit_check(context, project_id, resources, deltas):
|
def quota_limit_check(context, project_id, resources, deltas):
|
||||||
pass
|
pass
|
||||||
|
@ -60,9 +60,7 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
def __init__(self, plugin):
|
def __init__(self, plugin):
|
||||||
self._resource_name = RESOURCE_NAME
|
self._resource_name = RESOURCE_NAME
|
||||||
self._plugin = plugin
|
self._plugin = plugin
|
||||||
self._driver = importutils.import_class(
|
self._driver = importutils.import_class(cfg.CONF.QUOTAS.quota_driver)()
|
||||||
cfg.CONF.QUOTAS.quota_driver
|
|
||||||
)
|
|
||||||
self._update_extended_attributes = True
|
self._update_extended_attributes = True
|
||||||
|
|
||||||
def _update_attributes(self):
|
def _update_attributes(self):
|
||||||
|
@ -334,13 +334,13 @@ class TrackedResource(BaseResource):
|
|||||||
CountableResource instances.
|
CountableResource instances.
|
||||||
"""
|
"""
|
||||||
if count_db_registers:
|
if count_db_registers:
|
||||||
count = self._count_db_registers(context, project_id)
|
count = self.count_db_registers(context, project_id)
|
||||||
else:
|
else:
|
||||||
count = self.count_used(context, project_id, resync_usage)
|
count = self.count_used(context, project_id, resync_usage)
|
||||||
|
|
||||||
return count + self.count_reserved(context, project_id)
|
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.
|
"""Return the existing resources (self._model_class) in a project.
|
||||||
|
|
||||||
The query executed must be as fast as possible. To avoid retrieving all
|
The query executed must be as fast as possible. To avoid retrieving all
|
||||||
|
@ -15,18 +15,33 @@
|
|||||||
|
|
||||||
import itertools
|
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_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 api as quota_api
|
||||||
from neutron.db.quota import driver_nolock
|
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 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
|
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):
|
class TestDbQuotaDriverNoLock(test_driver.TestDbQuotaDriver):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestDbQuotaDriverNoLock, self).setUp()
|
super(TestDbQuotaDriverNoLock, self).setUp()
|
||||||
|
self.plugin = FakePlugin()
|
||||||
self.quota_driver = driver_nolock.DbQuotaNoLockDriver()
|
self.quota_driver = driver_nolock.DbQuotaNoLockDriver()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -60,3 +75,66 @@ class TestDbQuotaDriverNoLock(test_driver.TestDbQuotaDriver):
|
|||||||
self.quota_driver._remove_expired_reservations()
|
self.quota_driver._remove_expired_reservations()
|
||||||
res = quota_obj.Reservation.get_objects(self.context)
|
res = quota_obj.Reservation.get_objects(self.context)
|
||||||
self.assertEqual([], res)
|
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user