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