Add multipath enhancement to Storwize iSCSI driver
Add multipath enhancement to Storwize iSCSI driver. target portals, iqns and luns will be returned in initialize_connection if multipath is being used. Change-Id: Ie9bcb94f95effee7f6407aabe268cc0637a602f3 Implements: blueprint storwize-iscsi-multipath-enhancement
This commit is contained in:
parent
41b516234f
commit
ad59cb5ac2
@ -573,47 +573,52 @@ port_speed!N/A
|
||||
else:
|
||||
ip_addr1 = '1.234.56.78'
|
||||
ip_addr2 = '1.234.56.79'
|
||||
ip_addr3 = '1.234.56.80'
|
||||
ip_addr4 = '1.234.56.81'
|
||||
gw = '1.234.56.1'
|
||||
|
||||
rows = [None] * 17
|
||||
rows[0] = ['id', 'node_id', 'node_name', 'IP_address', 'mask',
|
||||
'gateway', 'IP_address_6', 'prefix_6', 'gateway_6', 'MAC',
|
||||
'duplex', 'state', 'speed', 'failover']
|
||||
'duplex', 'state', 'speed', 'failover', 'link_state']
|
||||
rows[1] = ['1', '1', 'node1', ip_addr1, '255.255.255.0',
|
||||
gw, '', '', '', '01:23:45:67:89:00', 'Full',
|
||||
'online', '1Gb/s', 'no']
|
||||
'online', '1Gb/s', 'no', 'active']
|
||||
rows[2] = ['1', '1', 'node1', '', '', '', '', '', '',
|
||||
'01:23:45:67:89:00', 'Full', 'online', '1Gb/s', 'yes']
|
||||
rows[3] = ['2', '1', 'node1', '', '', '', '', '', '',
|
||||
'01:23:45:67:89:01', 'Full', 'unconfigured', '1Gb/s', 'no']
|
||||
'01:23:45:67:89:00', 'Full', 'online', '1Gb/s', 'yes', '']
|
||||
rows[3] = ['2', '1', 'node1', ip_addr3, '255.255.255.0',
|
||||
gw, '', '', '', '01:23:45:67:89:01', 'Full',
|
||||
'configured', '1Gb/s', 'no', 'active']
|
||||
rows[4] = ['2', '1', 'node1', '', '', '', '', '', '',
|
||||
'01:23:45:67:89:01', 'Full', 'unconfigured', '1Gb/s', 'yes']
|
||||
'01:23:45:67:89:01', 'Full', 'unconfigured', '1Gb/s',
|
||||
'yes', 'inactive']
|
||||
rows[5] = ['3', '1', 'node1', '', '', '', '', '', '', '', '',
|
||||
'unconfigured', '', 'no']
|
||||
'unconfigured', '', 'no', '']
|
||||
rows[6] = ['3', '1', 'node1', '', '', '', '', '', '', '', '',
|
||||
'unconfigured', '', 'yes']
|
||||
'unconfigured', '', 'yes', '']
|
||||
rows[7] = ['4', '1', 'node1', '', '', '', '', '', '', '', '',
|
||||
'unconfigured', '', 'no']
|
||||
'unconfigured', '', 'no', '']
|
||||
rows[8] = ['4', '1', 'node1', '', '', '', '', '', '', '', '',
|
||||
'unconfigured', '', 'yes']
|
||||
'unconfigured', '', 'yes', '']
|
||||
rows[9] = ['1', '2', 'node2', ip_addr2, '255.255.255.0',
|
||||
gw, '', '', '', '01:23:45:67:89:02', 'Full',
|
||||
'online', '1Gb/s', 'no']
|
||||
'online', '1Gb/s', 'no', '']
|
||||
rows[10] = ['1', '2', 'node2', '', '', '', '', '', '',
|
||||
'01:23:45:67:89:02', 'Full', 'online', '1Gb/s', 'yes']
|
||||
rows[11] = ['2', '2', 'node2', '', '', '', '', '', '',
|
||||
'01:23:45:67:89:03', 'Full', 'unconfigured', '1Gb/s', 'no']
|
||||
'01:23:45:67:89:02', 'Full', 'online', '1Gb/s', 'yes', '']
|
||||
rows[11] = ['2', '2', 'node2', ip_addr4, '255.255.255.0',
|
||||
gw, '', '', '', '01:23:45:67:89:03', 'Full',
|
||||
'configured', '1Gb/s', 'no', 'inactive']
|
||||
rows[12] = ['2', '2', 'node2', '', '', '', '', '', '',
|
||||
'01:23:45:67:89:03', 'Full', 'unconfigured', '1Gb/s',
|
||||
'yes']
|
||||
'yes', '']
|
||||
rows[13] = ['3', '2', 'node2', '', '', '', '', '', '', '', '',
|
||||
'unconfigured', '', 'no']
|
||||
'unconfigured', '', 'no', '']
|
||||
rows[14] = ['3', '2', 'node2', '', '', '', '', '', '', '', '',
|
||||
'unconfigured', '', 'yes']
|
||||
'unconfigured', '', 'yes', '']
|
||||
rows[15] = ['4', '2', 'node2', '', '', '', '', '', '', '', '',
|
||||
'unconfigured', '', 'no']
|
||||
'unconfigured', '', 'no', '']
|
||||
rows[16] = ['4', '2', 'node2', '', '', '', '', '', '', '', '',
|
||||
'unconfigured', '', 'yes']
|
||||
'unconfigured', '', 'yes', '']
|
||||
|
||||
if self._next_cmd_error['lsportip'] == 'header_mismatch':
|
||||
rows[0].pop(2)
|
||||
@ -1884,21 +1889,18 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase):
|
||||
|
||||
def _generate_vol_info(self, vol_name, vol_id):
|
||||
pool = _get_test_pool()
|
||||
rand_id = six.text_type(random.randint(10000, 99999))
|
||||
prop = {'mdisk_grp_name': pool}
|
||||
if vol_name:
|
||||
return {'name': 'snap_volume%s' % rand_id,
|
||||
'volume_name': vol_name,
|
||||
'id': rand_id,
|
||||
'volume_id': vol_id,
|
||||
'volume_size': 10,
|
||||
'mdisk_grp_name': pool}
|
||||
prop.update(volume_name=vol_name,
|
||||
volume_id=vol_id,
|
||||
volume_size=10)
|
||||
else:
|
||||
return {'name': 'test_volume%s' % rand_id,
|
||||
'size': 10,
|
||||
'id': rand_id,
|
||||
'volume_type_id': None,
|
||||
'mdisk_grp_name': pool,
|
||||
'host': 'openstack@svc#%s' % pool}
|
||||
prop.update(size=10,
|
||||
volume_type_id=None,
|
||||
mdisk_grp_name=pool,
|
||||
host='openstack@svc#%s' % pool)
|
||||
vol = testutils.create_volume(self.ctxt, **prop)
|
||||
return vol
|
||||
|
||||
def _assert_vol_exists(self, name, exists):
|
||||
is_vol_defined = self.iscsi_driver._helpers.is_vdisk_defined(name)
|
||||
@ -2040,6 +2042,102 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase):
|
||||
host = self.iscsi_driver._helpers.get_host_from_connector(conn)
|
||||
self.assertIsNone(host)
|
||||
|
||||
def test_storwize_initialize_iscsi_connection_single_path(self):
|
||||
# Test the return value for _get_iscsi_properties
|
||||
|
||||
connector = {'host': 'storwize-svc-host',
|
||||
'wwnns': ['20000090fa17311e', '20000090fa17311f'],
|
||||
'wwpns': ['ff00000000000000', 'ff00000000000001'],
|
||||
'initiator': 'iqn.1993-08.org.debian:01:eac5ccc1aaa'}
|
||||
# Expected single path host-volume map return value
|
||||
exp_s_path = {'driver_volume_type': 'iscsi',
|
||||
'data': {'target_discovered': False,
|
||||
'target_iqn':
|
||||
'iqn.1982-01.com.ibm:1234.sim.node1',
|
||||
'target_portal': '1.234.56.78:3260',
|
||||
'target_lun': 0,
|
||||
'auth_method': 'CHAP',
|
||||
'discovery_auth_method': 'CHAP'}}
|
||||
|
||||
volume_iSCSI = self._create_volume()
|
||||
extra_spec = {'capabilities:storage_protocol': '<in> iSCSI'}
|
||||
vol_type_iSCSI = volume_types.create(self.ctxt, 'iSCSI', extra_spec)
|
||||
volume_iSCSI['volume_type_id'] = vol_type_iSCSI['id']
|
||||
|
||||
# Make sure that the volumes have been created
|
||||
self._assert_vol_exists(volume_iSCSI['name'], True)
|
||||
|
||||
# Check case where no hosts exist
|
||||
ret = self.iscsi_driver._helpers.get_host_from_connector(
|
||||
connector)
|
||||
self.assertIsNone(ret)
|
||||
|
||||
# Initialize connection to map volume to a host
|
||||
ret = self.iscsi_driver.initialize_connection(
|
||||
volume_iSCSI, connector)
|
||||
self.assertEqual(exp_s_path['driver_volume_type'],
|
||||
ret['driver_volume_type'])
|
||||
|
||||
# Check the single path host-volume map return value
|
||||
for k, v in exp_s_path['data'].items():
|
||||
self.assertEqual(v, ret['data'][k])
|
||||
|
||||
ret = self.iscsi_driver._helpers.get_host_from_connector(
|
||||
connector)
|
||||
self.assertIsNotNone(ret)
|
||||
|
||||
def test_storwize_initialize_iscsi_connection_multipath(self):
|
||||
# Test the return value for _get_iscsi_properties
|
||||
|
||||
connector = {'host': 'storwize-svc-host',
|
||||
'wwnns': ['20000090fa17311e', '20000090fa17311f'],
|
||||
'wwpns': ['ff00000000000000', 'ff00000000000001'],
|
||||
'initiator': 'iqn.1993-08.org.debian:01:eac5ccc1aaa',
|
||||
'multipath': True}
|
||||
|
||||
# Expected multipath host-volume map return value
|
||||
exp_m_path = {'driver_volume_type': 'iscsi',
|
||||
'data': {'target_discovered': False,
|
||||
'target_iqn':
|
||||
'iqn.1982-01.com.ibm:1234.sim.node1',
|
||||
'target_portal': '1.234.56.78:3260',
|
||||
'target_lun': 0,
|
||||
'target_iqns': [
|
||||
'iqn.1982-01.com.ibm:1234.sim.node1',
|
||||
'iqn.1982-01.com.ibm:1234.sim.node1',
|
||||
'iqn.1982-01.com.ibm:1234.sim.node2'],
|
||||
'target_portals':
|
||||
['1.234.56.78:3260',
|
||||
'1.234.56.80:3260',
|
||||
'1.234.56.79:3260'],
|
||||
'target_luns': [0, 0, 0],
|
||||
'auth_method': 'CHAP',
|
||||
'discovery_auth_method': 'CHAP'}}
|
||||
|
||||
volume_iSCSI = self._create_volume()
|
||||
extra_spec = {'capabilities:storage_protocol': '<in> iSCSI'}
|
||||
vol_type_iSCSI = volume_types.create(self.ctxt, 'iSCSI', extra_spec)
|
||||
volume_iSCSI['volume_type_id'] = vol_type_iSCSI['id']
|
||||
|
||||
# Check case where no hosts exist
|
||||
ret = self.iscsi_driver._helpers.get_host_from_connector(
|
||||
connector)
|
||||
self.assertIsNone(ret)
|
||||
|
||||
# Initialize connection to map volume to a host
|
||||
ret = self.iscsi_driver.initialize_connection(
|
||||
volume_iSCSI, connector)
|
||||
self.assertEqual(exp_m_path['driver_volume_type'],
|
||||
ret['driver_volume_type'])
|
||||
|
||||
# Check the multipath host-volume map return value
|
||||
for k, v in exp_m_path['data'].items():
|
||||
self.assertEqual(v, ret['data'][k])
|
||||
|
||||
ret = self.iscsi_driver._helpers.get_host_from_connector(
|
||||
connector)
|
||||
self.assertIsNotNone(ret)
|
||||
|
||||
def test_storwize_svc_iscsi_host_maps(self):
|
||||
# Create two volumes to be used in mappings
|
||||
|
||||
@ -2329,21 +2427,18 @@ class StorwizeSVCFcDriverTestCase(test.TestCase):
|
||||
|
||||
def _generate_vol_info(self, vol_name, vol_id):
|
||||
pool = _get_test_pool()
|
||||
rand_id = six.text_type(random.randint(10000, 99999))
|
||||
prop = {'mdisk_grp_name': pool}
|
||||
if vol_name:
|
||||
return {'name': 'snap_volume%s' % rand_id,
|
||||
'volume_name': vol_name,
|
||||
'id': rand_id,
|
||||
'volume_id': vol_id,
|
||||
'volume_size': 10,
|
||||
'mdisk_grp_name': pool}
|
||||
prop.update(volume_name=vol_name,
|
||||
volume_id=vol_id,
|
||||
volume_size=10)
|
||||
else:
|
||||
return {'name': 'test_volume%s' % rand_id,
|
||||
'size': 10,
|
||||
'id': '%s' % rand_id,
|
||||
'volume_type_id': None,
|
||||
'mdisk_grp_name': pool,
|
||||
'host': 'openstack@svc#%s' % pool}
|
||||
prop.update(size=10,
|
||||
volume_type_id=None,
|
||||
mdisk_grp_name=pool,
|
||||
host='openstack@svc#%s' % pool)
|
||||
vol = testutils.create_volume(self.ctxt, **prop)
|
||||
return vol
|
||||
|
||||
def _assert_vol_exists(self, name, exists):
|
||||
is_vol_defined = self.fc_driver._helpers.is_vdisk_defined(name)
|
||||
@ -3178,21 +3273,18 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
||||
|
||||
def _generate_vol_info(self, vol_name, vol_id):
|
||||
pool = _get_test_pool()
|
||||
rand_id = six.text_type(random.randint(10000, 99999))
|
||||
prop = {'mdisk_grp_name': pool}
|
||||
if vol_name:
|
||||
return {'name': 'snap_volume%s' % rand_id,
|
||||
'volume_name': vol_name,
|
||||
'id': rand_id,
|
||||
'volume_id': vol_id,
|
||||
'volume_size': 10,
|
||||
'mdisk_grp_name': pool}
|
||||
prop.update(volume_name=vol_name,
|
||||
volume_id=vol_id,
|
||||
volume_size=10)
|
||||
else:
|
||||
return {'name': 'test_volume%s' % rand_id,
|
||||
'size': 10,
|
||||
'id': '%s' % rand_id,
|
||||
'volume_type_id': None,
|
||||
'mdisk_grp_name': pool,
|
||||
'host': 'openstack@svc#%s' % pool}
|
||||
prop.update(size=10,
|
||||
volume_type_id=None,
|
||||
mdisk_grp_name=pool,
|
||||
host='openstack@svc#%s' % pool)
|
||||
vol = testutils.create_volume(self.ctxt, **prop)
|
||||
return vol
|
||||
|
||||
def _create_volume(self, **kwargs):
|
||||
pool = _get_test_pool()
|
||||
@ -3264,10 +3356,9 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
||||
ctxt = testutils.get_test_admin_context()
|
||||
type_ref = volume_types.create(ctxt, 'testtype', opts)
|
||||
volume = self._generate_vol_info(None, None)
|
||||
type_id = type_ref['id']
|
||||
type_ref = volume_types.get_volume_type(ctxt, type_id)
|
||||
volume['volume_type_id'] = type_id
|
||||
volume['volume_type'] = type_ref
|
||||
volume.volume_type_id = type_ref['id']
|
||||
volume.volume_typ = objects.VolumeType.get_by_id(ctxt,
|
||||
type_ref['id'])
|
||||
self.driver.create_volume(volume)
|
||||
|
||||
attrs = self.driver._helpers.get_vdisk_attributes(volume['name'])
|
||||
@ -3603,8 +3694,14 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
||||
|
||||
def test_storwize_svc_volume_name(self):
|
||||
# Create a volume with space in name
|
||||
volume = self._generate_vol_info(None, None)
|
||||
volume['name'] = 'volume_ space'
|
||||
pool = _get_test_pool()
|
||||
rand_id = six.text_type(random.randint(10000, 99999))
|
||||
volume = {'name': 'volume_ space',
|
||||
'size': 10,
|
||||
'id': '%s' % rand_id,
|
||||
'volume_type_id': None,
|
||||
'mdisk_grp_name': pool,
|
||||
'host': 'openstack@svc#%s' % pool}
|
||||
self.driver.create_volume(volume)
|
||||
self.driver.ensure_export(None, volume)
|
||||
|
||||
@ -3682,12 +3779,7 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
||||
self.driver.do_setup(None)
|
||||
|
||||
rand_id = random.randint(10000, 99999)
|
||||
pool = _get_test_pool()
|
||||
volume1 = {'name': u'unicode1_volume%s' % rand_id,
|
||||
'size': 2,
|
||||
'id': 1,
|
||||
'volume_type_id': None,
|
||||
'host': 'openstack@svc#%s' % pool}
|
||||
volume1 = self._generate_vol_info(None, None)
|
||||
self.driver.create_volume(volume1)
|
||||
self._assert_vol_exists(volume1['name'], True)
|
||||
|
||||
@ -3839,10 +3931,9 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
||||
ctxt = testutils.get_test_admin_context()
|
||||
type_ref = volume_types.create(ctxt, 'testtype', None)
|
||||
volume = self._generate_vol_info(None, None)
|
||||
type_id = type_ref['id']
|
||||
type_ref = volume_types.get_volume_type(ctxt, type_id)
|
||||
volume['volume_type_id'] = type_id
|
||||
volume['volume_type'] = type_ref
|
||||
volume.volume_type_id = type_ref['id']
|
||||
volume.volume_type = objects.VolumeType.get_by_id(ctxt,
|
||||
type_ref['id'])
|
||||
self.driver.create_volume(volume)
|
||||
self.assertEqual(volume['mdisk_grp_name'],
|
||||
self.driver.get_pool(volume))
|
||||
@ -4071,10 +4162,12 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
||||
new_type_ref['id'])
|
||||
|
||||
volume = self._generate_vol_info(None, None)
|
||||
old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
|
||||
old_type = objects.VolumeType.get_by_id(ctxt,
|
||||
old_type_ref['id'])
|
||||
volume['volume_type'] = old_type
|
||||
volume['host'] = host['host']
|
||||
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
|
||||
new_type = objects.VolumeType.get_by_id(ctxt,
|
||||
new_type_ref['id'])
|
||||
|
||||
self.driver.create_volume(volume)
|
||||
self.driver.retype(ctxt, volume, new_type, diff, host)
|
||||
@ -4161,10 +4254,12 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
||||
new_type_ref['id'])
|
||||
|
||||
volume = self._generate_vol_info(None, None)
|
||||
old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
|
||||
old_type = objects.VolumeType.get_by_id(ctxt,
|
||||
old_type_ref['id'])
|
||||
volume['volume_type'] = old_type
|
||||
volume['host'] = host['host']
|
||||
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
|
||||
new_type = objects.VolumeType.get_by_id(ctxt,
|
||||
new_type_ref['id'])
|
||||
|
||||
self.driver.create_volume(volume)
|
||||
self.driver.retype(ctxt, volume, new_type, diff, host)
|
||||
@ -4196,10 +4291,12 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
||||
new_type_ref['id'])
|
||||
|
||||
volume = self._generate_vol_info(None, None)
|
||||
old_type = volume_types.get_volume_type(ctxt, old_type_ref['id'])
|
||||
old_type = objects.VolumeType.get_by_id(ctxt,
|
||||
old_type_ref['id'])
|
||||
volume['volume_type'] = old_type
|
||||
volume['host'] = host['host']
|
||||
new_type = volume_types.get_volume_type(ctxt, new_type_ref['id'])
|
||||
new_type = objects.VolumeType.get_by_id(ctxt,
|
||||
new_type_ref['id'])
|
||||
|
||||
self.driver.create_volume(volume)
|
||||
self.driver.retype(ctxt, volume, new_type, diff, host)
|
||||
@ -4803,9 +4900,8 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase):
|
||||
spec = {'capabilities:replication': '<is> False'}
|
||||
type_ref = volume_types.create(self.ctxt, "replication_2", spec)
|
||||
|
||||
replication_type = volume_types.get_volume_type(self.ctxt,
|
||||
replication_type = objects.VolumeType.get_by_id(self.ctxt,
|
||||
type_ref['id'])
|
||||
|
||||
return replication_type
|
||||
|
||||
def _create_consistency_group_volume_type(self):
|
||||
|
@ -147,71 +147,17 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
|
||||
LOG.warning(_LW('CHAP secret exists for host but CHAP is '
|
||||
'disabled.'))
|
||||
|
||||
volume_attributes = self._helpers.get_vdisk_attributes(volume_name)
|
||||
if volume_attributes is None:
|
||||
msg = (_('initialize_connection: Failed to get attributes'
|
||||
' for volume %s.') % volume_name)
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeDriverException(message=msg)
|
||||
|
||||
multihostmap = self.configuration.storwize_svc_multihostmap_enabled
|
||||
lun_id = self._helpers.map_vol_to_host(volume_name, host_name,
|
||||
multihostmap)
|
||||
try:
|
||||
preferred_node = volume_attributes['preferred_node_id']
|
||||
IO_group = volume_attributes['IO_group_id']
|
||||
except KeyError as e:
|
||||
LOG.error(_LE('Did not find expected column name in '
|
||||
'lsvdisk: %s.'), e)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=_('initialize_connection: Missing volume attribute for '
|
||||
'volume %s.') % volume_name)
|
||||
|
||||
try:
|
||||
# Get preferred node and other nodes in I/O group
|
||||
preferred_node_entry = None
|
||||
io_group_nodes = []
|
||||
for node in self._state['storage_nodes'].values():
|
||||
if self.protocol not in node['enabled_protocols']:
|
||||
continue
|
||||
if node['id'] == preferred_node:
|
||||
preferred_node_entry = node
|
||||
if node['IO_group'] == IO_group:
|
||||
io_group_nodes.append(node)
|
||||
|
||||
if not len(io_group_nodes):
|
||||
msg = (_('initialize_connection: No node found in '
|
||||
'I/O group %(gid)s for volume %(vol)s.') %
|
||||
{'gid': IO_group, 'vol': volume_name})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
if not preferred_node_entry:
|
||||
# Get 1st node in I/O group
|
||||
preferred_node_entry = io_group_nodes[0]
|
||||
LOG.warning(_LW('initialize_connection: Did not find a '
|
||||
'preferred node for volume %s.'), volume_name)
|
||||
|
||||
properties = {}
|
||||
properties['target_discovered'] = False
|
||||
properties['target_lun'] = lun_id
|
||||
properties['volume_id'] = volume['id']
|
||||
|
||||
if len(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:
|
||||
properties['auth_method'] = 'CHAP'
|
||||
properties['auth_username'] = connector['initiator']
|
||||
properties['auth_password'] = chap_secret
|
||||
properties['discovery_auth_method'] = 'CHAP'
|
||||
properties['discovery_auth_username'] = (
|
||||
connector['initiator'])
|
||||
properties['discovery_auth_password'] = chap_secret
|
||||
|
||||
properties = self._get_single_iscsi_data(volume, connector,
|
||||
lun_id, chap_secret)
|
||||
multipath = connector.get('multipath', False)
|
||||
if multipath:
|
||||
properties = self._get_multi_iscsi_data(volume, connector,
|
||||
lun_id, properties)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self._do_terminate_connection(volume, connector)
|
||||
@ -222,12 +168,138 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver):
|
||||
'conn': connector})
|
||||
|
||||
LOG.debug('leave: initialize_connection:\n volume: %(vol)s\n '
|
||||
'connector %(conn)s\n properties: %(prop)s',
|
||||
'connector: %(conn)s\n properties: %(prop)s',
|
||||
{'vol': volume['id'], 'conn': connector,
|
||||
'prop': properties})
|
||||
|
||||
return {'driver_volume_type': 'iscsi', 'data': properties, }
|
||||
|
||||
def _get_single_iscsi_data(self, volume, connector, lun_id, chap_secret):
|
||||
LOG.debug('enter: _get_single_iscsi_data: volume %(vol)s with '
|
||||
'connector %(conn)s lun_id %(lun_id)s',
|
||||
{'vol': volume['id'], 'conn': connector,
|
||||
'lun_id': lun_id})
|
||||
|
||||
volume_name = volume.name
|
||||
volume_attributes = self._helpers.get_vdisk_attributes(volume_name)
|
||||
if volume_attributes is None:
|
||||
msg = (_('_get_single_iscsi_data: Failed to get attributes'
|
||||
' for volume %s.') % volume_name)
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeDriverException(message=msg)
|
||||
|
||||
try:
|
||||
preferred_node = volume_attributes['preferred_node_id']
|
||||
IO_group = volume_attributes['IO_group_id']
|
||||
except KeyError as e:
|
||||
msg = (_('_get_single_iscsi_data: Did not find expected column'
|
||||
' name in %(volume)s: %(key)s %(error)s.'),
|
||||
{'volume': volume_name, 'key': e.args[0],
|
||||
'error': e})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
# Get preferred node and other nodes in I/O group
|
||||
preferred_node_entry = None
|
||||
io_group_nodes = []
|
||||
for node in self._state['storage_nodes'].values():
|
||||
if self.protocol not in node['enabled_protocols']:
|
||||
continue
|
||||
|
||||
if node['IO_group'] != IO_group:
|
||||
continue
|
||||
io_group_nodes.append(node)
|
||||
if node['id'] == preferred_node:
|
||||
preferred_node_entry = node
|
||||
|
||||
if not len(io_group_nodes):
|
||||
msg = (_('_get_single_iscsi_data: No node found in '
|
||||
'I/O group %(gid)s for volume %(vol)s.') % {
|
||||
'gid': IO_group, 'vol': volume_name})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
if not preferred_node_entry:
|
||||
# Get 1st node in I/O group
|
||||
preferred_node_entry = io_group_nodes[0]
|
||||
LOG.warning(_LW('_get_single_iscsi_data: Did not find a '
|
||||
'preferred node for volume %s.'), volume_name)
|
||||
|
||||
properties = {
|
||||
'target_discovered': False,
|
||||
'target_lun': lun_id,
|
||||
'volume_id': volume.id}
|
||||
|
||||
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:
|
||||
properties.update(auth_method='CHAP',
|
||||
auth_username=connector['initiator'],
|
||||
auth_password=chap_secret,
|
||||
discovery_auth_method='CHAP',
|
||||
discovery_auth_username=connector['initiator'],
|
||||
discovery_auth_password= chap_secret)
|
||||
LOG.debug('leave: _get_single_iscsi_data:\n volume: %(vol)s\n '
|
||||
'connector: %(conn)s\n lun_id: %(lun_id)s\n '
|
||||
'properties: %(prop)s',
|
||||
{'vol': volume.id, 'conn': connector, 'lun_id': lun_id,
|
||||
'prop': properties})
|
||||
return properties
|
||||
|
||||
def _get_multi_iscsi_data(self, volume, connector, lun_id, properties):
|
||||
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 = self._helpers.ssh.lsportip()
|
||||
except Exception as ex:
|
||||
msg = (_('_get_multi_iscsi_data: Failed to '
|
||||
'get port ip because of exception: '
|
||||
'%s.') % ex)
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
properties['target_iqns'] = []
|
||||
properties['target_portals'] = []
|
||||
properties['target_luns'] = []
|
||||
for node in self._state['storage_nodes'].values():
|
||||
for ip_data in resp:
|
||||
if ip_data['node_id'] != node['id']:
|
||||
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 valid_port:
|
||||
properties['target_portals'].append(
|
||||
'%s:%s' % (valid_port, '3260'))
|
||||
properties['target_iqns'].append(
|
||||
node['iscsi_name'])
|
||||
properties['target_luns'].append(lun_id)
|
||||
|
||||
if not len(properties['target_portals']):
|
||||
msg = (_('_get_multi_iscsi_data: Failed to find valid port '
|
||||
'for volume %s.') % volume.name)
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
LOG.debug('leave: _get_multi_iscsi_data:\n volume: %(vol)s\n '
|
||||
'connector: %(conn)s\n lun_id: %(lun_id)s\n '
|
||||
'properties: %(prop)s',
|
||||
{'vol': volume.id, 'conn': connector, 'lun_id': lun_id,
|
||||
'prop': properties})
|
||||
|
||||
return properties
|
||||
|
||||
def terminate_connection(self, volume, connector, **kwargs):
|
||||
"""Cleanup after an iSCSI connection has been terminated."""
|
||||
# If a fake connector is generated by nova when the host
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Add multipath enhancement to Storwize iSCSI driver.
|
Loading…
Reference in New Issue
Block a user