IP Address: 192.168.4.53
""", 0)
- iscsi_connection_info = \
- {'data': {'target_discovered': True,
- 'target_iqn':
- 'iqn.1992-04.com.emc:cx.fnm00124000215.a4',
- 'target_lun': 2,
- 'target_portal': '10.244.214.118:3260',
- 'volume_id': '1'},
- 'driver_volume_type': 'iscsi'}
-
- iscsi_connection_info_mp = \
- {'data': {'target_discovered': True,
- 'target_iqns': [
- 'iqn.1992-04.com.emc:cx.fnm00124000215.a4',
- 'iqn.1992-04.com.emc:cx.fnm00124000215.a5'],
- 'target_luns': [2, 2],
- 'target_portals': [
- '10.244.214.118:3260',
- '10.244.214.119:3260'],
- 'volume_id': '1'},
- 'driver_volume_type': 'iscsi'}
+ iscsi_connection_info = {
+ 'data': {'target_discovered': True,
+ 'target_iqn':
+ 'iqn.1992-04.com.emc:cx.fnm00124000215.a4',
+ 'target_lun': 2,
+ 'target_portal': '10.244.214.118:3260',
+ 'target_iqns': ['iqn.1992-04.com.emc:cx.fnm00124000215.a4'],
+ 'target_luns': [2],
+ 'target_portals': ['10.244.214.118:3260'],
+ 'volume_id': '1'},
+ 'driver_volume_type': 'iscsi'}
+
+ iscsi_connection_info_mp = {
+ 'data': {'target_discovered': True,
+ 'target_iqns': [
+ 'iqn.1992-04.com.emc:cx.fnm00124000215.a4',
+ 'iqn.1992-04.com.emc:cx.fnm00124000215.a5'],
+ 'target_iqn': 'iqn.1992-04.com.emc:cx.fnm00124000215.a4',
+ 'target_luns': [2, 2],
+ 'target_lun': 2,
+ 'target_portals': [
+ '10.244.214.118:3260',
+ '10.244.214.119:3260'],
+ 'target_portal': '10.244.214.118:3260',
+ 'volume_id': '1'},
+ 'driver_volume_type': 'iscsi'}
PING_OK = ("Reply from 10.0.0.2: bytes=32 time=1ms TTL=30\n" +
"Reply from 10.0.0.2: bytes=32 time=1ms TTL=30\n" +
@mock.patch('random.randint',
mock.Mock(return_value=0))
+ @mock.patch('cinder.volume.drivers.emc.emc_vnx_cli.'
+ 'CommandLineHelper.ping_node',
+ mock.Mock(return_value=True))
+ @mock.patch('random.shuffle', mock.Mock(return_value=0))
def test_initialize_connection_multipath(self):
self.configuration.initiator_auto_registration = False
except NotImplementedError:
self.fail('Interface unmanage need to be implemented')
+ @mock.patch("random.shuffle", mock.Mock())
+ def test_find_available_iscsi_targets_without_pingnode(self):
+ self.configuration.iscsi_initiators = None
+ self.driverSetup()
+ port_a1 = {'Port WWN': 'fake_iqn_a1',
+ 'SP': 'A',
+ 'Port ID': 1,
+ 'Virtual Port ID': 0,
+ 'IP Address': 'fake_ip_a1'}
+ port_a2 = {'Port WWN': 'fake_iqn_a2',
+ 'SP': 'A',
+ 'Port ID': 2,
+ 'Virtual Port ID': 0,
+ 'IP Address': 'fake_ip_a2'}
+ port_b1 = {'Port WWN': 'fake_iqn_b1',
+ 'SP': 'B',
+ 'Port ID': 1,
+ 'Virtual Port ID': 0,
+ 'IP Address': 'fake_ip_b1'}
+ all_targets = {'A': [port_a1, port_a2],
+ 'B': [port_b1]}
+ targets = self.driver.cli._client.find_available_iscsi_targets(
+ 'fakehost',
+ 'B',
+ {('A', 2), ('B', 1)},
+ all_targets)
+ self.assertEqual([port_b1, port_a2], targets)
+
+ @mock.patch("random.shuffle", mock.Mock())
+ @mock.patch.object(emc_vnx_cli.CommandLineHelper,
+ 'ping_node')
+ def test_find_available_iscsi_targets_with_pingnode(self, ping_node):
+ self.configuration.iscsi_initiators = (
+ '{"fakehost": ["10.0.0.2"]}')
+ self.driverSetup()
+ port_a1 = {'Port WWN': 'fake_iqn_a1',
+ 'SP': 'A',
+ 'Port ID': 1,
+ 'Virtual Port ID': 0,
+ 'IP Address': 'fake_ip_a1'}
+ port_a2 = {'Port WWN': 'fake_iqn_a2',
+ 'SP': 'A',
+ 'Port ID': 2,
+ 'Virtual Port ID': 0,
+ 'IP Address': 'fake_ip_a2'}
+ port_b1 = {'Port WWN': 'fake_iqn_b1',
+ 'SP': 'B',
+ 'Port ID': 1,
+ 'Virtual Port ID': 0,
+ 'IP Address': 'fake_ip_b1'}
+ all_targets = {'A': [port_a1, port_a2],
+ 'B': [port_b1]}
+ ping_node.side_effect = [False, True]
+ targets = self.driver.cli._client.find_available_iscsi_targets(
+ 'fakehost',
+ 'B',
+ {('A', 2), ('A', 1), ('B', 1)},
+ all_targets)
+ self.assertEqual([port_a1, port_b1, port_a2], targets)
+ ping_node.side_effect = [False, False, True]
+ targets = self.driver.cli._client.find_available_iscsi_targets(
+ 'fakehost',
+ 'B',
+ {('A', 2), ('A', 1), ('B', 1)},
+ all_targets)
+ self.assertEqual([port_a2, port_b1, port_a1], targets)
+
+ @mock.patch('cinder.volume.drivers.emc.emc_vnx_cli.'
+ 'EMCVnxCliBase.get_lun_owner',
+ mock.Mock(return_value='A'))
+ @mock.patch('cinder.volume.drivers.emc.emc_vnx_cli.'
+ 'CommandLineHelper.get_registered_spport_set',
+ mock.Mock())
+ @mock.patch.object(emc_vnx_cli.CommandLineHelper,
+ 'find_available_iscsi_targets')
+ def test_vnx_get_iscsi_properties(self, find_available_iscsi_targets):
+ self.driverSetup()
+ port_a1 = {'Port WWN': 'fake_iqn_a1',
+ 'SP': 'A',
+ 'Port ID': 1,
+ 'Virtual Port ID': 0,
+ 'IP Address': 'fake_ip_a1'}
+ port_b1 = {'Port WWN': 'fake_iqn_b1',
+ 'SP': 'B',
+ 'Port ID': 1,
+ 'Virtual Port ID': 0,
+ 'IP Address': 'fake_ip_b1'}
+ find_available_iscsi_targets.return_value = [port_a1, port_b1]
+ connect_info = self.driver.cli.vnx_get_iscsi_properties(
+ self.testData.test_volume, self.testData.connector, 1, '')
+ expected_info = {
+ 'target_discovered': True,
+ 'target_iqns': [
+ 'fake_iqn_a1',
+ 'fake_iqn_b1'],
+ 'target_iqn': 'fake_iqn_a1',
+ 'target_luns': [1, 1],
+ 'target_lun': 1,
+ 'target_portals': [
+ 'fake_ip_a1:3260',
+ 'fake_ip_b1:3260'],
+ 'target_portal': 'fake_ip_a1:3260',
+ 'volume_id': '1'}
+ self.assertEqual(expected_info, connect_info)
+
class EMCVNXCLIDArrayBasedDriverTestCase(DriverTestCaseBase):
def setUp(self):
def find_available_iscsi_targets(self, hostname,
preferred_sp,
registered_spport_set,
- all_iscsi_targets,
- multipath=False):
+ all_iscsi_targets):
+ """Finds available iscsi targets for a host.
+
+ When the iscsi_initiator_map is configured, the driver will find
+ an accessible portal and put it as the first portal in the portal
+ list to ensure the accessible portal will be used when multipath
+ is not used. All the registered portals will be returned for Nova
+ to clean up all the unused devices related to this LUN created by
+ logging into these portals during attaching other LUNs on VNX.
+ """
+
if self.iscsi_initiator_map and hostname in self.iscsi_initiator_map:
iscsi_initiator_ips = list(self.iscsi_initiator_map[hostname])
random.shuffle(iscsi_initiator_ips)
else:
iscsi_initiator_ips = None
+
# Check the targets on the owner first
if preferred_sp == 'A':
target_sps = ('A', 'B')
else:
target_sps = ('B', 'A')
- if multipath:
- target_portals = []
- for target_sp in target_sps:
- sp_portals = all_iscsi_targets[target_sp]
- for portal in sp_portals:
- spport = (portal['SP'], portal['Port ID'])
- if spport not in registered_spport_set:
- LOG.debug("Skip SP Port %(port)s since "
- "no path from %(host)s is through it",
- {'port': spport,
- 'host': hostname})
- continue
- target_portals.append(portal)
- return target_portals
-
+ target_portals = []
for target_sp in target_sps:
- target_portals = list(all_iscsi_targets[target_sp])
- random.shuffle(target_portals)
- for target_portal in target_portals:
- spport = (target_portal['SP'], target_portal['Port ID'])
+ sp_portals = all_iscsi_targets[target_sp]
+ random.shuffle(sp_portals)
+ for portal in sp_portals:
+ spport = (portal['SP'], portal['Port ID'])
if spport not in registered_spport_set:
- LOG.debug("Skip SP Port %(port)s since "
- "no path from %(host)s is through it",
- {'port': spport,
- 'host': hostname})
+ LOG.debug(
+ "Skip SP Port %(port)s since "
+ "no path from %(host)s is through it.",
+ {'port': spport,
+ 'host': hostname})
continue
- if iscsi_initiator_ips is not None:
- for initiator_ip in iscsi_initiator_ips:
- if self.ping_node(target_portal, initiator_ip):
- return [target_portal]
+ target_portals.append(portal)
+
+ main_portal_index = None
+ if iscsi_initiator_ips:
+ for i, portal in enumerate(target_portals):
+ for initiator_ip in iscsi_initiator_ips:
+ if self.ping_node(portal, initiator_ip):
+ main_portal_index = i
+ break
else:
- LOG.debug("No iSCSI IP address of %(hostname)s is known. "
- "Return a random target portal %(portal)s.",
- {'hostname': hostname,
- 'portal': target_portal})
- return [target_portal]
+ # Else for the for loop. If there is no main portal found,
+ # continue to try next initiator IP.
+ continue
+ break
- return None
+ if main_portal_index is not None:
+ target_portals.insert(0, target_portals.pop(main_portal_index))
+ return target_portals
def _is_sp_unavailable_error(self, out):
error_pattern = '(^Error.*Message.*End of data stream.*)|'\
def vnx_get_iscsi_properties(self, volume, connector, hlu, sg_raw_output):
storage_group = connector['host']
- multipath = connector.get('multipath', False)
owner_sp = self.get_lun_owner(volume)
registered_spports = self._client.get_registered_spport_set(
connector['initiator'],
targets = self._client.find_available_iscsi_targets(
storage_group, owner_sp,
registered_spports,
- self.iscsi_targets,
- multipath)
-
- properties = {}
-
- if not multipath:
- properties = {'target_discovered': False,
- 'target_iqn': 'unknown',
- 'target_portal': 'unknown',
- 'target_lun': 'unknown',
- 'volume_id': volume['id']}
- if targets:
- properties['target_discovered'] = True
- properties['target_iqn'] = targets[0]['Port WWN']
- properties['target_portal'] = \
- "%s:3260" % targets[0]['IP Address']
- properties['target_lun'] = hlu
+ self.iscsi_targets)
+ properties = {'target_discovered': False,
+ 'target_iqn': 'unknown',
+ 'target_iqns': None,
+ 'target_portal': 'unknown',
+ 'target_portals': None,
+ 'target_lun': 'unknown',
+ 'target_luns': None,
+ 'volume_id': volume['id']}
+ if targets:
+ properties['target_discovered'] = True
+ properties['target_iqns'] = [t['Port WWN'] for t in targets]
+ properties['target_iqn'] = properties['target_iqns'][0]
+ properties['target_portals'] = [
+ "%s:3260" % t['IP Address'] for t in targets]
+ properties['target_portal'] = properties['target_portals'][0]
+ properties['target_luns'] = [hlu] * len(targets)
+ properties['target_lun'] = hlu
else:
- properties = {'target_discovered': False,
- 'target_iqns': None,
- 'target_portals': None,
- 'target_luns': None,
- 'volume_id': volume['id']}
- if targets:
- properties['target_discovered'] = True
- properties['target_iqns'] = [t['Port WWN'] for t in targets]
- properties['target_portals'] = [
- "%s:3260" % t['IP Address'] for t in targets]
- properties['target_luns'] = [hlu] * len(targets)
-
- if not targets:
LOG.error(_LE('Failed to find available iSCSI targets for %s.'),
storage_group)
+ LOG.debug('The iSCSI properties for %(host)s is %(properties)s.',
+ {'host': storage_group,
+ 'properties': properties})
return properties
def vnx_get_fc_properties(self, connector, device_number):