Merge "Add Generic Volume Group Into Quota Management" into stable/ocata
This commit is contained in:
@@ -25,6 +25,7 @@ from cinder import utils
|
|||||||
|
|
||||||
|
|
||||||
QUOTAS = quota.QUOTAS
|
QUOTAS = quota.QUOTAS
|
||||||
|
GROUP_QUOTAS = quota.GROUP_QUOTAS
|
||||||
|
|
||||||
|
|
||||||
authorize = extensions.extension_authorizer('volume', 'quota_classes')
|
authorize = extensions.extension_authorizer('volume', 'quota_classes')
|
||||||
@@ -46,9 +47,11 @@ class QuotaClassSetsController(wsgi.Controller):
|
|||||||
db.sqlalchemy.api.authorize_quota_class_context(context, id)
|
db.sqlalchemy.api.authorize_quota_class_context(context, id)
|
||||||
except exception.NotAuthorized:
|
except exception.NotAuthorized:
|
||||||
raise webob.exc.HTTPForbidden()
|
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,
|
return self._format_quota_set(id, quota_set)
|
||||||
QUOTAS.get_class_quotas(context, id))
|
|
||||||
|
|
||||||
def update(self, req, id, body):
|
def update(self, req, id, body):
|
||||||
context = req.environ['cinder.context']
|
context = req.environ['cinder.context']
|
||||||
@@ -63,7 +66,7 @@ class QuotaClassSetsController(wsgi.Controller):
|
|||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
for key, value in body['quota_class_set'].items():
|
for key, value in body['quota_class_set'].items():
|
||||||
if key in QUOTAS:
|
if key in QUOTAS or key in GROUP_QUOTAS:
|
||||||
try:
|
try:
|
||||||
value = utils.validate_integer(value, key, min_value=-1,
|
value = utils.validate_integer(value, key, min_value=-1,
|
||||||
max_value=db.MAX_INT)
|
max_value=db.MAX_INT)
|
||||||
@@ -72,8 +75,12 @@ class QuotaClassSetsController(wsgi.Controller):
|
|||||||
db.quota_class_create(context, quota_class, key, value)
|
db.quota_class_create(context, quota_class, key, value)
|
||||||
except exception.AdminRequired:
|
except exception.AdminRequired:
|
||||||
raise webob.exc.HTTPForbidden()
|
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):
|
class Quota_classes(extensions.ExtensionDescriptor):
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ from cinder import quota_utils
|
|||||||
from cinder import utils
|
from cinder import utils
|
||||||
|
|
||||||
QUOTAS = quota.QUOTAS
|
QUOTAS = quota.QUOTAS
|
||||||
|
GROUP_QUOTAS = quota.GROUP_QUOTAS
|
||||||
NON_QUOTA_KEYS = ['tenant_id', 'id']
|
NON_QUOTA_KEYS = ['tenant_id', 'id']
|
||||||
|
|
||||||
authorize_update = extensions.extension_authorizer('volume', 'quotas:update')
|
authorize_update = extensions.extension_authorizer('volume', 'quotas:update')
|
||||||
@@ -63,6 +64,9 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
|
|
||||||
def _get_quotas(self, context, id, usages=False):
|
def _get_quotas(self, context, id, usages=False):
|
||||||
values = QUOTAS.get_project_quotas(context, id, usages=usages)
|
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:
|
if usages:
|
||||||
return values
|
return values
|
||||||
@@ -225,7 +229,8 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
# NOTE(ankit): Pass #1 - In this loop for body['quota_set'].items(),
|
# NOTE(ankit): Pass #1 - In this loop for body['quota_set'].items(),
|
||||||
# we figure out if we have any bad keys.
|
# we figure out if we have any bad keys.
|
||||||
for key, value in body['quota_set'].items():
|
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)
|
bad_keys.append(key)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -259,6 +264,10 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
# resources.
|
# resources.
|
||||||
quota_values = QUOTAS.get_project_quotas(context, target_project_id,
|
quota_values = QUOTAS.get_project_quotas(context, target_project_id,
|
||||||
defaults=False)
|
defaults=False)
|
||||||
|
group_quota_values = GROUP_QUOTAS.get_project_quotas(context,
|
||||||
|
target_project_id,
|
||||||
|
defaults=False)
|
||||||
|
quota_values.update(group_quota_values)
|
||||||
valid_quotas = {}
|
valid_quotas = {}
|
||||||
reservations = []
|
reservations = []
|
||||||
for key in body['quota_set'].keys():
|
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
|
res_change = new_quota_from_target_proj - orig_quota_from_target_proj
|
||||||
if res_change != 0:
|
if res_change != 0:
|
||||||
deltas = {res: res_change}
|
deltas = {res: res_change}
|
||||||
|
resources = QUOTAS.resources
|
||||||
|
resources.update(GROUP_QUOTAS.resources)
|
||||||
reservations += quota_utils.update_alloc_to_next_hard_limit(
|
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
|
return reservations
|
||||||
|
|
||||||
def defaults(self, req, id):
|
def defaults(self, req, id):
|
||||||
context = req.environ['cinder.context']
|
context = req.environ['cinder.context']
|
||||||
authorize_show(context)
|
authorize_show(context)
|
||||||
|
defaults = QUOTAS.get_defaults(context, project_id=id)
|
||||||
return self._format_quota_set(id, QUOTAS.get_defaults(
|
group_defaults = GROUP_QUOTAS.get_defaults(context, project_id=id)
|
||||||
context, project_id=id))
|
defaults.update(group_defaults)
|
||||||
|
return self._format_quota_set(id, defaults)
|
||||||
|
|
||||||
def delete(self, req, id):
|
def delete(self, req, id):
|
||||||
"""Delete Quota for a particular tenant.
|
"""Delete Quota for a particular tenant.
|
||||||
@@ -366,6 +378,9 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
try:
|
try:
|
||||||
project_quotas = QUOTAS.get_project_quotas(
|
project_quotas = QUOTAS.get_project_quotas(
|
||||||
ctxt, proj_id, usages=True, defaults=False)
|
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:
|
except exception.NotAuthorized:
|
||||||
raise webob.exc.HTTPForbidden()
|
raise webob.exc.HTTPForbidden()
|
||||||
|
|
||||||
@@ -382,6 +397,7 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
parent_id)
|
parent_id)
|
||||||
|
|
||||||
defaults = QUOTAS.get_defaults(ctxt, proj_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
|
# If the project which is being deleted has allocated part of its
|
||||||
# quota to its subprojects, then subprojects' quotas should be
|
# quota to its subprojects, then subprojects' quotas should be
|
||||||
# deleted first.
|
# deleted first.
|
||||||
@@ -416,8 +432,11 @@ class QuotaSetsController(wsgi.Controller):
|
|||||||
ctxt = req.environ['cinder.context']
|
ctxt = req.environ['cinder.context']
|
||||||
params = req.params
|
params = req.params
|
||||||
try:
|
try:
|
||||||
|
resources = QUOTAS.resources
|
||||||
|
resources.update(GROUP_QUOTAS.resources)
|
||||||
|
|
||||||
quota_utils.validate_setup_for_nested_quota_use(
|
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'))
|
fix_allocated_quotas=params.get('fix_allocated_quotas'))
|
||||||
except exception.InvalidNestedQuotaSetup as e:
|
except exception.InvalidNestedQuotaSetup as e:
|
||||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ def update_alloc_to_next_hard_limit(context, resources, deltas, res,
|
|||||||
expire, project_id):
|
expire, project_id):
|
||||||
from cinder import quota
|
from cinder import quota
|
||||||
QUOTAS = quota.QUOTAS
|
QUOTAS = quota.QUOTAS
|
||||||
|
GROUP_QUOTAS = quota.GROUP_QUOTAS
|
||||||
reservations = []
|
reservations = []
|
||||||
projects = get_project_hierarchy(context, project_id,
|
projects = get_project_hierarchy(context, project_id,
|
||||||
parents_as_ids=True).parents
|
parents_as_ids=True).parents
|
||||||
@@ -156,6 +157,10 @@ def update_alloc_to_next_hard_limit(context, resources, deltas, res,
|
|||||||
while projects and not hard_limit_found:
|
while projects and not hard_limit_found:
|
||||||
cur_proj_id = list(projects)[0]
|
cur_proj_id = list(projects)[0]
|
||||||
projects = projects[cur_proj_id]
|
projects = projects[cur_proj_id]
|
||||||
|
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(
|
cur_quota_lim = QUOTAS.get_by_project_or_default(
|
||||||
context, cur_proj_id, res)
|
context, cur_proj_id, res)
|
||||||
hard_limit_found = (cur_quota_lim != -1)
|
hard_limit_found = (cur_quota_lim != -1)
|
||||||
|
|||||||
@@ -43,13 +43,14 @@ CONF = cfg.CONF
|
|||||||
|
|
||||||
def make_body(root=True, gigabytes=1000, snapshots=10,
|
def make_body(root=True, gigabytes=1000, snapshots=10,
|
||||||
volumes=10, backups=10, backup_gigabytes=1000,
|
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,
|
resources = {'gigabytes': gigabytes,
|
||||||
'snapshots': snapshots,
|
'snapshots': snapshots,
|
||||||
'volumes': volumes,
|
'volumes': volumes,
|
||||||
'backups': backups,
|
'backups': backups,
|
||||||
'backup_gigabytes': backup_gigabytes,
|
'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
|
# need to consider preexisting volume types as well
|
||||||
volume_types = db.volume_type_get_all(context.get_admin_context())
|
volume_types = db.volume_type_get_all(context.get_admin_context())
|
||||||
|
|
||||||
|
|||||||
@@ -33,19 +33,21 @@ from cinder.volume import volume_types
|
|||||||
|
|
||||||
|
|
||||||
QUOTAS = quota.QUOTAS
|
QUOTAS = quota.QUOTAS
|
||||||
|
GROUP_QUOTAS = quota.GROUP_QUOTAS
|
||||||
|
|
||||||
|
|
||||||
def make_body(root=True, gigabytes=1000, snapshots=10,
|
def make_body(root=True, gigabytes=1000, snapshots=10,
|
||||||
volumes=10, backups=10,
|
volumes=10, backups=10,
|
||||||
backup_gigabytes=1000, per_volume_gigabytes=-1,
|
backup_gigabytes=1000, per_volume_gigabytes=-1,
|
||||||
volume_types_faked=None,
|
volume_types_faked=None,
|
||||||
tenant_id=fake.PROJECT_ID):
|
tenant_id=fake.PROJECT_ID, groups=10):
|
||||||
resources = {'gigabytes': gigabytes,
|
resources = {'gigabytes': gigabytes,
|
||||||
'snapshots': snapshots,
|
'snapshots': snapshots,
|
||||||
'volumes': volumes,
|
'volumes': volumes,
|
||||||
'backups': backups,
|
'backups': backups,
|
||||||
'per_volume_gigabytes': per_volume_gigabytes,
|
'per_volume_gigabytes': per_volume_gigabytes,
|
||||||
'backup_gigabytes': backup_gigabytes}
|
'backup_gigabytes': backup_gigabytes,
|
||||||
|
'groups': groups}
|
||||||
if not volume_types_faked:
|
if not volume_types_faked:
|
||||||
volume_types_faked = {'fake_type': None}
|
volume_types_faked = {'fake_type': None}
|
||||||
for volume_type in volume_types_faked:
|
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:
|
if not ctxt:
|
||||||
ctxt = context.get_admin_context()
|
ctxt = context.get_admin_context()
|
||||||
resources.update(QUOTAS.get_class_quotas(ctxt, quota_class))
|
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']:
|
if not request_body and not request_body['quota_class_set']:
|
||||||
resources.update(request_body['quota_class_set'])
|
resources.update(request_body['quota_class_set'])
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Generic group is added into quota management.
|
||||||
Reference in New Issue
Block a user