Adding CHAP discovery logic to os-brick

The changes that were made to libvirt volume drivers to use
os-brick, left out the code that provided credentials to discover
iSCSI devices

Essentially, the code responsible for updating iscsiadm's CHAP
authentication parameters was left out

Closes-Bug: #1481914

Change-Id: I3fa0132247374d89b9fdcfdba3e9b1eac502a2ad
This commit is contained in:
Jose Porrua 2015-08-06 13:45:55 -04:00
parent d99fb09ee3
commit 2ff5d8edca
3 changed files with 127 additions and 11 deletions

View File

@ -104,3 +104,7 @@ class VolumePathNotRemoved(BrickException):
class ProtocolNotSupported(BrickException):
message = _("Connect to volume via protocol %(protocol)s not supported.")
class TargetPortalNotFound(BrickException):
message = _("Unable to find target portal %(target_portal)s.")

View File

@ -339,18 +339,60 @@ class ISCSIConnector(InitiatorConnector):
return zip(connection_properties['target_portals'],
connection_properties['target_iqns'])
# Discover and return every available target
out = self._run_iscsiadm_bare(['-m',
'discovery',
'-t',
'sendtargets',
'-p',
connection_properties['target_portal']],
check_exit_code=[0, 255])[0] \
or ""
out = None
if connection_properties.get('discovery_auth_method'):
try:
self._run_iscsiadm_update_discoverydb(connection_properties)
except putils.ProcessExecutionError as exception:
# iscsiadm returns 6 for "db record not found"
if exception.exit_code == 6:
# Create a new record for this target and update the db
self._run_iscsiadm_bare(
['-m', 'discoverydb',
'-t', 'sendtargets',
'-p', connection_properties['target_portal'],
'--op', 'new'],
check_exit_code=[0, 255])
self._run_iscsiadm_update_discoverydb(
connection_properties
)
else:
LOG.error(_LE("Unable to find target portal: "
"%(target_portal)s."),
{'target_portal': connection_properties[
'target_portal']})
raise
out = self._run_iscsiadm_bare(
['-m', 'discoverydb',
'-t', 'sendtargets',
'-p', connection_properties['target_portal'],
'--discover'],
check_exit_code=[0, 255])[0] or ""
else:
out = self._run_iscsiadm_bare(
['-m', 'discovery',
'-t', 'sendtargets',
'-p', connection_properties['target_portal']],
check_exit_code=[0, 255])[0] or ""
return self._get_target_portals_from_iscsiadm_output(out)
def _run_iscsiadm_update_discoverydb(self, connection_properties):
return self._execute(
'iscsiadm',
'-m', 'discoverydb',
'-t', 'sendtargets',
'-p', connection_properties['target_portal'],
'--op', 'update',
'-n', "discovery.sendtargets.auth.authmethod",
'-v', connection_properties['discovery_auth_method'],
'-n', "discovery.sendtargets.auth.username",
'-v', connection_properties['discovery_auth_username'],
'-n', "discovery.sendtargets.auth.password",
'-v', connection_properties['discovery_auth_password'],
run_as_root=True,
root_helper=self._root_helper)
@synchronized('connect_volume')
def connect_volume(self, connection_properties):
"""Attach the volume to instance_name.
@ -366,7 +408,11 @@ class ISCSIConnector(InitiatorConnector):
if self.use_multipath:
# Multipath installed, discovering other targets if available
ips_iqns = self._discover_iscsi_portals(connection_properties)
try:
ips_iqns = self._discover_iscsi_portals(connection_properties)
except Exception:
raise exception.TargetPortalNotFound(
target_portal=connection_properties['target_portal'])
if not connection_properties.get('target_iqns'):
# There are two types of iSCSI multipath devices. One which

View File

@ -228,6 +228,26 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
}
}
def iscsi_connection_chap(self, volume, location, iqn, auth_method,
auth_username, auth_password,
discovery_auth_method, discovery_auth_username,
discovery_auth_password):
return {
'driver_volume_type': 'iscsi',
'data': {
'auth_method': auth_method,
'auth_username': auth_username,
'auth_password': auth_password,
'discovery_auth_method': discovery_auth_method,
'discovery_auth_username': discovery_auth_username,
'discovery_auth_password': discovery_auth_password,
'target_lun': 1,
'volume_id': volume['id'],
'target_iqn': iqn,
'target_portal': location,
}
}
def test_get_initiator(self):
def initiator_no_file(*args, **kwargs):
raise putils.ProcessExecutionError('No file')
@ -427,6 +447,52 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
'type': 'block'}
self.assertEqual(result, expected_result)
@mock.patch.object(connector.ISCSIConnector,
'_run_iscsiadm_update_discoverydb')
@mock.patch.object(os.path, 'exists', return_value=True)
def test_iscsi_portals_with_chap_discovery(
self, exists, update_discoverydb):
location = '10.0.2.15:3260'
name = 'volume-00000001'
iqn = 'iqn.2010-10.org.openstack:%s' % name
vol = {'id': 1, 'name': name}
auth_method = 'CHAP'
auth_username = 'fake_chap_username'
auth_password = 'fake_chap_password'
discovery_auth_method = 'CHAP'
discovery_auth_username = 'fake_chap_username'
discovery_auth_password = 'fake_chap_password'
connection_properties = self.iscsi_connection_chap(
vol, location, iqn, auth_method, auth_username, auth_password,
discovery_auth_method, discovery_auth_username,
discovery_auth_password)
self.connector_with_multipath = connector.ISCSIConnector(
None, execute=self.fake_execute, use_multipath=True)
self.cmds = []
# The first call returns an error code = 6, mocking an empty
# discovery db. The second one mocks a successful return and the
# third one a dummy exit code, which will trigger the
# TargetPortalNotFound exception in connect_volume
update_discoverydb.side_effect = [
putils.ProcessExecutionError(None, None, 6),
("", ""),
putils.ProcessExecutionError(None, None, 9)]
self.connector_with_multipath._discover_iscsi_portals(
connection_properties['data'])
update_discoverydb.assert_called_with(connection_properties['data'])
expected_cmds = [
'iscsiadm -m discoverydb -t sendtargets -p %s --op new' %
location,
'iscsiadm -m discoverydb -t sendtargets -p %s --discover' %
location]
self.assertEqual(expected_cmds, self.cmds)
self.assertRaises(exception.TargetPortalNotFound,
self.connector_with_multipath.connect_volume,
connection_properties['data'])
@mock.patch.object(os.path, 'exists', return_value=True)
@mock.patch.object(host_driver.HostDriver, 'get_all_block_devices')
@mock.patch.object(connector.ISCSIConnector, '_get_multipath_device_map',
@ -1055,7 +1121,7 @@ class AoEConnectorTestCase(ConnectorTestCase):
@mock.patch.object(os.path, 'exists', side_effect=[True, True])
def test_connect_volume(self, exists_mock):
"""Ensure that if path exist aoe-revaliadte was called."""
"""Ensure that if path exist aoe-revalidate was called."""
aoe_device, aoe_path = self.connector._get_aoe_info(
self.connection_properties)
with mock.patch.object(self.connector, '_execute',