[SVf] Delete/Extend issue in reverse replication

[Spectrum Virtualize family] Deletion and extend of the volume
in reverse replication failed.

Added necessary code changes to storwize cinder driver to
delete and extend the volume successfully in reverse replication.
Also, added support to extend the volume for failover scenarios.

Bugs are inter-dependent on reverse-replication, hence addressed
the bugs in a single commit.

Closes-Bug: #1960315
Closes-Bug: #1966639

Change-Id: I00d56980e0e8b8dfebd4e7495f625173f0421b75
This commit is contained in:
sreerammounika 2022-04-21 10:35:08 +00:00
parent d5b21ab1ec
commit 685c5fb958
4 changed files with 210 additions and 86 deletions

View File

@ -12189,7 +12189,7 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
storwize_const.REPLICA_CHG_VOL_PREFIX + target_vol)
size = 1
self.driver._convert_global_mirror_volume_to_gmcv(gm_vol, target_vol,
size)
size, rcrel)
rcrel = self.driver._helpers.get_relationship_info(gm_vol.name)
self.assertEqual('multi', rcrel['cycling_mode'])
self.assertEqual(master_change_vol_name,
@ -12201,6 +12201,7 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
gm_vol, model_update = self._create_test_volume(self.gm_type)
self._validate_replic_vol_creation(gm_vol)
rcrel = self.driver._helpers.get_relationship_info(gm_vol.name)
with (mock.patch.object(storwize_svc_common.StorwizeHelpers,
'create_vdisk')) as create_vdisk:
with (mock.patch.object(storwize_svc_common.StorwizeHelpers,
@ -12210,7 +12211,7 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
storwize_const.REPLICA_AUX_VOL_PREFIX + gm_vol.name)
size = 1
self.driver._convert_global_mirror_volume_to_gmcv(
gm_vol, target_vol, size)
gm_vol, target_vol, size, rcrel)
create_vdisk.assert_called()
self.assertEqual(2, create_vdisk.call_count)
ch_relationship_cyclingmode.assert_called()
@ -12238,7 +12239,7 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
self.assertEqual([{'id': gm_vol.id, 'group_id': group.id}],
add_volumes_update)
self.assertEqual([], remove_volumes_update)
rccg_info = self.driver._helpers.get_rccg(rccg_name)
rccg_info = self.driver._helpers.get_rccg_info(gm_vol.name)
self.assertEqual('none', rccg_info['cycling_mode'])
rcrel = self.driver._helpers.get_relationship_info(gm_vol.name)
self.assertEqual('', rcrel['master_change_vdisk_name'])
@ -12252,7 +12253,8 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
storwize_const.REPLICA_CHG_VOL_PREFIX + target_vol)
size = 1
self.driver._convert_global_mirror_volume_to_gmcv(
gm_vol, target_vol, size, rccg_name=rccg_name)
gm_vol, target_vol, size, rcrel,
rccg_name=rccg_name)
rccg_info = self.driver._helpers.get_rccg_info(gm_vol.name)
self.assertEqual('multi', rccg_info['cycling_mode'])
rcrel = self.driver._helpers.get_relationship_info(gm_vol.name)
@ -12276,6 +12278,7 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
self.assertEqual(fields.ReplicationStatus.ENABLED,
model_update['replication_status'])
self._validate_replic_vol_creation(gm_vol)
rcrel = self.driver._helpers.get_relationship_info(gm_vol.name)
rccg_name = "fake_rccg_1"
with (mock.patch.object(storwize_svc_common.StorwizeHelpers,
'create_vdisk')) as create_vdisk:
@ -12283,7 +12286,8 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
storwize_const.REPLICA_AUX_VOL_PREFIX + gm_vol.name)
size = 1
self.driver._convert_global_mirror_volume_to_gmcv(
gm_vol, target_vol, size, rccg_name=rccg_name)
gm_vol, target_vol, size, rcrel,
rccg_name=rccg_name)
create_vdisk.assert_called()
self.assertEqual(2, create_vdisk.call_count)
stop_rccg.assert_called_once_with(rccg_name)
@ -12525,9 +12529,7 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
'delete_vdisk')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_delete_target_volume(self, get_relationship_info,
def test_delete_source_volume(self,
delete_relationship,
delete_vdisk):
# Set replication target.
@ -12536,11 +12538,8 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
[self.rep_target])
self.driver.do_setup(self.ctxt)
fake_name = 'volume-%s' % fake.VOLUME_ID
get_relationship_info.return_value = {'aux_vdisk_name':
fake_name}
self.driver._helpers.delete_rc_volume(fake_name)
get_relationship_info.assert_called_once_with(fake_name)
delete_relationship.assert_called_once_with(fake_name)
self.assertFalse(delete_relationship.called)
master_change_fake_name = (
storwize_const.REPLICA_CHG_VOL_PREFIX + fake_name)
calls = [mock.call(master_change_fake_name, force_delete=False,
@ -12549,23 +12548,52 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
delete_vdisk.assert_has_calls(calls, any_order=True)
self.assertEqual(2, delete_vdisk.call_count)
rel_info = {'aux_vdisk_name': fake_name,
'master_vdisk_name': 'volume-%s' % fake.VOLUME_ID}
self.driver._helpers.delete_rc_volume(fake_name, rel_info)
delete_relationship.assert_called_once_with(fake_name)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_vdisk')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_relationship')
def test_delete_target_volume(self,
delete_relationship,
delete_vdisk):
# Set replication target.
self.driver.configuration.set_override('replication_device',
[self.rep_target])
self.driver.do_setup(self.ctxt)
fake_name = 'aux_volume-%s' % fake.VOLUME_ID
rel_info = {'aux_vdisk_name': fake_name,
'master_vdisk_name': 'volume-%s' % fake.VOLUME_ID}
self.driver._aux_backend_helpers.delete_rc_volume(fake_name,
rel_info,
target_vol=True)
delete_relationship.assert_called_with(fake_name)
target_change_fake_name = (
storwize_const.REPLICA_CHG_VOL_PREFIX + fake_name)
calls = [mock.call(target_change_fake_name, force_delete=False,
force_unmap=True),
mock.call(fake_name, force_delete=False, force_unmap=True)]
delete_vdisk.assert_has_calls(calls, any_order=True)
self.assertEqual(2, delete_vdisk.call_count)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_vdisk')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_delete_target_volume_no_relationship(self, get_relationship_info,
def test_delete_target_volume_no_relationship(self,
delete_relationship,
delete_vdisk):
# Set replication target.
self.driver.configuration.set_override('replication_device',
[self.rep_target])
self.driver.do_setup(self.ctxt)
fake_name = 'volume-%s' % fake.VOLUME_ID
get_relationship_info.return_value = None
self.driver._helpers.delete_rc_volume(fake_name)
get_relationship_info.assert_called_once_with(fake_name)
fake_name = 'aux_volume-%s' % fake.VOLUME_ID
self.driver._aux_backend_helpers.delete_rc_volume(
fake_name, target_vol=True, rel_info=None)
self.assertFalse(delete_relationship.called)
self.assertTrue(delete_vdisk.called)
@ -12573,58 +12601,86 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
'delete_vdisk')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_delete_target_volume_fail(self, get_relationship_info,
def test_delete_target_volume_fail(self,
delete_relationship,
delete_vdisk):
fake_id = fake.VOLUME_ID
fake_name = 'volume-%s' % fake_id
get_relationship_info.return_value = {'aux_vdisk_name':
fake_name}
fake_name = 'aux_volume-%s' % fake_id
rel_info = {'aux_vdisk_name': fake_name,
'master_vdisk_name': 'volume-%s' % fake_id}
delete_vdisk.side_effect = Exception
self.assertRaises(exception.VolumeDriverException,
self.driver._helpers.delete_rc_volume,
fake_name)
get_relationship_info.assert_called_once_with(fake_name)
self.driver._aux_backend_helpers.delete_rc_volume,
fake_name, rel_info, target_vol=True)
delete_relationship.assert_called_once_with(fake_name)
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
def test_get_target_volume_information(self, get_relationship_info):
volume, model_update = self._create_test_volume(self.mm_type)
self.assertEqual(fields.ReplicationStatus.ENABLED,
model_update['replication_status'])
fake_aux_volume_name = storwize_const.REPLICA_AUX_VOL_PREFIX + \
volume.name
rel_info = {'aux_vdisk_name': fake_aux_volume_name,
'master_vdisk_name': volume.name}
get_relationship_info.return_value = rel_info
target_volume, rel_info = (
self.driver._helpers.get_target_volume_information(volume))
self.assertEqual(target_volume, fake_aux_volume_name)
self.assertIsNotNone(rel_info)
get_relationship_info.assert_called_with(volume.name)
get_relationship_info.return_value = None
target_volume, rel_info = (
self.driver._helpers.get_target_volume_information(volume))
self.assertEqual(target_volume, fake_aux_volume_name)
self.assertIsNone(rel_info)
get_relationship_info.assert_called_with(volume.name)
def test_is_replicated_volume_primary(self):
volume, model_update = self._create_test_volume(self.gm_type)
rel_info = self.driver._helpers.get_relationship_info(volume.name)
flag = self.driver._helpers.is_replicated_volume_primary(volume,
rel_info)
self.assertEqual(flag, True)
# GM volume with auxillary as Primary
rel_info["primary"] = "aux"
flag = self.driver._helpers.is_replicated_volume_primary(volume,
rel_info)
self.assertEqual(flag, False)
@ddt.data((True, True, 1), (False, True, 2),
(True, False, 2), (False, False, 2))
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_vdisk')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'delete_relationship')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_relationship_info')
@ddt.unpack
def test_retain_target_volume(self, target_volume, retain_aux_vol,
call_count, get_relationship_info,
delete_relationship, delete_vdisk):
def test_retain_target_volume(self, target_vol, retain_aux_vol,
call_count, delete_relationship,
delete_vdisk):
# Set replication target.
self.driver.configuration.set_override('replication_device',
[self.rep_target])
self.driver.do_setup(self.ctxt)
fake_name = 'volume-%s' % fake.VOLUME_ID
get_relationship_info.return_value = {'aux_vdisk_name':
fake_name}
self.driver._helpers.delete_rc_volume(fake_name,
target_vol=target_volume,
retain_aux_volume=retain_aux_vol)
fake_name = 'aux_volume-%s' % fake.VOLUME_ID
rel_info = {'aux_vdisk_name': fake_name,
'master_vdisk_name': 'volume-%s' % fake.VOLUME_ID}
self.driver._aux_backend_helpers.delete_rc_volume(
fake_name, rel_info, target_vol=target_vol,
retain_aux_volume=retain_aux_vol)
vol_name = fake_name
change_vol_name = (storwize_const.REPLICA_CHG_VOL_PREFIX + vol_name)
if target_volume:
vol_name = (storwize_const.REPLICA_AUX_VOL_PREFIX + fake_name)
change_vol_name = (
storwize_const.REPLICA_CHG_VOL_PREFIX + vol_name)
get_relationship_info.assert_called_once_with(vol_name)
delete_relationship.assert_called_once_with(vol_name)
if rel_info:
delete_relationship.assert_called_once_with(vol_name)
calls = [mock.call(change_vol_name, force_delete=False,
force_unmap=True)]
if (target_volume and not retain_aux_vol) or not target_volume:
if (target_vol and not retain_aux_vol) or not target_vol:
calls.extend([mock.call(vol_name, force_delete=False,
force_unmap=True)])
delete_vdisk.assert_has_calls(calls, any_order=True)

View File

@ -2638,34 +2638,59 @@ class StorwizeHelpers(object):
relationship = self.ssh.lsrcrelationship(vol_attrs['RC_name'])
return relationship[0] if len(relationship) > 0 else None
def delete_rc_volume(self, volume_name, target_vol=False,
force_unmap=True, retain_aux_volume=False):
vol_name = volume_name
if target_vol:
vol_name = storwize_const.REPLICA_AUX_VOL_PREFIX + volume_name
def is_replicated_volume_primary(self, volume, rel_info):
# Return true if either source_volume is the primary volume or
# onboarded auxiliary volume is primary [Reverse replication failover]
if ((rel_info["master_vdisk_name"] == volume.name and
rel_info["primary"] == "master") or
(rel_info["master_vdisk_name"] != volume.name and
rel_info["primary"] == "aux")):
return True
return False
def get_target_volume_information(self, source_volume):
source_volume_name = source_volume.name
rel_info = self.get_relationship_info(source_volume_name)
if rel_info:
if source_volume_name == rel_info["aux_vdisk_name"]:
target_volume = rel_info["master_vdisk_name"]
else:
target_volume = rel_info["aux_vdisk_name"]
else:
# Retrieving target volume based on Source volume name, if
# relationship not exists.
if source_volume_name[:4] == storwize_const.REPLICA_AUX_VOL_PREFIX:
target_volume = source_volume_name[4:]
else:
target_volume = (storwize_const.REPLICA_AUX_VOL_PREFIX +
source_volume_name)
return (target_volume, rel_info)
def delete_rc_volume(self, volume_name, rel_info=None,
target_vol=False, force_unmap=True,
retain_aux_volume=False):
try:
rel_info = self.get_relationship_info(vol_name)
# If relationship exists, will delete the relationship.
if rel_info:
self.delete_relationship(vol_name)
self.delete_relationship(volume_name)
# Delete change volume
self.delete_vdisk(
storwize_const.REPLICA_CHG_VOL_PREFIX + vol_name,
storwize_const.REPLICA_CHG_VOL_PREFIX + volume_name,
force_unmap=force_unmap,
force_delete=False)
# We want to retain/remove the aux volume after retyping of
# We want to retain/remove the secondary volume after retyping of
# primary volume from mirror to non-mirror storage template
# or on the delete of the primary volume based on user's
# choice of config value for storwize_svc_retain_aux_volume.
# The default value is False.
if (not retain_aux_volume and target_vol) or not target_vol:
self.delete_vdisk(vol_name,
self.delete_vdisk(volume_name,
force_unmap=force_unmap,
force_delete=False)
except Exception as e:
msg = (_('Unable to delete the volume for '
'volume %(vol)s. Exception: %(err)s.'),
{'vol': vol_name, 'err': e})
{'vol': volume_name, 'err': e})
LOG.exception(msg)
raise exception.VolumeDriverException(message=msg)
@ -3755,23 +3780,30 @@ class StorwizeSVCCommonDriver(san.SanDriver,
return
rep_type = self._get_volume_replicated_type(ctxt, volume)
if rep_type:
if rep_type or (
volume.replication_status not in ["not-capable", "disabled"]):
target_volume, rel_info = (
self._helpers.get_target_volume_information(volume))
if self._aux_backend_helpers:
self._aux_backend_helpers.delete_rc_volume(
volume['name'],
target_volume,
rel_info,
target_vol=True,
force_unmap=force_unmap,
retain_aux_volume=self.configuration.safe_get(
'storwize_svc_retain_aux_volume'))
# As the relationship got deleted, updated rel_info
# as None and sent to master_backend_helper
rel_info = None
if not self._active_backend_id:
self._master_backend_helpers.delete_rc_volume(
volume['name'], force_unmap=force_unmap)
volume['name'], rel_info, force_unmap=force_unmap)
else:
# If it's in fail over state, also try to delete the volume
# in master backend
try:
self._master_backend_helpers.delete_rc_volume(
volume['name'], force_unmap=force_unmap)
volume['name'], rel_info, force_unmap=force_unmap)
except Exception as ex:
LOG.error('Failed to get delete volume %(volume)s in '
'master backend. Exception: %(err)s.',
@ -3959,7 +3991,8 @@ class StorwizeSVCCommonDriver(san.SanDriver,
force_unmap = True
volume_name = self._get_target_vol(volume)
rel_info = self._helpers.get_relationship_info(volume_name)
tgt_vol, rel_info = self._helpers.get_target_volume_information(
volume)
ret = self._helpers.ensure_vdisk_no_fc_mappings(volume_name,
allow_snaps=False,
@ -4017,12 +4050,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
raise exception.VolumeDriverException(message=msg)
else:
try:
tgt_vol = (storwize_const.REPLICA_AUX_VOL_PREFIX +
volume.name)
if storwize_const.GMCV_MULTI != cyclingmode:
target_helper.extend_vdisk(tgt_vol, extend_amt)
master_helper.extend_vdisk(volume.name, extend_amt)
else:
if storwize_const.GMCV_MULTI == cyclingmode:
rccg_name = (
self._helpers.get_rccg_name_by_volume_name(
volume.name))
@ -4051,15 +4079,28 @@ class StorwizeSVCCommonDriver(san.SanDriver,
target_helper.delete_vdisk(tgt_change_vol,
force_unmap=force_unmap,
force_delete=True)
# Extend primary volume and auxiliary volume
# Extend primary volume and auxiliary volume
flag = self._helpers.is_replicated_volume_primary(
volume, rel_info)
if flag:
# source_volume is the primary volume or
# onboarded auxiliary volume is primary
# [Reverse replication failover]
target_helper.extend_vdisk(tgt_vol, extend_amt)
master_helper.extend_vdisk(volume.name, extend_amt)
else:
# Auxiliary volume is onboarded as source volume
# [Reverse Replication] or
# source volume with primary as aux [Failover]
master_helper.extend_vdisk(volume.name, extend_amt)
target_helper.extend_vdisk(tgt_vol, extend_amt)
if storwize_const.GMCV_MULTI == cyclingmode:
# Convert global mirror volume to GMCV volume with
# the new volume-size
self._convert_global_mirror_volume_to_gmcv(
volume, tgt_vol, new_size, rccg_name=rccg_name)
volume, tgt_vol, new_size, rel_info,
rccg_name=rccg_name)
except Exception as e:
msg = (_('Failed to extend a volume with remote copy '
'%(volume)s. Exception: '
@ -4073,7 +4114,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
# Convert global mirror volume to GMCV volume with
# the current volume-size
self._convert_global_mirror_volume_to_gmcv(
volume, tgt_vol, volume['size'],
volume, tgt_vol, volume['size'], rel_info,
rccg_name=rccg_name)
LOG.error(msg)
@ -4100,7 +4141,7 @@ class StorwizeSVCCommonDriver(san.SanDriver,
volume['id'], model_update['metadata'], False)
def _convert_global_mirror_volume_to_gmcv(self, volume, target_vol, size,
rccg_name=None):
rel_info, rccg_name=None):
master_helper = self._master_backend_helpers
target_helper = self._aux_backend_helpers
tgt_change_vol = (storwize_const.REPLICA_CHG_VOL_PREFIX + target_vol)
@ -4135,26 +4176,33 @@ class StorwizeSVCCommonDriver(san.SanDriver,
# Update consistency group cyclingmode to 'multi'
master_helper.stop_rccg(rccg_name)
master_helper.change_consistgrp_cyclingmode(rccg_name, 'multi')
# Set source_change_volume and target_change_volume
master_helper.change_relationship_changevolume(volume.name,
src_change_vol,
True)
target_helper.change_relationship_changevolume(target_vol,
tgt_change_vol,
False)
# Start gmcv consistency group relationship
master_helper.start_rccg(rccg_name)
else:
# Update volume cyclingmode to 'multi'
master_helper.stop_relationship(volume.name)
master_helper.change_relationship_cyclingmode(volume.name, 'multi')
# Set source_change_volume and target_change_volume
# Set source_change_volume and target_change_volume
if rel_info["master_vdisk_name"] == volume.name:
master_helper.change_relationship_changevolume(volume.name,
src_change_vol,
True)
target_helper.change_relationship_changevolume(target_vol,
tgt_change_vol,
False)
else:
# Auxiliary volume is onboarded as source volume
# [Reverse Replication Scenario]
master_helper.change_relationship_changevolume(volume.name,
src_change_vol,
False)
target_helper.change_relationship_changevolume(target_vol,
tgt_change_vol,
True)
if rccg_name:
# Start gmcv consistency group relationshi
master_helper.start_rccg(rccg_name)
else:
# Start gmcv volume relationship
master_helper.start_relationship(volume.name)
@ -5611,8 +5659,11 @@ class StorwizeSVCCommonDriver(san.SanDriver,
force_unmap = True
if old_rep_type and not new_rep_type:
target_volume, rel_info = (
self._helpers.get_target_volume_information(volume))
self._aux_backend_helpers.delete_rc_volume(
volume['name'],
target_volume,
rel_info,
target_vol=True,
force_unmap=force_unmap,
retain_aux_volume=self.configuration.safe_get(
@ -6562,10 +6613,13 @@ class StorwizeSVCCommonDriver(san.SanDriver,
for volume in volumes:
try:
target_volume, rel_info = (
self._helpers.get_target_volume_information(volume))
self._aux_backend_helpers.delete_rc_volume(
target_volume, rel_info, target_vol=True,
force_unmap=force_unmap)
self._master_backend_helpers.delete_rc_volume(
volume.name, force_unmap=force_unmap)
self._aux_backend_helpers.delete_rc_volume(
volume.name, target_vol=True, force_unmap=force_unmap)
volumes_model_update.append(
{'id': volume.id, 'status': 'deleted'})
except exception.VolumeDriverException as err:

View File

@ -0,0 +1,7 @@
---
fixes:
- |
IBM Spectrum Virtualize family driver
`Bug #1960315 <https://bugs.launchpad.net/cinder/+bug/1960315>`_:
Fixed delete and resize volume issues in during reverse replication
and added support to extend the volume for failover scenarios.

View File

@ -0,0 +1,7 @@
---
fixes:
- |
IBM Spectrum Virtualize family driver
`Bug #1966639 <https://bugs.launchpad.net/cinder/+bug/1966639>`_:
Fixed resize issue in reverse replication for the volumes
which are a part of a consistency group(CG).