blob: f03ec41ff4698e191f41935f3679ece15510f659 [file] [log] [blame]
#-*- coding: utf-8 -*-
import datetime
import logging
import os
import time
def singleton(class_):
instances = {}
def getinstance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return getinstance
logging.basicConfig()
logger = logging.getLogger('safe_rsync')
loglevel = os.environ.get('LOGLEVEL', 'INFO')
if loglevel not in ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'):
logger.warn('LOGLEVEL environment variable has wrong value=={}. Using '
'"INFO" by default.'.format(loglevel))
loglevel = 'INFO'
logger.setLevel(loglevel)
def logged(logger=None):
if logger is None:
logger = globals().get('logger')
def wrap(f):
def wrapped_f(*args, **kwargs):
logger.debug('Starting {}({}, {}) (defaults: {})'
''.format(f.__name__,
str(args),
str(kwargs),
str(f.__defaults__))
)
r = f(*args, **kwargs)
logger.debug('{} done with result "{}".'.
format(f.__name__, str(r)))
return r
return wrapped_f
return wrap
class ResultNotProduced(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class Retry(object):
"""
Waits while the function reaches the specified status.
:param function: function that returns some status
:param expected_status: status the machine should turn to
:param attempts: how many times to check status
:param timeout: timeout in seconds before attempts
:return: True if node moves to the specified status, False otherwise
:Examples:
Retry(timeout=3, attempts=10).wait(function, result, param1, param2)
Retry().wait_result(function, result, param1, param2)
"""
def __init__(self, timeout=5, attempts=10):
self.timeout = timeout
self.attempts = attempts
self.logger = globals().get('logger').getChild('Retry')
def wait_result(self, function, expected_result, *args, **kwargs):
self.logger.debug('Wait for {}() == {}...'
''.format(function.__name__, str(expected_result)))
@logged(self.logger)
def f():
return function(*args, **kwargs)
attempt = 1
while attempt <= self.attempts:
try:
result = f()
except Exception as e:
self.logger.error('Exception on function {}: {}'
''.format(function.__name__, str(e)))
raise
else:
if result == expected_result:
self.logger.debug('Got on attempt #{}:'.format(attempt))
return result
attempt += 1
time.sleep(self.timeout)
raise ResultNotProduced('Result "{}" was not produced during '
'{} attempts.'
''.format(expected_result, attempt - 1))