diff --git a/os_brick/initiator/linuxfc.py b/os_brick/initiator/linuxfc.py index 8f1fb2d4c..89ae65275 100644 --- a/os_brick/initiator/linuxfc.py +++ b/os_brick/initiator/linuxfc.py @@ -77,14 +77,38 @@ class LinuxFibreChannel(linuxscsi.LinuxSCSI): return None 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 - for hba in hbas: - # Try to get HBA channel and SCSI target to use as filters - cts = self._get_hba_channel_scsi_target(hba, connection_properties) - # If we couldn't get the channel and target use wildcards - if not cts: - cts = [('-', '-')] + # Use initiator_target_map provided by backend as HBA exclussion map + ports = connection_properties.get('initiator_target_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)) + for hba in hbas] + + # With no target map we'll check if target implements single WWNN for + # all ports, if it does we only use HBAs connected (info was found), + # otherwise we are forced to blindly scan all HBAs. + else: + with_info = [] + no_info = [] + + for hba in hbas: + cts = get_cts(hba, connection_properties) + target_list = with_info if cts else no_info + cts = cts or [('-', '-')] + target_list.append((hba, cts)) + + 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: LOG.debug('Scanning host %(host)s (wwnn: %(wwnn)s, c: ' '%(channel)s, t: %(target)s, l: %(lun)s)', diff --git a/os_brick/tests/initiator/test_linuxfc.py b/os_brick/tests/initiator/test_linuxfc.py index a5ccc5203..38b93d85f 100644 --- a/os_brick/tests/initiator/test_linuxfc.py +++ b/os_brick/tests/initiator/test_linuxfc.py @@ -137,10 +137,58 @@ class LinuxFCTestCase(base.TestCase): shell=True) self.assertIsNone(res) - def test_rescan_hosts(self): + 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']]] hbas, con_props = self.__get_rescan_info(zone_manager=True) + + # This HBA is not in the initiator map, so we should not scan it or try + # to get the channel and target + hbas.append({'device_path': ('/sys/devices/pci0000:00/0000:00:02.0/' + '0000:04:00.2/host8/fc_host/host8'), + 'host_device': 'host8', + 'node_name': '50014380186af83g', + 'port_name': '50014380186af83h'}) + + with mock.patch.object(self.lfc, '_execute', + return_value=None) as execute_mock, \ + mock.patch.object(self.lfc, '_get_hba_channel_scsi_target', + side_effect=get_chan_results) as mock_get_chan: + + self.lfc.rescan_hosts(hbas, con_props) + expected_commands = [ + mock.call('tee', '-a', '/sys/class/scsi_host/host6/scan', + process_input='2 3 1', + root_helper=None, run_as_root=True), + mock.call('tee', '-a', '/sys/class/scsi_host/host6/scan', + process_input='4 5 1', + root_helper=None, run_as_root=True), + mock.call('tee', '-a', '/sys/class/scsi_host/host7/scan', + process_input='6 7 1', + root_helper=None, run_as_root=True)] + + execute_mock.assert_has_calls(expected_commands) + self.assertEqual(len(expected_commands), execute_mock.call_count) + + expected_calls = [mock.call(hbas[0], con_props), + mock.call(hbas[1], con_props)] + mock_get_chan.assert_has_calls(expected_calls) + + 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']], None] + + 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'), + 'host_device': 'host8', + 'node_name': '50014380186af83g', + 'port_name': '50014380186af83h'}) + with mock.patch.object(self.lfc, '_execute', return_value=None) as execute_mock, \ mock.patch.object(self.lfc, '_get_hba_channel_scsi_target', @@ -166,7 +214,10 @@ class LinuxFCTestCase(base.TestCase): mock_get_chan.assert_has_calls(expected_calls) def test_rescan_hosts_wildcard(self): + """Test when we don't have initiator map or target is single WWNN.""" hbas, con_props = self.__get_rescan_info(zone_manager=True) + # Remove the initiator map + con_props.pop('initiator_target_map') with mock.patch.object(self.lfc, '_get_hba_channel_scsi_target', side_effect=(None, [])), \ mock.patch.object(self.lfc, '_execute',