dcf66a96a4
We currently only validate the existence of a project on the default types code so that we cannot set it for non existing projects, but there are other places where we would benefit from having this validation, such as volume type access, setting quotas. To achieve this we are moving methods from quota_utils and default_types to api_utils. This patch also modifies the method get_project_hierarchy to get_project and removes related hierarchy tests since with the removal of nested quota driver we don't require the hierarchy properties anymore. Related-Bug: #1638804 Change-Id: I20f8e500f583eb85f24a4c36f344c11c7face06c
145 lines
5.8 KiB
Python
145 lines
5.8 KiB
Python
# Copyright 2013 OpenStack Foundation
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
|
|
from cinder import exception
|
|
|
|
CONF = cfg.CONF
|
|
CONF.import_group('keystone_authtoken',
|
|
'keystonemiddleware.auth_token.__init__')
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def get_volume_type_reservation(ctxt, volume, type_id,
|
|
reserve_vol_type_only=False,
|
|
negative=False):
|
|
from cinder import quota
|
|
QUOTAS = quota.QUOTAS
|
|
# Reserve quotas for the given volume type
|
|
try:
|
|
reserve_opts = {'volumes': 1, 'gigabytes': volume['size']}
|
|
# When retyping a volume it may contain snapshots (if we are not
|
|
# migrating it) and we need to account for its snapshots' size
|
|
if volume.snapshots:
|
|
reserve_opts['snapshots'] = len(volume.snapshots)
|
|
if not CONF.no_snapshot_gb_quota:
|
|
reserve_opts['gigabytes'] += sum(snap.volume_size
|
|
for snap in volume.snapshots)
|
|
QUOTAS.add_volume_type_opts(ctxt,
|
|
reserve_opts,
|
|
type_id)
|
|
# If reserve_vol_type_only is True, just reserve volume_type quota,
|
|
# not volume quota.
|
|
if reserve_vol_type_only:
|
|
reserve_opts.pop('volumes')
|
|
reserve_opts.pop('gigabytes')
|
|
reserve_opts.pop('snapshots', None)
|
|
|
|
if negative:
|
|
for key, value in reserve_opts.items():
|
|
reserve_opts[key] = -value
|
|
|
|
# Note that usually the project_id on the volume will be the same as
|
|
# the project_id in the context. But, if they are different then the
|
|
# reservations must be recorded against the project_id that owns the
|
|
# volume.
|
|
project_id = volume['project_id']
|
|
reservations = QUOTAS.reserve(ctxt,
|
|
project_id=project_id,
|
|
**reserve_opts)
|
|
except exception.OverQuota as e:
|
|
process_reserve_over_quota(ctxt, e,
|
|
resource='volumes',
|
|
size=volume.size)
|
|
return reservations
|
|
|
|
|
|
def _filter_domain_id_from_parents(domain_id, tree):
|
|
"""Removes the domain_id from the tree if present"""
|
|
new_tree = None
|
|
if tree:
|
|
parent, children = next(iter(tree.items()))
|
|
# Don't add the domain id to the parents hierarchy
|
|
if parent != domain_id:
|
|
new_tree = {parent: _filter_domain_id_from_parents(domain_id,
|
|
children)}
|
|
|
|
return new_tree
|
|
|
|
|
|
OVER_QUOTA_RESOURCE_EXCEPTIONS = {'snapshots': exception.SnapshotLimitExceeded,
|
|
'backups': exception.BackupLimitExceeded,
|
|
'volumes': exception.VolumeLimitExceeded,
|
|
'groups': exception.GroupLimitExceeded}
|
|
|
|
|
|
def process_reserve_over_quota(context, over_quota_exception,
|
|
resource, size=None):
|
|
"""Handle OverQuota exception.
|
|
|
|
Analyze OverQuota exception, and raise new exception related to
|
|
resource type. If there are unexpected items in overs,
|
|
UnexpectedOverQuota is raised.
|
|
|
|
:param context: security context
|
|
:param over_quota_exception: OverQuota exception
|
|
:param resource: can be backups, snapshots, and volumes
|
|
:param size: requested size in reservation
|
|
"""
|
|
def _consumed(name):
|
|
return (usages[name]['reserved'] + usages[name]['in_use'])
|
|
|
|
overs = over_quota_exception.kwargs['overs']
|
|
usages = over_quota_exception.kwargs['usages']
|
|
quotas = over_quota_exception.kwargs['quotas']
|
|
invalid_overs = []
|
|
|
|
for over in overs:
|
|
if 'gigabytes' in over:
|
|
msg = ("Quota exceeded for %(s_pid)s, tried to create "
|
|
"%(s_size)dG %(s_resource)s (%(d_consumed)dG of "
|
|
"%(d_quota)dG already consumed).")
|
|
LOG.warning(msg, {'s_pid': context.project_id,
|
|
's_size': size,
|
|
's_resource': resource[:-1],
|
|
'd_consumed': _consumed(over),
|
|
'd_quota': quotas[over]})
|
|
if resource == 'backups':
|
|
exc = exception.VolumeBackupSizeExceedsAvailableQuota
|
|
else:
|
|
exc = exception.VolumeSizeExceedsAvailableQuota
|
|
raise exc(
|
|
name=over,
|
|
requested=size,
|
|
consumed=_consumed(over),
|
|
quota=quotas[over])
|
|
if (resource in OVER_QUOTA_RESOURCE_EXCEPTIONS.keys() and
|
|
resource in over):
|
|
msg = ("Quota exceeded for %(s_pid)s, tried to create "
|
|
"%(s_resource)s (%(d_consumed)d %(s_resource)ss "
|
|
"already consumed).")
|
|
LOG.warning(msg, {'s_pid': context.project_id,
|
|
'd_consumed': _consumed(over),
|
|
's_resource': resource[:-1]})
|
|
raise OVER_QUOTA_RESOURCE_EXCEPTIONS[resource](
|
|
allowed=quotas[over],
|
|
name=over)
|
|
invalid_overs.append(over)
|
|
|
|
if invalid_overs:
|
|
raise exception.UnexpectedOverQuota(name=', '.join(invalid_overs))
|