HPE 3PAR: use vlan iscsi ips

In the code hpe_3par_iscsi.py, only the trunk ips
(192.168.68.203) are considered. If vlan ips (192.168.100.1)
are specified, they aren't considered while forming list of
iscsi ips.

Sample output of "showport" command from HPE 3par storage system:

CSSOS-SSA04 cli% showport -iscsivlans
N:S:P VLAN IPAddr        Netmask
0:2:1  -  192.168.68.203 255.255.192.0
0:2:1 100 192.168.100.1  255.255.192.0
0:2:1 200 192.168.100.2  255.255.255.0

Kindly refer launchpad bug for details.

This patch adds support for using vlan iscsi ips.

Closes-Bug: #2015034
Change-Id: Idaea18411ae03c5a9696b2ab67e84087177fcc74
This commit is contained in:
raghavendrat 2023-04-03 05:26:41 +00:00
parent 456b6399be
commit 4482ec892f
3 changed files with 184 additions and 28 deletions

View File

@ -8996,6 +8996,75 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
self.assertDictEqual(self.multipath_properties, result)
def test_initialize_connection_multipath_vlan_ip(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HPE3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
{'name': self.FAKE_HOST}]
mock_client.queryHost.return_value = {
'members': [{
'name': self.FAKE_HOST
}]
}
mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'),
[{'active': True,
'volumeName': self.VOLUME_3PAR_NAME,
'lun': self.TARGET_LUN, 'type': 0,
'portPos': {'node': 8, 'slot': 1, 'cardPort': 1}}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME,
'lun_id': self.TARGET_LUN,
'host': self.FAKE_HOST,
'nsp': 'something'})
mock_client.createVLUN.return_value = location
mock_client.getiSCSIPorts.return_value = [{
'IPAddr': '1.1.1.2',
'iSCSIName': self.TARGET_IQN,
'iSCSIVlans': [{'IPAddr': '192.168.100.1',
'iSCSIName': self.TARGET_IQN}]
}]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = copy.deepcopy(self.volume)
volume.replication_status = 'disabled'
result = self.driver.initialize_connection(
volume,
self.connector_multipath_enabled)
expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME),
mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
mock.call.getHost(self.FAKE_HOST),
mock.call.getiSCSIPorts(
state=self.mock_client_conf['PORT_STATE_READY']),
mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.createVLUN(
self.VOLUME_3PAR_NAME,
auto=True,
hostname=self.FAKE_HOST,
portPos=self.FAKE_ISCSI_PORT['portPos'],
lun=None),
mock.call.getHostVLUNs(self.FAKE_HOST)]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
self.assertDictEqual(self.multipath_properties, result)
def test_terminate_connection_for_clear_chap_creds_not_found(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
@ -10530,6 +10599,24 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
self.driver.initialize_iscsi_ports,
common)
def test_initialize_iscsi_ports_with_vlan_ip(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
conf = self.setup_configuration()
conf.hpe3par_iscsi_ips = ["192.168.100.1:1234"]
mock_client = self.setup_driver(config=conf)
mock_client.getPorts.return_value = PORTS_VLAN_RET
expected = [mock.call.getPorts()]
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
common = self.driver._login()
self.driver.initialize_iscsi_ports(common)
mock_client.assert_has_calls(expected)
def test_ensure_export(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client
@ -10882,6 +10969,19 @@ PORTS_RET = ({'members':
'HWAddr': '2C27D75375D6',
'type': 8}]})
PORTS_VLAN_RET = ({'members':
[{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.220.252',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21820002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8,
'iSCSIVlans': [{'IPAddr': '192.168.100.1'}],
}]})
VLUNS1_RET = ({'members':
[{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
'hostname': 'foo', 'active': True},

View File

@ -129,10 +129,11 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
4.0.4 - Added Peer Persistence feature
4.0.5 - Added Primera array check. bug #1849525
4.0.6 - Allow iSCSI support for Primera 4.2 onwards
4.0.7 - Use vlan iscsi ips. Bug #2015034
"""
VERSION = "4.0.6"
VERSION = "4.0.7"
# The name of the CI wiki page.
CI_WIKI_NAME = "HPE_Storage_CI"
@ -164,6 +165,13 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
finally:
self._logout(common)
def _update_dicts(self, temp_iscsi_ip, iscsi_ip_list, ip, port):
ip_port = temp_iscsi_ip[ip]['ip_port']
iscsi_ip_list[ip] = {'ip_port': ip_port,
'nsp': port['nsp'],
'iqn': port['iSCSIName']}
del temp_iscsi_ip[ip]
def initialize_iscsi_ports(self, common,
remote_target=None, remote_client=None):
# map iscsi_ip-> ip_port
@ -202,15 +210,20 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
# when found, add the valid iSCSI ip, ip port, iqn and nsp
# to the iSCSI IP dictionary
iscsi_ports = common.get_active_iscsi_target_ports(remote_client)
LOG.debug("iscsi_ports: %(iscsi_ports)s", {'iscsi_ports': iscsi_ports})
for port in iscsi_ports:
ip = port['IPAddr']
if ip in temp_iscsi_ip:
ip_port = temp_iscsi_ip[ip]['ip_port']
iscsi_ip_list[ip] = {'ip_port': ip_port,
'nsp': port['nsp'],
'iqn': port['iSCSIName']}
del temp_iscsi_ip[ip]
self._update_dicts(temp_iscsi_ip, iscsi_ip_list, ip, port)
if 'iSCSIVlans' in port:
for vip in port['iSCSIVlans']:
ip = vip['IPAddr']
if ip in temp_iscsi_ip:
LOG.debug("vlan ip: %(ip)s", {'ip': ip})
self._update_dicts(temp_iscsi_ip, iscsi_ip_list,
ip, port)
# if the single value iscsi_ip_address option is still in the
# temp dictionary it's because it defaults to $my_ip which doesn't
@ -237,6 +250,44 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
self.iscsi_ips[common._client_conf['hpe3par_api_url']] = (
iscsi_ip_list)
def _vlun_create_or_use_existing(self, volume, common, host, iscsi_ips,
target_portals, target_iqns,
target_luns, remote_client,
target_portal_ips,
existing_vluns, iscsi_ip,
lun_id, port):
vlun = None
# check for an already existing VLUN matching the
# nsp for this iSCSI IP. If one is found, use it
# instead of creating a new VLUN.
for v in existing_vluns:
portPos = common.build_portPos(
iscsi_ips[iscsi_ip]['nsp'])
if v['portPos'] == portPos:
vlun = v
break
else:
vlun = common.create_vlun(
volume, host, iscsi_ips[iscsi_ip]['nsp'],
lun_id=lun_id, remote_client=remote_client)
# This function is called multiple times (from a for loop).
# We want to use the same LUN ID for every port.
# For first port, lun_id is received as None.
# - assign lun_id = vlun['lun'] and return it.
# Thus for subsequent ports, that same lun_id is used
# in create_vlun() above.
if lun_id is None:
lun_id = vlun['lun']
iscsi_ip_port = "%s:%s" % (
iscsi_ip, iscsi_ips[iscsi_ip]['ip_port'])
target_portals.append(iscsi_ip_port)
target_iqns.append(port['iSCSIName'])
target_luns.append(vlun['lun'])
return lun_id
def _initialize_connection_common(self, volume, connector, common,
host, iscsi_ips, ready_ports,
target_portals, target_iqns, target_luns,
@ -255,30 +306,30 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
for port in ready_ports:
iscsi_ip = port['IPAddr']
if iscsi_ip in target_portal_ips:
vlun = None
# check for an already existing VLUN matching the
# nsp for this iSCSI IP. If one is found, use it
# instead of creating a new VLUN.
for v in existing_vluns:
portPos = common.build_portPos(
iscsi_ips[iscsi_ip]['nsp'])
if v['portPos'] == portPos:
vlun = v
break
else:
vlun = common.create_vlun(
volume, host, iscsi_ips[iscsi_ip]['nsp'],
lun_id=lun_id, remote_client=remote_client)
lun_id = (
self._vlun_create_or_use_existing(
volume, common, host, iscsi_ips,
target_portals, target_iqns,
target_luns, remote_client,
target_portal_ips,
existing_vluns, iscsi_ip,
lun_id, port))
# We want to use the same LUN ID for every port
if lun_id is None:
lun_id = vlun['lun']
if 'iSCSIVlans' in port:
for vip in port['iSCSIVlans']:
iscsi_ip = vip['IPAddr']
if iscsi_ip in target_portal_ips:
LOG.debug("vlan ip: %(ip)s", {'ip': iscsi_ip})
lun_id = (
self._vlun_create_or_use_existing(
volume, common, host, iscsi_ips,
target_portals, target_iqns,
target_luns, remote_client,
target_portal_ips,
existing_vluns, iscsi_ip,
lun_id, port))
iscsi_ip_port = "%s:%s" % (
iscsi_ip, iscsi_ips[iscsi_ip]['ip_port'])
target_portals.append(iscsi_ip_port)
target_iqns.append(port['iSCSIName'])
target_luns.append(vlun['lun'])
else:
LOG.warning("iSCSI IP: '%s' was not found in "
"hpe3par_iscsi_ips list defined in "

View File

@ -0,0 +1,5 @@
fixes:
- |
HPE 3PAR driver `Bug #2015034 <https://bugs.launchpad.net/cinder/+bug/2015034>`_:
Added handling for VLAN iscsi IPs in the 3PAR iSCSI driver.