blob: 3fba2b04771ff51a497cf68e0d9ffc9fbe6b0578 [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 kdanilov4af1c1d2015-05-18 15:48:58 +03003import sys
koder aka kdanilovafd98742015-04-24 01:27:22 +03004import socket
koder aka kdanilove21d7472015-02-14 19:02:04 -08005import logging
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08006import threading
7import contextlib
koder aka kdanilov652cd802015-04-13 12:21:07 +03008import subprocess
koder aka kdanilov6ab4d432015-06-22 00:26:28 +03009import collections
koder aka kdanilov4643fd62015-02-10 16:20:13 -080010
koder aka kdanilovbb5fe072015-05-21 02:50:23 +030011try:
12 import psutil
13except ImportError:
14 psutil = None
15
koder aka kdanilove21d7472015-02-14 19:02:04 -080016
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030017logger = logging.getLogger("wally")
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080018
19
koder aka kdanilov209e85d2015-04-27 23:11:05 +030020def is_ip(data):
21 if data.count('.') != 3:
22 return False
23
24 try:
25 for part in map(int, data.split('.')):
26 if part > 255 or part < 0:
27 raise ValueError()
28 except ValueError:
29 return False
30 return True
31
32
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030033class StopTestError(RuntimeError):
34 def __init__(self, reason, orig_exc=None):
35 RuntimeError.__init__(self, reason)
36 self.orig_exc = orig_exc
37
38
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030039@contextlib.contextmanager
40def log_block(message, exc_logger=None):
41 logger.debug("Starts : " + message)
42 with log_error(message, exc_logger):
43 yield
44 # try:
45 # yield
46 # except Exception as exc:
47 # if isinstance(exc, types) and not isinstance(exc, StopIteration):
48 # templ = "Error during {0} stage: {1!s}"
49 # logger.debug(templ.format(action, exc))
50 # raise
51
52
53class log_error(object):
54 def __init__(self, message, exc_logger=None):
55 self.message = message
56 self.exc_logger = exc_logger
57
58 def __enter__(self):
59 return self
60
61 def __exit__(self, tp, value, traceback):
62 if value is None or isinstance(value, StopTestError):
63 return
64
65 if self.exc_logger is None:
66 exc_logger = sys._getframe(1).f_globals.get('logger', logger)
67 else:
68 exc_logger = self.exc_logger
69
70 exc_logger.exception(self.message, exc_info=(tp, value, traceback))
71 raise StopTestError(self.message, value)
72
73
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030074def check_input_param(is_ok, message):
75 if not is_ok:
76 logger.error(message)
77 raise StopTestError(message)
78
79
koder aka kdanilove06762a2015-03-22 23:32:09 +020080def parse_creds(creds):
81 # parse user:passwd@host
82 user, passwd_host = creds.split(":", 1)
83
84 if '@' not in passwd_host:
85 passwd, host = passwd_host, None
86 else:
87 passwd, host = passwd_host.rsplit('@', 1)
88
89 return user, passwd, host
90
91
koder aka kdanilov2c473092015-03-29 17:12:13 +030092class TaksFinished(Exception):
93 pass
koder aka kdanilov4643fd62015-02-10 16:20:13 -080094
koder aka kdanilov2c473092015-03-29 17:12:13 +030095
96class Barrier(object):
97 def __init__(self, count):
98 self.count = count
99 self.curr_count = 0
100 self.cond = threading.Condition()
101 self.exited = False
102
103 def wait(self, timeout=None):
104 with self.cond:
105 if self.exited:
106 raise TaksFinished()
107
108 self.curr_count += 1
109 if self.curr_count == self.count:
110 self.curr_count = 0
111 self.cond.notify_all()
koder aka kdanilov652cd802015-04-13 12:21:07 +0300112 return True
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800113 else:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300114 self.cond.wait(timeout=timeout)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300115 return False
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800116
koder aka kdanilov2c473092015-03-29 17:12:13 +0300117 def exit(self):
118 with self.cond:
119 self.exited = True
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800120
121
koder aka kdanilov2c473092015-03-29 17:12:13 +0300122SMAP = dict(k=1024, m=1024 ** 2, g=1024 ** 3, t=1024 ** 4)
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200123
124
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300125def ssize2b(ssize):
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200126 try:
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300127 if isinstance(ssize, (int, long)):
128 return ssize
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200129
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300130 ssize = ssize.lower()
koder aka kdanilov2c473092015-03-29 17:12:13 +0300131 if ssize[-1] in SMAP:
132 return int(ssize[:-1]) * SMAP[ssize[-1]]
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200133 return int(ssize)
134 except (ValueError, TypeError, AttributeError):
koder aka kdanilov2e928022015-04-08 13:47:15 +0300135 raise ValueError("Unknow size format {0!r}".format(ssize))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300136
137
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300138RSMAP = [('K', 1024),
139 ('M', 1024 ** 2),
140 ('G', 1024 ** 3),
141 ('T', 1024 ** 4)]
142
143
144def b2ssize(size):
145 if size < 1024:
146 return str(size)
147
148 for name, scale in RSMAP:
149 if size < 1024 * scale:
150 if size % scale == 0:
151 return "{0} {1}i".format(size // scale, name)
152 else:
153 return "{0:.1f} {1}i".format(float(size) / scale, name)
154
155 return "{0}{1}i".format(size // scale, name)
156
157
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300158RSMAP_10 = [('k', 1000),
159 ('m', 1000 ** 2),
160 ('g', 1000 ** 3),
161 ('t', 1000 ** 4)]
162
163
164def b2ssize_10(size):
165 if size < 1000:
166 return str(size)
167
168 for name, scale in RSMAP_10:
169 if size < 1000 * scale:
170 if size % scale == 0:
171 return "{0} {1}".format(size // scale, name)
172 else:
173 return "{0:.1f} {1}".format(float(size) / scale, name)
174
175 return "{0}{1}".format(size // scale, name)
176
177
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300178def run_locally(cmd, input_data="", timeout=20):
179 shell = isinstance(cmd, basestring)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300180 proc = subprocess.Popen(cmd,
181 shell=shell,
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300182 stdin=subprocess.PIPE,
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300183 stdout=subprocess.PIPE,
184 stderr=subprocess.PIPE)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300185 res = []
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300186
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300187 def thread_func():
188 rr = proc.communicate(input_data)
189 res.extend(rr)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300190
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300191 thread = threading.Thread(target=thread_func,
192 name="Local cmd execution")
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300193 thread.daemon = True
194 thread.start()
195 thread.join(timeout)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300196
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300197 if thread.is_alive():
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300198 if psutil is not None:
199 parent = psutil.Process(proc.pid)
200 for child in parent.children(recursive=True):
201 child.kill()
202 parent.kill()
203 else:
204 proc.kill()
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300205
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300206 thread.join()
207 raise RuntimeError("Local process timeout: " + str(cmd))
208
209 out, err = res
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300210 if 0 != proc.returncode:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300211 raise subprocess.CalledProcessError(proc.returncode,
212 cmd, out + err)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300213
214 return out
215
216
koder aka kdanilov652cd802015-04-13 12:21:07 +0300217def get_ip_for_target(target_ip):
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300218 if not is_ip(target_ip):
koder aka kdanilovafd98742015-04-24 01:27:22 +0300219 target_ip = socket.gethostbyname(target_ip)
220
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300221 first_dig = map(int, target_ip.split("."))
222 if first_dig == 127:
koder aka kdanilovafd98742015-04-24 01:27:22 +0300223 return '127.0.0.1'
224
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300225 data = run_locally('ip route get to'.split(" ") + [target_ip])
koder aka kdanilov652cd802015-04-13 12:21:07 +0300226
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300227 rr1 = r'{0} via [.0-9]+ dev (?P<dev>.*?) src (?P<ip>[.0-9]+)$'
228 rr1 = rr1.replace(" ", r'\s+')
229 rr1 = rr1.format(target_ip.replace('.', r'\.'))
230
231 rr2 = r'{0} dev (?P<dev>.*?) src (?P<ip>[.0-9]+)$'
232 rr2 = rr2.replace(" ", r'\s+')
233 rr2 = rr2.format(target_ip.replace('.', r'\.'))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300234
235 data_line = data.split("\n")[0].strip()
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300236 res1 = re.match(rr1, data_line)
237 res2 = re.match(rr2, data_line)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300238
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300239 if res1 is not None:
240 return res1.group('ip')
koder aka kdanilov652cd802015-04-13 12:21:07 +0300241
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300242 if res2 is not None:
243 return res2.group('ip')
244
245 raise OSError("Can't define interface for {0}".format(target_ip))
246
247
248def open_for_append_or_create(fname):
249 if not os.path.exists(fname):
250 return open(fname, "w")
251
252 fd = open(fname, 'r+')
253 fd.seek(0, os.SEEK_END)
254 return fd
255
256
257def sec_to_str(seconds):
258 h = seconds // 3600
259 m = (seconds % 3600) // 60
260 s = seconds % 60
261 return "{0}:{1:02d}:{2:02d}".format(h, m, s)
koder aka kdanilov168f6092015-04-19 02:33:38 +0300262
263
264def yamable(data):
265 if isinstance(data, (tuple, list)):
266 return map(yamable, data)
267
268 if isinstance(data, unicode):
269 return str(data)
270
271 if isinstance(data, dict):
272 res = {}
273 for k, v in data.items():
274 res[yamable(k)] = yamable(v)
275 return res
276
277 return data
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300278
279
280CLEANING = []
281
282
283def clean_resource(func, *args, **kwargs):
284 CLEANING.append((func, args, kwargs))
285
286
287def iter_clean_func():
288 while CLEANING != []:
289 yield CLEANING.pop()
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300290
291
292def flatten(data):
293 res = []
294 for i in data:
295 if isinstance(i, (list, tuple, set)):
296 res.extend(flatten(i))
297 else:
298 res.append(i)
299 return res
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300300
301
302def get_creds_openrc(path):
303 fc = open(path).read()
304
koder aka kdanilovb7197432015-07-15 00:40:43 +0300305 echo = 'echo "$OS_INSECURE:$OS_TENANT_NAME:$OS_USERNAME:$OS_PASSWORD@$OS_AUTH_URL"'
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300306
307 msg = "Failed to get creads from openrc file"
308 with log_error(msg):
309 data = run_locally(['/bin/bash'], input_data=fc + "\n" + echo)
310
311 msg = "Failed to get creads from openrc file: " + data
312 with log_error(msg):
313 data = data.strip()
koder aka kdanilovb7197432015-07-15 00:40:43 +0300314 insecure_str, user, tenant, passwd_auth_url = data.split(':', 3)
315 insecure = (insecure_str in ('1', 'True', 'true'))
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300316 passwd, auth_url = passwd_auth_url.rsplit("@", 1)
317 assert (auth_url.startswith("https://") or
318 auth_url.startswith("http://"))
319
koder aka kdanilovb7197432015-07-15 00:40:43 +0300320 return user, passwd, tenant, auth_url, insecure
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300321
322
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300323os_release = collections.namedtuple("Distro", ["distro", "release", "arch"])
324
325
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300326def get_os(run_func):
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300327 arch = run_func("arch", nolog=True).strip()
328
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300329 try:
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300330 run_func("ls -l /etc/redhat-release", nolog=True)
331 return os_release('redhat', None, arch)
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300332 except:
333 pass
334
335 try:
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300336 run_func("ls -l /etc/debian_version", nolog=True)
337
338 release = None
339 for line in run_func("lsb_release -a", nolog=True).split("\n"):
340 if ':' not in line:
341 continue
342 opt, val = line.split(":", 1)
343
344 if opt == 'Codename':
345 release = val.strip()
346
347 return os_release('ubuntu', release, arch)
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300348 except:
349 pass
350
351 raise RuntimeError("Unknown os")
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300352
353
354@contextlib.contextmanager
355def empty_ctx(val=None):
356 yield val
357
358
359def mkdirs_if_unxists(path):
360 if not os.path.exists(path):
361 os.makedirs(path)
362
363
364def log_nodes_statistic(nodes):
365 logger.info("Found {0} nodes total".format(len(nodes)))
366 per_role = collections.defaultdict(lambda: 0)
367 for node in nodes:
368 for role in node.roles:
369 per_role[role] += 1
370
371 for role, count in sorted(per_role.items()):
372 logger.debug("Found {0} nodes with role {1}".format(count, role))
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300373
374
375def which(program):
376 def is_exe(fpath):
377 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
378
379 for path in os.environ["PATH"].split(os.pathsep):
380 path = path.strip('"')
381 exe_file = os.path.join(path, program)
382 if is_exe(exe_file):
383 return exe_file
384
385 return None