Merge "[SVf] Manage host attachment using portsets"

This commit is contained in:
Zuul 2022-03-01 00:13:24 +00:00 committed by Gerrit Code Review
commit 2cdabb6a97
5 changed files with 279 additions and 67 deletions

@ -88,7 +88,9 @@ class StorwizeSVCManagementSimulator(object):
self._other_pools = {'openstack2': {}, 'openstack3': {}}
self._next_cmd_error = {
'lsportip': '',
'lsip': '',
'lsfabric': '',
'lsfcportsetmember': '',
'lsiscsiauth': '',
'lsnodecanister': '',
'mkvdisk': '',
@ -747,45 +749,45 @@ port_speed!N/A
ports = [None] * 17
ports[0] = ['id', 'WWPN', 'WWNN', 'port_id', 'owning_node_id',
'current_node_id', 'nportid', 'host_io_permitted',
'virtualized']
'virtualized', 'fc_io_port_id']
ports[1] = ['0', '5005076801106CFE', '5005076801106CFE', '1', '1',
'1', '042200', 'no', 'no']
'1', '042200', 'no', 'no', '']
ports[2] = ['0', '5005076801996CFE', '5005076801106CFE', '1', '1',
'1', '042200', 'yes', 'yes']
'1', '042200', 'yes', 'yes', '']
ports[3] = ['0', '5005076801206CFE', '5005076801106CFE', '2', '1',
'1', '042200', 'no', 'no']
'1', '042200', 'no', 'no', '']
ports[4] = ['0', '5005076801A96CFE', '5005076801106CFE', '2', '1',
'1', '042200', 'yes', 'yes']
'1', '042200', 'yes', 'yes', '']
ports[5] = ['0', '5005076801306CFE', '5005076801106CFE', '3', '1',
'', '042200', 'no', 'no']
'', '042200', 'no', 'no', '']
ports[6] = ['0', '5005076801B96CFE', '5005076801106CFE', '3', '1',
'', '042200', 'yes', 'yes']
'', '042200', 'yes', 'yes', '']
ports[7] = ['0', '5005076801406CFE', '5005076801106CFE', '4', '1',
'', '042200', 'no', 'no']
'', '042200', 'no', 'no', '']
ports[8] = ['0', '5005076801C96CFE', '5005076801106CFE', '4', '1',
'', '042200', 'yes', 'yes']
'', '042200', 'yes', 'yes', '']
ports[9] = ['0', '5005076801101806', '5005076801101806', '1', '2',
'2', '042200', 'no', 'no']
'2', '042200', 'no', 'no', '']
ports[10] = ['0', '5005076801991806', '5005076801101806', '1', '2',
'2', '042200', 'yes', 'yes']
'2', '042200', 'yes', 'yes', '']
ports[11] = ['0', '5005076801201806', '5005076801101806', '2', '2',
'2', '042200', 'no', 'no']
'2', '042200', 'no', 'no', '']
ports[12] = ['0', '5005076801A91806', '5005076801101806', '2', '2',
'2', '042200', 'yes', 'yes']
'2', '042200', 'yes', 'yes', '']
ports[13] = ['0', '5005076801301806', '5005076801101806', '3', '2',
'', '042200', 'no', 'no']
'', '042200', 'no', 'no', '']
ports[14] = ['0', '5005076801B91806', '5005076801101806', '3', '2',
'', '042200', 'yes', 'yes']
'', '042200', 'yes', 'yes', '']
ports[15] = ['0', '5005076801401806', '5005076801101806', '4', '2',
'', '042200', 'no', 'no']
'', '042200', 'no', 'no', '']
ports[16] = ['0', '5005076801C91806', '5005076801101806', '4', '2',
'', '042200', 'yes', 'yes']
'', '042200', 'yes', 'yes', '']
if 'filtervalue' in kwargs:
rows = []
rows.append(['id', 'WWPN', 'WWNN', 'port_id', 'owning_node_id',
'current_node_id', 'nportid', 'host_io_permitted',
'virtualized'])
'virtualized', 'fc_io_port_id'])
if ':' in kwargs['filtervalue']:
filter1 = kwargs['filtervalue'].split(':')[0]
@ -805,6 +807,73 @@ port_speed!N/A
rows = ports
return self._print_info_cmd(rows=rows, **kwargs)
# Print mostly made-up stuff in the correct syntax
def _cmd_lsfcportsetmember(self, **kwargs):
rows = [None] * 7
rows[0] = ['id', 'fc_io_port_id', 'portset_id', 'portset_name',
'owner_id', 'owner_name']
rows[1] = ['0', '5', '6', 'portset6', '', '']
rows[2] = ['1', '5', '64', 'portset64', '', '']
rows[3] = ['2', '6', '6', 'portset6', '', '']
rows[4] = ['3', '6', '64', 'portset64', '', '']
rows[5] = ['4', '7', '64', 'portset64', '', '']
rows[6] = ['5', '8', '64', 'portset64', '', '']
if self._next_cmd_error['lsfcportsetmember'] == 'header_mismatch':
rows[0].pop(2)
self._next_cmd_error['lsfcportsetmember'] = ''
if self._next_cmd_error['lsfcportsetmember'] == 'remove_field':
for row in rows:
row.pop(1)
self._next_cmd_error['lsfcportsetmember'] = ''
return self._print_info_cmd(rows=rows, **kwargs)
# Print mostly made-up stuff in the correct syntax
def _cmd_lsip(self, **kwargs):
ports = [None] * 9
ports[0] = ['id', 'node_id', 'node_name', 'port_id', 'portset_id',
'portset_name', 'IP_address', 'prefix', 'vlan', 'gateway',
'owner_id', 'owner_name']
ports[1] = ['0', '1', 'node1', '5', '0', 'portset0', '1.234.50.11',
'24', '1001', '', '', '']
ports[2] = ['1', '1', 'node1', '6', '4', 'portset4', '1.234.51.11',
'24', '1002', '', '', '']
ports[3] = ['2', '1', 'node1', '7', '5', 'portset5', '1.234.52.11',
'24', '1003', '', '', '']
ports[4] = ['3', '1', 'node1', '8', '6', 'portset6', '1.234.53.11',
'24', '1004', '', '', '']
ports[5] = ['4', '2', 'node2', '5', '0', 'portset0', '1.234.54.11',
'24', '1005', '', '', '']
ports[6] = ['5', '2', 'node2', '6', '4', 'portset4', '1.234.55.11',
'24', '1006', '', '', '']
ports[7] = ['6', '2', 'node2', '7', '5', 'portset5', '1.234.56.11',
'24', '1007', '', '', '']
ports[8] = ['7', '2', 'node2', '8', '6', 'portset6', '1.234.57.11',
'24', '1008', '', '', '']
if 'filtervalue' in kwargs:
rows = []
rows.append(['id', 'node_id', 'node_name', 'port_id', 'portset_id',
'portset_name', 'IP_address', 'prefix', 'vlan',
'gateway', 'owner_id', 'owner_name'])
value = kwargs['filtervalue'].split('=')[1]
for v in ports:
if six.text_type(v[5]) == value:
rows.append(v)
else:
rows = ports
if self._next_cmd_error['lsip'] == 'header_mismatch':
rows[0].pop(2)
self._next_cmd_error['lsip'] = ''
if self._next_cmd_error['lsip'] == 'remove_field':
for row in rows:
row.pop(1)
self._next_cmd_error['lsip'] = ''
return self._print_info_cmd(rows=rows, **kwargs)
# Print mostly made-up stuff in the correct syntax
def _cmd_lsportip(self, **kwargs):
if self._next_cmd_error['lsportip'] == 'ip_no_config':
@ -1247,6 +1316,10 @@ port_speed!N/A
host_info['site_name'] = kwargs['site'].strip('\'\"')
else:
host_info['site_name'] = ''
if 'portset' in kwargs:
host_info['portset_name'] = kwargs['portset'].strip('\'\"')
else:
host_info['portset_name'] = ''
out, err = self._add_port_to_host(host_info, **kwargs)
if not len(err):
self._hosts_list[host_name] = host_info
@ -4826,6 +4899,26 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
self.sim.error_injection('lsportip', 'remove_field')
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.do_setup, None)
self.sim.error_injection('lsfcportsetmember', 'invalid_input')
self.driver.do_setup(None)
with mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_system_info') as get_system_info:
fake_system_info = {'code_level': (8, 5, 0, 0),
'topology': 'standard',
'system_name': 'storwize-svc-sim',
'system_id': '0123456789ABCDEF'}
get_system_info.return_value = fake_system_info
if self.USESIM:
self.sim.error_injection('lsip', 'invalid_portset')
self.driver.do_setup(None)
self.sim.error_injection('lsip', 'header_mismatch')
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.do_setup, None)
self.sim.error_injection('lsip', 'remove_field')
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.do_setup, None)
# Check with bad parameters
self._set_flag('san_ip', '')
@ -4873,6 +4966,37 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
# Finally, check with good parameters
self.driver.do_setup(None)
@mock.patch.object(storwize_svc_common.StorwizeSSH,
'mkhost')
def test_storwize_create_host_with_portset(self, mkhost):
self.driver.do_setup(self.ctxt)
connector = {'host': 'storwize-svc-host',
'initiator': 'iqn.1993-08.org.debian:01:eac5ccc1aaa',
'ip': '127.0.0.1'}
# Using portset other than default portset0
portset = "portset1"
self.driver._helpers.create_host(connector, iscsi=True,
portset=portset)
host_name = self.driver._helpers.get_host_from_connector(
connector, iscsi=True)
self.assertIsNotNone(host_name)
@mock.patch.object(storwize_svc_common.StorwizeSSH,
'mkhost')
def test_storwize_create_host_with_portset_from_config(self, mkhost):
self.driver.do_setup(self.ctxt)
connector = {'host': 'storwize-svc-host',
'initiator': 'iqn.1993-08.org.debian:01:eac5ccc1aaa',
'ip': '127.0.0.1'}
# Using portset other than default portset0
self._set_flag('storwize_portset', "portset1")
self.driver._helpers.create_host(
connector, iscsi=True,
portset=self.driver.configuration.storwize_portset)
host_name = self.driver._helpers.get_host_from_connector(
connector, iscsi=True)
self.assertIsNotNone(host_name)
@mock.patch.object(ssh_utils, 'SSHPool')
@mock.patch.object(processutils, 'ssh_execute')
def test_run_ssh_set_up_with_san_ip(self, mock_ssh_execute, mock_ssh_pool):
@ -5288,6 +5412,7 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
'mirror_pool': None,
'volume_topology': None,
'peer_pool': None,
'storwize_portset': None,
'storwize_svc_src_child_pool': None,
'storwize_svc_target_child_pool': None,
'cycle_period_seconds': 300

