Add CHAP credentials support
Currently, the Hyper-V driver ignores the according CHAP credentials when logging in iSCSI targets. For this reason, attaching volumes fails when CHAP authentication is enforced. This patch adds one way CHAP authentication support. Change-Id: Id4d8800ae08dc24a01fc146f65c68b0b41d49e3d
This commit is contained in:
parent
8accd1cb44
commit
0309ba9480
@ -61,27 +61,23 @@ def get_fake_volume_info_data(target_portal, volume_id):
|
||||
return {
|
||||
'driver_volume_type': 'iscsi',
|
||||
'data': {
|
||||
'volume_id': 1,
|
||||
'volume_id': volume_id,
|
||||
'target_iqn': 'iqn.2010-10.org.openstack:volume-' + volume_id,
|
||||
'target_portal': target_portal,
|
||||
'target_lun': 1,
|
||||
'auth_method': 'CHAP',
|
||||
}
|
||||
'auth_username': 'fake_username',
|
||||
'auth_password': 'fake_password',
|
||||
'target_discovered': False,
|
||||
},
|
||||
'mount_device': 'vda',
|
||||
'delete_on_termination': False
|
||||
}
|
||||
|
||||
|
||||
def get_fake_block_device_info(target_portal, volume_id):
|
||||
return {'block_device_mapping': [{'connection_info': {
|
||||
'driver_volume_type': 'iscsi',
|
||||
'data': {'target_lun': 1,
|
||||
'volume_id': volume_id,
|
||||
'target_iqn':
|
||||
'iqn.2010-10.org.openstack:volume-' +
|
||||
volume_id,
|
||||
'target_portal': target_portal,
|
||||
'target_discovered': False}},
|
||||
'mount_device': 'vda',
|
||||
'delete_on_termination': False}],
|
||||
connection_info = get_fake_volume_info_data(target_portal, volume_id)
|
||||
return {'block_device_mapping': [{'connection_info': connection_info}],
|
||||
'root_device_name': 'fake_root_device_name',
|
||||
'ephemerals': [],
|
||||
'swap': None
|
||||
|
@ -1193,7 +1193,9 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
|
||||
|
||||
volumeutils.VolumeUtils.login_storage_target(target_lun,
|
||||
target_iqn,
|
||||
target_portal)
|
||||
target_portal,
|
||||
'fake_username',
|
||||
'fake_password')
|
||||
|
||||
self._mock_get_mounted_disk_from_lun(target_iqn, target_lun,
|
||||
fake_mounted_disk,
|
||||
|
@ -73,12 +73,19 @@ class VolumeUtilsTestCase(test_basevolumeutils.BaseVolumeUtilsTestCase):
|
||||
def test_login_new_portal(self):
|
||||
self._test_login_target_portal(False)
|
||||
|
||||
def _test_login_target(self, target_connected, raise_exception=False):
|
||||
def _test_login_target(self, target_connected=False, raise_exception=False,
|
||||
use_chap=False):
|
||||
fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR,
|
||||
self._FAKE_PORTAL_PORT)
|
||||
self._volutils.execute = mock.MagicMock()
|
||||
self._volutils._login_target_portal = mock.MagicMock()
|
||||
|
||||
if use_chap:
|
||||
username, password = (mock.sentinel.username,
|
||||
mock.sentinel.password)
|
||||
else:
|
||||
username, password = None, None
|
||||
|
||||
if target_connected:
|
||||
self._volutils.execute.return_value = self._FAKE_TARGET
|
||||
elif raise_exception:
|
||||
@ -90,28 +97,34 @@ class VolumeUtilsTestCase(test_basevolumeutils.BaseVolumeUtilsTestCase):
|
||||
if raise_exception:
|
||||
self.assertRaises(vmutils.HyperVException,
|
||||
self._volutils.login_storage_target,
|
||||
self._FAKE_LUN, self._FAKE_TARGET, fake_portal)
|
||||
self._FAKE_LUN, self._FAKE_TARGET,
|
||||
fake_portal, username, password)
|
||||
else:
|
||||
self._volutils.login_storage_target(self._FAKE_LUN,
|
||||
self._FAKE_TARGET,
|
||||
fake_portal)
|
||||
|
||||
call_list = self._volutils.execute.call_args_list
|
||||
all_call_args = [arg for call in call_list for arg in call[0]]
|
||||
fake_portal,
|
||||
username, password)
|
||||
|
||||
if target_connected:
|
||||
call_list = self._volutils.execute.call_args_list
|
||||
all_call_args = [arg for call in call_list for arg in call[0]]
|
||||
self.assertNotIn('qlogintarget', all_call_args)
|
||||
else:
|
||||
self.assertIn('qlogintarget', all_call_args)
|
||||
self._volutils.execute.assert_any_call(
|
||||
'iscsicli.exe', 'qlogintarget',
|
||||
self._FAKE_TARGET, username, password)
|
||||
|
||||
def test_login_connected_target(self):
|
||||
self._test_login_target(True)
|
||||
self._test_login_target(target_connected=True)
|
||||
|
||||
def test_login_disconncted_target(self):
|
||||
self._test_login_target(False)
|
||||
self._test_login_target()
|
||||
|
||||
def test_login_target_exception(self):
|
||||
self._test_login_target(False, True)
|
||||
self._test_login_target(raise_exception=True)
|
||||
|
||||
def test_login_target_using_chap(self):
|
||||
self._test_login_target(use_chap=True)
|
||||
|
||||
def _test_execute_wrapper(self, raise_exception):
|
||||
fake_cmd = ('iscsicli.exe', 'ListTargetPortals')
|
||||
|
@ -68,7 +68,8 @@ class VolumeUtilsV2TestCase(test.NoDBTestCase):
|
||||
def test_login_new_portal(self):
|
||||
self._test_login_target_portal(False)
|
||||
|
||||
def _test_login_target(self, target_connected, raise_exception=False):
|
||||
def _test_login_target(self, target_connected=False, raise_exception=False,
|
||||
use_chap=False):
|
||||
fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR,
|
||||
self._FAKE_PORTAL_PORT)
|
||||
|
||||
@ -88,6 +89,18 @@ class VolumeUtilsV2TestCase(test.NoDBTestCase):
|
||||
self._volutilsv2._conn_storage.MSFT_iSCSITarget = (
|
||||
fake_target_object)
|
||||
|
||||
if use_chap:
|
||||
username, password = (mock.sentinel.username,
|
||||
mock.sentinel.password)
|
||||
auth = {
|
||||
'AuthenticationType': self._volutilsv2._CHAP_AUTH_TYPE,
|
||||
'ChapUsername': username,
|
||||
'ChapSecret': password,
|
||||
}
|
||||
else:
|
||||
username, password = None, None
|
||||
auth = {}
|
||||
|
||||
if raise_exception:
|
||||
self.assertRaises(vmutils.HyperVException,
|
||||
self._volutilsv2.login_storage_target,
|
||||
@ -95,22 +108,26 @@ class VolumeUtilsV2TestCase(test.NoDBTestCase):
|
||||
else:
|
||||
self._volutilsv2.login_storage_target(self._FAKE_LUN,
|
||||
self._FAKE_TARGET,
|
||||
fake_portal)
|
||||
fake_portal,
|
||||
username, password)
|
||||
|
||||
if target_connected:
|
||||
fake_target_object.Update.assert_called_with()
|
||||
else:
|
||||
fake_target_object.Connect.assert_called_once_with(
|
||||
IsPersistent=True, NodeAddress=self._FAKE_TARGET)
|
||||
IsPersistent=True, NodeAddress=self._FAKE_TARGET, **auth)
|
||||
|
||||
def test_login_connected_target(self):
|
||||
self._test_login_target(True)
|
||||
self._test_login_target(target_connected=True)
|
||||
|
||||
def test_login_disconncted_target(self):
|
||||
self._test_login_target(False)
|
||||
self._test_login_target()
|
||||
|
||||
def test_login_target_exception(self):
|
||||
self._test_login_target(False, True)
|
||||
self._test_login_target(raise_exception=True)
|
||||
|
||||
def test_login_target_using_chap(self):
|
||||
self._test_login_target(use_chap=True)
|
||||
|
||||
def test_logout_storage_target(self):
|
||||
mock_msft_target = self._volutilsv2._conn_storage.MSFT_iSCSITarget
|
||||
|
@ -95,6 +95,17 @@ class VolumeOps(object):
|
||||
target_lun = data['target_lun']
|
||||
target_iqn = data['target_iqn']
|
||||
target_portal = data['target_portal']
|
||||
auth_method = data.get('auth_method')
|
||||
auth_username = data.get('auth_username')
|
||||
auth_password = data.get('auth_password')
|
||||
|
||||
if auth_method and auth_method.upper() != 'CHAP':
|
||||
raise vmutils.HyperVException(
|
||||
_("Cannot log in target %(target_iqn)s. Unsupported iSCSI "
|
||||
"authentication method: %(auth_method)s.") %
|
||||
{'target_iqn': target_iqn,
|
||||
'auth_method': auth_method})
|
||||
|
||||
# Check if we already logged in
|
||||
if self._volutils.get_device_number_for_target(target_iqn, target_lun):
|
||||
LOG.debug("Already logged in on storage target. No need to "
|
||||
@ -109,7 +120,8 @@ class VolumeOps(object):
|
||||
{'target_portal': target_portal,
|
||||
'target_iqn': target_iqn, 'target_lun': target_lun})
|
||||
self._volutils.login_storage_target(target_lun, target_iqn,
|
||||
target_portal)
|
||||
target_portal, auth_username,
|
||||
auth_password)
|
||||
# Wait for the target to be mounted
|
||||
self._get_mounted_disk_from_lun(target_iqn, target_lun, True)
|
||||
|
||||
|
@ -70,7 +70,8 @@ class VolumeUtils(basevolumeutils.BaseVolumeUtils):
|
||||
'*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*',
|
||||
'*', '*')
|
||||
|
||||
def login_storage_target(self, target_lun, target_iqn, target_portal):
|
||||
def login_storage_target(self, target_lun, target_iqn, target_portal,
|
||||
auth_username=None, auth_password=None):
|
||||
"""Ensure that the target is logged in."""
|
||||
|
||||
self._login_target_portal(target_portal)
|
||||
@ -90,7 +91,8 @@ class VolumeUtils(basevolumeutils.BaseVolumeUtils):
|
||||
session_info = self.execute('iscsicli.exe', 'SessionList')
|
||||
if session_info.find(target_iqn) == -1:
|
||||
# Sending login
|
||||
self.execute('iscsicli.exe', 'qlogintarget', target_iqn)
|
||||
self.execute('iscsicli.exe', 'qlogintarget', target_iqn,
|
||||
auth_username, auth_password)
|
||||
else:
|
||||
return
|
||||
except vmutils.HyperVException as exc:
|
||||
|
@ -37,6 +37,8 @@ CONF = cfg.CONF
|
||||
|
||||
|
||||
class VolumeUtilsV2(basevolumeutils.BaseVolumeUtils):
|
||||
_CHAP_AUTH_TYPE = 'ONEWAYCHAP'
|
||||
|
||||
def __init__(self, host='.'):
|
||||
super(VolumeUtilsV2, self).__init__(host)
|
||||
|
||||
@ -62,7 +64,8 @@ class VolumeUtilsV2(basevolumeutils.BaseVolumeUtils):
|
||||
portal.New(TargetPortalAddress=target_address,
|
||||
TargetPortalPortNumber=target_port)
|
||||
|
||||
def login_storage_target(self, target_lun, target_iqn, target_portal):
|
||||
def login_storage_target(self, target_lun, target_iqn, target_portal,
|
||||
auth_username=None, auth_password=None):
|
||||
"""Ensure that the target is logged in."""
|
||||
|
||||
self._login_target_portal(target_portal)
|
||||
@ -88,8 +91,13 @@ class VolumeUtilsV2(basevolumeutils.BaseVolumeUtils):
|
||||
return
|
||||
try:
|
||||
target = self._conn_storage.MSFT_iSCSITarget
|
||||
auth = {}
|
||||
if auth_username and auth_password:
|
||||
auth['AuthenticationType'] = self._CHAP_AUTH_TYPE
|
||||
auth['ChapUsername'] = auth_username
|
||||
auth['ChapSecret'] = auth_password
|
||||
target.Connect(NodeAddress=target_iqn,
|
||||
IsPersistent=True)
|
||||
IsPersistent=True, **auth)
|
||||
time.sleep(CONF.hyperv.volume_attach_retry_interval)
|
||||
except wmi.x_wmi as exc:
|
||||
LOG.debug("Attempt %(attempt)d to connect to target "
|
||||
|
Loading…
Reference in New Issue
Block a user