Merge "3PAR: Error out if vol cannot be converted to base" into stable/2023.1
This commit is contained in:
commit
01c51a9784
@ -3499,6 +3499,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
}
|
}
|
||||||
|
|
||||||
mock_client = self.setup_driver(mock_conf=conf)
|
mock_client = self.setup_driver(mock_conf=conf)
|
||||||
|
mock_client.getVolumeSnapshots.return_value = []
|
||||||
with mock.patch.object(hpecommon.HPE3PARCommon,
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
'_create_client') as mock_create_client:
|
'_create_client') as mock_create_client:
|
||||||
mock_create_client.return_value = mock_client
|
mock_create_client.return_value = mock_client
|
||||||
@ -3527,6 +3528,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
{
|
{
|
||||||
'comment': comment,
|
'comment': comment,
|
||||||
'readOnly': False}),
|
'readOnly': False}),
|
||||||
|
mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
|
||||||
mock.call.copyVolume(
|
mock.call.copyVolume(
|
||||||
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
||||||
mock.call.getTask(mock.ANY),
|
mock.call.getTask(mock.ANY),
|
||||||
@ -3550,6 +3552,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
}
|
}
|
||||||
|
|
||||||
mock_client = self.setup_driver(mock_conf=conf)
|
mock_client = self.setup_driver(mock_conf=conf)
|
||||||
|
mock_client.getVolumeSnapshots.return_value = []
|
||||||
_mock_volume_types.return_value = {
|
_mock_volume_types.return_value = {
|
||||||
'name': 'gold',
|
'name': 'gold',
|
||||||
'extra_specs': {
|
'extra_specs': {
|
||||||
@ -3591,6 +3594,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
'comment': comment,
|
'comment': comment,
|
||||||
'readOnly': False}),
|
'readOnly': False}),
|
||||||
mock.call.getCPG(HPE3PAR_CPG),
|
mock.call.getCPG(HPE3PAR_CPG),
|
||||||
|
mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
|
||||||
mock.call.copyVolume(
|
mock.call.copyVolume(
|
||||||
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
||||||
mock.call.getTask(mock.ANY),
|
mock.call.getTask(mock.ANY),
|
||||||
@ -3707,6 +3711,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
'getVolume.return_value': {}
|
'getVolume.return_value': {}
|
||||||
}
|
}
|
||||||
mock_client = self.setup_driver(mock_conf=conf)
|
mock_client = self.setup_driver(mock_conf=conf)
|
||||||
|
mock_client.getVolumeSnapshots.return_value = []
|
||||||
volume_type_hos = copy.deepcopy(self.volume_type_hos)
|
volume_type_hos = copy.deepcopy(self.volume_type_hos)
|
||||||
volume_type_hos['extra_specs']['convert_to_base'] = True
|
volume_type_hos['extra_specs']['convert_to_base'] = True
|
||||||
_mock_volume_types.return_value = volume_type_hos
|
_mock_volume_types.return_value = volume_type_hos
|
||||||
@ -3736,6 +3741,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
{
|
{
|
||||||
'comment': comment,
|
'comment': comment,
|
||||||
'readOnly': False}),
|
'readOnly': False}),
|
||||||
|
mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
|
||||||
mock.call.copyVolume(
|
mock.call.copyVolume(
|
||||||
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
||||||
mock.call.getTask(mock.ANY),
|
mock.call.getTask(mock.ANY),
|
||||||
@ -3757,6 +3763,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
'getVolume.return_value': {}
|
'getVolume.return_value': {}
|
||||||
}
|
}
|
||||||
mock_client = self.setup_driver(mock_conf=conf)
|
mock_client = self.setup_driver(mock_conf=conf)
|
||||||
|
mock_client.getVolumeSnapshots.return_value = []
|
||||||
_mock_volume_types.return_value = self.volume_type_hos
|
_mock_volume_types.return_value = self.volume_type_hos
|
||||||
with mock.patch.object(hpecommon.HPE3PARCommon,
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
'_create_client') as mock_create_client:
|
'_create_client') as mock_create_client:
|
||||||
@ -3785,6 +3792,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
{
|
{
|
||||||
'comment': comment,
|
'comment': comment,
|
||||||
'readOnly': False}),
|
'readOnly': False}),
|
||||||
|
mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
|
||||||
mock.call.copyVolume(
|
mock.call.copyVolume(
|
||||||
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
||||||
mock.call.getTask(mock.ANY),
|
mock.call.getTask(mock.ANY),
|
||||||
@ -3807,6 +3815,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
'getVolume.return_value': {}
|
'getVolume.return_value': {}
|
||||||
}
|
}
|
||||||
mock_client = self.setup_driver(mock_conf=conf)
|
mock_client = self.setup_driver(mock_conf=conf)
|
||||||
|
mock_client.getVolumeSnapshots.return_value = []
|
||||||
volume_type_hos = copy.deepcopy(self.volume_type_hos)
|
volume_type_hos = copy.deepcopy(self.volume_type_hos)
|
||||||
volume_type_hos['extra_specs']['convert_to_base'] = True
|
volume_type_hos['extra_specs']['convert_to_base'] = True
|
||||||
_mock_volume_types.return_value = volume_type_hos
|
_mock_volume_types.return_value = volume_type_hos
|
||||||
@ -3837,6 +3846,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
{
|
{
|
||||||
'comment': comment,
|
'comment': comment,
|
||||||
'readOnly': False}),
|
'readOnly': False}),
|
||||||
|
mock.call.getVolumeSnapshots(self.VOLUME_3PAR_NAME),
|
||||||
mock.call.copyVolume(
|
mock.call.copyVolume(
|
||||||
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
osv_matcher, omv_matcher, HPE3PAR_CPG, mock.ANY),
|
||||||
mock.call.getTask(mock.ANY),
|
mock.call.getTask(mock.ANY),
|
||||||
@ -3935,6 +3945,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
}
|
}
|
||||||
|
|
||||||
mock_client = self.setup_driver(mock_conf=conf)
|
mock_client = self.setup_driver(mock_conf=conf)
|
||||||
|
mock_client.getVolumeSnapshots.return_value = []
|
||||||
with mock.patch.object(hpecommon.HPE3PARCommon,
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
'_create_client') as mock_create_client:
|
'_create_client') as mock_create_client:
|
||||||
mock_create_client.return_value = mock_client
|
mock_create_client.return_value = mock_client
|
||||||
@ -3958,6 +3969,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
}
|
}
|
||||||
|
|
||||||
mock_client = self.setup_driver(mock_conf=conf)
|
mock_client = self.setup_driver(mock_conf=conf)
|
||||||
|
mock_client.getVolumeSnapshots.return_value = []
|
||||||
with mock.patch.object(hpecommon.HPE3PARCommon,
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
'_create_client') as mock_create_client:
|
'_create_client') as mock_create_client:
|
||||||
mock_create_client.return_value = mock_client
|
mock_create_client.return_value = mock_client
|
||||||
@ -3969,6 +3981,18 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
|
|||||||
self.volume,
|
self.volume,
|
||||||
str(new_size))
|
str(new_size))
|
||||||
|
|
||||||
|
def test__convert_to_base_volume_failure(self):
|
||||||
|
mock_client = self.setup_driver()
|
||||||
|
mock_client.getVolumeSnapshots.return_value = (
|
||||||
|
['oss-nwJVbXaEQMi0w.xPutFRQw'])
|
||||||
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
|
'_create_client') as mock_create_client:
|
||||||
|
mock_create_client.return_value = mock_client
|
||||||
|
common = self.driver._login()
|
||||||
|
self.assertRaises(exception.VolumeIsBusy,
|
||||||
|
common._convert_to_base_volume,
|
||||||
|
self.volume)
|
||||||
|
|
||||||
@mock.patch.object(volume_types, 'get_volume_type')
|
@mock.patch.object(volume_types, 'get_volume_type')
|
||||||
def test_extend_volume_replicated(self, _mock_volume_types):
|
def test_extend_volume_replicated(self, _mock_volume_types):
|
||||||
# Managed vs. unmanaged and periodic vs. sync are not relevant when
|
# Managed vs. unmanaged and periodic vs. sync are not relevant when
|
||||||
|
@ -301,6 +301,8 @@ class HPE3PARCommon(object):
|
|||||||
4.0.16 - In multi host env, fix multi-detach operation. Bug #1958122
|
4.0.16 - In multi host env, fix multi-detach operation. Bug #1958122
|
||||||
4.0.17 - Added get_manageable_volumes and get_manageable_snapshots.
|
4.0.17 - Added get_manageable_volumes and get_manageable_snapshots.
|
||||||
Bug #1819903
|
Bug #1819903
|
||||||
|
4.0.18 - During conversion of volume to base volume,
|
||||||
|
error out if it has child snapshot(s). Bug #1994521
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@ -3149,6 +3151,21 @@ class HPE3PARCommon(object):
|
|||||||
|
|
||||||
compression = self.get_compression_policy(
|
compression = self.get_compression_policy(
|
||||||
type_info['hpe3par_keys'])
|
type_info['hpe3par_keys'])
|
||||||
|
|
||||||
|
# If volume (osv-) has snapshot, while converting the volume
|
||||||
|
# to base volume (omv-), snapshot cannot be transferred to
|
||||||
|
# new base volume (omv-) i.e it remain with volume (osv-).
|
||||||
|
# So error out for such volume.
|
||||||
|
snap_list = self.client.getVolumeSnapshots(volume_name)
|
||||||
|
if snap_list:
|
||||||
|
snap_str = ",".join(snap_list)
|
||||||
|
msg = (_("Volume %(name)s has dependent snapshots: %(snap)s."
|
||||||
|
" Either flatten or remove the dependent snapshots:"
|
||||||
|
" %(snap)s for the conversion of volume %(name)s to"
|
||||||
|
" succeed." % {'name': volume_name,
|
||||||
|
'snap': snap_str}))
|
||||||
|
raise exception.VolumeIsBusy(message=msg)
|
||||||
|
|
||||||
# Create a physical copy of the volume
|
# Create a physical copy of the volume
|
||||||
task_id = self._copy_volume(volume_name, temp_vol_name,
|
task_id = self._copy_volume(volume_name, temp_vol_name,
|
||||||
cpg, cpg, type_info['tpvv'],
|
cpg, cpg, type_info['tpvv'],
|
||||||
@ -3172,16 +3189,18 @@ class HPE3PARCommon(object):
|
|||||||
comment = self._get_3par_vol_comment(volume_name)
|
comment = self._get_3par_vol_comment(volume_name)
|
||||||
if comment:
|
if comment:
|
||||||
self.client.modifyVolume(temp_vol_name, {'comment': comment})
|
self.client.modifyVolume(temp_vol_name, {'comment': comment})
|
||||||
LOG.debug('Volume rename completed: convert_to_base_volume: '
|
LOG.debug('Assigned the comment: convert_to_base_volume: '
|
||||||
'id=%s.', volume['id'])
|
'id=%s.', volume['id'])
|
||||||
|
|
||||||
# Delete source volume after the copy is complete
|
# Delete source volume (osv-) after the copy is complete
|
||||||
self.client.deleteVolume(volume_name)
|
self.client.deleteVolume(volume_name)
|
||||||
LOG.debug('Delete src volume completed: convert_to_base_volume: '
|
LOG.debug('Delete src volume completed: convert_to_base_volume: '
|
||||||
'id=%s.', volume['id'])
|
'id=%s.', volume['id'])
|
||||||
|
|
||||||
# Rename the new volume to the original name
|
# Rename the new volume (omv-) to the original name (osv-)
|
||||||
self.client.modifyVolume(temp_vol_name, {'newName': volume_name})
|
self.client.modifyVolume(temp_vol_name, {'newName': volume_name})
|
||||||
|
LOG.debug('Volume rename completed: convert_to_base_volume: '
|
||||||
|
'id=%s.', volume['id'])
|
||||||
|
|
||||||
LOG.info('Completed: convert_to_base_volume: '
|
LOG.info('Completed: convert_to_base_volume: '
|
||||||
'id=%s.', volume['id'])
|
'id=%s.', volume['id'])
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
HPE 3PAR driver `Bug #1994521 <https://bugs.launchpad.net/cinder/+bug/1994521>`_:
|
||||||
|
Fixed: While performing a delete snapshot (s1) operation, the volumes (v2)
|
||||||
|
dependent on the snapshot (s1) are converted to base volumes. This
|
||||||
|
operation fails if these dependent volumes (v2) have their own dependent
|
||||||
|
snapshots (s2). The errors during the failure were vague and not helpful.
|
||||||
|
With this release, we added conditions to fail this operation early and
|
||||||
|
also added useful error message.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user