diff --git a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py index 7b568c97a52..8beb473b3f7 100644 --- a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py +++ b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py @@ -726,6 +726,7 @@ class HPE3PARBaseDriver(test.TestCase): configuration.filter_function = FILTER_FUNCTION configuration.image_volume_cache_enabled = False configuration.replication_device = None + configuration.hpe3par_target_nsp = None return configuration @mock.patch( @@ -6940,6 +6941,8 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() + mock_client.getStorageSystemInfo.return_value = ( + {'id': self.CLIENT_ID}) mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG} mock_client.getCPG.return_value = {} mock_client.getHost.side_effect = [ @@ -7004,6 +7007,8 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver): mock.call.getHostVLUNs(self.FAKE_HOST)] mock_client.assert_has_calls( + self.get_id_login + + self.standard_logout + self.standard_login + expected + self.standard_logout) @@ -7025,6 +7030,8 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver): return fake_map mock_lookup.return_value = fake_lookup_object() mock_client = self.setup_driver() + mock_client.getStorageSystemInfo.return_value = ( + {'id': self.CLIENT_ID}) mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG} mock_client.getCPG.return_value = {} mock_client.getHost.side_effect = [ @@ -7098,6 +7105,89 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver): mock.call.getHostVLUNs(self.FAKE_HOST)] mock_client.assert_has_calls( + self.get_id_login + + self.standard_logout + + self.standard_login + + expected + + self.standard_logout) + + self.assertDictEqual(expected_properties, result) + + def test_initialize_connection_single_path_target_nsp(self): + # setup_mock_client drive with default configuration + # and return the mock HTTP 3PAR client + mock_client = self.setup_driver() + self.driver.configuration.hpe3par_target_nsp = '2:1:2' + mock_client.getStorageSystemInfo.return_value = ( + {'id': self.CLIENT_ID}) + mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG} + mock_client.getCPG.return_value = {} + mock_client.getHost.side_effect = [ + hpeexceptions.HTTPNotFound('fake'), + {'name': self.FAKE_HOST, + 'FCPaths': [{'driverVersion': None, + 'firmwareVersion': None, + 'hostSpeed': 0, + 'model': None, + 'vendor': None, + 'wwn': self.wwn[0]}]}] + mock_client.queryHost.return_value = { + 'members': [{ + 'name': self.FAKE_HOST + }] + } + + mock_client.getHostVLUNs.side_effect = [ + hpeexceptions.HTTPNotFound('fake'), + [{'active': True, + 'volumeName': self.VOLUME_3PAR_NAME, + 'portPos': {'node': 7, 'slot': 1, 'cardPort': 1}, + 'remoteName': self.wwn[0], + 'lun': 90, 'type': 0}]] + + location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" % + {'volume_name': self.VOLUME_3PAR_NAME, + 'lun_id': 90, + 'host': self.FAKE_HOST, + 'nsp': 'something'}) + mock_client.createVLUN.return_value = location + user_target_wwn = '0987654321234' + expected_properties = { + 'driver_volume_type': 'fibre_channel', + 'data': { + 'encrypted': False, + 'target_lun': 90, + 'target_wwn': [user_target_wwn], + 'target_discovered': True, + 'initiator_target_map': + {'123456789012345': [user_target_wwn]}}} + + with mock.patch.object(hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + result = self.driver.initialize_connection( + self.volume, + self.connector.copy()) + + expected = [ + mock.call.getVolume(self.VOLUME_3PAR_NAME), + mock.call.getCPG(HPE3PAR_CPG), + mock.call.getHost(self.FAKE_HOST), + mock.call.queryHost(wwns=['123456789012345']), + mock.call.getHost(self.FAKE_HOST), + mock.call.getPorts(), + mock.call.getPorts(), + mock.call.getHostVLUNs(self.FAKE_HOST), + mock.call.createVLUN( + self.VOLUME_3PAR_NAME, + auto=True, + hostname=self.FAKE_HOST, + lun=None), + mock.call.getHostVLUNs(self.FAKE_HOST)] + + mock_client.assert_has_calls( + self.get_id_login + + self.standard_logout + self.standard_login + expected + self.standard_logout) diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index fb96b591b63..e3e3d397de3 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -118,6 +118,14 @@ hpe3par_opts = [ cfg.BoolOpt('hpe3par_iscsi_chap_enabled', default=False, help="Enable CHAP authentication for iSCSI connections."), + cfg.StrOpt('hpe3par_target_nsp', + default="", + help="The nsp of 3PAR backend to be used when: " + "(1) multipath is not enabled in cinder.conf. " + "(2) Fiber Channel Zone Manager is not used. " + "(3) the 3PAR backend is prezoned with this " + "specific nsp only. For example if nsp is 2 1 2, the " + "format of the option's value is 2:1:2"), ] diff --git a/cinder/volume/drivers/hpe/hpe_3par_fc.py b/cinder/volume/drivers/hpe/hpe_3par_fc.py index cbf3c35b64b..4cae80cc0fa 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_fc.py +++ b/cinder/volume/drivers/hpe/hpe_3par_fc.py @@ -15,8 +15,8 @@ # License for the specific language governing permissions and limitations # under the License. # -""" -Volume driver for HPE 3PAR Storage array. +"""Volume driver for HPE 3PAR Storage array. + This driver requires 3.1.3 or later firmware on the 3PAR array, using the 4.x version of the hpe3parclient. @@ -112,6 +112,7 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase): 4.0.4 - Handle force detach case. bug #1686745 4.0.5 - Set proper backend on subsequent operation, after group failover. bug #1773069 + 4.0.6 - Set NSP for single path attachments. Bug #1809249 """ @@ -171,12 +172,22 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase): try: # we have to make sure we have a host host = self._create_host(common, volume, connector) - target_wwns, init_targ_map, numPaths = \ - self._build_initiator_target_map(common, connector) - if not connector.get('multipath'): - target_wwns = target_wwns[:1] + target_wwns, init_targ_map, numPaths = ( + self._build_initiator_target_map(common, connector)) + + multipath = connector.get('multipath') + LOG.debug("multipath: %s", multipath) + user_target = None + if not multipath: + user_target = self._get_user_target(common) initiator = connector.get('wwpns')[0] - init_targ_map[initiator] = init_targ_map[initiator][:1] + if user_target is None: + target_wwns = target_wwns[:1] + init_targ_map[initiator] = init_targ_map[initiator][:1] + else: + target_wwns = [user_target] + init_targ_map[initiator] = [user_target] + # check if a VLUN already exists for this host existing_vlun = common.find_existing_vlun(volume, host) @@ -411,3 +422,25 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase): self._modify_3par_fibrechan_host(common, host['name'], new_wwns) host = common._get_3par_host(host['name']) return host + + def _get_user_target(self, common): + target_nsp = common.config.hpe3par_target_nsp + + if not target_nsp: + return None + + # Get target wwn from target nsp + fc_ports = common.get_active_fc_target_ports() + + target_wwn = None + for port in fc_ports: + nsp = port['nsp'] + if target_nsp == nsp: + target_wwn = port['portWWN'] + break + + if not target_wwn: + LOG.warning("Did not get wwn for target nsp: " + "%(nsp)s", {'nsp': target_nsp}) + + return target_wwn diff --git a/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst b/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst index bb0a315b169..b8480f8ac4b 100644 --- a/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst +++ b/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst @@ -413,3 +413,51 @@ the HPE 3PAR Fibre Channel and iSCSI drivers. :config-target: 3PAR cinder.volume.drivers.hpe.hpe_3par_common + + +Specify NSP for FC Bootable Volume +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Given a system connected to HPE 3PAR via FC and multipath setting is +NOT used in cinder.conf. When the user tries to create a bootable +volume, it fails intermittently with the following error: +Fibre Channel volume device not found + +This happens when a zone is created using second or later target from +3PAR backend. In this case, HPE 3PAR client code picks up first target +to form initiator target map. This can be illustrated with below +example. + +Sample output of showport command: + +``$ showport -sortcol 6`` + +.. code-block:: console + + N:S:P Mode State ----Node_WWN---- -Port_WWN/HW_Addr- Type Protocol Partner FailoverState + 0:1:1 target ready 2FF70002AC002DB6 20110002AC002DB6 host FC - - + 0:1:2 target ready 2FF70002AC002DB6 20120002AC002DB6 host FC 1:1:2 none + 1:1:1 initiator ready 2FF70002AC002DB6 21110002AC002DB6 rcfc FC - - + 1:1:2 target ready 2FF70002AC002DB6 21120002AC002DB6 host FC 0:1:2 none + 2:1:1 initiator ready 2FF70002AC002DB6 22110002AC002DB6 rcfc FC - - + 2:1:2 target ready 2FF70002AC002DB6 22120002AC002DB6 host FC 3:1:2 none + 3:1:1 target ready 2FF70002AC002DB6 23110002AC002DB6 host FC - - + 3:1:2 target ready 2FF70002AC002DB6 23120002AC002DB6 host FC 2:1:2 none + +Suppose zone is created using targets "2:1:2" and "3:1:2" from above +output. Then initiator target map is created using target "0:1:1" only. +In such a case, the path is not found, and bootable volume creation fails. + +To avoid above mentioned failure, the user can specify the target in 3PAR +backend section of cinder.conf as follows: + +``hpe3par_target_nsp = 3:1:2`` + +Using above mentioned nsp, respective wwn information is fetched. +Later initiator target map is created using wwn information and +bootable volume is created successfully. + +Note: If above mentioned option (nsp) is not specified in cinder.conf, +then the original flow is executed i.e first target is picked and +bootable volume creation may fail. + diff --git a/releasenotes/notes/hpe-3par-specify-nsp-for-fc-bootable-volume-f372879e1b625b4d.yaml b/releasenotes/notes/hpe-3par-specify-nsp-for-fc-bootable-volume-f372879e1b625b4d.yaml new file mode 100644 index 00000000000..3cff39eaee0 --- /dev/null +++ b/releasenotes/notes/hpe-3par-specify-nsp-for-fc-bootable-volume-f372879e1b625b4d.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + `Bug 1809249 `_ - + 3PAR driver adds the config option `hpe3par_target_nsp` that can be + set to the 3PAR backend to use when multipath is not enabled and + the Fibre Channel Zone Manager is not used.