diff --git a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py index 1f809841ff3..d04438f66a3 100644 --- a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py +++ b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py @@ -2203,7 +2203,7 @@ port_speed!N/A rcrel_info['status'] = 'online' rcrel_info['sync'] = '' rcrel_info['copy_type'] = 'global' if 'global' in kwargs else 'metro' - rcrel_info['cycling_mode'] = cyclingmode if cyclingmode else '' + rcrel_info['cycling_mode'] = cyclingmode if cyclingmode else 'none' rcrel_info['cycle_period_seconds'] = '300' rcrel_info['master_change_vdisk_id'] = '' rcrel_info['master_change_vdisk_name'] = '' @@ -2393,6 +2393,11 @@ port_speed!N/A return '', '' + def _cmd_chrcconsistgrp(self, **kwargs): + if 'obj' not in kwargs: + return self._errors['CMMVC5701E'] + return self._chrcconsistgrp_attr(**kwargs) + def _cmd_rmrcrelationship(self, **kwargs): if 'obj' not in kwargs: return self._errors['CMMVC5701E'] @@ -2460,6 +2465,22 @@ port_speed!N/A rcrel['cycling_mode'] = cyclingmode return ('', '') + def _chrcconsistgrp_attr(self, **kwargs): + if 'obj' not in kwargs: + return self._errors['CMMVC5707E'] + id_num = kwargs['obj'] + + try: + rccg = self._rcconsistgrp_list[id_num] + except KeyError: + return self._errors['CMMVC5753E'] + + if 'cyclingmode' in kwargs: + cyclingmode = kwargs['cyclingmode'].strip('\'\"') + rccg['cycling_mode'] = cyclingmode + + return ('', '') + def _rc_state_transition(self, function, rcrel): if (function == 'wait' and 'wait' not in self._rc_transitions[rcrel['state']]): @@ -10740,6 +10761,17 @@ class StorwizeSSHTestCase(test.TestCase): 'none') self.assertIsNone(ret) + def test_ch_rcconsistgrp_cyclingmode(self): + with mock.patch.object( + storwize_svc_common.StorwizeSSH, + 'run_ssh_assert_no_output') as run_ssh_assert_no_output: + run_ssh_assert_no_output.return_value = None + ret = self.storwize_ssh.ch_rcconsistgrp_cyclingmode('fake_rccg-1', + 'multi') + self.assertIsNone(ret) + ret = self.storwize_ssh.ch_rcconsistgrp_cyclingmode('fake_rccg-1') + self.assertIsNone(ret) + def test_mkvdiskhostmap(self): # mkvdiskhostmap should not be returning anything with mock.patch.object( @@ -12085,43 +12117,44 @@ class StorwizeSVCReplicationTestCase(test.TestCase): self.driver.delete_volume(gmcv_volume) self._validate_replic_vol_deletion(gmcv_volume) - # Extend gmcv volume that added to group with replication. + def test_storwize_extend_gmcv_volume_part_of_group(self): + """Extend gmcv volume that added to group with replication.""" + # Create group with replication. + group = self._create_test_rccg(self.rccg_type, + [self.gmcv_default_type.id]) + rccg_name = self.driver._get_rccg_name(group) + # Create gmcv volume + volume, model_update = self._create_test_volume( + self.gmcv_default_type) + self._validate_replic_vol_creation(volume, True) + rcrel = self.driver._helpers.get_relationship_info(volume.name) + self.sim._rc_state_transition('wait', rcrel) + # Add gmcv volume to group. + add_vols = [volume] + (model_update, add_volumes_update, + remove_volumes_update) = self.driver.update_group( + self.ctxt, group, add_vols, []) + self.assertEqual( + rccg_name, + self.driver._helpers.get_rccg_name_by_volume_name(volume.name)) + self.assertEqual(fields.GroupStatus.AVAILABLE, + model_update['status']) + self.assertEqual([{'id': volume.id, 'group_id': group.id}], + add_volumes_update) + self.assertEqual([], remove_volumes_update) + # Extend gmcv volume which is a part of group + self.driver.extend_volume(volume, 2) + attrs = self.driver._helpers.get_vdisk_attributes(volume['name']) + vol_size = int(attrs['capacity']) / units.Gi + self.assertAlmostEqual(vol_size, 2) + with mock.patch.object(storwize_svc_common.StorwizeHelpers, 'extend_vdisk') as extend_vdisk: - # Create group with replication. - group = self._create_test_rccg(self.rccg_type, - [self.gmcv_default_type.id]) - rccg_name = self.driver._get_rccg_name(group) - # Create gloabl mirror replication with change volumes. - volume, model_update = self._create_test_volume( - self.gmcv_default_type) - self._validate_replic_vol_creation(volume, True) - rcrel = self.driver._helpers.get_relationship_info(volume.name) - self.sim._rc_state_transition('wait', rcrel) - # Add gmcv volume to group. - add_vols = [volume] - (model_update, add_volumes_update, - remove_volumes_update) = self.driver.update_group( - self.ctxt, group, add_vols, []) - self.assertEqual( - rccg_name, - self.driver._helpers.get_rccg_info(volume.name)['name']) - self.assertEqual(fields.GroupStatus.AVAILABLE, - model_update['status']) - self.assertEqual([{'id': volume.id, 'group_id': group.id}], - add_volumes_update) - self.assertEqual([], remove_volumes_update) + self.driver.extend_volume(volume, 3) + extend_vdisk.assert_called_with(volume.name, 2) - self.assertRaises(exception.VolumeDriverException, - self.driver.extend_volume, volume, 15) - - self.assertFalse(extend_vdisk.called) - attrs = self.driver._helpers.get_vdisk_attributes(volume['name']) - vol_size = int(attrs['capacity']) / units.Gi - self.assertAlmostEqual(vol_size, 1) - - self.driver.delete_volume(volume) - self._validate_replic_vol_deletion(volume) + self.driver.delete_volume(volume) + self._validate_replic_vol_deletion(volume) def test_convert_global_mirror_volume_to_gmcv(self): """Test volume conversion from global to gmcv.""" @@ -12134,9 +12167,9 @@ class StorwizeSVCReplicationTestCase(test.TestCase): model_update['replication_status']) self._validate_replic_vol_creation(gm_vol) rcrel = self.driver._helpers.get_relationship_info(gm_vol.name) - self.assertEqual(rcrel['cycling_mode'], '') - self.assertEqual(rcrel['master_change_vdisk_name'], '') - self.assertEqual(rcrel['aux_change_vdisk_name'], '') + self.assertEqual('none', rcrel['cycling_mode']) + self.assertEqual('', rcrel['master_change_vdisk_name']) + self.assertEqual('', rcrel['aux_change_vdisk_name']) # Validating volume conversion from global to gmcv by checking a few # property values of RC relationship @@ -12149,11 +12182,11 @@ class StorwizeSVCReplicationTestCase(test.TestCase): self.driver._convert_global_mirror_volume_to_gmcv(gm_vol, target_vol, size) rcrel = self.driver._helpers.get_relationship_info(gm_vol.name) - self.assertEqual(rcrel['cycling_mode'], 'multi') - self.assertEqual(rcrel['master_change_vdisk_name'], - master_change_vol_name) - self.assertEqual(rcrel['aux_change_vdisk_name'], - aux_change_vol_name) + self.assertEqual('multi', rcrel['cycling_mode']) + self.assertEqual(master_change_vol_name, + rcrel['master_change_vdisk_name']) + self.assertEqual(aux_change_vol_name, + rcrel['aux_change_vdisk_name']) self.driver.delete_volume(gm_vol) self._validate_replic_vol_deletion(gm_vol) @@ -12175,6 +12208,82 @@ class StorwizeSVCReplicationTestCase(test.TestCase): self.driver.delete_volume(gm_vol) self._validate_replic_vol_deletion(gm_vol) + def test_convert_global_mirror_volume_to_gmcv_part_of_group(self): + """Test volume conversion from global to gmcv part of group.""" + group = self._create_test_rccg(self.rccg_type, + [self.gm_type.id]) + rccg_name = self.driver._get_rccg_name(group) + gm_vol, model_update = self._create_test_volume(self.gm_type) + self.assertEqual(fields.ReplicationStatus.ENABLED, + model_update['replication_status']) + self._validate_replic_vol_creation(gm_vol) + add_vols = [gm_vol] + (model_update, add_volumes_update, + remove_volumes_update) = self.driver.update_group( + self.ctxt, group, add_vols, []) + self.assertEqual( + rccg_name, + self.driver._helpers.get_rccg_name_by_volume_name(gm_vol.name)) + self.assertEqual(fields.GroupStatus.AVAILABLE, + model_update['status']) + 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) + self.assertEqual('none', rccg_info['cycling_mode']) + rcrel = self.driver._helpers.get_relationship_info(gm_vol.name) + self.assertEqual('', rcrel['master_change_vdisk_name']) + self.assertEqual('', rcrel['aux_change_vdisk_name']) + # Validating volume conversion from global to gmcv by checking a few + # property values of rccg and RC relationship + target_vol = storwize_const.REPLICA_AUX_VOL_PREFIX + gm_vol.name + master_change_vol_name = ( + storwize_const.REPLICA_CHG_VOL_PREFIX + gm_vol.name) + aux_change_vol_name = ( + 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) + 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) + self.assertEqual(master_change_vol_name, + rcrel['master_change_vdisk_name']) + self.assertEqual(aux_change_vol_name, + rcrel['aux_change_vdisk_name']) + self.driver.delete_volume(gm_vol) + self._validate_replic_vol_deletion(gm_vol) + + @mock.patch.object(storwize_svc_common.StorwizeHelpers, + 'start_rccg') + @mock.patch.object(storwize_svc_common.StorwizeHelpers, + 'change_consistgrp_cyclingmode') + @mock.patch.object(storwize_svc_common.StorwizeHelpers, + 'stop_rccg') + def test_calls_in_convert_global_mirror_volume_to_gmcv_part_of_group( + self, start_rccg, change_consistgrp_cyclingmode, stop_rccg): + # Create global mirror replication. + gm_vol, model_update = self._create_test_volume(self.gm_type) + self.assertEqual(fields.ReplicationStatus.ENABLED, + model_update['replication_status']) + self._validate_replic_vol_creation(gm_vol) + rccg_name = "fake_rccg_1" + with (mock.patch.object(storwize_svc_common.StorwizeHelpers, + 'create_vdisk')) as create_vdisk: + target_vol = ( + 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) + create_vdisk.assert_called() + self.assertEqual(2, create_vdisk.call_count) + stop_rccg.assert_called_once_with(rccg_name) + change_consistgrp_cyclingmode.assert_called_once_with(rccg_name, + 'multi') + start_rccg.assert_called_once_with(rccg_name) + self.driver.delete_volume(gm_vol) + self._validate_replic_vol_deletion(gm_vol) + def test_storwize_manage_existing_mismatch_with_volume_replication(self): # Set replication target. self.driver.configuration.set_override('replication_device', diff --git a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py index b25d6f2717e..dabb933322f 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py +++ b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py @@ -384,16 +384,19 @@ class StorwizeSSH(object): ssh_cmd.append(rc_rel) self.run_ssh_assert_no_output(ssh_cmd) + def ch_rcconsistgrp_cyclingmode(self, consistgrp, + cyclingmode='none'): + ssh_cmd = ['svctask', 'chrcconsistgrp', + '-cyclingmode', cyclingmode, consistgrp] + self.run_ssh_assert_no_output(ssh_cmd) + def ch_rcrelationship_cyclingmode(self, relationship, - cyclingmode): + cyclingmode='none'): # Note: Can only change one attribute at a time, # so define three ch_rcrelationship_xxx here - if cyclingmode: - ssh_cmd = ['svctask', 'chrcrelationship'] - ssh_cmd.extend(['-cyclingmode', - str(cyclingmode)]) - ssh_cmd.append(relationship) - self.run_ssh_assert_no_output(ssh_cmd) + ssh_cmd = ['svctask', 'chrcrelationship', + '-cyclingmode', cyclingmode, relationship] + self.run_ssh_assert_no_output(ssh_cmd) def ch_rcrelationship_cycleperiod(self, relationship, cycle_period_seconds): @@ -2605,12 +2608,18 @@ class StorwizeHelpers(object): self.ssh.ch_rcrelationship_cycleperiod(vol_attrs['RC_name'], cycle_period_seconds) - def change_relationship_cyclingmode(self, volume_name, cyclingmode): + def change_relationship_cyclingmode(self, volume_name, + cyclingmode='none'): vol_attrs = self.get_vdisk_attributes(volume_name) if vol_attrs['RC_name'] and cyclingmode: self.ssh.ch_rcrelationship_cyclingmode(vol_attrs['RC_name'], cyclingmode) + def change_consistgrp_cyclingmode(self, rccg_name, + cyclingmode='none'): + self.ssh.ch_rcconsistgrp_cyclingmode(rccg_name, + cyclingmode) + def delete_relationship(self, volume_name): vol_attrs = self.get_vdisk_attributes(volume_name) if vol_attrs['RC_name']: @@ -4011,11 +4020,20 @@ class StorwizeSVCCommonDriver(san.SanDriver, target_helper.extend_vdisk(tgt_vol, extend_amt) master_helper.extend_vdisk(volume.name, extend_amt) else: + rccg_name = ( + self._helpers.get_rccg_name_by_volume_name( + volume.name)) # Update gmcv volume cyclingmode to 'none' - master_helper.stop_relationship(volume.name) - master_helper.change_relationship_cyclingmode( - volume.name, 'none') - master_helper.start_relationship(volume.name) + if rccg_name: + master_helper.stop_rccg(rccg_name) + master_helper.change_consistgrp_cyclingmode( + rccg_name) + master_helper.start_rccg(rccg_name) + else: + master_helper.stop_relationship(volume.name) + master_helper.change_relationship_cyclingmode( + volume.name) + master_helper.start_relationship(volume.name) tgt_change_vol = ( storwize_const.REPLICA_CHG_VOL_PREFIX + tgt_vol) @@ -4038,7 +4056,7 @@ class StorwizeSVCCommonDriver(san.SanDriver, # Convert global mirror volume to GMCV volume with # the new volume-size self._convert_global_mirror_volume_to_gmcv( - volume, tgt_vol, new_size) + volume, tgt_vol, new_size, rccg_name=rccg_name) except Exception as e: msg = (_('Failed to extend a volume with remote copy ' '%(volume)s. Exception: ' @@ -4052,7 +4070,8 @@ 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'], + rccg_name=rccg_name) LOG.error(msg) raise exception.VolumeDriverException(message=msg) @@ -4077,7 +4096,8 @@ class StorwizeSVCCommonDriver(san.SanDriver, context.get_admin_context(), volume['id'], model_update['metadata'], False) - def _convert_global_mirror_volume_to_gmcv(self, volume, target_vol, size): + def _convert_global_mirror_volume_to_gmcv(self, volume, target_vol, size, + 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) @@ -4108,16 +4128,32 @@ class StorwizeSVCCommonDriver(san.SanDriver, target_helper.create_vdisk(tgt_change_vol, str(int(size)), 'gb', target_change_pool, target_change_opts) - # 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 - master_helper.change_relationship_changevolume(volume.name, - src_change_vol, True) - target_helper.change_relationship_changevolume(target_vol, - tgt_change_vol, False) - # Start gmcv volume relationship - master_helper.start_relationship(volume.name) + if rccg_name: + # 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 + master_helper.change_relationship_changevolume(volume.name, + src_change_vol, + True) + target_helper.change_relationship_changevolume(target_vol, + tgt_change_vol, + False) + # Start gmcv volume relationship + master_helper.start_relationship(volume.name) def _qos_model_update(self, model_update, volume): """add volume wwn and IOThrottle_rate to the metadata of the volume""" diff --git a/releasenotes/notes/bug-1960314-ibm-svf-Resize_of_GMCV_volumes_in_group-f9a176153518204c.yaml b/releasenotes/notes/bug-1960314-ibm-svf-Resize_of_GMCV_volumes_in_group-f9a176153518204c.yaml new file mode 100644 index 00000000000..6936f844134 --- /dev/null +++ b/releasenotes/notes/bug-1960314-ibm-svf-Resize_of_GMCV_volumes_in_group-f9a176153518204c.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + IBM Spectrum Virtualize family driver + `Bug #1960314 `_: + Fixed resize issue for GMCV volumes which are a part of + a consistency group(CG).