@ -133,6 +133,10 @@ storwize_svc_opts = [
default=None,
help='Specifies the name of the pool in which mirrored copy '
'is stored. Example: "pool2"'),
cfg.StrOpt('storwize_portset',
default=None,
help='Specifies the name of the portset in which '
'host to be created.'),
cfg.StrOpt('storwize_svc_src_child_pool',
default=None,
help='Specifies the name of the source child pool in which '
@ -272,11 +276,13 @@ class StorwizeSSH(object):
port.append(port_name)
return port
def mkhost(self, host_name, port_type, port_name, site=None):
def mkhost(self, host_name, port_type, port_name, site=None, portset=None):
port = self._create_port_arg(port_type, port_name)
ssh_cmd = ['svctask', 'mkhost', '-force'] + port
if site:
ssh_cmd += ['-site', '"%s"' % site]
if portset:
ssh_cmd += ['-portset', '"%s"' % portset]
ssh_cmd += ['-name', '"%s"' % host_name]
return self.run_ssh_check_created(ssh_cmd)
@ -318,6 +324,12 @@ class StorwizeSSH(object):
ssh_cmd = ['svcinfo', 'lsiscsiauth', '-delim', '!']
return self.run_ssh_info(ssh_cmd, with_header=True)
def lsip(self, portset=None):
ssh_cmd = ['svcinfo', 'lsip', '-delim', '!']
if portset:
ssh_cmd += ['-filtervalue', 'portset_name=%s' % portset]
return self.run_ssh_info(ssh_cmd, with_header=True)
def lsfabric(self, wwpn=None, host=None):
ssh_cmd = ['svcinfo', 'lsfabric', '-delim', '!']
if wwpn:
@ -765,6 +777,10 @@ class StorwizeSSH(object):
ssh_cmd += ['-filtervalue', 'current_node_id=%s' % current_node_id]
return self.run_ssh_info(ssh_cmd, with_header=True)
def lsfcportsetmember(self):
ssh_cmd = ['svcinfo', 'lsfcportsetmember', '-delim', '!']
return self.run_ssh_info(ssh_cmd, with_header=True)
def migratevdisk(self, vdisk, dest_pool, copy_id='0'):
ssh_cmd = ['svctask', 'migratevdisk', '-mdiskgrp', dest_pool, '-copy',
copy_id, '-vdisk', vdisk]
@ -1070,6 +1086,7 @@ class StorwizeHelpers(object):
node['WWPN'] = []
node['ipv4'] = []
node['ipv6'] = []
node['IP_address'] = []
node['enabled_protocols'] = []
nodes[node['id']] = node
node['site_id'] = (node_data['site_id']
@ -1080,21 +1097,37 @@ class StorwizeHelpers(object):
self.handle_keyerror('lsnode', node_data)
return nodes
def add_iscsi_ip_addrs(self, storage_nodes):
def add_iscsi_ip_addrs(self, storage_nodes, code_level, portset=None):
"""Add iSCSI IP addresses to system node information."""
resp = self.ssh.lsportip()
for ip_data in resp:
try:
state = ip_data['state']
if ip_data['node_id'] in storage_nodes and (
state == 'configured' or state == 'online'):
node = storage_nodes[ip_data['node_id']]
if len(ip_data['IP_address']):
node['ipv4'].append(ip_data['IP_address'])
if len(ip_data['IP_address_6']):
node['ipv6'].append(ip_data['IP_address_6'])
except KeyError:
self.handle_keyerror('lsportip', ip_data)
if code_level >= (8, 4, 2, 0):
portset_name = portset if portset else 'portset0'
lsip_resp = self.ssh.lsip(portset=portset_name)
for node_data in storage_nodes:
ip_addresses = []
try:
for ip_data in lsip_resp:
if ip_data['node_id'] in node_data:
if (ip_data['IP_address']):
ip_addresses.append(ip_data['IP_address'])
except KeyError:
self.handle_keyerror('lsip', ip_data)
if ip_addresses:
storage_nodes[ip_data['node_id']]['IP_address'] = (
ip_addresses)
else:
lsportip_resp = self.ssh.lsportip()
for ip_data in lsportip_resp:
try:
state = ip_data['state']
if ip_data['node_id'] in storage_nodes and (
state == 'configured' or state == 'online'):
node = storage_nodes[ip_data['node_id']]
if len(ip_data['IP_address']):
node['ipv4'].append(ip_data['IP_address'])
if len(ip_data['IP_address_6']):
node['ipv6'].append(ip_data['IP_address_6'])
except KeyError:
self.handle_keyerror('lsportip', ip_data)
def add_fc_wwpns(self, storage_nodes, code_level):
"""Add FC WWPNs to system node information."""
@ -1110,20 +1143,36 @@ class StorwizeHelpers(object):
port_info['status'] == 'active'):
wwpns.add(port_info['WWPN'])
else:
npiv_wwpns = self.get_npiv_wwpns(node_id=node['id'])
npiv_wwpns = self.get_npiv_wwpns(code_level,
node_id=node['id'])
wwpns.update(npiv_wwpns)
node['WWPN'] = list(wwpns)
LOG.info('WWPN on node %(node)s: %(wwpn)s.',
{'node': node['id'], 'wwpn': node['WWPN']})
def get_npiv_wwpns(self, node_id=None, host_io=None):
def get_npiv_wwpns(self, code_level, node_id=None, host_io=None,
portset=None):
wwpns = set()
# In the response of lstargetportfc, the host_io_permitted
# indicates whether the port can be used for host I/O
resp = self.ssh.lstargetportfc(current_node_id=node_id,
host_io_permitted=host_io)
for port_info in resp:
wwpns.add(port_info['WWPN'])
targetportfc_resp = self.ssh.lstargetportfc(current_node_id=node_id,
host_io_permitted=host_io)
if code_level >= (8, 4, 2, 0):
portset_name = portset if portset else 'portset64'
port_ids = set()
fcportsetmember_resp = self.ssh.lsfcportsetmember()
for portset_member in fcportsetmember_resp:
if portset_member['portset_name'] == portset_name:
port_ids.add(portset_member['fc_io_port_id'])
for port_info in targetportfc_resp:
for port_id in port_ids:
if port_id == port_info['fc_io_port_id']:
wwpns.add(port_info['WWPN'])
break
else:
for port_info in targetportfc_resp:
wwpns.add(port_info['WWPN'])
return list(wwpns)
def add_chap_secret_to_host(self, host_name):
@ -1288,7 +1337,7 @@ class StorwizeHelpers(object):
LOG.debug('Leave: get_host_from_connector: host %s.', host_name)
return host_name
def create_host(self, connector, iscsi=False, site=None):
def create_host(self, connector, iscsi=False, site=None, portset=None):
"""Create a new host on the storage system.
We create a host name and associate it with the given connection
@ -1343,7 +1392,7 @@ class StorwizeHelpers(object):
# Create a host with one port
port = ports.pop(0)
# Host site_id is necessary for hyperswap volume.
self.ssh.mkhost(host_name, port[0], port[1], site)
self.ssh.mkhost(host_name, port[0], port[1], site, portset)
# Add any additional ports to the host
for port in ports:
@ -1514,6 +1563,7 @@ class StorwizeHelpers(object):
'mirror_pool': config.storwize_svc_mirror_pool,
'volume_topology': None,
'peer_pool': config.storwize_peer_pool,
'storwize_portset': config.storwize_portset,
'storwize_svc_src_child_pool':
config.storwize_svc_src_child_pool,
'storwize_svc_target_child_pool':
@ -3292,14 +3342,15 @@ class StorwizeSVCCommonDriver(san.SanDriver,
state['storage_nodes'] = helper.get_node_info()
# Add the iSCSI IP addresses and WWPNs to the storage node info
helper.add_iscsi_ip_addrs(state['storage_nodes'])
helper.add_iscsi_ip_addrs(state['storage_nodes'], state['code_level'])
helper.add_fc_wwpns(state['storage_nodes'], state['code_level'])
# For each node, check what connection modes it supports. Delete any
# nodes that do not support any types (may be partially configured).
to_delete = []
for k, node in state['storage_nodes'].items():
if ((len(node['ipv4']) or len(node['ipv6']))
if ((len(node['ipv4']) or len(node['ipv6']) or
len(node['IP_address']))
and len(node['iscsi_name'])):
node['enabled_protocols'].append('iSCSI')
state['enabled_protocols'].add('iSCSI')

@ -96,9 +96,10 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
2.2.3 - Add replication group support
2.2.4 - Add backup snapshots support
2.2.5 - Add hyperswap support
2.2.6 - Add support for host attachment using portsets
"""
VERSION = "2.2.5"
VERSION = "2.2.6"
# ThirdPartySystems wiki page
CI_WIKI_NAME = "IBM_STORAGE_CI"
@ -190,7 +191,10 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
# this connector info.
host_name = None
try:
host_name = backend_helper.create_host(connector, site=host_site)
opts = self._get_vdisk_params(volume.volume_type_id)
host_name = (
backend_helper.create_host(connector, site=host_site,
portset=opts['storwize_portset']))
except exception.VolumeBackendAPIException as excp:
if "CMMVC6035E" in excp.msg:
msg = (_('Host already exists for connector '
@ -266,8 +270,10 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
conn_wwpns.extend(node['WWPN'])
else:
npiv_wwpns = backend_helper.get_npiv_wwpns(
node_state['code_level'],
node_id=node['id'],
host_io="yes")
host_io="yes",
portset=opts['storwize_portset'])
conn_wwpns.extend(npiv_wwpns)
properties['target_wwn'] = conn_wwpns
@ -423,8 +429,10 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
if node_state['code_level'] < (7, 7, 0, 0):
conn_wwpns.extend(node['WWPN'])
else:
npivwwpns = backend_helper.get_npiv_wwpns(node_id=node['id'],
host_io="yes")
npivwwpns = (
backend_helper.get_npiv_wwpns(node_state['code_level'],
node_id=node['id'],
host_io="yes"))
conn_wwpns.extend(npivwwpns)
i_t_map = self._make_initiator_target_map(connector['wwpns'],

@ -94,9 +94,10 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
2.2.2 - Add replication group support
2.2.3 - Add backup snapshots support
2.2.4 - Add hyperswap support
2.2.5 - Add support for host attachment using portsets
"""
VERSION = "2.2.4"
VERSION = "2.2.5"
# ThirdPartySystems wiki page
CI_WIKI_NAME = "IBM_STORAGE_CI"
@ -183,8 +184,11 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
# this connector info.
host_name = None
try:
host_name = backend_helper.create_host(connector, iscsi=True,
site=host_site)
opts = self._get_vdisk_params(volume.volume_type_id)
host_name = (
backend_helper.create_host(connector, iscsi=True,
site=host_site,
portset=opts['storwize_portset']))
except exception.VolumeBackendAPIException as excp:
if "CMMVC6578E" in excp.msg:
msg = (_('Host already exists for connector '
@ -213,13 +217,15 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
try:
properties = self._get_single_iscsi_data(volume, connector,
lun_id, chap_secret)
lun_id, chap_secret,
opts['storwize_portset'])
multipath = connector.get('multipath', False)
if multipath:
properties = self._get_multi_iscsi_data(volume, connector,
lun_id, properties,
backend_helper,
node_state)
properties = (
self._get_multi_iscsi_data(volume, connector,
lun_id, properties,
backend_helper, node_state,
opts['storwize_portset']))
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error('initialize_connection: Failed to export volume '
@ -240,7 +246,8 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
return {'driver_volume_type': 'iscsi', 'data': properties, }
def _get_single_iscsi_data(self, volume, connector, lun_id, chap_secret):
def _get_single_iscsi_data(self, volume, connector, lun_id,
chap_secret, portset):
LOG.debug('enter: _get_single_iscsi_data: volume %(vol)s with '
'connector %(conn)s lun_id %(lun_id)s',
{'vol': volume.id, 'conn': connector,
@ -277,6 +284,11 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
# Get preferred node and other nodes in I/O group
preferred_node_entry = None
io_group_nodes = []
if node_state['code_level'] >= (8, 4, 2, 0):
backend_helper.add_iscsi_ip_addrs(node_state['storage_nodes'],
node_state['code_level'],
portset=portset)
for node in node_state['storage_nodes'].values():
if self.protocol not in node['enabled_protocols']:
continue
@ -305,10 +317,14 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
'target_lun': lun_id,
'volume_id': volume.id}
if preferred_node_entry['ipv4']:
ipaddr = preferred_node_entry['ipv4'][0]
if node_state['code_level'] >= (8, 4, 2, 0):
if preferred_node_entry['IP_address']:
ipaddr = preferred_node_entry['IP_address'][0]
else:
ipaddr = preferred_node_entry['ipv6'][0]
if preferred_node_entry['ipv4']:
ipaddr = preferred_node_entry['ipv4'][0]
else:
ipaddr = preferred_node_entry['ipv6'][0]
properties['target_portal'] = '%s:%s' % (ipaddr, '3260')
properties['target_iqn'] = preferred_node_entry['iscsi_name']
if chap_secret:
@ -327,14 +343,18 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
return properties
def _get_multi_iscsi_data(self, volume, connector, lun_id, properties,
backend_helper, node_state):
backend_helper, node_state, portset):
LOG.debug('enter: _get_multi_iscsi_data: volume %(vol)s with '
'connector %(conn)s lun_id %(lun_id)s',
{'vol': volume.id, 'conn': connector,
'lun_id': lun_id})
try:
resp = backend_helper.ssh.lsportip()
if node_state['code_level'] >= (8, 4, 2, 0):
portset_name = portset if portset else 'portset0'
resp = backend_helper.ssh.lsip(portset=portset_name)
else:
resp = backend_helper.ssh.lsportip()
except Exception as ex:
msg = (_('_get_multi_iscsi_data: Failed to '
'get port ip because of exception: '
@ -351,11 +371,14 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
continue
link_state = ip_data.get('link_state', None)
valid_port = ''
if ((ip_data['state'] == 'configured' and
link_state == 'active') or
ip_data['state'] == 'online'):
valid_port = (ip_data['IP_address'] or
ip_data['IP_address_6'])
if node_state['code_level'] >= (8, 4, 2, 0):
valid_port = ip_data['IP_address']
else:
if ((ip_data['state'] == 'configured' and
link_state == 'active') or
ip_data['state'] == 'online'):
valid_port = (ip_data['IP_address'] or
ip_data['IP_address_6'])
if valid_port:
properties['target_portals'].append(
'%s:%s' % (valid_port, '3260'))

@ -0,0 +1,5 @@
---
features:
- |
IBM Spectrum Virtualize Family driver: Added support to manage host
attachment using portsets for code level >= 8.4.2.0