blob: bb4c52454edf06ba18205e1999236fc5f6ea179f [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
Harald Welte27205342017-06-03 09:51:45 +02008# it under the terms of the GNU General Public License as
Neels Hofmeyr3531a192017-03-28 14:30:28 +02009# 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
Harald Welte27205342017-06-03 09:51:45 +020015# GNU General Public License for more details.
Neels Hofmeyr3531a192017-03-28 14:30:28 +020016#
Harald Welte27205342017-06-03 09:51:45 +020017# You should have received a copy of the GNU General Public License
Neels Hofmeyr3531a192017-03-28 14:30:28 +020018# 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:
Pau Espin Pedrol74a76762017-08-10 12:07:49 +020047 return line.split()[-1]
Pau Espin Pedrol13143bc2017-05-08 17:07:28 +020048 except Exception as e:
49 pass
50 return None
51
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020052class listdict(dict):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020053 'a dict of lists { "a": [1, 2, 3], "b": [1, 2] }'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020054
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020055 def add(self, name, item):
56 l = self.get(name)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020057 if not l:
58 l = []
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020059 self[name] = l
Neels Hofmeyr3531a192017-03-28 14:30:28 +020060 l.append(item)
61 return l
62
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020063 def add_dict(self, d):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020064 for k,v in d.items():
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020065 self.add(k, v)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020066
67class DictProxy:
68 '''
69 allow accessing dict entries like object members
70 syntactical sugar, adapted from http://stackoverflow.com/a/31569634
71 so that e.g. templates can do ${bts.member} instead of ${bts['member']}
72 '''
73 def __init__(self, obj):
74 self.obj = obj
75
76 def __getitem__(self, key):
77 return dict2obj(self.obj[key])
78
79 def __getattr__(self, key):
Your Name44af3412017-04-13 03:11:59 +020080 'provide error information to know which template item was missing'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020081 try:
82 return dict2obj(getattr(self.obj, key))
83 except AttributeError:
84 try:
85 return self[key]
86 except KeyError:
87 raise AttributeError(key)
88
Neels Hofmeyr3531a192017-03-28 14:30:28 +020089def dict2obj(value):
Your Name44af3412017-04-13 03:11:59 +020090 if is_list(value) or is_dict(value):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020091 return DictProxy(value)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020092 return value
93
94
95class FileLock:
96 def __init__(self, path, owner):
97 self.path = path
98 self.owner = owner
99 self.f = None
100
101 def __enter__(self):
102 if self.f is not None:
103 return
Neels Hofmeyr936a81e2017-09-14 01:31:41 +0200104 self.fd = os.open(self.path, os.O_CREAT | os.O_WRONLY)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200105 fcntl.flock(self.fd, fcntl.LOCK_EX)
106 os.truncate(self.fd, 0)
107 os.write(self.fd, str(self.owner).encode('utf-8'))
108 os.fsync(self.fd)
109
110 def __exit__(self, *exc_info):
111 #fcntl.flock(self.fd, fcntl.LOCK_UN)
112 os.truncate(self.fd, 0)
113 os.fsync(self.fd)
114 os.close(self.fd)
115 self.fd = -1
116
117 def lock(self):
118 self.__enter__()
119
120 def unlock(self):
121 self.__exit__()
122
123
124class Dir():
125 LOCK_FILE = 'lock'
126
127 def __init__(self, path):
128 self.path = path
129 self.lock_path = os.path.join(self.path, Dir.LOCK_FILE)
130
131 def lock(self, origin_id):
132 '''
133 return lock context, usage:
134
135 with my_dir.lock(origin):
136 read_from(my_dir.child('foo.txt'))
137 write_to(my_dir.child('bar.txt'))
138 '''
139 self.mkdir()
140 return FileLock(self.lock_path, origin_id)
141
142 @staticmethod
143 def ensure_abs_dir_exists(*path_elements):
144 l = len(path_elements)
145 if l < 1:
146 raise RuntimeError('Cannot create empty path')
147 if l == 1:
148 path = path_elements[0]
149 else:
150 path = os.path.join(*path_elements)
151 if not os.path.isdir(path):
152 os.makedirs(path)
153
154 def child(self, *rel_path):
155 if not rel_path:
156 return self.path
157 return os.path.join(self.path, *rel_path)
158
159 def mk_parentdir(self, *rel_path):
160 child = self.child(*rel_path)
161 child_parent = os.path.dirname(child)
162 Dir.ensure_abs_dir_exists(child_parent)
163 return child
164
165 def mkdir(self, *rel_path):
166 child = self.child(*rel_path)
167 Dir.ensure_abs_dir_exists(child)
168 return child
169
170 def children(self):
171 return os.listdir(self.path)
172
173 def exists(self, *rel_path):
174 return os.path.exists(self.child(*rel_path))
175
176 def isdir(self, *rel_path):
177 return os.path.isdir(self.child(*rel_path))
178
179 def isfile(self, *rel_path):
180 return os.path.isfile(self.child(*rel_path))
181
182 def new_child(self, *rel_path):
183 attempt = 1
184 prefix, suffix = os.path.splitext(self.child(*rel_path))
185 rel_path_fmt = '%s%%s%s' % (prefix, suffix)
186 while True:
187 path = rel_path_fmt % (('_%d'%attempt) if attempt > 1 else '')
188 if not os.path.exists(path):
189 break
190 attempt += 1
191 continue
192 Dir.ensure_abs_dir_exists(os.path.dirname(path))
193 return path
194
195 def rel_path(self, path):
196 return os.path.relpath(path, self.path)
197
198 def touch(self, *rel_path):
199 touch_file(self.child(*rel_path))
200
201 def new_file(self, *rel_path):
202 path = self.new_child(*rel_path)
203 touch_file(path)
204 return path
205
206 def new_dir(self, *rel_path):
207 path = self.new_child(*rel_path)
208 Dir.ensure_abs_dir_exists(path)
209 return path
210
211 def __str__(self):
212 return self.path
213 def __repr__(self):
214 return self.path
215
216def touch_file(path):
217 with open(path, 'a') as f:
218 f.close()
219
220def is_dict(l):
221 return isinstance(l, dict)
222
223def is_list(l):
224 return isinstance(l, (list, tuple))
225
226
227def dict_add(a, *b, **c):
228 for bb in b:
229 a.update(bb)
230 a.update(c)
231 return a
232
233def _hash_recurse(acc, obj, ignore_keys):
234 if is_dict(obj):
235 for key, val in sorted(obj.items()):
236 if key in ignore_keys:
237 continue
238 _hash_recurse(acc, val, ignore_keys)
239 return
240
241 if is_list(obj):
242 for item in obj:
243 _hash_recurse(acc, item, ignore_keys)
244 return
245
246 acc.update(str(obj).encode('utf-8'))
247
248def hash_obj(obj, *ignore_keys):
249 acc = hashlib.sha1()
250 _hash_recurse(acc, obj, ignore_keys)
251 return acc.hexdigest()
252
253
254def md5(of_content):
255 if isinstance(of_content, str):
256 of_content = of_content.encode('utf-8')
257 return hashlib.md5(of_content).hexdigest()
258
259def md5_of_file(path):
260 with open(path, 'rb') as f:
261 return md5(f.read())
262
263_tempdir = None
264
265def get_tempdir(remove_on_exit=True):
266 global _tempdir
267 if _tempdir is not None:
268 return _tempdir
269 _tempdir = tempfile.mkdtemp()
270 if remove_on_exit:
271 atexit.register(lambda: shutil.rmtree(_tempdir))
272 return _tempdir
273
274
275if hasattr(importlib.util, 'module_from_spec'):
276 def run_python_file(module_name, path):
277 spec = importlib.util.spec_from_file_location(module_name, path)
278 spec.loader.exec_module( importlib.util.module_from_spec(spec) )
279else:
280 from importlib.machinery import SourceFileLoader
281 def run_python_file(module_name, path):
282 SourceFileLoader(module_name, path).load_module()
283
284def msisdn_inc(msisdn_str):
285 'add 1 and preserve leading zeros'
286 return ('%%0%dd' % len(msisdn_str)) % (int(msisdn_str) + 1)
287
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200288class InputThread(threading.Thread):
289 def __init__(self, prompt):
290 super().__init__()
291 self.prompt = prompt
292 self.result = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200293
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200294 def run(self):
295 self.result = input(self.prompt)
296
297def input_polling(prompt, poll_func):
298 input_thread = InputThread(prompt)
299 input_thread.start()
300
301 while input_thread.is_alive():
302 poll_func()
303 time.sleep(1)
304
305 input_thread.join()
306 return input_thread.result
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200307
Pau Espin Pedrol404e1502017-08-22 11:17:43 +0200308def str2bool(val):
309 if val is None or not val:
310 return False
311 if val.upper() in ['FALSE', 'NO', 'OFF']:
312 return False
313 if val.upper() in ['TRUE','YES', 'ON']:
314 return True
315 raise ValueError('Invalid BOOL field: %r' % val)
316
Pau Espin Pedrol43737da2017-08-28 14:04:07 +0200317def list_validate_same_elem_type(li):
318 '''
319 Checks that all elements in the list are of the same type and returns that type.
320 If the list is empty, returns None
321 If one of the elements is not of the same type, it throws a ValueError exception.
322 '''
323 if len(li) == 0:
324 return None
325 t = type(li[0])
326 for elem in li:
327 if type(elem) != t:
328 raise ValueError('List contains elements of different types: %r vs %r' % (t, type(elem)))
329 return t
330
331def empty_instance_type(t):
332 if t == dict:
333 return {}
334 elif t == list:
335 return []
336 elif t == tuple:
337 return ()
338 raise ValueError('type %r not supported!' % t)
339
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200340# vim: expandtab tabstop=4 shiftwidth=4