1# Copyright 2016 Cloudbase Solutions Srl 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16from unittest import mock 17 18import ddt 19from os_win import exceptions as os_win_exc 20 21from os_brick import exception 22from os_brick.initiator.windows import iscsi 23from os_brick.tests.windows import test_base 24 25 26@ddt.ddt 27class WindowsISCSIConnectorTestCase(test_base.WindowsConnectorTestBase): 28 @mock.patch.object(iscsi.WindowsISCSIConnector, 'validate_initiators') 29 def setUp(self, mock_validate_connectors): 30 super(WindowsISCSIConnectorTestCase, self).setUp() 31 32 self._diskutils = mock.Mock() 33 self._iscsi_utils = mock.Mock() 34 35 self._connector = iscsi.WindowsISCSIConnector( 36 device_scan_interval=mock.sentinel.rescan_interval) 37 self._connector._diskutils = self._diskutils 38 self._connector._iscsi_utils = self._iscsi_utils 39 40 @ddt.data({'requested_initiators': [mock.sentinel.initiator_0], 41 'available_initiators': [mock.sentinel.initiator_0, 42 mock.sentinel.initiator_1]}, 43 {'requested_initiators': [mock.sentinel.initiator_0], 44 'available_initiators': [mock.sentinel.initiator_1]}, 45 {'requested_initiators': [], 46 'available_initiators': [mock.sentinel.software_initiator]}) 47 @ddt.unpack 48 def test_validate_initiators(self, requested_initiators, 49 available_initiators): 50 self._iscsi_utils.get_iscsi_initiators.return_value = ( 51 available_initiators) 52 self._connector.initiator_list = requested_initiators 53 54 expected_valid_initiator = not ( 55 set(requested_initiators).difference(set(available_initiators))) 56 valid_initiator = self._connector.validate_initiators() 57 58 self.assertEqual(expected_valid_initiator, valid_initiator) 59 60 def test_get_initiator(self): 61 initiator = self._connector.get_initiator() 62 self.assertEqual(self._iscsi_utils.get_iscsi_initiator.return_value, 63 initiator) 64 65 @mock.patch.object(iscsi, 'utilsfactory') 66 def test_get_connector_properties(self, mock_utilsfactory): 67 mock_iscsi_utils = ( 68 mock_utilsfactory.get_iscsi_initiator_utils.return_value) 69 70 props = self._connector.get_connector_properties() 71 expected_props = dict( 72 initiator=mock_iscsi_utils.get_iscsi_initiator.return_value) 73 74 self.assertEqual(expected_props, props) 75 76 @mock.patch.object(iscsi.WindowsISCSIConnector, '_get_all_targets') 77 def test_get_all_paths(self, mock_get_all_targets): 78 initiators = [mock.sentinel.initiator_0, mock.sentinel.initiator_1] 79 all_targets = [(mock.sentinel.portal_0, mock.sentinel.target_0, 80 mock.sentinel.lun_0), 81 (mock.sentinel.portal_1, mock.sentinel.target_1, 82 mock.sentinel.lun_1)] 83 84 self._connector.initiator_list = initiators 85 mock_get_all_targets.return_value = all_targets 86 87 expected_paths = [ 88 (initiator_name, target_portal, target_iqn, target_lun) 89 for target_portal, target_iqn, target_lun in all_targets 90 for initiator_name in initiators] 91 all_paths = self._connector._get_all_paths(mock.sentinel.conn_props) 92 93 self.assertEqual(expected_paths, all_paths) 94 mock_get_all_targets.assert_called_once_with(mock.sentinel.conn_props) 95 96 @ddt.data(True, False) 97 @mock.patch.object(iscsi.WindowsISCSIConnector, '_get_scsi_wwn') 98 @mock.patch.object(iscsi.WindowsISCSIConnector, '_get_all_paths') 99 def test_connect_volume(self, use_multipath, 100 mock_get_all_paths, mock_get_scsi_wwn): 101 fake_paths = [(mock.sentinel.initiator_name, 102 mock.sentinel.target_portal, 103 mock.sentinel.target_iqn, 104 mock.sentinel.target_lun)] * 3 105 fake_conn_props = dict(auth_username=mock.sentinel.auth_username, 106 auth_password=mock.sentinel.auth_password) 107 108 mock_get_all_paths.return_value = fake_paths 109 self._iscsi_utils.login_storage_target.side_effect = [ 110 os_win_exc.OSWinException, None, None] 111 self._iscsi_utils.get_device_number_and_path.return_value = ( 112 mock.sentinel.device_number, mock.sentinel.device_path) 113 self._connector.use_multipath = use_multipath 114 115 device_info = self._connector.connect_volume(fake_conn_props) 116 expected_device_info = dict(type='block', 117 path=mock.sentinel.device_path, 118 number=mock.sentinel.device_number, 119 scsi_wwn=mock_get_scsi_wwn.return_value) 120 121 self.assertEqual(expected_device_info, device_info) 122 123 mock_get_all_paths.assert_called_once_with(fake_conn_props) 124 expected_login_attempts = 3 if use_multipath else 2 125 self._iscsi_utils.login_storage_target.assert_has_calls( 126 [mock.call(target_lun=mock.sentinel.target_lun, 127 target_iqn=mock.sentinel.target_iqn, 128 target_portal=mock.sentinel.target_portal, 129 auth_username=mock.sentinel.auth_username, 130 auth_password=mock.sentinel.auth_password, 131 mpio_enabled=use_multipath, 132 initiator_name=mock.sentinel.initiator_name, 133 ensure_lun_available=False)] * 134 expected_login_attempts) 135 self._iscsi_utils.get_device_number_and_path.assert_called_once_with( 136 mock.sentinel.target_iqn, mock.sentinel.target_lun, 137 retry_attempts=self._connector.device_scan_attempts, 138 retry_interval=self._connector.device_scan_interval, 139 rescan_disks=True, 140 ensure_mpio_claimed=use_multipath) 141 mock_get_scsi_wwn.assert_called_once_with(mock.sentinel.device_number) 142 143 @mock.patch.object(iscsi.WindowsISCSIConnector, '_get_all_paths') 144 def test_connect_volume_exc(self, mock_get_all_paths): 145 fake_paths = [(mock.sentinel.initiator_name, 146 mock.sentinel.target_portal, 147 mock.sentinel.target_iqn, 148 mock.sentinel.target_lun)] * 3 149 150 mock_get_all_paths.return_value = fake_paths 151 self._iscsi_utils.login_storage_target.side_effect = ( 152 os_win_exc.OSWinException) 153 self._connector.use_multipath = True 154 155 self.assertRaises(exception.BrickException, 156 self._connector.connect_volume, 157 connection_properties={}) 158 159 @mock.patch.object(iscsi.WindowsISCSIConnector, '_get_all_targets') 160 def test_disconnect_volume(self, mock_get_all_targets): 161 targets = [ 162 (mock.sentinel.portal_0, mock.sentinel.tg_0, mock.sentinel.lun_0), 163 (mock.sentinel.portal_1, mock.sentinel.tg_1, mock.sentinel.lun_1)] 164 165 mock_get_all_targets.return_value = targets 166 self._iscsi_utils.get_target_luns.return_value = [mock.sentinel.lun_0] 167 168 self._connector.disconnect_volume(mock.sentinel.conn_props, 169 mock.sentinel.dev_info) 170 171 self._diskutils.rescan_disks.assert_called_once_with() 172 mock_get_all_targets.assert_called_once_with(mock.sentinel.conn_props) 173 self._iscsi_utils.logout_storage_target.assert_called_once_with( 174 mock.sentinel.tg_0) 175 self._iscsi_utils.get_target_luns.assert_has_calls( 176 [mock.call(mock.sentinel.tg_0), mock.call(mock.sentinel.tg_1)]) 177 178 @mock.patch.object(iscsi.WindowsISCSIConnector, '_get_all_targets') 179 @mock.patch.object(iscsi.WindowsISCSIConnector, '_check_device_paths') 180 def test_get_volume_paths(self, mock_check_dev_paths, 181 mock_get_all_targets): 182 targets = [ 183 (mock.sentinel.portal_0, mock.sentinel.tg_0, mock.sentinel.lun_0), 184 (mock.sentinel.portal_1, mock.sentinel.tg_1, mock.sentinel.lun_1)] 185 186 mock_get_all_targets.return_value = targets 187 self._iscsi_utils.get_device_number_and_path.return_value = [ 188 mock.sentinel.dev_num, mock.sentinel.dev_path] 189 190 volume_paths = self._connector.get_volume_paths( 191 mock.sentinel.conn_props) 192 expected_paths = [mock.sentinel.dev_path] 193 194 self.assertEqual(expected_paths, volume_paths) 195 mock_check_dev_paths.assert_called_once_with(set(expected_paths)) 196