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:
parent
86f8afb3d0
commit
51656c7576
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ), ))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue