Commit | Line | Data |
---|---|---|
b86761ff JK |
1 | # SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | import json as _json | |
31611cea JK |
4 | import random |
5 | import re | |
b86761ff | 6 | import subprocess |
31611cea JK |
7 | import time |
8 | ||
b86761ff JK |
9 | |
10 | class cmd: | |
1a20a9a0 | 11 | def __init__(self, comm, shell=True, fail=True, ns=None, background=False, host=None): |
b86761ff | 12 | if ns: |
b86761ff JK |
13 | comm = f'ip netns exec {ns} ' + comm |
14 | ||
15 | self.stdout = None | |
16 | self.stderr = None | |
17 | self.ret = None | |
18 | ||
19 | self.comm = comm | |
1a20a9a0 JK |
20 | if host: |
21 | self.proc = host.cmd(comm) | |
22 | else: | |
23 | self.proc = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, | |
24 | stderr=subprocess.PIPE) | |
b86761ff JK |
25 | if not background: |
26 | self.process(terminate=False, fail=fail) | |
27 | ||
28 | def process(self, terminate=True, fail=None): | |
29 | if terminate: | |
30 | self.proc.terminate() | |
1a20a9a0 | 31 | stdout, stderr = self.proc.communicate(timeout=5) |
b86761ff JK |
32 | self.stdout = stdout.decode("utf-8") |
33 | self.stderr = stderr.decode("utf-8") | |
34 | self.proc.stdout.close() | |
35 | self.proc.stderr.close() | |
36 | self.ret = self.proc.returncode | |
37 | ||
38 | if self.proc.returncode != 0 and fail: | |
39 | if len(stderr) > 0 and stderr[-1] == "\n": | |
40 | stderr = stderr[:-1] | |
232d79aa JK |
41 | raise Exception("Command failed: %s\nSTDOUT: %s\nSTDERR: %s" % |
42 | (self.proc.args, stdout, stderr)) | |
b86761ff JK |
43 | |
44 | ||
31611cea JK |
45 | class bkg(cmd): |
46 | def __init__(self, comm, shell=True, fail=True, ns=None, host=None, | |
47 | exit_wait=False): | |
48 | super().__init__(comm, background=True, | |
49 | shell=shell, fail=fail, ns=ns, host=host) | |
50 | self.terminate = not exit_wait | |
51 | ||
52 | def __enter__(self): | |
53 | return self | |
54 | ||
55 | def __exit__(self, ex_type, ex_value, ex_tb): | |
56 | return self.process(terminate=self.terminate) | |
57 | ||
58 | ||
32a4ca13 JK |
59 | def tool(name, args, json=None, ns=None, host=None): |
60 | cmd_str = name + ' ' | |
b86761ff | 61 | if json: |
32a4ca13 | 62 | cmd_str += '--json ' |
b86761ff | 63 | cmd_str += args |
1a20a9a0 | 64 | cmd_obj = cmd(cmd_str, ns=ns, host=host) |
b86761ff JK |
65 | if json: |
66 | return _json.loads(cmd_obj.stdout) | |
67 | return cmd_obj | |
31611cea JK |
68 | |
69 | ||
32a4ca13 JK |
70 | def ip(args, json=None, ns=None, host=None): |
71 | if ns: | |
72 | args = f'-netns {ns} ' + args | |
73 | return tool('ip', args, json=json, host=host) | |
74 | ||
75 | ||
31611cea JK |
76 | def rand_port(): |
77 | """ | |
78 | Get unprivileged port, for now just random, one day we may decide to check if used. | |
79 | """ | |
ee2512d6 | 80 | return random.randint(10000, 65535) |
31611cea JK |
81 | |
82 | ||
83 | def wait_port_listen(port, proto="tcp", ns=None, host=None, sleep=0.005, deadline=5): | |
84 | end = time.monotonic() + deadline | |
85 | ||
86 | pattern = f":{port:04X} .* " | |
87 | if proto == "tcp": # for tcp protocol additionally check the socket state | |
88 | pattern += "0A" | |
89 | pattern = re.compile(pattern) | |
90 | ||
91 | while True: | |
92 | data = cmd(f'cat /proc/net/{proto}*', ns=ns, host=host, shell=True).stdout | |
93 | for row in data.split("\n"): | |
94 | if pattern.search(row): | |
95 | return | |
96 | if time.monotonic() > end: | |
97 | raise Exception("Waiting for port listen timed out") | |
98 | time.sleep(sleep) |