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
This commit is contained in:
Alex O'Rourke 2016-05-23 15:21:32 -07:00
parent 7103cf353a
commit e62ed285f9
3 changed files with 50 additions and 11 deletions

View File

@ -73,7 +73,7 @@ class ClientException(Exception):
return formatted_string return formatted_string
class HTTPConflict(Exception): class HTTPConflict(ClientException):
http_status = 409 http_status = 409
message = "Conflict" message = "Conflict"
@ -85,7 +85,7 @@ class HTTPConflict(Exception):
return self._error_desc return self._error_desc
class HTTPNotFound(Exception): class HTTPNotFound(ClientException):
http_status = 404 http_status = 404
message = "Not found" message = "Not found"
@ -95,7 +95,7 @@ class HTTPForbidden(ClientException):
message = "Forbidden" message = "Forbidden"
class HTTPBadRequest(Exception): class HTTPBadRequest(ClientException):
http_status = 400 http_status = 400
message = "Bad request" message = "Bad request"
@ -105,7 +105,7 @@ class HTTPUnauthorized(ClientException):
message = "Unauthorized" message = "Unauthorized"
class HTTPServerError(Exception): class HTTPServerError(ClientException):
http_status = 500 http_status = 500
message = "Error" message = "Error"

View File

@ -2557,6 +2557,30 @@ class HPE3PARBaseDriver(object):
expected + expected +
self.standard_logout) 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): def test_extend_volume(self):
# setup_mock_client drive with default configuration # setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client # and return the mock HTTP 3PAR client

View File

@ -241,10 +241,11 @@ class HPE3PARCommon(object):
3.0.22 - Rework delete_vlun. Bug #1582922 3.0.22 - Rework delete_vlun. Bug #1582922
3.0.23 - Fix CG create failures with long display name or special 3.0.23 - Fix CG create failures with long display name or special
characters. bug #1573647 characters. bug #1573647
3.0.24 - Fix terminate connection on failover
""" """
VERSION = "3.0.23" VERSION = "3.0.24"
stats = {} stats = {}
@ -2509,6 +2510,20 @@ class HPE3PARCommon(object):
return return
except hpeexceptions.HTTPNotFound as e: except hpeexceptions.HTTPNotFound as e:
if 'host does not exist' in e.get_description(): if 'host does not exist' in e.get_description():
# 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 # use the wwn to see if we can find the hostname
hostname = self._get_3par_hostname_from_wwn_iqn(wwn, iqn) hostname = self._get_3par_hostname_from_wwn_iqn(wwn, iqn)
# no 3par host, re-throw # no 3par host, re-throw