From 682fb1de14c142816df2a453062abfd351157a4b Mon Sep 17 00:00:00 2001 From: liyingjun Date: Fri, 17 May 2013 11:20:50 +0800 Subject: [PATCH] Editable default quota support Implement blueprint edit-default-quota DocImpact Using the class quotas named `default` as the default editable quotas. We can use the following novaclient command to update default quota: nova quota-class-update default Change-Id: I5a5001fadcbd61d550ebd5bdc33613b0ffdf29b2 --- nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 13 +++++++++++++ nova/quota.py | 10 ++++++++-- nova/tests/test_db_api.py | 22 ++++++++++++++++++++++ nova/tests/test_quota.py | 35 +++++++++++++++++++++++++++-------- 5 files changed, 75 insertions(+), 10 deletions(-) diff --git a/nova/db/api.py b/nova/db/api.py index d294ee1d7731..ffb992359976 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -911,6 +911,11 @@ def quota_class_get(context, class_name, resource): return IMPL.quota_class_get(context, class_name, resource) +def quota_class_get_default(context): + """Retrieve all default quotas.""" + return IMPL.quota_class_get_default(context) + + def quota_class_get_all_by_name(context, class_name): """Retrieve all quotas associated with a given quota class.""" return IMPL.quota_class_get_all_by_name(context, class_name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index a35e8154d3e1..2edd7ca528c9 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -81,6 +81,7 @@ get_session = db_session.get_session _SHADOW_TABLE_PREFIX = 'shadow_' +_DEFAULT_QUOTA_NAME = 'default' def get_backend(): @@ -2576,6 +2577,18 @@ def quota_class_get(context, class_name, resource): return result +def quota_class_get_default(context): + rows = model_query(context, models.QuotaClass, read_deleted="no").\ + filter_by(class_name=_DEFAULT_QUOTA_NAME).\ + all() + + result = {'class_name': _DEFAULT_QUOTA_NAME} + for row in rows: + result[row.resource] = row.hard_limit + + return result + + @require_context def quota_class_get_all_by_name(context, class_name): nova.context.authorize_quota_class_context(context, class_name) diff --git a/nova/quota.py b/nova/quota.py index 8aaa3ed31db5..a1c877ecc0f0 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -105,14 +105,18 @@ class DbQuotaDriver(object): def get_defaults(self, context, resources): """Given a list of resources, retrieve the default quotas. + Use the class quotas named `_DEFAULT_QUOTA_NAME` as default quotas, + if it exists. :param context: The request context, for access checks. :param resources: A dictionary of the registered resources. """ quotas = {} + default_quotas = db.quota_class_get_default(context) for resource in resources.values(): - quotas[resource.name] = resource.default + quotas[resource.name] = default_quotas.get(resource.name, + resource.default) return quotas @@ -180,6 +184,8 @@ class DbQuotaDriver(object): else: class_quotas = {} + default_quotas = self.get_defaults(context, resources) + for resource in resources.values(): # Omit default/quota class values if not defaults and resource.name not in project_quotas: @@ -187,7 +193,7 @@ class DbQuotaDriver(object): quotas[resource.name] = dict( limit=project_quotas.get(resource.name, class_quotas.get( - resource.name, resource.default)), + resource.name, default_quotas[resource.name])), ) # Include usages if desired. This is optional because one diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py index 1239c46275a1..4bcbc12cd091 100644 --- a/nova/tests/test_db_api.py +++ b/nova/tests/test_db_api.py @@ -3930,6 +3930,28 @@ class KeyPairTestCase(test.TestCase, ModelsObjectComparatorMixin): param['user_id'], param['name']) +class QuotaClassTestCase(test.TestCase, ModelsObjectComparatorMixin): + + def setUp(self): + super(QuotaClassTestCase, self).setUp() + self.ctxt = context.get_admin_context() + + def test_quota_class_get_default(self): + params = { + 'test_resource1': '10', + 'test_resource2': '20', + 'test_resource3': '30', + } + for res, limit in params.items(): + db.quota_class_create(self.ctxt, 'default', res, limit) + + defaults = db.quota_class_get_default(self.ctxt) + self.assertEqual(defaults, dict(class_name='default', + test_resource1=10, + test_resource2=20, + test_resource3=30)) + + class ArchiveTestCase(test.TestCase): def setUp(self): diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index b585aede5053..30f823ace1fa 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -739,23 +739,36 @@ class DbQuotaDriverTestCase(test.TestCase): def test_get_defaults(self): # Use our pre-defined resources + self._stub_quota_class_get_default() result = self.driver.get_defaults(None, quota.QUOTAS._resources) self.assertEqual(result, dict( - instances=10, + instances=5, cores=20, - ram=50 * 1024, + ram=25 * 1024, floating_ips=10, fixed_ips=10, - metadata_items=128, + metadata_items=64, injected_files=5, - injected_file_content_bytes=10 * 1024, + injected_file_content_bytes=5 * 1024, injected_file_path_bytes=255, security_groups=10, security_group_rules=20, key_pairs=100, )) + def _stub_quota_class_get_default(self): + # Stub out quota_class_get_default + def fake_qcgd(context): + self.calls.append('quota_class_get_default') + return dict( + instances=5, + ram=25 * 1024, + metadata_items=64, + injected_file_content_bytes=5 * 1024, + ) + self.stubs.Set(db, 'quota_class_get_default', fake_qcgd) + def _stub_quota_class_get_all_by_name(self): # Stub out quota_class_get_all_by_name def fake_qcgabn(context, quota_class): @@ -831,6 +844,7 @@ class DbQuotaDriverTestCase(test.TestCase): self.stubs.Set(db, 'quota_usage_get_all_by_project', fake_qugabp) self._stub_quota_class_get_all_by_name() + self._stub_quota_class_get_default() def test_get_project_quotas(self): self.maxDiff = None @@ -843,6 +857,7 @@ class DbQuotaDriverTestCase(test.TestCase): 'quota_get_all_by_project', 'quota_usage_get_all_by_project', 'quota_class_get_all_by_name', + 'quota_class_get_default', ]) self.assertEqual(result, dict( instances=dict( @@ -917,10 +932,11 @@ class DbQuotaDriverTestCase(test.TestCase): self.assertEqual(self.calls, [ 'quota_get_all_by_project', 'quota_usage_get_all_by_project', + 'quota_class_get_default', ]) self.assertEqual(result, dict( instances=dict( - limit=10, + limit=5, in_use=2, reserved=2, ), @@ -930,7 +946,7 @@ class DbQuotaDriverTestCase(test.TestCase): reserved=4, ), ram=dict( - limit=50 * 1024, + limit=25 * 1024, in_use=10 * 1024, reserved=0, ), @@ -945,7 +961,7 @@ class DbQuotaDriverTestCase(test.TestCase): reserved=0, ), metadata_items=dict( - limit=128, + limit=64, in_use=0, reserved=0, ), @@ -955,7 +971,7 @@ class DbQuotaDriverTestCase(test.TestCase): reserved=0, ), injected_file_content_bytes=dict( - limit=10 * 1024, + limit=5 * 1024, in_use=0, reserved=0, ), @@ -992,6 +1008,7 @@ class DbQuotaDriverTestCase(test.TestCase): 'quota_get_all_by_project', 'quota_usage_get_all_by_project', 'quota_class_get_all_by_name', + 'quota_class_get_default', ]) self.assertEqual(result, dict( instances=dict( @@ -1066,6 +1083,7 @@ class DbQuotaDriverTestCase(test.TestCase): 'quota_get_all_by_project', 'quota_usage_get_all_by_project', 'quota_class_get_all_by_name', + 'quota_class_get_default', ]) self.assertEqual(result, dict( cores=dict( @@ -1094,6 +1112,7 @@ class DbQuotaDriverTestCase(test.TestCase): self.assertEqual(self.calls, [ 'quota_get_all_by_project', 'quota_class_get_all_by_name', + 'quota_class_get_default', ]) self.assertEqual(result, dict( instances=dict(