Merge "FC Allow for multipath volumes with different LUNs"
This commit is contained in:
commit
641337bec2
@ -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:
|
||||
|
@ -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'],
|
||||
|
@ -11,6 +11,8 @@
|
||||
# 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
|
||||
import six
|
||||
@ -23,6 +25,7 @@ from os_brick.initiator import linuxscsi
|
||||
from os_brick.tests.initiator import test_connector
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class FibreChannelConnectorTestCase(test_connector.ConnectorTestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -65,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')
|
||||
@ -123,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']
|
||||
@ -167,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' %
|
||||
@ -186,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'])
|
||||
|
||||
@ -450,3 +460,160 @@ class FibreChannelConnectorTestCase(test_connector.ConnectorTestCase):
|
||||
find_mp_dev_mock,
|
||||
'rw',
|
||||
True)
|
||||
|
||||
@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'])
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user