blob: dc7aee05391eb34391cf1f0af1746d021029fb00 [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 Hofmeyr3531a192017-03-28 14:30:28 +020034suite = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020035log = None
36dbg = None
37err = None
38wait = None
Pau Espin Pedrol927344b2017-05-22 16:38:49 +020039wait_no_raise = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020040sleep = None
41poll = None
42prompt = None
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +020043Sms = None
Pau Espin Pedrol878b2c62018-05-18 15:57:06 +020044process = None
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +020045tenv = None
Neels Hofmeyr3b493f32020-12-01 03:51:27 +010046print = None
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020047
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020048class Timeout(Exception):
49 pass
50
51class TestEnv(log_module.Origin):
52 def __init__(self, suite_run, test):
53 super().__init__(log_module.C_TST, test.name())
54 self.suite_run = suite_run
55 self._test = test
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020056 self._processes = []
57 self.test_import_modules_to_clean_up = []
58 self.objects_to_clean_up = None
59 MainLoop.register_poll_func(self.poll)
Pau Espin Pedrolc3cf6822020-06-12 17:54:55 +020060 if self._test.timeout is not None: # aimed at firing once
Pau Espin Pedrol683d1962020-06-15 11:39:41 +020061 MainLoop.register_poll_func(self._timeout_expired, timestep=self._test.timeout)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020062
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020063 def test(self):
64 return self._test
65
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020066 def suite(self):
67 return self.suite_run
68
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020069 def remember_to_stop(self, process, respawn=False):
70 '''Ask suite to monitor and manage lifecycle of the Process object. If a
71 process managed by suite finishes before cleanup time, the current test
72 will be marked as FAIL and end immediatelly. If respwan=True, then suite
73 will respawn() the process instead.'''
74 self._processes.insert(0, (process, respawn))
75
76 def stop_processes(self):
77 if len(self._processes) == 0:
78 return
79 strategy = process_module.ParallelTerminationStrategy()
80 while self._processes:
81 proc, _ = self._processes.pop()
82 strategy.add_process(proc)
83 strategy.terminate_all()
84
85 def stop_process(self, process):
86 'Remove process from monitored list and stop it'
87 for proc_respawn in self._processes:
88 proc, respawn = proc_respawn
89 if proc == process:
90 self._processes.remove(proc_respawn)
91 proc.terminate()
92
93 def register_for_cleanup(self, *obj):
94 assert all([hasattr(o, 'cleanup') for o in obj])
95 self.objects_to_clean_up = self.objects_to_clean_up or []
96 self.objects_to_clean_up.extend(obj)
97
98 def objects_cleanup(self):
99 while self.objects_to_clean_up:
100 obj = self.objects_to_clean_up.pop()
101 try:
102 obj.cleanup()
103 except Exception:
104 log_module.log_exn()
105
106 def test_import_modules_register_for_cleanup(self, mod):
107 '''
108 Tests are required to call this API for any module loaded from its own
109 lib subdir, because they are loaded in the global namespace. Otherwise
110 later tests importing modules with the same name will re-use an already
111 loaded module.
112 '''
113 if mod not in self.test_import_modules_to_clean_up:
114 self.dbg('registering module %r for cleanup' % mod)
115 self.test_import_modules_to_clean_up.append(mod)
116
117 def test_import_modules_cleanup(self):
118 while self.test_import_modules_to_clean_up:
119 mod = self.test_import_modules_to_clean_up.pop()
120 try:
121 self.dbg('Cleaning up module %r' % mod)
122 del sys.modules[mod.__name__]
123 del mod
124 except Exception:
125 log_module.log_exn()
126
Pau Espin Pedrol683d1962020-06-15 11:39:41 +0200127 def _timeout_expired(self):
Pau Espin Pedrolc3cf6822020-06-12 17:54:55 +0200128 # Avoid timeout being called several times:
Pau Espin Pedrol683d1962020-06-15 11:39:41 +0200129 MainLoop.unregister_poll_func(self._timeout_expired)
Pau Espin Pedrolc3cf6822020-06-12 17:54:55 +0200130 raise log_module.Error('Test Timeout triggered: %d seconds elapsed' % self._test.elapsed_time())
131
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200132 def poll(self):
133 for proc, respawn in self._processes:
134 if proc.terminated():
135 if respawn == True:
136 proc.respawn()
137 else:
138 proc.log_stdout_tail()
139 proc.log_stderr_tail()
140 log_module.ctx(proc)
141 raise log_module.Error('Process ended prematurely: %s' % proc.name())
142
143 def stop(self):
144 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
145 # base exception is raised. Make sure to stop processes in this
146 # finally section. Resources are automatically freed with 'atexit'.
147 self.stop_processes()
148 self.objects_cleanup()
149 self.suite_run.reserved_resources.put_all()
150 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrol683d1962020-06-15 11:39:41 +0200151 MainLoop.unregister_poll_func(self._timeout_expired)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200152 self.test_import_modules_cleanup()
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200153 self.set_overlay_template_dir(None)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200154
Pau Espin Pedrol30637302020-05-06 21:11:02 +0200155 def config_suite_specific(self):
156 return self.suite_run.config_suite_specific()
157
158 def config_test_specific(self):
Pau Espin Pedrola75f85a2020-06-12 17:13:26 +0200159 return self._test.config_test_specific()
Pau Espin Pedrol30637302020-05-06 21:11:02 +0200160
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200161 def set_overlay_template_dir(self, template_dir=None):
162 '''Overlay a directory on top of default one when looking for
163 directories. It must be called everytime a template file is updated.'''
164 if template_dir is None:
165 template.set_templates_dir(template.default_templates_dir())
166 else:
Pau Espin Pedrola4bb6d32020-06-11 16:14:47 +0200167 self.dbg('template dir overlay set: %s' % template_dir)
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200168 template.set_templates_dir(template_dir, template.default_templates_dir())
169
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200170 def prompt(self, *msgs, **msg_details):
171 'ask for user interaction. Do not use in tests that should run automatically!'
172 if msg_details:
173 msgs = list(msgs)
174 msgs.append('{%s}' %
175 (', '.join(['%s=%r' % (k,v)
176 for k,v in sorted(msg_details.items())])))
177 msg = ' '.join(msgs) or 'Hit Enter to continue'
178 self.log('prompt:', msg)
179 sys.__stdout__.write('\n\n--- PROMPT ---\n')
180 sys.__stdout__.write(msg)
181 sys.__stdout__.write('\n')
182 sys.__stdout__.flush()
183 entered = util.input_polling('> ', MainLoop.poll)
184 self.log('prompt entered:', repr(entered))
185 return entered
186
187 def get_reserved_resource(self, resource_class_str, specifics=None):
188 return self.suite_run.get_reserved_resource(resource_class_str, specifics)
189
190 def ip_address(self, specifics=None):
191 return self.get_reserved_resource(resource.R_IP_ADDRESS, specifics)
192
193 def nitb(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200194 from .obj.nitb_osmo import OsmoNitb
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200195 if ip_address is None:
196 ip_address = self.ip_address()
Neels Hofmeyr012a17d2020-12-08 17:36:04 +0100197 nitb_obj = OsmoNitb(self, ip_address)
198 self.register_for_cleanup(nitb_obj)
199 return nitb_obj
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200200
201 def hlr(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200202 from .obj.hlr_osmo import OsmoHlr
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200203 if ip_address is None:
204 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200205 return OsmoHlr(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200206
207 def ggsn(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200208 from .obj.ggsn_osmo import OsmoGgsn
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200209 if ip_address is None:
210 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200211 return OsmoGgsn(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200212
213 def sgsn(self, hlr, ggsn, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200214 from .obj import sgsn_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200215 if ip_address is None:
216 ip_address = self.ip_address()
217 return sgsn_osmo.OsmoSgsn(self, hlr, ggsn, ip_address)
218
219 def mgcpgw(self, ip_address=None, bts_ip=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200220 from .obj.mgcpgw_osmo import OsmoMgcpgw
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200221 if ip_address is None:
222 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200223 return OsmoMgcpgw(self, ip_address, bts_ip)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200224
225 def mgw(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200226 from .obj.mgw_osmo import OsmoMgw
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200227 if ip_address is None:
228 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200229 return OsmoMgw(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200230
231 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200232 from .obj import msc_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200233 if ip_address is None:
234 ip_address = self.ip_address()
Neels Hofmeyr012a17d2020-12-08 17:36:04 +0100235 msc_obj = msc_osmo.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
236 self.register_for_cleanup(msc_obj)
237 return msc_obj
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200238
239 def bsc(self, msc, mgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200240 from .obj.bsc_osmo import OsmoBsc
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200241 if ip_address is None:
242 ip_address = self.ip_address()
Neels Hofmeyraf4e2312020-11-27 08:20:56 +0100243 bsc_obj = OsmoBsc(self, msc, mgw, stp, ip_address)
244 self.register_for_cleanup(bsc_obj)
245 return bsc_obj
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200246
247 def stp(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200248 from .obj.stp_osmo import OsmoStp
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200249 if ip_address is None:
250 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200251 return OsmoStp(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200252
253 def ms_driver(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200254 from .obj.ms_driver import MsDriver
255 ms = MsDriver(self)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200256 self.register_for_cleanup(ms)
257 return ms
258
259 def bts(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200260 from .obj.bts import Bts
261 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 +0200262 bts_obj.set_lac(self.lac())
263 bts_obj.set_rac(self.rac())
264 bts_obj.set_cellid(self.cellid())
265 bts_obj.set_bvci(self.bvci())
266 self.register_for_cleanup(bts_obj)
267 return bts_obj
268
269 def modem(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200270 from .obj.ms import MS
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200271 conf = self.get_reserved_resource(resource.R_MODEM, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200272 ms_obj = MS.get_instance_by_type(self, conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200273 self.register_for_cleanup(ms_obj)
274 return ms_obj
275
276 def modems(self, count):
277 l = []
278 for i in range(count):
279 l.append(self.modem())
280 return l
281
282 def all_resources(self, resource_func):
283 """Returns all yielded resource."""
284 l = []
285 while True:
286 try:
287 l.append(resource_func())
288 except resource.NoResourceExn:
289 return l
290
291 def esme(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200292 from .obj.esme import Esme
293 esme_obj = Esme(self.msisdn())
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200294 self.register_for_cleanup(esme_obj)
295 return esme_obj
296
297 def run_node(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200298 from .obj.run_node import RunNode
299 return RunNode.from_conf(self.get_reserved_resource(resource.R_RUN_NODE, specifics=specifics))
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200300
301 def enb(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200302 from .obj.enb import eNodeB
303 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 +0200304 self.register_for_cleanup(enb_obj)
305 return enb_obj
306
307 def epc(self, run_node=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200308 from .obj.epc import EPC
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200309 if run_node is None:
310 run_node = self.run_node()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200311 epc_obj = EPC.get_instance_by_type(self, run_node)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200312 self.register_for_cleanup(epc_obj)
313 return epc_obj
314
315 def osmocon(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200316 from .obj.osmocon import Osmocon
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200317 conf = self.get_reserved_resource(resource.R_OSMOCON, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200318 osmocon_obj = Osmocon(self, conf=conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200319 self.register_for_cleanup(osmocon_obj)
320 return osmocon_obj
321
322 def iperf3srv(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200323 from .obj.iperf3 import IPerf3Server
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200324 if ip_address is None:
325 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200326 iperf3srv_obj = IPerf3Server(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200327 return iperf3srv_obj
328
Pau Espin Pedrol969a4ee2020-07-02 13:21:08 +0200329 def stress(self, run_node=None):
330 from .obj.stress import StressTool
331 stress_obj = StressTool(self, run_node)
332 return stress_obj
333
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200334 def msisdn(self):
335 msisdn = self.suite_run.resource_pool().next_msisdn(self)
336 self.log('using MSISDN', msisdn)
337 return msisdn
338
339 def lac(self):
340 lac = self.suite_run.resource_pool().next_lac(self)
341 self.log('using LAC', lac)
342 return lac
343
344 def rac(self):
345 rac = self.suite_run.resource_pool().next_rac(self)
346 self.log('using RAC', rac)
347 return rac
348
349 def cellid(self):
350 cellid = self.suite_run.resource_pool().next_cellid(self)
351 self.log('using CellId', cellid)
352 return cellid
353
354 def bvci(self):
355 bvci = self.suite_run.resource_pool().next_bvci(self)
356 self.log('using BVCI', bvci)
357 return bvci
358
Neels Hofmeyr3b493f32020-12-01 03:51:27 +0100359 def print(self, *messages, **named_items):
360 log_module.log(*messages, _origin=self.test(), _src=3, **named_items)
361
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200362
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200363def setup(suite_run, _test):
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200364 from .core.event_loop import MainLoop
365 from .obj.sms import Sms as Sms_class
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200366
Neels Hofmeyr3b493f32020-12-01 03:51:27 +0100367 global test, log, dbg, err, wait, wait_no_raise, sleep, poll, prompt, Sms, process, tenv, print
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200368
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200369 test = _test
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200370 log = test.log
371 dbg = test.dbg
372 err = test.err
Pau Espin Pedrol664e3832020-06-10 19:30:33 +0200373 tenv = TestEnv(suite_run, _test)
374 wait = MainLoop.wait
375 wait_no_raise = MainLoop.wait_no_raise
376 sleep = MainLoop.sleep
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200377 poll = MainLoop.poll
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200378 Sms = Sms_class
Pau Espin Pedrol878b2c62018-05-18 15:57:06 +0200379 process = process_module
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +0200380 prompt = tenv.prompt
Neels Hofmeyr3b493f32020-12-01 03:51:27 +0100381 print = tenv.print
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +0200382 return tenv
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200383
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200384# vim: expandtab tabstop=4 shiftwidth=4