blob: 14e3a6ccf15fb07f728e0d2e44c34ad0e0b0ca45 [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 kdanilov22d134e2016-11-08 11:33:19 +02004import time
5import uuid
koder aka kdanilovafd98742015-04-24 01:27:22 +03006import socket
koder aka kdanilove21d7472015-02-14 19:02:04 -08007import logging
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +03008import ipaddress
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08009import threading
10import contextlib
koder aka kdanilov652cd802015-04-13 12:21:07 +030011import subprocess
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030012import collections
koder aka kdanilov4643fd62015-02-10 16:20:13 -080013
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020014from .node_interfaces import IRPCNode
koder aka kdanilov22d134e2016-11-08 11:33:19 +020015from typing import Any, Tuple, Union, List, Iterator, Dict, Callable, Iterable, Optional, IO, Sequence
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030016
koder aka kdanilovbb5fe072015-05-21 02:50:23 +030017try:
18 import psutil
19except ImportError:
20 psutil = None
21
koder aka kdanilov22d134e2016-11-08 11:33:19 +020022try:
23 from petname import Generate as pet_generate
24except ImportError:
25 def pet_generate(x: str, y: str) -> str:
26 return str(uuid.uuid4())
27
koder aka kdanilove21d7472015-02-14 19:02:04 -080028
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030029logger = logging.getLogger("wally")
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080030
31
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030032def is_ip(data: str) -> bool:
koder aka kdanilov209e85d2015-04-27 23:11:05 +030033 try:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030034 ipaddress.ip_address(data)
35 return True
koder aka kdanilov209e85d2015-04-27 23:11:05 +030036 except ValueError:
37 return False
koder aka kdanilov209e85d2015-04-27 23:11:05 +030038
39
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030040class StopTestError(RuntimeError):
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030041 pass
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030042
43
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030044class LogError:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020045 def __init__(self, message: str, exc_logger: logging.Logger = None) -> None:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030046 self.message = message
47 self.exc_logger = exc_logger
48
koder aka kdanilov22d134e2016-11-08 11:33:19 +020049 def __enter__(self) -> 'LogError':
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030050 return self
51
koder aka kdanilov22d134e2016-11-08 11:33:19 +020052 def __exit__(self, tp: type, value: Exception, traceback: Any) -> bool:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030053 if value is None or isinstance(value, StopTestError):
koder aka kdanilov22d134e2016-11-08 11:33:19 +020054 return False
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030055
56 if self.exc_logger is None:
57 exc_logger = sys._getframe(1).f_globals.get('logger', logger)
58 else:
59 exc_logger = self.exc_logger
60
61 exc_logger.exception(self.message, exc_info=(tp, value, traceback))
koder aka kdanilov22d134e2016-11-08 11:33:19 +020062 raise StopTestError(self.message) from value
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030063
64
koder aka kdanilov22d134e2016-11-08 11:33:19 +020065def log_block(message: str, exc_logger:logging.Logger = None) -> LogError:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030066 logger.debug("Starts : " + message)
67 return LogError(message, exc_logger)
68
69
70def check_input_param(is_ok: bool, message: str) -> None:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030071 if not is_ok:
72 logger.error(message)
73 raise StopTestError(message)
74
75
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030076def parse_creds(creds: str) -> Tuple[str, str, str]:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020077 """Parse simple credentials format user[:passwd]@host"""
koder aka kdanilove06762a2015-03-22 23:32:09 +020078 user, passwd_host = creds.split(":", 1)
79
80 if '@' not in passwd_host:
81 passwd, host = passwd_host, None
82 else:
83 passwd, host = passwd_host.rsplit('@', 1)
84
85 return user, passwd, host
86
87
koder aka kdanilov22d134e2016-11-08 11:33:19 +020088class TaskFinished(Exception):
koder aka kdanilov2c473092015-03-29 17:12:13 +030089 pass
koder aka kdanilov4643fd62015-02-10 16:20:13 -080090
koder aka kdanilov2c473092015-03-29 17:12:13 +030091
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030092class Barrier:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020093 def __init__(self, count: int) -> None:
koder aka kdanilov2c473092015-03-29 17:12:13 +030094 self.count = count
95 self.curr_count = 0
96 self.cond = threading.Condition()
97 self.exited = False
98
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030099 def wait(self, timeout: int=None) -> bool:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300100 with self.cond:
101 if self.exited:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200102 raise TaskFinished()
koder aka kdanilov2c473092015-03-29 17:12:13 +0300103
104 self.curr_count += 1
105 if self.curr_count == self.count:
106 self.curr_count = 0
107 self.cond.notify_all()
koder aka kdanilov652cd802015-04-13 12:21:07 +0300108 return True
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800109 else:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300110 self.cond.wait(timeout=timeout)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300111 return False
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800112
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300113 def exit(self) -> None:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300114 with self.cond:
115 self.exited = True
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800116
117
koder aka kdanilov2c473092015-03-29 17:12:13 +0300118SMAP = dict(k=1024, m=1024 ** 2, g=1024 ** 3, t=1024 ** 4)
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200119
120
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300121def ssize2b(ssize: Union[str, int]) -> int:
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200122 try:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300123 if isinstance(ssize, int):
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300124 return ssize
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200125
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300126 ssize = ssize.lower()
koder aka kdanilov2c473092015-03-29 17:12:13 +0300127 if ssize[-1] in SMAP:
128 return int(ssize[:-1]) * SMAP[ssize[-1]]
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200129 return int(ssize)
130 except (ValueError, TypeError, AttributeError):
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300131 raise ValueError("Unknow size format {!r}".format(ssize))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300132
133
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300134RSMAP = [('K', 1024),
135 ('M', 1024 ** 2),
136 ('G', 1024 ** 3),
137 ('T', 1024 ** 4)]
138
139
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300140def b2ssize(size: int) -> str:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300141 if size < 1024:
142 return str(size)
143
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200144 # make mypy happy
145 scale = 1
146 name = ""
147
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300148 for name, scale in RSMAP:
149 if size < 1024 * scale:
150 if size % scale == 0:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300151 return "{} {}i".format(size // scale, name)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300152 else:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300153 return "{:.1f} {}i".format(float(size) / scale, name)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300154
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300155 return "{}{}i".format(size // scale, name)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300156
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
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300164def b2ssize_10(size: int) -> str:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300165 if size < 1000:
166 return str(size)
167
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200168 # make mypy happy
169 scale = 1
170 name = ""
171
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300172 for name, scale in RSMAP_10:
173 if size < 1000 * scale:
174 if size % scale == 0:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300175 return "{} {}".format(size // scale, name)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300176 else:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300177 return "{:.1f} {}".format(float(size) / scale, name)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300178
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300179 return "{}{}".format(size // scale, name)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300180
181
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300182def run_locally(cmd: Union[str, List[str]], input_data: str="", timeout:int =20) -> str:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200183 if isinstance(cmd, str):
184 shell = True
185 cmd_str = cmd
186 else:
187 cmd_str = " ".join(cmd)
188
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300189 proc = subprocess.Popen(cmd,
190 shell=shell,
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300191 stdin=subprocess.PIPE,
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300192 stdout=subprocess.PIPE,
193 stderr=subprocess.PIPE)
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200194 res = [] # type: List[Tuple[bytes, bytes]]
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300195
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300196 def thread_func() -> None:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200197 rr = proc.communicate(input_data.encode("utf8"))
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300198 res.extend(rr)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300199
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300200 thread = threading.Thread(target=thread_func,
201 name="Local cmd execution")
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300202 thread.daemon = True
203 thread.start()
204 thread.join(timeout)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300205
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300206 if thread.is_alive():
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300207 if psutil is not None:
208 parent = psutil.Process(proc.pid)
209 for child in parent.children(recursive=True):
210 child.kill()
211 parent.kill()
212 else:
213 proc.kill()
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300214
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300215 thread.join()
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200216 raise RuntimeError("Local process timeout: " + cmd_str)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300217
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200218 stdout_data, stderr_data = zip(*res) # type: List[bytes], List[bytes]
219
220 out = b"".join(stdout_data).decode("utf8")
221 err = b"".join(stderr_data).decode("utf8")
222
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300223 if 0 != proc.returncode:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300224 raise subprocess.CalledProcessError(proc.returncode,
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200225 cmd_str, out + err)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300226
227 return out
228
229
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300230def get_ip_for_target(target_ip: str) -> str:
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300231 if not is_ip(target_ip):
koder aka kdanilovafd98742015-04-24 01:27:22 +0300232 target_ip = socket.gethostbyname(target_ip)
233
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300234 first_dig = map(int, target_ip.split("."))
235 if first_dig == 127:
koder aka kdanilovafd98742015-04-24 01:27:22 +0300236 return '127.0.0.1'
237
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300238 data = run_locally('ip route get to'.split(" ") + [target_ip])
koder aka kdanilov652cd802015-04-13 12:21:07 +0300239
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300240 rr1 = r'{0} via [.0-9]+ dev (?P<dev>.*?) src (?P<ip>[.0-9]+)$'
241 rr1 = rr1.replace(" ", r'\s+')
242 rr1 = rr1.format(target_ip.replace('.', r'\.'))
243
244 rr2 = r'{0} dev (?P<dev>.*?) src (?P<ip>[.0-9]+)$'
245 rr2 = rr2.replace(" ", r'\s+')
246 rr2 = rr2.format(target_ip.replace('.', r'\.'))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300247
248 data_line = data.split("\n")[0].strip()
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300249 res1 = re.match(rr1, data_line)
250 res2 = re.match(rr2, data_line)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300251
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300252 if res1 is not None:
253 return res1.group('ip')
koder aka kdanilov652cd802015-04-13 12:21:07 +0300254
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300255 if res2 is not None:
256 return res2.group('ip')
257
258 raise OSError("Can't define interface for {0}".format(target_ip))
259
260
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200261def open_for_append_or_create(fname: str) -> IO:
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300262 if not os.path.exists(fname):
263 return open(fname, "w")
264
265 fd = open(fname, 'r+')
266 fd.seek(0, os.SEEK_END)
267 return fd
268
269
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300270def sec_to_str(seconds: int) -> str:
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300271 h = seconds // 3600
272 m = (seconds % 3600) // 60
273 s = seconds % 60
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300274 return "{}:{:02d}:{:02d}".format(h, m, s)
koder aka kdanilov168f6092015-04-19 02:33:38 +0300275
276
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300277def yamable(data: Any) -> Any:
koder aka kdanilov168f6092015-04-19 02:33:38 +0300278 if isinstance(data, (tuple, list)):
279 return map(yamable, data)
280
koder aka kdanilov168f6092015-04-19 02:33:38 +0300281 if isinstance(data, dict):
282 res = {}
283 for k, v in data.items():
284 res[yamable(k)] = yamable(v)
285 return res
286
287 return data
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300288
289
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200290CLEANING = [] # type: List[Tuple[Callable[..., Any], List[Any], Dict[str, Any]]]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300291
292
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200293def clean_resource(func: Callable[..., Any], *args: Any, **kwargs: Any) -> None:
294 CLEANING.append((func, list(args), kwargs))
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300295
296
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200297def iter_clean_func() -> Iterator[Tuple[Callable[..., Any], List[Any], Dict[str, Any]]]:
298 while CLEANING:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300299 yield CLEANING.pop()
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300300
301
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300302def flatten(data: Iterable[Any]) -> List[Any]:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300303 res = []
304 for i in data:
305 if isinstance(i, (list, tuple, set)):
306 res.extend(flatten(i))
307 else:
308 res.append(i)
309 return res
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300310
311
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200312def get_creds_openrc(path: str) -> Tuple[str, str, str, str, bool]:
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300313 fc = open(path).read()
314
koder aka kdanilovb7197432015-07-15 00:40:43 +0300315 echo = 'echo "$OS_INSECURE:$OS_TENANT_NAME:$OS_USERNAME:$OS_PASSWORD@$OS_AUTH_URL"'
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300316
317 msg = "Failed to get creads from openrc file"
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300318 with LogError(msg):
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300319 data = run_locally(['/bin/bash'], input_data=fc + "\n" + echo)
320
321 msg = "Failed to get creads from openrc file: " + data
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300322 with LogError(msg):
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300323 data = data.strip()
koder aka kdanilovb7197432015-07-15 00:40:43 +0300324 insecure_str, user, tenant, passwd_auth_url = data.split(':', 3)
325 insecure = (insecure_str in ('1', 'True', 'true'))
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300326 passwd, auth_url = passwd_auth_url.rsplit("@", 1)
327 assert (auth_url.startswith("https://") or
328 auth_url.startswith("http://"))
329
koder aka kdanilovb7197432015-07-15 00:40:43 +0300330 return user, passwd, tenant, auth_url, insecure
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300331
332
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300333os_release = collections.namedtuple("Distro", ["distro", "release", "arch"])
334
335
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200336def get_os(node: IRemoteNode) -> os_release:
337 """return os type, release and architecture for node.
338 """
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300339 arch = node.run("arch", nolog=True).strip()
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300340
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300341 try:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300342 node.run("ls -l /etc/redhat-release", nolog=True)
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300343 return os_release('redhat', None, arch)
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300344 except:
345 pass
346
347 try:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300348 node.run("ls -l /etc/debian_version", nolog=True)
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300349
350 release = None
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300351 for line in node.run("lsb_release -a", nolog=True).split("\n"):
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300352 if ':' not in line:
353 continue
354 opt, val = line.split(":", 1)
355
356 if opt == 'Codename':
357 release = val.strip()
358
359 return os_release('ubuntu', release, arch)
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300360 except:
361 pass
362
363 raise RuntimeError("Unknown os")
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300364
365
366@contextlib.contextmanager
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200367def empty_ctx(val: Any=None) -> Iterator[Any]:
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300368 yield val
369
370
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300371def mkdirs_if_unxists(path: str) -> None:
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300372 if not os.path.exists(path):
373 os.makedirs(path)
374
375
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200376def log_nodes_statistic(nodes: Sequence[IRemoteNode]) -> None:
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300377 logger.info("Found {0} nodes total".format(len(nodes)))
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200378
379 per_role = collections.defaultdict(int) # type: Dict[str, int]
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300380 for node in nodes:
381 for role in node.roles:
382 per_role[role] += 1
383
384 for role, count in sorted(per_role.items()):
385 logger.debug("Found {0} nodes with role {1}".format(count, role))
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300386
387
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300388def which(program: str) -> Optional[str]:
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300389 def is_exe(fpath):
390 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
391
392 for path in os.environ["PATH"].split(os.pathsep):
393 path = path.strip('"')
394 exe_file = os.path.join(path, program)
395 if is_exe(exe_file):
396 return exe_file
397
398 return None
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200399
400
401def get_uniq_path_uuid(path: str, max_iter: int = 10) -> Tuple[str, str]:
402 for i in range(max_iter):
403 run_uuid = pet_generate(2, "_")
404 results_dir = os.path.join(path, run_uuid)
405 if not os.path.exists(results_dir):
406 break
407 else:
408 run_uuid = str(uuid.uuid4())
409 results_dir = os.path.join(path, run_uuid)
410
411 return results_dir, run_uuid
412
413
414class Timeout:
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200415 def __init__(self, timeout: int, message: str = None, min_tick: int = 1, no_exc: bool = False) -> None:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200416 self.etime = time.time() + timeout
417 self.message = message
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200418 self.min_tick = min_tick
419 self.prev_tick_at = time.time()
420 self.no_exc = no_exc
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200421
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200422 def tick(self) -> bool:
423 ctime = time.time()
424 if ctime > self.etime:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200425 if self.message:
426 msg = "Timeout: {}".format(self.message)
427 else:
428 msg = "Timeout"
429
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200430 if self.no_exc:
431 return False
432 raise TimeoutError(msg)
433
434 dtime = self.min_tick - (ctime - self.prev_tick_at)
435 if dtime > 0:
436 time.sleep(dtime)
437
438 self.prev_tick_at = time.time()
439 return True
440
441 def __iter__(self):
442 return self
443
444 def __next__(self) -> float:
445 if not self.tick():
446 raise StopIteration()
447 return self.etime - time.time()