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
This commit is contained in:
raghavendrat 2023-05-30 19:45:50 +00:00
parent 28672aeb73
commit 577e2ee108
4 changed files with 107 additions and 19 deletions

View File

@ -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': '<is> 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 = {

View File

@ -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

View File

@ -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:

View File

@ -0,0 +1,7 @@
---
fixes:
- |
HPE 3PAR driver `bug #2021941
<https://bugs.launchpad.net/cinder/+bug/2021941>`_:
Fixed: Now clone of replicated volume can be created