Merge "Do not track active reservations"
This commit is contained in:
@@ -29,12 +29,8 @@ def utcnow():
|
|||||||
|
|
||||||
|
|
||||||
class QuotaUsageInfo(collections.namedtuple(
|
class QuotaUsageInfo(collections.namedtuple(
|
||||||
'QuotaUsageInfo', ['resource', 'tenant_id', 'used', 'reserved', 'dirty'])):
|
'QuotaUsageInfo', ['resource', 'tenant_id', 'used', 'dirty'])):
|
||||||
|
"""Information about resource quota usage."""
|
||||||
@property
|
|
||||||
def total(self):
|
|
||||||
"""Total resource usage (reserved and used)."""
|
|
||||||
return self.reserved + self.used
|
|
||||||
|
|
||||||
|
|
||||||
class ReservationInfo(collections.namedtuple(
|
class ReservationInfo(collections.namedtuple(
|
||||||
@@ -66,7 +62,6 @@ def get_quota_usage_by_resource_and_tenant(context, resource, tenant_id,
|
|||||||
return QuotaUsageInfo(result.resource,
|
return QuotaUsageInfo(result.resource,
|
||||||
result.tenant_id,
|
result.tenant_id,
|
||||||
result.in_use,
|
result.in_use,
|
||||||
result.reserved,
|
|
||||||
result.dirty)
|
result.dirty)
|
||||||
|
|
||||||
|
|
||||||
@@ -76,7 +71,6 @@ def get_quota_usage_by_resource(context, resource):
|
|||||||
return [QuotaUsageInfo(item.resource,
|
return [QuotaUsageInfo(item.resource,
|
||||||
item.tenant_id,
|
item.tenant_id,
|
||||||
item.in_use,
|
item.in_use,
|
||||||
item.reserved,
|
|
||||||
item.dirty) for item in query]
|
item.dirty) for item in query]
|
||||||
|
|
||||||
|
|
||||||
@@ -86,12 +80,11 @@ def get_quota_usage_by_tenant_id(context, tenant_id):
|
|||||||
return [QuotaUsageInfo(item.resource,
|
return [QuotaUsageInfo(item.resource,
|
||||||
item.tenant_id,
|
item.tenant_id,
|
||||||
item.in_use,
|
item.in_use,
|
||||||
item.reserved,
|
|
||||||
item.dirty) for item in query]
|
item.dirty) for item in query]
|
||||||
|
|
||||||
|
|
||||||
def set_quota_usage(context, resource, tenant_id,
|
def set_quota_usage(context, resource, tenant_id,
|
||||||
in_use=None, reserved=None, delta=False):
|
in_use=None, delta=False):
|
||||||
"""Set resource quota usage.
|
"""Set resource quota usage.
|
||||||
|
|
||||||
:param context: instance of neutron context with db session
|
:param context: instance of neutron context with db session
|
||||||
@@ -100,10 +93,8 @@ def set_quota_usage(context, resource, tenant_id,
|
|||||||
being set
|
being set
|
||||||
:param in_use: integer specifying the new quantity of used resources,
|
:param in_use: integer specifying the new quantity of used resources,
|
||||||
or a delta to apply to current used resource
|
or a delta to apply to current used resource
|
||||||
:param reserved: integer specifying the new quantity of reserved resources,
|
:param delta: Specifies whether in_use is an absolute number
|
||||||
or a delta to apply to current reserved resources
|
or a delta (default to False)
|
||||||
:param delta: Specififies whether in_use or reserved are absolute numbers
|
|
||||||
or deltas (default to False)
|
|
||||||
"""
|
"""
|
||||||
query = common_db_api.model_query(context, quota_models.QuotaUsage)
|
query = common_db_api.model_query(context, quota_models.QuotaUsage)
|
||||||
query = query.filter_by(resource=resource).filter_by(tenant_id=tenant_id)
|
query = query.filter_by(resource=resource).filter_by(tenant_id=tenant_id)
|
||||||
@@ -120,16 +111,11 @@ def set_quota_usage(context, resource, tenant_id,
|
|||||||
if delta:
|
if delta:
|
||||||
in_use = usage_data.in_use + in_use
|
in_use = usage_data.in_use + in_use
|
||||||
usage_data.in_use = in_use
|
usage_data.in_use = in_use
|
||||||
if reserved is not None:
|
|
||||||
if delta:
|
|
||||||
reserved = usage_data.reserved + reserved
|
|
||||||
usage_data.reserved = reserved
|
|
||||||
# After an explicit update the dirty bit should always be reset
|
# After an explicit update the dirty bit should always be reset
|
||||||
usage_data.dirty = False
|
usage_data.dirty = False
|
||||||
return QuotaUsageInfo(usage_data.resource,
|
return QuotaUsageInfo(usage_data.resource,
|
||||||
usage_data.tenant_id,
|
usage_data.tenant_id,
|
||||||
usage_data.in_use,
|
usage_data.in_use,
|
||||||
usage_data.reserved,
|
|
||||||
usage_data.dirty)
|
usage_data.dirty)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -126,21 +126,8 @@ class DbQuotaDriver(object):
|
|||||||
|
|
||||||
return dict((k, v) for k, v in quotas.items())
|
return dict((k, v) for k, v in quotas.items())
|
||||||
|
|
||||||
def _handle_expired_reservations(self, context, tenant_id,
|
def _handle_expired_reservations(self, context, tenant_id):
|
||||||
resource, expired_amount):
|
LOG.debug("Deleting expired reservations for tenant:%s" % tenant_id)
|
||||||
LOG.debug(("Adjusting usage for resource %(resource)s: "
|
|
||||||
"removing %(expired)d reserved items"),
|
|
||||||
{'resource': resource,
|
|
||||||
'expired': expired_amount})
|
|
||||||
# TODO(salv-orlando): It should be possible to do this
|
|
||||||
# operation for all resources with a single query.
|
|
||||||
# Update reservation usage
|
|
||||||
quota_api.set_quota_usage(
|
|
||||||
context,
|
|
||||||
resource,
|
|
||||||
tenant_id,
|
|
||||||
reserved=-expired_amount,
|
|
||||||
delta=True)
|
|
||||||
# Delete expired reservations (we don't want them to accrue
|
# Delete expired reservations (we don't want them to accrue
|
||||||
# in the database)
|
# in the database)
|
||||||
quota_api.remove_expired_reservations(
|
quota_api.remove_expired_reservations(
|
||||||
@@ -209,8 +196,7 @@ class DbQuotaDriver(object):
|
|||||||
if res_headroom < deltas[resource]:
|
if res_headroom < deltas[resource]:
|
||||||
resources_over_limit.append(resource)
|
resources_over_limit.append(resource)
|
||||||
if expired_reservations:
|
if expired_reservations:
|
||||||
self._handle_expired_reservations(
|
self._handle_expired_reservations(context, tenant_id)
|
||||||
context, tenant_id, resource, expired_reservations)
|
|
||||||
|
|
||||||
if resources_over_limit:
|
if resources_over_limit:
|
||||||
raise exceptions.OverQuota(overs=sorted(resources_over_limit))
|
raise exceptions.OverQuota(overs=sorted(resources_over_limit))
|
||||||
|
|||||||
@@ -213,14 +213,13 @@ class TrackedResource(BaseResource):
|
|||||||
max_retries=db_api.MAX_RETRIES,
|
max_retries=db_api.MAX_RETRIES,
|
||||||
exception_checker=lambda exc:
|
exception_checker=lambda exc:
|
||||||
isinstance(exc, oslo_db_exception.DBDuplicateEntry))
|
isinstance(exc, oslo_db_exception.DBDuplicateEntry))
|
||||||
def _set_quota_usage(self, context, tenant_id, in_use, reserved):
|
def _set_quota_usage(self, context, tenant_id, in_use):
|
||||||
return quota_api.set_quota_usage(context, self.name, tenant_id,
|
return quota_api.set_quota_usage(
|
||||||
in_use=in_use, reserved=reserved)
|
context, self.name, tenant_id, in_use=in_use)
|
||||||
|
|
||||||
def _resync(self, context, tenant_id, in_use, reserved):
|
def _resync(self, context, tenant_id, in_use):
|
||||||
# Update quota usage
|
# Update quota usage
|
||||||
usage_info = self._set_quota_usage(
|
usage_info = self._set_quota_usage(context, tenant_id, in_use)
|
||||||
context, tenant_id, in_use, reserved)
|
|
||||||
|
|
||||||
self._dirty_tenants.discard(tenant_id)
|
self._dirty_tenants.discard(tenant_id)
|
||||||
self._out_of_sync_tenants.discard(tenant_id)
|
self._out_of_sync_tenants.discard(tenant_id)
|
||||||
@@ -238,14 +237,17 @@ class TrackedResource(BaseResource):
|
|||||||
in_use = context.session.query(self._model_class).filter_by(
|
in_use = context.session.query(self._model_class).filter_by(
|
||||||
tenant_id=tenant_id).count()
|
tenant_id=tenant_id).count()
|
||||||
# Update quota usage
|
# Update quota usage
|
||||||
return self._resync(context, tenant_id, in_use, reserved=0)
|
return self._resync(context, tenant_id, in_use)
|
||||||
|
|
||||||
def count(self, context, _plugin, tenant_id, resync_usage=False):
|
def count(self, context, _plugin, tenant_id, resync_usage=False):
|
||||||
"""Return the current usage count for the resource.
|
"""Return the current usage count for the resource.
|
||||||
|
|
||||||
This method will fetch the information from resource usage data,
|
This method will fetch aggregate information for resource usage
|
||||||
unless usage data are marked as "dirty", in which case both used and
|
data, unless usage data are marked as "dirty".
|
||||||
reserved resource are explicitly counted.
|
In the latter case resource usage will be calculated counting
|
||||||
|
rows for tenant_id in the resource's database model.
|
||||||
|
Active reserved amount are instead always calculated by summing
|
||||||
|
amounts for matching records in the 'reservations' database model.
|
||||||
|
|
||||||
The _plugin and _resource parameters are unused but kept for
|
The _plugin and _resource parameters are unused but kept for
|
||||||
compatibility with the signature of the count method for
|
compatibility with the signature of the count method for
|
||||||
@@ -254,6 +256,11 @@ class TrackedResource(BaseResource):
|
|||||||
# Load current usage data, setting a row-level lock on the DB
|
# Load current usage data, setting a row-level lock on the DB
|
||||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||||
context, self.name, tenant_id, lock_for_update=True)
|
context, self.name, tenant_id, lock_for_update=True)
|
||||||
|
# Always fetch reservations, as they are not tracked by usage counters
|
||||||
|
reservations = quota_api.get_reservations_for_resources(
|
||||||
|
context, tenant_id, [self.name])
|
||||||
|
reserved = reservations.get(self.name, 0)
|
||||||
|
|
||||||
# If dirty or missing, calculate actual resource usage querying
|
# If dirty or missing, calculate actual resource usage querying
|
||||||
# the database and set/create usage info data
|
# the database and set/create usage info data
|
||||||
# NOTE: this routine "trusts" usage counters at service startup. This
|
# NOTE: this routine "trusts" usage counters at service startup. This
|
||||||
@@ -273,23 +280,20 @@ class TrackedResource(BaseResource):
|
|||||||
# typically one counts before adding a record, and that would mark
|
# typically one counts before adding a record, and that would mark
|
||||||
# the usage counter as dirty again)
|
# the usage counter as dirty again)
|
||||||
if resync_usage or not usage_info:
|
if resync_usage or not usage_info:
|
||||||
usage_info = self._resync(context, tenant_id,
|
usage_info = self._resync(context, tenant_id, in_use)
|
||||||
in_use, reserved=0)
|
|
||||||
else:
|
else:
|
||||||
# NOTE(salv-orlando): Passing 0 for reserved amount as
|
# NOTE(salv-orlando): Passing 0 for reserved amount as
|
||||||
# reservations are currently not supported
|
# reservations are currently not supported
|
||||||
usage_info = quota_api.QuotaUsageInfo(usage_info.resource,
|
usage_info = quota_api.QuotaUsageInfo(usage_info.resource,
|
||||||
usage_info.tenant_id,
|
usage_info.tenant_id,
|
||||||
in_use,
|
in_use,
|
||||||
0,
|
|
||||||
usage_info.dirty)
|
usage_info.dirty)
|
||||||
|
|
||||||
LOG.debug(("Quota usage for %(resource)s was recalculated. "
|
LOG.debug(("Quota usage for %(resource)s was recalculated. "
|
||||||
"Used quota:%(used)d; Reserved quota:%(reserved)d"),
|
"Used quota:%(used)d."),
|
||||||
{'resource': self.name,
|
{'resource': self.name,
|
||||||
'used': usage_info.used,
|
'used': usage_info.used})
|
||||||
'reserved': usage_info.reserved})
|
return usage_info.used + reserved
|
||||||
return usage_info.total
|
|
||||||
|
|
||||||
def register_events(self):
|
def register_events(self):
|
||||||
event.listen(self._model_class, 'after_insert', self._db_event_handler)
|
event.listen(self._model_class, 'after_insert', self._db_event_handler)
|
||||||
|
|||||||
@@ -34,16 +34,14 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
return quota_api.create_reservation(
|
return quota_api.create_reservation(
|
||||||
self.context, tenant_id, resource_deltas, expiration)
|
self.context, tenant_id, resource_deltas, expiration)
|
||||||
|
|
||||||
def _create_quota_usage(self, resource, used, reserved, tenant_id=None):
|
def _create_quota_usage(self, resource, used, tenant_id=None):
|
||||||
tenant_id = tenant_id or self.tenant_id
|
tenant_id = tenant_id or self.tenant_id
|
||||||
return quota_api.set_quota_usage(
|
return quota_api.set_quota_usage(
|
||||||
self.context, resource, tenant_id,
|
self.context, resource, tenant_id, in_use=used)
|
||||||
in_use=used, reserved=reserved)
|
|
||||||
|
|
||||||
def _verify_quota_usage(self, usage_info,
|
def _verify_quota_usage(self, usage_info,
|
||||||
expected_resource=None,
|
expected_resource=None,
|
||||||
expected_used=None,
|
expected_used=None,
|
||||||
expected_reserved=None,
|
|
||||||
expected_dirty=None):
|
expected_dirty=None):
|
||||||
self.assertEqual(self.tenant_id, usage_info.tenant_id)
|
self.assertEqual(self.tenant_id, usage_info.tenant_id)
|
||||||
if expected_resource:
|
if expected_resource:
|
||||||
@@ -52,57 +50,42 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self.assertEqual(expected_dirty, usage_info.dirty)
|
self.assertEqual(expected_dirty, usage_info.dirty)
|
||||||
if expected_used is not None:
|
if expected_used is not None:
|
||||||
self.assertEqual(expected_used, usage_info.used)
|
self.assertEqual(expected_used, usage_info.used)
|
||||||
if expected_reserved is not None:
|
|
||||||
self.assertEqual(expected_reserved, usage_info.reserved)
|
|
||||||
if expected_used is not None and expected_reserved is not None:
|
|
||||||
self.assertEqual(expected_used + expected_reserved,
|
|
||||||
usage_info.total)
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestQuotaDbApi, self).setUp()
|
super(TestQuotaDbApi, self).setUp()
|
||||||
self._set_context()
|
self._set_context()
|
||||||
|
|
||||||
def test_create_quota_usage(self):
|
def test_create_quota_usage(self):
|
||||||
usage_info = self._create_quota_usage('goals', 26, 10)
|
usage_info = self._create_quota_usage('goals', 26)
|
||||||
self._verify_quota_usage(usage_info,
|
self._verify_quota_usage(usage_info,
|
||||||
expected_resource='goals',
|
expected_resource='goals',
|
||||||
expected_used=26,
|
expected_used=26)
|
||||||
expected_reserved=10)
|
|
||||||
|
|
||||||
def test_update_quota_usage(self):
|
def test_update_quota_usage(self):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
# Higuain scores a double
|
# Higuain scores a double
|
||||||
usage_info_1 = quota_api.set_quota_usage(
|
usage_info_1 = quota_api.set_quota_usage(
|
||||||
self.context, 'goals', self.tenant_id,
|
self.context, 'goals', self.tenant_id,
|
||||||
in_use=28)
|
in_use=28)
|
||||||
self._verify_quota_usage(usage_info_1,
|
self._verify_quota_usage(usage_info_1,
|
||||||
expected_used=28,
|
expected_used=28)
|
||||||
expected_reserved=10)
|
|
||||||
usage_info_2 = quota_api.set_quota_usage(
|
usage_info_2 = quota_api.set_quota_usage(
|
||||||
self.context, 'goals', self.tenant_id,
|
self.context, 'goals', self.tenant_id,
|
||||||
reserved=8)
|
in_use=24)
|
||||||
self._verify_quota_usage(usage_info_2,
|
self._verify_quota_usage(usage_info_2,
|
||||||
expected_used=28,
|
expected_used=24)
|
||||||
expected_reserved=8)
|
|
||||||
|
|
||||||
def test_update_quota_usage_with_deltas(self):
|
def test_update_quota_usage_with_deltas(self):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
# Higuain scores a double
|
# Higuain scores a double
|
||||||
usage_info_1 = quota_api.set_quota_usage(
|
usage_info_1 = quota_api.set_quota_usage(
|
||||||
self.context, 'goals', self.tenant_id,
|
self.context, 'goals', self.tenant_id,
|
||||||
in_use=2, delta=True)
|
in_use=2, delta=True)
|
||||||
self._verify_quota_usage(usage_info_1,
|
self._verify_quota_usage(usage_info_1,
|
||||||
expected_used=28,
|
expected_used=28)
|
||||||
expected_reserved=10)
|
|
||||||
usage_info_2 = quota_api.set_quota_usage(
|
|
||||||
self.context, 'goals', self.tenant_id,
|
|
||||||
reserved=-2, delta=True)
|
|
||||||
self._verify_quota_usage(usage_info_2,
|
|
||||||
expected_used=28,
|
|
||||||
expected_reserved=8)
|
|
||||||
|
|
||||||
def test_set_quota_usage_dirty(self):
|
def test_set_quota_usage_dirty(self):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
# Higuain needs a shower after the match
|
# Higuain needs a shower after the match
|
||||||
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
self.assertEqual(1, quota_api.set_quota_usage_dirty(
|
||||||
self.context, 'goals', self.tenant_id))
|
self.context, 'goals', self.tenant_id))
|
||||||
@@ -123,9 +106,9 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self.context, 'meh', self.tenant_id))
|
self.context, 'meh', self.tenant_id))
|
||||||
|
|
||||||
def test_set_resources_quota_usage_dirty(self):
|
def test_set_resources_quota_usage_dirty(self):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11, 5)
|
self._create_quota_usage('assists', 11)
|
||||||
self._create_quota_usage('bookings', 3, 1)
|
self._create_quota_usage('bookings', 3)
|
||||||
self.assertEqual(2, quota_api.set_resources_quota_usage_dirty(
|
self.assertEqual(2, quota_api.set_resources_quota_usage_dirty(
|
||||||
self.context, ['goals', 'bookings'], self.tenant_id))
|
self.context, ['goals', 'bookings'], self.tenant_id))
|
||||||
usage_info_goals = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info_goals = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||||
@@ -139,9 +122,9 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self._verify_quota_usage(usage_info_bookings, expected_dirty=True)
|
self._verify_quota_usage(usage_info_bookings, expected_dirty=True)
|
||||||
|
|
||||||
def test_set_resources_quota_usage_dirty_with_empty_list(self):
|
def test_set_resources_quota_usage_dirty_with_empty_list(self):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11, 5)
|
self._create_quota_usage('assists', 11)
|
||||||
self._create_quota_usage('bookings', 3, 1)
|
self._create_quota_usage('bookings', 3)
|
||||||
# Expect all the resources for the tenant to be set dirty
|
# Expect all the resources for the tenant to be set dirty
|
||||||
self.assertEqual(3, quota_api.set_resources_quota_usage_dirty(
|
self.assertEqual(3, quota_api.set_resources_quota_usage_dirty(
|
||||||
self.context, [], self.tenant_id))
|
self.context, [], self.tenant_id))
|
||||||
@@ -164,8 +147,8 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
expected_dirty=False)
|
expected_dirty=False)
|
||||||
|
|
||||||
def _test_set_all_quota_usage_dirty(self, expected):
|
def _test_set_all_quota_usage_dirty(self, expected):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('goals', 12, 6, tenant_id='Callejon')
|
self._create_quota_usage('goals', 12, tenant_id='Callejon')
|
||||||
self.assertEqual(expected, quota_api.set_all_quota_usage_dirty(
|
self.assertEqual(expected, quota_api.set_all_quota_usage_dirty(
|
||||||
self.context, 'goals'))
|
self.context, 'goals'))
|
||||||
|
|
||||||
@@ -175,10 +158,10 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self._test_set_all_quota_usage_dirty(expected=1)
|
self._test_set_all_quota_usage_dirty(expected=1)
|
||||||
|
|
||||||
def test_get_quota_usage_by_tenant(self):
|
def test_get_quota_usage_by_tenant(self):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11, 5)
|
self._create_quota_usage('assists', 11)
|
||||||
# Create a resource for a different tenant
|
# Create a resource for a different tenant
|
||||||
self._create_quota_usage('mehs', 99, 99, tenant_id='buffon')
|
self._create_quota_usage('mehs', 99, tenant_id='buffon')
|
||||||
usage_infos = quota_api.get_quota_usage_by_tenant_id(
|
usage_infos = quota_api.get_quota_usage_by_tenant_id(
|
||||||
self.context, self.tenant_id)
|
self.context, self.tenant_id)
|
||||||
|
|
||||||
@@ -188,26 +171,24 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self.assertIn('assists', resources)
|
self.assertIn('assists', resources)
|
||||||
|
|
||||||
def test_get_quota_usage_by_resource(self):
|
def test_get_quota_usage_by_resource(self):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11, 5)
|
self._create_quota_usage('assists', 11)
|
||||||
self._create_quota_usage('goals', 12, 6, tenant_id='Callejon')
|
self._create_quota_usage('goals', 12, tenant_id='Callejon')
|
||||||
usage_infos = quota_api.get_quota_usage_by_resource(
|
usage_infos = quota_api.get_quota_usage_by_resource(
|
||||||
self.context, 'goals')
|
self.context, 'goals')
|
||||||
# Only 1 result expected in tenant context
|
# Only 1 result expected in tenant context
|
||||||
self.assertEqual(1, len(usage_infos))
|
self.assertEqual(1, len(usage_infos))
|
||||||
self._verify_quota_usage(usage_infos[0],
|
self._verify_quota_usage(usage_infos[0],
|
||||||
expected_resource='goals',
|
expected_resource='goals',
|
||||||
expected_used=26,
|
expected_used=26)
|
||||||
expected_reserved=10)
|
|
||||||
|
|
||||||
def test_get_quota_usage_by_tenant_and_resource(self):
|
def test_get_quota_usage_by_tenant_and_resource(self):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
|
||||||
self.context, 'goals', self.tenant_id)
|
self.context, 'goals', self.tenant_id)
|
||||||
self._verify_quota_usage(usage_info,
|
self._verify_quota_usage(usage_info,
|
||||||
expected_resource='goals',
|
expected_resource='goals',
|
||||||
expected_used=26,
|
expected_used=26)
|
||||||
expected_reserved=10)
|
|
||||||
|
|
||||||
def test_get_non_existing_quota_usage_returns_none(self):
|
def test_get_non_existing_quota_usage_returns_none(self):
|
||||||
self.assertIsNone(quota_api.get_quota_usage_by_resource_and_tenant(
|
self.assertIsNone(quota_api.get_quota_usage_by_resource_and_tenant(
|
||||||
@@ -226,7 +207,7 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self.assertEqual(self.tenant_id, resv.tenant_id)
|
self.assertEqual(self.tenant_id, resv.tenant_id)
|
||||||
self._verify_reserved_resources(resources, resv.deltas)
|
self._verify_reserved_resources(resources, resv.deltas)
|
||||||
|
|
||||||
def test_create_reservation_with_expirtion(self):
|
def test_create_reservation_with_expiration(self):
|
||||||
resources = {'goals': 2, 'assists': 1}
|
resources = {'goals': 2, 'assists': 1}
|
||||||
exp_date = datetime.datetime(2016, 3, 31, 14, 30)
|
exp_date = datetime.datetime(2016, 3, 31, 14, 30)
|
||||||
resv = self._create_reservation(resources, expiration=exp_date)
|
resv = self._create_reservation(resources, expiration=exp_date)
|
||||||
@@ -234,22 +215,6 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self.assertEqual(exp_date, resv.expiration)
|
self.assertEqual(exp_date, resv.expiration)
|
||||||
self._verify_reserved_resources(resources, resv.deltas)
|
self._verify_reserved_resources(resources, resv.deltas)
|
||||||
|
|
||||||
def _test_remove_reservation(self, set_dirty):
|
|
||||||
resources = {'goals': 2, 'assists': 1}
|
|
||||||
resv = self._create_reservation(resources)
|
|
||||||
self.assertEqual(1, quota_api.remove_reservation(
|
|
||||||
self.context, resv.reservation_id, set_dirty=set_dirty))
|
|
||||||
|
|
||||||
def test_remove_reservation(self):
|
|
||||||
self._test_remove_reservation(False)
|
|
||||||
|
|
||||||
def test_remove_reservation_and_set_dirty(self):
|
|
||||||
routine = 'neutron.db.quota.api.set_resources_quota_usage_dirty'
|
|
||||||
with mock.patch(routine) as mock_routine:
|
|
||||||
self._test_remove_reservation(False)
|
|
||||||
mock_routine.assert_called_once_with(
|
|
||||||
self.context, mock.ANY, self.tenant_id)
|
|
||||||
|
|
||||||
def test_remove_non_existent_reservation(self):
|
def test_remove_non_existent_reservation(self):
|
||||||
self.assertIsNone(quota_api.remove_reservation(self.context, 'meh'))
|
self.assertIsNone(quota_api.remove_reservation(self.context, 'meh'))
|
||||||
|
|
||||||
@@ -298,6 +263,22 @@ class TestQuotaDbApi(testlib_api.SqlTestCaseLight):
|
|||||||
self.assertIsNone(quota_api.get_reservations_for_resources(
|
self.assertIsNone(quota_api.get_reservations_for_resources(
|
||||||
self.context, self.tenant_id, []))
|
self.context, self.tenant_id, []))
|
||||||
|
|
||||||
|
def _test_remove_reservation(self, set_dirty):
|
||||||
|
resources = {'goals': 2, 'assists': 1}
|
||||||
|
resv = self._create_reservation(resources)
|
||||||
|
self.assertEqual(1, quota_api.remove_reservation(
|
||||||
|
self.context, resv.reservation_id, set_dirty=set_dirty))
|
||||||
|
|
||||||
|
def test_remove_reservation(self):
|
||||||
|
self._test_remove_reservation(False)
|
||||||
|
|
||||||
|
def test_remove_reservation_and_set_dirty(self):
|
||||||
|
routine = 'neutron.db.quota.api.set_resources_quota_usage_dirty'
|
||||||
|
with mock.patch(routine) as mock_routine:
|
||||||
|
self._test_remove_reservation(False)
|
||||||
|
mock_routine.assert_called_once_with(
|
||||||
|
self.context, mock.ANY, self.tenant_id)
|
||||||
|
|
||||||
def test_remove_expired_reservations(self):
|
def test_remove_expired_reservations(self):
|
||||||
with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow:
|
with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow:
|
||||||
mock_utcnow.return_value = datetime.datetime(
|
mock_utcnow.return_value = datetime.datetime(
|
||||||
@@ -342,9 +323,9 @@ class TestQuotaDbApiAdminContext(TestQuotaDbApi):
|
|||||||
load_admin_roles=False)
|
load_admin_roles=False)
|
||||||
|
|
||||||
def test_get_quota_usage_by_resource(self):
|
def test_get_quota_usage_by_resource(self):
|
||||||
self._create_quota_usage('goals', 26, 10)
|
self._create_quota_usage('goals', 26)
|
||||||
self._create_quota_usage('assists', 11, 5)
|
self._create_quota_usage('assists', 11)
|
||||||
self._create_quota_usage('goals', 12, 6, tenant_id='Callejon')
|
self._create_quota_usage('goals', 12, tenant_id='Callejon')
|
||||||
usage_infos = quota_api.get_quota_usage_by_resource(
|
usage_infos = quota_api.get_quota_usage_by_resource(
|
||||||
self.context, 'goals')
|
self.context, 'goals')
|
||||||
# 2 results expected in admin context
|
# 2 results expected in admin context
|
||||||
|
|||||||
@@ -165,8 +165,7 @@ class TestTrackedResource(testlib_api.SqlTestCaseLight):
|
|||||||
res.count(self.context, None, self.tenant_id,
|
res.count(self.context, None, self.tenant_id,
|
||||||
resync_usage=True)
|
resync_usage=True)
|
||||||
mock_set_quota_usage.assert_called_once_with(
|
mock_set_quota_usage.assert_called_once_with(
|
||||||
self.context, self.resource, self.tenant_id,
|
self.context, self.resource, self.tenant_id, in_use=2)
|
||||||
reserved=0, in_use=2)
|
|
||||||
|
|
||||||
def test_count_with_dirty_true_no_usage_info(self):
|
def test_count_with_dirty_true_no_usage_info(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
@@ -185,8 +184,7 @@ class TestTrackedResource(testlib_api.SqlTestCaseLight):
|
|||||||
self.tenant_id)
|
self.tenant_id)
|
||||||
res.count(self.context, None, self.tenant_id, resync_usage=True)
|
res.count(self.context, None, self.tenant_id, resync_usage=True)
|
||||||
mock_set_quota_usage.assert_called_once_with(
|
mock_set_quota_usage.assert_called_once_with(
|
||||||
self.context, self.resource, self.tenant_id,
|
self.context, self.resource, self.tenant_id, in_use=2)
|
||||||
reserved=0, in_use=2)
|
|
||||||
|
|
||||||
def test_add_delete_data_triggers_event(self):
|
def test_add_delete_data_triggers_event(self):
|
||||||
res = self._create_resource()
|
res = self._create_resource()
|
||||||
@@ -253,5 +251,4 @@ class TestTrackedResource(testlib_api.SqlTestCaseLight):
|
|||||||
# and now it should be in sync
|
# and now it should be in sync
|
||||||
self.assertNotIn(self.tenant_id, res._out_of_sync_tenants)
|
self.assertNotIn(self.tenant_id, res._out_of_sync_tenants)
|
||||||
mock_set_quota_usage.assert_called_once_with(
|
mock_set_quota_usage.assert_called_once_with(
|
||||||
self.context, self.resource, self.tenant_id,
|
self.context, self.resource, self.tenant_id, in_use=2)
|
||||||
reserved=0, in_use=2)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user