blob: 8fb6e1984fed1b7781fa630e1a8fc77d99e92f30 [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
71 SETCAP_NET_BIN = 'osmo-gsm-tester_setcap_net_raw.sh'
72 proc = Process(SETCAP_NET_BIN, run_dir, ['sudo', 'osmo-gsm-tester_setcap_net_raw.sh', binary])
73 proc.launch()
74 proc.wait()
75 if proc.result != 0:
76 raise RuntimeError('%s finished with err code %d' % (SETCAP_NET_BIN, proc.result))
77
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020078class listdict(dict):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020079 'a dict of lists { "a": [1, 2, 3], "b": [1, 2] }'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020080
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020081 def add(self, name, item):
82 l = self.get(name)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020083 if not l:
84 l = []
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020085 self[name] = l
Neels Hofmeyr3531a192017-03-28 14:30:28 +020086 l.append(item)
87 return l
88
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020089 def add_dict(self, d):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020090 for k,v in d.items():
Neels Hofmeyr803d87c2017-05-10 13:31:41 +020091 self.add(k, v)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020092
93class DictProxy:
94 '''
95 allow accessing dict entries like object members
96 syntactical sugar, adapted from http://stackoverflow.com/a/31569634
97 so that e.g. templates can do ${bts.member} instead of ${bts['member']}
98 '''
99 def __init__(self, obj):
100 self.obj = obj
101
102 def __getitem__(self, key):
103 return dict2obj(self.obj[key])
104
105 def __getattr__(self, key):
Your Name44af3412017-04-13 03:11:59 +0200106 'provide error information to know which template item was missing'
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200107 try:
108 return dict2obj(getattr(self.obj, key))
109 except AttributeError:
110 try:
111 return self[key]
112 except KeyError:
113 raise AttributeError(key)
114
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200115def dict2obj(value):
Your Name44af3412017-04-13 03:11:59 +0200116 if is_list(value) or is_dict(value):
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200117 return DictProxy(value)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200118 return value
119
120
121class FileLock:
122 def __init__(self, path, owner):
123 self.path = path
124 self.owner = owner
125 self.f = None
126
127 def __enter__(self):
128 if self.f is not None:
129 return
Neels Hofmeyr936a81e2017-09-14 01:31:41 +0200130 self.fd = os.open(self.path, os.O_CREAT | os.O_WRONLY)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200131 fcntl.flock(self.fd, fcntl.LOCK_EX)
132 os.truncate(self.fd, 0)
133 os.write(self.fd, str(self.owner).encode('utf-8'))
134 os.fsync(self.fd)
135
136 def __exit__(self, *exc_info):
137 #fcntl.flock(self.fd, fcntl.LOCK_UN)
138 os.truncate(self.fd, 0)
139 os.fsync(self.fd)
140 os.close(self.fd)
141 self.fd = -1
142
143 def lock(self):
144 self.__enter__()
145
146 def unlock(self):
147 self.__exit__()
148
149
150class Dir():
151 LOCK_FILE = 'lock'
152
153 def __init__(self, path):
154 self.path = path
155 self.lock_path = os.path.join(self.path, Dir.LOCK_FILE)
156
157 def lock(self, origin_id):
158 '''
159 return lock context, usage:
160
161 with my_dir.lock(origin):
162 read_from(my_dir.child('foo.txt'))
163 write_to(my_dir.child('bar.txt'))
164 '''
165 self.mkdir()
166 return FileLock(self.lock_path, origin_id)
167
168 @staticmethod
169 def ensure_abs_dir_exists(*path_elements):
170 l = len(path_elements)
171 if l < 1:
172 raise RuntimeError('Cannot create empty path')
173 if l == 1:
174 path = path_elements[0]
175 else:
176 path = os.path.join(*path_elements)
177 if not os.path.isdir(path):
178 os.makedirs(path)
179
180 def child(self, *rel_path):
181 if not rel_path:
182 return self.path
183 return os.path.join(self.path, *rel_path)
184
185 def mk_parentdir(self, *rel_path):
186 child = self.child(*rel_path)
187 child_parent = os.path.dirname(child)
188 Dir.ensure_abs_dir_exists(child_parent)
189 return child
190
191 def mkdir(self, *rel_path):
192 child = self.child(*rel_path)
193 Dir.ensure_abs_dir_exists(child)
194 return child
195
196 def children(self):
197 return os.listdir(self.path)
198
199 def exists(self, *rel_path):
200 return os.path.exists(self.child(*rel_path))
201
202 def isdir(self, *rel_path):
203 return os.path.isdir(self.child(*rel_path))
204
205 def isfile(self, *rel_path):
206 return os.path.isfile(self.child(*rel_path))
207
208 def new_child(self, *rel_path):
209 attempt = 1
210 prefix, suffix = os.path.splitext(self.child(*rel_path))
211 rel_path_fmt = '%s%%s%s' % (prefix, suffix)
212 while True:
213 path = rel_path_fmt % (('_%d'%attempt) if attempt > 1 else '')
214 if not os.path.exists(path):
215 break
216 attempt += 1
217 continue
218 Dir.ensure_abs_dir_exists(os.path.dirname(path))
219 return path
220
221 def rel_path(self, path):
222 return os.path.relpath(path, self.path)
223
224 def touch(self, *rel_path):
225 touch_file(self.child(*rel_path))
226
227 def new_file(self, *rel_path):
228 path = self.new_child(*rel_path)
229 touch_file(path)
230 return path
231
232 def new_dir(self, *rel_path):
233 path = self.new_child(*rel_path)
234 Dir.ensure_abs_dir_exists(path)
235 return path
236
237 def __str__(self):
238 return self.path
239 def __repr__(self):
240 return self.path
241
242def touch_file(path):
243 with open(path, 'a') as f:
244 f.close()
245
246def is_dict(l):
247 return isinstance(l, dict)
248
249def is_list(l):
250 return isinstance(l, (list, tuple))
251
252
253def dict_add(a, *b, **c):
254 for bb in b:
255 a.update(bb)
256 a.update(c)
257 return a
258
259def _hash_recurse(acc, obj, ignore_keys):
260 if is_dict(obj):
261 for key, val in sorted(obj.items()):
262 if key in ignore_keys:
263 continue
264 _hash_recurse(acc, val, ignore_keys)
265 return
266
267 if is_list(obj):
268 for item in obj:
269 _hash_recurse(acc, item, ignore_keys)
270 return
271
272 acc.update(str(obj).encode('utf-8'))
273
274def hash_obj(obj, *ignore_keys):
275 acc = hashlib.sha1()
276 _hash_recurse(acc, obj, ignore_keys)
277 return acc.hexdigest()
278
279
280def md5(of_content):
281 if isinstance(of_content, str):
282 of_content = of_content.encode('utf-8')
283 return hashlib.md5(of_content).hexdigest()
284
285def md5_of_file(path):
286 with open(path, 'rb') as f:
287 return md5(f.read())
288
289_tempdir = None
290
291def get_tempdir(remove_on_exit=True):
292 global _tempdir
293 if _tempdir is not None:
294 return _tempdir
295 _tempdir = tempfile.mkdtemp()
296 if remove_on_exit:
297 atexit.register(lambda: shutil.rmtree(_tempdir))
298 return _tempdir
299
300
301if hasattr(importlib.util, 'module_from_spec'):
302 def run_python_file(module_name, path):
303 spec = importlib.util.spec_from_file_location(module_name, path)
304 spec.loader.exec_module( importlib.util.module_from_spec(spec) )
305else:
306 from importlib.machinery import SourceFileLoader
307 def run_python_file(module_name, path):
308 SourceFileLoader(module_name, path).load_module()
309
310def msisdn_inc(msisdn_str):
311 'add 1 and preserve leading zeros'
312 return ('%%0%dd' % len(msisdn_str)) % (int(msisdn_str) + 1)
313
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200314class InputThread(threading.Thread):
315 def __init__(self, prompt):
316 super().__init__()
317 self.prompt = prompt
318 self.result = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200319
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200320 def run(self):
321 self.result = input(self.prompt)
322
323def input_polling(prompt, poll_func):
324 input_thread = InputThread(prompt)
325 input_thread.start()
326
327 while input_thread.is_alive():
328 poll_func()
329 time.sleep(1)
330
331 input_thread.join()
332 return input_thread.result
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200333
Pau Espin Pedrol404e1502017-08-22 11:17:43 +0200334def str2bool(val):
335 if val is None or not val:
336 return False
337 if val.upper() in ['FALSE', 'NO', 'OFF']:
338 return False
339 if val.upper() in ['TRUE','YES', 'ON']:
340 return True
341 raise ValueError('Invalid BOOL field: %r' % val)
342
Pau Espin Pedrol43737da2017-08-28 14:04:07 +0200343def list_validate_same_elem_type(li):
344 '''
345 Checks that all elements in the list are of the same type and returns that type.
346 If the list is empty, returns None
347 If one of the elements is not of the same type, it throws a ValueError exception.
348 '''
349 if len(li) == 0:
350 return None
351 t = type(li[0])
352 for elem in li:
353 if type(elem) != t:
354 raise ValueError('List contains elements of different types: %r vs %r' % (t, type(elem)))
355 return t
356
357def empty_instance_type(t):
358 if t == dict:
359 return {}
360 elif t == list:
361 return []
362 elif t == tuple:
363 return ()
364 raise ValueError('type %r not supported!' % t)
365
Pau Espin Pedrolabd556a2017-09-04 16:26:08 +0200366def encryption2osmovty(val):
367 assert val[:3] == 'a5_'
368 return 'a5 ' + val[3:]
369
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200370# vim: expandtab tabstop=4 shiftwidth=4