Add Generic Volume Group Into Quota Management

Generic Volume Group currently has it own quota mechanism.
But we can't get or set any information about the Generic
Volume Group quota at API layer.

This patch add Generic Volume Group into quota management.

DocImpact
APIImpact

Change-Id: I9ff0357984a1e95c1ea8c85f8c508402ef7135ac
Implements: blueprint add-volumegroup-into-quota-management
This commit is contained in:
wangxiyuan 2017-03-01 16:46:45 +08:00
parent ac989f682b
commit 608de666fa
6 changed files with 55 additions and 17 deletions

View File

@ -25,6 +25,7 @@ from cinder import utils
QUOTAS = quota.QUOTAS
GROUP_QUOTAS = quota.GROUP_QUOTAS
authorize = extensions.extension_authorizer('volume', 'quota_classes')
@ -46,9 +47,11 @@ class QuotaClassSetsController(wsgi.Controller):
db.sqlalchemy.api.authorize_quota_class_context(context, id)
except exception.NotAuthorized:
raise webob.exc.HTTPForbidden()
quota_set = QUOTAS.get_class_quotas(context, id)
group_quota_set = GROUP_QUOTAS.get_class_quotas(context, id)
quota_set.update(group_quota_set)
return self._format_quota_set(id,
QUOTAS.get_class_quotas(context, id))
return self._format_quota_set(id, quota_set)
def update(self, req, id, body):
context = req.environ['cinder.context']
@ -63,7 +66,7 @@ class QuotaClassSetsController(wsgi.Controller):
raise webob.exc.HTTPBadRequest(explanation=msg)
for key, value in body['quota_class_set'].items():
if key in QUOTAS:
if key in QUOTAS or key in GROUP_QUOTAS:
try:
value = utils.validate_integer(value, key, min_value=-1,
max_value=db.MAX_INT)
@ -72,8 +75,12 @@ class QuotaClassSetsController(wsgi.Controller):
db.quota_class_create(context, quota_class, key, value)
except exception.AdminRequired:
raise webob.exc.HTTPForbidden()
return {'quota_class_set': QUOTAS.get_class_quotas(context,
quota_class)}
quota_set = QUOTAS.get_class_quotas(context, quota_class)
group_quota_set = GROUP_QUOTAS.get_class_quotas(context, quota_class)
quota_set.update(group_quota_set)
return {'quota_class_set': quota_set}
class Quota_classes(extensions.ExtensionDescriptor):

View File

