Handling unlimited values when updating quota

Changes:
- Adding a general approach to handle unlimited values when performing
calculations with quota values.
- New test that fails without the fix.
- Updated test cases to support the fix.

Change-Id: I762a8e67261c0e0286307832cd13ad12e6d96504
Closes-Bug: #1292494
This commit is contained in:
gseverina
2014-03-14 13:06:45 -03:00
parent a8e051b6ec
commit 362e998e89
2 changed files with 116 additions and 16 deletions
+37 -9
View File
@@ -92,6 +92,8 @@ class DbQuotaDriver(object):
quota information. The default driver utilizes the local
database.
"""
UNLIMITED_VALUE = -1
def get_by_project_and_user(self, context, project_id, user_id, resource):
"""Get a specific quota by project and user."""
@@ -273,6 +275,30 @@ class DbQuotaDriver(object):
defaults=defaults, usages=project_usages,
remains=remains)
def _is_unlimited_value(self, v):
"""A helper method to check for unlimited value.
"""
return v <= self.UNLIMITED_VALUE
def _sum_quota_values(self, v1, v2):
"""A helper method that handles unlimited values when performing
sum operation.
"""
if self._is_unlimited_value(v1) or self._is_unlimited_value(v2):
return self.UNLIMITED_VALUE
return v1 + v2
def _sub_quota_values(self, v1, v2):
"""A helper method that handles unlimited values when performing
subtraction operation.
"""
if self._is_unlimited_value(v1) or self._is_unlimited_value(v2):
return self.UNLIMITED_VALUE
return v1 - v2
def get_settable_quotas(self, context, resources, project_id,
user_id=None):
"""Given a list of resources, retrieve the range of settable quotas for
@@ -283,6 +309,7 @@ class DbQuotaDriver(object):
:param project_id: The ID of the project to return quotas for.
:param user_id: The ID of the user to return quotas for.
"""
settable_quotas = {}
db_proj_quotas = db.quota_get_all_by_project(context, project_id)
project_quotas = self.get_project_quotas(context, resources,
@@ -297,17 +324,18 @@ class DbQuotaDriver(object):
project_quotas=db_proj_quotas,
user_quotas=setted_quotas)
for key, value in user_quotas.items():
maximum = project_quotas[key]['remains'] +\
setted_quotas.get(key, 0)
settable_quotas[key] = dict(
minimum=value['in_use'] + value['reserved'],
maximum=maximum
)
maximum = \
self._sum_quota_values(project_quotas[key]['remains'],
setted_quotas.get(key, 0))
minimum = value['in_use'] + value['reserved']
settable_quotas[key] = {'minimum': minimum, 'maximum': maximum}
else:
for key, value in project_quotas.items():
minimum = max(int(value['limit'] - value['remains']),
int(value['in_use'] + value['reserved']))
settable_quotas[key] = dict(minimum=minimum, maximum=-1)
minimum = \
max(int(self._sub_quota_values(value['limit'],
value['remains'])),
int(value['in_use'] + value['reserved']))
settable_quotas[key] = {'minimum': minimum, 'maximum': -1}
return settable_quotas
def _get_quotas(self, context, resources, keys, has_sync, project_id=None,
+79 -7
View File
@@ -1590,14 +1590,20 @@ class DbQuotaDriverTestCase(test.TestCase):
self.calls.append('get_project_quotas')
result = {}
for k, v in resources.items():
limit = v.default
reserved = 0
if k == 'instances':
remains = v.default - 5
in_use = 1
elif k == 'cores':
remains = -1
in_use = 5
limit = -1
else:
remains = v.default
in_use = 0
result[k] = {'limit': v.default, 'in_use': in_use,
'reserved': 0, 'remains': remains}
result[k] = {'limit': limit, 'in_use': in_use,
'reserved': reserved, 'remains': remains}
return result
def fake_get_user_quotas(context, resources, project_id, user_id,
@@ -1607,17 +1613,21 @@ class DbQuotaDriverTestCase(test.TestCase):
self.calls.append('get_user_quotas')
result = {}
for k, v in resources.items():
reserved = 0
if k == 'instances':
in_use = 1
elif k == 'cores':
in_use = 5
reserved = 10
else:
in_use = 0
result[k] = {'limit': v.default,
'in_use': in_use, 'reserved': 0}
'in_use': in_use, 'reserved': reserved}
return result
def fake_qgabpau(context, project_id, user_id):
self.calls.append('quota_get_all_by_project_and_user')
return {'instances': 2}
return {'instances': 2, 'cores': -1}
self.stubs.Set(self.driver, 'get_project_quotas',
fake_get_project_quotas)
@@ -1643,8 +1653,8 @@ class DbQuotaDriverTestCase(test.TestCase):
'maximum': 7,
},
'cores': {
'minimum': 0,
'maximum': 20,
'minimum': 15,
'maximum': -1,
},
'ram': {
'minimum': 0,
@@ -1703,7 +1713,7 @@ class DbQuotaDriverTestCase(test.TestCase):
'maximum': -1,
},
'cores': {
'minimum': 0,
'minimum': 5,
'maximum': -1,
},
'ram': {
@@ -1748,6 +1758,68 @@ class DbQuotaDriverTestCase(test.TestCase):
},
})
def test_get_settable_quotas_by_user_with_unlimited_value(self):
self._stub_get_settable_quotas()
result = self.driver.get_settable_quotas(
FakeContext('test_project', 'test_class'),
quota.QUOTAS._resources, 'test_project', user_id='test_user')
self.assertEqual(self.calls, [
'get_project_quotas',
'quota_get_all_by_project_and_user',
'get_user_quotas',
])
self.assertEqual(result, {
'instances': {
'minimum': 1,
'maximum': 7,
},
'cores': {
'minimum': 15,
'maximum': -1,
},
'ram': {
'minimum': 0,
'maximum': 50 * 1024,
},
'floating_ips': {
'minimum': 0,
'maximum': 10,
},
'fixed_ips': {
'minimum': 0,
'maximum': 10,
},
'metadata_items': {
'minimum': 0,
'maximum': 128,
},
'injected_files': {
'minimum': 0,
'maximum': 5,
},
'injected_file_content_bytes': {
'minimum': 0,
'maximum': 10 * 1024,
},
'injected_file_path_bytes': {
'minimum': 0,
'maximum': 255,
},
'security_groups': {
'minimum': 0,
'maximum': 10,
},
'security_group_rules': {
'minimum': 0,
'maximum': 20,
},
'key_pairs': {
'minimum': 0,
'maximum': 100,
},
})
def _stub_get_project_quotas(self):
def fake_get_project_quotas(context, resources, project_id,
quota_class=None, defaults=True,