snap = db.snapshot_get(context.get_admin_context(), snapshot['id'])
self.assertEqual(snap['display_name'], 'test update name')
- def test_extend_volume(self):
+ @mock.patch.object(QUOTAS, 'reserve')
+ def test_extend_volume(self, reserve):
"""Test volume can be extended at API level."""
# create a volume and assign to host
volume = tests_utils.create_volume(self.context, size=2,
2)
# works when new_size > orig_size
+ reserve.return_value = ["RESERVATION"]
volume_api.extend(self.context, volume, 3)
-
volume = db.volume_get(context.get_admin_context(), volume['id'])
self.assertEqual(volume['status'], 'extending')
+ # Test the quota exceeded
+ volume['status'] = 'available'
+ reserve.side_effect = exception.OverQuota(overs=['gigabytes'],
+ quotas={'gigabytes': 20},
+ usages={'gigabytes':
+ {'reserved': 5,
+ 'in_use': 15}})
+ self.assertRaises(exception.VolumeSizeExceedsAvailableQuota,
+ volume_api.extend, self.context,
+ volume, 3)
+
# clean up
self.volume.delete_volume(self.context, volume['id'])
def test_extend_volume_driver_not_initialized(self):
"""Test volume can be extended at API level."""
# create a volume and assign to host
+ fake_reservations = ['RESERVATION']
volume = tests_utils.create_volume(self.context, size=2,
status='available',
host=CONF.host)
self.assertRaises(exception.DriverNotInitialized,
self.volume.extend_volume,
- self.context, volume['id'], 3)
+ self.context, volume['id'], 3,
+ fake_reservations)
volume = db.volume_get(context.get_admin_context(), volume['id'])
self.assertEqual(volume.status, 'error_extending')
def test_extend_volume_manager(self):
"""Test volume can be extended at the manager level."""
- def fake_reserve(context, expire=None, project_id=None, **deltas):
- return ['RESERVATION']
-
- def fake_reserve_exc(context, expire=None, project_id=None, **deltas):
- raise exception.OverQuota(overs=['gigabytes'],
- quotas={'gigabytes': 20},
- usages={'gigabytes': {'reserved': 5,
- 'in_use': 15}})
-
- def fake_extend_exc(volume, new_size):
- raise exception.CinderException('fake exception')
+ def fake_extend(volume, new_size):
+ volume['size'] = new_size
+ fake_reservations = ['RESERVATION']
volume = tests_utils.create_volume(self.context, size=2,
status='creating', host=CONF.host)
self.volume.create_volume(self.context, volume['id'])
- # Test quota exceeded
- self.stubs.Set(QUOTAS, 'reserve', fake_reserve_exc)
- self.stubs.Set(QUOTAS, 'commit', lambda x, y, project_id=None: True)
- self.stubs.Set(QUOTAS, 'rollback', lambda x, y: True)
- volume['status'] = 'extending'
- self.volume.extend_volume(self.context, volume['id'], '4')
- volume = db.volume_get(context.get_admin_context(), volume['id'])
- self.assertEqual(volume['size'], 2)
- self.assertEqual(volume['status'], 'error_extending')
-
# Test driver exception
- self.stubs.Set(QUOTAS, 'reserve', fake_reserve)
- self.stubs.Set(self.volume.driver, 'extend_volume', fake_extend_exc)
- volume['status'] = 'extending'
- self.volume.extend_volume(self.context, volume['id'], '4')
- volume = db.volume_get(context.get_admin_context(), volume['id'])
- self.assertEqual(volume['size'], 2)
- self.assertEqual(volume['status'], 'error_extending')
+ with mock.patch.object(self.volume.driver,
+ 'extend_volume') as extend_volume:
+ extend_volume.side_effect =\
+ exception.CinderException('fake exception')
+ volume['status'] = 'extending'
+ self.volume.extend_volume(self.context, volume['id'], '4',
+ fake_reservations)
+ volume = db.volume_get(context.get_admin_context(), volume['id'])
+ self.assertEqual(volume['size'], 2)
+ self.assertEqual(volume['status'], 'error_extending')
# Test driver success
- self.stubs.Set(self.volume.driver, 'extend_volume',
- lambda x, y: True)
- volume['status'] = 'extending'
- self.volume.extend_volume(self.context, volume['id'], '4')
- volume = db.volume_get(context.get_admin_context(), volume['id'])
- self.assertEqual(volume['size'], 4)
- self.assertEqual(volume['status'], 'available')
+ with mock.patch.object(self.volume.driver,
+ 'extend_volume') as extend_volume:
+ extend_volume.return_value = fake_extend
+ volume['status'] = 'extending'
+ self.volume.extend_volume(self.context, volume['id'], '4',
+ fake_reservations)
+ volume = db.volume_get(context.get_admin_context(), volume['id'])
+ self.assertEqual(volume['size'], 4)
+ self.assertEqual(volume['status'], 'available')
# clean up
self.volume.delete_volume(self.context, volume['id'])
'size': volume['size']})
raise exception.InvalidInput(reason=msg)
+ try:
+ reservations = QUOTAS.reserve(context, gigabytes=+size_increase)
+ except exception.OverQuota as exc:
+ self.db.volume_update(context, volume['id'],
+ {'status': 'error_extending'})
+ usages = exc.kwargs['usages']
+ quotas = exc.kwargs['quotas']
+
+ def _consumed(name):
+ return (usages[name]['reserved'] + usages[name]['in_use'])
+
+ msg = _("Quota exceeded for %(s_pid)s, tried to extend volume by "
+ "%(s_size)sG, (%(d_consumed)dG of %(d_quota)dG already "
+ "consumed).")
+ LOG.error(msg % {'s_pid': context.project_id,
+ 's_size': size_increase,
+ 'd_consumed': _consumed('gigabytes'),
+ 'd_quota': quotas['gigabytes']})
+ raise exception.VolumeSizeExceedsAvailableQuota(
+ requested=size_increase,
+ consumed=_consumed('gigabytes'),
+ quota=quotas['gigabytes'])
+
self.update(context, volume, {'status': 'extending'})
- self.volume_rpcapi.extend_volume(context, volume, new_size)
+ self.volume_rpcapi.extend_volume(context, volume, new_size,
+ reservations)
@wrap_check_policy
def migrate_volume(self, context, volume, host, force_host_copy):
class VolumeManager(manager.SchedulerDependentManager):
"""Manages attachable block storage devices."""
- RPC_API_VERSION = '1.13'
+ RPC_API_VERSION = '1.14'
def __init__(self, volume_driver=None, service_name=None,
*args, **kwargs):
context, snapshot, event_suffix,
extra_usage_info=extra_usage_info, host=self.host)
- def extend_volume(self, context, volume_id, new_size):
+ def extend_volume(self, context, volume_id, new_size, reservations):
try:
# NOTE(flaper87): Verify the driver is enabled
# before going forward. The exception will be caught
volume = self.db.volume_get(context, volume_id)
size_increase = (int(new_size)) - volume['size']
-
- try:
- reservations = QUOTAS.reserve(context, gigabytes=+size_increase)
- except exception.OverQuota as exc:
- self.db.volume_update(context, volume['id'],
- {'status': 'error_extending'})
- overs = exc.kwargs['overs']
- usages = exc.kwargs['usages']
- quotas = exc.kwargs['quotas']
-
- def _consumed(name):
- return (usages[name]['reserved'] + usages[name]['in_use'])
-
- if 'gigabytes' in overs:
- msg = _("Quota exceeded for %(s_pid)s, "
- "tried to extend volume by "
- "%(s_size)sG, (%(d_consumed)dG of %(d_quota)dG "
- "already consumed)")
- LOG.error(msg % {'s_pid': context.project_id,
- 's_size': size_increase,
- 'd_consumed': _consumed('gigabytes'),
- 'd_quota': quotas['gigabytes']})
- return
-
self._notify_about_volume_usage(context, volume, "resize.start")
try:
LOG.info(_("volume %s: extending"), volume['id'])
try:
self.db.volume_update(context, volume['id'],
{'status': 'error_extending'})
+ raise exception.CinderException(_("Volume %s: Error trying "
+ "to extend volume") %
+ volume_id)
finally:
QUOTAS.rollback(context, reservations)
return