From 48771cc74445536d315c788c58fe0e2ffeab219e Mon Sep 17 00:00:00 2001 From: kushal Date: Thu, 21 Sep 2017 03:29:12 -0700 Subject: [PATCH] HPE 3PAR: fix delete operation of replicated volume When QoS is set to the replicated volume, at Storage level the same volume becomes part of vvset This patch delete vvset if one volume per vvset else it remove volume from vvset. Then delete the volume Change-Id: Iebac77f569adbf763f0a5668cfdc5ea546846d67 Closes-Bug: #1717875 --- .../unit/volume/drivers/hpe/test_hpe3par.py | 9 +++- cinder/volume/drivers/hpe/hpe_3par_common.py | 42 ++++++++++++------- 2 files changed, 36 insertions(+), 15 deletions(-) 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."""