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
|
return None
|
||||||
|
|
||||||
def rescan_hosts(self, hbas, connection_properties):
|
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']
|
target_lun = connection_properties['target_lun']
|
||||||
|
get_cts = self._get_hba_channel_scsi_target
|
||||||
|
|
||||||
for hba in hbas:
|
# Use initiator_target_map provided by backend as HBA exclussion map
|
||||||
# Try to get HBA channel and SCSI target to use as filters
|
ports = connection_properties.get('initiator_target_map')
|
||||||
cts = self._get_hba_channel_scsi_target(hba, connection_properties)
|
if ports:
|
||||||
# If we couldn't get the channel and target use wildcards
|
hbas = [hba for hba in hbas if hba['port_name'] in ports]
|
||||||
if not cts:
|
LOG.debug('Using initiator target map to exclude HBAs')
|
||||||
cts = [('-', '-')]
|
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:
|
for hba_channel, target_id in cts:
|
||||||
LOG.debug('Scanning host %(host)s (wwnn: %(wwnn)s, c: '
|
LOG.debug('Scanning host %(host)s (wwnn: %(wwnn)s, c: '
|
||||||
'%(channel)s, t: %(target)s, l: %(lun)s)',
|
'%(channel)s, t: %(target)s, l: %(lun)s)',
|
||||||
|
|
|
@ -137,10 +137,58 @@ class LinuxFCTestCase(base.TestCase):
|
||||||
shell=True)
|
shell=True)
|
||||||
self.assertIsNone(res)
|
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']]]
|
get_chan_results = [[['2', '3'], ['4', '5']], [['6', '7']]]
|
||||||
|
|
||||||
hbas, con_props = self.__get_rescan_info(zone_manager=True)
|
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',
|
with mock.patch.object(self.lfc, '_execute',
|
||||||
return_value=None) as execute_mock, \
|
return_value=None) as execute_mock, \
|
||||||
mock.patch.object(self.lfc, '_get_hba_channel_scsi_target',
|
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)
|
mock_get_chan.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
def test_rescan_hosts_wildcard(self):
|
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)
|
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',
|
with mock.patch.object(self.lfc, '_get_hba_channel_scsi_target',
|
||||||
side_effect=(None, [])), \
|
side_effect=(None, [])), \
|
||||||
mock.patch.object(self.lfc, '_execute',
|
mock.patch.object(self.lfc, '_execute',
|
||||||
|
|
Loading…
Reference in New Issue