blob: 0849ccbbd079974e3a02de9692c5d6e6040adc8f [file] [log] [blame]
Neels Hofmeyr3531a192017-03-28 14:30:28 +02001# osmo_gsm_tester: language snippets
2#
3# Copyright (C) 2016-2017 by sysmocom - s.f.m.c. GmbH
4#
5# Author: Neels Hofmeyr <neels@hofmeyr.de>
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20import os
21import sys
22import time
23import fcntl
24import hashlib
25import tempfile
26import shutil
27import atexit
28import threading
29import importlib.util
30import fcntl
31import tty
Neels Hofmeyracf0c932017-05-06 16:05:33 +020032import readline
Pau Espin Pedrol13143bc2017-05-08 17:07:28 +020033import subprocess
Neels Hofmeyr3531a192017-03-28 14:30:28 +020034
Pau Espin Pedrole39c6f12017-05-08 16:21:38 +020035def prepend_library_path(path):
36 lp = os.getenv('LD_LIBRARY_PATH')
37 if not lp:
38 return path
39 return path + ':' + lp
40
Pau Espin Pedrol13143bc2017-05-08 17:07:28 +020041def ip_to_iface(ip):
42 try:
43 for iface in os.listdir('/sys/class/net'):
44 proc = subprocess.Popen(['ip', 'addr', 'show', 'dev', iface], stdout=subprocess.PIPE, universal_newlines=True)
45 for line in proc.stdout.readlines():
46 if 'inet' in line and ' ' + ip + '/' in line:
47 return iface
48 except Exception as e:
49 pass
50 return None
51
Neels Hofmeyr3531a192017-03-28 14:30:28 +020052class listdict:
53 'a dict of lists { "a": [1, 2, 3], "b": [1, 2] }'
54 def __getattr__(ld, name):
55 if name == 'add':
56 return ld.__getattribute__(name)
57 return ld.__dict__.__getattribute__(name)
58
59 def add(ld, name, item):
60 l = ld.__dict__.get(name)
61 if not l:
62 l = []
63 ld.__dict__[name] = l
64 l.append(item)
65 return l
66
67 def add_dict(ld, d):
68 for k,v in d.items():
69 ld.add(k, v)
70
71 def __setitem__(ld, name, val):
72 return ld.__dict__.__setitem__(name, val)
73
74 def __getitem__(ld, name):
75 return ld.__dict__.__getitem__(name)
76
77 def __str__(ld):
78 return ld.__dict__.__str__()
79
80
81class DictProxy:
82 '''
83 allow accessing dict entries like object members
84 syntactical sugar, adapted from http://stackoverflow.com/a/31569634
85 so that e.g. templates can do ${bts.member} instead of ${bts['member']}
86 '''
87 def __init__(self, obj):
88 self.obj = obj
89
90 def __getitem__(self, key):
91 return dict2obj(self.obj[key])
92
93 def __getattr__(self, key):
Your Name44af3412017-04-13 03:11:59 +020094 'provide error information to know which template item was missing'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020095 try:
96 return dict2obj(getattr(self.obj, key))
97 except AttributeError:
98 try:
99 return self[key]
100 except KeyError:
101 raise AttributeError(key)
102
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200103def dict2obj(value):
Your Name44af3412017-04-13 03:11:59 +0200104 if is_list(value) or is_dict(value):
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200105 return DictProxy(value)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200106 return value
107
108
109class FileLock:
110 def __init__(self, path, owner):
111 self.path = path
112 self.owner = owner
113 self.f = None
114
115 def __enter__(self):
116 if self.f is not None:
117 return
118 self.fd = os.open(self.path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
119 fcntl.flock(self.fd, fcntl.LOCK_EX)
120 os.truncate(self.fd, 0)
121 os.write(self.fd, str(self.owner).encode('utf-8'))
122 os.fsync(self.fd)
123
124 def __exit__(self, *exc_info):
125 #fcntl.flock(self.fd, fcntl.LOCK_UN)
126 os.truncate(self.fd, 0)
127 os.fsync(self.fd)
128 os.close(self.fd)
129 self.fd = -1
130
131 def lock(self):
132 self.__enter__()
133
134 def unlock(self):
135 self.__exit__()
136
137
138class Dir():
139 LOCK_FILE = 'lock'
140
141 def __init__(self, path):
142 self.path = path
143 self.lock_path = os.path.join(self.path, Dir.LOCK_FILE)
144
145 def lock(self, origin_id):
146 '''
147 return lock context, usage:
148
149 with my_dir.lock(origin):
150 read_from(my_dir.child('foo.txt'))
151 write_to(my_dir.child('bar.txt'))
152 '''
153 self.mkdir()
154 return FileLock(self.lock_path, origin_id)
155
156 @staticmethod
157 def ensure_abs_dir_exists(*path_elements):
158 l = len(path_elements)
159 if l < 1:
160 raise RuntimeError('Cannot create empty path')
161 if l == 1:
162 path = path_elements[0]
163 else:
164 path = os.path.join(*path_elements)
165 if not os.path.isdir(path):
166 os.makedirs(path)
167
168 def child(self, *rel_path):
169 if not rel_path:
170 return self.path
171 return os.path.join(self.path, *rel_path)
172
173 def mk_parentdir(self, *rel_path):
174 child = self.child(*rel_path)
175 child_parent = os.path.dirname(child)
176 Dir.ensure_abs_dir_exists(child_parent)
177 return child
178
179 def mkdir(self, *rel_path):
180 child = self.child(*rel_path)
181 Dir.ensure_abs_dir_exists(child)
182 return child
183
184 def children(self):
185 return os.listdir(self.path)
186
187 def exists(self, *rel_path):
188 return os.path.exists(self.child(*rel_path))
189
190 def isdir(self, *rel_path):
191 return os.path.isdir(self.child(*rel_path))
192
193 def isfile(self, *rel_path):
194 return os.path.isfile(self.child(*rel_path))
195
196 def new_child(self, *rel_path):
197 attempt = 1
198 prefix, suffix = os.path.splitext(self.child(*rel_path))
199 rel_path_fmt = '%s%%s%s' % (prefix, suffix)
200 while True:
201 path = rel_path_fmt % (('_%d'%attempt) if attempt > 1 else '')
202 if not os.path.exists(path):
203 break
204 attempt += 1
205 continue
206 Dir.ensure_abs_dir_exists(os.path.dirname(path))
207 return path
208
209 def rel_path(self, path):
210 return os.path.relpath(path, self.path)
211
212 def touch(self, *rel_path):
213 touch_file(self.child(*rel_path))
214
215 def new_file(self, *rel_path):
216 path = self.new_child(*rel_path)
217 touch_file(path)
218 return path
219
220 def new_dir(self, *rel_path):
221 path = self.new_child(*rel_path)
222 Dir.ensure_abs_dir_exists(path)
223 return path
224
225 def __str__(self):
226 return self.path
227 def __repr__(self):
228 return self.path
229
230def touch_file(path):
231 with open(path, 'a') as f:
232 f.close()
233
234def is_dict(l):
235 return isinstance(l, dict)
236
237def is_list(l):
238 return isinstance(l, (list, tuple))
239
240
241def dict_add(a, *b, **c):
242 for bb in b:
243 a.update(bb)
244 a.update(c)
245 return a
246
247def _hash_recurse(acc, obj, ignore_keys):
248 if is_dict(obj):
249 for key, val in sorted(obj.items()):
250 if key in ignore_keys:
251 continue
252 _hash_recurse(acc, val, ignore_keys)
253 return
254
255 if is_list(obj):
256 for item in obj:
257 _hash_recurse(acc, item, ignore_keys)
258 return
259
260 acc.update(str(obj).encode('utf-8'))
261
262def hash_obj(obj, *ignore_keys):
263 acc = hashlib.sha1()
264 _hash_recurse(acc, obj, ignore_keys)
265 return acc.hexdigest()
266
267
268def md5(of_content):
269 if isinstance(of_content, str):
270 of_content = of_content.encode('utf-8')
271 return hashlib.md5(of_content).hexdigest()
272
273def md5_of_file(path):
274 with open(path, 'rb') as f:
275 return md5(f.read())
276
277_tempdir = None
278
279def get_tempdir(remove_on_exit=True):
280 global _tempdir
281 if _tempdir is not None:
282 return _tempdir
283 _tempdir = tempfile.mkdtemp()
284 if remove_on_exit:
285 atexit.register(lambda: shutil.rmtree(_tempdir))
286 return _tempdir
287
288
289if hasattr(importlib.util, 'module_from_spec'):
290 def run_python_file(module_name, path):
291 spec = importlib.util.spec_from_file_location(module_name, path)
292 spec.loader.exec_module( importlib.util.module_from_spec(spec) )
293else:
294 from importlib.machinery import SourceFileLoader
295 def run_python_file(module_name, path):
296 SourceFileLoader(module_name, path).load_module()
297
298def msisdn_inc(msisdn_str):
299 'add 1 and preserve leading zeros'
300 return ('%%0%dd' % len(msisdn_str)) % (int(msisdn_str) + 1)
301
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200302class InputThread(threading.Thread):
303 def __init__(self, prompt):
304 super().__init__()
305 self.prompt = prompt
306 self.result = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200307
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200308 def run(self):
309 self.result = input(self.prompt)
310
311def input_polling(prompt, poll_func):
312 input_thread = InputThread(prompt)
313 input_thread.start()
314
315 while input_thread.is_alive():
316 poll_func()
317 time.sleep(1)
318
319 input_thread.join()
320 return input_thread.result
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200321
322# vim: expandtab tabstop=4 shiftwidth=4