Fix FC: Only scan connected HBAs
Current OS-Brick FC code always scans all present HBAs, which could
unintentionally add unwanted devices, for example in the following
environment:
+-------+ +------+ +-----------------+
| host5 +-----+ +---+ Port.A VNX |
| | | FCSW +---+ Port.B |
| host6 +-----+ | +-----------------+
| | +------+
| | +------+
| host7 +-----+ | +-----------------+
| | | FCSW +---+ Port.C XtremIO |
| host8 +-----+ +---+ Port.D |
+-------+ +------+ +-----------------+
This patch limits what HBAs get scanned:
- If we have an initiator map, we only scan on the HBAs that are there
- If we are in the single WWNN for all ports case we only scan HBAs that
are connected
- If we can't do any better we scan all HBAs with wildcards
Closes-Bug: #1765000
Change-Id: I3ba8f9683211d550727a97fc455175f2b0482886
(cherry picked from commit db40d98044
)
This commit is contained in:
parent
773fad170f
commit
8fab1a01cb
|
@ -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
|
||||
|
||||
# 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:
|
||||
# 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 = [('-', '-')]
|
||||
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)',
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue