max_retries=db_api.MAX_RETRIES,
exception_checker=lambda exc:
isinstance(exc, oslo_db_exception.DBDuplicateEntry))
- def _set_quota_usage(self, context, tenant_id, in_use, reserved):
- return quota_api.set_quota_usage(context, self.name, tenant_id,
- in_use=in_use, reserved=reserved)
+ def _set_quota_usage(self, context, tenant_id, in_use):
+ return quota_api.set_quota_usage(
+ 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
- usage_info = self._set_quota_usage(
- context, tenant_id, in_use, reserved)
+ usage_info = self._set_quota_usage(context, tenant_id, in_use)
self._dirty_tenants.discard(tenant_id)
self._out_of_sync_tenants.discard(tenant_id)
in_use = context.session.query(self._model_class).filter_by(
tenant_id=tenant_id).count()
# 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):
"""Return the current usage count for the resource.
- This method will fetch the information from resource usage data,
- unless usage data are marked as "dirty", in which case both used and
- reserved resource are explicitly counted.
+ This method will fetch aggregate information for resource usage
+ data, unless usage data are marked as "dirty".
+ 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
compatibility with the signature of the count method for
# Load current usage data, setting a row-level lock on the DB
usage_info = quota_api.get_quota_usage_by_resource_and_tenant(
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
# the database and set/create usage info data
# NOTE: this routine "trusts" usage counters at service startup. This
# typically one counts before adding a record, and that would mark
# the usage counter as dirty again)
if resync_usage or not usage_info:
- usage_info = self._resync(context, tenant_id,
- in_use, reserved=0)
+ usage_info = self._resync(context, tenant_id, in_use)
else:
# NOTE(salv-orlando): Passing 0 for reserved amount as
# reservations are currently not supported
usage_info = quota_api.QuotaUsageInfo(usage_info.resource,
usage_info.tenant_id,
in_use,
- 0,
usage_info.dirty)
LOG.debug(("Quota usage for %(resource)s was recalculated. "
- "Used quota:%(used)d; Reserved quota:%(reserved)d"),
+ "Used quota:%(used)d."),
{'resource': self.name,
- 'used': usage_info.used,
- 'reserved': usage_info.reserved})
- return usage_info.total
+ 'used': usage_info.used})
+ return usage_info.used + reserved
def register_events(self):
event.listen(self._model_class, 'after_insert', self._db_event_handler)
return quota_api.create_reservation(
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
return quota_api.set_quota_usage(
- self.context, resource, tenant_id,
- in_use=used, reserved=reserved)
+ self.context, resource, tenant_id, in_use=used)
def _verify_quota_usage(self, usage_info,
expected_resource=None,
expected_used=None,
- expected_reserved=None,
expected_dirty=None):
self.assertEqual(self.tenant_id, usage_info.tenant_id)
if expected_resource:
self.assertEqual(expected_dirty, usage_info.dirty)
if expected_used is not None:
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):
super(TestQuotaDbApi, self).setUp()
self._set_context()
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,
expected_resource='goals',
- expected_used=26,
- expected_reserved=10)
+ expected_used=26)
def test_update_quota_usage(self):
- self._create_quota_usage('goals', 26, 10)
+ self._create_quota_usage('goals', 26)
# Higuain scores a double
usage_info_1 = quota_api.set_quota_usage(
self.context, 'goals', self.tenant_id,
in_use=28)
self._verify_quota_usage(usage_info_1,
- expected_used=28,
- expected_reserved=10)
+ expected_used=28)
usage_info_2 = quota_api.set_quota_usage(
self.context, 'goals', self.tenant_id,
- reserved=8)
+ in_use=24)
self._verify_quota_usage(usage_info_2,
- expected_used=28,
- expected_reserved=8)
+ expected_used=24)
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
usage_info_1 = quota_api.set_quota_usage(
self.context, 'goals', self.tenant_id,
in_use=2, delta=True)
self._verify_quota_usage(usage_info_1,
- 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)
+ expected_used=28)
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
self.assertEqual(1, quota_api.set_quota_usage_dirty(
self.context, 'goals', self.tenant_id))
self.context, 'meh', self.tenant_id))
def test_set_resources_quota_usage_dirty(self):
- self._create_quota_usage('goals', 26, 10)
- self._create_quota_usage('assists', 11, 5)
- self._create_quota_usage('bookings', 3, 1)
+ self._create_quota_usage('goals', 26)
+ self._create_quota_usage('assists', 11)
+ self._create_quota_usage('bookings', 3)
self.assertEqual(2, quota_api.set_resources_quota_usage_dirty(
self.context, ['goals', 'bookings'], self.tenant_id))
usage_info_goals = quota_api.get_quota_usage_by_resource_and_tenant(
self._verify_quota_usage(usage_info_bookings, expected_dirty=True)
def test_set_resources_quota_usage_dirty_with_empty_list(self):
- self._create_quota_usage('goals', 26, 10)
- self._create_quota_usage('assists', 11, 5)
- self._create_quota_usage('bookings', 3, 1)
+ self._create_quota_usage('goals', 26)
+ self._create_quota_usage('assists', 11)
+ self._create_quota_usage('bookings', 3)
# Expect all the resources for the tenant to be set dirty
self.assertEqual(3, quota_api.set_resources_quota_usage_dirty(
self.context, [], self.tenant_id))
expected_dirty=False)
def _test_set_all_quota_usage_dirty(self, expected):
- self._create_quota_usage('goals', 26, 10)
- self._create_quota_usage('goals', 12, 6, tenant_id='Callejon')
+ self._create_quota_usage('goals', 26)
+ self._create_quota_usage('goals', 12, tenant_id='Callejon')
self.assertEqual(expected, quota_api.set_all_quota_usage_dirty(
self.context, 'goals'))
self._test_set_all_quota_usage_dirty(expected=1)
def test_get_quota_usage_by_tenant(self):
- self._create_quota_usage('goals', 26, 10)
- self._create_quota_usage('assists', 11, 5)
+ self._create_quota_usage('goals', 26)
+ self._create_quota_usage('assists', 11)
# 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(
self.context, self.tenant_id)
self.assertIn('assists', resources)
def test_get_quota_usage_by_resource(self):
- self._create_quota_usage('goals', 26, 10)
- self._create_quota_usage('assists', 11, 5)
- self._create_quota_usage('goals', 12, 6, tenant_id='Callejon')
+ self._create_quota_usage('goals', 26)
+ self._create_quota_usage('assists', 11)
+ self._create_quota_usage('goals', 12, tenant_id='Callejon')
usage_infos = quota_api.get_quota_usage_by_resource(
self.context, 'goals')
# Only 1 result expected in tenant context
self.assertEqual(1, len(usage_infos))
self._verify_quota_usage(usage_infos[0],
expected_resource='goals',
- expected_used=26,
- expected_reserved=10)
+ expected_used=26)
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(
self.context, 'goals', self.tenant_id)
self._verify_quota_usage(usage_info,
expected_resource='goals',
- expected_used=26,
- expected_reserved=10)
+ expected_used=26)
def test_get_non_existing_quota_usage_returns_none(self):
self.assertIsNone(quota_api.get_quota_usage_by_resource_and_tenant(
self.assertEqual(self.tenant_id, resv.tenant_id)
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}
exp_date = datetime.datetime(2016, 3, 31, 14, 30)
resv = self._create_reservation(resources, expiration=exp_date)
self.assertEqual(exp_date, resv.expiration)
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):
self.assertIsNone(quota_api.remove_reservation(self.context, 'meh'))
self.assertIsNone(quota_api.get_reservations_for_resources(
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):
with mock.patch('neutron.db.quota.api.utcnow') as mock_utcnow:
mock_utcnow.return_value = datetime.datetime(
load_admin_roles=False)
def test_get_quota_usage_by_resource(self):
- self._create_quota_usage('goals', 26, 10)
- self._create_quota_usage('assists', 11, 5)
- self._create_quota_usage('goals', 12, 6, tenant_id='Callejon')
+ self._create_quota_usage('goals', 26)
+ self._create_quota_usage('assists', 11)
+ self._create_quota_usage('goals', 12, tenant_id='Callejon')
usage_infos = quota_api.get_quota_usage_by_resource(
self.context, 'goals')
# 2 results expected in admin context