From e62ed285f9648f3fe8bf68072526eab0530e3306 Mon Sep 17 00:00:00 2001 From: Alex O'Rourke Date: Mon, 23 May 2016 15:21:32 -0700 Subject: [PATCH] 3PAR: Fix terminate_connection when failed over Terminate connection will fail when a detach is called on a failed over volume. This patch allows terminate_connection to succeed in order to allow reattachments to secondary arrays. Closes-Bug: #1580691 Change-Id: Ibf91882b94ea5cca51dc7be91ec05179391a9b78 --- .../tests/unit/fake_hpe_client_exceptions.py | 8 ++--- cinder/tests/unit/test_hpe3par.py | 24 +++++++++++++++ cinder/volume/drivers/hpe/hpe_3par_common.py | 29 ++++++++++++++----- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/cinder/tests/unit/fake_hpe_client_exceptions.py b/cinder/tests/unit/fake_hpe_client_exceptions.py index f2d73bb0274..c496e07bf62 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,7 +95,7 @@ class HTTPForbidden(ClientException): message = "Forbidden" -class HTTPBadRequest(Exception): +class HTTPBadRequest(ClientException): http_status = 400 message = "Bad request" @@ -105,7 +105,7 @@ class HTTPUnauthorized(ClientException): message = "Unauthorized" -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 0d8a5d3221c..d1b0b23b4d0 100644 --- a/cinder/tests/unit/test_hpe3par.py +++ b/cinder/tests/unit/test_hpe3par.py @@ -2557,6 +2557,30 @@ class HPE3PARBaseDriver(object): expected + self.standard_logout) + def test_terminate_connection_from_primary_when_failed_over(self): + # setup_mock_client drive with default configuration + # and return the mock HTTP 3PAR client + mock_client = self.setup_driver() + mock_client.getHostVLUNs.side_effect = hpeexceptions.HTTPNotFound( + error={'desc': 'The host does not exist.'}) + + with mock.patch.object(hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + + self.driver._active_backend_id = 'some_id' + self.driver.terminate_connection( + self.volume, + self.connector, + force=True) + + # When the volume is still attached to the primary array after a + # fail-over, there should be no call to delete the VLUN(s) or the + # host. We can assert these methods were not called to make sure + # the proper exceptions are being raised. + self.assertEqual(0, mock_client.deleteVLUN.call_count) + self.assertEqual(0, mock_client.deleteHost.call_count) + def test_extend_volume(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index 623e37c64fe..66cbee24705 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -241,10 +241,11 @@ class HPE3PARCommon(object): 3.0.22 - Rework delete_vlun. Bug #1582922 3.0.23 - Fix CG create failures with long display name or special characters. bug #1573647 + 3.0.24 - Fix terminate connection on failover """ - VERSION = "3.0.23" + VERSION = "3.0.24" stats = {} @@ -2509,12 +2510,26 @@ class HPE3PARCommon(object): return except hpeexceptions.HTTPNotFound as e: if 'host does not exist' in e.get_description(): - # use the wwn to see if we can find the hostname - hostname = self._get_3par_hostname_from_wwn_iqn(wwn, iqn) - # no 3par host, re-throw - if hostname is None: - LOG.error(_LE("Exception: %s"), e) - raise + # If a host is failed-over, we want to allow the detach to + # 'succeed' when it cannot find the host. We can simply + # return out of the terminate connection in order for things + # to be updated correctly. + if self._active_backend_id: + LOG.warning(_LW("Because the host is currently in a " + "failed-over state, the volume will not " + "be properly detached from the primary " + "array. The detach will be considered a " + "success as far as Cinder is concerned. " + "The volume can now be attached to the " + "secondary target.")) + return + else: + # use the wwn to see if we can find the hostname + hostname = self._get_3par_hostname_from_wwn_iqn(wwn, iqn) + # no 3par host, re-throw + if hostname is None: + LOG.error(_LE("Exception: %s"), e) + raise else: # not a 'host does not exist' HTTPNotFound exception, re-throw LOG.error(_LE("Exception: %s"), e)