blob: 13cd675e8a0220a173892b9a06d356993ab41858 [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 kdanilovffaf48d2016-12-27 02:25:29 +02003import abc
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +03004import sys
koder aka kdanilovffaf48d2016-12-27 02:25:29 +02005import math
koder aka kdanilov22d134e2016-11-08 11:33:19 +02006import time
7import uuid
koder aka kdanilovafd98742015-04-24 01:27:22 +03008import socket
koder aka kdanilove21d7472015-02-14 19:02:04 -08009import logging
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030010import ipaddress
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080011import threading
12import contextlib
koder aka kdanilov652cd802015-04-13 12:21:07 +030013import subprocess
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030014import collections
koder aka kdanilov4643fd62015-02-10 16:20:13 -080015
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020016from .node_interfaces import IRPCNode
koder aka kdanilovffaf48d2016-12-27 02:25:29 +020017from typing import (Any, Tuple, Union, List, Iterator, Dict, Iterable, Optional,
18 IO, Sequence, NamedTuple, cast, TypeVar)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030019
koder aka kdanilovbb5fe072015-05-21 02:50:23 +030020try:
21 import psutil
22except ImportError:
23 psutil = None
24
koder aka kdanilov22d134e2016-11-08 11:33:19 +020025try:
26 from petname import Generate as pet_generate
27except ImportError:
28 def pet_generate(x: str, y: str) -> str:
29 return str(uuid.uuid4())
30
koder aka kdanilove21d7472015-02-14 19:02:04 -080031
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030032logger = logging.getLogger("wally")
koder aka kdanilovffaf48d2016-12-27 02:25:29 +020033TNumber = TypeVar('TNumber', int, float)
34Number = Union[int, float]
koder aka kdanilov209e85d2015-04-27 23:11:05 +030035
36
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030037class StopTestError(RuntimeError):
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030038 pass
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030039
40
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030041class LogError:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020042 def __init__(self, message: str, exc_logger: logging.Logger = None) -> None:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030043 self.message = message
44 self.exc_logger = exc_logger
45
koder aka kdanilov22d134e2016-11-08 11:33:19 +020046 def __enter__(self) -> 'LogError':
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030047 return self
48
koder aka kdanilov22d134e2016-11-08 11:33:19 +020049 def __exit__(self, tp: type, value: Exception, traceback: Any) -> bool:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030050 if value is None or isinstance(value, StopTestError):
koder aka kdanilov22d134e2016-11-08 11:33:19 +020051 return False
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030052
53 if self.exc_logger is None:
54 exc_logger = sys._getframe(1).f_globals.get('logger', logger)
55 else:
56 exc_logger = self.exc_logger
57
58 exc_logger.exception(self.message, exc_info=(tp, value, traceback))
koder aka kdanilov22d134e2016-11-08 11:33:19 +020059 raise StopTestError(self.message) from value
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030060
61
koder aka kdanilovffaf48d2016-12-27 02:25:29 +020062class IStorable(metaclass=abc.ABCMeta):
63 """Interface for type, which can be stored"""
64
65 @abc.abstractmethod
66 def raw(self) -> Dict[str, Any]:
67 pass
68
69 @abc.abstractclassmethod
70 def fromraw(cls, data: Dict[str, Any]) -> 'IStorable':
71 pass
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030072
73
koder aka kdanilovffaf48d2016-12-27 02:25:29 +020074Basic = Union[int, str, bytes, bool, None]
75Storable = Union[IStorable, Dict[str, Any], List[Any], int, str, bytes, bool, None]
koder aka kdanilove06762a2015-03-22 23:32:09 +020076
77
koder aka kdanilov22d134e2016-11-08 11:33:19 +020078class TaskFinished(Exception):
koder aka kdanilov2c473092015-03-29 17:12:13 +030079 pass
koder aka kdanilov4643fd62015-02-10 16:20:13 -080080
koder aka kdanilov2c473092015-03-29 17:12:13 +030081
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030082class Barrier:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020083 def __init__(self, count: int) -> None:
koder aka kdanilov2c473092015-03-29 17:12:13 +030084 self.count = count
85 self.curr_count = 0
86 self.cond = threading.Condition()
87 self.exited = False
88
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030089 def wait(self, timeout: int=None) -> bool:
koder aka kdanilov2c473092015-03-29 17:12:13 +030090 with self.cond:
91 if self.exited:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020092 raise TaskFinished()
koder aka kdanilov2c473092015-03-29 17:12:13 +030093
94 self.curr_count += 1
95 if self.curr_count == self.count:
96 self.curr_count = 0
97 self.cond.notify_all()
koder aka kdanilov652cd802015-04-13 12:21:07 +030098 return True
koder aka kdanilov4643fd62015-02-10 16:20:13 -080099 else:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300100 self.cond.wait(timeout=timeout)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300101 return False
koder aka kdanilov4643fd62015-02-10 16:20:13 -0800102
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300103 def exit(self) -> None:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300104 with self.cond:
105 self.exited = True
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800106
107
koder aka kdanilovffaf48d2016-12-27 02:25:29 +0200108class Timeout(Iterable[float]):
109 def __init__(self, timeout: int, message: str = None, min_tick: int = 1, no_exc: bool = False) -> None:
110 self.end_time = time.time() + timeout
111 self.message = message
112 self.min_tick = min_tick
113 self.prev_tick_at = time.time()
114 self.no_exc = no_exc
115
116 def tick(self) -> bool:
117 current_time = time.time()
118
119 if current_time > self.end_time:
120 if self.message:
121 msg = "Timeout: {}".format(self.message)
122 else:
123 msg = "Timeout"
124
125 if self.no_exc:
126 return False
127
128 raise TimeoutError(msg)
129
130 sleep_time = self.min_tick - (current_time - self.prev_tick_at)
131 if sleep_time > 0:
132 time.sleep(sleep_time)
133 self.prev_tick_at = time.time()
134 else:
135 self.prev_tick_at = current_time
136
137 return True
138
139 def __iter__(self) -> Iterator[float]:
140 return cast(Iterator[float], self)
141
142 def __next__(self) -> float:
143 if not self.tick():
144 raise StopIteration()
145 return self.end_time - time.time()
146
147
148def greater_digit_pos(val: Number) -> int:
149 return int(math.floor(math.log10(val))) + 1
150
151
152def round_digits(val: TNumber, num_digits: int = 3) -> TNumber:
153 pow = 10 ** (greater_digit_pos(val) - num_digits)
154 return type(val)(int(val / pow) * pow)
155
156
157def is_ip(data: str) -> bool:
158 try:
159 ipaddress.ip_address(data)
160 return True
161 except ValueError:
162 return False
163
164
165def log_block(message: str, exc_logger:logging.Logger = None) -> LogError:
166 logger.debug("Starts : " + message)
167 return LogError(message, exc_logger)
168
169
170def check_input_param(is_ok: bool, message: str) -> None:
171 if not is_ok:
172 logger.error(message)
173 raise StopTestError(message)
174
175
176def parse_creds(creds: str) -> Tuple[str, str, str]:
177 """Parse simple credentials format user[:passwd]@host"""
178 user, passwd_host = creds.split(":", 1)
179
180 if '@' not in passwd_host:
181 passwd, host = passwd_host, None
182 else:
183 passwd, host = passwd_host.rsplit('@', 1)
184
185 return user, passwd, host
186
187
koder aka kdanilov2c473092015-03-29 17:12:13 +0300188SMAP = dict(k=1024, m=1024 ** 2, g=1024 ** 3, t=1024 ** 4)
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200189
190
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300191def ssize2b(ssize: Union[str, int]) -> int:
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200192 try:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300193 if isinstance(ssize, int):
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300194 return ssize
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200195
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300196 ssize = ssize.lower()
koder aka kdanilov2c473092015-03-29 17:12:13 +0300197 if ssize[-1] in SMAP:
198 return int(ssize[:-1]) * SMAP[ssize[-1]]
koder aka kdanilov8ad6e812015-03-22 14:42:18 +0200199 return int(ssize)
200 except (ValueError, TypeError, AttributeError):
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300201 raise ValueError("Unknow size format {!r}".format(ssize))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300202
203
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300204RSMAP = [('K', 1024),
205 ('M', 1024 ** 2),
206 ('G', 1024 ** 3),
207 ('T', 1024 ** 4)]
208
209
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300210def b2ssize(size: int) -> str:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300211 if size < 1024:
212 return str(size)
213
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200214 # make mypy happy
215 scale = 1
216 name = ""
217
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300218 for name, scale in RSMAP:
219 if size < 1024 * scale:
220 if size % scale == 0:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300221 return "{} {}i".format(size // scale, name)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300222 else:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300223 return "{:.1f} {}i".format(float(size) / scale, name)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300224
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300225 return "{}{}i".format(size // scale, name)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300226
227
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300228RSMAP_10 = [('k', 1000),
229 ('m', 1000 ** 2),
230 ('g', 1000 ** 3),
231 ('t', 1000 ** 4)]
232
233
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300234def b2ssize_10(size: int) -> str:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300235 if size < 1000:
236 return str(size)
237
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200238 # make mypy happy
239 scale = 1
240 name = ""
241
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300242 for name, scale in RSMAP_10:
243 if size < 1000 * scale:
244 if size % scale == 0:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300245 return "{} {}".format(size // scale, name)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300246 else:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300247 return "{:.1f} {}".format(float(size) / scale, name)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300248
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300249 return "{}{}".format(size // scale, name)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300250
251
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300252def run_locally(cmd: Union[str, List[str]], input_data: str="", timeout:int =20) -> str:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200253 if isinstance(cmd, str):
254 shell = True
255 cmd_str = cmd
256 else:
koder aka kdanilov73084622016-11-16 21:51:08 +0200257 shell = False
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200258 cmd_str = " ".join(cmd)
259
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300260 proc = subprocess.Popen(cmd,
261 shell=shell,
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300262 stdin=subprocess.PIPE,
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300263 stdout=subprocess.PIPE,
264 stderr=subprocess.PIPE)
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200265 res = [] # type: List[Tuple[bytes, bytes]]
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300266
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300267 def thread_func() -> None:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200268 rr = proc.communicate(input_data.encode("utf8"))
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300269 res.extend(rr)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300270
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300271 thread = threading.Thread(target=thread_func,
272 name="Local cmd execution")
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300273 thread.daemon = True
274 thread.start()
275 thread.join(timeout)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300276
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300277 if thread.is_alive():
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300278 if psutil is not None:
279 parent = psutil.Process(proc.pid)
280 for child in parent.children(recursive=True):
281 child.kill()
282 parent.kill()
283 else:
284 proc.kill()
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300285
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300286 thread.join()
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200287 raise RuntimeError("Local process timeout: " + cmd_str)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300288
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200289 stdout_data, stderr_data = zip(*res) # type: List[bytes], List[bytes]
290
291 out = b"".join(stdout_data).decode("utf8")
292 err = b"".join(stderr_data).decode("utf8")
293
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300294 if 0 != proc.returncode:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300295 raise subprocess.CalledProcessError(proc.returncode,
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200296 cmd_str, out + err)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300297
298 return out
299
300
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300301def get_ip_for_target(target_ip: str) -> str:
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300302 if not is_ip(target_ip):
koder aka kdanilovafd98742015-04-24 01:27:22 +0300303 target_ip = socket.gethostbyname(target_ip)
304
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300305 first_dig = map(int, target_ip.split("."))
306 if first_dig == 127:
koder aka kdanilovafd98742015-04-24 01:27:22 +0300307 return '127.0.0.1'
308
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300309 data = run_locally('ip route get to'.split(" ") + [target_ip])
koder aka kdanilov652cd802015-04-13 12:21:07 +0300310
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300311 rr1 = r'{0} via [.0-9]+ dev (?P<dev>.*?) src (?P<ip>[.0-9]+)$'
312 rr1 = rr1.replace(" ", r'\s+')
313 rr1 = rr1.format(target_ip.replace('.', r'\.'))
314
315 rr2 = r'{0} dev (?P<dev>.*?) src (?P<ip>[.0-9]+)$'
316 rr2 = rr2.replace(" ", r'\s+')
317 rr2 = rr2.format(target_ip.replace('.', r'\.'))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300318
319 data_line = data.split("\n")[0].strip()
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300320 res1 = re.match(rr1, data_line)
321 res2 = re.match(rr2, data_line)
koder aka kdanilov652cd802015-04-13 12:21:07 +0300322
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300323 if res1 is not None:
324 return res1.group('ip')
koder aka kdanilov652cd802015-04-13 12:21:07 +0300325
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300326 if res2 is not None:
327 return res2.group('ip')
328
329 raise OSError("Can't define interface for {0}".format(target_ip))
330
331
koder aka kdanilov39e449e2016-12-17 15:15:26 +0200332def open_for_append_or_create(fname: str) -> IO[str]:
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300333 if not os.path.exists(fname):
334 return open(fname, "w")
335
336 fd = open(fname, 'r+')
337 fd.seek(0, os.SEEK_END)
338 return fd
339
340
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300341def sec_to_str(seconds: int) -> str:
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300342 h = seconds // 3600
343 m = (seconds % 3600) // 60
344 s = seconds % 60
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300345 return "{}:{:02d}:{:02d}".format(h, m, s)
koder aka kdanilov168f6092015-04-19 02:33:38 +0300346
347
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300348def yamable(data: Any) -> Any:
koder aka kdanilov168f6092015-04-19 02:33:38 +0300349 if isinstance(data, (tuple, list)):
350 return map(yamable, data)
351
koder aka kdanilov168f6092015-04-19 02:33:38 +0300352 if isinstance(data, dict):
353 res = {}
354 for k, v in data.items():
355 res[yamable(k)] = yamable(v)
356 return res
357
358 return data
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300359
360
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300361def flatten(data: Iterable[Any]) -> List[Any]:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300362 res = []
363 for i in data:
364 if isinstance(i, (list, tuple, set)):
365 res.extend(flatten(i))
366 else:
367 res.append(i)
368 return res
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300369
370
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200371def get_creds_openrc(path: str) -> Tuple[str, str, str, str, bool]:
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300372 fc = open(path).read()
373
koder aka kdanilovb7197432015-07-15 00:40:43 +0300374 echo = 'echo "$OS_INSECURE:$OS_TENANT_NAME:$OS_USERNAME:$OS_PASSWORD@$OS_AUTH_URL"'
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300375
376 msg = "Failed to get creads from openrc file"
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300377 with LogError(msg):
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300378 data = run_locally(['/bin/bash'], input_data=fc + "\n" + echo)
379
380 msg = "Failed to get creads from openrc file: " + data
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300381 with LogError(msg):
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300382 data = data.strip()
koder aka kdanilovb7197432015-07-15 00:40:43 +0300383 insecure_str, user, tenant, passwd_auth_url = data.split(':', 3)
384 insecure = (insecure_str in ('1', 'True', 'true'))
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300385 passwd, auth_url = passwd_auth_url.rsplit("@", 1)
386 assert (auth_url.startswith("https://") or
387 auth_url.startswith("http://"))
388
koder aka kdanilovb7197432015-07-15 00:40:43 +0300389 return user, passwd, tenant, auth_url, insecure
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300390
391
koder aka kdanilov73084622016-11-16 21:51:08 +0200392OSRelease = NamedTuple("OSRelease",
393 [("distro", str),
394 ("release", str),
395 ("arch", str)])
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300396
397
koder aka kdanilov73084622016-11-16 21:51:08 +0200398def get_os(node: IRPCNode) -> OSRelease:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200399 """return os type, release and architecture for node.
400 """
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300401 arch = node.run("arch", nolog=True).strip()
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300402
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300403 try:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300404 node.run("ls -l /etc/redhat-release", nolog=True)
koder aka kdanilov73084622016-11-16 21:51:08 +0200405 return OSRelease('redhat', None, arch)
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300406 except:
407 pass
408
409 try:
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300410 node.run("ls -l /etc/debian_version", nolog=True)
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300411
412 release = None
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300413 for line in node.run("lsb_release -a", nolog=True).split("\n"):
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300414 if ':' not in line:
415 continue
416 opt, val = line.split(":", 1)
417
418 if opt == 'Codename':
419 release = val.strip()
420
koder aka kdanilov73084622016-11-16 21:51:08 +0200421 return OSRelease('ubuntu', release, arch)
koder aka kdanilov89fb6102015-06-13 02:58:08 +0300422 except:
423 pass
424
425 raise RuntimeError("Unknown os")
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300426
427
428@contextlib.contextmanager
koder aka kdanilov73084622016-11-16 21:51:08 +0200429def empty_ctx(val: Any = None) -> Iterator[Any]:
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300430 yield val
431
432
koder aka kdanilov73084622016-11-16 21:51:08 +0200433def log_nodes_statistic(nodes: Sequence[IRPCNode]) -> None:
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300434 logger.info("Found {0} nodes total".format(len(nodes)))
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200435
436 per_role = collections.defaultdict(int) # type: Dict[str, int]
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300437 for node in nodes:
koder aka kdanilov73084622016-11-16 21:51:08 +0200438 for role in node.info.roles:
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300439 per_role[role] += 1
440
441 for role, count in sorted(per_role.items()):
442 logger.debug("Found {0} nodes with role {1}".format(count, role))
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300443
444
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300445def which(program: str) -> Optional[str]:
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300446 def is_exe(fpath):
447 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
448
449 for path in os.environ["PATH"].split(os.pathsep):
450 path = path.strip('"')
451 exe_file = os.path.join(path, program)
452 if is_exe(exe_file):
453 return exe_file
454
455 return None
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200456
457
458def get_uniq_path_uuid(path: str, max_iter: int = 10) -> Tuple[str, str]:
459 for i in range(max_iter):
460 run_uuid = pet_generate(2, "_")
461 results_dir = os.path.join(path, run_uuid)
462 if not os.path.exists(results_dir):
463 break
464 else:
465 run_uuid = str(uuid.uuid4())
466 results_dir = os.path.join(path, run_uuid)
467
468 return results_dir, run_uuid
469
470
koder aka kdanilovbbbe1dc2016-12-20 01:19:56 +0200471def to_ip(host_or_ip: str) -> str:
472 # translate hostname to address
473 try:
474 ipaddress.ip_address(host_or_ip)
475 return host_or_ip
476 except ValueError:
477 ip_addr = socket.gethostbyname(host_or_ip)
478 logger.info("Will use ip_addr %r instead of hostname %r", ip_addr, host_or_ip)
479 return ip_addr