Handle quota exceed exception
This patch solves two problems: 1. Code duplication about handling quota exceed exceptions. 2. When extending a volume, if OverQuota is caught, Cinder raised VolumeSizeExceedsAvailableQuota with data from quota gigabytes. This is not correct, as it may exceed gigabytes of volume type. This patch is to transfer quota data from gigabytes or [volume_type_name]gigabytes to VolumeSizeExceedsAvailableQuota. 3. Raise new exception if overs are not expected. Change-Id: I811ce51a4aefe8a99f0f5197ab071212322c584d Closes-bug: 1546890
This commit is contained in:
@@ -33,11 +33,12 @@ from cinder.backup import rpcapi as backup_rpcapi
|
||||
from cinder import context
|
||||
from cinder.db import base
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LI, _LW
|
||||
from cinder.i18n import _, _LI
|
||||
from cinder import objects
|
||||
from cinder.objects import fields
|
||||
import cinder.policy
|
||||
from cinder import quota
|
||||
from cinder import quota_utils
|
||||
from cinder import utils
|
||||
import cinder.volume
|
||||
from cinder.volume import utils as volume_utils
|
||||
@@ -279,37 +280,10 @@ class API(base.Base):
|
||||
'backup_gigabytes': volume['size']}
|
||||
reservations = QUOTAS.reserve(context, **reserve_opts)
|
||||
except exception.OverQuota as e:
|
||||
overs = e.kwargs['overs']
|
||||
usages = e.kwargs['usages']
|
||||
quotas = e.kwargs['quotas']
|
||||
|
||||
def _consumed(resource_name):
|
||||
return (usages[resource_name]['reserved'] +
|
||||
usages[resource_name]['in_use'])
|
||||
|
||||
for over in overs:
|
||||
if 'gigabytes' in over:
|
||||
msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
|
||||
"%(s_size)sG backup (%(d_consumed)dG of "
|
||||
"%(d_quota)dG already consumed)")
|
||||
LOG.warning(msg, {'s_pid': context.project_id,
|
||||
's_size': volume['size'],
|
||||
'd_consumed': _consumed(over),
|
||||
'd_quota': quotas[over]})
|
||||
raise exception.VolumeBackupSizeExceedsAvailableQuota(
|
||||
requested=volume['size'],
|
||||
consumed=_consumed('backup_gigabytes'),
|
||||
quota=quotas['backup_gigabytes'])
|
||||
elif 'backups' in over:
|
||||
msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
|
||||
"backups (%(d_consumed)d backups "
|
||||
"already consumed)")
|
||||
|
||||
LOG.warning(msg, {'s_pid': context.project_id,
|
||||
'd_consumed': _consumed(over)})
|
||||
raise exception.BackupLimitExceeded(
|
||||
allowed=quotas[over])
|
||||
|
||||
quota_utils.process_reserve_over_quota(
|
||||
context, e,
|
||||
resource='backups',
|
||||
size=volume.size)
|
||||
# Find the latest backup and use it as the parent backup to do an
|
||||
# incremental backup.
|
||||
latest_backup = None
|
||||
|
||||
@@ -540,6 +540,10 @@ class SnapshotLimitExceeded(QuotaError):
|
||||
message = _("Maximum number of snapshots allowed (%(allowed)d) exceeded")
|
||||
|
||||
|
||||
class UnexpectedOverQuota(QuotaError):
|
||||
message = _("Unexpected over quota on %(name)s.")
|
||||
|
||||
|
||||
class BackupLimitExceeded(QuotaError):
|
||||
message = _("Maximum number of backups allowed (%(allowed)d) exceeded")
|
||||
|
||||
|
||||
@@ -70,37 +70,9 @@ def get_volume_type_reservation(ctxt, volume, type_id,
|
||||
project_id=project_id,
|
||||
**reserve_opts)
|
||||
except exception.OverQuota as e:
|
||||
overs = e.kwargs['overs']
|
||||
usages = e.kwargs['usages']
|
||||
quotas = e.kwargs['quotas']
|
||||
|
||||
def _consumed(name):
|
||||
return (usages[name]['reserved'] + usages[name]['in_use'])
|
||||
|
||||
for over in overs:
|
||||
if 'gigabytes' in over:
|
||||
s_size = volume['size']
|
||||
d_quota = quotas[over]
|
||||
d_consumed = _consumed(over)
|
||||
LOG.warning(
|
||||
_LW("Quota exceeded for %(s_pid)s, tried to create "
|
||||
"%(s_size)sG volume - (%(d_consumed)dG of "
|
||||
"%(d_quota)dG already consumed)"),
|
||||
{'s_pid': ctxt.project_id,
|
||||
's_size': s_size,
|
||||
'd_consumed': d_consumed,
|
||||
'd_quota': d_quota})
|
||||
raise exception.VolumeSizeExceedsAvailableQuota(
|
||||
requested=s_size, quota=d_quota, consumed=d_consumed)
|
||||
elif 'volumes' in over:
|
||||
LOG.warning(
|
||||
_LW("Quota exceeded for %(s_pid)s, tried to create "
|
||||
"volume (%(d_consumed)d volumes "
|
||||
"already consumed)"),
|
||||
{'s_pid': ctxt.project_id,
|
||||
'd_consumed': _consumed(over)})
|
||||
raise exception.VolumeLimitExceeded(
|
||||
allowed=quotas[over])
|
||||
process_reserve_over_quota(ctxt, e,
|
||||
resource='volumes',
|
||||
size=volume.size)
|
||||
return reservations
|
||||
|
||||
|
||||
@@ -261,3 +233,65 @@ def _keystone_client(context, version=(3, 0)):
|
||||
(CONF.keystone_authtoken.cafile or True))
|
||||
return client.Client(auth_url=CONF.keystone_authtoken.auth_uri,
|
||||
session=client_session, version=version)
|
||||
|
||||
|
||||
OVER_QUOTA_RESOURCE_EXCEPTIONS = {'snapshots': exception.SnapshotLimitExceeded,
|
||||
'backups': exception.BackupLimitExceeded,
|
||||
'volumes': exception.VolumeLimitExceeded, }
|
||||
|
||||
|
||||
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 = _LW("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 = _LW("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))
|
||||
|
||||
@@ -165,3 +165,80 @@ class QuotaUtilsTest(test.TestCase):
|
||||
self.assertRaises(exception.CinderException,
|
||||
quota_utils.validate_setup_for_nested_quota_use,
|
||||
self.context, [], None)
|
||||
|
||||
def _process_reserve_over_quota(self, overs, usages, quotas,
|
||||
expected_ex,
|
||||
resource='volumes'):
|
||||
ctxt = context.get_admin_context()
|
||||
ctxt.project_id = 'fake'
|
||||
size = 1
|
||||
kwargs = {'overs': overs,
|
||||
'usages': usages,
|
||||
'quotas': quotas}
|
||||
exc = exception.OverQuota(**kwargs)
|
||||
|
||||
self.assertRaises(expected_ex,
|
||||
quota_utils.process_reserve_over_quota,
|
||||
ctxt, exc,
|
||||
resource=resource,
|
||||
size=size)
|
||||
|
||||
def test_volume_size_exceed_quota(self):
|
||||
overs = ['gigabytes']
|
||||
usages = {'gigabytes': {'reserved': 1, 'in_use': 9}}
|
||||
quotas = {'gigabytes': 10, 'snapshots': 10}
|
||||
self._process_reserve_over_quota(
|
||||
overs, usages, quotas,
|
||||
exception.VolumeSizeExceedsAvailableQuota)
|
||||
|
||||
def test_snapshot_limit_exceed_quota(self):
|
||||
overs = ['snapshots']
|
||||
usages = {'snapshots': {'reserved': 1, 'in_use': 9}}
|
||||
quotas = {'gigabytes': 10, 'snapshots': 10}
|
||||
self._process_reserve_over_quota(
|
||||
overs, usages, quotas,
|
||||
exception.SnapshotLimitExceeded,
|
||||
resource='snapshots')
|
||||
|
||||
def test_backup_gigabytes_exceed_quota(self):
|
||||
overs = ['backup_gigabytes']
|
||||
usages = {'backup_gigabytes': {'reserved': 1, 'in_use': 9}}
|
||||
quotas = {'backup_gigabytes': 10}
|
||||
self._process_reserve_over_quota(
|
||||
overs, usages, quotas,
|
||||
exception.VolumeBackupSizeExceedsAvailableQuota,
|
||||
resource='backups')
|
||||
|
||||
def test_backup_limit_quota(self):
|
||||
overs = ['backups']
|
||||
usages = {'backups': {'reserved': 1, 'in_use': 9}}
|
||||
quotas = {'backups': 9}
|
||||
self._process_reserve_over_quota(
|
||||
overs, usages, quotas,
|
||||
exception.BackupLimitExceeded,
|
||||
resource='backups')
|
||||
|
||||
def test_volumes_limit_quota(self):
|
||||
overs = ['volumes']
|
||||
usages = {'volumes': {'reserved': 1, 'in_use': 9}}
|
||||
quotas = {'volumes': 9}
|
||||
self._process_reserve_over_quota(
|
||||
overs, usages, quotas,
|
||||
exception.VolumeLimitExceeded)
|
||||
|
||||
def test_unknown_quota(self):
|
||||
overs = ['unknown']
|
||||
usages = {'volumes': {'reserved': 1, 'in_use': 9}}
|
||||
quotas = {'volumes': 9}
|
||||
self._process_reserve_over_quota(
|
||||
overs, usages, quotas,
|
||||
exception.UnexpectedOverQuota)
|
||||
|
||||
def test_unknown_quota2(self):
|
||||
overs = ['volumes']
|
||||
usages = {'volumes': {'reserved': 1, 'in_use': 9}}
|
||||
quotas = {'volumes': 9}
|
||||
self._process_reserve_over_quota(
|
||||
overs, usages, quotas,
|
||||
exception.UnexpectedOverQuota,
|
||||
resource='snapshots')
|
||||
|
||||
@@ -88,6 +88,12 @@ fake_opt = [
|
||||
]
|
||||
|
||||
|
||||
OVER_SNAPSHOT_QUOTA_EXCEPTION = exception.OverQuota(
|
||||
overs=['snapshots'],
|
||||
usages = {'snapshots': {'reserved': 1, 'in_use': 9}},
|
||||
quotas = {'gigabytes': 10, 'snapshots': 10})
|
||||
|
||||
|
||||
def create_snapshot(volume_id, size=1, metadata=None, ctxt=None,
|
||||
**kwargs):
|
||||
"""Create a snapshot object."""
|
||||
@@ -3169,10 +3175,8 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
"""Test exception handling when create snapshot in db failed."""
|
||||
test_volume = tests_utils.create_volume(
|
||||
self.context,
|
||||
**self.volume_params)
|
||||
self.volume.create_volume(self.context, test_volume.id,
|
||||
volume=test_volume)
|
||||
test_volume['status'] = 'available'
|
||||
status='available',
|
||||
host=CONF.host)
|
||||
volume_api = cinder.volume.api.API()
|
||||
self.assertRaises(exception.InvalidSnapshot,
|
||||
volume_api.create_snapshot,
|
||||
@@ -3185,10 +3189,8 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
"""Test exception handling when create snapshot in maintenance."""
|
||||
test_volume = tests_utils.create_volume(
|
||||
self.context,
|
||||
**self.volume_params)
|
||||
self.volume.create_volume(self.context, test_volume.id,
|
||||
volume=test_volume)
|
||||
test_volume['status'] = 'maintenance'
|
||||
status='maintenance',
|
||||
host=CONF.host)
|
||||
volume_api = cinder.volume.api.API()
|
||||
self.assertRaises(exception.InvalidVolume,
|
||||
volume_api.create_snapshot,
|
||||
@@ -3204,10 +3206,8 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
"""Test exception handling when snapshot quota commit failed."""
|
||||
test_volume = tests_utils.create_volume(
|
||||
self.context,
|
||||
**self.volume_params)
|
||||
self.volume.create_volume(self.context, test_volume.id,
|
||||
request_spec={}, volume=test_volume)
|
||||
test_volume['status'] = 'available'
|
||||
status='available',
|
||||
host=CONF.host)
|
||||
volume_api = cinder.volume.api.API()
|
||||
self.assertRaises(exception.QuotaError,
|
||||
volume_api.create_snapshot,
|
||||
@@ -3216,6 +3216,40 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
'fake_name',
|
||||
'fake_description')
|
||||
|
||||
@mock.patch.object(QUOTAS, 'reserve',
|
||||
side_effect = OVER_SNAPSHOT_QUOTA_EXCEPTION)
|
||||
def test_create_snapshot_failed_quota_reserve(self, mock_reserve):
|
||||
"""Test exception handling when snapshot quota reserve failed."""
|
||||
test_volume = tests_utils.create_volume(
|
||||
self.context,
|
||||
status='available',
|
||||
host=CONF.host)
|
||||
volume_api = cinder.volume.api.API()
|
||||
self.assertRaises(exception.SnapshotLimitExceeded,
|
||||
volume_api.create_snapshot,
|
||||
self.context,
|
||||
test_volume,
|
||||
'fake_name',
|
||||
'fake_description')
|
||||
|
||||
@mock.patch.object(QUOTAS, 'reserve',
|
||||
side_effect = OVER_SNAPSHOT_QUOTA_EXCEPTION)
|
||||
def test_create_snapshots_in_db_failed_quota_reserve(self, mock_reserve):
|
||||
"""Test exception handling when snapshot quota reserve failed."""
|
||||
test_volume = tests_utils.create_volume(
|
||||
self.context,
|
||||
status='available',
|
||||
host=CONF.host)
|
||||
volume_api = cinder.volume.api.API()
|
||||
self.assertRaises(exception.SnapshotLimitExceeded,
|
||||
volume_api.create_snapshots_in_db,
|
||||
self.context,
|
||||
[test_volume],
|
||||
'fake_name',
|
||||
'fake_description',
|
||||
False,
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
|
||||
def test_cannot_delete_volume_in_use(self):
|
||||
"""Test volume can't be deleted in in-use status."""
|
||||
self._test_cannot_delete_volume('in-use')
|
||||
@@ -4589,6 +4623,20 @@ class VolumeTestCase(BaseVolumeTestCase):
|
||||
'is_snapshot': False}
|
||||
self.assertEqual(expected_result, result)
|
||||
|
||||
@mock.patch.object(QUOTAS, 'reserve',
|
||||
side_effect = OVER_SNAPSHOT_QUOTA_EXCEPTION)
|
||||
def test_existing_snapshot_failed_quota_reserve(self, mock_reserve):
|
||||
vol = tests_utils.create_volume(self.context)
|
||||
snap = tests_utils.create_snapshot(self.context, vol.id)
|
||||
with mock.patch.object(
|
||||
self.volume.driver,
|
||||
'manage_existing_snapshot_get_size') as mock_get_size:
|
||||
mock_get_size.return_value = 1
|
||||
self.assertRaises(exception.SnapshotLimitExceeded,
|
||||
self.volume.manage_existing_snapshot,
|
||||
self.context,
|
||||
snap)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class VolumeMigrationTestCase(BaseVolumeTestCase):
|
||||
|
||||
@@ -816,20 +816,3 @@ class VolumeUtilsTestCase(test.TestCase):
|
||||
self.assertEqual(
|
||||
expected_dict,
|
||||
volume_utils.convert_config_string_to_dict(test_string))
|
||||
|
||||
def test_process_reserve_over_quota(self):
|
||||
ctxt = context.get_admin_context()
|
||||
ctxt.project_id = 'fake'
|
||||
overs_one = ['gigabytes']
|
||||
over_two = ['snapshots']
|
||||
usages = {'gigabytes': {'reserved': 1, 'in_use': 9},
|
||||
'snapshots': {'reserved': 1, 'in_use': 9}}
|
||||
quotas = {'gigabytes': 10, 'snapshots': 10}
|
||||
size = 1
|
||||
|
||||
self.assertRaises(exception.VolumeSizeExceedsAvailableQuota,
|
||||
volume_utils.process_reserve_over_quota,
|
||||
ctxt, overs_one, usages, quotas, size)
|
||||
self.assertRaises(exception.SnapshotLimitExceeded,
|
||||
volume_utils.process_reserve_over_quota,
|
||||
ctxt, over_two, usages, quotas, size)
|
||||
|
||||
@@ -29,8 +29,9 @@ import six
|
||||
|
||||
from cinder.db import base
|
||||
from cinder import exception
|
||||
from cinder.i18n import _, _LE, _LI, _LW
|
||||
from cinder.i18n import _, _LE, _LI
|
||||
from cinder import quota
|
||||
from cinder import quota_utils
|
||||
from cinder.volume import api as volume_api
|
||||
from cinder.volume import utils as volume_utils
|
||||
|
||||
@@ -178,35 +179,9 @@ class API(base.Base):
|
||||
vol_ref.volume_type_id)
|
||||
reservations = QUOTAS.reserve(context, **reserve_opts)
|
||||
except exception.OverQuota as e:
|
||||
overs = e.kwargs['overs']
|
||||
usages = e.kwargs['usages']
|
||||
quotas = e.kwargs['quotas']
|
||||
|
||||
def _consumed(name):
|
||||
return (usages[name]['reserved'] + usages[name]['in_use'])
|
||||
|
||||
for over in overs:
|
||||
if 'gigabytes' in over:
|
||||
msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
|
||||
"%(s_size)sG volume (%(d_consumed)dG of "
|
||||
"%(d_quota)dG already consumed)")
|
||||
LOG.warning(msg, {'s_pid': context.project_id,
|
||||
's_size': vol_ref['size'],
|
||||
'd_consumed': _consumed(over),
|
||||
'd_quota': quotas[over]})
|
||||
raise exception.VolumeSizeExceedsAvailableQuota(
|
||||
requested=vol_ref['size'],
|
||||
consumed=_consumed(over),
|
||||
quota=quotas[over])
|
||||
elif 'volumes' in over:
|
||||
msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
|
||||
"volume (%(d_consumed)d volumes "
|
||||
"already consumed)")
|
||||
LOG.warning(msg, {'s_pid': context.project_id,
|
||||
'd_consumed': _consumed(over)})
|
||||
raise exception.VolumeLimitExceeded(allowed=quotas[over],
|
||||
name=over)
|
||||
|
||||
quota_utils.process_reserve_over_quota(context, e,
|
||||
resource='volumes',
|
||||
size=vol_ref.size)
|
||||
try:
|
||||
donor_id = vol_ref['project_id']
|
||||
reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
|
||||
|
||||
@@ -750,36 +750,10 @@ class API(base.Base):
|
||||
volume.get('volume_type_id'))
|
||||
reservations = QUOTAS.reserve(context, **reserve_opts)
|
||||
except exception.OverQuota as e:
|
||||
overs = e.kwargs['overs']
|
||||
usages = e.kwargs['usages']
|
||||
quotas = e.kwargs['quotas']
|
||||
|
||||
def _consumed(name):
|
||||
return (usages[name]['reserved'] + usages[name]['in_use'])
|
||||
|
||||
for over in overs:
|
||||
if 'gigabytes' in over:
|
||||
msg = _LW("Quota exceeded for %(s_pid)s, tried to "
|
||||
"create %(s_size)sG snapshot (%(d_consumed)d"
|
||||
"G of %(d_quota)dG already consumed).")
|
||||
LOG.warning(msg, {'s_pid': context.project_id,
|
||||
's_size': volume['size'],
|
||||
'd_consumed': _consumed(over),
|
||||
'd_quota': quotas[over]})
|
||||
raise exception.VolumeSizeExceedsAvailableQuota(
|
||||
requested=volume['size'],
|
||||
consumed=_consumed('gigabytes'),
|
||||
quota=quotas['gigabytes'])
|
||||
elif 'snapshots' in over:
|
||||
msg = _LW("Quota exceeded for %(s_pid)s, tried to "
|
||||
"create snapshot (%(d_consumed)d snapshots "
|
||||
"already consumed).")
|
||||
|
||||
LOG.warning(msg, {'s_pid': context.project_id,
|
||||
'd_consumed': _consumed(over)})
|
||||
raise exception.SnapshotLimitExceeded(
|
||||
allowed=quotas[over])
|
||||
|
||||
quota_utils.process_reserve_over_quota(
|
||||
context, e,
|
||||
resource='snapshots',
|
||||
size=volume.size)
|
||||
self._check_metadata_properties(metadata)
|
||||
|
||||
snapshot = None
|
||||
@@ -893,11 +867,9 @@ class API(base.Base):
|
||||
total_reserve_opts[key] + value
|
||||
reservations = QUOTAS.reserve(context, **total_reserve_opts)
|
||||
except exception.OverQuota as e:
|
||||
overs = e.kwargs['overs']
|
||||
usages = e.kwargs['usages']
|
||||
quotas = e.kwargs['quotas']
|
||||
volume_utils.process_reserve_over_quota(context, overs, usages,
|
||||
quotas, volume['size'])
|
||||
quota_utils.process_reserve_over_quota(context, e,
|
||||
resource='snapshots',
|
||||
size=volume.size)
|
||||
|
||||
return reservations
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ from cinder import objects
|
||||
from cinder.objects import fields
|
||||
from cinder import policy
|
||||
from cinder import quota
|
||||
from cinder import quota_utils
|
||||
from cinder import utils
|
||||
from cinder.volume.flows import common
|
||||
from cinder.volume import utils as vol_utils
|
||||
@@ -601,53 +602,9 @@ class QuotaReserveTask(flow_utils.CinderTask):
|
||||
'reservations': reservations,
|
||||
}
|
||||
except exception.OverQuota as e:
|
||||
overs = e.kwargs['overs']
|
||||
quotas = e.kwargs['quotas']
|
||||
usages = e.kwargs['usages']
|
||||
|
||||
def _consumed(name):
|
||||
usage = usages[name]
|
||||
return usage['reserved'] + usage['in_use'] + usage.get(
|
||||
'allocated', 0)
|
||||
|
||||
def _get_over(name):
|
||||
for over in overs:
|
||||
if name in over:
|
||||
return over
|
||||
return None
|
||||
|
||||
over_name = _get_over('gigabytes')
|
||||
exceeded_vol_limit_name = _get_over('volumes')
|
||||
if over_name:
|
||||
# TODO(mc_nair): improve error message for child -1 limit
|
||||
msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
|
||||
"%(s_size)sG volume (%(d_consumed)dG "
|
||||
"of %(d_quota)dG already consumed)")
|
||||
LOG.warning(msg, {'s_pid': context.project_id,
|
||||
's_size': size,
|
||||
'd_consumed': _consumed(over_name),
|
||||
'd_quota': quotas[over_name]})
|
||||
raise exception.VolumeSizeExceedsAvailableQuota(
|
||||
name=over_name,
|
||||
requested=size,
|
||||
consumed=_consumed(over_name),
|
||||
quota=quotas[over_name])
|
||||
elif exceeded_vol_limit_name:
|
||||
msg = _LW("Quota %(s_name)s exceeded for %(s_pid)s, tried "
|
||||
"to create volume (%(d_consumed)d volume(s) "
|
||||
"already consumed).")
|
||||
LOG.warning(msg,
|
||||
{'s_name': exceeded_vol_limit_name,
|
||||
's_pid': context.project_id,
|
||||
'd_consumed':
|
||||
_consumed(exceeded_vol_limit_name)})
|
||||
# TODO(mc_nair): improve error message for child -1 limit
|
||||
raise exception.VolumeLimitExceeded(
|
||||
allowed=quotas[exceeded_vol_limit_name],
|
||||
name=exceeded_vol_limit_name)
|
||||
else:
|
||||
# If nothing was reraised, ensure we reraise the initial error
|
||||
raise
|
||||
quota_utils.process_reserve_over_quota(context, e,
|
||||
resource='volumes',
|
||||
size=size)
|
||||
|
||||
def revert(self, context, result, optional_args, **kwargs):
|
||||
# We never produced a result and therefore can't destroy anything.
|
||||
|
||||
@@ -23,6 +23,7 @@ from cinder import flow_utils
|
||||
from cinder.i18n import _, _LE, _LI
|
||||
from cinder import objects
|
||||
from cinder import quota
|
||||
from cinder import quota_utils
|
||||
from cinder.volume.flows import common as flow_common
|
||||
from cinder.volume import utils as volume_utils
|
||||
|
||||
@@ -153,11 +154,10 @@ class QuotaReserveTask(flow_utils.CinderTask):
|
||||
'reservations': reservations,
|
||||
}
|
||||
except exception.OverQuota as e:
|
||||
overs = e.kwargs['overs']
|
||||
quotas = e.kwargs['quotas']
|
||||
usages = e.kwargs['usages']
|
||||
volume_utils.process_reserve_over_quota(context, overs, usages,
|
||||
quotas, size)
|
||||
quota_utils.process_reserve_over_quota(
|
||||
context, e,
|
||||
resource='snapshots',
|
||||
size=size)
|
||||
|
||||
def revert(self, context, result, optional_args, **kwargs):
|
||||
# We never produced a result and therefore can't destroy anything.
|
||||
|
||||
@@ -710,29 +710,3 @@ def convert_config_string_to_dict(config_string):
|
||||
{'config_string': config_string})
|
||||
|
||||
return resultant_dict
|
||||
|
||||
|
||||
def process_reserve_over_quota(context, overs, usages, quotas, size):
|
||||
def _consumed(name):
|
||||
return (usages[name]['reserved'] + usages[name]['in_use'])
|
||||
|
||||
for over in overs:
|
||||
if 'gigabytes' in over:
|
||||
msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
|
||||
"%(s_size)sG snapshot (%(d_consumed)dG of "
|
||||
"%(d_quota)dG already consumed).")
|
||||
LOG.warning(msg, {'s_pid': context.project_id,
|
||||
's_size': size,
|
||||
'd_consumed': _consumed(over),
|
||||
'd_quota': quotas[over]})
|
||||
raise exception.VolumeSizeExceedsAvailableQuota(
|
||||
requested=size,
|
||||
consumed=_consumed('gigabytes'),
|
||||
quota=quotas['gigabytes'])
|
||||
elif 'snapshots' in over:
|
||||
msg = _LW("Quota exceeded for %(s_pid)s, tried to create "
|
||||
"snapshot (%(d_consumed)d snapshots "
|
||||
"already consumed).")
|
||||
LOG.warning(msg, {'s_pid': context.project_id,
|
||||
'd_consumed': _consumed(over)})
|
||||
raise exception.SnapshotLimitExceeded(allowed=quotas[over])
|
||||
|
||||
Reference in New Issue
Block a user