blob: f3ac02e071e8710ceb5d63cc1cc2ff832b399200 [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
27from .core import log as log_module
28from .core import process as process_module
29from .core import resource
30from .core.event_loop import MainLoop
31
Neels Hofmeyr3531a192017-03-28 14:30:28 +020032suite = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020033log = None
34dbg = None
35err = None
36wait = None
Pau Espin Pedrol927344b2017-05-22 16:38:49 +020037wait_no_raise = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020038sleep = None
39poll = None
40prompt = None
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +020041Sms = None
Pau Espin Pedrol878b2c62018-05-18 15:57:06 +020042process = None
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +020043tenv = None
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020044
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020045class Timeout(Exception):
46 pass
47
48class TestEnv(log_module.Origin):
49 def __init__(self, suite_run, test):
50 super().__init__(log_module.C_TST, test.name())
51 self.suite_run = suite_run
52 self._test = test
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020053 self._processes = []
54 self.test_import_modules_to_clean_up = []
55 self.objects_to_clean_up = None
56 MainLoop.register_poll_func(self.poll)
57
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020058 def test(self):
59 return self._test
60
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020061 def suite(self):
62 return self.suite_run
63
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +020064 def remember_to_stop(self, process, respawn=False):
65 '''Ask suite to monitor and manage lifecycle of the Process object. If a
66 process managed by suite finishes before cleanup time, the current test
67 will be marked as FAIL and end immediatelly. If respwan=True, then suite
68 will respawn() the process instead.'''
69 self._processes.insert(0, (process, respawn))
70
71 def stop_processes(self):
72 if len(self._processes) == 0:
73 return
74 strategy = process_module.ParallelTerminationStrategy()
75 while self._processes:
76 proc, _ = self._processes.pop()
77 strategy.add_process(proc)
78 strategy.terminate_all()
79
80 def stop_process(self, process):
81 'Remove process from monitored list and stop it'
82 for proc_respawn in self._processes:
83 proc, respawn = proc_respawn
84 if proc == process:
85 self._processes.remove(proc_respawn)
86 proc.terminate()
87
88 def register_for_cleanup(self, *obj):
89 assert all([hasattr(o, 'cleanup') for o in obj])
90 self.objects_to_clean_up = self.objects_to_clean_up or []
91 self.objects_to_clean_up.extend(obj)
92
93 def objects_cleanup(self):
94 while self.objects_to_clean_up:
95 obj = self.objects_to_clean_up.pop()
96 try:
97 obj.cleanup()
98 except Exception:
99 log_module.log_exn()
100
101 def test_import_modules_register_for_cleanup(self, mod):
102 '''
103 Tests are required to call this API for any module loaded from its own
104 lib subdir, because they are loaded in the global namespace. Otherwise
105 later tests importing modules with the same name will re-use an already
106 loaded module.
107 '''
108 if mod not in self.test_import_modules_to_clean_up:
109 self.dbg('registering module %r for cleanup' % mod)
110 self.test_import_modules_to_clean_up.append(mod)
111
112 def test_import_modules_cleanup(self):
113 while self.test_import_modules_to_clean_up:
114 mod = self.test_import_modules_to_clean_up.pop()
115 try:
116 self.dbg('Cleaning up module %r' % mod)
117 del sys.modules[mod.__name__]
118 del mod
119 except Exception:
120 log_module.log_exn()
121
122 def poll(self):
123 for proc, respawn in self._processes:
124 if proc.terminated():
125 if respawn == True:
126 proc.respawn()
127 else:
128 proc.log_stdout_tail()
129 proc.log_stderr_tail()
130 log_module.ctx(proc)
131 raise log_module.Error('Process ended prematurely: %s' % proc.name())
132
133 def stop(self):
134 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
135 # base exception is raised. Make sure to stop processes in this
136 # finally section. Resources are automatically freed with 'atexit'.
137 self.stop_processes()
138 self.objects_cleanup()
139 self.suite_run.reserved_resources.put_all()
140 MainLoop.unregister_poll_func(self.poll)
141 self.test_import_modules_cleanup()
142
Pau Espin Pedrol30637302020-05-06 21:11:02 +0200143 def config_suite_specific(self):
144 return self.suite_run.config_suite_specific()
145
146 def config_test_specific(self):
147 return self.suite_run.config_suite_specific().get(self._test.module_name(), {})
148
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200149 def prompt(self, *msgs, **msg_details):
150 'ask for user interaction. Do not use in tests that should run automatically!'
151 if msg_details:
152 msgs = list(msgs)
153 msgs.append('{%s}' %
154 (', '.join(['%s=%r' % (k,v)
155 for k,v in sorted(msg_details.items())])))
156 msg = ' '.join(msgs) or 'Hit Enter to continue'
157 self.log('prompt:', msg)
158 sys.__stdout__.write('\n\n--- PROMPT ---\n')
159 sys.__stdout__.write(msg)
160 sys.__stdout__.write('\n')
161 sys.__stdout__.flush()
162 entered = util.input_polling('> ', MainLoop.poll)
163 self.log('prompt entered:', repr(entered))
164 return entered
165
166 def get_reserved_resource(self, resource_class_str, specifics=None):
167 return self.suite_run.get_reserved_resource(resource_class_str, specifics)
168
169 def ip_address(self, specifics=None):
170 return self.get_reserved_resource(resource.R_IP_ADDRESS, specifics)
171
172 def nitb(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200173 from .obj.nitb_osmo import OsmoNitb
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200174 if ip_address is None:
175 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200176 return OsmoNitb(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200177
178 def hlr(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200179 from .obj.hlr_osmo import OsmoHlr
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200180 if ip_address is None:
181 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200182 return OsmoHlr(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200183
184 def ggsn(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200185 from .obj.ggsn_osmo import OsmoGgsn
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200186 if ip_address is None:
187 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200188 return OsmoGgsn(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200189
190 def sgsn(self, hlr, ggsn, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200191 from .obj import sgsn_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200192 if ip_address is None:
193 ip_address = self.ip_address()
194 return sgsn_osmo.OsmoSgsn(self, hlr, ggsn, ip_address)
195
196 def mgcpgw(self, ip_address=None, bts_ip=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200197 from .obj.mgcpgw_osmo import OsmoMgcpgw
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200198 if ip_address is None:
199 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200200 return OsmoMgcpgw(self, ip_address, bts_ip)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200201
202 def mgw(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200203 from .obj.mgw_osmo import OsmoMgw
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 OsmoMgw(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200207
208 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200209 from .obj import msc_osmo
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200210 if ip_address is None:
211 ip_address = self.ip_address()
212 return msc_osmo.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
213
214 def bsc(self, msc, mgw, stp, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200215 from .obj.bsc_osmo import OsmoBsc
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200216 if ip_address is None:
217 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200218 return OsmoBsc(self, msc, mgw, stp, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200219
220 def stp(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200221 from .obj.stp_osmo import OsmoStp
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 OsmoStp(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200225
226 def ms_driver(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200227 from .obj.ms_driver import MsDriver
228 ms = MsDriver(self)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200229 self.register_for_cleanup(ms)
230 return ms
231
232 def bts(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200233 from .obj.bts import Bts
234 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 +0200235 bts_obj.set_lac(self.lac())
236 bts_obj.set_rac(self.rac())
237 bts_obj.set_cellid(self.cellid())
238 bts_obj.set_bvci(self.bvci())
239 self.register_for_cleanup(bts_obj)
240 return bts_obj
241
242 def modem(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200243 from .obj.ms import MS
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200244 conf = self.get_reserved_resource(resource.R_MODEM, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200245 ms_obj = MS.get_instance_by_type(self, conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200246 self.register_for_cleanup(ms_obj)
247 return ms_obj
248
249 def modems(self, count):
250 l = []
251 for i in range(count):
252 l.append(self.modem())
253 return l
254
255 def all_resources(self, resource_func):
256 """Returns all yielded resource."""
257 l = []
258 while True:
259 try:
260 l.append(resource_func())
261 except resource.NoResourceExn:
262 return l
263
264 def esme(self):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200265 from .obj.esme import Esme
266 esme_obj = Esme(self.msisdn())
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200267 self.register_for_cleanup(esme_obj)
268 return esme_obj
269
270 def run_node(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200271 from .obj.run_node import RunNode
272 return RunNode.from_conf(self.get_reserved_resource(resource.R_RUN_NODE, specifics=specifics))
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200273
274 def enb(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200275 from .obj.enb import eNodeB
276 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 +0200277 self.register_for_cleanup(enb_obj)
278 return enb_obj
279
280 def epc(self, run_node=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200281 from .obj.epc import EPC
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200282 if run_node is None:
283 run_node = self.run_node()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200284 epc_obj = EPC.get_instance_by_type(self, run_node)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200285 self.register_for_cleanup(epc_obj)
286 return epc_obj
287
288 def osmocon(self, specifics=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200289 from .obj.osmocon import Osmocon
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200290 conf = self.get_reserved_resource(resource.R_OSMOCON, specifics=specifics)
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200291 osmocon_obj = Osmocon(self, conf=conf)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200292 self.register_for_cleanup(osmocon_obj)
293 return osmocon_obj
294
295 def iperf3srv(self, ip_address=None):
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200296 from .obj.iperf3 import IPerf3Server
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200297 if ip_address is None:
298 ip_address = self.ip_address()
Pau Espin Pedrol5b9c7b92020-05-05 14:35:04 +0200299 iperf3srv_obj = IPerf3Server(self, ip_address)
Pau Espin Pedrolaa1cbdc2020-05-04 20:21:31 +0200300 return iperf3srv_obj
301
302 def msisdn(self):
303 msisdn = self.suite_run.resource_pool().next_msisdn(self)
304 self.log('using MSISDN', msisdn)
305 return msisdn
306
307 def lac(self):
308 lac = self.suite_run.resource_pool().next_lac(self)
309 self.log('using LAC', lac)
310 return lac
311
312 def rac(self):
313 rac = self.suite_run.resource_pool().next_rac(self)
314 self.log('using RAC', rac)
315 return rac
316
317 def cellid(self):
318 cellid = self.suite_run.resource_pool().next_cellid(self)
319 self.log('using CellId', cellid)
320 return cellid
321
322 def bvci(self):
323 bvci = self.suite_run.resource_pool().next_bvci(self)
324 self.log('using BVCI', bvci)
325 return bvci
326
327
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200328def setup(suite_run, _test):
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200329 from .core.event_loop import MainLoop
330 from .obj.sms import Sms as Sms_class
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200331
Pau Espin Pedrol315ba572020-06-04 17:09:16 +0200332 global test, log, dbg, err, wait, wait_no_raise, sleep, poll, prompt, Sms, process, tenv
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200333
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200334 test = _test
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200335 log = test.log
336 dbg = test.dbg
337 err = test.err
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200338 wait = lambda *args, **kwargs: MainLoop.wait(suite_run, *args, **kwargs)
339 wait_no_raise = lambda *args, **kwargs: MainLoop.wait_no_raise(suite_run, *args, **kwargs)
340 sleep = lambda *args, **kwargs: MainLoop.sleep(suite_run, *args, **kwargs)
341 poll = MainLoop.poll
Pau Espin Pedrolee217b02020-05-04 19:06:47 +0200342 Sms = Sms_class
Pau Espin Pedrol878b2c62018-05-18 15:57:06 +0200343 process = process_module
Pau Espin Pedrol40c7bc72020-05-05 13:41:42 +0200344 tenv = TestEnv(suite_run, _test)
345 prompt = tenv.prompt
346 return tenv
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200347
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200348# vim: expandtab tabstop=4 shiftwidth=4