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 os_win import exceptions as os_win_exc 17from os_win import utilsfactory 18from oslo_log import log as logging 19 20from os_brick import exception 21from os_brick.i18n import _ 22from os_brick.initiator.connectors import base_iscsi 23from os_brick.initiator.windows import base as win_conn_base 24from os_brick import utils 25 26LOG = logging.getLogger(__name__) 27 28 29class WindowsISCSIConnector(win_conn_base.BaseWindowsConnector, 30 base_iscsi.BaseISCSIConnector): 31 def __init__(self, *args, **kwargs): 32 super(WindowsISCSIConnector, self).__init__(*args, **kwargs) 33 self.use_multipath = kwargs.pop('use_multipath', False) 34 self.initiator_list = kwargs.pop('initiator_list', []) 35 36 self._iscsi_utils = utilsfactory.get_iscsi_initiator_utils() 37 38 self.validate_initiators() 39 40 def validate_initiators(self): 41 """Validates the list of requested initiator HBAs 42 43 Validates the list of requested initiator HBAs to be used 44 when establishing iSCSI sessions. 45 """ 46 valid_initiator_list = True 47 if not self.initiator_list: 48 LOG.info("No iSCSI initiator was explicitly requested. " 49 "The Microsoft iSCSI initiator will choose the " 50 "initiator when establishing sessions.") 51 else: 52 available_initiators = self._iscsi_utils.get_iscsi_initiators() 53 for initiator in self.initiator_list: 54 if initiator not in available_initiators: 55 LOG.warning("The requested initiator %(req_initiator)s " 56 "is not in the list of available initiators: " 57 "%(avail_initiators)s.", 58 dict(req_initiator=initiator, 59 avail_initiators=available_initiators)) 60 valid_initiator_list = False 61 return valid_initiator_list 62 63 def get_initiator(self): 64 """Returns the iSCSI initiator node name.""" 65 return self._iscsi_utils.get_iscsi_initiator() 66 67 @staticmethod 68 def get_connector_properties(*args, **kwargs): 69 iscsi_utils = utilsfactory.get_iscsi_initiator_utils() 70 initiator = iscsi_utils.get_iscsi_initiator() 71 return dict(initiator=initiator) 72 73 def _get_all_paths(self, connection_properties): 74 initiator_list = self.initiator_list or [None] 75 all_targets = self._get_all_targets(connection_properties) 76 paths = [(initiator_name, target_portal, target_iqn, target_lun) 77 for target_portal, target_iqn, target_lun in all_targets 78 for initiator_name in initiator_list] 79 return paths 80 81 @utils.trace 82 def connect_volume(self, connection_properties): 83 connected_target_mappings = set() 84 volume_connected = False 85 86 for (initiator_name, 87 target_portal, 88 target_iqn, 89 target_lun) in self._get_all_paths(connection_properties): 90 try: 91 LOG.info("Attempting to establish an iSCSI session to " 92 "target %(target_iqn)s on portal %(target_portal)s " 93 "accessing LUN %(target_lun)s using initiator " 94 "%(initiator_name)s.", 95 dict(target_portal=target_portal, 96 target_iqn=target_iqn, 97 target_lun=target_lun, 98 initiator_name=initiator_name)) 99 self._iscsi_utils.login_storage_target( 100 target_lun=target_lun, 101 target_iqn=target_iqn, 102 target_portal=target_portal, 103 auth_username=connection_properties.get('auth_username'), 104 auth_password=connection_properties.get('auth_password'), 105 mpio_enabled=self.use_multipath, 106 initiator_name=initiator_name, 107 ensure_lun_available=False) 108 109 connected_target_mappings.add((target_iqn, target_lun)) 110 111 if not self.use_multipath: 112 break 113 except os_win_exc.OSWinException: 114 LOG.exception("Could not establish the iSCSI session.") 115 116 for target_iqn, target_lun in connected_target_mappings: 117 try: 118 (device_number, 119 device_path) = self._iscsi_utils.get_device_number_and_path( 120 target_iqn, target_lun, 121 retry_attempts=self.device_scan_attempts, 122 retry_interval=self.device_scan_interval, 123 rescan_disks=True, 124 ensure_mpio_claimed=self.use_multipath) 125 volume_connected = True 126 except os_win_exc.OSWinException: 127 LOG.exception("Could not retrieve device path for target " 128 "%(target_iqn)s and lun %(target_lun)s.", 129 dict(target_iqn=target_iqn, 130 target_lun=target_lun)) 131 132 if not volume_connected: 133 raise exception.BrickException( 134 _("Could not connect volume %s.") % connection_properties) 135 136 scsi_wwn = self._get_scsi_wwn(device_number) 137 138 device_info = {'type': 'block', 139 'path': device_path, 140 'number': device_number, 141 'scsi_wwn': scsi_wwn} 142 return device_info 143 144 @utils.trace 145 def disconnect_volume(self, connection_properties, device_info=None, 146 force=False, ignore_errors=False): 147 # We want to refresh the cached information first. 148 self._diskutils.rescan_disks() 149 for (target_portal, 150 target_iqn, 151 target_lun) in self._get_all_targets(connection_properties): 152 153 luns = self._iscsi_utils.get_target_luns(target_iqn) 154 # We disconnect the target only if it does not expose other 155 # luns which may be in use. 156 if not luns or luns == [target_lun]: 157 self._iscsi_utils.logout_storage_target(target_iqn) 158 159 @utils.trace 160 def get_volume_paths(self, connection_properties): 161 device_paths = set() 162 163 for (target_portal, 164 target_iqn, 165 target_lun) in self._get_all_targets(connection_properties): 166 167 (device_number, 168 device_path) = self._iscsi_utils.get_device_number_and_path( 169 target_iqn, target_lun, 170 ensure_mpio_claimed=self.use_multipath) 171 if device_path: 172 device_paths.add(device_path) 173 174 self._check_device_paths(device_paths) 175 return list(device_paths) 176