blob: 266243c17fcfd1f818b001db99cf9046912047cf [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:
47 return iface
48 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
104 self.fd = os.open(self.path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
105 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
308# vim: expandtab tabstop=4 shiftwidth=4