Server IP : 85.214.239.14 / Your IP : 18.118.33.239 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/3/cwd/proc/3/root/proc/2/task/2/cwd/usr/lib/python3/dist-packages/libcloud/utils/ |
Upload File : |
# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You 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 socket import ssl import time from datetime import datetime, timedelta from functools import wraps import logging from libcloud.utils.py3 import httplib from libcloud.common.exceptions import RateLimitReachedError __all__ = [ 'Retry', 'RetryForeverOnRateLimitError', ] _logger = logging.getLogger(__name__) # Error message which indicates a transient SSL error upon which request # can be retried TRANSIENT_SSL_ERROR = 'The read operation timed out' class TransientSSLError(ssl.SSLError): """Represent transient SSL errors, e.g. timeouts""" pass # Constants used by the ``retry`` class # All the time values (timeout, delay, backoff) are in seconds DEFAULT_TIMEOUT = 30 # default retry timeout DEFAULT_DELAY = 1 # default sleep delay used in each iterator DEFAULT_BACKOFF = 1 # retry backup multiplier RETRY_EXCEPTIONS = (RateLimitReachedError, socket.error, socket.gaierror, httplib.NotConnected, httplib.ImproperConnectionState, TransientSSLError) class MinimalRetry: def __init__(self, retry_delay=DEFAULT_DELAY, timeout=DEFAULT_TIMEOUT, backoff=DEFAULT_BACKOFF): """ Wrapper around retrying that helps to handle common transient exceptions. This minimalistic version only retries SSL errors and rate limiting. :param retry_delay: retry delay between the attempts. :param timeout: maximum time to wait. :param backoff: multiplier added to delay between attempts. :Example: retry_request = MinimalRetry(timeout=1, retry_delay=1, backoff=1) retry_request(self.connection.request)() """ if retry_delay is None: retry_delay = DEFAULT_DELAY if timeout is None: timeout = DEFAULT_TIMEOUT if backoff is None: backoff = DEFAULT_BACKOFF timeout = max(timeout, 0) self.retry_delay = retry_delay self.timeout = timeout self.backoff = backoff def __call__(self, func): def transform_ssl_error(function, *args, **kwargs): try: return function(*args, **kwargs) except ssl.SSLError as exc: if TRANSIENT_SSL_ERROR in str(exc): raise TransientSSLError(*exc.args) raise exc @wraps(func) def retry_loop(*args, **kwargs): current_delay = self.retry_delay end = datetime.now() + timedelta(seconds=self.timeout) last_exc = None while datetime.now() < end: try: return transform_ssl_error(func, *args, **kwargs) except Exception as exc: last_exc = exc if isinstance(exc, RateLimitReachedError): _logger.debug("You are being rate limited, backing " "off...") # NOTE: Retry after defaults to 0 in the # RateLimitReachedError class so we a use more # reasonable default in case that attribute is not # present. This way we prevent busy waiting, etc. retry_after = exc.retry_after if exc.retry_after else 2 time.sleep(retry_after) # Reset delay if we're told to wait due to rate # limiting current_delay = self.retry_delay elif self.should_retry(exc): time.sleep(current_delay) current_delay *= self.backoff else: raise raise last_exc return retry_loop def should_retry(self, exception): return False class Retry(MinimalRetry): def __init__(self, retry_exceptions=RETRY_EXCEPTIONS, retry_delay=DEFAULT_DELAY, timeout=DEFAULT_TIMEOUT, backoff=DEFAULT_BACKOFF): """ Wrapper around retrying that helps to handle common transient exceptions. This version retries the errors that `libcloud.utils.retry:MinimalRetry` retries and all errors of the exception types that are given. :param retry_exceptions: types of exceptions to retry on. :param retry_delay: retry delay between the attempts. :param timeout: maximum time to wait. :param backoff: multiplier added to delay between attempts. :Example: retry_request = Retry(retry_exceptions=(httplib.NotConnected,), timeout=1, retry_delay=1, backoff=1) retry_request(self.connection.request)() """ super().__init__(retry_delay=retry_delay, timeout=timeout, backoff=backoff) if retry_exceptions is None: retry_exceptions = RETRY_EXCEPTIONS self.retry_exceptions = retry_exceptions def should_retry(self, exception): return isinstance(exception, tuple(self.retry_exceptions)) class RetryForeverOnRateLimitError(Retry): """ This class is only here for backward compatibility reasons with pre-Libcloud v3.3.2. If works by ignoring timeout argument and retrying forever until API is returning 429 RateLimitReached errors. In most cases using this class is not a good idea since it can cause code to hang and retry for ever in case API continues to return retry limit reached. """ def __call__(self, func): def transform_ssl_error(function, *args, **kwargs): try: return function(*args, **kwargs) except ssl.SSLError as exc: if TRANSIENT_SSL_ERROR in str(exc): raise TransientSSLError(*exc.args) raise exc @wraps(func) def retry_loop(*args, **kwargs): current_delay = self.retry_delay end = datetime.now() + timedelta(seconds=self.timeout) while True: try: return transform_ssl_error(func, *args, **kwargs) except Exception as exc: if isinstance(exc, RateLimitReachedError): time.sleep(exc.retry_after) # Reset retries if we're told to wait due to rate # limiting current_delay = self.retry_delay end = datetime.now() + timedelta( seconds=exc.retry_after + self.timeout) elif datetime.now() >= end: raise elif self.should_retry(exc): time.sleep(current_delay) current_delay *= self.backoff else: raise return retry_loop