blob: 77d844af579808af9a38c96bc1645949c7a6098b [file] [log] [blame]
Neels Hofmeyr3531a192017-03-28 14:30:28 +02001# osmo_gsm_tester: context for individual test runs
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +02002#
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 Hofmeyrdae3d3c2017-03-28 12:16:58 +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 Hofmeyrdae3d3c2017-03-28 12:16:58 +020016#
Harald Welte27205342017-06-03 09:51:45 +020017# You should have received a copy of the GNU General Public License
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020018# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
Neels Hofmeyr3531a192017-03-28 14:30:28 +020020# These will be initialized before each test run.
21# A test script can thus establish its context by doing:
Pau Espin Pedroldfe38ad2017-11-09 13:57:39 +010022# from osmo_gsm_tester.testenv import *
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020023
24import sys
25
26from .core import process
Pau Espin Pedrol166dc102020-06-04 18:44:42 +020027from .core import template
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020028from .core import log as log_module
29from .core import process as process_module
30from .core import resource
31from .core.event_loop import MainLoop
32
Neels Hofmeyr3531a192017-03-28 14:30:28 +020033suite = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020034log = None
35dbg = None
36err = None
37wait = None
Pau Espin Pedrol927344b2017-05-22 16:38:49 +020038wait_no_raise = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020039sleep = None
40poll = None
41prompt = None
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +020042Sms = None
Pau Espin Pedrol878b2c62018-05-18 15:57:06 +020043process = None
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +020044tenv = None
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020045
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020046class Timeout(Exception):
47 pass
48
49class TestEnv(log_module.Origin):
50 def __init__(self, suite_run, test):
51 super().__init__(log_module.C_TST, test.name())
52 self.suite_run = suite_run
53 self._test = test
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020054 self._processes = []
55 self.test_import_modules_to_clean_up = []
56 self.objects_to_clean_up = None
57 MainLoop.register_poll_func(self.poll)
Pau Espin Pedrolc3cf6822020-06-12 17:54:55 +020058 if self._test.timeout is not None: # aimed at firing once
59 MainLoop.register_poll_func(self.timeout_expired, timestep=self._test.timeout)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020060
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020061 def test(self):
62 return self._test
63
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020064 def suite(self):
65 return self.suite_run
66
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020067 def remember_to_stop(self, process, respawn=False):
68 '''Ask suite to monitor and manage lifecycle of the Process object. If a
69 process managed by suite finishes before cleanup time, the current test
70 will be marked as FAIL and end immediatelly. If respwan=True, then suite
71 will respawn() the process instead.'''
72 self._processes.insert(0, (process, respawn))
73
74 def stop_processes(self):
75 if len(self._processes) == 0:
76 return
77 strategy = process_module.ParallelTerminationStrategy()
78 while self._processes:
79 proc, _ = self._processes.pop()
80 strategy.add_process(proc)
81 strategy.terminate_all()
82
83 def stop_process(self, process):
84 'Remove process from monitored list and stop it'
85 for proc_respawn in self._processes:
86 proc, respawn = proc_respawn
87 if proc == process:
88 self._processes.remove(proc_respawn)
89 proc.terminate()
90
91 def register_for_cleanup(self, *obj):
92 assert all([hasattr(o, 'cleanup') for o in obj])
93 self.objects_to_clean_up = self.objects_to_clean_up or []
94 self.objects_to_clean_up.extend(obj)
95
96 def objects_cleanup(self):
97 while self.objects_to_clean_up:
98 obj = self.objects_to_clean_up.pop()
99 try:
100 obj.cleanup()
101 except Exception:
102 log_module.log_exn()
103
104 def test_import_modules_register_for_cleanup(self, mod):
105 '''
106 Tests are required to call this API for any module loaded from its own
107 lib subdir, because they are loaded in the global namespace. Otherwise
108 later tests importing modules with the same name will re-use an already
109 loaded module.
110 '''
111 if mod not in self.test_import_modules_to_clean_up:
112 self.dbg('registering module %r for cleanup' % mod)
113 self.test_import_modules_to_clean_up.append(mod)
114
115 def test_import_modules_cleanup(self):
116 while self.test_import_modules_to_clean_up:
117 mod = self.test_import_modules_to_clean_up.pop()
118 try:
119 self.dbg('Cleaning up module %r' % mod)
120 del sys.modules[mod.__name__]
121 del mod
122 except Exception:
123 log_module.log_exn()
124
Pau Espin Pedrolc3cf6822020-06-12 17:54:55 +0200125 def timeout_expired(self):
126 # Avoid timeout being called several times:
127 MainLoop.unregister_poll_func(self.timeout_expired)
128 raise log_module.Error('Test Timeout triggered: %d seconds elapsed' % self._test.elapsed_time())
129
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200130 def poll(self):
131 for proc, respawn in self._processes:
132 if proc.terminated():
133 if respawn == True:
134 proc.respawn()
135 else:
136 proc.log_stdout_tail()
137 proc.log_stderr_tail()
138 log_module.ctx(proc)
139 raise log_module.Error('Process ended prematurely: %s' % proc.name())
140
141 def stop(self):
142 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
143 # base exception is raised. Make sure to stop processes in this
144 # finally section. Resources are automatically freed with 'atexit'.
145 self.stop_processes()
146 self.objects_cleanup()
147 self.suite_run.reserved_resources.put_all()
148 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrolc3cf6822020-06-12 17:54:55 +0200149 MainLoop.unregister_poll_func(self.timeout_expired)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200150 self.test_import_modules_cleanup()
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200151 self.set_overlay_template_dir(None)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200152
Pau Espin Pedrol30637302020-05-06 21:11:02 +0200153 def config_suite_specific(self):
154 return self.suite_run.config_suite_specific()
155
156 def config_test_specific(self):
Pau Espin Pedrola75f85a2020-06-12 17:13:26 +0200157 return self._test.config_test_specific()
Pau Espin Pedrol30637302020-05-06 21:11:02 +0200158
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200159 def set_overlay_template_dir(self, template_dir=None):
160 '''Overlay a directory on top of default one when looking for
161 directories. It must be called everytime a template file is updated.'''
162 if template_dir is None:
163 template.set_templates_dir(template.default_templates_dir())
164 else:
Pau Espin Pedrola4bb6d32020-06-11 16:14:47 +0200165 self.dbg('template dir overlay set: %s' % template_dir)
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200166 template.set_templates_dir(template_dir, template.default_templates_dir())
167
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200168 def prompt(self, *msgs, **msg_details):
169 'ask for user interaction. Do not use in tests that should run automatically!'
170 if msg_details:
171 msgs = list(msgs)
172 msgs.append('{%s}' %
173 (', '.join(['%s=%r' % (k,v)
174 for k,v in sorted(msg_details.items())])))
175 msg = ' '.join(msgs) or 'Hit Enter to continue'
176 self.log('prompt:', msg)
177 sys.__stdout__.write('\n\n--- PROMPT ---\n')
178 sys.__stdout__.write(msg)
179 sys.__stdout__.write('\n')
180 sys.__stdout__.flush()
181 entered = util.input_polling('> ', MainLoop.poll)
182 self.log('prompt entered:', repr(entered))
183 return entered
184
185 def get_reserved_resource(self, resource_class_str, specifics=None):
186 return self.suite_run.get_reserved_resource(resource_class_str, specifics)
187
188 def ip_address(self, specifics=None):
189 return self.get_reserved_resource(resource.R_IP_ADDRESS, specifics)
190
191 def nitb(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200192 from .obj.nitb_osmo import OsmoNitb
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200193 if ip_address is None:
194 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200195 return OsmoNitb(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200196
197 def hlr(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200198 from .obj.hlr_osmo import OsmoHlr
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200199 if ip_address is None:
200 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200201 return OsmoHlr(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200202
203 def ggsn(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200204 from .obj.ggsn_osmo import OsmoGgsn
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200205 if ip_address is None:
206 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200207 return OsmoGgsn(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200208
209 def sgsn(self, hlr, ggsn, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200210 from .obj import sgsn_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200211 if ip_address is None:
212 ip_address = self.ip_address()
213 return sgsn_osmo.OsmoSgsn(self, hlr, ggsn, ip_address)
214
215 def mgcpgw(self, ip_address=None, bts_ip=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200216 from .obj.mgcpgw_osmo import OsmoMgcpgw
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200217 if ip_address is None:
218 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200219 return OsmoMgcpgw(self, ip_address, bts_ip)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200220
221 def mgw(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200222 from .obj.mgw_osmo import OsmoMgw
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200223 if ip_address is None:
224 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200225 return OsmoMgw(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200226
227 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200228 from .obj import msc_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200229 if ip_address is None:
230 ip_address = self.ip_address()
231 return msc_osmo.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
232
233 def bsc(self, msc, mgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200234 from .obj.bsc_osmo import OsmoBsc
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200235 if ip_address is None:
236 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200237 return OsmoBsc(self, msc, mgw, stp, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200238
239 def stp(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200240 from .obj.stp_osmo import OsmoStp
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200241 if ip_address is None:
242 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200243 return OsmoStp(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200244
245 def ms_driver(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200246 from .obj.ms_driver import MsDriver
247 ms = MsDriver(self)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200248 self.register_for_cleanup(ms)
249 return ms
250
251 def bts(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200252 from .obj.bts import Bts
253 bts_obj = Bts.get_instance_by_type(self, self.get_reserved_resource(resource.R_BTS, specifics=specifics))
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200254 bts_obj.set_lac(self.lac())
255 bts_obj.set_rac(self.rac())
256 bts_obj.set_cellid(self.cellid())
257 bts_obj.set_bvci(self.bvci())
258 self.register_for_cleanup(bts_obj)
259 return bts_obj
260
261 def modem(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200262 from .obj.ms import MS
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200263 conf = self.get_reserved_resource(resource.R_MODEM, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200264 ms_obj = MS.get_instance_by_type(self, conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200265 self.register_for_cleanup(ms_obj)
266 return ms_obj
267
268 def modems(self, count):
269 l = []
270 for i in range(count):
271 l.append(self.modem())
272 return l
273
274 def all_resources(self, resource_func):
275 """Returns all yielded resource."""
276 l = []
277 while True:
278 try:
279 l.append(resource_func())
280 except resource.NoResourceExn:
281 return l
282
283 def esme(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200284 from .obj.esme import Esme
285 esme_obj = Esme(self.msisdn())
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200286 self.register_for_cleanup(esme_obj)
287 return esme_obj
288
289 def run_node(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200290 from .obj.run_node import RunNode
291 return RunNode.from_conf(self.get_reserved_resource(resource.R_RUN_NODE, specifics=specifics))
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200292
293 def enb(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200294 from .obj.enb import eNodeB
295 enb_obj = eNodeB.get_instance_by_type(self, self.get_reserved_resource(resource.R_ENB, specifics=specifics))
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200296 self.register_for_cleanup(enb_obj)
297 return enb_obj
298
299 def epc(self, run_node=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200300 from .obj.epc import EPC
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200301 if run_node is None:
302 run_node = self.run_node()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200303 epc_obj = EPC.get_instance_by_type(self, run_node)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200304 self.register_for_cleanup(epc_obj)
305 return epc_obj
306
307 def osmocon(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200308 from .obj.osmocon import Osmocon
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200309 conf = self.get_reserved_resource(resource.R_OSMOCON, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200310 osmocon_obj = Osmocon(self, conf=conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200311 self.register_for_cleanup(osmocon_obj)
312 return osmocon_obj
313
314 def iperf3srv(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200315 from .obj.iperf3 import IPerf3Server
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200316 if ip_address is None:
317 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200318 iperf3srv_obj = IPerf3Server(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200319 return iperf3srv_obj
320
321 def msisdn(self):
322 msisdn = self.suite_run.resource_pool().next_msisdn(self)
323 self.log('using MSISDN', msisdn)
324 return msisdn
325
326 def lac(self):
327 lac = self.suite_run.resource_pool().next_lac(self)
328 self.log('using LAC', lac)
329 return lac
330
331 def rac(self):
332 rac = self.suite_run.resource_pool().next_rac(self)
333 self.log('using RAC', rac)
334 return rac
335
336 def cellid(self):
337 cellid = self.suite_run.resource_pool().next_cellid(self)
338 self.log('using CellId', cellid)
339 return cellid
340
341 def bvci(self):
342 bvci = self.suite_run.resource_pool().next_bvci(self)
343 self.log('using BVCI', bvci)
344 return bvci
345
346
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200347def setup(suite_run, _test):
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200348 from .core.event_loop import MainLoop
349 from .obj.sms import Sms as Sms_class
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200350
Pau Espin Pedrol315ba572020-06-04 17:09:16 +0200351 global test, log, dbg, err, wait, wait_no_raise, sleep, poll, prompt, Sms, process, tenv
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200352
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200353 test = _test
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200354 log = test.log
355 dbg = test.dbg
356 err = test.err
Pau Espin Pedrol664e3832020-06-10 19:30:33 +0200357 tenv = TestEnv(suite_run, _test)
358 wait = MainLoop.wait
359 wait_no_raise = MainLoop.wait_no_raise
360 sleep = MainLoop.sleep
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200361 poll = MainLoop.poll
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200362 Sms = Sms_class
Pau Espin Pedrol878b2c62018-05-18 15:57:06 +0200363 process = process_module
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +0200364 prompt = tenv.prompt
365 return tenv
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200366
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200367# vim: expandtab tabstop=4 shiftwidth=4