Fix silent iSCSI login failures

This patch fixes an issue where every iSCSI login attempt fails
and the code doesn't raise an exception.  It used to silently fail
with nothing but a log warning.

The iSCSI connector now checks to make sure that at least 1 of the
N potential target portals login attempts is successful.

Change-Id: Id248c3f1b12385c9d26150c6aa9dd504c01d0df8
Closes-Bug: #1464988
This commit is contained in:
Walter A. Boring IV 2015-10-06 15:15:35 -07:00 committed by Walter A. Boring IV (hemna)
parent dc8f8a170f
commit 166820ad11
3 changed files with 62 additions and 2 deletions

View File

@ -108,3 +108,7 @@ class ProtocolNotSupported(BrickException):
class TargetPortalNotFound(BrickException):
message = _("Unable to find target portal %(target_portal)s.")
class FailedISCSITargetPortalLogin(BrickException):
message = _("Unable to login to iSCSI Target Portal")

View File

@ -446,6 +446,7 @@ class ISCSIConnector(InitiatorConnector):
device_info = {'type': 'block'}
connected_to_portal = False
if self.use_multipath:
# Multipath installed, discovering other targets if available
try:
@ -474,7 +475,8 @@ class ISCSIConnector(InitiatorConnector):
props = copy.deepcopy(connection_properties)
props['target_portal'] = ip
props['target_iqn'] = iqn
self._connect_to_iscsi_portal(props)
if self._connect_to_iscsi_portal(props):
connected_to_portal = True
self._rescan_iscsi()
host_devices = self._get_device_path(connection_properties)
@ -483,13 +485,19 @@ class ISCSIConnector(InitiatorConnector):
for props in self._iterate_all_targets(connection_properties):
if self._connect_to_iscsi_portal(props):
target_props = props
connected_to_portal = True
host_devices = self._get_device_path(target_props)
break
else:
LOG.warning(_LW(
'Failed to connect to iSCSI portal %(portal)s.'),
{'portal': props['target_portal']})
host_devices = self._get_device_path(target_props)
# make sure we've logged into an iSCSI portal
if not connected_to_portal:
msg = _("Could not login to any iSCSI portal.")
LOG.error(msg)
raise exception.FailedISCSITargetPortalLogin(message=msg)
# The /dev/disk/by-path/... node is not always present immediately
# TODO(justinsb): This retry-with-delay is a pattern, move to utils?

View File

@ -706,6 +706,54 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
self.assertEqual(expected_result, result)
self.assertEqual(expected_calls, mock_connect.call_args_list)
@mock.patch.object(os.path, 'exists', return_value=True)
@mock.patch.object(connector.ISCSIConnector,
'_get_target_portals_from_iscsiadm_output')
@mock.patch.object(connector.ISCSIConnector, '_connect_to_iscsi_portal')
@mock.patch.object(host_driver.HostDriver, 'get_all_block_devices')
@mock.patch.object(connector.ISCSIConnector, '_get_iscsi_devices')
@mock.patch.object(connector.ISCSIConnector, '_rescan_multipath')
@mock.patch.object(connector.ISCSIConnector, '_run_multipath')
@mock.patch.object(connector.ISCSIConnector, '_get_multipath_device_name')
def test_connect_volume_multipath_failed_iscsi_login(
self, mock_device_name, mock_run_multipath,
mock_rescan_multipath, mock_iscsi_devices, mock_devices,
mock_connect, mock_portals, mock_exists):
location1 = '10.0.2.15:3260'
location2 = '10.0.3.15:3260'
name1 = 'volume-00000001-1'
name2 = 'volume-00000001-2'
iqn1 = 'iqn.2010-10.org.openstack:%s' % name1
iqn2 = 'iqn.2010-10.org.openstack:%s' % name2
fake_multipath_dev = '/dev/mapper/fake-multipath-dev'
vol = {'id': 1, 'name': name1}
connection_properties = self.iscsi_connection(vol, location1, iqn1)
devs = ['/dev/disk/by-path/ip-%s-iscsi-%s-lun-1' % (location1, iqn1),
'/dev/disk/by-path/ip-%s-iscsi-%s-lun-2' % (location2, iqn2)]
mock_devices.return_value = devs
mock_iscsi_devices.return_value = devs
mock_device_name.return_value = fake_multipath_dev
mock_portals.return_value = [[location1, iqn1], [location2, iqn1],
[location2, iqn2]]
mock_connect.return_value = False
self.assertRaises(exception.FailedISCSITargetPortalLogin,
self.connector_with_multipath.connect_volume,
connection_properties['data'])
@mock.patch.object(connector.ISCSIConnector, '_connect_to_iscsi_portal')
def test_connect_volume_failed_iscsi_login(self, mock_connect):
location1 = '10.0.2.15:3260'
name1 = 'volume-00000001-1'
iqn1 = 'iqn.2010-10.org.openstack:%s' % name1
vol = {'id': 1, 'name': name1}
connection_properties = self.iscsi_connection(vol, location1, iqn1)
mock_connect.return_value = False
self.assertRaises(exception.FailedISCSITargetPortalLogin,
self.connector.connect_volume,
connection_properties['data'])
@mock.patch.object(time, 'sleep')
@mock.patch.object(os.path, 'exists', return_value=False)
def test_connect_volume_with_not_found_device(self, exists_mock,