blob: 11199c2cd4315c1459c41aa0ceefa3eb55ad99d4 [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)
58
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020059 def test(self):
60 return self._test
61
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020062 def suite(self):
63 return self.suite_run
64
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020065 def remember_to_stop(self, process, respawn=False):
66 '''Ask suite to monitor and manage lifecycle of the Process object. If a
67 process managed by suite finishes before cleanup time, the current test
68 will be marked as FAIL and end immediatelly. If respwan=True, then suite
69 will respawn() the process instead.'''
70 self._processes.insert(0, (process, respawn))
71
72 def stop_processes(self):
73 if len(self._processes) == 0:
74 return
75 strategy = process_module.ParallelTerminationStrategy()
76 while self._processes:
77 proc, _ = self._processes.pop()
78 strategy.add_process(proc)
79 strategy.terminate_all()
80
81 def stop_process(self, process):
82 'Remove process from monitored list and stop it'
83 for proc_respawn in self._processes:
84 proc, respawn = proc_respawn
85 if proc == process:
86 self._processes.remove(proc_respawn)
87 proc.terminate()
88
89 def register_for_cleanup(self, *obj):
90 assert all([hasattr(o, 'cleanup') for o in obj])
91 self.objects_to_clean_up = self.objects_to_clean_up or []
92 self.objects_to_clean_up.extend(obj)
93
94 def objects_cleanup(self):
95 while self.objects_to_clean_up:
96 obj = self.objects_to_clean_up.pop()
97 try:
98 obj.cleanup()
99 except Exception:
100 log_module.log_exn()
101
102 def test_import_modules_register_for_cleanup(self, mod):
103 '''
104 Tests are required to call this API for any module loaded from its own
105 lib subdir, because they are loaded in the global namespace. Otherwise
106 later tests importing modules with the same name will re-use an already
107 loaded module.
108 '''
109 if mod not in self.test_import_modules_to_clean_up:
110 self.dbg('registering module %r for cleanup' % mod)
111 self.test_import_modules_to_clean_up.append(mod)
112
113 def test_import_modules_cleanup(self):
114 while self.test_import_modules_to_clean_up:
115 mod = self.test_import_modules_to_clean_up.pop()
116 try:
117 self.dbg('Cleaning up module %r' % mod)
118 del sys.modules[mod.__name__]
119 del mod
120 except Exception:
121 log_module.log_exn()
122
123 def poll(self):
124 for proc, respawn in self._processes:
125 if proc.terminated():
126 if respawn == True:
127 proc.respawn()
128 else:
129 proc.log_stdout_tail()
130 proc.log_stderr_tail()
131 log_module.ctx(proc)
132 raise log_module.Error('Process ended prematurely: %s' % proc.name())
133
134 def stop(self):
135 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
136 # base exception is raised. Make sure to stop processes in this
137 # finally section. Resources are automatically freed with 'atexit'.
138 self.stop_processes()
139 self.objects_cleanup()
140 self.suite_run.reserved_resources.put_all()
141 MainLoop.unregister_poll_func(self.poll)
142 self.test_import_modules_cleanup()
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200143 self.set_overlay_template_dir(None)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200144
Pau Espin Pedrol30637302020-05-06 21:11:02 +0200145 def config_suite_specific(self):
146 return self.suite_run.config_suite_specific()
147
148 def config_test_specific(self):
Pau Espin Pedrola75f85a2020-06-12 17:13:26 +0200149 return self._test.config_test_specific()
Pau Espin Pedrol30637302020-05-06 21:11:02 +0200150
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200151 def set_overlay_template_dir(self, template_dir=None):
152 '''Overlay a directory on top of default one when looking for
153 directories. It must be called everytime a template file is updated.'''
154 if template_dir is None:
155 template.set_templates_dir(template.default_templates_dir())
156 else:
Pau Espin Pedrola4bb6d32020-06-11 16:14:47 +0200157 self.dbg('template dir overlay set: %s' % template_dir)
Pau Espin Pedrol166dc102020-06-04 18:44:42 +0200158 template.set_templates_dir(template_dir, template.default_templates_dir())
159
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200160 def prompt(self, *msgs, **msg_details):
161 'ask for user interaction. Do not use in tests that should run automatically!'
162 if msg_details:
163 msgs = list(msgs)
164 msgs.append('{%s}' %
165 (', '.join(['%s=%r' % (k,v)
166 for k,v in sorted(msg_details.items())])))
167 msg = ' '.join(msgs) or 'Hit Enter to continue'
168 self.log('prompt:', msg)
169 sys.__stdout__.write('\n\n--- PROMPT ---\n')
170 sys.__stdout__.write(msg)
171 sys.__stdout__.write('\n')
172 sys.__stdout__.flush()
173 entered = util.input_polling('> ', MainLoop.poll)
174 self.log('prompt entered:', repr(entered))
175 return entered
176
177 def get_reserved_resource(self, resource_class_str, specifics=None):
178 return self.suite_run.get_reserved_resource(resource_class_str, specifics)
179
180 def ip_address(self, specifics=None):
181 return self.get_reserved_resource(resource.R_IP_ADDRESS, specifics)
182
183 def nitb(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200184 from .obj.nitb_osmo import OsmoNitb
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200185 if ip_address is None:
186 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200187 return OsmoNitb(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200188
189 def hlr(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200190 from .obj.hlr_osmo import OsmoHlr
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200191 if ip_address is None:
192 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200193 return OsmoHlr(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200194
195 def ggsn(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200196 from .obj.ggsn_osmo import OsmoGgsn
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200197 if ip_address is None:
198 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200199 return OsmoGgsn(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200200
201 def sgsn(self, hlr, ggsn, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200202 from .obj import sgsn_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200203 if ip_address is None:
204 ip_address = self.ip_address()
205 return sgsn_osmo.OsmoSgsn(self, hlr, ggsn, ip_address)
206
207 def mgcpgw(self, ip_address=None, bts_ip=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200208 from .obj.mgcpgw_osmo import OsmoMgcpgw
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 OsmoMgcpgw(self, ip_address, bts_ip)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200212
213 def mgw(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200214 from .obj.mgw_osmo import OsmoMgw
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200215 if ip_address is None:
216 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200217 return OsmoMgw(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200218
219 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200220 from .obj import msc_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200221 if ip_address is None:
222 ip_address = self.ip_address()
223 return msc_osmo.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
224
225 def bsc(self, msc, mgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200226 from .obj.bsc_osmo import OsmoBsc
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 OsmoBsc(self, msc, mgw, stp, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200230
231 def stp(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200232 from .obj.stp_osmo import OsmoStp
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200233 if ip_address is None:
234 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200235 return OsmoStp(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200236
237 def ms_driver(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200238 from .obj.ms_driver import MsDriver
239 ms = MsDriver(self)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200240 self.register_for_cleanup(ms)
241 return ms
242
243 def bts(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200244 from .obj.bts import Bts
245 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 +0200246 bts_obj.set_lac(self.lac())
247 bts_obj.set_rac(self.rac())
248 bts_obj.set_cellid(self.cellid())
249 bts_obj.set_bvci(self.bvci())
250 self.register_for_cleanup(bts_obj)
251 return bts_obj
252
253 def modem(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200254 from .obj.ms import MS
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200255 conf = self.get_reserved_resource(resource.R_MODEM, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200256 ms_obj = MS.get_instance_by_type(self, conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200257 self.register_for_cleanup(ms_obj)
258 return ms_obj
259
260 def modems(self, count):
261 l = []
262 for i in range(count):
263 l.append(self.modem())
264 return l
265
266 def all_resources(self, resource_func):
267 """Returns all yielded resource."""
268 l = []
269 while True:
270 try:
271 l.append(resource_func())
272 except resource.NoResourceExn:
273 return l
274
275 def esme(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200276 from .obj.esme import Esme
277 esme_obj = Esme(self.msisdn())
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200278 self.register_for_cleanup(esme_obj)
279 return esme_obj
280
281 def run_node(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200282 from .obj.run_node import RunNode
283 return RunNode.from_conf(self.get_reserved_resource(resource.R_RUN_NODE, specifics=specifics))
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200284
285 def enb(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200286 from .obj.enb import eNodeB
287 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 +0200288 self.register_for_cleanup(enb_obj)
289 return enb_obj
290
291 def epc(self, run_node=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200292 from .obj.epc import EPC
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200293 if run_node is None:
294 run_node = self.run_node()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200295 epc_obj = EPC.get_instance_by_type(self, run_node)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200296 self.register_for_cleanup(epc_obj)
297 return epc_obj
298
299 def osmocon(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200300 from .obj.osmocon import Osmocon
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200301 conf = self.get_reserved_resource(resource.R_OSMOCON, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200302 osmocon_obj = Osmocon(self, conf=conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200303 self.register_for_cleanup(osmocon_obj)
304 return osmocon_obj
305
306 def iperf3srv(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200307 from .obj.iperf3 import IPerf3Server
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200308 if ip_address is None:
309 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200310 iperf3srv_obj = IPerf3Server(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200311 return iperf3srv_obj
312
313 def msisdn(self):
314 msisdn = self.suite_run.resource_pool().next_msisdn(self)
315 self.log('using MSISDN', msisdn)
316 return msisdn
317
318 def lac(self):
319 lac = self.suite_run.resource_pool().next_lac(self)
320 self.log('using LAC', lac)
321 return lac
322
323 def rac(self):
324 rac = self.suite_run.resource_pool().next_rac(self)
325 self.log('using RAC', rac)
326 return rac
327
328 def cellid(self):
329 cellid = self.suite_run.resource_pool().next_cellid(self)
330 self.log('using CellId', cellid)
331 return cellid
332
333 def bvci(self):
334 bvci = self.suite_run.resource_pool().next_bvci(self)
335 self.log('using BVCI', bvci)
336 return bvci
337
338
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200339def setup(suite_run, _test):
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200340 from .core.event_loop import MainLoop
341 from .obj.sms import Sms as Sms_class
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200342
Pau Espin Pedrol315ba572020-06-04 17:09:16 +0200343 global test, log, dbg, err, wait, wait_no_raise, sleep, poll, prompt, Sms, process, tenv
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200344
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200345 test = _test
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200346 log = test.log
347 dbg = test.dbg
348 err = test.err
Pau Espin Pedrol664e3832020-06-10 19:30:33 +0200349 tenv = TestEnv(suite_run, _test)
350 wait = MainLoop.wait
351 wait_no_raise = MainLoop.wait_no_raise
352 sleep = MainLoop.sleep
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200353 poll = MainLoop.poll
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200354 Sms = Sms_class
Pau Espin Pedrol878b2c62018-05-18 15:57:06 +0200355 process = process_module
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +0200356 prompt = tenv.prompt
357 return tenv
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200358
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200359# vim: expandtab tabstop=4 shiftwidth=4