Dell SC: Retype fixes
The retype command would fail to retype volumes to or from a replicated type. In addition retype failed to return a tuple of status, model_update when returning a model_update. Change-Id: I89c184d48f0235f0a38399ce0b3aac2a504fdd19 Closes-Bug: #1642055 Closes-Bug: #1642058
This commit is contained in:
parent
b5045489f8
commit
c514b25b07
@ -2705,20 +2705,21 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
mock_init):
|
mock_init):
|
||||||
|
|
||||||
res = self.driver.retype(
|
res = self.driver.retype(
|
||||||
None, {'id': fake.VOLUME_ID}, None,
|
None, {'id': fake.VOLUME_ID},
|
||||||
|
{'extra_specs': {'replication_enabled': [None, '<is> True']}},
|
||||||
{'extra_specs': {'replication_enabled': [None, '<is> True']}},
|
{'extra_specs': {'replication_enabled': [None, '<is> True']}},
|
||||||
None)
|
None)
|
||||||
self.assertTrue(mock_create_replications.called)
|
self.assertTrue(mock_create_replications.called)
|
||||||
self.assertFalse(mock_delete_replications.called)
|
self.assertFalse(mock_delete_replications.called)
|
||||||
self.assertEqual({'replication_status': 'enabled',
|
self.assertEqual((True, {'replication_status': 'enabled',
|
||||||
'replication_driver_data': '54321'}, res)
|
'replication_driver_data': '54321'}), res)
|
||||||
res = self.driver.retype(
|
res = self.driver.retype(
|
||||||
None, {'id': fake.VOLUME_ID}, None,
|
None, {'id': fake.VOLUME_ID}, None,
|
||||||
{'extra_specs': {'replication_enabled': ['<is> True', None]}},
|
{'extra_specs': {'replication_enabled': ['<is> True', None]}},
|
||||||
None)
|
None)
|
||||||
self.assertTrue(mock_delete_replications.called)
|
self.assertTrue(mock_delete_replications.called)
|
||||||
self.assertEqual({'replication_status': 'disabled',
|
self.assertEqual((True, {'replication_status': 'disabled',
|
||||||
'replication_driver_data': ''}, res)
|
'replication_driver_data': ''}), res)
|
||||||
|
|
||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'update_replicate_active_replay')
|
'update_replicate_active_replay')
|
||||||
|
@ -7804,6 +7804,8 @@ class DellSCSanAPIConnectionTestCase(test.TestCase):
|
|||||||
response_bad._content = ''
|
response_bad._content = ''
|
||||||
response_bad._content_consumed = True
|
response_bad._content_consumed = True
|
||||||
response_bad.reason = u'bad request'
|
response_bad.reason = u'bad request'
|
||||||
|
response_bad._content = ''
|
||||||
|
response_bad._content_consumed = True
|
||||||
RESPONSE_400 = response_bad
|
RESPONSE_400 = response_bad
|
||||||
|
|
||||||
APIDICT = {u'instanceId': u'0',
|
APIDICT = {u'instanceId': u'0',
|
||||||
|
@ -169,12 +169,12 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
if profile:
|
if profile:
|
||||||
api.update_cg_volumes(profile, [volume])
|
api.update_cg_volumes(profile, [volume])
|
||||||
|
|
||||||
def _get_replication_specs(self, obj):
|
def _get_replication_specs(self, specs):
|
||||||
"""Checks if we can do replication.
|
"""Checks if we can do replication.
|
||||||
|
|
||||||
Need the extra spec set and we have to be talking to EM.
|
Need the extra spec set and we have to be talking to EM.
|
||||||
|
|
||||||
:param obj: Cinder Volume or snapshot object.
|
:param specs: Cinder Volume or snapshot extra specs.
|
||||||
:return: rinfo dict.
|
:return: rinfo dict.
|
||||||
"""
|
"""
|
||||||
rinfo = {'enabled': False, 'sync': False,
|
rinfo = {'enabled': False, 'sync': False,
|
||||||
@ -182,7 +182,6 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
'autofailover': False}
|
'autofailover': False}
|
||||||
# Repl does not work with direct connect.
|
# Repl does not work with direct connect.
|
||||||
if not self.is_direct_connect:
|
if not self.is_direct_connect:
|
||||||
specs = self._get_volume_extra_specs(obj)
|
|
||||||
if (not self.failed_over and
|
if (not self.failed_over and
|
||||||
specs.get('replication_enabled') == '<is> True'):
|
specs.get('replication_enabled') == '<is> True'):
|
||||||
rinfo['enabled'] = True
|
rinfo['enabled'] = True
|
||||||
@ -215,22 +214,26 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
return rinfo
|
return rinfo
|
||||||
|
|
||||||
def _is_live_vol(self, obj):
|
def _is_live_vol(self, obj):
|
||||||
rspecs = self._get_replication_specs(obj)
|
rspecs = self._get_replication_specs(self._get_volume_extra_specs(obj))
|
||||||
return rspecs['enabled'] and rspecs['live']
|
return rspecs['enabled'] and rspecs['live']
|
||||||
|
|
||||||
def _create_replications(self, api, volume, scvolume):
|
def _create_replications(self, api, volume, scvolume, extra_specs=None):
|
||||||
"""Creates any appropriate replications for a given volume.
|
"""Creates any appropriate replications for a given volume.
|
||||||
|
|
||||||
:param api: Dell REST API object.
|
:param api: Dell REST API object.
|
||||||
:param volume: Cinder volume object.
|
:param volume: Cinder volume object.
|
||||||
:param scvolume: Dell Storage Center Volume object.
|
:param scvolume: Dell Storage Center Volume object.
|
||||||
|
:param extra_specs: Extra specs if we have them otherwise gets them
|
||||||
|
from the volume.
|
||||||
:return: model_update
|
:return: model_update
|
||||||
"""
|
"""
|
||||||
# Replication V2
|
# Replication V2
|
||||||
# for now we assume we have an array named backends.
|
# for now we assume we have an array named backends.
|
||||||
replication_driver_data = None
|
replication_driver_data = None
|
||||||
# Replicate if we are supposed to.
|
# Replicate if we are supposed to.
|
||||||
rspecs = self._get_replication_specs(volume)
|
if not extra_specs:
|
||||||
|
extra_specs = self._get_volume_extra_specs(volume)
|
||||||
|
rspecs = self._get_replication_specs(extra_specs)
|
||||||
if rspecs['enabled']:
|
if rspecs['enabled']:
|
||||||
for backend in self.backends:
|
for backend in self.backends:
|
||||||
targetdeviceid = backend['target_device_id']
|
targetdeviceid = backend['target_device_id']
|
||||||
@ -421,7 +424,8 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
LOG.debug('Deleting volume %s', volume_name)
|
LOG.debug('Deleting volume %s', volume_name)
|
||||||
with self._client.open_connection() as api:
|
with self._client.open_connection() as api:
|
||||||
try:
|
try:
|
||||||
rspecs = self._get_replication_specs(volume)
|
rspecs = self._get_replication_specs(
|
||||||
|
self._get_volume_extra_specs(volume))
|
||||||
if rspecs['enabled']:
|
if rspecs['enabled']:
|
||||||
if rspecs['live']:
|
if rspecs['live']:
|
||||||
self._delete_live_volume(api, volume)
|
self._delete_live_volume(api, volume)
|
||||||
@ -1045,6 +1049,35 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
LOG.info(_LI('Retype was to same Storage Profile.'))
|
LOG.info(_LI('Retype was to same Storage Profile.'))
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
def _retype_replication(self, api, volume, scvolume, new_type, diff):
|
||||||
|
model_update = None
|
||||||
|
ret = True
|
||||||
|
# Replication.
|
||||||
|
current, requested = (
|
||||||
|
self._get_retype_spec(diff, volume.get('id'),
|
||||||
|
'replication_enabled',
|
||||||
|
'replication_enabled'))
|
||||||
|
# We only toggle at the repl level.
|
||||||
|
if current != requested:
|
||||||
|
# If we are changing to on...
|
||||||
|
if requested == '<is> True':
|
||||||
|
# We create our replication using our new type's extra specs.
|
||||||
|
model_update = self._create_replications(
|
||||||
|
api, volume, scvolume,
|
||||||
|
new_type.get('extra_specs'))
|
||||||
|
elif current == '<is> True':
|
||||||
|
# If we are killing replication we have to see if we currently
|
||||||
|
# have live volume enabled or not.
|
||||||
|
if self._is_live_vol(volume):
|
||||||
|
ret = self._delete_live_volume(api, volume)
|
||||||
|
else:
|
||||||
|
self._delete_replications(api, volume)
|
||||||
|
model_update = {'replication_status':
|
||||||
|
fields.ReplicationStatus.DISABLED,
|
||||||
|
'replication_driver_data': ''}
|
||||||
|
# TODO(tswanson): Add support for changing replication options.
|
||||||
|
return ret, model_update
|
||||||
|
|
||||||
def retype(self, ctxt, volume, new_type, diff, host):
|
def retype(self, ctxt, volume, new_type, diff, host):
|
||||||
"""Convert the volume to be of the new type.
|
"""Convert the volume to be of the new type.
|
||||||
|
|
||||||
@ -1057,6 +1090,7 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
:param host: A dictionary describing the host to migrate to, where
|
:param host: A dictionary describing the host to migrate to, where
|
||||||
host['host'] is its name, and host['capabilities'] is a
|
host['host'] is its name, and host['capabilities'] is a
|
||||||
dictionary of its reported capabilities (Not Used).
|
dictionary of its reported capabilities (Not Used).
|
||||||
|
:returns: Boolean or Boolean, model_update tuple.
|
||||||
"""
|
"""
|
||||||
LOG.info(_LI('retype: volume_name: %(name)s new_type: %(newtype)s '
|
LOG.info(_LI('retype: volume_name: %(name)s new_type: %(newtype)s '
|
||||||
'diff: %(diff)s host: %(host)s'),
|
'diff: %(diff)s host: %(host)s'),
|
||||||
@ -1132,24 +1166,6 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
'profile'))
|
'profile'))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Replication_enabled.
|
|
||||||
current, requested = (
|
|
||||||
self._get_retype_spec(diff,
|
|
||||||
volume_name,
|
|
||||||
'replication_enabled',
|
|
||||||
'replication_enabled'))
|
|
||||||
# if there is a change and it didn't work fast fail.
|
|
||||||
if current != requested:
|
|
||||||
if requested == '<is> True':
|
|
||||||
model_update = self._create_replications(api,
|
|
||||||
volume,
|
|
||||||
scvolume)
|
|
||||||
elif current == '<is> True':
|
|
||||||
self._delete_replications(api, volume)
|
|
||||||
model_update = {'replication_status':
|
|
||||||
fields.ReplicationStatus.DISABLED,
|
|
||||||
'replication_driver_data': ''}
|
|
||||||
|
|
||||||
# Active Replay
|
# Active Replay
|
||||||
current, requested = (
|
current, requested = (
|
||||||
self._get_retype_spec(diff, volume_name,
|
self._get_retype_spec(diff, volume_name,
|
||||||
@ -1162,12 +1178,18 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
'replication:activereplay setting'))
|
'replication:activereplay setting'))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Deal with replication.
|
||||||
|
ret, model_update = self._retype_replication(
|
||||||
|
api, volume, scvolume, new_type, diff)
|
||||||
|
if not ret:
|
||||||
|
return False
|
||||||
|
|
||||||
except exception.VolumeBackendAPIException:
|
except exception.VolumeBackendAPIException:
|
||||||
# We do nothing with this. We simply return failure.
|
# We do nothing with this. We simply return failure.
|
||||||
return False
|
return False
|
||||||
# If we have something to send down...
|
# If we have something to send down...
|
||||||
if model_update:
|
if model_update:
|
||||||
return model_update
|
return True, model_update
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _parse_secondary(self, api, secondary):
|
def _parse_secondary(self, api, secondary):
|
||||||
@ -1493,7 +1515,8 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
LOG.info(_LI('failback_volumes: starting volume: %s'), volume)
|
LOG.info(_LI('failback_volumes: starting volume: %s'), volume)
|
||||||
model_update = {}
|
model_update = {}
|
||||||
if volume.get('replication_driver_data'):
|
if volume.get('replication_driver_data'):
|
||||||
rspecs = self._get_replication_specs(volume)
|
rspecs = self._get_replication_specs(
|
||||||
|
self._get_volume_extra_specs(volume))
|
||||||
if rspecs['live']:
|
if rspecs['live']:
|
||||||
model_update = self._failback_live_volume(
|
model_update = self._failback_live_volume(
|
||||||
api, volume['id'], volume['provider_id'])
|
api, volume['id'], volume['provider_id'])
|
||||||
@ -1606,7 +1629,8 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
for volume in volumes:
|
for volume in volumes:
|
||||||
model_update = {}
|
model_update = {}
|
||||||
if volume.get('replication_driver_data'):
|
if volume.get('replication_driver_data'):
|
||||||
rspecs = self._get_replication_specs(volume)
|
rspecs = self._get_replication_specs(
|
||||||
|
self._get_volume_extra_specs(volume))
|
||||||
if rspecs['live']:
|
if rspecs['live']:
|
||||||
model_update = self._failover_live_volume(
|
model_update = self._failover_live_volume(
|
||||||
api, volume['id'],
|
api, volume['id'],
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
issues:
|
||||||
|
- With the Dell SC Cinder Driver if a volume is retyped
|
||||||
|
to a new storage profile all volumes created via
|
||||||
|
snapshots from this volume will also change to the
|
||||||
|
new storage profile.
|
||||||
|
- With the Dell SC Cinder Driver retyping from one
|
||||||
|
replication type to another type (ex. regular
|
||||||
|
replication to live volume replication) is not
|
||||||
|
supported.
|
||||||
|
fixes:
|
||||||
|
- With the Dell SC Cinder Driver retyping to or from a
|
||||||
|
replicated type should now work.
|
||||||
|
- With the Dell SC Cinder Driver retype failed to
|
||||||
|
return a tuple if it had to return an update to the
|
||||||
|
volume state.
|
Loading…
x
Reference in New Issue
Block a user