os-win/os_win/tests/unit/utils/storage/initiator/test_iscsi_utils.py

850 lines
36 KiB
Python

# Copyright 2016 Cloudbase Solutions Srl
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import collections
import ctypes
from unittest import mock
import ddt
import six
from os_win import _utils
from os_win import constants
from os_win import exceptions
from os_win.tests.unit import test_base
from os_win.utils.storage.initiator import iscsi_utils
from os_win.utils.winapi import constants as w_const
from os_win.utils.winapi.errmsg import iscsierr
from os_win.utils.winapi.libs import iscsidsc as iscsi_struct
@ddt.ddt
class ISCSIInitiatorUtilsTestCase(test_base.OsWinBaseTestCase):
"""Unit tests for the Hyper-V ISCSIInitiatorUtils class."""
_autospec_classes = [
iscsi_utils.win32utils.Win32Utils,
iscsi_utils.diskutils.DiskUtils,
]
def setUp(self):
super(ISCSIInitiatorUtilsTestCase, self).setUp()
self._initiator = iscsi_utils.ISCSIInitiatorUtils()
self._diskutils = self._initiator._diskutils
self._iscsidsc = mock.patch.object(
iscsi_utils, 'iscsidsc', create=True).start()
self._run_mocker = mock.patch.object(self._initiator,
'_run_and_check_output')
self._mock_run = self._run_mocker.start()
iscsi_utils.portal_map = collections.defaultdict(set)
def _mock_ctypes(self):
self._ctypes = mock.Mock()
# This is used in order to easily make assertions on the variables
# passed by reference.
self._ctypes.byref = lambda x: (x, "byref")
mock.patch.object(iscsi_utils, 'ctypes', self._ctypes).start()
def _get_fake_iscsi_utils_getter_func(self, func_side_effect,
decorator_args,
returned_element_count=None,
required_buff_sz=None):
@iscsi_utils.ensure_buff_and_retrieve_items(**decorator_args)
def fake_func(inst, buff=None, buff_size=None,
element_count=None, *args, **kwargs):
raised_exc = None
try:
# Those arguments will always be ULONGs, as requested
# by the iscsidsc functions.
self.assertIsInstance(buff_size, ctypes.c_ulong)
self.assertIsInstance(element_count, ctypes.c_ulong)
func_side_effect(buff=buff, buff_size_val=buff_size.value,
element_count_val=element_count.value,
*args, **kwargs)
except Exception as ex:
raised_exc = ex
if returned_element_count:
element_count.value = returned_element_count
if required_buff_sz:
buff_size.value = required_buff_sz
if raised_exc:
raise raised_exc
return mock.sentinel.ret_val
return fake_func
@mock.patch.object(iscsi_utils, '_get_items_from_buff')
def _test_ensure_buff_decorator(self, mock_get_items,
required_buff_sz=None,
returned_element_count=None,
parse_output=False):
insufficient_buff_exc = exceptions.Win32Exception(
message='fake_err_msg',
error_code=w_const.ERROR_INSUFFICIENT_BUFFER)
func_requests_buff_sz = required_buff_sz is not None
struct_type = ctypes.c_uint
decorator_args = dict(struct_type=struct_type,
parse_output=parse_output,
func_requests_buff_sz=func_requests_buff_sz)
func_side_effect = mock.Mock(side_effect=(insufficient_buff_exc, None))
fake_func = self._get_fake_iscsi_utils_getter_func(
returned_element_count=returned_element_count,
required_buff_sz=required_buff_sz,
func_side_effect=func_side_effect,
decorator_args=decorator_args)
ret_val = fake_func(self._initiator, fake_arg=mock.sentinel.arg)
if parse_output:
self.assertEqual(mock_get_items.return_value, ret_val)
else:
self.assertEqual(mock.sentinel.ret_val, ret_val)
# We expect our decorated method to be called exactly two times.
first_call_args_dict = func_side_effect.call_args_list[0][1]
self.assertIsInstance(first_call_args_dict['buff'],
ctypes.POINTER(struct_type))
self.assertEqual(first_call_args_dict['buff_size_val'], 0)
self.assertEqual(first_call_args_dict['element_count_val'], 0)
second_call_args_dict = func_side_effect.call_args_list[1][1]
self.assertIsInstance(second_call_args_dict['buff'],
ctypes.POINTER(struct_type))
self.assertEqual(second_call_args_dict['buff_size_val'],
required_buff_sz or 0)
self.assertEqual(second_call_args_dict['element_count_val'],
returned_element_count or 0)
def test_ensure_buff_func_requests_buff_sz(self):
self._test_ensure_buff_decorator(required_buff_sz=10,
parse_output=True)
def test_ensure_buff_func_requests_el_count(self):
self._test_ensure_buff_decorator(returned_element_count=5)
def test_ensure_buff_func_unexpected_exception(self):
fake_exc = exceptions.Win32Exception(message='fake_message',
error_code=1)
func_side_effect = mock.Mock(side_effect=fake_exc)
fake_func = self._get_fake_iscsi_utils_getter_func(
func_side_effect=func_side_effect,
decorator_args={'struct_type': ctypes.c_ubyte})
self.assertRaises(exceptions.Win32Exception, fake_func,
self._initiator)
def test_get_items_from_buff(self):
fake_buff_contents = 'fake_buff_contents'
fake_buff = (ctypes.c_wchar * len(fake_buff_contents))()
fake_buff.value = fake_buff_contents
fake_buff = ctypes.cast(fake_buff, ctypes.POINTER(ctypes.c_ubyte))
result = iscsi_utils._get_items_from_buff(fake_buff, ctypes.c_wchar,
len(fake_buff_contents))
self.assertEqual(fake_buff_contents, result.value)
def test_run_and_check_output(self):
self._run_mocker.stop()
self._initiator._win32utils = mock.Mock()
mock_win32utils_run_and_check_output = (
self._initiator._win32utils.run_and_check_output)
self._initiator._run_and_check_output(mock.sentinel.func,
mock.sentinel.arg,
fake_kwarg=mock.sentinel.kwarg)
mock_win32utils_run_and_check_output.assert_called_once_with(
mock.sentinel.func,
mock.sentinel.arg,
fake_kwarg=mock.sentinel.kwarg,
error_msg_src=iscsierr.err_msg_dict,
failure_exc=exceptions.ISCSIInitiatorAPIException)
def test_get_iscsi_persistent_logins(self):
self._mock_ctypes()
_get_iscsi_persistent_logins = _utils.get_wrapped_function(
self._initiator._get_iscsi_persistent_logins)
_get_iscsi_persistent_logins(
self._initiator,
buff=mock.sentinel.buff,
buff_size=mock.sentinel.buff_size,
element_count=mock.sentinel.element_count)
self._mock_run.assert_called_once_with(
self._iscsidsc.ReportIScsiPersistentLoginsW,
self._ctypes.byref(mock.sentinel.element_count),
mock.sentinel.buff,
self._ctypes.byref(mock.sentinel.buff_size))
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_parse_string_list')
def test_get_targets(self, mock_parse_string_list):
self._mock_ctypes()
get_targets = _utils.get_wrapped_function(
self._initiator.get_targets)
mock_el_count = mock.Mock(value=mock.sentinel.element_count)
resulted_target_list = get_targets(
self._initiator,
forced_update=mock.sentinel.forced_update,
element_count=mock_el_count,
buff=mock.sentinel.buff)
self.assertEqual(mock_parse_string_list.return_value,
resulted_target_list)
self._mock_run.assert_called_once_with(
self._iscsidsc.ReportIScsiTargetsW,
mock.sentinel.forced_update,
self._ctypes.byref(mock_el_count),
mock.sentinel.buff)
mock_parse_string_list.assert_called_once_with(
mock.sentinel.buff, mock.sentinel.element_count)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_parse_string_list')
def test_get_initiators(self, mock_parse_string_list):
self._mock_ctypes()
get_initiators = _utils.get_wrapped_function(
self._initiator.get_iscsi_initiators)
mock_el_count = mock.Mock(value=mock.sentinel.element_count)
resulted_initator_list = get_initiators(
self._initiator,
element_count=mock_el_count,
buff=mock.sentinel.buff)
self.assertEqual(mock_parse_string_list.return_value,
resulted_initator_list)
self._mock_run.assert_called_once_with(
self._iscsidsc.ReportIScsiInitiatorListW,
self._ctypes.byref(mock_el_count),
mock.sentinel.buff)
mock_parse_string_list.assert_called_once_with(
mock.sentinel.buff, mock.sentinel.element_count)
def test_parse_string_list(self):
self._mock_ctypes()
fake_buff = 'fake\x00buff\x00\x00'
self._ctypes.cast.return_value = fake_buff
str_list = self._initiator._parse_string_list(fake_buff,
len(fake_buff))
self.assertEqual(['fake', 'buff'], str_list)
self._ctypes.cast.assert_called_once_with(
fake_buff, self._ctypes.POINTER.return_value)
self._ctypes.POINTER.assert_called_once_with(self._ctypes.c_wchar)
def test_get_iscsi_initiator(self):
self._mock_ctypes()
self._ctypes.c_wchar = mock.MagicMock()
fake_buff = (self._ctypes.c_wchar * (
w_const.MAX_ISCSI_NAME_LEN + 1))()
fake_buff.value = mock.sentinel.buff_value
resulted_iscsi_initiator = self._initiator.get_iscsi_initiator()
self._mock_run.assert_called_once_with(
self._iscsidsc.GetIScsiInitiatorNodeNameW,
fake_buff)
self.assertEqual(mock.sentinel.buff_value,
resulted_iscsi_initiator)
@mock.patch('socket.getfqdn')
def test_get_iscsi_initiator_exception(self, mock_get_fqdn):
fake_fqdn = 'fakehost.FAKE-DOMAIN.com'
fake_exc = exceptions.ISCSIInitiatorAPIException(
message='fake_message',
error_code=1,
func_name='fake_func')
self._mock_run.side_effect = fake_exc
mock_get_fqdn.return_value = fake_fqdn
resulted_iqn = self._initiator.get_iscsi_initiator()
expected_iqn = "%s:%s" % (self._initiator._MS_IQN_PREFIX,
fake_fqdn.lower())
self.assertEqual(expected_iqn, resulted_iqn)
@mock.patch.object(ctypes, 'byref')
@mock.patch.object(iscsi_struct, 'ISCSI_UNIQUE_CONNECTION_ID')
@mock.patch.object(iscsi_struct, 'ISCSI_UNIQUE_SESSION_ID')
def test_login_iscsi_target(self, mock_cls_ISCSI_UNIQUE_SESSION_ID,
mock_cls_ISCSI_UNIQUE_CONNECTION_ID,
mock_byref):
fake_target_name = 'fake_target_name'
resulted_session_id, resulted_conection_id = (
self._initiator._login_iscsi_target(fake_target_name))
args_list = self._mock_run.call_args_list[0][0]
self.assertIsInstance(args_list[1], ctypes.c_wchar_p)
self.assertEqual(fake_target_name, args_list[1].value)
self.assertIsInstance(args_list[4], ctypes.c_ulong)
self.assertEqual(
ctypes.c_ulong(w_const.ISCSI_ANY_INITIATOR_PORT).value,
args_list[4].value)
self.assertIsInstance(args_list[6], ctypes.c_ulonglong)
self.assertEqual(0, args_list[6].value)
self.assertIsInstance(args_list[9], ctypes.c_ulong)
self.assertEqual(0, args_list[9].value)
mock_byref.assert_has_calls([
mock.call(mock_cls_ISCSI_UNIQUE_SESSION_ID.return_value),
mock.call(mock_cls_ISCSI_UNIQUE_CONNECTION_ID.return_value)])
self.assertEqual(
mock_cls_ISCSI_UNIQUE_SESSION_ID.return_value,
resulted_session_id)
self.assertEqual(
mock_cls_ISCSI_UNIQUE_CONNECTION_ID.return_value,
resulted_conection_id)
def test_get_iscsi_sessions(self):
self._mock_ctypes()
_get_iscsi_sessions = _utils.get_wrapped_function(
self._initiator._get_iscsi_sessions)
_get_iscsi_sessions(
self._initiator,
buff=mock.sentinel.buff,
buff_size=mock.sentinel.buff_size,
element_count=mock.sentinel.element_count)
self._mock_run.assert_called_once_with(
self._iscsidsc.GetIScsiSessionListW,
self._ctypes.byref(mock.sentinel.buff_size),
self._ctypes.byref(mock.sentinel.element_count),
mock.sentinel.buff)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_sessions')
def test_get_iscsi_target_sessions(self, mock_get_iscsi_sessions,
target_sessions_found=True):
fake_session = mock.Mock(TargetNodeName="FAKE_TARGET_NAME",
ConnectionCount=1)
fake_disconn_session = mock.Mock(
TargetNodeName="fake_target_name",
ConnectionCount=0)
other_session = mock.Mock(TargetNodeName="other_target_name",
ConnectionCount=1)
sessions = [fake_session, fake_disconn_session, other_session]
mock_get_iscsi_sessions.return_value = sessions
resulted_tgt_sessions = self._initiator._get_iscsi_target_sessions(
"fake_target_name")
self.assertEqual([fake_session], resulted_tgt_sessions)
def test_get_iscsi_session_devices(self):
self._mock_ctypes()
_get_iscsi_session_devices = _utils.get_wrapped_function(
self._initiator._get_iscsi_session_devices)
_get_iscsi_session_devices(
self._initiator,
mock.sentinel.session_id,
buff=mock.sentinel.buff,
element_count=mock.sentinel.element_count)
self._mock_run.assert_called_once_with(
self._iscsidsc.GetDevicesForIScsiSessionW,
self._ctypes.byref(mock.sentinel.session_id),
self._ctypes.byref(mock.sentinel.element_count),
mock.sentinel.buff)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_session_devices')
def test_get_iscsi_session_luns(self, mock_get_iscsi_session_devices):
fake_device = mock.Mock()
fake_device.StorageDeviceNumber.DeviceType = w_const.FILE_DEVICE_DISK
mock_get_iscsi_session_devices.return_value = [fake_device,
mock.Mock()]
resulted_luns = self._initiator._get_iscsi_session_disk_luns(
mock.sentinel.session_id)
expected_luns = [fake_device.ScsiAddress.Lun]
mock_get_iscsi_session_devices.assert_called_once_with(
mock.sentinel.session_id)
self.assertEqual(expected_luns, resulted_luns)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_session_devices')
def test_get_iscsi_device_from_session(self,
mock_get_iscsi_session_devices):
fake_device = mock.Mock()
fake_device.ScsiAddress.Lun = mock.sentinel.target_lun
mock_get_iscsi_session_devices.return_value = [mock.Mock(),
fake_device]
resulted_device = self._initiator._get_iscsi_device_from_session(
mock.sentinel.session_id,
mock.sentinel.target_lun)
mock_get_iscsi_session_devices.assert_called_once_with(
mock.sentinel.session_id)
self.assertEqual(fake_device, resulted_device)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'get_device_number_and_path')
def test_get_device_number_for_target(self, mock_get_dev_num_and_path):
dev_num = self._initiator.get_device_number_for_target(
mock.sentinel.target_name, mock.sentinel.lun,
mock.sentinel.fail_if_not_found)
mock_get_dev_num_and_path.assert_called_once_with(
mock.sentinel.target_name, mock.sentinel.lun,
mock.sentinel.fail_if_not_found)
self.assertEqual(mock_get_dev_num_and_path.return_value[0], dev_num)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'ensure_lun_available')
def test_get_device_number_and_path(self, mock_ensure_lun_available):
mock_ensure_lun_available.return_value = (mock.sentinel.dev_num,
mock.sentinel.dev_path)
dev_num, dev_path = self._initiator.get_device_number_and_path(
mock.sentinel.target_name, mock.sentinel.lun,
retry_attempts=mock.sentinel.retry_attempts,
retry_interval=mock.sentinel.retry_interval,
rescan_disks=mock.sentinel.rescan_disks,
ensure_mpio_claimed=mock.sentinel.ensure_mpio_claimed)
mock_ensure_lun_available.assert_called_once_with(
mock.sentinel.target_name, mock.sentinel.lun,
rescan_attempts=mock.sentinel.retry_attempts,
retry_interval=mock.sentinel.retry_interval,
rescan_disks=mock.sentinel.rescan_disks,
ensure_mpio_claimed=mock.sentinel.ensure_mpio_claimed)
self.assertEqual(mock.sentinel.dev_num, dev_num)
self.assertEqual(mock.sentinel.dev_path, dev_path)
@ddt.data(True, False)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'ensure_lun_available')
def test_get_device_number_and_path_exc(self, fail_if_not_found,
mock_ensure_lun_available):
raised_exc = exceptions.ISCSILunNotAvailable
mock_ensure_lun_available.side_effect = raised_exc(
target_iqn=mock.sentinel.target_iqn,
target_lun=mock.sentinel.target_lun)
if fail_if_not_found:
self.assertRaises(raised_exc,
self._initiator.get_device_number_and_path,
mock.sentinel.target_name,
mock.sentinel.lun,
fail_if_not_found)
else:
dev_num, dev_path = self._initiator.get_device_number_and_path(
mock.sentinel.target_name,
mock.sentinel.lun,
fail_if_not_found)
self.assertIsNone(dev_num)
self.assertIsNone(dev_path)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_target_sessions')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_session_disk_luns')
def test_get_target_luns(self, mock_get_iscsi_session_disk_luns,
mock_get_iscsi_target_sessions):
fake_session = mock.Mock()
mock_get_iscsi_target_sessions.return_value = [fake_session]
retrieved_luns = [mock.sentinel.lun_0]
mock_get_iscsi_session_disk_luns.return_value = retrieved_luns
resulted_luns = self._initiator.get_target_luns(
mock.sentinel.target_name)
mock_get_iscsi_target_sessions.assert_called_once_with(
mock.sentinel.target_name)
mock_get_iscsi_session_disk_luns.assert_called_once_with(
fake_session.SessionId)
self.assertEqual(retrieved_luns, resulted_luns)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'get_target_luns')
def test_get_target_lun_count(self, mock_get_target_luns):
target_luns = [mock.sentinel.lun0, mock.sentinel.lun1]
mock_get_target_luns.return_value = target_luns
lun_count = self._initiator.get_target_lun_count(
mock.sentinel.target_name)
self.assertEqual(len(target_luns), lun_count)
mock_get_target_luns.assert_called_once_with(
mock.sentinel.target_name)
def test_logout_iscsi_target(self):
self._mock_ctypes()
self._initiator._logout_iscsi_target(mock.sentinel.session_id)
self._mock_run.assert_called_once_with(
self._iscsidsc.LogoutIScsiTarget,
self._ctypes.byref(mock.sentinel.session_id))
def test_add_static_target(self):
self._mock_ctypes()
is_persistent = True
self._initiator._add_static_target(mock.sentinel.target_name,
is_persistent=is_persistent)
self._mock_run.assert_called_once_with(
self._iscsidsc.AddIScsiStaticTargetW,
self._ctypes.c_wchar_p(mock.sentinel.target_name),
None, 0, is_persistent, None, None, None)
def test_remove_static_target(self):
self._mock_ctypes()
self._initiator._remove_static_target(mock.sentinel.target_name)
expected_ignored_err_codes = [w_const.ISDSC_TARGET_NOT_FOUND]
self._mock_run.assert_called_once_with(
self._iscsidsc.RemoveIScsiStaticTargetW,
self._ctypes.c_wchar_p(mock.sentinel.target_name),
ignored_error_codes=expected_ignored_err_codes)
def test_get_login_opts(self):
fake_username = 'fake_chap_username'
fake_password = 'fake_chap_secret'
auth_type = constants.ISCSI_CHAP_AUTH_TYPE
login_flags = w_const.ISCSI_LOGIN_FLAG_MULTIPATH_ENABLED
login_opts = self._initiator._get_login_opts(
auth_username=fake_username,
auth_password=fake_password,
auth_type=auth_type,
login_flags=login_flags)
self.assertEqual(len(fake_username), login_opts.UsernameLength)
self.assertEqual(len(fake_password), login_opts.PasswordLength)
username_struct_contents = ctypes.cast(
login_opts.Username,
ctypes.POINTER(ctypes.c_char * len(fake_username))).contents.value
pwd_struct_contents = ctypes.cast(
login_opts.Password,
ctypes.POINTER(ctypes.c_char * len(fake_password))).contents.value
self.assertEqual(six.b(fake_username), username_struct_contents)
self.assertEqual(six.b(fake_password), pwd_struct_contents)
expected_info_bitmap = (w_const.ISCSI_LOGIN_OPTIONS_USERNAME |
w_const.ISCSI_LOGIN_OPTIONS_PASSWORD |
w_const.ISCSI_LOGIN_OPTIONS_AUTH_TYPE)
self.assertEqual(expected_info_bitmap,
login_opts.InformationSpecified)
self.assertEqual(login_flags,
login_opts.LoginFlags)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_session_devices')
def test_session_on_path_exists(self, mock_get_iscsi_session_devices):
mock_device = mock.Mock(InitiatorName=mock.sentinel.initiator_name)
mock_get_iscsi_session_devices.return_value = [mock_device]
fake_connection = mock.Mock(TargetAddress=mock.sentinel.portal_addr,
TargetSocket=mock.sentinel.portal_port)
fake_connections = [mock.Mock(), fake_connection]
fake_session = mock.Mock(ConnectionCount=len(fake_connections),
Connections=fake_connections)
fake_sessions = [mock.Mock(Connections=[], ConnectionCount=0),
fake_session]
session_on_path_exists = self._initiator._session_on_path_exists(
fake_sessions, mock.sentinel.portal_addr,
mock.sentinel.portal_port,
mock.sentinel.initiator_name)
self.assertTrue(session_on_path_exists)
mock_get_iscsi_session_devices.assert_has_calls(
[mock.call(session.SessionId) for session in fake_sessions])
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_target_sessions')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_session_on_path_exists')
def _test_new_session_required(self, mock_session_on_path_exists,
mock_get_iscsi_target_sessions,
sessions=None,
mpio_enabled=False,
session_on_path_exists=False):
mock_get_iscsi_target_sessions.return_value = sessions
mock_session_on_path_exists.return_value = session_on_path_exists
expected_result = (not sessions or
(mpio_enabled and not session_on_path_exists))
result = self._initiator._new_session_required(
mock.sentinel.target_iqn,
mock.sentinel.portal_addr,
mock.sentinel.portal_port,
mock.sentinel.initiator_name,
mpio_enabled)
self.assertEqual(expected_result, result)
if sessions and mpio_enabled:
mock_session_on_path_exists.assert_called_once_with(
sessions,
mock.sentinel.portal_addr,
mock.sentinel.portal_port,
mock.sentinel.initiator_name)
def test_new_session_required_no_sessions(self):
self._test_new_session_required()
def test_new_session_required_existing_sessions_no_mpio(self):
self._test_new_session_required(sessions=mock.sentinel.sessions)
def test_new_session_required_existing_sessions_mpio_enabled(self):
self._test_new_session_required(sessions=mock.sentinel.sessions,
mpio_enabled=True)
def test_new_session_required_session_on_path_exists(self):
self._test_new_session_required(sessions=mock.sentinel.sessions,
mpio_enabled=True,
session_on_path_exists=True)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_login_opts')
@mock.patch.object(iscsi_struct, 'ISCSI_TARGET_PORTAL')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_new_session_required')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils, 'get_targets')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils, '_login_iscsi_target')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'ensure_lun_available')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_add_static_target')
def _test_login_storage_target(self, mock_add_static_target,
mock_ensure_lun_available,
mock_login_iscsi_target,
mock_get_targets,
mock_session_required,
mock_cls_ISCSI_TARGET_PORTAL,
mock_get_login_opts,
mpio_enabled=False,
login_required=True):
fake_portal_addr = '127.0.0.1'
fake_portal_port = 3260
fake_target_portal = '%s:%s' % (fake_portal_addr, fake_portal_port)
fake_portal = mock_cls_ISCSI_TARGET_PORTAL.return_value
fake_login_opts = mock_get_login_opts.return_value
mock_get_targets.return_value = []
mock_login_iscsi_target.return_value = (mock.sentinel.session_id,
mock.sentinel.conn_id)
mock_session_required.return_value = login_required
self._initiator.login_storage_target(
mock.sentinel.target_lun,
mock.sentinel.target_iqn,
fake_target_portal,
auth_username=mock.sentinel.auth_username,
auth_password=mock.sentinel.auth_password,
auth_type=mock.sentinel.auth_type,
mpio_enabled=mpio_enabled,
rescan_attempts=mock.sentinel.rescan_attempts)
mock_get_targets.assert_called_once_with()
mock_add_static_target.assert_called_once_with(
mock.sentinel.target_iqn)
if login_required:
expected_login_flags = (
w_const.ISCSI_LOGIN_FLAG_MULTIPATH_ENABLED
if mpio_enabled else 0)
mock_get_login_opts.assert_called_once_with(
mock.sentinel.auth_username,
mock.sentinel.auth_password,
mock.sentinel.auth_type,
expected_login_flags)
mock_cls_ISCSI_TARGET_PORTAL.assert_called_once_with(
Address=fake_portal_addr,
Socket=fake_portal_port)
mock_login_iscsi_target.assert_has_calls([
mock.call(mock.sentinel.target_iqn,
fake_portal,
fake_login_opts,
is_persistent=True),
mock.call(mock.sentinel.target_iqn,
fake_portal,
fake_login_opts,
is_persistent=False)])
else:
self.assertFalse(mock_login_iscsi_target.called)
mock_ensure_lun_available.assert_called_once_with(
mock.sentinel.target_iqn,
mock.sentinel.target_lun,
mock.sentinel.rescan_attempts)
def test_login_storage_target_path_exists(self):
self._test_login_storage_target(login_required=False)
def test_login_new_storage_target_no_mpio(self):
self._test_login_storage_target()
def test_login_storage_target_new_path_using_mpio(self):
self._test_login_storage_target(mpio_enabled=True)
@ddt.data(dict(rescan_disks=True),
dict(retry_interval=mock.sentinel.retry_interval))
@ddt.unpack
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_device_from_session')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_target_sessions')
@mock.patch('time.sleep')
def test_ensure_lun_available(self, mock_sleep,
mock_get_iscsi_target_sessions,
mock_get_iscsi_device_from_session,
rescan_disks=False, retry_interval=0):
retry_count = 6
mock_get_iscsi_target_sessions.return_value = [
mock.Mock(SessionId=mock.sentinel.session_id)]
fake_exc = exceptions.ISCSIInitiatorAPIException(
message='fake_message',
error_code=1,
func_name='fake_func')
dev_num_side_eff = [None, -1] + [mock.sentinel.dev_num] * 3
dev_path_side_eff = ([mock.sentinel.dev_path] * 2 +
[None] + [mock.sentinel.dev_path] * 2)
fake_device = mock.Mock()
type(fake_device.StorageDeviceNumber).DeviceNumber = (
mock.PropertyMock(side_effect=dev_num_side_eff))
type(fake_device).LegacyName = (
mock.PropertyMock(side_effect=dev_path_side_eff))
mock_get_dev_side_eff = [None, fake_exc] + [fake_device] * 5
mock_get_iscsi_device_from_session.side_effect = mock_get_dev_side_eff
self._diskutils.is_mpio_disk.side_effect = [False, True]
dev_num, dev_path = self._initiator.ensure_lun_available(
mock.sentinel.target_iqn,
mock.sentinel.target_lun,
rescan_attempts=retry_count,
retry_interval=retry_interval,
rescan_disks=rescan_disks,
ensure_mpio_claimed=True)
self.assertEqual(mock.sentinel.dev_num, dev_num)
self.assertEqual(mock.sentinel.dev_path, dev_path)
mock_get_iscsi_target_sessions.assert_has_calls(
[mock.call(mock.sentinel.target_iqn)] * (retry_count + 1))
mock_get_iscsi_device_from_session.assert_has_calls(
[mock.call(mock.sentinel.session_id,
mock.sentinel.target_lun)] * retry_count)
self._diskutils.is_mpio_disk.assert_has_calls(
[mock.call(mock.sentinel.dev_num)] * 2)
expected_rescan_count = retry_count if rescan_disks else 0
self.assertEqual(
expected_rescan_count,
self._diskutils.rescan_disks.call_count)
if retry_interval:
mock_sleep.assert_has_calls(
[mock.call(retry_interval)] * retry_count)
else:
self.assertFalse(mock_sleep.called)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_target_sessions')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_logout_iscsi_target')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_remove_target_persistent_logins')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_remove_static_target')
def test_logout_storage_target(self, mock_remove_static_target,
mock_remove_target_persistent_logins,
mock_logout_iscsi_target,
mock_get_iscsi_target_sessions):
fake_session = mock.Mock(SessionId=mock.sentinel.session_id)
mock_get_iscsi_target_sessions.return_value = [fake_session]
self._initiator.logout_storage_target(mock.sentinel.target_iqn)
mock_get_iscsi_target_sessions.assert_called_once_with(
mock.sentinel.target_iqn, connected_only=False)
mock_logout_iscsi_target.assert_called_once_with(
mock.sentinel.session_id)
mock_remove_target_persistent_logins.assert_called_once_with(
mock.sentinel.target_iqn)
mock_remove_static_target.assert_called_once_with(
mock.sentinel.target_iqn)
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_remove_persistent_login')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils,
'_get_iscsi_persistent_logins')
def test_remove_target_persistent_logins(self,
mock_get_iscsi_persistent_logins,
mock_remove_persistent_login):
fake_persistent_login = mock.Mock(TargetName=mock.sentinel.target_iqn)
mock_get_iscsi_persistent_logins.return_value = [fake_persistent_login]
self._initiator._remove_target_persistent_logins(
mock.sentinel.target_iqn)
mock_remove_persistent_login.assert_called_once_with(
fake_persistent_login)
mock_get_iscsi_persistent_logins.assert_called_once_with()
@mock.patch.object(ctypes, 'byref')
def test_remove_persistent_login(self, mock_byref):
fake_persistent_login = mock.Mock()
fake_persistent_login.InitiatorInstance = 'fake_initiator_instance'
fake_persistent_login.TargetName = 'fake_target_name'
self._initiator._remove_persistent_login(fake_persistent_login)
args_list = self._mock_run.call_args_list[0][0]
self.assertIsInstance(args_list[1], ctypes.c_wchar_p)
self.assertEqual(fake_persistent_login.InitiatorInstance,
args_list[1].value)
self.assertIsInstance(args_list[3], ctypes.c_wchar_p)
self.assertEqual(fake_persistent_login.TargetName,
args_list[3].value)
mock_byref.assert_called_once_with(fake_persistent_login.TargetPortal)