Merge "Add white list support for target ports in VNX driver"

This commit is contained in:
Jenkins 2015-07-19 15:38:06 +00:00 committed by Gerrit Code Review
commit 8cd069e8d5
4 changed files with 513 additions and 45 deletions

View File

@ -738,7 +738,50 @@ State: Ready
"SP: A\n" +
"Port ID: 5\n" +
"Port WWN: iqn.1992-04.com.emc:cx.fnm00124000215.a5\n" +
"iSCSI Alias: 0215.a5\n", 0)
"iSCSI Alias: 0215.a5\n" +
"SP: A\n" +
"Port ID: 0\n" +
"Port WWN: iqn.1992-04.com.emc:cx.fnm00124000215.a0\n" +
"iSCSI Alias: 0215.a0\n\n" +
"Virtual Port ID: 0\n" +
"VLAN ID: Disabled\n" +
"IP Address: 10.244.214.119\n\n" +
"SP: B\n" +
"Port ID: 2\n" +
"Port WWN: iqn.1992-04.com.emc:cx.fnm00124000215.b2\n" +
"iSCSI Alias: 0215.b2\n\n" +
"Virtual Port ID: 0\n" +
"VLAN ID: Disabled\n" +
"IP Address: 10.244.214.120\n\n", 0)
WHITE_LIST_PORTS = ("""SP: A
Port ID: 0
Port WWN: iqn.1992-04.com.emc:cx.fnmxxx.a0
iSCSI Alias: 0235.a7
Virtual Port ID: 0
VLAN ID: Disabled
IP Address: 192.168.3.52
SP: A
Port ID: 9
Port WWN: iqn.1992-04.com.emc:cx.fnmxxx.a9
iSCSI Alias: 0235.a9
SP: A
Port ID: 4
Port WWN: iqn.1992-04.com.emc:cx.fnmxxx.a4
iSCSI Alias: 0235.a4
SP: B
Port ID: 2
Port WWN: iqn.1992-04.com.emc:cx.fnmxxx.b2
iSCSI Alias: 0235.b6
Virtual Port ID: 0
VLAN ID: Disabled
IP Address: 192.168.4.53
""", 0)
iscsi_connection_info = \
{'data': {'target_discovered': True,
@ -793,7 +836,18 @@ State: Ready
"50:06:01:62:08:60:01:95\n" +
"Link Status: Down\n" +
"Port Status: Online\n" +
"Switch Present: NO\n", 0)
"Switch Present: NO\n" +
"\n" +
"SP Name: SP B\n" +
"SP Port ID: 2\n" +
"SP UID: 50:06:01:60:88:60:08:0F:"
"50:06:01:6A:08:60:08:0F\n" +
"Link Status: Up\n" +
"Port Status: Online\n" +
"Switch Present: YES\n" +
"Switch UID: 10:00:50:EB:1A:03:3F:59:"
"20:11:50:EB:1A:03:3F:59\n" +
"SP Source ID: 69888\n", 0)
FAKEHOST_PORTS = (
"Information about each HBA:\n" +
@ -822,6 +876,14 @@ State: Ready
" Defined: YES\n" +
" Initiator Type: 3\n" +
" StorageGroup Name: fakehost\n\n" +
" SP Name: SP B\n" +
" SP Port ID: 2\n" +
" HBA Devicename:\n" +
" Trusted: NO\n" +
" Logged In: YES\n" +
" Defined: YES\n" +
" Initiator Type: 3\n" +
" StorageGroup Name: fakehost\n\n"
"Information about each SPPORT:\n" +
"\n" +
"SP Name: SP A\n" +
@ -849,7 +911,18 @@ State: Ready
"50:06:01:62:08:60:01:95\n" +
"Link Status: Down\n" +
"Port Status: Online\n" +
"Switch Present: NO\n", 0)
"Switch Present: NO\n" +
"\n" +
"SP Name: SP B\n" +
"SP Port ID: 2\n" +
"SP UID: 50:06:01:60:88:60:01:95:" +
"50:06:01:6A:08:60:08:0F\n" +
"Link Status: Up\n" +
"Port Status: Online\n" +
"Switch Present: YES\n" +
"Switch UID: 10:00:00:05:1E:72:EC:A6:" +
"20:46:00:05:1E:72:EC:A6\n" +
"SP Source ID: 272896\n", 0)
def LUN_PROPERTY(self, name, is_thin=False, has_snap=False, size=1,
state='Ready', faulted='false', operation='None',
@ -886,6 +959,26 @@ State: Ready
'operation': operation,
'is_thin': 'Yes' if is_thin else 'No'}, 0)
def STORAGE_GROUP_ISCSI_FC_HBA(self, sgname):
return ("""\
Storage Group Name: %s
Storage Group UID: 54:46:57:0F:15:A2:E3:11:9A:8D:FF:E5:3A:03:FD:6D
HBA/SP Pairs:
HBA UID SP Name SPPort
------- ------- ------
iqn.1993-08.org.debian:01:222 SP A 4
22:34:56:78:90:12:34:56:12:34:56:78:90:12:34:56 SP B 2
22:34:56:78:90:54:32:16:12:34:56:78:90:54:32:16 SP B 2
HLU/ALU Pairs:
HLU Number ALU Number
---------- ----------
1 1
Shareable: YES""" % sgname, 0)
def STORAGE_GROUP_NO_MAP(self, sgname):
return ("""\
Storage Group Name: %s
@ -910,6 +1003,26 @@ State: Ready
1 1
Shareable: YES""" % sgname, 0)
def STORAGE_GROUP_HAS_MAP_ISCSI(self, sgname):
return ("""\
Storage Group Name: %s
Storage Group UID: 54:46:57:0F:15:A2:E3:11:9A:8D:FF:E5:3A:03:FD:6D
HBA/SP Pairs:
HBA UID SP Name SPPort
------- ------- ------
iqn.1993-08.org.debian:01:222 SP A 2
iqn.1993-08.org.debian:01:222 SP A 0
iqn.1993-08.org.debian:01:222 SP B 2
HLU/ALU Pairs:
HLU Number ALU Number
---------- ----------
1 1
Shareable: YES""" % sgname, 0)
def STORAGE_GROUP_HAS_MAP_MP(self, sgname):
return ("""\
@ -1100,12 +1213,15 @@ class DriverTestCaseBase(test.TestCase):
return standard_default
def fake_command_execute_for_driver_setup(self, *command, **kwargv):
if command == ('connection', '-getport', '-address', '-vlanid'):
if (command == ('connection', '-getport', '-address', '-vlanid') or
command == ('connection', '-getport', '-vlanid')):
return self.testData.ALL_PORTS
elif command == ('storagepool', '-list', '-state'):
return self.testData.POOL_GET_STATE_RESULT([
{'pool_name': self.testData.test_pool_name, 'state': "Ready"},
{'pool_name': "unit_test_pool2", 'state': "Ready"}])
if command == self.testData.GETFCPORT_CMD():
return self.testData.FC_PORTS
else:
return SUCCEED
@ -1754,6 +1870,14 @@ Time Remaining: 0 second(s)
'-hbauid', 'iqn.1993-08.org.debian:01:222',
'-sp', 'A', '-spport', 4, '-spvport', 0,
'-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost', '-setpath',
'-hbauid', 'iqn.1993-08.org.debian:01:222',
'-sp', 'A', '-spport', 0, '-spvport', 0,
'-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost', '-setpath',
'-hbauid', 'iqn.1993-08.org.debian:01:222',
'-sp', 'B', '-spport', 2, '-spvport', 0,
'-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
mock.call('storagegroup', '-list', '-gname', 'fakehost',
poll=True),
mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
@ -1921,6 +2045,74 @@ Time Remaining: 0 second(s)
'10.0.0.2'))]
fake_cli.assert_has_calls(expected)
@mock.patch('random.randint',
mock.Mock(return_value=0))
def test_initialize_connection_iscsi_white_list(self):
self.configuration.io_port_list = 'a-0-0,B-2-0'
test_volume = self.testData.test_volume.copy()
test_volume['provider_location'] = 'system^fakesn|type^lun|id^1'
# Test for auto registration
self.configuration.initiator_auto_registration = True
commands = [('storagegroup', '-list', '-gname', 'fakehost')]
results = [[("No group", 83),
self.testData.STORAGE_GROUP_HAS_MAP_ISCSI('fakehost')]]
fake_cli = self.driverSetup(commands, results)
self.driver.cli.iscsi_targets = {'A': [{'SP': 'A', 'Port ID': 0,
'Virtual Port ID': 0,
'Port WWN': 'fake_iqn',
'IP Address': '192.168.1.1'}],
'B': [{'SP': 'B', 'Port ID': 2,
'Virtual Port ID': 0,
'Port WWN': 'fake_iqn1',
'IP Address': '192.168.1.2'}]}
self.driver.initialize_connection(
test_volume,
self.testData.connector)
expected = [mock.call('storagegroup', '-list', '-gname', 'fakehost',
poll=False),
mock.call('storagegroup', '-create', '-gname', 'fakehost'),
mock.call('storagegroup', '-gname', 'fakehost', '-setpath',
'-hbauid', 'iqn.1993-08.org.debian:01:222',
'-sp', 'A', '-spport', 0, '-spvport', 0,
'-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost', '-setpath',
'-hbauid', 'iqn.1993-08.org.debian:01:222',
'-sp', 'B', '-spport', 2, '-spvport', 0,
'-ip', '10.0.0.2', '-host', 'fakehost', '-o'),
mock.call('storagegroup', '-list', '-gname', 'fakehost',
poll=True),
mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
'-gname', 'fakehost', '-o',
poll=False),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol1'),
poll=False)]
fake_cli.assert_has_calls(expected)
@mock.patch('cinder.volume.drivers.emc.emc_vnx_cli.'
'EMCVnxCliBase._build_pool_stats',
mock.Mock(return_value=None))
@mock.patch('cinder.volume.drivers.emc.emc_vnx_cli.'
'CommandLineHelper.get_pool',
mock.Mock(return_value={'total_capacity_gb': 0.0,
'free_capacity_gb': 0.0}))
def test_update_iscsi_io_ports(self):
self.configuration.io_port_list = 'a-0-0,B-2-0'
# Test for auto registration
self.configuration.initiator_auto_registration = True
commands = [self.testData.GETPORT_CMD()]
results = [self.testData.WHITE_LIST_PORTS]
fake_cli = self.driverSetup(commands, results)
self.driver.cli.update_volume_stats()
expected = [mock.call(*self.testData.GETPORT_CMD(), poll=False)]
fake_cli.assert_has_calls(expected)
io_ports = self.driver.cli.iscsi_targets
self.assertEqual((0, 'iqn.1992-04.com.emc:cx.fnmxxx.a0'),
(io_ports['A'][0]['Port ID'],
io_ports['A'][0]['Port WWN']))
self.assertEqual((2, 'iqn.1992-04.com.emc:cx.fnmxxx.b2'),
(io_ports['B'][0]['Port ID'],
io_ports['B'][0]['Port WWN']))
@mock.patch(
"oslo_concurrency.processutils.execute",
mock.Mock(
@ -3959,12 +4151,24 @@ class EMCVNXCLIDriverFCTestCase(DriverTestCaseBase):
'90:12:34:56',
'-sp', 'A', '-spport', '0', '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost',
'-setpath', '-hbauid',
'22:34:56:78:90:12:34:56:12:34:56:78:'
'90:12:34:56',
'-sp', 'B', '-spport', '2', '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost',
'-setpath', '-hbauid',
'22:34:56:78:90:54:32:16:12:34:56:78:'
'90:54:32:16',
'-sp', 'A', '-spport', '0', '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost',
'-setpath', '-hbauid',
'22:34:56:78:90:54:32:16:12:34:56:78:'
'90:54:32:16',
'-sp', 'B', '-spport', '2', '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-list', '-gname', 'fakehost',
poll=True),
mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
@ -4049,6 +4253,106 @@ class EMCVNXCLIDriverFCTestCase(DriverTestCaseBase):
mock.call('port', '-list', '-sp')]
fake_cli.assert_has_calls(expected)
@mock.patch('random.randint',
mock.Mock(return_value=0))
def test_initialize_connection_fc_white_list(self):
self.configuration.io_port_list = 'a-0,B-2'
test_volume = self.testData.test_volume.copy()
test_volume['provider_location'] = 'system^fakesn|type^lun|id^1'
self.configuration.initiator_auto_registration = True
commands = [('storagegroup', '-list', '-gname', 'fakehost'),
self.testData.GETFCPORT_CMD(),
('port', '-list', '-gname', 'fakehost')]
results = [[("No group", 83),
self.testData.STORAGE_GROUP_HAS_MAP_ISCSI('fakehost')],
self.testData.FC_PORTS,
self.testData.FAKEHOST_PORTS]
fake_cli = self.driverSetup(commands, results)
data = self.driver.initialize_connection(
test_volume,
self.testData.connector)
expected = [mock.call('storagegroup', '-list', '-gname', 'fakehost',
poll=False),
mock.call('storagegroup', '-create', '-gname', 'fakehost'),
mock.call('storagegroup', '-gname', 'fakehost',
'-setpath', '-hbauid',
'22:34:56:78:90:12:34:56:'
'12:34:56:78:90:12:34:56',
'-sp', 'A', '-spport', 0, '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost',
'-setpath', '-hbauid',
'22:34:56:78:90:12:34:56:'
'12:34:56:78:90:12:34:56',
'-sp', 'B', '-spport', 2, '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost',
'-setpath', '-hbauid',
'22:34:56:78:90:54:32:16:'
'12:34:56:78:90:54:32:16',
'-sp', 'A', '-spport', 0, '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost',
'-setpath', '-hbauid',
'22:34:56:78:90:54:32:16:'
'12:34:56:78:90:54:32:16',
'-sp', 'B', '-spport', 2, '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-list', '-gname', 'fakehost',
poll=True),
mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
'-gname', 'fakehost', '-o',
poll=False),
mock.call('port', '-list', '-gname', 'fakehost')]
fake_cli.assert_has_calls(expected)
self.assertEqual(['5006016A0860080F', '5006016008600195'],
data['data']['target_wwn'])
@mock.patch('random.randint',
mock.Mock(return_value=0))
def test_initialize_connection_fc_port_registered_wl(self):
self.configuration.io_port_list = 'a-0,B-2'
test_volume = self.testData.test_volume.copy()
test_volume['provider_location'] = 'system^fakesn|type^lun|id^1'
self.configuration.initiator_auto_registration = True
commands = [('storagegroup', '-list', '-gname', 'fakehost'),
self.testData.GETFCPORT_CMD(),
('port', '-list', '-gname', 'fakehost')]
results = [self.testData.STORAGE_GROUP_ISCSI_FC_HBA('fakehost'),
self.testData.FC_PORTS,
self.testData.FAKEHOST_PORTS]
fake_cli = self.driverSetup(commands, results)
data = self.driver.initialize_connection(
test_volume,
self.testData.connector)
expected = [mock.call('storagegroup', '-list', '-gname', 'fakehost',
poll=False),
mock.call('storagegroup', '-gname', 'fakehost',
'-setpath', '-hbauid',
'22:34:56:78:90:12:34:56:'
'12:34:56:78:90:12:34:56',
'-sp', 'A', '-spport', 0, '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-gname', 'fakehost',
'-setpath', '-hbauid',
'22:34:56:78:90:54:32:16:'
'12:34:56:78:90:54:32:16',
'-sp', 'A', '-spport', 0, '-ip', '10.0.0.2',
'-host', 'fakehost', '-o'),
mock.call('storagegroup', '-list', '-gname', 'fakehost',
poll=True),
mock.call('storagegroup', '-addhlu', '-hlu', 2, '-alu', 1,
'-gname', 'fakehost', '-o',
poll=False),
mock.call('port', '-list', '-gname', 'fakehost')]
fake_cli.assert_has_calls(expected)
self.assertEqual(['5006016A0860080F', '5006016008600195'],
data['data']['target_wwn'])
@mock.patch(
"cinder.zonemanager.fc_san_lookup_service.FCSanLookupService." +
"get_device_mapping_from_network",

View File

@ -58,6 +58,7 @@ class EMCCLIFCDriver(driver.FibreChannelDriver):
Create consistency group from cgsnapshot support
Multiple pools support enhancement
Manage/unmanage volume revise
White list target ports support
"""
def __init__(self, *args, **kwargs):

View File

@ -56,6 +56,7 @@ class EMCCLIISCSIDriver(driver.ISCSIDriver):
Create consistency group from cgsnapshot support
Multiple pools support enhancement
Manage/unmanage volume revise
White list target ports support
"""
def __init__(self, *args, **kwargs):

View File

@ -99,6 +99,10 @@ loc_opts = [
default='',
help='Mapping between hostname and '
'its iSCSI initiator IP addresses.'),
cfg.StrOpt('io_port_list',
default='*',
help='Comma separated iSCSI or FC ports '
'to be used in Nova or Cinder.'),
cfg.BoolOpt('initiator_auto_registration',
default=False,
help='Automatically register initiators. '
@ -1367,7 +1371,8 @@ class CommandLineHelper(object):
return data
def get_status_up_ports(self, storage_group_name, poll=True):
def get_status_up_ports(self, storage_group_name, io_ports=None,
poll=True):
"""Function to get ports whose status are up."""
cmd_get_hba = ('storagegroup', '-list', '-gname', storage_group_name)
out, rc = self.command_execute(*cmd_get_hba, poll=poll)
@ -1383,6 +1388,9 @@ class CommandLineHelper(object):
if 0 != rc:
self._raise_cli_error(cmd_get_port, rc, out)
for i, sp in enumerate(sps):
if io_ports: # Skip ports which are not in io_ports
if (sp.split()[1], int(portid[i])) not in io_ports:
continue
wwn = self.get_port_wwn(sp, portid[i], out)
if (wwn is not None) and (wwn not in wwns):
LOG.debug('Add wwn:%(wwn)s for sg:%(sg)s.',
@ -1396,8 +1404,8 @@ class CommandLineHelper(object):
self._raise_cli_error(cmd_get_hba, rc, out)
return wwns
def get_login_ports(self, storage_group_name, connector_wwpns):
def get_login_ports(self, storage_group_name, connector_wwpns,
io_ports=None):
cmd_list_hba = ('port', '-list', '-gname', storage_group_name)
out, rc = self.command_execute(*cmd_list_hba)
ports = []
@ -1418,9 +1426,14 @@ class CommandLineHelper(object):
'HBA Devicename:.*\n\s*' +
'Trusted:.*\n\s*' +
'Logged In:\s*YES\n')
for each in connector_hba_list:
ports.extend(re.findall(port_pat, each))
ports = list(set(ports))
if io_ports:
ports = filter(lambda po:
(po[0].split()[1], int(po[1])) in io_ports,
ports)
for each in ports:
wwn = self.get_port_wwn(each[0], each[1], allports)
if wwn:
@ -1430,16 +1443,16 @@ class CommandLineHelper(object):
return wwns
def get_port_wwn(self, sp, port_id, allports=None):
"""Returns wwn via sp and port_id
:param sp: should be in this format 'SP A'
:param port_id: '0' or 0
"""
wwn = None
if allports is None:
cmd_get_port = ('port', '-list', '-sp')
out, rc = self.command_execute(*cmd_get_port)
if 0 != rc:
self._raise_cli_error(cmd_get_port, rc, out)
else:
allports = out
allports, rc = self.get_port_output()
_re_port_wwn = re.compile('SP Name:\s*' + sp +
'\nSP Port ID:\s*' + port_id +
'\nSP Port ID:\s*' + str(port_id) +
'\nSP UID:\s*((\w\w:){15}(\w\w))' +
'\nLink Status: Up' +
'\nPort Status: Online')
@ -1449,11 +1462,7 @@ class CommandLineHelper(object):
return wwn
def get_fc_targets(self):
fc_getport = ('port', '-list', '-sp')
out, rc = self.command_execute(*fc_getport)
if rc != 0:
self._raise_cli_error(fc_getport, rc, out)
out, rc = self.get_port_output()
fc_target_dict = {'A': [], 'B': []}
_fcport_pat = (r'SP Name: SP\s(\w)\s*'
@ -1469,7 +1478,42 @@ class CommandLineHelper(object):
'Port ID': sp_port_id})
return fc_target_dict
def get_iscsi_targets(self, poll=True):
def get_port_output(self):
cmd_get_port = ('port', '-list', '-sp')
out, rc = self.command_execute(*cmd_get_port)
if 0 != rc:
self._raise_cli_error(cmd_get_port, rc, out)
return out, rc
def get_connection_getport_output(self):
connection_getport_cmd = ('connection', '-getport', '-vlanid')
out, rc = self.command_execute(*connection_getport_cmd)
if 0 != rc:
self._raise_cli_error(connection_getport_cmd, rc, out)
return out, rc
def _filter_iscsi_ports(self, all_ports, io_ports):
"""Filter ports in white list from all iSCSI ports."""
new_iscsi_ports = {'A': [], 'B': []}
valid_ports = []
for sp in all_ports:
for port in all_ports[sp]:
port_tuple = (port['SP'],
port['Port ID'],
port['Virtual Port ID'])
if port_tuple in io_ports:
new_iscsi_ports[sp].append(port)
valid_ports.append(port_tuple)
if len(io_ports) != len(valid_ports):
invalid_port_set = set(io_ports) - set(valid_ports)
for invalid in invalid_port_set:
LOG.warning(_LW('Invalid iSCSI port %(sp)s-%(port)s-%(vlan)s '
'found in io_port_list, will be ignored.'),
{'sp': invalid[0], 'port': invalid[1],
'vlan': invalid[2]})
return new_iscsi_ports
def get_iscsi_targets(self, poll=False, io_ports=None):
cmd_getport = ('connection', '-getport', '-address', '-vlanid')
out, rc = self.command_execute(*cmd_getport, poll=poll)
if rc != 0:
@ -1502,7 +1546,8 @@ class CommandLineHelper(object):
'Port WWN': iqn,
'Virtual Port ID': vport_id,
'IP Address': ip_addr})
if io_ports:
return self._filter_iscsi_ports(iscsi_target_dict, io_ports)
return iscsi_target_dict
def get_registered_spport_set(self, initiator_iqn, sgname, sg_raw_out):
@ -1759,8 +1804,11 @@ class EMCVnxCliBase(object):
conf_pools = self.configuration.safe_get("storage_vnx_pool_names")
self.storage_pools = self._get_managed_storage_pools(conf_pools)
self.array_serial = None
self.io_ports = self._parse_ports(self.configuration.io_port_list,
self.protocol)
if self.protocol == 'iSCSI':
self.iscsi_targets = self._client.get_iscsi_targets(poll=True)
self.iscsi_targets = self._client.get_iscsi_targets(
poll=True, io_ports=self.io_ports)
self.hlu_cache = {}
self.force_delete_lun_in_sg = (
self.configuration.force_delete_lun_in_storagegroup)
@ -1806,6 +1854,59 @@ class EMCVnxCliBase(object):
"manage all the pools on the VNX system.")
return storage_pools
def _parse_ports(self, io_port_list, protocol):
"""Validates IO port format, supported format is a-1, b-3, a-3-0."""
if not io_port_list or io_port_list == '*':
return None
ports = re.split('\s*,\s*', io_port_list)
valid_ports = []
invalid_ports = []
if 'iSCSI' == protocol:
out, rc = self._client.get_connection_getport_output()
for port in ports:
port_tuple = port.split('-')
if (re.match('[abAB]-\d+-\d+$', port) and
self._validate_iscsi_port(
port_tuple[0], port_tuple[1], port_tuple[2], out)):
valid_ports.append(
(port_tuple[0].upper(), int(port_tuple[1]),
int(port_tuple[2])))
else:
invalid_ports.append(port)
elif 'FC' == protocol:
out, rc = self._client.get_port_output()
for port in ports:
port_tuple = port.split('-')
if re.match('[abAB]-\d+$', port) and self._validate_fc_port(
port_tuple[0], port_tuple[1], out):
valid_ports.append(
(port_tuple[0].upper(), int(port_tuple[1])))
else:
invalid_ports.append(port)
if len(invalid_ports) > 0:
msg = _('Invalid %(protocol)s ports %(port)s specified '
'for io_port_list.') % {'protocol': self.protocol,
'port': ','.join(invalid_ports)}
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
return valid_ports
def _validate_iscsi_port(self, sp, port_id, vlan_id, cmd_output):
"""Validates whether the iSCSI port is existed on VNX"""
iscsi_pattern = ('SP:\s+' + sp.upper() +
'\nPort ID:\s+' + str(port_id) +
'\nPort WWN:\s+.*' +
'\niSCSI Alias:\s+.*\n'
'\nVirtual Port ID:\s+' + str(vlan_id))
return re.search(iscsi_pattern, cmd_output)
def _validate_fc_port(self, sp, port_id, cmd_output):
"""Validates whether the FC port is existed on VNX"""
fc_pattern = ('SP Name:\s*SP\s*' + sp.upper() +
'\nSP Port ID:\s*' + str(port_id) +
'\nSP UID:\s*((\w\w:){15}(\w\w))')
return re.search(fc_pattern, cmd_output)
def get_array_serial(self):
if not self.array_serial:
self.array_serial = self._client.get_array_serial()
@ -2279,7 +2380,6 @@ class EMCVnxCliBase(object):
self.stats['consistencygroup_support'] = (
'True' if '-VNXSnapshots' in self.enablers else 'False')
return self.stats
def create_snapshot(self, snapshot):
@ -2677,8 +2777,60 @@ class EMCVnxCliBase(object):
'portid': port_id,
'msg': out})
def _register_iscsi_initiator(self, ip, host, initiator_uids):
iscsi_targets = self.iscsi_targets
def auto_register_with_io_port_filter(self, connector, sgdata,
io_port_filter):
"""Automatically register specific IO ports to storage group."""
initiator = connector['initiator']
ip = connector['ip']
host = connector['host']
new_white = {'A': [], 'B': []}
if self.protocol == 'iSCSI':
if sgdata:
sp_ports = self._client.get_registered_spport_set(
initiator, host, sgdata['raw_output'])
# Normalize io_ports
for sp in ('A', 'B'):
new_ports = filter(
lambda pt: (pt['SP'], pt['Port ID']) not in sp_ports,
self.iscsi_targets[sp])
new_white[sp] = map(lambda white:
{'SP': white['SP'],
'Port ID': white['Port ID'],
'Virtual Port ID':
white['Virtual Port ID']},
new_ports)
else:
new_white = self.iscsi_targets
self._register_iscsi_initiator(ip, host, [initiator], new_white)
elif self.protocol == 'FC':
wwns = self._extract_fc_uids(connector)
ports_list = []
if sgdata:
for wwn in wwns:
for port in io_port_filter:
if ((port not in ports_list) and
(not re.search(wwn + '\s+SP\s+' +
port[0] + '\s+' + str(port[1]),
sgdata['raw_output'],
re.IGNORECASE))):
# Record ports to be added
ports_list.append(port)
new_white[port[0]].append({
'SP': port[0],
'Port ID': port[1]})
else:
# Need to translate to dict format
for fc_port in io_port_filter:
new_white[fc_port[0]].append({'SP': fc_port[0],
'Port ID': fc_port[1]})
self._register_fc_initiator(ip, host, wwns, new_white)
return new_white['A'] or new_white['B']
def _register_iscsi_initiator(self, ip, host, initiator_uids,
port_to_register=None):
iscsi_targets = (port_to_register if port_to_register else
self.iscsi_targets)
for initiator_uid in initiator_uids:
LOG.info(_LI('Get ISCSI targets %(tg)s to register '
'initiator %(in)s.'),
@ -2702,8 +2854,10 @@ class EMCVnxCliBase(object):
self._exec_command_setpath(initiator_uid, sp, port_id,
ip, host, vport_id)
def _register_fc_initiator(self, ip, host, initiator_uids):
fc_targets = self._client.get_fc_targets()
def _register_fc_initiator(self, ip, host, initiator_uids,
ports_to_register=None):
fc_targets = (ports_to_register if ports_to_register else
self._client.get_fc_targets())
for initiator_uid in initiator_uids:
LOG.info(_LI('Get FC targets %(tg)s to register '
'initiator %(in)s.'),
@ -2758,7 +2912,7 @@ class EMCVnxCliBase(object):
unregistered_initiators.append(initiator_uid)
return unregistered_initiators
def auto_register_initiator(self, connector, sgdata):
def auto_register_initiator_to_all(self, connector, sgdata):
"""Automatically registers available initiators.
Returns True if has registered initiator otherwise returns False.
@ -2803,6 +2957,17 @@ class EMCVnxCliBase(object):
self._register_fc_initiator(ip, host, itors_toReg)
return True
def auto_register_initiator(self, connector, sgdata, io_ports_filter=None):
"""Automatically register available initiators.
:returns: True if has registered initiator otherwise return False
"""
if io_ports_filter:
return self.auto_register_with_io_port_filter(connector, sgdata,
io_ports_filter)
else:
return self.auto_register_initiator_to_all(connector, sgdata)
def assure_host_access(self, volume, connector):
hostname = connector['host']
volumename = volume['name']
@ -2816,7 +2981,7 @@ class EMCVnxCliBase(object):
# Storage Group has not existed yet
self.assure_storage_group(hostname)
if self.itor_auto_reg:
self.auto_register_initiator(connector, None)
self.auto_register_initiator(connector, None, self.io_ports)
auto_registration_done = True
else:
self._client.connect_host_to_storage_group(hostname, hostname)
@ -2825,7 +2990,8 @@ class EMCVnxCliBase(object):
poll=True)
if self.itor_auto_reg and not auto_registration_done:
new_registerred = self.auto_register_initiator(connector, sgdata)
new_registerred = self.auto_register_initiator(connector, sgdata,
self.io_ports)
if new_registerred:
sgdata = self._client.get_storage_group(hostname,
poll=True)
@ -2913,13 +3079,6 @@ class EMCVnxCliBase(object):
properties['target_portal'] = \
"%s:3260" % targets[0]['IP Address']
properties['target_lun'] = hlu
auth = volume['provider_auth']
if auth:
(auth_method, auth_username, auth_secret) = auth.split()
properties['auth_method'] = auth_method
properties['auth_username'] = auth_username
properties['auth_password'] = auth_secret
else:
properties = {'target_discovered': False,
'target_iqns': None,
@ -2944,11 +3103,12 @@ class EMCVnxCliBase(object):
'target_dicovered': True,
'target_wwn': None}
if self.zonemanager_lookup_service is None:
fc_properties['target_wwn'] = self.get_login_ports(connector)
fc_properties['target_wwn'] = self.get_login_ports(connector,
self.io_ports)
else:
target_wwns, itor_tgt_map = self.get_initiator_target_map(
connector['wwpns'],
self.get_status_up_ports(connector))
self.get_status_up_ports(connector, self.io_ports))
fc_properties['target_wwn'] = target_wwns
fc_properties['initiator_target_map'] = itor_tgt_map
return fc_properties
@ -3098,12 +3258,14 @@ class EMCVnxCliBase(object):
self._build_provider_location_for_lun(lun_id)}
return model_update
def get_login_ports(self, connector):
def get_login_ports(self, connector, io_ports=None):
return self._client.get_login_ports(connector['host'],
connector['wwpns'])
connector['wwpns'],
io_ports)
def get_status_up_ports(self, connector):
return self._client.get_status_up_ports(connector['host'])
def get_status_up_ports(self, connector, io_ports=None):
return self._client.get_status_up_ports(connector['host'],
io_ports=io_ports)
def get_initiator_target_map(self, fc_initiators, fc_targets):
target_wwns = []
@ -3276,9 +3438,9 @@ class EMCVnxCliBase(object):
def update_volume_stats(self):
"""Retrieves stats info."""
self.update_enabler_in_volume_stats()
if self.protocol == 'iSCSI':
self.iscsi_targets = self._client.get_iscsi_targets(poll=False)
self.iscsi_targets = self._client.get_iscsi_targets(
poll=False, io_ports=self.io_ports)
properties = [self._client.POOL_FREE_CAPACITY,
self._client.POOL_TOTAL_CAPACITY,