From cae3ce60f47922450fa17130b4b91c9e78530168 Mon Sep 17 00:00:00 2001 From: Patrick East Date: Mon, 16 Jul 2018 10:31:52 -0700 Subject: [PATCH] FC Allow for multipath volumes with different LUNs We made assumptions in the fibre channel connector code that there was only ever a single lun per volume, even with many wwns per connections. There is need to support multiple luns per multipath device, similar to how the iSCSI volumes work. What we do is allow a list for 'target_luns' and 'target_wwns' in the connection properties, similar to how the iSCSI connector treats things like 'target_portals', 'target_luns', etc. we then group together 'targets' as combination of wwpns and the lun associated with them. This grouping is used to through the attach and detach workflow now to determine dev paths and scsi target information for rescans. All existing calls with 'target_lun' and 'target_wwn' will continue working as before, the new plural keys are optional. Change-Id: I393a028457a162228666d8497b695984fefdfab4 Closes-Bug: #1774293 (cherry picked from commit ba2569855d2f4a2a639205ed73cf20cdd3abda10) --- .../initiator/connectors/fibre_channel.py | 109 ++++++++--- os_brick/initiator/linuxfc.py | 66 +++---- .../connectors/test_fibre_channel.py | 185 +++++++++++++++++- os_brick/tests/initiator/test_linuxfc.py | 87 ++++++-- 4 files changed, 361 insertions(+), 86 deletions(-) diff --git a/os_brick/initiator/connectors/fibre_channel.py b/os_brick/initiator/connectors/fibre_channel.py index 849fb7e9a..b66ce47fd 100644 --- a/os_brick/initiator/connectors/fibre_channel.py +++ b/os_brick/initiator/connectors/fibre_channel.py @@ -20,6 +20,7 @@ from oslo_service import loopingcall import six from os_brick import exception +from os_brick.i18n import _ from os_brick import initiator from os_brick.initiator.connectors import base from os_brick.initiator import linuxfc @@ -70,12 +71,70 @@ class FibreChannelConnector(base.BaseLinuxConnector): """Where do we look for FC based volumes.""" return '/dev/disk/by-path' - def _get_possible_volume_paths(self, connection_properties, hbas): - ports = connection_properties['target_wwn'] - possible_devs = self._get_possible_devices(hbas, ports) + def _add_targets_to_connection_properties(self, connection_properties): + target_wwn = connection_properties.get('target_wwn') + target_wwns = connection_properties.get('target_wwns') + if target_wwns: + wwns = target_wwns + elif isinstance(target_wwn, list): + wwns = target_wwn + elif isinstance(target_wwn, six.string_types): + wwns = [target_wwn] + else: + wwns = [] - lun = connection_properties.get('target_lun', 0) - host_paths = self._get_host_devices(possible_devs, lun) + target_lun = connection_properties.get('target_lun', 0) + target_luns = connection_properties.get('target_luns') + if target_luns: + luns = target_luns + elif isinstance(target_lun, int): + luns = [target_lun] + else: + luns = [] + + if len(luns) == len(wwns): + # Handles single wwwn + lun or multiple, potentially + # different wwns or luns + targets = list(zip(wwns, luns)) + elif len(luns) == 1 and len(wwns) > 1: + # For the case of multiple wwns, but a single lun (old path) + targets = [] + for wwn in wwns: + targets.append((wwn, luns[0])) + else: + # Something is wrong, this shouldn't happen + msg = _("Unable to find potential volume paths for FC device " + "with lun: %(lun)s and wwn: %(wwn)s") % { + "lun": target_lun, "wwn": target_wwn} + LOG.error(msg) + raise exception.VolumePathsNotFound(msg) + + connection_properties['targets'] = targets + + wwpn_lun_map = dict() + for wwpn, lun in targets: + wwpn_lun_map[wwpn] = lun + + # If there is an initiator_target_map we can update it too + if 'initiator_target_map' in connection_properties: + itmap = connection_properties['initiator_target_map'] + new_itmap = dict() + for init_wwpn in itmap: + target_wwpns = itmap[init_wwpn] + init_targets = [] + for target_wwpn in target_wwpns: + if target_wwpn in wwpn_lun_map: + init_targets.append((target_wwpn, + wwpn_lun_map[target_wwpn])) + new_itmap[init_wwpn] = init_targets + connection_properties['initiator_target_lun_map'] = new_itmap + + return connection_properties + + def _get_possible_volume_paths(self, connection_properties, hbas): + targets = connection_properties['targets'] + possible_devs = self._get_possible_devices(hbas, targets) + host_paths = self._get_host_devices(possible_devs) return host_paths def get_volume_paths(self, connection_properties): @@ -100,6 +159,9 @@ class FibreChannelConnector(base.BaseLinuxConnector): Try and update the local kernel's size information for an FC volume. """ + connection_properties = self._add_targets_to_connection_properties( + connection_properties) + volume_paths = self.get_volume_paths(connection_properties) if volume_paths: return self._linuxscsi.extend_volume(volume_paths) @@ -126,6 +188,9 @@ class FibreChannelConnector(base.BaseLinuxConnector): LOG.debug("execute = %s", self._execute) device_info = {'type': 'block'} + connection_properties = self._add_targets_to_connection_properties( + connection_properties) + hbas = self._linuxfc.get_fc_hbas_info() host_devices = self._get_possible_volume_paths( connection_properties, hbas) @@ -194,9 +259,14 @@ class FibreChannelConnector(base.BaseLinuxConnector): LOG.debug("connect_volume returning %s", device_info) return device_info - def _get_host_devices(self, possible_devs, lun): + def _get_host_devices(self, possible_devs): + """Compute the device paths on the system with an id, wwn, and lun + + :param possible_devs: list of (pci_id, wwn, lun) tuples + :return: list of device paths on the system based on the possible_devs + """ host_devices = [] - for pci_num, target_wwn in possible_devs: + for pci_num, target_wwn, lun in possible_devs: host_device = "/dev/disk/by-path/pci-%s-fc-%s-lun-%s" % ( pci_num, target_wwn, @@ -204,14 +274,13 @@ class FibreChannelConnector(base.BaseLinuxConnector): host_devices.append(host_device) return host_devices - def _get_possible_devices(self, hbas, wwnports): + def _get_possible_devices(self, hbas, targets): """Compute the possible fibre channel device options. :param hbas: available hba devices. - :param wwnports: possible wwn addresses. Can either be string - or list of strings. + :param targets: tuple of possible wwn addresses and lun combinations. - :returns: list of (pci_id, wwn) tuples + :returns: list of (pci_id, wwn, lun) tuples Given one or more wwn (mac addresses for fibre channel) ports do the matrix math to figure out a set of pci device, wwn @@ -219,23 +288,13 @@ class FibreChannelConnector(base.BaseLinuxConnector): provides a search space for the device connection. """ - # the wwn (think mac addresses for fiber channel devices) can - # either be a single value or a list. Normalize it to a list - # for further operations. - wwns = [] - if isinstance(wwnports, list): - for wwn in wwnports: - wwns.append(str(wwn)) - elif isinstance(wwnports, six.string_types): - wwns.append(str(wwnports)) - raw_devices = [] for hba in hbas: pci_num = self._get_pci_num(hba) if pci_num is not None: - for wwn in wwns: + for wwn, lun in targets: target_wwn = "0x%s" % wwn.lower() - raw_devices.append((pci_num, target_wwn)) + raw_devices.append((pci_num, target_wwn, lun)) return raw_devices @utils.trace @@ -257,6 +316,10 @@ class FibreChannelConnector(base.BaseLinuxConnector): devices = [] wwn = None + + connection_properties = self._add_targets_to_connection_properties( + connection_properties) + volume_paths = self.get_volume_paths(connection_properties) mpath_path = None for path in volume_paths: diff --git a/os_brick/initiator/linuxfc.py b/os_brick/initiator/linuxfc.py index a9db91b28..4921e9d99 100644 --- a/os_brick/initiator/linuxfc.py +++ b/os_brick/initiator/linuxfc.py @@ -19,7 +19,6 @@ import os from oslo_concurrency import processutils as putils from oslo_log import log as logging -import six from os_brick.initiator import linuxscsi @@ -42,20 +41,18 @@ class LinuxFibreChannel(linuxscsi.LinuxSCSI): single WWNN for all ports, so caller should expect us to return either explicit channel and targets or wild cards if we cannot determine them. - :returns: List of lists with [c, t] entries, the channel and target + The connection properties will need to have "target" values defined in + it which are expected to be tuples of (wwpn, lun). + + :returns: List of lists with [c, t, l] entries, the channel and target may be '-' wildcards if unable to determine them. """ # We want the target's WWPNs, so we use the initiator_target_map if # present for this hba or default to target_wwns if not present. - wwpns = conn_props['target_wwn'] + targets = conn_props['targets'] if 'initiator_target_map' in conn_props: - wwpns = conn_props['initiator_target_map'].get(hba['port_name'], - wwpns) - - # If it's not a string then it's an iterable (most likely a list), - # so we need to create a BRE for the grep query. - if not isinstance(wwpns, six.string_types): - wwpns = '\|'.join(wwpns) + targets = conn_props['initiator_target_lun_map'].get( + hba['port_name'], targets) # Leave only the number from the host_device field (ie: host6) host_device = hba['host_device'] @@ -63,33 +60,36 @@ class LinuxFibreChannel(linuxscsi.LinuxSCSI): host_device = host_device[4:] path = '/sys/class/fc_transport/target%s:' % host_device - # Since we'll run the command in a shell ensure BRE are being used - cmd = 'grep -Gil "%(wwpns)s" %(path)s*/port_name' % {'wwpns': wwpns, - 'path': path} - try: - # We need to run command in shell to expand the * glob - out, _err = self._execute(cmd, shell=True) - return [line.split('/')[4].split(':')[1:] - for line in out.split('\n') if line.startswith(path)] - except Exception as exc: - LOG.debug('Could not get HBA channel and SCSI target ID, path: ' - '%(path)s*, reason: %(reason)s', {'path': path, - 'reason': exc}) - return [['-', '-']] + ctls = [] + for wwpn, lun in targets: + cmd = 'grep -Gil "%(wwpns)s" %(path)s*/port_name' % {'wwpns': wwpn, + 'path': path} + try: + # We need to run command in shell to expand the * glob + out, _err = self._execute(cmd, shell=True) + ctls += [line.split('/')[4].split(':')[1:] + [lun] + for line in out.split('\n') if line.startswith(path)] + except Exception as exc: + LOG.debug('Could not get HBA channel and SCSI target ID, path:' + ' %(path)s*, reason: %(reason)s', {'path': path, + 'reason': exc}) + # If we didn't find any paths just give back wildcards for + # the channel and target ids. + ctls.append(['-', '-', lun]) + return ctls def rescan_hosts(self, hbas, connection_properties): LOG.debug('Rescaning HBAs %(hbas)s with connection properties ' '%(conn_props)s', {'hbas': hbas, 'conn_props': connection_properties}) - target_lun = connection_properties['target_lun'] - get_cts = self._get_hba_channel_scsi_target + get_ctsl = self._get_hba_channel_scsi_target - # Use initiator_target_map provided by backend as HBA exclussion map - ports = connection_properties.get('initiator_target_map') + # Use initiator_target_map provided by backend as HBA exclusion map + ports = connection_properties.get('initiator_target_lun_map') if ports: hbas = [hba for hba in hbas if hba['port_name'] in ports] LOG.debug('Using initiator target map to exclude HBAs') - process = [(hba, get_cts(hba, connection_properties)) + process = [(hba, get_ctsl(hba, connection_properties)) for hba in hbas] # With no target map we'll check if target implements single WWNN for @@ -100,20 +100,20 @@ class LinuxFibreChannel(linuxscsi.LinuxSCSI): no_info = [] for hba in hbas: - cts = get_cts(hba, connection_properties) + ctls = get_ctsl(hba, connection_properties) found_info = True - for hba_channel, target_id in cts: + for hba_channel, target_id, target_lun in ctls: if hba_channel == '-' or target_id == '-': found_info = False target_list = with_info if found_info else no_info - target_list.append((hba, cts)) + target_list.append((hba, ctls)) process = with_info or no_info msg = "implements" if with_info else "doesn't implement" LOG.debug('FC target %s single WWNN for all ports.', msg) - for hba, cts in process: - for hba_channel, target_id in cts: + for hba, ctls in process: + for hba_channel, target_id, target_lun in ctls: LOG.debug('Scanning host %(host)s (wwnn: %(wwnn)s, c: ' '%(channel)s, t: %(target)s, l: %(lun)s)', {'host': hba['host_device'], diff --git a/os_brick/tests/initiator/connectors/test_fibre_channel.py b/os_brick/tests/initiator/connectors/test_fibre_channel.py index c8c0465e2..47f6b0557 100644 --- a/os_brick/tests/initiator/connectors/test_fibre_channel.py +++ b/os_brick/tests/initiator/connectors/test_fibre_channel.py @@ -11,6 +11,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + import ddt import mock import os @@ -67,13 +68,13 @@ class FibreChannelConnectorTestCase(test_connector.ConnectorTestCase): 'device_path': hbas[0]['ClassDevicePath']}] return info - def fibrechan_connection(self, volume, location, wwn): + def fibrechan_connection(self, volume, location, wwn, lun=1): return {'driver_volume_type': 'fibrechan', 'data': { 'volume_id': volume['id'], 'target_portal': location, 'target_wwn': wwn, - 'target_lun': 1, + 'target_lun': lun, }} @mock.patch.object(linuxfc.LinuxFibreChannel, 'get_fc_hbas') @@ -125,8 +126,10 @@ class FibreChannelConnectorTestCase(test_connector.ConnectorTestCase): location = '10.0.2.15:3260' wwn = '1234567890123456' connection_info = self.fibrechan_connection(vol, location, wwn) - volume_paths = self.connector.get_volume_paths( - connection_info['data']) + conn_data = self.connector._add_targets_to_connection_properties( + connection_info['data'] + ) + volume_paths = self.connector.get_volume_paths(conn_data) expected = ['/dev/disk/by-path/pci-0000:05:00.2' '-fc-0x1234567890123456-lun-1'] @@ -169,10 +172,15 @@ class FibreChannelConnectorTestCase(test_connector.ConnectorTestCase): name = 'volume-00000001' vol = {'id': 1, 'name': name} # Should work for string, unicode, and list - wwns = ['1234567890123456', six.text_type('1234567890123456'), - ['1234567890123456', '1234567890123457']] - for wwn in wwns: - connection_info = self.fibrechan_connection(vol, location, wwn) + wwns_luns = [ + ('1234567890123456', 1), + (six.text_type('1234567890123456'), 1), + (['1234567890123456', '1234567890123457'], 1), + (['1234567890123456', '1234567890123457'], 1), + ] + for wwn, lun in wwns_luns: + connection_info = self.fibrechan_connection(vol, location, + wwn, lun) dev_info = self.connector.connect_volume(connection_info['data']) exp_wwn = wwn[0] if isinstance(wwn, list) else wwn dev_str = ('/dev/disk/by-path/pci-0000:05:00.2-fc-0x%s-lun-1' % @@ -188,13 +196,13 @@ class FibreChannelConnectorTestCase(test_connector.ConnectorTestCase): # Should not work for anything other than string, unicode, and list connection_info = self.fibrechan_connection(vol, location, 123) - self.assertRaises(exception.NoFibreChannelHostsFound, + self.assertRaises(exception.VolumePathsNotFound, self.connector.connect_volume, connection_info['data']) get_fc_hbas_mock.side_effect = [[]] get_fc_hbas_info_mock.side_effect = [[]] - self.assertRaises(exception.NoFibreChannelHostsFound, + self.assertRaises(exception.VolumePathsNotFound, self.connector.connect_volume, connection_info['data']) @@ -470,3 +478,160 @@ class FibreChannelConnectorTestCase(test_connector.ConnectorTestCase): was_multipath) remove_mock.assert_called_once_with('/dev/sda', flush=flush_mock.return_value) + + @ddt.data( + { + "target_info": { + "target_lun": 1, + "target_wwn": '1234567890123456', + }, + "expected_targets": [ + ('1234567890123456', 1) + ] + }, + { + "target_info": { + "target_lun": 1, + "target_wwn": ['1234567890123456', '1234567890123457'], + }, + "expected_targets": [ + ('1234567890123456', 1), + ('1234567890123457', 1), + ] + }, + { + "target_info": { + "target_luns": [1, 1], + "target_wwn": ['1234567890123456', '1234567890123457'], + }, + "expected_targets": [ + ('1234567890123456', 1), + ('1234567890123457', 1), + ] + }, + { + "target_info": { + "target_luns": [1, 2], + "target_wwn": ['1234567890123456', '1234567890123457'], + }, + "expected_targets": [ + ('1234567890123456', 1), + ('1234567890123457', 2), + ] + }, + { + "target_info": { + "target_luns": [1, 1], + "target_wwns": ['1234567890123456', '1234567890123457'], + }, + "expected_targets": [ + ('1234567890123456', 1), + ('1234567890123457', 1), + ] + }, + { + "target_info": { + "target_lun": 7, + "target_luns": [1, 1], + "target_wwn": 'foo', + "target_wwns": ['1234567890123456', '1234567890123457'], + }, + "expected_targets": [ + ('1234567890123456', 1), + ('1234567890123457', 1), + ] + }, + # Add the zone map in now + { + "target_info": { + "target_lun": 1, + "target_wwn": '1234567890123456', + }, + "expected_targets": [ + ('1234567890123456', 1) + ], + "itmap": { + '0004567890123456': ['1234567890123456'] + }, + "expected_map": { + '0004567890123456': [('1234567890123456', 1)] + } + }, + { + "target_info": { + "target_lun": 1, + "target_wwn": ['1234567890123456', '1234567890123457'], + }, + "expected_targets": [ + ('1234567890123456', 1), + ('1234567890123457', 1), + ], + "itmap": { + '0004567890123456': ['1234567890123456', + '1234567890123457'] + }, + "expected_map": { + '0004567890123456': [('1234567890123456', 1), + ('1234567890123457', 1)] + } + }, + { + "target_info": { + "target_luns": [1, 2], + "target_wwn": ['1234567890123456', '1234567890123457'], + }, + "expected_targets": [ + ('1234567890123456', 1), + ('1234567890123457', 2), + ], + "itmap": { + '0004567890123456': ['1234567890123456'], + '1004567890123456': ['1234567890123457'], + }, + "expected_map": { + '0004567890123456': [('1234567890123456', 1)], + '1004567890123456': [('1234567890123457', 2)], + } + }, + { + "target_info": { + "target_luns": [1, 2], + "target_wwn": ['1234567890123456', '1234567890123457'], + }, + "expected_targets": [ + ('1234567890123456', 1), + ('1234567890123457', 2), + ], + "itmap": { + '0004567890123456': ['1234567890123456', + '1234567890123457'] + }, + "expected_map": { + '0004567890123456': [('1234567890123456', 1), + ('1234567890123457', 2)] + } + }, + + ) + @ddt.unpack + def test__add_targets_to_connection_properties(self, target_info, + expected_targets, + itmap=None, + expected_map=None): + volume = {'id': 'fake_uuid'} + wwn = '1234567890123456' + conn = self.fibrechan_connection(volume, "10.0.2.15:3260", wwn) + conn['data'].update(target_info) + + if itmap: + conn['data']['initiator_target_map'] = itmap + + connection_info = self.connector._add_targets_to_connection_properties( + conn['data']) + self.assertIn('targets', connection_info) + self.assertEqual(expected_targets, connection_info['targets']) + + if itmap: + self.assertIn('initiator_target_lun_map', connection_info) + self.assertEqual(expected_map, + connection_info['initiator_target_lun_map']) diff --git a/os_brick/tests/initiator/test_linuxfc.py b/os_brick/tests/initiator/test_linuxfc.py index dbe6414cc..3107594c0 100644 --- a/os_brick/tests/initiator/test_linuxfc.py +++ b/os_brick/tests/initiator/test_linuxfc.py @@ -50,9 +50,17 @@ class LinuxFCTestCase(base.TestCase): connection_properties = { 'initiator_target_map': {'50014380186af83c': ['514f0c50023f6c00'], '50014380186af83e': ['514f0c50023f6c01']}, + 'initiator_target_lun_map': { + '50014380186af83c': [('514f0c50023f6c00', 1)], + '50014380186af83e': [('514f0c50023f6c01', 1)] + }, 'target_discovered': False, 'target_lun': 1, - 'target_wwn': ['514f0c50023f6c00', '514f0c50023f6c01'] + 'target_wwn': ['514f0c50023f6c00', '514f0c50023f6c01'], + 'targets': [ + ('514f0c50023f6c00', 1), + ('514f0c50023f6c01', 1), + ] } hbas = [ @@ -69,6 +77,7 @@ class LinuxFCTestCase(base.TestCase): ] if not zone_manager: del connection_properties['initiator_target_map'] + del connection_properties['initiator_target_lun_map'] return hbas, connection_properties def test__get_hba_channel_scsi_target_single_wwpn(self): @@ -76,6 +85,7 @@ class LinuxFCTestCase(base.TestCase): '') hbas, con_props = self.__get_rescan_info() con_props['target_wwn'] = con_props['target_wwn'][0] + con_props['targets'] = con_props['targets'][0:1] with mock.patch.object(self.lfc, '_execute', return_value=execute_results) as execute_mock: res = self.lfc._get_hba_channel_scsi_target(hbas[0], con_props) @@ -83,22 +93,56 @@ class LinuxFCTestCase(base.TestCase): 'grep -Gil "514f0c50023f6c00" ' '/sys/class/fc_transport/target6:*/port_name', shell=True) - expected = [['0', '1']] + expected = [['0', '1', 1]] self.assertListEqual(expected, res) def test__get_hba_channel_scsi_target_multiple_wwpn(self): - execute_results = ('/sys/class/fc_transport/target6:0:1/port_name\n' - '/sys/class/fc_transport/target6:0:2/port_name\n', - '') + execute_results = [ + ['/sys/class/fc_transport/target6:0:1/port_name\n', ''], + ['/sys/class/fc_transport/target6:0:2/port_name\n', ''], + ] hbas, con_props = self.__get_rescan_info() with mock.patch.object(self.lfc, '_execute', - return_value=execute_results) as execute_mock: + side_effect=execute_results) as execute_mock: res = self.lfc._get_hba_channel_scsi_target(hbas[0], con_props) - execute_mock.assert_called_once_with( - 'grep -Gil "514f0c50023f6c00\|514f0c50023f6c01" ' - '/sys/class/fc_transport/target6:*/port_name', - shell=True) - expected = [['0', '1'], ['0', '2']] + expected_cmds = [ + mock.call('grep -Gil "514f0c50023f6c00" ' + '/sys/class/fc_transport/target6:*/port_name', + shell=True), + mock.call('grep -Gil "514f0c50023f6c01" ' + '/sys/class/fc_transport/target6:*/port_name', + shell=True), + ] + execute_mock.assert_has_calls(expected_cmds) + + expected = [['0', '1', 1], ['0', '2', 1]] + self.assertListEqual(expected, res) + + def test__get_hba_channel_scsi_target_multiple_wwpn_and_luns(self): + execute_results = [ + ['/sys/class/fc_transport/target6:0:1/port_name\n', ''], + ['/sys/class/fc_transport/target6:0:2/port_name\n', ''], + ] + hbas, con_props = self.__get_rescan_info() + con_props['target_lun'] = [1, 7] + con_props['targets'] = [ + ('514f0c50023f6c00', 1), + ('514f0c50023f6c01', 7), + ] + with mock.patch.object(self.lfc, '_execute', + side_effect=execute_results) as execute_mock: + res = self.lfc._get_hba_channel_scsi_target(hbas[0], con_props) + expected_cmds = [ + mock.call('grep -Gil "514f0c50023f6c00" ' + '/sys/class/fc_transport/target6:*/port_name', + shell=True), + mock.call('grep -Gil "514f0c50023f6c01" ' + '/sys/class/fc_transport/target6:*/port_name', + shell=True), + ] + execute_mock.assert_has_calls(expected_cmds) + + expected = [['0', '1', 1], ['0', '2', 7]] self.assertListEqual(expected, res) def test__get_hba_channel_scsi_target_zone_manager(self): @@ -112,7 +156,7 @@ class LinuxFCTestCase(base.TestCase): 'grep -Gil "514f0c50023f6c00" ' '/sys/class/fc_transport/target6:*/port_name', shell=True) - expected = [['0', '1']] + expected = [['0', '1', 1]] self.assertListEqual(expected, res) def test__get_hba_channel_scsi_target_not_found(self): @@ -135,11 +179,11 @@ class LinuxFCTestCase(base.TestCase): 'grep -Gil "514f0c50023f6c00" ' '/sys/class/fc_transport/target6:*/port_name', shell=True) - self.assertEqual([['-', '-']], res) + self.assertEqual(res, [['-', '-', 1]]) def test_rescan_hosts_initiator_map(self): """Test FC rescan with initiator map and not every HBA connected.""" - get_chan_results = [[['2', '3'], ['4', '5']], [['6', '7']]] + get_chan_results = [[['2', '3', 1], ['4', '5', 1]], [['6', '7', 1]]] hbas, con_props = self.__get_rescan_info(zone_manager=True) @@ -177,12 +221,14 @@ class LinuxFCTestCase(base.TestCase): def test_rescan_hosts_single_wwnn(self): """Test FC rescan with no initiator map and single WWNN for ports.""" - get_chan_results = [[['2', '3'], ['4', '5']], - [['6', '7']], [['-', '-']]] + get_chan_results = [ + [['2', '3', 1], ['4', '5', 1]], + [['6', '7', 1]], + [['-', '-', 1]], + ] + + hbas, con_props = self.__get_rescan_info(zone_manager=False) - hbas, con_props = self.__get_rescan_info(zone_manager=True) - # Remove the initiator map - con_props.pop('initiator_target_map') # This HBA is the one that is not included in the single WWNN. hbas.append({'device_path': ('/sys/devices/pci0000:00/0000:00:02.0/' '0000:04:00.2/host8/fc_host/host8'), @@ -216,10 +262,11 @@ class LinuxFCTestCase(base.TestCase): def test_rescan_hosts_wildcard(self): """Test when we don't have initiator map or target is single WWNN.""" - get_chan_results = [[['-', '-']], [['-', '-']]] + get_chan_results = [[['-', '-', 1]], [['-', '-', 1]]] hbas, con_props = self.__get_rescan_info(zone_manager=True) # Remove the initiator map con_props.pop('initiator_target_map') + con_props.pop('initiator_target_lun_map') with mock.patch.object(self.lfc, '_get_hba_channel_scsi_target', side_effect=get_chan_results), \ mock.patch.object(self.lfc, '_execute',