From 492cf46f63c829ec722c0b8fb06de678e85afc5e Mon Sep 17 00:00:00 2001 From: wangxiyuan Date: Wed, 1 Mar 2017 16:46:45 +0800 Subject: [PATCH] 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. This is not typically something we would want to backport, but for the sake of keeping API consistency for later branches that expect to include this information, the easiest path for all is to just add this to the stable branch. Change-Id: I9ff0357984a1e95c1ea8c85f8c508402ef7135ac Implements: blueprint add-volumegroup-into-quota-management (cherry picked from commit 608de666fabf9ab65fa905a3b9a95f7cbad83013) --- cinder/api/contrib/quota_classes.py | 17 +++++++--- cinder/api/contrib/quotas.py | 31 +++++++++++++++---- cinder/quota_utils.py | 9 ++++-- cinder/tests/unit/api/contrib/test_quotas.py | 5 +-- .../unit/api/contrib/test_quotas_classes.py | 7 +++-- ...quota-manage-support-559629ad07a406f4.yaml | 3 ++ 6 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 releasenotes/notes/generic-group-quota-manage-support-559629ad07a406f4.yaml diff --git a/cinder/api/contrib/quota_classes.py b/cinder/api/contrib/quota_classes.py index a694d856cb7..bffd1a56fd5 100644 --- a/cinder/api/contrib/quota_classes.py +++ b/cinder/api/contrib/quota_classes.py @@ -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): diff --git a/cinder/api/contrib/quotas.py b/cinder/api/contrib/quotas.py index 1dbe8201f32..334426eed08 100644 --- a/cinder/api/contrib/quotas.py +++ b/cinder/api/contrib/quotas.py @@ -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) diff --git a/cinder/quota_utils.py b/cinder/quota_utils.py index eee4ddf2809..0425b6f0c00 100644 --- a/cinder/quota_utils.py +++ b/cinder/quota_utils.py @@ -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]} diff --git a/cinder/tests/unit/api/contrib/test_quotas.py b/cinder/tests/unit/api/contrib/test_quotas.py index cf499136f3f..8753fa476e6 100644 --- a/cinder/tests/unit/api/contrib/test_quotas.py +++ b/cinder/tests/unit/api/contrib/test_quotas.py @@ -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()) diff --git a/cinder/tests/unit/api/contrib/test_quotas_classes.py b/cinder/tests/unit/api/contrib/test_quotas_classes.py index 945f693cb00..d0b15870227 100644 --- a/cinder/tests/unit/api/contrib/test_quotas_classes.py +++ b/cinder/tests/unit/api/contrib/test_quotas_classes.py @@ -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']) diff --git a/releasenotes/notes/generic-group-quota-manage-support-559629ad07a406f4.yaml b/releasenotes/notes/generic-group-quota-manage-support-559629ad07a406f4.yaml new file mode 100644 index 00000000000..cc058ac783c --- /dev/null +++ b/releasenotes/notes/generic-group-quota-manage-support-559629ad07a406f4.yaml @@ -0,0 +1,3 @@ +--- +features: + - Generic group is added into quota management.