diff --git a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py index 87aabf87cc2..88b66147640 100644 --- a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py +++ b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py @@ -2146,7 +2146,10 @@ class HPE3PARBaseDriver(object): # and return the mock HTTP 3PAR client mock_client = self.setup_driver() mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} - + ex = hpeexceptions.HTTPConflict("In use") + ex._error_code = 34 + mock_client.deleteVolume = mock.Mock(side_effect=[ex, 200]) + mock_client.findVolumeSet.return_value = self.VVS_NAME _mock_volume_types.return_value = { 'name': 'replicated', 'extra_specs': { @@ -2170,6 +2173,10 @@ class HPE3PARBaseDriver(object): self.VOLUME_3PAR_NAME, removeFromTarget=True), mock.call.removeRemoteCopyGroup(self.RCG_3PAR_NAME), + mock.call.deleteVolume(self.VOLUME_3PAR_NAME), + mock.call.findVolumeSet(self.VOLUME_3PAR_NAME), + mock.call.removeVolumeFromVolumeSet(self.VVS_NAME, + self.VOLUME_3PAR_NAME), mock.call.deleteVolume(self.VOLUME_3PAR_NAME)] mock_client.assert_has_calls( diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index c5c9dfa2a3e..989c8b0ab2b 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -262,11 +262,13 @@ class HPE3PARCommon(object): 3.0.35 - Add volume to consistency group if flag enabled. bug #1702317 3.0.36 - Swap volume name in migration. bug #1699733 3.0.37 - Fixed image cache enabled capability. bug #1686985 + 3.0.38 - Fixed delete operation of replicated volume which is part + of QOS. bug #1717875 """ - VERSION = "3.0.37" + VERSION = "3.0.38" stats = {} @@ -2266,19 +2268,7 @@ class HPE3PARCommon(object): if ex.get_code() == 34: # This is a special case which means the # volume is part of a volume set. - vvset_name = self.client.findVolumeSet(volume_name) - LOG.debug("Returned vvset_name = %s", vvset_name) - if vvset_name is not None and \ - vvset_name.startswith('vvs-'): - # We have a single volume per volume set, so - # remove the volume set. - self.client.deleteVolumeSet( - self._get_3par_vvs_name(volume['id'])) - elif vvset_name is not None: - # We have a pre-defined volume set just remove the - # volume and leave the volume set. - self.client.removeVolumeFromVolumeSet(vvset_name, - volume_name) + self._delete_vvset(volume) self.client.deleteVolume(volume_name) elif ex.get_code() == 151: if self.client.isOnlinePhysicalCopy(volume_name): @@ -3680,6 +3670,12 @@ class HPE3PARCommon(object): try: if not retype: self.client.deleteVolume(vol_name) + except hpeexceptions.HTTPConflict as ex: + if ex.get_code() == 34: + # This is a special case which means the + # volume is part of a volume set. + self._delete_vvset(volume) + self.client.deleteVolume(vol_name) except Exception: pass @@ -3709,6 +3705,24 @@ class HPE3PARCommon(object): self.client.toggleRemoteCopyConfigMirror(target_name, mirror_config=True) + def _delete_vvset(self, volume): + + # volume is part of a volume set. + volume_name = self._get_3par_vol_name(volume['id']) + vvset_name = self.client.findVolumeSet(volume_name) + LOG.debug("Returned vvset_name = %s", vvset_name) + if vvset_name is not None: + if vvset_name.startswith('vvs-'): + # We have a single volume per volume set, so + # remove the volume set. + self.client.deleteVolumeSet( + self._get_3par_vvs_name(volume['id'])) + else: + # We have a pre-defined volume set just remove the + # volume and leave the volume set. + self.client.removeVolumeFromVolumeSet(vvset_name, + volume_name) + class TaskWaiter(object): """TaskWaiter waits for task to be not active and returns status."""