blob: 1fa74c5f747ed1c8bb3ee14432f604ffba982d49 [file] [log] [blame]
koder aka kdanilov652cd802015-04-13 12:21:07 +03001import re
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03002import os
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +03003import time
koder aka kdanilov416b87a2015-05-12 00:26:04 +03004import psutil
koder aka kdanilovafd98742015-04-24 01:27:22 +03005import socket
koder aka kdanilove21d7472015-02-14 19:02:04 -08006import logging
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08007import threading
8import contextlib
koder aka kdanilov652cd802015-04-13 12:21:07 +03009import subprocess
koder aka kdanilov4643fd62015-02-10 16:20:13 -080010
koder aka kdanilove21d7472015-02-14 19:02:04 -080011
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030012logger = logging.getLogger("wally")
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080013
14
koder aka kdanilov209e85d2015-04-27 23:11:05 +030015def is_ip(data):
16 if data.count('.') != 3:
17 return False
18
19 try:
20 for part in map(int, data.split('.')):
21 if part > 255 or part < 0:
22 raise ValueError()
23 except ValueError:
24 return False
25 return True
26
27
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030028class StopTestError(RuntimeError):
29 def __init__(self, reason, orig_exc=None):
30 RuntimeError.__init__(self, reason)
31 self.orig_exc = orig_exc
32
33
34def check_input_param(is_ok, message):
35 if not is_ok:
36 logger.error(message)
37 raise StopTestError(message)
38
39
koder aka kdanilove06762a2015-03-22 23:32:09 +020040def parse_creds(creds):
41 # parse user:passwd@host
42 user, passwd_host = creds.split(":", 1)
43
44 if '@' not in passwd_host:
45 passwd, host = passwd_host, None
46 else:
47 passwd, host = passwd_host.rsplit('@', 1)
48
49 return user, passwd, host
50
51
koder aka kdanilov2c473092015-03-29 17:12:13 +030052class TaksFinished(Exception):
53 pass
koder aka kdanilov4643fd62015-02-10 16:20:13 -080054
koder aka kdanilov2c473092015-03-29 17:12:13 +030055
56class Barrier(object):
57 def __init__(self, count):
58 self.count = count
59 self.curr_count = 0
60 self.cond = threading.Condition()
61 self.exited = False
62
63 def wait(self, timeout=None):
64 with self.cond:
65 if self.exited:
66 raise TaksFinished()
67
68 self.curr_count += 1
69 if self.curr_count == self.count:
70 self.curr_count = 0
71 self.cond.notify_all()
koder aka kdanilov652cd802015-04-13 12:21:07 +030072 return True
koder aka kdanilov4643fd62015-02-10 16:20:13 -080073 else:
koder aka kdanilov2c473092015-03-29 17:12:13 +030074 self.cond.wait(timeout=timeout)
koder aka kdanilov652cd802015-04-13 12:21:07 +030075 return False
koder aka kdanilov4643fd62015-02-10 16:20:13 -080076
koder aka kdanilov2c473092015-03-29 17:12:13 +030077 def exit(self):
78 with self.cond:
79 self.exited = True
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080080
81
82@contextlib.contextmanager
83def log_error(action, types=(Exception,)):
84 if not action.startswith("!"):
koder aka kdanilove21d7472015-02-14 19:02:04 -080085 logger.debug("Starts : " + action)
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080086 else:
87 action = action[1:]
88
89 try:
90 yield
91 except Exception as exc:
92 if isinstance(exc, types) and not isinstance(exc, StopIteration):
koder aka kdanilovec1b9732015-04-23 20:43:29 +030093 templ = "Error during {0} stage: {1!s}"
94 logger.debug(templ.format(action, exc))
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080095 raise
96
97
koder aka kdanilov2c473092015-03-29 17:12:13 +030098SMAP = dict(k=1024, m=1024 ** 2, g=1024 ** 3, t=1024 ** 4)
koder aka kdanilov8ad6e812015-03-22 14:42:18 +020099
100
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300101def ssize2b(ssize):
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200102 try:
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300103 if isinstance(ssize, (int, long)):
104 return ssize
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200105
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300106 ssize = ssize.lower()
koder aka kdanilov2c473092015-03-29 17:12:13 +0300107 if ssize[-1] in SMAP:
108 return int(ssize[:-1]) * SMAP[ssize[-1]]
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200109 return int(ssize)
110 except (ValueError, TypeError, AttributeError):
koder aka kdanilov2e928022015-04-08 13:47:15 +0300111 raise ValueError("Unknow size format {0!r}".format(ssize))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300112
113
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300114RSMAP = [('K', 1024),
115 ('M', 1024 ** 2),
116 ('G', 1024 ** 3),
117 ('T', 1024 ** 4)]
118
119
120def b2ssize(size):
121 if size < 1024:
122 return str(size)
123
124 for name, scale in RSMAP:
125 if size < 1024 * scale:
126 if size % scale == 0:
127 return "{0} {1}i".format(size // scale, name)
128 else:
129 return "{0:.1f} {1}i".format(float(size) / scale, name)
130
131 return "{0}{1}i".format(size // scale, name)
132
133
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300134RSMAP_10 = [('k', 1000),
135 ('m', 1000 ** 2),
136 ('g', 1000 ** 3),
137 ('t', 1000 ** 4)]
138
139
140def b2ssize_10(size):
141 if size < 1000:
142 return str(size)
143
144 for name, scale in RSMAP_10:
145 if size < 1000 * scale:
146 if size % scale == 0:
147 return "{0} {1}".format(size // scale, name)
148 else:
149 return "{0:.1f} {1}".format(float(size) / scale, name)
150
151 return "{0}{1}".format(size // scale, name)
152
153
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300154def run_locally(cmd, input_data="", timeout=20):
155 shell = isinstance(cmd, basestring)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300156 proc = subprocess.Popen(cmd,
157 shell=shell,
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300158 stdin=subprocess.PIPE,
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300159 stdout=subprocess.PIPE,
160 stderr=subprocess.PIPE)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300161 res = []
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300162
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300163 def thread_func():
164 rr = proc.communicate(input_data)
165 res.extend(rr)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300166
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300167 thread = threading.Thread(target=thread_func)
168 thread.daemon = True
169 thread.start()
170 thread.join(timeout)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300171
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300172 if thread.is_alive():
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300173
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300174 parent = psutil.Process(proc.pid)
175 for child in parent.children(recursive=True):
176 child.kill()
177 parent.kill()
178 thread.join()
179 raise RuntimeError("Local process timeout: " + str(cmd))
180
181 out, err = res
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300182 if 0 != proc.returncode:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300183 raise subprocess.CalledProcessError(proc.returncode,
184 cmd, out + err)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300185
186 return out
187
188
koder aka kdanilov652cd802015-04-13 12:21:07 +0300189def get_ip_for_target(target_ip):
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300190 if not is_ip(target_ip):
koder aka kdanilovafd98742015-04-24 01:27:22 +0300191 target_ip = socket.gethostbyname(target_ip)
192
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300193 first_dig = map(int, target_ip.split("."))
194 if first_dig == 127:
koder aka kdanilovafd98742015-04-24 01:27:22 +0300195 return '127.0.0.1'
196
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300197 data = run_locally('ip route get to'.split(" ") + [target_ip])
koder aka kdanilov652cd802015-04-13 12:21:07 +0300198
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300199 rr1 = r'{0} via [.0-9]+ dev (?P<dev>.*?) src (?P<ip>[.0-9]+)$'
200 rr1 = rr1.replace(" ", r'\s+')
201 rr1 = rr1.format(target_ip.replace('.', r'\.'))
202
203 rr2 = r'{0} dev (?P<dev>.*?) src (?P<ip>[.0-9]+)$'
204 rr2 = rr2.replace(" ", r'\s+')
205 rr2 = rr2.format(target_ip.replace('.', r'\.'))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300206
207 data_line = data.split("\n")[0].strip()
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300208 res1 = re.match(rr1, data_line)
209 res2 = re.match(rr2, data_line)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300210
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300211 if res1 is not None:
212 return res1.group('ip')
koder aka kdanilov652cd802015-04-13 12:21:07 +0300213
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300214 if res2 is not None:
215 return res2.group('ip')
216
217 raise OSError("Can't define interface for {0}".format(target_ip))
218
219
220def open_for_append_or_create(fname):
221 if not os.path.exists(fname):
222 return open(fname, "w")
223
224 fd = open(fname, 'r+')
225 fd.seek(0, os.SEEK_END)
226 return fd
227
228
229def sec_to_str(seconds):
230 h = seconds // 3600
231 m = (seconds % 3600) // 60
232 s = seconds % 60
233 return "{0}:{1:02d}:{2:02d}".format(h, m, s)
koder aka kdanilov168f6092015-04-19 02:33:38 +0300234
235
236def yamable(data):
237 if isinstance(data, (tuple, list)):
238 return map(yamable, data)
239
240 if isinstance(data, unicode):
241 return str(data)
242
243 if isinstance(data, dict):
244 res = {}
245 for k, v in data.items():
246 res[yamable(k)] = yamable(v)
247 return res
248
249 return data
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300250
251
252CLEANING = []
253
254
255def clean_resource(func, *args, **kwargs):
256 CLEANING.append((func, args, kwargs))
257
258
259def iter_clean_func():
260 while CLEANING != []:
261 yield CLEANING.pop()