blob: f03ec41ff4698e191f41935f3679ece15510f659 [file] [log] [blame]
Max Rasskazovc018e382015-06-03 15:22:04 +03001#-*- coding: utf-8 -*-
2
3
Max Rasskazov55e1bbc2015-06-05 03:29:29 +03004import datetime
Max Rasskazovc018e382015-06-03 15:22:04 +03005import logging
6import os
7import time
8
9
Max Rasskazov55e1bbc2015-06-05 03:29:29 +030010def singleton(class_):
11 instances = {}
12 def getinstance(*args, **kwargs):
13 if class_ not in instances:
14 instances[class_] = class_(*args, **kwargs)
15 return instances[class_]
16 return getinstance
17
18
Max Rasskazovc018e382015-06-03 15:22:04 +030019logging.basicConfig()
20logger = logging.getLogger('safe_rsync')
21
22loglevel = os.environ.get('LOGLEVEL', 'INFO')
23if loglevel not in ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'):
24 logger.warn('LOGLEVEL environment variable has wrong value=={}. Using '
25 '"INFO" by default.'.format(loglevel))
26 loglevel = 'INFO'
27logger.setLevel(loglevel)
28
29
30def logged(logger=None):
31 if logger is None:
32 logger = globals().get('logger')
33
34 def wrap(f):
35 def wrapped_f(*args, **kwargs):
36 logger.debug('Starting {}({}, {}) (defaults: {})'
37 ''.format(f.__name__,
38 str(args),
39 str(kwargs),
40 str(f.__defaults__))
41 )
42 r = f(*args, **kwargs)
43 logger.debug('{} done with result "{}".'.
44 format(f.__name__, str(r)))
45 return r
46 return wrapped_f
47 return wrap
48
49
Max Rasskazovc018e382015-06-03 15:22:04 +030050class ResultNotProduced(Exception):
51 def __init__(self, value):
52 self.value = value
53
54 def __str__(self):
55 return repr(self.value)
56
57
58class Retry(object):
59 """
60 Waits while the function reaches the specified status.
61
62 :param function: function that returns some status
63 :param expected_status: status the machine should turn to
64 :param attempts: how many times to check status
65 :param timeout: timeout in seconds before attempts
66 :return: True if node moves to the specified status, False otherwise
67 :Examples:
68 Retry(timeout=3, attempts=10).wait(function, result, param1, param2)
69 Retry().wait_result(function, result, param1, param2)
70 """
71
72 def __init__(self, timeout=5, attempts=10):
73 self.timeout = timeout
74 self.attempts = attempts
75 self.logger = globals().get('logger').getChild('Retry')
76
77 def wait_result(self, function, expected_result, *args, **kwargs):
78
79 self.logger.debug('Wait for {}() == {}...'
80 ''.format(function.__name__, str(expected_result)))
81
82 @logged(self.logger)
83 def f():
84 return function(*args, **kwargs)
85
86 attempt = 1
87 while attempt <= self.attempts:
88 try:
89 result = f()
90 except Exception as e:
91 self.logger.error('Exception on function {}: {}'
92 ''.format(function.__name__, str(e)))
93 raise
94 else:
95 if result == expected_result:
96 self.logger.debug('Got on attempt #{}:'.format(attempt))
97 return result
98 attempt += 1
99 time.sleep(self.timeout)
100
101 raise ResultNotProduced('Result "{}" was not produced during '
102 '{} attempts.'
103 ''.format(expected_result, attempt - 1))