diff --git a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py index 4bdebf4ad64..3bf2e3761c3 100644 --- a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py +++ b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py @@ -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': ' 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': ' 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': ' 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): diff --git a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py index 819f071590c..94af0f10ffb 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py +++ b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py @@ -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 diff --git a/releasenotes/notes/storwize_iscsi_multipath_enhance-9de9dc29661869cd.yaml b/releasenotes/notes/storwize_iscsi_multipath_enhance-9de9dc29661869cd.yaml new file mode 100644 index 00000000000..1579a893d97 --- /dev/null +++ b/releasenotes/notes/storwize_iscsi_multipath_enhance-9de9dc29661869cd.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add multipath enhancement to Storwize iSCSI driver.