Editable default quota support for cinder

Implement blueprint edit-default-quota

DocImpact

Using the class quotas named `default` as the default editable quotas.

We can use the following cinderclient command to update default quota:

cinder quota-class-update default <key> <value>

Change-Id: I9f97506b41157066026dd43163ec4b1827d5b9cf
This commit is contained in:
liyingjun 2013-05-23 16:24:26 +08:00
parent 86f8afb3d0
commit 51656c7576
5 changed files with 69 additions and 10 deletions

View File

@ -617,6 +617,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)

View File

@ -52,6 +52,8 @@ db_session.set_defaults(sql_connection='sqlite:///$state_path/$sqlite_db',
get_engine = db_session.get_engine
get_session = db_session.get_session
_DEFAULT_QUOTA_NAME = 'default'
def get_backend():
"""The backend is this module itself."""
@ -499,6 +501,18 @@ def quota_class_get(context, class_name, resource, session=None):
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):
authorize_quota_class_context(context, class_name)

View File

@ -54,7 +54,10 @@ quota_opts = [
help='number of seconds between subsequent usage refreshes'),
cfg.StrOpt('quota_driver',
default='cinder.quota.DbQuotaDriver',
help='default driver to use for quota checks'), ]
help='default driver to use for quota checks'),
cfg.BoolOpt('use_default_quota_class',
default='True',
help='whether to use default quota class for default quota'), ]
CONF = cfg.CONF
CONF.register_opts(quota_opts)
@ -79,14 +82,26 @@ 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 = {}
if CONF.use_default_quota_class:
default_quotas = db.quota_class_get_default(context)
for resource in resources.values():
quotas[resource.name] = resource.default
if resource.name not in default_quotas:
LOG.deprecated(_("Default quota for resource: %(res)s is set "
"by the default quota flag: quota_%(res)s, "
"it is now deprecated. Please use the "
"the default quota class for default "
"quota.") % {'res': resource.name})
quotas[resource.name] = default_quotas.get(resource.name,
resource.default)
return quotas
@ -154,15 +169,19 @@ 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:
continue
quotas[resource.name] = dict(
limit=project_quotas.get(resource.name,
class_quotas.get(resource.name,
resource.default)), )
limit=project_quotas.get(
resource.name,
class_quotas.get(resource.name,
default_quotas[resource.name])),
)
# Include usages if desired. This is optional because one
# internal consumer of this interface wants to access the

View File

@ -666,6 +666,7 @@ 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(
@ -675,6 +676,15 @@ class DbQuotaDriverTestCase(test.TestCase):
snapshots=10,
gigabytes=1000, ))
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(volumes=10,
snapshots=10,
gigabytes=1000,)
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):
@ -720,6 +730,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._stub_get_by_project()
@ -729,7 +740,8 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, ['quota_get_all_by_project',
'quota_usage_get_all_by_project',
'quota_class_get_all_by_name', ])
'quota_class_get_all_by_name',
'quota_class_get_default', ])
self.assertEqual(result, dict(volumes=dict(limit=10,
in_use=2,
reserved=0, ),
@ -747,7 +759,8 @@ class DbQuotaDriverTestCase(test.TestCase):
quota.QUOTAS._resources, 'test_project')
self.assertEqual(self.calls, ['quota_get_all_by_project',
'quota_usage_get_all_by_project', ])
'quota_usage_get_all_by_project',
'quota_class_get_default', ])
self.assertEqual(result, dict(volumes=dict(limit=10,
in_use=2,
reserved=0, ),
@ -766,7 +779,8 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, ['quota_get_all_by_project',
'quota_usage_get_all_by_project',
'quota_class_get_all_by_name', ])
'quota_class_get_all_by_name',
'quota_class_get_default', ])
self.assertEqual(result, dict(volumes=dict(limit=10,
in_use=2,
reserved=0, ),
@ -785,7 +799,8 @@ class DbQuotaDriverTestCase(test.TestCase):
self.assertEqual(self.calls, ['quota_get_all_by_project',
'quota_usage_get_all_by_project',
'quota_class_get_all_by_name', ])
'quota_class_get_all_by_name',
'quota_class_get_default', ])
self.assertEqual(result,
dict(gigabytes=dict(limit=50,
in_use=10,
@ -804,7 +819,8 @@ class DbQuotaDriverTestCase(test.TestCase):
quota.QUOTAS._resources, 'test_project', usages=False)
self.assertEqual(self.calls, ['quota_get_all_by_project',
'quota_class_get_all_by_name', ])
'quota_class_get_all_by_name',
'quota_class_get_default', ])
self.assertEqual(result, dict(volumes=dict(limit=10, ),
snapshots=dict(limit=10, ),
gigabytes=dict(limit=50, ), ))

View File

@ -210,6 +210,11 @@
# Options defined in cinder.quota
#
# If True, uses the default quota class for default quota, the
# quota_* option will be deprecated if the default quota for
# the related resource is set by default quota class (boolean value)
#use_default_quota_class=true
# number of volumes allowed per project (integer value)
#quota_volumes=10