Merge "Move retype quota checks to API"
This commit is contained in:
@@ -216,6 +216,7 @@ class SchedulerManager(manager.Manager):
|
|||||||
context, ex, request_spec, msg)
|
context, ex, request_spec, msg)
|
||||||
|
|
||||||
reservations = request_spec.get('quota_reservations')
|
reservations = request_spec.get('quota_reservations')
|
||||||
|
old_reservations = request_spec.get('old_reservations', None)
|
||||||
new_type = request_spec.get('volume_type')
|
new_type = request_spec.get('volume_type')
|
||||||
if new_type is None:
|
if new_type is None:
|
||||||
msg = _('New volume type not specified in request_spec.')
|
msg = _('New volume type not specified in request_spec.')
|
||||||
@@ -245,7 +246,9 @@ class SchedulerManager(manager.Manager):
|
|||||||
else:
|
else:
|
||||||
volume_rpcapi.VolumeAPI().retype(context, volume,
|
volume_rpcapi.VolumeAPI().retype(context, volume,
|
||||||
new_type['id'], tgt_host,
|
new_type['id'], tgt_host,
|
||||||
migration_policy, reservations)
|
migration_policy,
|
||||||
|
reservations,
|
||||||
|
old_reservations)
|
||||||
|
|
||||||
def manage_existing(self, context, topic, volume_id,
|
def manage_existing(self, context, topic, volume_id,
|
||||||
request_spec, filter_properties=None):
|
request_spec, filter_properties=None):
|
||||||
|
|||||||
@@ -4843,6 +4843,14 @@ class VolumeMigrationTestCase(VolumeTestCase):
|
|||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
**reserve_opts)
|
**reserve_opts)
|
||||||
|
|
||||||
|
old_reserve_opts = {'volumes': -1, 'gigabytes': -volume.size}
|
||||||
|
QUOTAS.add_volume_type_opts(self.context,
|
||||||
|
old_reserve_opts,
|
||||||
|
old_vol_type['id'])
|
||||||
|
old_reservations = QUOTAS.reserve(self.context,
|
||||||
|
project_id=project_id,
|
||||||
|
**old_reserve_opts)
|
||||||
|
|
||||||
with mock.patch.object(self.volume.driver, 'retype') as _retype,\
|
with mock.patch.object(self.volume.driver, 'retype') as _retype,\
|
||||||
mock.patch.object(volume_types, 'volume_types_diff') as _diff,\
|
mock.patch.object(volume_types, 'volume_types_diff') as _diff,\
|
||||||
mock.patch.object(self.volume, 'migrate_volume') as _mig,\
|
mock.patch.object(self.volume, 'migrate_volume') as _mig,\
|
||||||
@@ -4860,6 +4868,7 @@ class VolumeMigrationTestCase(VolumeTestCase):
|
|||||||
vol_type['id'], host_obj,
|
vol_type['id'], host_obj,
|
||||||
migration_policy=policy,
|
migration_policy=policy,
|
||||||
reservations=reservations,
|
reservations=reservations,
|
||||||
|
old_reservations=old_reservations,
|
||||||
volume=volume)
|
volume=volume)
|
||||||
else:
|
else:
|
||||||
self.assertRaises(exc, self.volume.retype,
|
self.assertRaises(exc, self.volume.retype,
|
||||||
@@ -4867,6 +4876,7 @@ class VolumeMigrationTestCase(VolumeTestCase):
|
|||||||
vol_type['id'], host_obj,
|
vol_type['id'], host_obj,
|
||||||
migration_policy=policy,
|
migration_policy=policy,
|
||||||
reservations=reservations,
|
reservations=reservations,
|
||||||
|
old_reservations=old_reservations,
|
||||||
volume=volume)
|
volume=volume)
|
||||||
|
|
||||||
# get volume/quota properties
|
# get volume/quota properties
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import copy
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
import oslo_messaging as messaging
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
from cinder import context
|
from cinder import context
|
||||||
@@ -459,27 +460,52 @@ class VolumeRpcAPITestCase(test.TestCase):
|
|||||||
new_type_id='fake',
|
new_type_id='fake',
|
||||||
dest_host=dest_host,
|
dest_host=dest_host,
|
||||||
migration_policy='never',
|
migration_policy='never',
|
||||||
reservations=None,
|
reservations=self.fake_reservations,
|
||||||
version='1.34')
|
old_reservations=self.fake_reservations,
|
||||||
can_send_version.assert_called_once_with('1.34')
|
version='1.37')
|
||||||
|
can_send_version.assert_called_once_with('1.37')
|
||||||
|
|
||||||
@mock.patch('oslo_messaging.RPCClient.can_send_version',
|
def test_retype_version_134(self):
|
||||||
return_value=False)
|
|
||||||
def test_retype_old(self, can_send_version):
|
|
||||||
class FakeHost(object):
|
class FakeHost(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.host = 'host'
|
self.host = 'host'
|
||||||
self.capabilities = {}
|
self.capabilities = {}
|
||||||
dest_host = FakeHost()
|
dest_host = FakeHost()
|
||||||
self._test_volume_api('retype',
|
with mock.patch.object(messaging.RPCClient,
|
||||||
rpc_method='cast',
|
'can_send_version',
|
||||||
volume=self.fake_volume_obj,
|
side_effect=[False, True]) as can_send_version:
|
||||||
new_type_id='fake',
|
self._test_volume_api('retype',
|
||||||
dest_host=dest_host,
|
rpc_method='cast',
|
||||||
migration_policy='never',
|
volume=self.fake_volume_obj,
|
||||||
reservations=None,
|
new_type_id='fake',
|
||||||
version='1.12')
|
dest_host=dest_host,
|
||||||
can_send_version.assert_called_once_with('1.34')
|
migration_policy='never',
|
||||||
|
reservations=self.fake_reservations,
|
||||||
|
old_reservations=self.fake_reservations,
|
||||||
|
version='1.34')
|
||||||
|
can_send_version.assert_any_call('1.37')
|
||||||
|
can_send_version.assert_any_call('1.34')
|
||||||
|
|
||||||
|
def test_retype_version_112(self):
|
||||||
|
class FakeHost(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.host = 'host'
|
||||||
|
self.capabilities = {}
|
||||||
|
dest_host = FakeHost()
|
||||||
|
with mock.patch.object(messaging.RPCClient,
|
||||||
|
'can_send_version',
|
||||||
|
side_effect=[False, False]) as can_send_version:
|
||||||
|
self._test_volume_api('retype',
|
||||||
|
rpc_method='cast',
|
||||||
|
volume=self.fake_volume_obj,
|
||||||
|
new_type_id='fake',
|
||||||
|
dest_host=dest_host,
|
||||||
|
migration_policy='never',
|
||||||
|
reservations=self.fake_reservations,
|
||||||
|
old_reservations=self.fake_reservations,
|
||||||
|
version='1.12')
|
||||||
|
can_send_version.assert_any_call('1.37')
|
||||||
|
can_send_version.assert_any_call('1.34')
|
||||||
|
|
||||||
def test_manage_existing(self):
|
def test_manage_existing(self):
|
||||||
self._test_volume_api('manage_existing',
|
self._test_volume_api('manage_existing',
|
||||||
|
|||||||
@@ -1506,6 +1506,22 @@ class API(base.Base):
|
|||||||
reservations = quota_utils.get_volume_type_reservation(context, volume,
|
reservations = quota_utils.get_volume_type_reservation(context, volume,
|
||||||
vol_type_id)
|
vol_type_id)
|
||||||
|
|
||||||
|
# Get old reservations
|
||||||
|
try:
|
||||||
|
reserve_opts = {'volumes': -1, 'gigabytes': -volume.size}
|
||||||
|
QUOTAS.add_volume_type_opts(context,
|
||||||
|
reserve_opts,
|
||||||
|
old_vol_type_id)
|
||||||
|
old_reservations = QUOTAS.reserve(context,
|
||||||
|
project_id=volume.project_id,
|
||||||
|
**reserve_opts)
|
||||||
|
except Exception:
|
||||||
|
volume.status = volume.previous_status
|
||||||
|
volume.save()
|
||||||
|
msg = _("Failed to update quota usage while retyping volume.")
|
||||||
|
LOG.exception(msg, resource=volume)
|
||||||
|
raise exception.CinderException(msg)
|
||||||
|
|
||||||
self.update(context, volume, {'status': 'retyping',
|
self.update(context, volume, {'status': 'retyping',
|
||||||
'previous_status': volume.status})
|
'previous_status': volume.status})
|
||||||
|
|
||||||
@@ -1513,7 +1529,8 @@ class API(base.Base):
|
|||||||
'volume_id': volume.id,
|
'volume_id': volume.id,
|
||||||
'volume_type': vol_type,
|
'volume_type': vol_type,
|
||||||
'migration_policy': migration_policy,
|
'migration_policy': migration_policy,
|
||||||
'quota_reservations': reservations}
|
'quota_reservations': reservations,
|
||||||
|
'old_reservations': old_reservations}
|
||||||
|
|
||||||
self.scheduler_rpcapi.retype(context, CONF.volume_topic, volume.id,
|
self.scheduler_rpcapi.retype(context, CONF.volume_topic, volume.id,
|
||||||
request_spec=request_spec,
|
request_spec=request_spec,
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ def locked_snapshot_operation(f):
|
|||||||
class VolumeManager(manager.SchedulerDependentManager):
|
class VolumeManager(manager.SchedulerDependentManager):
|
||||||
"""Manages attachable block storage devices."""
|
"""Manages attachable block storage devices."""
|
||||||
|
|
||||||
RPC_API_VERSION = '1.36'
|
RPC_API_VERSION = '1.37'
|
||||||
|
|
||||||
target = messaging.Target(version=RPC_API_VERSION)
|
target = messaging.Target(version=RPC_API_VERSION)
|
||||||
|
|
||||||
@@ -2069,7 +2069,8 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
resource=volume)
|
resource=volume)
|
||||||
|
|
||||||
def retype(self, ctxt, volume_id, new_type_id, host,
|
def retype(self, ctxt, volume_id, new_type_id, host,
|
||||||
migration_policy='never', reservations=None, volume=None):
|
migration_policy='never', reservations=None,
|
||||||
|
volume=None, old_reservations=None):
|
||||||
|
|
||||||
def _retype_error(context, volume, old_reservations,
|
def _retype_error(context, volume, old_reservations,
|
||||||
new_reservations, status_update):
|
new_reservations, status_update):
|
||||||
@@ -2108,22 +2109,26 @@ class VolumeManager(manager.SchedulerDependentManager):
|
|||||||
volume.update(status_update)
|
volume.update(status_update)
|
||||||
volume.save()
|
volume.save()
|
||||||
|
|
||||||
# Get old reservations
|
# If old_reservations has been passed in from the API, we should
|
||||||
try:
|
# skip quotas.
|
||||||
reserve_opts = {'volumes': -1, 'gigabytes': -volume.size}
|
# TODO(ntpttr): These reservation checks are left in to be backwards
|
||||||
QUOTAS.add_volume_type_opts(context,
|
# compatible with Liberty and can be removed in N.
|
||||||
reserve_opts,
|
if not old_reservations:
|
||||||
volume.volume_type_id)
|
# Get old reservations
|
||||||
old_reservations = QUOTAS.reserve(context,
|
try:
|
||||||
project_id=project_id,
|
reserve_opts = {'volumes': -1, 'gigabytes': -volume.size}
|
||||||
**reserve_opts)
|
QUOTAS.add_volume_type_opts(context,
|
||||||
except Exception:
|
reserve_opts,
|
||||||
volume.update(status_update)
|
volume.volume_type_id)
|
||||||
volume.save()
|
old_reservations = QUOTAS.reserve(context,
|
||||||
LOG.exception(_LE("Failed to update usages "
|
project_id=project_id,
|
||||||
"while retyping volume."))
|
**reserve_opts)
|
||||||
raise exception.CinderException(_("Failed to get old volume type"
|
except Exception:
|
||||||
" quota reservations"))
|
volume.update(status_update)
|
||||||
|
volume.save()
|
||||||
|
msg = _("Failed to update quota usage while retyping volume.")
|
||||||
|
LOG.exception(msg, resource=volume)
|
||||||
|
raise exception.CinderException(msg)
|
||||||
|
|
||||||
# We already got the new reservations
|
# We already got the new reservations
|
||||||
new_reservations = reservations
|
new_reservations = reservations
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ class VolumeAPI(object):
|
|||||||
1.35 - Adds support for sending objects over RPC in extend_volume().
|
1.35 - Adds support for sending objects over RPC in extend_volume().
|
||||||
1.36 - Adds support for sending objects over RPC in migrate_volume(),
|
1.36 - Adds support for sending objects over RPC in migrate_volume(),
|
||||||
migrate_volume_completion(), and update_migrated_volume().
|
migrate_volume_completion(), and update_migrated_volume().
|
||||||
|
1.37 - Adds old_reservations parameter to retype to support quota
|
||||||
|
checks in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
BASE_RPC_API_VERSION = '1.0'
|
BASE_RPC_API_VERSION = '1.0'
|
||||||
@@ -279,17 +281,22 @@ class VolumeAPI(object):
|
|||||||
return cctxt.call(ctxt, 'migrate_volume_completion', **msg_args)
|
return cctxt.call(ctxt, 'migrate_volume_completion', **msg_args)
|
||||||
|
|
||||||
def retype(self, ctxt, volume, new_type_id, dest_host,
|
def retype(self, ctxt, volume, new_type_id, dest_host,
|
||||||
migration_policy='never', reservations=None):
|
migration_policy='never', reservations=None,
|
||||||
|
old_reservations=None):
|
||||||
host_p = {'host': dest_host.host,
|
host_p = {'host': dest_host.host,
|
||||||
'capabilities': dest_host.capabilities}
|
'capabilities': dest_host.capabilities}
|
||||||
msg_args = {'volume_id': volume.id, 'new_type_id': new_type_id,
|
msg_args = {'volume_id': volume.id, 'new_type_id': new_type_id,
|
||||||
'host': host_p, 'migration_policy': migration_policy,
|
'host': host_p, 'migration_policy': migration_policy,
|
||||||
'reservations': reservations}
|
'reservations': reservations}
|
||||||
if self.client.can_send_version('1.34'):
|
if self.client.can_send_version('1.37'):
|
||||||
version = '1.34'
|
version = '1.37'
|
||||||
msg_args['volume'] = volume
|
msg_args.update(volume=volume, old_reservations=old_reservations)
|
||||||
else:
|
else:
|
||||||
version = '1.12'
|
if self.client.can_send_version('1.34'):
|
||||||
|
version = '1.34'
|
||||||
|
msg_args['volume'] = volume
|
||||||
|
else:
|
||||||
|
version = '1.12'
|
||||||
|
|
||||||
new_host = utils.extract_host(volume.host)
|
new_host = utils.extract_host(volume.host)
|
||||||
cctxt = self.client.prepare(server=new_host, version=version)
|
cctxt = self.client.prepare(server=new_host, version=version)
|
||||||
|
|||||||
Reference in New Issue
Block a user