@ -28,6 +28,7 @@ from cinder import quota_utils
from cinder import utils
QUOTAS = quota.QUOTAS
GROUP_QUOTAS = quota.GROUP_QUOTAS
NON_QUOTA_KEYS = ['tenant_id', 'id']
authorize_update = extensions.extension_authorizer('volume', 'quotas:update')
@ -63,6 +64,9 @@ class QuotaSetsController(wsgi.Controller):
def _get_quotas(self, context, id, usages=False):
values = QUOTAS.get_project_quotas(context, id, usages=usages)
group_values = GROUP_QUOTAS.get_project_quotas(context, id,
usages=usages)
values.update(group_values)
if usages:
return values
@ -225,7 +229,8 @@ class QuotaSetsController(wsgi.Controller):
# NOTE(ankit): Pass #1 - In this loop for body['quota_set'].items(),
# we figure out if we have any bad keys.
for key, value in body['quota_set'].items():
if (key not in QUOTAS and key not in NON_QUOTA_KEYS):
if (key not in QUOTAS and key not in GROUP_QUOTAS and key not in
NON_QUOTA_KEYS):
bad_keys.append(key)
continue
@ -259,6 +264,10 @@ class QuotaSetsController(wsgi.Controller):
# resources.
quota_values = QUOTAS.get_project_quotas(context, target_project_id,
defaults=False)
group_quota_values = GROUP_QUOTAS.get_project_quotas(context,
target_project_id,
defaults=False)
quota_values.update(group_quota_values)
valid_quotas = {}
reservations = []
for key in body['quota_set'].keys():
@ -326,17 +335,20 @@ class QuotaSetsController(wsgi.Controller):
res_change = new_quota_from_target_proj - orig_quota_from_target_proj
if res_change != 0:
deltas = {res: res_change}
resources = QUOTAS.resources
resources.update(GROUP_QUOTAS.resources)
reservations += quota_utils.update_alloc_to_next_hard_limit(
ctxt, QUOTAS.resources, deltas, res, None, target_project.id)
ctxt, resources, deltas, res, None, target_project.id)
return reservations
def defaults(self, req, id):
context = req.environ['cinder.context']
authorize_show(context)
return self._format_quota_set(id, QUOTAS.get_defaults(
context, project_id=id))
defaults = QUOTAS.get_defaults(context, project_id=id)
group_defaults = GROUP_QUOTAS.get_defaults(context, project_id=id)
defaults.update(group_defaults)
return self._format_quota_set(id, defaults)
def delete(self, req, id):
"""Delete Quota for a particular tenant.
@ -366,6 +378,9 @@ class QuotaSetsController(wsgi.Controller):
try:
project_quotas = QUOTAS.get_project_quotas(
ctxt, proj_id, usages=True, defaults=False)
project_group_quotas = GROUP_QUOTAS.get_project_quotas(
ctxt, proj_id, usages=True, defaults=False)
project_quotas.update(project_group_quotas)
except exception.NotAuthorized:
raise webob.exc.HTTPForbidden()
@ -382,6 +397,7 @@ class QuotaSetsController(wsgi.Controller):
parent_id)
defaults = QUOTAS.get_defaults(ctxt, proj_id)
defaults.update(GROUP_QUOTAS.get_defaults(ctxt, proj_id))
# If the project which is being deleted has allocated part of its
# quota to its subprojects, then subprojects' quotas should be
# deleted first.
@ -416,8 +432,11 @@ class QuotaSetsController(wsgi.Controller):
ctxt = req.environ['cinder.context']
params = req.params
try:
resources = QUOTAS.resources
resources.update(GROUP_QUOTAS.resources)
quota_utils.validate_setup_for_nested_quota_use(
ctxt, QUOTAS.resources, quota.NestedDbQuotaDriver(),
ctxt, resources, quota.NestedDbQuotaDriver(),
fix_allocated_quotas=params.get('fix_allocated_quotas'))
except exception.InvalidNestedQuotaSetup as e:
raise webob.exc.HTTPBadRequest(explanation=e.msg)

View File

@ -147,6 +147,7 @@ def update_alloc_to_next_hard_limit(context, resources, deltas, res,
expire, project_id):
from cinder import quota
QUOTAS = quota.QUOTAS
GROUP_QUOTAS = quota.GROUP_QUOTAS
reservations = []
projects = get_project_hierarchy(context, project_id,
parents_as_ids=True).parents
@ -156,8 +157,12 @@ def update_alloc_to_next_hard_limit(context, resources, deltas, res,
while projects and not hard_limit_found:
cur_proj_id = list(projects)[0]
projects = projects[cur_proj_id]
cur_quota_lim = QUOTAS.get_by_project_or_default(
context, cur_proj_id, res)
if res == 'groups':
cur_quota_lim = GROUP_QUOTAS.get_by_project_or_default(
context, cur_proj_id, res)
else:
cur_quota_lim = QUOTAS.get_by_project_or_default(
context, cur_proj_id, res)
hard_limit_found = (cur_quota_lim != -1)
cur_quota = {res: cur_quota_lim}
cur_delta = {res: deltas[res]}

View File

@ -43,13 +43,14 @@ CONF = cfg.CONF
def make_body(root=True, gigabytes=1000, snapshots=10,
volumes=10, backups=10, backup_gigabytes=1000,
tenant_id=fake.PROJECT_ID, per_volume_gigabytes=-1):
tenant_id=fake.PROJECT_ID, per_volume_gigabytes=-1, groups=10):
resources = {'gigabytes': gigabytes,
'snapshots': snapshots,
'volumes': volumes,
'backups': backups,
'backup_gigabytes': backup_gigabytes,
'per_volume_gigabytes': per_volume_gigabytes, }
'per_volume_gigabytes': per_volume_gigabytes,
'groups': groups}
# need to consider preexisting volume types as well
volume_types = db.volume_type_get_all(context.get_admin_context())

View File

@ -33,19 +33,21 @@ from cinder.volume import volume_types
QUOTAS = quota.QUOTAS
GROUP_QUOTAS = quota.GROUP_QUOTAS
def make_body(root=True, gigabytes=1000, snapshots=10,
volumes=10, backups=10,
backup_gigabytes=1000, per_volume_gigabytes=-1,
volume_types_faked=None,
tenant_id=fake.PROJECT_ID):
tenant_id=fake.PROJECT_ID, groups=10):
resources = {'gigabytes': gigabytes,
'snapshots': snapshots,
'volumes': volumes,
'backups': backups,
'per_volume_gigabytes': per_volume_gigabytes,
'backup_gigabytes': backup_gigabytes}
'backup_gigabytes': backup_gigabytes,
'groups': groups}
if not volume_types_faked:
volume_types_faked = {'fake_type': None}
for volume_type in volume_types_faked:
@ -68,6 +70,7 @@ def make_response_body(root=True, ctxt=None, quota_class='foo',
if not ctxt:
ctxt = context.get_admin_context()
resources.update(QUOTAS.get_class_quotas(ctxt, quota_class))
resources.update(GROUP_QUOTAS.get_class_quotas(ctxt, quota_class))
if not request_body and not request_body['quota_class_set']:
resources.update(request_body['quota_class_set'])

View File

@ -0,0 +1,3 @@
---
features:
- Generic group is added into quota management.