From 5248fca729f34d8b2dea780ce0d80c3d817992ad Mon Sep 17 00:00:00 2001 From: Alex O'Rourke Date: Thu, 9 Jun 2016 09:13:13 -0700 Subject: [PATCH] 3PAR: Fix delete volume when online clone When an online clone volume is deleted, the clone needs to be stopped first. On delete, a new exception is raised from the 3PAR. We need to check for the error code when hpeexceptions.HTTPConflict is raised. Change-Id: Iceb1b35e0312b109b2b19cd7d5c45b4a6242ccc5 Closes-Bug: #1349639 (cherry picked from commit f0dda71610de8c48abb979c54a650e0b024134a6) --- .../tests/unit/fake_hpe_client_exceptions.py | 8 ++--- cinder/tests/unit/test_hpe3par.py | 23 +++++++++++++ cinder/volume/drivers/hpe/hpe_3par_common.py | 32 ++++++++++++------- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/cinder/tests/unit/fake_hpe_client_exceptions.py b/cinder/tests/unit/fake_hpe_client_exceptions.py index 71593e929eb..0226a55bc03 100644 --- a/cinder/tests/unit/fake_hpe_client_exceptions.py +++ b/cinder/tests/unit/fake_hpe_client_exceptions.py @@ -73,7 +73,7 @@ class ClientException(Exception): return formatted_string -class HTTPConflict(Exception): +class HTTPConflict(ClientException): http_status = 409 message = "Conflict" @@ -85,7 +85,7 @@ class HTTPConflict(Exception): return self._error_desc -class HTTPNotFound(Exception): +class HTTPNotFound(ClientException): http_status = 404 message = "Not found" @@ -95,12 +95,12 @@ class HTTPForbidden(ClientException): message = "Forbidden" -class HTTPBadRequest(Exception): +class HTTPBadRequest(ClientException): http_status = 400 message = "Bad request" -class HTTPServerError(Exception): +class HTTPServerError(ClientException): http_status = 500 message = "Error" diff --git a/cinder/tests/unit/test_hpe3par.py b/cinder/tests/unit/test_hpe3par.py index 58b5db914ea..f29305b1f30 100644 --- a/cinder/tests/unit/test_hpe3par.py +++ b/cinder/tests/unit/test_hpe3par.py @@ -1683,6 +1683,29 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) + def test_delete_volume_online_clone_active(self): + # setup_mock_client drive with default configuration + # and return the mock HTTP 3PAR client + mock_client = self.setup_driver() + with mock.patch.object(hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + ex = hpeexceptions.HTTPConflict("Online clone is active.") + ex._error_code = 151 + mock_client.deleteVolume = mock.Mock(side_effect=ex) + mock_client.isOnlinePhysicalCopy.return_value = True + self.driver.delete_volume(self.volume) + + expected = [ + mock.call.deleteVolume(self.VOLUME_3PAR_NAME), + mock.call.isOnlinePhysicalCopy(self.VOLUME_3PAR_NAME), + mock.call.stopOnlinePhysicalCopy(self.VOLUME_3PAR_NAME)] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + @mock.patch.object(volume_types, 'get_volume_type') def test_delete_volume_replicated(self, _mock_volume_types): # setup_mock_client drive with default configuration diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index 104993aef3e..1ff0b23a680 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -234,10 +234,12 @@ class HPE3PARCommon(object): 3.0.18.1 - Rework delete_vlun. Bug #1582922 (backported from Newton) 3.0.18.2 - Driver no longer fails to initialize if System Reporter license is missing. bug #1568078 (backported from Newton) + 3.0.18.3 - Fix delete volume when online clone is active. bug #1349639. + (backported from Newton) """ - VERSION = "3.0.18.2" + VERSION = "3.0.18.3" stats = {} @@ -2091,16 +2093,24 @@ class HPE3PARCommon(object): self.client.removeVolumeFromVolumeSet(vvset_name, volume_name) self.client.deleteVolume(volume_name) - elif (ex.get_code() == 151): - # the volume is being operated on in a background - # task on the 3PAR. - # TODO(walter-boring) do a retry a few times. - # for now lets log a better message - msg = _("The volume is currently busy on the 3PAR" - " and cannot be deleted at this time. " - "You can try again later.") - LOG.error(msg) - raise exception.VolumeIsBusy(message=msg) + elif ex.get_code() == 151: + if self.client.isOnlinePhysicalCopy(volume_name): + LOG.debug("Found an online copy for %(volume)s", + {'volume': volume_name}) + # the volume is in process of being cloned. + # stopOnlinePhysicalCopy will also delete + # the volume once it stops the copy. + self.client.stopOnlinePhysicalCopy(volume_name) + else: + # the volume is being operated on in a background + # task on the 3PAR. + # TODO(walter-boring) do a retry a few times. + # for now lets log a better message + msg = _("The volume is currently busy on the 3PAR" + " and cannot be deleted at this time. " + "You can try again later.") + LOG.error(msg) + raise exception.VolumeIsBusy(message=msg) elif (ex.get_code() == 32): # Error 32 means that the volume has children