From a433c19ef09c2f3e44dd3dd23fa1283c401aaf30 Mon Sep 17 00:00:00 2001 From: Vivek Soni Date: Fri, 15 Jun 2018 01:31:17 -0700 Subject: [PATCH] HPE3PAR: Fix pointing to backend in group failover Issue: After group failover, subsequent operations like attach & detach volume, which are part of that failed over group points to primary backend instead of secondary. This patch fixes the above issue by setting up the appropriate backend in case of subsequent operation on volume, which are part of group which is failed over. Change-Id: I679b11317c91ad28cefdf995a8d6849dc71bc1c5 Closes-Bug: #1773069 --- .../unit/volume/drivers/hpe/test_hpe3par.py | 34 ++++++++++++++++++- cinder/volume/drivers/hpe/hpe_3par_base.py | 15 ++++++-- cinder/volume/drivers/hpe/hpe_3par_common.py | 23 ++++++++----- cinder/volume/drivers/hpe/hpe_3par_fc.py | 10 ++++-- cinder/volume/drivers/hpe/hpe_3par_iscsi.py | 8 +++-- 5 files changed, 72 insertions(+), 18 deletions(-) diff --git a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py index fecfcaaea83..1b57575f4f1 100644 --- a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py +++ b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py @@ -3526,7 +3526,39 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): # 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_terminate_connection_from_primary_when_group_failed_over(self): + mock_conf = { + 'getStorageSystemInfo.return_value': { + 'id': self.REPLICATION_CLIENT_ID, + 'name': 'CSIM-EOS12_1611702'}} + + conf = self.setup_configuration() + conf.replication_device = self.replication_targets + mock_client = self.setup_driver(config=conf, mock_conf=mock_conf) + + 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 + + volume = self.volume_tiramisu.copy() + volume['replication_status'] = 'failed-over' + volume['replication_driver_data'] = self.REPLICATION_CLIENT_ID + + self.driver._active_backend_id = "CSIM-EOS12_1611702" + 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) def test_extend_volume(self): # setup_mock_client drive with default configuration diff --git a/cinder/volume/drivers/hpe/hpe_3par_base.py b/cinder/volume/drivers/hpe/hpe_3par_base.py index 43dda299d3f..61db07c2d30 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_base.py +++ b/cinder/volume/drivers/hpe/hpe_3par_base.py @@ -57,10 +57,12 @@ class HPE3PARDriverBase(driver.ManageableVD, 1.0.2 - Adds capability. 1.0.3 - Added Tiramisu feature on 3PAR. 1.0.4 - Fixed Volume migration for "in-use" volume. bug #1744021 + 1.0.5 - Set proper backend on subsequent operation, after group + failover. bug #1773069 """ - VERSION = "1.0.4" + VERSION = "1.0.5" def __init__(self, *args, **kwargs): super(HPE3PARDriverBase, self).__init__(*args, **kwargs) @@ -73,12 +75,13 @@ class HPE3PARDriverBase(driver.ManageableVD, return hpecommon.HPE3PARCommon(self.configuration, self._active_backend_id) - def _login(self, timeout=None): + def _login(self, timeout=None, array_id=None): common = self._init_common() # If replication is enabled and we cannot login, we do not want to # raise an exception so a failover can still be executed. try: - common.do_setup(None, timeout=timeout, stats=self._stats) + common.do_setup(None, timeout=timeout, stats=self._stats, + array_id=array_id) common.client_login() except Exception: if common._replication_enabled: @@ -105,6 +108,12 @@ class HPE3PARDriverBase(driver.ManageableVD, 'san_password'] common.check_flags(self.configuration, required_flags) + def get_volume_replication_driver_data(self, volume): + if (volume.get("group_id") and volume.get("replication_status") and + volume.get("replication_status") == "failed-over"): + return int(volume.get("replication_driver_data")) + return None + @utils.trace def get_volume_stats(self, refresh=False): common = self._login() diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index 0ff80a8c24a..c17ac64f38e 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -267,11 +267,13 @@ class HPE3PARCommon(object): 4.0.6 - Monitor task of promoting a virtual copy. bug #1749642 4.0.7 - Handle force detach case. bug #1686745 4.0.8 - Added support for report backend state in service list. + 4.0.9 - Set proper backend on subsequent operation, after group + failover. bug #1773069 """ - VERSION = "4.0.8" + VERSION = "4.0.9" stats = {} @@ -430,7 +432,7 @@ class HPE3PARCommon(object): if client is not None: client.logout() - def do_setup(self, context, timeout=None, stats=None): + def do_setup(self, context, timeout=None, stats=None, array_id=None): if hpe3parclient is None: msg = _('You must install hpe3parclient before using 3PAR' ' drivers. Run "pip install python-3parclient" to' @@ -442,7 +444,7 @@ class HPE3PARCommon(object): # to communicate with the 3PAR array. It will contain either # the values for the primary array or secondary array in the # case of a fail-over. - self._get_3par_config() + self._get_3par_config(array_id=array_id) self.client = self._create_client(timeout=timeout) wsapi_version = self.client.getWsApiVersion() self.API_VERSION = wsapi_version['build'] @@ -461,7 +463,7 @@ class HPE3PARCommon(object): except hpeexceptions.UnsupportedVersion as ex: # In the event we cannot contact the configured primary array, # we want to allow a failover if replication is enabled. - self._do_replication_setup() + self._do_replication_setup(array_id=array_id) if self._replication_enabled: self.client = None raise exception.InvalidInput(ex) @@ -3591,7 +3593,7 @@ class HPE3PARCommon(object): return True - def _do_replication_setup(self): + def _do_replication_setup(self, array_id=None): replication_targets = [] replication_devices = self.config.replication_device if replication_devices: @@ -3624,8 +3626,11 @@ class HPE3PARCommon(object): cl = None try: cl = self._create_replication_client(remote_array) - array_id = six.text_type(cl.getStorageSystemInfo()['id']) - remote_array['id'] = array_id + info = cl.getStorageSystemInfo() + remote_array['id'] = six.text_type(info['id']) + if array_id and array_id == info['id']: + self._active_backend_id = six.text_type(info['name']) + wsapi_version = cl.getWsApiVersion()['build'] if wsapi_version < REMOTE_COPY_API_VERSION: @@ -3779,8 +3784,8 @@ class HPE3PARCommon(object): ret_mode = self.PERIODIC return ret_mode - def _get_3par_config(self): - self._do_replication_setup() + def _get_3par_config(self, array_id=None): + self._do_replication_setup(array_id=array_id) conf = None if self._replication_enabled: for target in self._replication_targets: diff --git a/cinder/volume/drivers/hpe/hpe_3par_fc.py b/cinder/volume/drivers/hpe/hpe_3par_fc.py index e5aa9b37620..1a8d629cc00 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_fc.py +++ b/cinder/volume/drivers/hpe/hpe_3par_fc.py @@ -109,10 +109,12 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase): 4.0.2 - Create one vlun in single path configuration. bug #1727176 4.0.3 - Create FC vlun as host sees. bug #1734505 4.0.4 - Handle force detach case. bug #1686745 + 4.0.5 - Set proper backend on subsequent operation, after group + failover. bug #1773069 """ - VERSION = "4.0.4" + VERSION = "4.0.5" # The name of the CI wiki page. CI_WIKI_NAME = "HPE_Storage_CI" @@ -162,7 +164,8 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase): * Create a VLUN for that HOST with the volume we want to export. """ - common = self._login() + array_id = self.get_volume_replication_driver_data(volume) + common = self._login(array_id=array_id) try: # we have to make sure we have a host host = self._create_host(common, volume, connector) @@ -207,7 +210,8 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase): @utils.trace def terminate_connection(self, volume, connector, **kwargs): """Driver entry point to unattach a volume from an instance.""" - common = self._login() + array_id = self.get_volume_replication_driver_data(volume) + common = self._login(array_id=array_id) try: is_force_detach = connector is None if is_force_detach: diff --git a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py index d18087e5d7e..df4ccd63b8a 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py +++ b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py @@ -123,6 +123,8 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase): 4.0.1 - Update CHAP on host record when volume is migrated to new compute host. bug # 1737181 4.0.2 - Handle force detach case. bug #1686745 + 4.0.3 - Set proper backend on subsequent operation, after group + failover. bug #1773069 """ @@ -232,7 +234,8 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase): * Create a host on the 3par * create vlun on the 3par """ - common = self._login() + array_id = self.get_volume_replication_driver_data(volume) + common = self._login(array_id=array_id) try: # If the volume has been failed over, we need to reinitialize # iSCSI ports so they represent the new array. @@ -363,7 +366,8 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase): @utils.trace def terminate_connection(self, volume, connector, **kwargs): """Driver entry point to unattach a volume from an instance.""" - common = self._login() + array_id = self.get_volume_replication_driver_data(volume) + common = self._login(array_id=array_id) try: is_force_detach = connector is None if is_force_detach: