codekingpro/portable-devtools
114k
1"""2"""3 4# Created on 2014.03.045#6# Author: Giovanni Cannata7#8# Copyright 2014 - 2020 Giovanni Cannata9#10# This file is part of ldap3.11#12# ldap3 is free software: you can redistribute it and/or modify13# it under the terms of the GNU Lesser General Public License as published14# by the Free Software Foundation, either version 3 of the License, or15# (at your option) any later version.16#17# ldap3 is distributed in the hope that it will be useful,18# but WITHOUT ANY WARRANTY; without even the implied warranty of19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the20# GNU Lesser General Public License for more details.21#22# You should have received a copy of the GNU Lesser General Public License23# along with ldap3 in the COPYING and COPYING.LESSER files.24# If not, see <http://www.gnu.org/licenses/>.25 26from time import sleep27import socket28 29from .. import get_config_parameter30from .sync import SyncStrategy31from ..core.exceptions import LDAPSocketOpenError, LDAPOperationResult, LDAPMaximumRetriesError, LDAPStartTLSError32from ..utils.log import log, log_enabled, ERROR, BASIC33 34 35# noinspection PyBroadException,PyProtectedMember36class RestartableStrategy(SyncStrategy):37 def __init__(self, ldap_connection):38 SyncStrategy.__init__(self, ldap_connection)39 self.sync = True40 self.no_real_dsa = False41 self.pooled = False42 self.can_stream = False43 self.restartable_sleep_time = get_config_parameter('RESTARTABLE_SLEEPTIME')44 self.restartable_tries = get_config_parameter('RESTARTABLE_TRIES')45 self._restarting = False46 self._last_bind_controls = None47 self._current_message_type = None48 self._current_request = None49 self._current_controls = None50 self._restart_tls = None51 self.exception_history = []52 53 def open(self, reset_usage=False, read_server_info=True):54 SyncStrategy.open(self, reset_usage, read_server_info)55 56 def _open_socket(self, address, use_ssl=False, unix_socket=False):57 """58 Try to open and connect a socket to a Server59 raise LDAPExceptionError if unable to open or connect socket60 if connection is restartable tries for the number of restarting requested or forever61 """62 try:63 SyncStrategy._open_socket(self, address, use_ssl, unix_socket) # try to open socket using SyncWait64 self._reset_exception_history()65 return66 except Exception as e: # machinery for restartable connection67 if log_enabled(ERROR):68 log(ERROR, '<%s> while restarting <%s>', e, self.connection)69 self._add_exception_to_history(type(e)(str(e)))70 71 if not self._restarting: # if not already performing a restart72 self._restarting = True73 counter = self.restartable_tries74 while counter > 0: # includes restartable_tries == True75 if log_enabled(BASIC):76 log(BASIC, 'try #%d to open Restartable connection <%s>', self.restartable_tries - counter, self.connection)77 sleep(self.restartable_sleep_time)78 if not self.connection.closed:79 try: # resetting connection80 self.connection.unbind()81 except (socket.error, LDAPSocketOpenError): # don't trace catch socket errors because socket could already be closed82 pass83 except Exception as e:84 if log_enabled(ERROR):85 log(ERROR, '<%s> while restarting <%s>', e, self.connection)86 self._add_exception_to_history(type(e)(str(e)))87 try: # reissuing same operation88 if self.connection.server_pool:89 new_server = self.connection.server_pool.get_server(self.connection) # get a server from the server_pool if available90 if self.connection.server != new_server:91 self.connection.server = new_server92 if self.connection.usage:93 self.connection._usage.servers_from_pool += 194 SyncStrategy._open_socket(self, address, use_ssl, unix_socket) # calls super (not restartable) _open_socket()95 if self.connection.usage:96 self.connection._usage.restartable_successes += 197 self.connection.closed = False98 self._restarting = False99 self._reset_exception_history()100 return101 except Exception as e:102 if log_enabled(ERROR):103 log(ERROR, '<%s> while restarting <%s>', e, self.connection)104 self._add_exception_to_history(type(e)(str(e)))105 if self.connection.usage:106 self.connection._usage.restartable_failures += 1107 if not isinstance(self.restartable_tries, bool):108 counter -= 1109 self._restarting = False110 self.connection.last_error = 'restartable connection strategy failed while opening socket'111 if log_enabled(ERROR):112 log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)113 raise LDAPMaximumRetriesError(self.connection.last_error, self.exception_history, self.restartable_tries)114 115 def send(self, message_type, request, controls=None):116 self._current_message_type = message_type117 self._current_request = request118 self._current_controls = controls119 if not self._restart_tls: # RFCs doesn't define how to stop tls once started120 self._restart_tls = self.connection.tls_started121 if message_type == 'bindRequest': # stores controls used in bind operation to be used again when restarting the connection122 self._last_bind_controls = controls123 124 try:125 message_id = SyncStrategy.send(self, message_type, request, controls) # tries to send using SyncWait126 self._reset_exception_history()127 return message_id128 except Exception as e:129 if log_enabled(ERROR):130 log(ERROR, '<%s> while restarting <%s>', e, self.connection)131 self._add_exception_to_history(type(e)(str(e)))132 if not self._restarting: # machinery for restartable connection133 self._restarting = True134 counter = self.restartable_tries135 while counter > 0:136 if log_enabled(BASIC):137 log(BASIC, 'try #%d to send in Restartable connection <%s>', self.restartable_tries - counter, self.connection)138 sleep(self.restartable_sleep_time)139 if not self.connection.closed:140 try: # resetting connection141 self.connection.unbind()142 except (socket.error, LDAPSocketOpenError): # don't trace socket errors because socket could already be closed143 pass144 except Exception as e:145 if log_enabled(ERROR):146 log(ERROR, '<%s> while restarting <%s>', e, self.connection)147 self._add_exception_to_history(type(e)(str(e)))148 failure = False149 try: # reopening connection150 self.connection.open(reset_usage=False, read_server_info=False)151 if self._restart_tls: # restart tls if start_tls was previously used152 if not self.connection.start_tls(read_server_info=False):153 error = 'restart tls in restartable not successful' + (' - ' + self.connection.last_error if self.connection.last_error else '')154 if log_enabled(ERROR):155 log(ERROR, '%s for <%s>', error, self)156 self.connection.unbind()157 raise LDAPStartTLSError(error)158 if message_type != 'bindRequest':159 self.connection.bind(read_server_info=False, controls=self._last_bind_controls) # binds with previously used controls unless the request is already a bindRequest160 if not self.connection.server.schema and not self.connection.server.info:161 self.connection.refresh_server_info()162 else:163 self.connection._fire_deferred(read_info=False) # in case of lazy connection, not open by the refresh_server_info164 except Exception as e:165 if log_enabled(ERROR):166 log(ERROR, '<%s> while restarting <%s>', e, self.connection)167 self._add_exception_to_history(type(e)(str(e)))168 failure = True169 170 if not failure:171 try: # reissuing same operation172 ret_value = self.connection.send(message_type, request, controls)173 if self.connection.usage:174 self.connection._usage.restartable_successes += 1175 self._restarting = False176 self._reset_exception_history()177 return ret_value # successful send178 except Exception as e:179 if log_enabled(ERROR):180 log(ERROR, '<%s> while restarting <%s>', e, self.connection)181 self._add_exception_to_history(type(e)(str(e)))182 failure = True183 184 if failure and self.connection.usage:185 self.connection._usage.restartable_failures += 1186 187 if not isinstance(self.restartable_tries, bool):188 counter -= 1189 190 self._restarting = False191 192 self.connection.last_error = 'restartable connection failed to send'193 if log_enabled(ERROR):194 log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)195 raise LDAPMaximumRetriesError(self.connection.last_error, self.exception_history, self.restartable_tries)196 197 def post_send_single_response(self, message_id):198 try:199 ret_value = SyncStrategy.post_send_single_response(self, message_id)200 self._reset_exception_history()201 return ret_value202 except Exception as e:203 if log_enabled(ERROR):204 log(ERROR, '<%s> while restarting <%s>', e, self.connection)205 self._add_exception_to_history(type(e)(str(e)))206 207 # if an LDAPExceptionError is raised then resend the request208 try:209 ret_value = SyncStrategy.post_send_single_response(self, self.send(self._current_message_type, self._current_request, self._current_controls))210 self._reset_exception_history()211 return ret_value212 except Exception as e:213 if log_enabled(ERROR):214 log(ERROR, '<%s> while restarting <%s>', e, self.connection)215 self._add_exception_to_history(type(e)(str(e)))216 if not isinstance(e, LDAPOperationResult):217 self.connection.last_error = 'restartable connection strategy failed in post_send_single_response'218 if log_enabled(ERROR):219 log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)220 raise221 222 def post_send_search(self, message_id):223 try:224 ret_value = SyncStrategy.post_send_search(self, message_id)225 self._reset_exception_history()226 return ret_value227 except Exception as e:228 if log_enabled(ERROR):229 log(ERROR, '<%s> while restarting <%s>', e, self.connection)230 self._add_exception_to_history(type(e)(str(e)))231 232 # if an LDAPExceptionError is raised then resend the request233 try:234 ret_value = SyncStrategy.post_send_search(self, self.connection.send(self._current_message_type, self._current_request, self._current_controls))235 self._reset_exception_history()236 return ret_value237 except Exception as e:238 if log_enabled(ERROR):239 log(ERROR, '<%s> while restarting <%s>', e, self.connection)240 self._add_exception_to_history(type(e)(str(e)))241 if not isinstance(e, LDAPOperationResult):242 self.connection.last_error = e.args243 if log_enabled(ERROR):244 log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)245 raise e246 247 def _add_exception_to_history(self, exc):248 if not isinstance(self.restartable_tries, bool): # doesn't accumulate when restarting forever249 if not isinstance(exc, LDAPMaximumRetriesError): # doesn't add the LDAPMaximumRetriesError exception250 self.exception_history.append(exc)251 252 def _reset_exception_history(self):253 if self.exception_history:254 self.exception_history = []255 256 def get_stream(self):257 raise NotImplementedError258 259 def set_stream(self, value):260 raise NotImplementedError261 