Use target_portals/iqns/luns for alternative target information
This makes brick to use target_portals, target_iqns, target_luns to get alternative iSCSI targets list on fail-over. Change-Id: I672497e819c3bffd71a0b7c6074c7a946eb74c91 Implements: blueprint iscsi-alternative-portal
This commit is contained in:
parent
312386616c
commit
2ee70a0f89
@ -224,23 +224,27 @@ class ISCSIConnector(InitiatorConnector):
|
|||||||
super(ISCSIConnector, self).set_execute(execute)
|
super(ISCSIConnector, self).set_execute(execute)
|
||||||
self._linuxscsi.set_execute(execute)
|
self._linuxscsi.set_execute(execute)
|
||||||
|
|
||||||
def _iterate_multiple_targets(self, connection_properties, ips_iqns_luns):
|
def _iterate_all_targets(self, connection_properties):
|
||||||
for ip, iqn, lun in ips_iqns_luns:
|
for ip, iqn, lun in self._get_all_targets(connection_properties):
|
||||||
props = copy.deepcopy(connection_properties)
|
props = copy.deepcopy(connection_properties)
|
||||||
props['target_portal'] = ip
|
props['target_portal'] = ip
|
||||||
props['target_iqn'] = iqn
|
props['target_iqn'] = iqn
|
||||||
props['target_lun'] = lun
|
props['target_lun'] = lun
|
||||||
|
for key in ('target_portals', 'target_iqns', 'target_luns'):
|
||||||
|
props.pop(key, None)
|
||||||
yield props
|
yield props
|
||||||
|
|
||||||
def _alternative_targets(self, connection_properties):
|
def _get_all_targets(self, connection_properties):
|
||||||
return zip(connection_properties.get('target_alternative_portals', []),
|
if all([key in connection_properties for key in ('target_portals',
|
||||||
connection_properties.get('target_alternative_iqns', []),
|
'target_iqns',
|
||||||
connection_properties.get('target_alternative_luns', []))
|
'target_luns')]):
|
||||||
|
return zip(connection_properties['target_portals'],
|
||||||
|
connection_properties['target_iqns'],
|
||||||
|
connection_properties['target_luns'])
|
||||||
|
|
||||||
def _multipath_targets(self, connection_properties):
|
return [(connection_properties['target_portal'],
|
||||||
return zip(connection_properties.get('target_portals', []),
|
connection_properties['target_iqn'],
|
||||||
connection_properties.get('target_iqns', []),
|
connection_properties.get('target_lun', 0))]
|
||||||
connection_properties.get('target_luns', []))
|
|
||||||
|
|
||||||
def _discover_iscsi_portals(self, connection_properties):
|
def _discover_iscsi_portals(self, connection_properties):
|
||||||
if all([key in connection_properties for key in ('target_portals',
|
if all([key in connection_properties for key in ('target_portals',
|
||||||
@ -286,13 +290,10 @@ class ISCSIConnector(InitiatorConnector):
|
|||||||
host_devices = self._get_device_path(connection_properties)
|
host_devices = self._get_device_path(connection_properties)
|
||||||
else:
|
else:
|
||||||
target_props = connection_properties
|
target_props = connection_properties
|
||||||
if not self._connect_to_iscsi_portal(target_props):
|
for props in self._iterate_all_targets(connection_properties):
|
||||||
for props in self._iterate_multiple_targets(
|
if self._connect_to_iscsi_portal(props):
|
||||||
connection_properties,
|
target_props = props
|
||||||
self._alternative_targets(connection_properties)):
|
break
|
||||||
if self._connect_to_iscsi_portal(props):
|
|
||||||
target_props = props
|
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
LOG.warn(_LW(
|
LOG.warn(_LW(
|
||||||
'Failed to login to any of the iSCSI targets.'))
|
'Failed to login to any of the iSCSI targets.'))
|
||||||
@ -381,14 +382,7 @@ class ISCSIConnector(InitiatorConnector):
|
|||||||
|
|
||||||
# When multiple portals/iqns/luns are specified, we need to remove
|
# When multiple portals/iqns/luns are specified, we need to remove
|
||||||
# unused devices created by logging into other LUNs' session.
|
# unused devices created by logging into other LUNs' session.
|
||||||
ips_iqns_luns = self._multipath_targets(connection_properties)
|
for props in self._iterate_all_targets(connection_properties):
|
||||||
if not ips_iqns_luns:
|
|
||||||
ips_iqns_luns = ([[connection_properties['target_portal'],
|
|
||||||
connection_properties['target_iqn'],
|
|
||||||
connection_properties.get('target_lun', 0)]] +
|
|
||||||
self._alternative_targets(connection_properties))
|
|
||||||
for props in self._iterate_multiple_targets(connection_properties,
|
|
||||||
ips_iqns_luns):
|
|
||||||
self._disconnect_volume_iscsi(props)
|
self._disconnect_volume_iscsi(props)
|
||||||
|
|
||||||
def _disconnect_volume_iscsi(self, connection_properties):
|
def _disconnect_volume_iscsi(self, connection_properties):
|
||||||
@ -412,16 +406,8 @@ class ISCSIConnector(InitiatorConnector):
|
|||||||
self._disconnect_from_iscsi_portal(connection_properties)
|
self._disconnect_from_iscsi_portal(connection_properties)
|
||||||
|
|
||||||
def _get_device_path(self, connection_properties):
|
def _get_device_path(self, connection_properties):
|
||||||
multipath_targets = self._multipath_targets(connection_properties)
|
return ["/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" % x for x in
|
||||||
if multipath_targets:
|
self._get_all_targets(connection_properties)]
|
||||||
return ["/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" % x for x in
|
|
||||||
multipath_targets]
|
|
||||||
|
|
||||||
path = ("/dev/disk/by-path/ip-%(portal)s-iscsi-%(iqn)s-lun-%(lun)s" %
|
|
||||||
{'portal': connection_properties['target_portal'],
|
|
||||||
'iqn': connection_properties['target_iqn'],
|
|
||||||
'lun': connection_properties.get('target_lun', 0)})
|
|
||||||
return [path]
|
|
||||||
|
|
||||||
def get_initiator(self):
|
def get_initiator(self):
|
||||||
"""Secure helper to read file as root."""
|
"""Secure helper to read file as root."""
|
||||||
|
@ -278,11 +278,13 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
|
|||||||
@testtools.skipUnless(os.path.exists('/dev/disk/by-path'),
|
@testtools.skipUnless(os.path.exists('/dev/disk/by-path'),
|
||||||
'Test requires /dev/disk/by-path')
|
'Test requires /dev/disk/by-path')
|
||||||
def test_connect_volume_with_alternative_targets(self):
|
def test_connect_volume_with_alternative_targets(self):
|
||||||
|
location = '10.0.2.15:3260'
|
||||||
location2 = '10.0.3.15:3260'
|
location2 = '10.0.3.15:3260'
|
||||||
|
iqn = 'iqn.2010-10.org.openstack:volume-00000001'
|
||||||
iqn2 = 'iqn.2010-10.org.openstack:volume-00000001-2'
|
iqn2 = 'iqn.2010-10.org.openstack:volume-00000001-2'
|
||||||
extra_props = {'target_alternative_portals': [location2],
|
extra_props = {'target_portals': [location, location2],
|
||||||
'target_alternative_iqns': [iqn2],
|
'target_iqns': [iqn, iqn2],
|
||||||
'target_alternative_luns': [2]}
|
'target_luns': [1, 2]}
|
||||||
additional_commands = [('blockdev --flushbufs /dev/sdb'),
|
additional_commands = [('blockdev --flushbufs /dev/sdb'),
|
||||||
('tee -a /sys/block/sdb/device/delete'),
|
('tee -a /sys/block/sdb/device/delete'),
|
||||||
('iscsiadm -m node -T %s -p %s --op update'
|
('iscsiadm -m node -T %s -p %s --op update'
|
||||||
@ -307,9 +309,9 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
|
|||||||
iqn2 = 'iqn.2010-10.org.openstack:%s-2' % name
|
iqn2 = 'iqn.2010-10.org.openstack:%s-2' % name
|
||||||
vol = {'id': 1, 'name': name}
|
vol = {'id': 1, 'name': name}
|
||||||
connection_info = self.iscsi_connection(vol, location, iqn)
|
connection_info = self.iscsi_connection(vol, location, iqn)
|
||||||
connection_info['data']['target_alternative_portals'] = [location2]
|
connection_info['data']['target_portals'] = [location, location2]
|
||||||
connection_info['data']['target_alternative_iqns'] = [iqn2]
|
connection_info['data']['target_iqns'] = [iqn, iqn2]
|
||||||
connection_info['data']['target_alternative_luns'] = [2]
|
connection_info['data']['target_luns'] = [1, 2]
|
||||||
dev_str2 = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-2' % (location2, iqn2)
|
dev_str2 = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-2' % (location2, iqn2)
|
||||||
|
|
||||||
def fake_run_iscsiadm(iscsi_properties, iscsi_command, **kwargs):
|
def fake_run_iscsiadm(iscsi_properties, iscsi_command, **kwargs):
|
||||||
@ -324,6 +326,8 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
|
|||||||
self.assertEqual('block', device['type'])
|
self.assertEqual('block', device['type'])
|
||||||
self.assertEqual(dev_str2, device['path'])
|
self.assertEqual(dev_str2, device['path'])
|
||||||
props = connection_info['data'].copy()
|
props = connection_info['data'].copy()
|
||||||
|
for key in ('target_portals', 'target_iqns', 'target_luns'):
|
||||||
|
props.pop(key, None)
|
||||||
props['target_portal'] = location2
|
props['target_portal'] = location2
|
||||||
props['target_iqn'] = iqn2
|
props['target_iqn'] = iqn2
|
||||||
props['target_lun'] = 2
|
props['target_lun'] = 2
|
||||||
@ -333,6 +337,8 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
|
|||||||
mock_iscsiadm.reset_mock()
|
mock_iscsiadm.reset_mock()
|
||||||
self.connector.disconnect_volume(connection_info['data'], device)
|
self.connector.disconnect_volume(connection_info['data'], device)
|
||||||
props = connection_info['data'].copy()
|
props = connection_info['data'].copy()
|
||||||
|
for key in ('target_portals', 'target_iqns', 'target_luns'):
|
||||||
|
props.pop(key, None)
|
||||||
mock_iscsiadm.assert_any_call(props, ('--logout',),
|
mock_iscsiadm.assert_any_call(props, ('--logout',),
|
||||||
check_exit_code=[0, 21, 255])
|
check_exit_code=[0, 21, 255])
|
||||||
props['target_portal'] = location2
|
props['target_portal'] = location2
|
||||||
|
Loading…
Reference in New Issue
Block a user