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