[SVF]:Fix multiple lshost calls during attach.

[Spectrum Virtualize Family] During attach or detach operation
there is multiple lshost calls from "get_host_from_connector",
which is causing some delay during attach or detach operations.

This patch fixes the issue by caching the host information during
initialization and searching for host from the cached information.

Closes-Bug: #1913363
Change-Id: I73e58f8b0f4908e9fb097f7ce151a9178cf6b96f
This commit is contained in:
GirishChilukuri 2021-01-27 04:43:16 +00:00
parent ac6a5c51e1
commit 0dc4daa214
5 changed files with 152 additions and 49 deletions

View File

@ -176,6 +176,9 @@ class StorwizeSVCManagementSimulator(object):
'CMMVC8783E': ('', 'CMMVC8783E The volume copy was not deleted '
'because the volume is part of a consistency '
'group.'),
'CMMVC6578E': ('', 'CMMVC6578E The command has failed because '
'the iSCSI name is already assigned or is '
'not valid.'),
}
self._fc_transitions = {'begin': {'make': 'idle_or_copied'},
'idle_or_copied': {'prepare': 'preparing',
@ -1207,7 +1210,10 @@ port_speed!N/A
continue
for port in v[added_key]:
if port == added_val:
return self._errors['CMMVC6581E']
error = 'CMMVC6035E'
if 'iscsiname' in kwargs:
error = 'CMMVC6578E'
return self._errors[error]
return ('', '')
# Make a host
@ -3262,7 +3268,10 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase):
snapshot,
connector)
def test_storwize_initialize_iscsi_connection_with_host_site(self):
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'initialize_host_info')
def test_storwize_initialize_iscsi_connection_with_host_site(self,
init_host):
connector = {'host': 'storwize-svc-host',
'wwnns': ['20000090fa17311e', '20000090fa17311f'],
'wwpns': ['ff00000000000000', 'ff00000000000001'],
@ -3277,6 +3286,8 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase):
volume_iSCSI_2 = self._create_volume()
volume_iSCSI_2['volume_type_id'] = vol_type_iSCSI['id']
self.iscsi_driver.initialize_connection(volume_iSCSI, connector)
init_host.assert_called()
self.assertEqual(1, init_host.call_count)
host_name = self.iscsi_driver._helpers.get_host_from_connector(
connector, iscsi=True)
host_info = self.iscsi_driver._helpers.ssh.lshost(host=host_name)
@ -3335,7 +3346,10 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase):
volume_iSCSI, connector)
term_conn.assert_called_once_with(volume_iSCSI, connector)
def test_storwize_terminate_iscsi_connection_multi_attach(self):
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'initialize_host_info')
def test_storwize_terminate_iscsi_connection_multi_attach(self,
init_host_info):
# create an iSCSI volume
volume_iSCSI = self._create_volume()
extra_spec = {'capabilities:storage_protocol': '<in> iSCSI'}
@ -3353,6 +3367,7 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase):
# map and unmap the volume to two hosts normal case
self.iscsi_driver.initialize_connection(volume_iSCSI, connector)
init_host_info.assert_called()
self.iscsi_driver.initialize_connection(volume_iSCSI, connector2)
for conn in [connector, connector2]:
host = self.iscsi_driver._helpers.get_host_from_connector(
@ -3380,6 +3395,7 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase):
rmmap.side_effect = Exception('boom')
self.iscsi_driver.terminate_connection(volume_iSCSI,
connector2)
init_host_info.assert_called()
host_name = self.iscsi_driver._helpers.get_host_from_connector(
connector2, iscsi=True)
self.assertIsNone(host_name)
@ -4399,12 +4415,6 @@ class StorwizeSVCFcDriverTestCase(test.TestCase):
# Check bad output from lsfabric for the 2nd volume
if protocol == 'FC' and self.USESIM:
for error in ['remove_field', 'header_mismatch']:
self.sim.error_injection('lsfabric', error)
self.assertRaises(exception.VolumeBackendAPIException,
self.fc_driver.initialize_connection,
volume2, self._connector)
with mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_conn_fc_wwpns') as conn_fc_wwpns:
conn_fc_wwpns.return_value = []
@ -9714,17 +9724,17 @@ class StorwizeHelpersTestCase(test.TestCase):
access=access)
startrcrelationship.assert_called_once_with(opts['RC_name'], None)
@mock.patch.object(storwize_svc_common.StorwizeSSH, 'lsfabric')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_host_from_host_info')
@mock.patch.object(storwize_svc_common.StorwizeSSH, 'lshost')
@mock.patch.object(storwize_svc_common.StorwizeSSH, 'lsvdiskhostmap')
def test_get_host_from_connector_with_vol(self,
lsvdishosmap,
lshost,
lsfabric):
get_host_from_host_info):
vol = 'testvol'
connector = {"wwpns": ["10000090fa3870d7", "C050760825191B00"]}
lsfabric.return_value = [{"remote_wwpn": "10000090fa3870d7",
"name": "test_host"}]
get_host_from_host_info.return_value = "test_host", []
raw = "id!name!host_id!host_name!vdisk_UID\n2594!testvol!315!"\
"test_host!60050768028110A4700000000001168E"
ssh_cmd = ['svcinfo', 'lsvdiskhostmap', '-delim', '!', '"%s"' % vol]
@ -9735,23 +9745,23 @@ class StorwizeHelpersTestCase(test.TestCase):
host = self.storwize_svc_common.get_host_from_connector(connector,
vol)
self.assertEqual(host, "test_host")
lsfabric.assert_called_with(wwpn='10000090fa3870d7')
self.assertEqual(1, lsfabric.call_count)
get_host_from_host_info.assert_called_with(connector, False)
self.assertEqual(1, get_host_from_host_info.call_count)
lsvdishosmap.assert_called_with(vol)
self.assertEqual(1, lsvdishosmap.call_count)
lshost.assert_not_called()
@mock.patch.object(storwize_svc_common.StorwizeSSH, 'lsfabric')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_host_from_host_info')
@mock.patch.object(storwize_svc_common.StorwizeSSH, 'lshost')
@mock.patch.object(storwize_svc_common.StorwizeSSH, 'lsvdiskhostmap')
def test_get_host_from_connector_wo_vol(self,
lsvdishosmap,
lshost,
lsfabric):
get_host_from_host_info):
vol = 'testvol'
connector = {"wwpns": ["10000090fa3870d7", "C050760825191B00"]}
lsfabric.return_value = [{"remote_wwpn": "10000090fa3870d7",
"name": "test_host"}]
get_host_from_host_info.return_value = "test_host", []
raw = "id!name!host_id!host_name!vdisk_UID\n2594!testvol!315!"\
"test_host!60050768028110A4700000000001168E"
ssh_cmd = ['svcinfo', 'lsvdiskhostmap', '-delim', '!', '"%s"' % vol]
@ -9761,8 +9771,8 @@ class StorwizeHelpersTestCase(test.TestCase):
True)
host = self.storwize_svc_common.get_host_from_connector(connector)
self.assertEqual(host, "test_host")
lsfabric.assert_called_with(wwpn='10000090fa3870d7')
self.assertEqual(1, lsfabric.call_count)
get_host_from_host_info.assert_called_with(connector, False)
self.assertEqual(1, get_host_from_host_info.call_count)
lsvdishosmap.assert_not_called()
self.assertEqual(0, lsvdishosmap.call_count)
lshost.assert_not_called()
@ -12553,11 +12563,14 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
self.driver._sync_with_aux_grp(self.ctxt, rccg_name)
startrccg.assert_called_with(rccg_name, 'aux')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'initialize_host_info')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'get_host_from_connector')
@mock.patch.object(storwize_svc_common.StorwizeHelpers,
'check_vol_mapped_to_host')
def test_get_map_info_from_connector(self, is_mapped, get_host_from_conn):
def test_get_map_info_from_connector(self, is_mapped, get_host_from_conn,
initialize_host_info):
self.driver.configuration.set_override('replication_device',
[self.rep_target])
self.driver.do_setup(self.ctxt)
@ -12584,6 +12597,8 @@ class StorwizeSVCReplicationTestCase(test.TestCase):
backend_helper)
self.assertEqual(self.driver._master_state,
node_state)
initialize_host_info.assert_called()
self.assertEqual(1, initialize_host_info.call_count)
connector = {'host': 'storwize-svc-host',
'wwnns': ['20000090fa17311e', '20000090fa17311f'],

View File

@ -788,6 +788,7 @@ class StorwizeHelpers(object):
self.check_fcmapping_interval = 3
self.code_level = None
self.stats = {}
self.Host_connector_info = {"FC": {}, "ISCSI": {}}
@staticmethod
def handle_keyerror(cmd, out):
@ -1121,28 +1122,48 @@ class StorwizeHelpers(object):
wwpns.add(wwpn)
return list(wwpns)
def initialize_host_info(self):
"""Get the host,wwpn,iscsi and store in Host_connector_info."""
if (not self.Host_connector_info['FC'] and
not self.Host_connector_info['ISCSI']):
hosts_info = self.ssh.lshost()
host_list = list(hosts_info.select('name'))
for eachhost in host_list:
resp = self.ssh.lshost(host=eachhost)
if list(resp.select("WWPN")) != [None]:
for wwpn in resp.select('WWPN'):
if wwpn not in self.Host_connector_info['FC'].keys():
self.Host_connector_info['FC'][wwpn] = eachhost
elif list(resp.select('iscsi_name')) != [None]:
for iscsi_name in resp.select('iscsi_name'):
if (iscsi_name not in
self.Host_connector_info['ISCSI'].keys()):
self.Host_connector_info['ISCSI'][iscsi_name] = (
eachhost)
def get_host_from_host_info(self, connector, iscsi=False):
host_name = None
new_wwpn = []
if iscsi and 'initiator' in connector:
if connector['initiator'] in self.Host_connector_info['ISCSI']:
iqn = connector['initiator']
host_name = self.Host_connector_info['ISCSI'][iqn]
elif 'wwpns' in connector:
for wwpn in connector['wwpns']:
if wwpn.upper() in self.Host_connector_info['FC']:
host_name = self.Host_connector_info['FC'][wwpn.upper()]
else:
new_wwpn.append(['wwpn', '%s' % wwpn])
return host_name, new_wwpn
def get_host_from_connector(self, connector, volume_name=None,
iscsi=False):
"""Return the Storwize host described by the connector."""
LOG.debug('Enter: get_host_from_connector: %s.', connector)
# If we have FC information, we have a faster lookup option
host_name = None
if 'wwpns' in connector and not iscsi:
for wwpn in connector['wwpns']:
resp = self.ssh.lsfabric(wwpn=wwpn)
for wwpn_info in resp:
try:
if (wwpn_info['remote_wwpn'] and
wwpn_info['name'] and
wwpn_info['remote_wwpn'].lower() ==
wwpn.lower()):
host_name = wwpn_info['name']
break
except KeyError:
self.handle_keyerror('lsfabric', wwpn_info)
if host_name:
break
host_name, new_wwpn = self.get_host_from_host_info(connector, iscsi)
if host_name and volume_name:
hosts_map_info = self.ssh.lsvdiskhostmap(volume_name)
@ -1158,6 +1179,11 @@ class StorwizeHelpers(object):
host_name = None
if host_name:
for port in new_wwpn:
LOG.debug('update wwpn %(wwpn)s to host %(host)s.',
{'wwpn': port, 'host': host_name})
self.ssh.addhostport(host_name, port[0], port[1])
LOG.debug('Leave: get_host_from_connector: host %s.', host_name)
return host_name
@ -1288,6 +1314,13 @@ class StorwizeHelpers(object):
for port in ports:
self.ssh.addhostport(host_name, port[0], port[1])
if iscsi and 'initiator' in connector:
iqn = connector['initiator']
self.Host_connector_info['ISCSI'][iqn] = host_name
elif 'wwpns' in connector:
for wwpn in connector['wwpns']:
self.Host_connector_info['FC'][wwpn.upper()] = host_name
LOG.debug('Leave: create_host: host %(host)s - %(host_name)s.',
{'host': connector['host'], 'host_name': host_name})
return host_name
@ -1298,6 +1331,23 @@ class StorwizeHelpers(object):
def delete_host(self, host_name):
self.ssh.rmhost(host_name)
if host_name in self.Host_connector_info['ISCSI'].values():
host_iqn = None
for iqn, host in self.Host_connector_info['ISCSI'].items():
if host == host_name:
host_iqn = iqn
break
if host_iqn:
self.Host_connector_info['ISCSI'].pop(host_iqn)
elif host_name in self.Host_connector_info['FC'].values():
host_wwpn = []
for wwpn, host in self.Host_connector_info['FC'].items():
if host == host_name:
host_wwpn.append(wwpn)
for wwpn in host_wwpn:
self.Host_connector_info['FC'].pop(wwpn)
def _get_unused_lun_id(self, host_name):
luns_used = []
result_lun = '-1'
@ -4852,6 +4902,8 @@ class StorwizeSVCCommonDriver(san.SanDriver,
vol_name, backend_helper, node_state = self._get_vol_sys_info(
volume)
backend_helper.initialize_host_info()
info = {}
if 'host' in connector:
# get host according to FC protocol

View File

@ -172,6 +172,8 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
volume)
host_site = None
backend_helper.initialize_host_info()
is_hyper_volume = self.is_volume_hyperswap(volume)
if is_hyper_volume:
host_site = self._get_volume_host_site_from_conf(volume,
@ -183,12 +185,24 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver):
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
# Check if a host object is defined for this host name
host_name = backend_helper.get_host_from_connector(connector)
if host_name is None:
# Host does not exist - add a new host to Storwize/SVC
# Try creating the host, if host creation is successfull continue
# with intialization flow, else search for host object defined for
# this connector info.
host_name = None
try:
host_name = backend_helper.create_host(connector, site=host_site)
elif is_hyper_volume:
except exception.VolumeBackendAPIException as excp:
if "CMMVC6035E" in excp.msg:
msg = (_('Host already exists for connector '
'%(conn)s'), {'conn': connector})
LOG.info(msg)
host_name = backend_helper.get_host_from_connector(connector)
else:
msg = (_('Error creating host %(ex)s'), {'ex': excp.msg})
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
if is_hyper_volume:
self._update_host_site_for_hyperswap_volume(host_name, host_site)
volume_attributes = backend_helper.get_vdisk_attributes(volume_name)

View File

@ -166,6 +166,8 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
volume_name, backend_helper, node_state = self._get_vol_sys_info(
volume)
backend_helper.initialize_host_info()
host_site = self._get_volume_host_site_from_conf(volume,
connector,
iscsi=True)
@ -176,14 +178,26 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
# Check if a host object is defined for this host name
host_name = backend_helper.get_host_from_connector(connector,
iscsi=True)
if host_name is None:
# Host does not exist - add a new host to Storwize/SVC
# Try creating the host, if host creation is successfull continue
# with intialization flow, else search for host object defined for
# this connector info.
host_name = None
try:
host_name = backend_helper.create_host(connector, iscsi=True,
site=host_site)
elif is_hyper_volume:
except exception.VolumeBackendAPIException as excp:
if "CMMVC6578E" in excp.msg:
msg = (_('Host already exists for connector '
'%(conn)s'), {'conn': connector})
LOG.info(msg)
host_name = backend_helper.get_host_from_connector(connector,
iscsi=True)
else:
msg = (_('Error creating host %(ex)s'), {'ex': excp.msg})
LOG.error(msg)
raise exception.VolumeDriverException(message=msg)
if is_hyper_volume:
self._update_host_site_for_hyperswap_volume(host_name, host_site)
chap_secret = backend_helper.get_chap_secret_for_host(host_name)

View File

@ -0,0 +1,8 @@
---
fixes:
- |
IBM Spectrum Virtualize Family driver `Bug #1913363
<https://bugs.launchpad.net/cinder/+bug/1913363>`_: Fixed
issue in get_host_from_connector by caching the host
information during attach or detach operations and using
host details from cached information.