From 577e2ee108a5e3ec2b727928ee4e9c8b35331339 Mon Sep 17 00:00:00 2001 From: raghavendrat Date: Tue, 30 May 2023 19:45:50 +0000 Subject: [PATCH] HPE 3par: Unable to create clone of replicated vol Two possibilities of clone volume: 1] same size, online copy Existing behaviour: start clone & return from function. Error occur because clone is not yet complete and code tries to create vol on secondary array. 2] size is different, offline copy Existing behaviour: (i) create new replicated vol. (ii) during clone operation below error occur: Volume is involved in remote copy (iii) Since clone operation fails, delete new replicated vol (as cleanup). To overcome both possibilities, code changes are done. For clone of replicated vol, create offline copy only. Steps: (i) Create new vol without replication. (ii) Perform clone operation; wait till completion (offline copy). (iii) Create vol on secondary array. Closes-Bug: #2021941 Change-Id: I1f025542a2509e36919ece01b29064377dbbe189 --- .../unit/volume/drivers/hpe/test_hpe3par.py | 63 +++++++++++++++++++ cinder/volume/drivers/hpe/hpe_3par_base.py | 2 +- cinder/volume/drivers/hpe/hpe_3par_common.py | 54 ++++++++++------ ...ar-clone-of-repl-vol-914a6e0e105996b4.yaml | 7 +++ 4 files changed, 107 insertions(+), 19 deletions(-) create mode 100644 releasenotes/notes/hpe-3par-clone-of-repl-vol-914a6e0e105996b4.yaml diff --git a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py index ef507270f30..096daa98e3c 100644 --- a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py +++ b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py @@ -2992,6 +2992,69 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): mock_client.assert_has_calls(expected) + @mock.patch.object(volume_types, 'get_volume_type') + def test_create_cloned_replicated_volume(self, _mock_volume_types): + # setup_mock_client drive with default configuration + # and return the mock HTTP 3PAR client + conf = self.setup_configuration() + self.replication_targets[0]['replication_mode'] = 'sync' + conf.replication_device = self.replication_targets + mock_client = self.setup_driver(config=conf) + + mock_client.getStorageSystemInfo.return_value = ( + {'id': self.CLIENT_ID}) + mock_client.getRemoteCopyGroup.side_effect = ( + hpeexceptions.HTTPNotFound) + mock_client.getCPG.return_value = {'domain': None} + + _mock_volume_types.return_value = { + 'name': 'replicated', + 'extra_specs': { + 'replication_enabled': ' True', + 'replication:mode': 'sync', + 'volume_type': self.volume_type_replicated}} + + mock_client = self.setup_driver() + mock_client.getVolume.return_value = {'name': mock.ANY} + task_id = 1 + mock_client.copyVolume.return_value = {'taskid': task_id} + mock_client.getTask.return_value = {'status': 1} + with mock.patch.object(hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + + type_id_replicated = HPE3PARBaseDriver.VOLUME_TYPE_ID_REPLICATED + volume = copy.deepcopy(self.volume_replicated) + src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID, + 'name': HPE3PARBaseDriver.VOLUME_NAME, + 'size': 2, 'status': 'available', + 'volume_type': 'replicated', + 'volume_type_id': type_id_replicated} + model_update = self.driver.create_cloned_volume(volume, src_vref) + self.assertEqual(model_update['replication_status'], + fields.ReplicationStatus.ENABLED) + + common = hpecommon.HPE3PARCommon(None) + vol_name = common._get_3par_vol_name(volume['id']) + src_vol_name = common._get_3par_vol_name(src_vref['id']) + optional = {'priority': 1} + comment = mock.ANY + + expected = [ + mock.call.createVolume(vol_name, 'OpenStackCPG', + 2048, comment), + mock.call.copyVolume( + src_vol_name, + vol_name, + None, + optional=optional), + mock.call.getTask(task_id), + mock.call.getRemoteCopyGroup('rcg-0DM4qZEVSKON-DXN-N'), + mock.call.startRemoteCopy('rcg-0DM4qZEVSKON-DXN-N') + ] + + mock_client.assert_has_calls(expected) + def test_migrate_volume(self): conf = { diff --git a/cinder/volume/drivers/hpe/hpe_3par_base.py b/cinder/volume/drivers/hpe/hpe_3par_base.py index 497fb7f3600..d4c730985f4 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_base.py +++ b/cinder/volume/drivers/hpe/hpe_3par_base.py @@ -147,7 +147,7 @@ class HPE3PARDriverBase(driver.ManageableVD, pass @volume_utils.trace - def create_volume(self, volume): + def create_volume(self, volume, perform_replica=True): return self.common.create_volume(volume) @volume_utils.trace diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index 2718a3cf01a..22f4e0f2bbe 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -306,11 +306,12 @@ class HPE3PARCommon(object): 4.0.19 - Update code to work with new WSAPI (of 2023). Bug #2015746 4.0.20 - Use small QoS Latency value. Bug #2018994 4.0.21 - Fix issue seen during retype/migrate. Bug #2026718 + 4.0.22 - Fixed clone of replicated volume. Bug #2021941 """ - VERSION = "4.0.21" + VERSION = "4.0.22" stats = {} @@ -2371,7 +2372,7 @@ class HPE3PARCommon(object): return volume_settings - def create_volume(self, volume): + def create_volume(self, volume, perform_replica=True): LOG.debug('CREATE VOLUME (%(disp_name)s: %(vol_name)s %(id)s on ' '%(host)s)', {'disp_name': volume['display_name'], @@ -2469,10 +2470,11 @@ class HPE3PARCommon(object): LOG.error("Exception: %s", ex) raise exception.CinderException(ex) - if (self._volume_of_replicated_type(volume, - hpe_tiramisu_check=True) - and self._do_volume_replication_setup(volume)): - replication_flag = True + if perform_replica: + if (self._volume_of_replicated_type(volume, + hpe_tiramisu_check=True) + and self._do_volume_replication_setup(volume)): + replication_flag = True except hpeexceptions.HTTPConflict: msg = _("Volume (%s) already exists on array") % volume_name @@ -2614,13 +2616,17 @@ class HPE3PARCommon(object): if str(src_vref['status']) == 'backing-up': back_up_process = True - # if the sizes of the 2 volumes are the same and except backup - # process for ISCSI volume with chap enabled on it. + # (i) if the sizes of the 2 volumes are the same and + # (ii) this is not a backup process for ISCSI volume with chap + # enabled on it and + # (iii) volume is not replicated # we can do an online copy, which is a background process # on the 3PAR that makes the volume instantly available. # We can't resize a volume, while it's being copied. if volume['size'] == src_vref['size'] and not ( - back_up_process and vol_chap_enabled): + back_up_process and vol_chap_enabled) and not ( + self._volume_of_replicated_type(volume, + hpe_tiramisu_check=True)): LOG.debug("Creating a clone of volume, using online copy.") type_info = self.get_volume_settings_from_type(volume) @@ -2656,18 +2662,11 @@ class HPE3PARCommon(object): LOG.error(msg) raise exception.CinderException(msg) - # v2 replication check - replication_flag = False - if (self._volume_of_replicated_type(volume, - hpe_tiramisu_check=True) - and self._do_volume_replication_setup(volume)): - replication_flag = True - if self._volume_of_hpe_tiramisu_type(volume): hpe_tiramisu = True return self._get_model_update(volume['host'], cpg, - replication=replication_flag, + replication=False, provider_location=self.client.id, hpe_tiramisu=hpe_tiramisu) else: @@ -2677,7 +2676,8 @@ class HPE3PARCommon(object): LOG.debug("Creating a clone of volume, using non-online copy.") # we first have to create the destination volume - model_update = self.create_volume(volume) + model_update = self.create_volume(volume, + perform_replica=False) optional = {'priority': 1} body = self.client.copyVolume(src_vol_name, vol_name, None, @@ -2694,6 +2694,24 @@ class HPE3PARCommon(object): LOG.debug('Copy volume completed: create_cloned_volume: ' 'id=%s.', volume['id']) + # v2 replication check + LOG.debug("v2 replication check") + replication_flag = False + if (self._volume_of_replicated_type(volume, + hpe_tiramisu_check=True) + and self._do_volume_replication_setup(volume)): + replication_flag = True + type_info = self.get_volume_settings_from_type(volume) + cpg = type_info['cpg'] + model_update = self._get_model_update( + volume['host'], cpg, + replication=True, + provider_location=self.client.id, + hpe_tiramisu=hpe_tiramisu) + + LOG.debug("replication_flag: %(flag)s", + {'flag': replication_flag}) + return model_update except hpeexceptions.HTTPForbidden: diff --git a/releasenotes/notes/hpe-3par-clone-of-repl-vol-914a6e0e105996b4.yaml b/releasenotes/notes/hpe-3par-clone-of-repl-vol-914a6e0e105996b4.yaml new file mode 100644 index 00000000000..56fb986ca09 --- /dev/null +++ b/releasenotes/notes/hpe-3par-clone-of-repl-vol-914a6e0e105996b4.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + HPE 3PAR driver `bug #2021941 + `_: + Fixed: Now clone of replicated volume can be created +