blob: e1d3a0a93529f83929d23abc57ebb41dde8ee0a8 [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
Pau Espin Pedrol7cb6bad2020-09-18 15:17:08 +020026from .core import util
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020027from .core import process
Pau Espin Pedrol166dc102020-06-04 18:44:42 +020028from .core import template
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020029from .core import log as log_module
30from .core import process as process_module
31from .core import resource
32from .core.event_loop import MainLoop
33
Neels Hofmeyrde6743d2020-11-27 08:24:56 +010034test = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020035suite = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020036log = None
37dbg = None
38err = None
39wait = None
Pau Espin Pedrol927344b2017-05-22 16:38:49 +020040wait_no_raise = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020041sleep = None
42poll = None
43prompt = None
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +020044Sms = None
Pau Espin Pedrol878b2c62018-05-18 15:57:06 +020045process = None
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +020046tenv = None
Neels Hofmeyr3b493f32020-12-01 03:51:27 +010047print = None
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020048
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020049class Timeout(Exception):
50 pass
51
52class TestEnv(log_module.Origin):
53 def __init__(self, suite_run, test):
54 super().__init__(log_module.C_TST, test.name())
55 self.suite_run = suite_run
56 self._test = test
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020057 self._processes = []
58 self.test_import_modules_to_clean_up = []
59 self.objects_to_clean_up = None
60 MainLoop.register_poll_func(self.poll)
Pau Espin Pedrolc3cf6822020-06-12 17:54:55 +020061 if self._test.timeout is not None: # aimed at firing once
Pau Espin Pedrol683d1962020-06-15 11:39:41 +020062 MainLoop.register_poll_func(self._timeout_expired, timestep=self._test.timeout)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020063
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020064 def test(self):
65 return self._test
66
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020067 def suite(self):
68 return self.suite_run
69
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020070 def remember_to_stop(self, process, respawn=False):
71 '''Ask suite to monitor and manage lifecycle of the Process object. If a
72 process managed by suite finishes before cleanup time, the current test
73 will be marked as FAIL and end immediatelly. If respwan=True, then suite
74 will respawn() the process instead.'''
75 self._processes.insert(0, (process, respawn))
76
77 def stop_processes(self):
78 if len(self._processes) == 0:
79 return
80 strategy = process_module.ParallelTerminationStrategy()
81 while self._processes:
82 proc, _ = self._processes.pop()
83 strategy.add_process(proc)
84 strategy.terminate_all()
85
86 def stop_process(self, process):
87 'Remove process from monitored list and stop it'
88 for proc_respawn in self._processes:
89 proc, respawn = proc_respawn
90 if proc == process:
91 self._processes.remove(proc_respawn)
92 proc.terminate()
93
94 def register_for_cleanup(self, *obj):
95 assert all([hasattr(o, 'cleanup') for o in obj])
96 self.objects_to_clean_up = self.objects_to_clean_up or []
97 self.objects_to_clean_up.extend(obj)
98
99 def objects_cleanup(self):
100 while self.objects_to_clean_up:
101 obj = self.objects_to_clean_up.pop()
102 try:
103 obj.cleanup()
104 except Exception:
105 log_module.log_exn()
106
107 def test_import_modules_register_for_cleanup(self, mod):
108 '''
109 Tests are required to call this API for any module loaded from its own
110 lib subdir, because they are loaded in the global namespace. Otherwise
111 later tests importing modules with the same name will re-use an already
112 loaded module.
113 '''
114 if mod not in self.test_import_modules_to_clean_up:
115 self.dbg('registering module %r for cleanup' % mod)
116 self.test_import_modules_to_clean_up.append(mod)
117
118 def test_import_modules_cleanup(self):
119 while self.test_import_modules_to_clean_up:
120 mod = self.test_import_modules_to_clean_up.pop()
121 try:
122 self.dbg('Cleaning up module %r' % mod)
123 del sys.modules[mod.__name__]
124 del mod
125 except Exception:
126 log_module.log_exn()
127
Pau Espin Pedrol683d1962020-06-15 11:39:41 +0200128 def _timeout_expired(self):
Pau Espin Pedrolc3cf6822020-06-12 17:54:55 +0200129 # Avoid timeout being called several times:
Pau Espin Pedrol683d1962020-06-15 11:39:41 +0200130 MainLoop.unregister_poll_func(self._timeout_expired)
Pau Espin Pedrolc3cf6822020-06-12 17:54:55 +0200131 raise log_module.Error('Test Timeout triggered: %d seconds elapsed' % self._test.elapsed_time())
132
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200133 def poll(self):
134 for proc, respawn in self._processes:
135 if proc.terminated():
136 if respawn == True:
137 proc.respawn()
138 else:
139 proc.log_stdout_tail()
140 proc.log_stderr_tail()
141 log_module.ctx(proc)
142 raise log_module.Error('Process ended prematurely: %s' % proc.name())
143
144 def stop(self):
145 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
146 # base exception is raised. Make sure to stop processes in this
147 # finally section. Resources are automatically freed with 'atexit'.
148 self.stop_processes()
149 self.objects_cleanup()
150 self.suite_run.reserved_resources.put_all()
151 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrol683d1962020-06-15 11:39:41 +0200152 MainLoop.unregister_poll_func(self._timeout_expired)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200153 self.test_import_modules_cleanup()
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200154 self.set_overlay_template_dir(None)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200155
Pau Espin Pedrol30637302020-05-06 21:11:02 +0200156 def config_suite_specific(self):
157 return self.suite_run.config_suite_specific()
158
159 def config_test_specific(self):
Pau Espin Pedrola75f85a2020-06-12 17:13:26 +0200160 return self._test.config_test_specific()
Pau Espin Pedrol30637302020-05-06 21:11:02 +0200161
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200162 def set_overlay_template_dir(self, template_dir=None):
163 '''Overlay a directory on top of default one when looking for
164 directories. It must be called everytime a template file is updated.'''
165 if template_dir is None:
166 template.set_templates_dir(template.default_templates_dir())
167 else:
Pau Espin Pedrola4bb6d32020-06-11 16:14:47 +0200168 self.dbg('template dir overlay set: %s' % template_dir)
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200169 template.set_templates_dir(template_dir, template.default_templates_dir())
170
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200171 def prompt(self, *msgs, **msg_details):
172 'ask for user interaction. Do not use in tests that should run automatically!'
173 if msg_details:
174 msgs = list(msgs)
175 msgs.append('{%s}' %
176 (', '.join(['%s=%r' % (k,v)
177 for k,v in sorted(msg_details.items())])))
178 msg = ' '.join(msgs) or 'Hit Enter to continue'
179 self.log('prompt:', msg)
180 sys.__stdout__.write('\n\n--- PROMPT ---\n')
181 sys.__stdout__.write(msg)
182 sys.__stdout__.write('\n')
183 sys.__stdout__.flush()
184 entered = util.input_polling('> ', MainLoop.poll)
185 self.log('prompt entered:', repr(entered))
186 return entered
187
188 def get_reserved_resource(self, resource_class_str, specifics=None):
189 return self.suite_run.get_reserved_resource(resource_class_str, specifics)
190
191 def ip_address(self, specifics=None):
192 return self.get_reserved_resource(resource.R_IP_ADDRESS, specifics)
193
194 def nitb(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200195 from .obj.nitb_osmo import OsmoNitb
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200196 if ip_address is None:
197 ip_address = self.ip_address()
Neels Hofmeyr012a17d2020-12-08 17:36:04 +0100198 nitb_obj = OsmoNitb(self, ip_address)
199 self.register_for_cleanup(nitb_obj)
200 return nitb_obj
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200201
202 def hlr(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200203 from .obj.hlr_osmo import OsmoHlr
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200204 if ip_address is None:
205 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200206 return OsmoHlr(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200207
208 def ggsn(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200209 from .obj.ggsn_osmo import OsmoGgsn
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200210 if ip_address is None:
211 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200212 return OsmoGgsn(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200213
214 def sgsn(self, hlr, ggsn, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200215 from .obj import sgsn_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200216 if ip_address is None:
217 ip_address = self.ip_address()
218 return sgsn_osmo.OsmoSgsn(self, hlr, ggsn, ip_address)
219
220 def mgcpgw(self, ip_address=None, bts_ip=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200221 from .obj.mgcpgw_osmo import OsmoMgcpgw
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200222 if ip_address is None:
223 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200224 return OsmoMgcpgw(self, ip_address, bts_ip)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200225
226 def mgw(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200227 from .obj.mgw_osmo import OsmoMgw
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200228 if ip_address is None:
229 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200230 return OsmoMgw(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200231
232 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200233 from .obj import msc_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200234 if ip_address is None:
235 ip_address = self.ip_address()
Neels Hofmeyr012a17d2020-12-08 17:36:04 +0100236 msc_obj = msc_osmo.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
237 self.register_for_cleanup(msc_obj)
238 return msc_obj
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200239
240 def bsc(self, msc, mgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200241 from .obj.bsc_osmo import OsmoBsc
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200242 if ip_address is None:
243 ip_address = self.ip_address()
Neels Hofmeyraf4e2312020-11-27 08:20:56 +0100244 bsc_obj = OsmoBsc(self, msc, mgw, stp, ip_address)
245 self.register_for_cleanup(bsc_obj)
246 return bsc_obj
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200247
248 def stp(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200249 from .obj.stp_osmo import OsmoStp
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200250 if ip_address is None:
251 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200252 return OsmoStp(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200253
254 def ms_driver(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200255 from .obj.ms_driver import MsDriver
256 ms = MsDriver(self)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200257 self.register_for_cleanup(ms)
258 return ms
259
260 def bts(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200261 from .obj.bts import Bts
262 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 +0200263 bts_obj.set_lac(self.lac())
264 bts_obj.set_rac(self.rac())
265 bts_obj.set_cellid(self.cellid())
266 bts_obj.set_bvci(self.bvci())
267 self.register_for_cleanup(bts_obj)
268 return bts_obj
269
270 def modem(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200271 from .obj.ms import MS
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200272 conf = self.get_reserved_resource(resource.R_MODEM, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200273 ms_obj = MS.get_instance_by_type(self, conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200274 self.register_for_cleanup(ms_obj)
275 return ms_obj
276
277 def modems(self, count):
278 l = []
279 for i in range(count):
280 l.append(self.modem())
281 return l
282
283 def all_resources(self, resource_func):
284 """Returns all yielded resource."""
285 l = []
286 while True:
287 try:
288 l.append(resource_func())
289 except resource.NoResourceExn:
290 return l
291
292 def esme(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200293 from .obj.esme import Esme
294 esme_obj = Esme(self.msisdn())
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200295 self.register_for_cleanup(esme_obj)
296 return esme_obj
297
298 def run_node(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200299 from .obj.run_node import RunNode
300 return RunNode.from_conf(self.get_reserved_resource(resource.R_RUN_NODE, specifics=specifics))
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200301
302 def enb(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200303 from .obj.enb import eNodeB
304 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 +0200305 self.register_for_cleanup(enb_obj)
306 return enb_obj
307
308 def epc(self, run_node=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200309 from .obj.epc import EPC
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200310 if run_node is None:
311 run_node = self.run_node()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200312 epc_obj = EPC.get_instance_by_type(self, run_node)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200313 self.register_for_cleanup(epc_obj)
314 return epc_obj
315
316 def osmocon(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200317 from .obj.osmocon import Osmocon
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200318 conf = self.get_reserved_resource(resource.R_OSMOCON, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200319 osmocon_obj = Osmocon(self, conf=conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200320 self.register_for_cleanup(osmocon_obj)
321 return osmocon_obj
322
323 def iperf3srv(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200324 from .obj.iperf3 import IPerf3Server
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200325 if ip_address is None:
326 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200327 iperf3srv_obj = IPerf3Server(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200328 return iperf3srv_obj
329
Pau Espin Pedrol969a4ee2020-07-02 13:21:08 +0200330 def stress(self, run_node=None):
331 from .obj.stress import StressTool
332 stress_obj = StressTool(self, run_node)
333 return stress_obj
334
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200335 def msisdn(self):
336 msisdn = self.suite_run.resource_pool().next_msisdn(self)
337 self.log('using MSISDN', msisdn)
338 return msisdn
339
340 def lac(self):
341 lac = self.suite_run.resource_pool().next_lac(self)
342 self.log('using LAC', lac)
343 return lac
344
345 def rac(self):
346 rac = self.suite_run.resource_pool().next_rac(self)
347 self.log('using RAC', rac)
348 return rac
349
350 def cellid(self):
351 cellid = self.suite_run.resource_pool().next_cellid(self)
352 self.log('using CellId', cellid)
353 return cellid
354
355 def bvci(self):
356 bvci = self.suite_run.resource_pool().next_bvci(self)
357 self.log('using BVCI', bvci)
358 return bvci
359
Neels Hofmeyr3b493f32020-12-01 03:51:27 +0100360 def print(self, *messages, **named_items):
361 log_module.log(*messages, _origin=self.test(), _src=3, **named_items)
362
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200363
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200364def setup(suite_run, _test):
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200365 from .core.event_loop import MainLoop
366 from .obj.sms import Sms as Sms_class
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200367
Neels Hofmeyr3b493f32020-12-01 03:51:27 +0100368 global test, log, dbg, err, wait, wait_no_raise, sleep, poll, prompt, Sms, process, tenv, print
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200369
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200370 test = _test
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200371 log = test.log
372 dbg = test.dbg
373 err = test.err
Pau Espin Pedrol664e3832020-06-10 19:30:33 +0200374 tenv = TestEnv(suite_run, _test)
375 wait = MainLoop.wait
376 wait_no_raise = MainLoop.wait_no_raise
377 sleep = MainLoop.sleep
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200378 poll = MainLoop.poll
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200379 Sms = Sms_class
Pau Espin Pedrol878b2c62018-05-18 15:57:06 +0200380 process = process_module
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +0200381 prompt = tenv.prompt
Neels Hofmeyr3b493f32020-12-01 03:51:27 +0100382 print = tenv.print
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +0200383 return tenv
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200384
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200385# vim: expandtab tabstop=4 shiftwidth=4