blob: ed1a25897ddb834b7f7973370e03a51e7f75d9b4 [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 Pedroled6d4452017-10-25 17:06:53 +020041def change_elf_rpath(binary, paths, run_dir):
42 '''
43 Change RPATH field in ELF executable binary.
44 This feature can be used to tell the loaded to load the trial libraries, as
45 LD_LIBRARY_PATH is disabled for paths with modified capabilities.
46 '''
47 from .process import Process
48 proc = Process('patchelf', run_dir, ['patchelf', '--set-rpath', paths, binary])
49 proc.launch()
50 proc.wait()
51 if proc.result != 0:
52 raise RuntimeError('patchelf finished with err code %d' % proc.result)
53
Pau Espin Pedrol13143bc2017-05-08 17:07:28 +020054def ip_to_iface(ip):
55 try:
56 for iface in os.listdir('/sys/class/net'):
57 proc = subprocess.Popen(['ip', 'addr', 'show', 'dev', iface], stdout=subprocess.PIPE, universal_newlines=True)
58 for line in proc.stdout.readlines():
59 if 'inet' in line and ' ' + ip + '/' in line:
Pau Espin Pedrol74a76762017-08-10 12:07:49 +020060 return line.split()[-1]
Pau Espin Pedrol13143bc2017-05-08 17:07:28 +020061 except Exception as e:
62 pass
63 return None
64
Pau Espin Pedrol33c154b2017-10-25 16:16:25 +020065def setcap_net_raw(binary, run_dir):
66 '''
67 This functionality requires specific setup on the host running
68 osmo-gsm-tester. See osmo-gsm-tester manual for more information.
69 '''
70 from .process import Process
Pau Espin Pedrolf0b8e372017-12-13 16:08:35 +010071 SETCAP_NET_RAW_BIN = 'osmo-gsm-tester_setcap_net_raw.sh'
72 proc = Process(SETCAP_NET_RAW_BIN, run_dir, ['sudo', SETCAP_NET_RAW_BIN, binary])
Pau Espin Pedrol33c154b2017-10-25 16:16:25 +020073 proc.launch()
74 proc.wait()
75 if proc.result != 0:
Pau Espin Pedrolf0b8e372017-12-13 16:08:35 +010076 raise RuntimeError('%s finished with err code %d' % (SETCAP_NET_RAW_BIN, proc.result))
77
78def setcap_net_admin(binary, run_dir):
79 '''
80 This functionality requires specific setup on the host running
81 osmo-gsm-tester. See osmo-gsm-tester manual for more information.
82 '''
83 from .process import Process
84 SETCAP_NET_ADMIN_BIN = 'osmo-gsm-tester_setcap_net_admin.sh'
85 proc = Process(SETCAP_NET_ADMIN_BIN, run_dir, ['sudo', SETCAP_NET_ADMIN_BIN, binary])
86 proc.launch()
87 proc.wait()
88 if proc.result != 0:
89 raise RuntimeError('%s finished with err code %d' % (SETCAP_NET_ADMIN_BIN, proc.result))
Pau Espin Pedrol33c154b2017-10-25 16:16:25 +020090
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020091class listdict(dict):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020092 'a dict of lists { "a": [1, 2, 3], "b": [1, 2] }'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020093
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020094 def add(self, name, item):
95 l = self.get(name)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020096 if not l:
97 l = []
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020098 self[name] = l
Neels Hofmeyr3531a192017-03-28 14:30:28 +020099 l.append(item)
100 return l
101
Neels Hofmeyr803d87c2017-05-10 13:31:41 +0200102 def add_dict(self, d):
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200103 for k,v in d.items():
Neels Hofmeyr803d87c2017-05-10 13:31:41 +0200104 self.add(k, v)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200105
106class DictProxy:
107 '''
108 allow accessing dict entries like object members
109 syntactical sugar, adapted from http://stackoverflow.com/a/31569634
110 so that e.g. templates can do ${bts.member} instead of ${bts['member']}
111 '''
112 def __init__(self, obj):
113 self.obj = obj
114
115 def __getitem__(self, key):
116 return dict2obj(self.obj[key])
117
118 def __getattr__(self, key):
Your Name44af3412017-04-13 03:11:59 +0200119 'provide error information to know which template item was missing'
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200120 try:
121 return dict2obj(getattr(self.obj, key))
122 except AttributeError:
123 try:
124 return self[key]
125 except KeyError:
126 raise AttributeError(key)
127
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200128def dict2obj(value):
Your Name44af3412017-04-13 03:11:59 +0200129 if is_list(value) or is_dict(value):
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200130 return DictProxy(value)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200131 return value
132
133
134class FileLock:
135 def __init__(self, path, owner):
136 self.path = path
137 self.owner = owner
138 self.f = None
139
140 def __enter__(self):
141 if self.f is not None:
142 return
Neels Hofmeyr936a81e2017-09-14 01:31:41 +0200143 self.fd = os.open(self.path, os.O_CREAT | os.O_WRONLY)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200144 fcntl.flock(self.fd, fcntl.LOCK_EX)
145 os.truncate(self.fd, 0)
146 os.write(self.fd, str(self.owner).encode('utf-8'))
147 os.fsync(self.fd)
148
149 def __exit__(self, *exc_info):
150 #fcntl.flock(self.fd, fcntl.LOCK_UN)
151 os.truncate(self.fd, 0)
152 os.fsync(self.fd)
153 os.close(self.fd)
154 self.fd = -1
155
156 def lock(self):
157 self.__enter__()
158
159 def unlock(self):
160 self.__exit__()
161
162
163class Dir():
164 LOCK_FILE = 'lock'
165
166 def __init__(self, path):
167 self.path = path
168 self.lock_path = os.path.join(self.path, Dir.LOCK_FILE)
169
170 def lock(self, origin_id):
171 '''
172 return lock context, usage:
173
174 with my_dir.lock(origin):
175 read_from(my_dir.child('foo.txt'))
176 write_to(my_dir.child('bar.txt'))
177 '''
178 self.mkdir()
179 return FileLock(self.lock_path, origin_id)
180
181 @staticmethod
182 def ensure_abs_dir_exists(*path_elements):
183 l = len(path_elements)
184 if l < 1:
185 raise RuntimeError('Cannot create empty path')
186 if l == 1:
187 path = path_elements[0]
188 else:
189 path = os.path.join(*path_elements)
190 if not os.path.isdir(path):
191 os.makedirs(path)
192
193 def child(self, *rel_path):
194 if not rel_path:
195 return self.path
196 return os.path.join(self.path, *rel_path)
197
198 def mk_parentdir(self, *rel_path):
199 child = self.child(*rel_path)
200 child_parent = os.path.dirname(child)
201 Dir.ensure_abs_dir_exists(child_parent)
202 return child
203
204 def mkdir(self, *rel_path):
205 child = self.child(*rel_path)
206 Dir.ensure_abs_dir_exists(child)
207 return child
208
209 def children(self):
210 return os.listdir(self.path)
211
212 def exists(self, *rel_path):
213 return os.path.exists(self.child(*rel_path))
214
215 def isdir(self, *rel_path):
216 return os.path.isdir(self.child(*rel_path))
217
218 def isfile(self, *rel_path):
219 return os.path.isfile(self.child(*rel_path))
220
221 def new_child(self, *rel_path):
222 attempt = 1
223 prefix, suffix = os.path.splitext(self.child(*rel_path))
224 rel_path_fmt = '%s%%s%s' % (prefix, suffix)
225 while True:
226 path = rel_path_fmt % (('_%d'%attempt) if attempt > 1 else '')
227 if not os.path.exists(path):
228 break
229 attempt += 1
230 continue
231 Dir.ensure_abs_dir_exists(os.path.dirname(path))
232 return path
233
234 def rel_path(self, path):
235 return os.path.relpath(path, self.path)
236
237 def touch(self, *rel_path):
238 touch_file(self.child(*rel_path))
239
240 def new_file(self, *rel_path):
241 path = self.new_child(*rel_path)
242 touch_file(path)
243 return path
244
245 def new_dir(self, *rel_path):
246 path = self.new_child(*rel_path)
247 Dir.ensure_abs_dir_exists(path)
248 return path
249
250 def __str__(self):
251 return self.path
252 def __repr__(self):
253 return self.path
254
255def touch_file(path):
256 with open(path, 'a') as f:
257 f.close()
258
259def is_dict(l):
260 return isinstance(l, dict)
261
262def is_list(l):
263 return isinstance(l, (list, tuple))
264
265
266def dict_add(a, *b, **c):
267 for bb in b:
268 a.update(bb)
269 a.update(c)
270 return a
271
272def _hash_recurse(acc, obj, ignore_keys):
273 if is_dict(obj):
274 for key, val in sorted(obj.items()):
275 if key in ignore_keys:
276 continue
277 _hash_recurse(acc, val, ignore_keys)
278 return
279
280 if is_list(obj):
281 for item in obj:
282 _hash_recurse(acc, item, ignore_keys)
283 return
284
285 acc.update(str(obj).encode('utf-8'))
286
287def hash_obj(obj, *ignore_keys):
288 acc = hashlib.sha1()
289 _hash_recurse(acc, obj, ignore_keys)
290 return acc.hexdigest()
291
292
293def md5(of_content):
294 if isinstance(of_content, str):
295 of_content = of_content.encode('utf-8')
296 return hashlib.md5(of_content).hexdigest()
297
298def md5_of_file(path):
299 with open(path, 'rb') as f:
300 return md5(f.read())
301
302_tempdir = None
303
304def get_tempdir(remove_on_exit=True):
305 global _tempdir
306 if _tempdir is not None:
307 return _tempdir
308 _tempdir = tempfile.mkdtemp()
309 if remove_on_exit:
310 atexit.register(lambda: shutil.rmtree(_tempdir))
311 return _tempdir
312
313
314if hasattr(importlib.util, 'module_from_spec'):
315 def run_python_file(module_name, path):
316 spec = importlib.util.spec_from_file_location(module_name, path)
317 spec.loader.exec_module( importlib.util.module_from_spec(spec) )
318else:
319 from importlib.machinery import SourceFileLoader
320 def run_python_file(module_name, path):
321 SourceFileLoader(module_name, path).load_module()
322
323def msisdn_inc(msisdn_str):
324 'add 1 and preserve leading zeros'
325 return ('%%0%dd' % len(msisdn_str)) % (int(msisdn_str) + 1)
326
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200327class InputThread(threading.Thread):
328 def __init__(self, prompt):
329 super().__init__()
330 self.prompt = prompt
331 self.result = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200332
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200333 def run(self):
334 self.result = input(self.prompt)
335
336def input_polling(prompt, poll_func):
337 input_thread = InputThread(prompt)
338 input_thread.start()
339
340 while input_thread.is_alive():
341 poll_func()
342 time.sleep(1)
343
344 input_thread.join()
345 return input_thread.result
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200346
Pau Espin Pedrol404e1502017-08-22 11:17:43 +0200347def str2bool(val):
348 if val is None or not val:
349 return False
350 if val.upper() in ['FALSE', 'NO', 'OFF']:
351 return False
352 if val.upper() in ['TRUE','YES', 'ON']:
353 return True
354 raise ValueError('Invalid BOOL field: %r' % val)
355
Pau Espin Pedrol43737da2017-08-28 14:04:07 +0200356def list_validate_same_elem_type(li):
357 '''
358 Checks that all elements in the list are of the same type and returns that type.
359 If the list is empty, returns None
360 If one of the elements is not of the same type, it throws a ValueError exception.
361 '''
362 if len(li) == 0:
363 return None
364 t = type(li[0])
365 for elem in li:
366 if type(elem) != t:
367 raise ValueError('List contains elements of different types: %r vs %r' % (t, type(elem)))
368 return t
369
370def empty_instance_type(t):
371 if t == dict:
372 return {}
373 elif t == list:
374 return []
375 elif t == tuple:
376 return ()
377 raise ValueError('type %r not supported!' % t)
378
Pau Espin Pedrolabd556a2017-09-04 16:26:08 +0200379def encryption2osmovty(val):
380 assert val[:3] == 'a5_'
381 return 'a5 ' + val[3:]
382
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200383# vim: expandtab tabstop=4 shiftwidth=4