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:
parent
28672aeb73
commit
577e2ee108
@ -2992,6 +2992,69 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
|
|
||||||
mock_client.assert_has_calls(expected)
|
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):
|
def test_migrate_volume(self):
|
||||||
|
|
||||||
conf = {
|
conf = {
|
||||||
|
@ -147,7 +147,7 @@ class HPE3PARDriverBase(driver.ManageableVD,
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@volume_utils.trace
|
@volume_utils.trace
|
||||||
def create_volume(self, volume):
|
def create_volume(self, volume, perform_replica=True):
|
||||||
return self.common.create_volume(volume)
|
return self.common.create_volume(volume)
|
||||||
|
|
||||||
@volume_utils.trace
|
@volume_utils.trace
|
||||||
|
@ -306,11 +306,12 @@ class HPE3PARCommon(object):
|
|||||||
4.0.19 - Update code to work with new WSAPI (of 2023). Bug #2015746
|
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.20 - Use small QoS Latency value. Bug #2018994
|
||||||
4.0.21 - Fix issue seen during retype/migrate. Bug #2026718
|
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 = {}
|
stats = {}
|
||||||
|
|
||||||
@ -2371,7 +2372,7 @@ class HPE3PARCommon(object):
|
|||||||
|
|
||||||
return volume_settings
|
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 '
|
LOG.debug('CREATE VOLUME (%(disp_name)s: %(vol_name)s %(id)s on '
|
||||||
'%(host)s)',
|
'%(host)s)',
|
||||||
{'disp_name': volume['display_name'],
|
{'disp_name': volume['display_name'],
|
||||||
@ -2469,10 +2470,11 @@ class HPE3PARCommon(object):
|
|||||||
LOG.error("Exception: %s", ex)
|
LOG.error("Exception: %s", ex)
|
||||||
raise exception.CinderException(ex)
|
raise exception.CinderException(ex)
|
||||||
|
|
||||||
if (self._volume_of_replicated_type(volume,
|
if perform_replica:
|
||||||
hpe_tiramisu_check=True)
|
if (self._volume_of_replicated_type(volume,
|
||||||
and self._do_volume_replication_setup(volume)):
|
hpe_tiramisu_check=True)
|
||||||
replication_flag = True
|
and self._do_volume_replication_setup(volume)):
|
||||||
|
replication_flag = True
|
||||||
|
|
||||||
except hpeexceptions.HTTPConflict:
|
except hpeexceptions.HTTPConflict:
|
||||||
msg = _("Volume (%s) already exists on array") % volume_name
|
msg = _("Volume (%s) already exists on array") % volume_name
|
||||||
@ -2614,13 +2616,17 @@ class HPE3PARCommon(object):
|
|||||||
if str(src_vref['status']) == 'backing-up':
|
if str(src_vref['status']) == 'backing-up':
|
||||||
back_up_process = True
|
back_up_process = True
|
||||||
|
|
||||||
# if the sizes of the 2 volumes are the same and except backup
|
# (i) if the sizes of the 2 volumes are the same and
|
||||||
# process for ISCSI volume with chap enabled on it.
|
# (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
|
# we can do an online copy, which is a background process
|
||||||
# on the 3PAR that makes the volume instantly available.
|
# on the 3PAR that makes the volume instantly available.
|
||||||
# We can't resize a volume, while it's being copied.
|
# We can't resize a volume, while it's being copied.
|
||||||
if volume['size'] == src_vref['size'] and not (
|
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.")
|
LOG.debug("Creating a clone of volume, using online copy.")
|
||||||
|
|
||||||
type_info = self.get_volume_settings_from_type(volume)
|
type_info = self.get_volume_settings_from_type(volume)
|
||||||
@ -2656,18 +2662,11 @@ class HPE3PARCommon(object):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.CinderException(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):
|
if self._volume_of_hpe_tiramisu_type(volume):
|
||||||
hpe_tiramisu = True
|
hpe_tiramisu = True
|
||||||
|
|
||||||
return self._get_model_update(volume['host'], cpg,
|
return self._get_model_update(volume['host'], cpg,
|
||||||
replication=replication_flag,
|
replication=False,
|
||||||
provider_location=self.client.id,
|
provider_location=self.client.id,
|
||||||
hpe_tiramisu=hpe_tiramisu)
|
hpe_tiramisu=hpe_tiramisu)
|
||||||
else:
|
else:
|
||||||
@ -2677,7 +2676,8 @@ class HPE3PARCommon(object):
|
|||||||
LOG.debug("Creating a clone of volume, using non-online copy.")
|
LOG.debug("Creating a clone of volume, using non-online copy.")
|
||||||
|
|
||||||
# we first have to create the destination volume
|
# 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}
|
optional = {'priority': 1}
|
||||||
body = self.client.copyVolume(src_vol_name, vol_name, None,
|
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: '
|
LOG.debug('Copy volume completed: create_cloned_volume: '
|
||||||
'id=%s.', volume['id'])
|
'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
|
return model_update
|
||||||
|
|
||||||
except hpeexceptions.HTTPForbidden:
|
except hpeexceptions.HTTPForbidden:
|
||||||
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user