blob: 457bfb345b4f17fdc800e971c1cba4f85f96ed1a [file] [log] [blame]
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +02001# osmo_gsm_tester: test suite
2#
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
20import os
Neels Hofmeyr3531a192017-03-28 14:30:28 +020021import sys
22import time
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +020023import pprint
Holger Hans Peter Freytherd03acdf2018-09-23 18:06:48 +010024from . import config, log, util, resource, test
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +020025from .event_loop import MainLoop
Holger Hans Peter Freyther74243012019-02-27 08:18:38 +000026from . import osmo_nitb, osmo_hlr, osmo_mgcpgw, osmo_mgw, osmo_msc, osmo_bsc, osmo_stp, osmo_ggsn, osmo_sgsn, esme, osmocon, ms_driver, iperf3, process
Pau Espin Pedrol116a2c42020-02-11 17:41:13 +010027from . import run_node
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020028
Neels Hofmeyr1ffc3fe2017-05-07 02:15:21 +020029class Timeout(Exception):
30 pass
31
Neels Hofmeyr3531a192017-03-28 14:30:28 +020032class SuiteDefinition(log.Origin):
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020033 '''A test suite reserves resources for a number of tests.
34 Each test requires a specific number of modems, BTSs etc., which are
35 reserved beforehand by a test suite. This way several test suites can be
36 scheduled dynamically without resource conflicts arising halfway through
37 the tests.'''
38
39 CONF_FILENAME = 'suite.conf'
40
Neels Hofmeyr3531a192017-03-28 14:30:28 +020041 def __init__(self, suite_dir):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020042 self.suite_dir = suite_dir
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020043 super().__init__(log.C_CNF, os.path.basename(self.suite_dir))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020044 self.read_conf()
45
46 def read_conf(self):
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020047 self.dbg('reading %s' % SuiteDefinition.CONF_FILENAME)
48 if not os.path.isdir(self.suite_dir):
49 raise RuntimeError('No such directory: %r' % self.suite_dir)
50 self.conf = config.read(os.path.join(self.suite_dir,
51 SuiteDefinition.CONF_FILENAME),
Pau Espin Pedrol0b302792017-09-10 16:33:10 +020052 resource.CONF_SCHEMA)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020053 self.load_test_basenames()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020054
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020055 def load_test_basenames(self):
56 self.test_basenames = []
57 for basename in sorted(os.listdir(self.suite_dir)):
58 if not basename.endswith('.py'):
59 continue
60 self.test_basenames.append(basename)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020061
Neels Hofmeyr3531a192017-03-28 14:30:28 +020062class SuiteRun(log.Origin):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020063 UNKNOWN = 'UNKNOWN'
64 PASS = 'PASS'
65 FAIL = 'FAIL'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020066
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020067 def __init__(self, trial, suite_scenario_str, suite_definition, scenarios=[]):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020068 super().__init__(log.C_TST, suite_scenario_str)
Pau Espin Pedrol58603672018-08-09 13:45:55 +020069 self.start_timestamp = None
70 self.duration = None
71 self.reserved_resources = None
72 self.objects_to_clean_up = None
73 self.test_import_modules_to_clean_up = []
74 self._resource_requirements = None
Pau Espin Pedrolaab56922018-08-21 14:58:29 +020075 self._resource_modifiers = None
Pau Espin Pedrol58603672018-08-09 13:45:55 +020076 self._config = None
Pau Espin Pedrol806aae82019-04-04 17:44:33 +020077 self._processes = []
Pau Espin Pedrol58603672018-08-09 13:45:55 +020078 self._run_dir = None
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020079 self.trial = trial
Neels Hofmeyr3531a192017-03-28 14:30:28 +020080 self.definition = suite_definition
81 self.scenarios = scenarios
Neels Hofmeyr3531a192017-03-28 14:30:28 +020082 self.resources_pool = resource.ResourcesPool()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020083 self.status = SuiteRun.UNKNOWN
84 self.load_tests()
85
86 def load_tests(self):
87 self.tests = []
88 for test_basename in self.definition.test_basenames:
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010089 self.tests.append(test.Test(self, test_basename))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020090
Neels Hofmeyr4d688c22017-05-29 04:13:58 +020091 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()
Pau Espin Pedrol6100b622017-07-31 18:19:06 +020099 try:
100 obj.cleanup()
101 except Exception:
102 log.log_exn()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200103
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200104 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.log_exn()
124
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200125 def mark_start(self):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200126 self.start_timestamp = time.time()
127 self.duration = 0
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200128 self.status = SuiteRun.UNKNOWN
129
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200130 def combined(self, conf_name, replicate_times=True):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200131 log.dbg(combining=conf_name)
132 log.ctx(combining_scenarios=conf_name)
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200133 combination = self.definition.conf.get(conf_name, {})
134 if replicate_times:
135 combination = config.replicate_times(combination)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200136 log.dbg(definition_conf=combination)
137 for scenario in self.scenarios:
138 log.ctx(combining_scenarios=conf_name, scenario=scenario.name())
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200139 c = scenario.get(conf_name, {})
140 if replicate_times:
141 c = config.replicate_times(c)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200142 log.dbg(scenario=scenario.name(), conf=c)
143 if c is None:
144 continue
145 config.combine(combination, c)
146 return combination
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200147
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200148 def get_run_dir(self):
149 if self._run_dir is None:
150 self._run_dir = util.Dir(self.trial.get_run_dir().new_dir(self.name()))
151 return self._run_dir
152
153 def get_test_run_dir(self):
154 if self.current_test:
155 return self.current_test.get_run_dir()
156 return self.get_run_dir()
157
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200158 def resource_requirements(self):
159 if self._resource_requirements is None:
160 self._resource_requirements = self.combined('resources')
161 return self._resource_requirements
162
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200163 def resource_modifiers(self):
164 if self._resource_modifiers is None:
165 self._resource_modifiers = self.combined('modifiers')
166 return self._resource_modifiers
167
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200168 def config(self):
169 if self._config is None:
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200170 self._config = self.combined('config', False)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200171 return self._config
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200172
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200173 def reserve_resources(self):
174 if self.reserved_resources:
175 raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')
Neels Hofmeyr7e2e8f12017-05-14 03:37:13 +0200176 self.log('reserving resources in', self.resources_pool.state_dir, '...')
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200177 self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements(), self.resource_modifiers())
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200178
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200179 def run_tests(self, names=None):
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200180 suite_libdir = os.path.join(self.definition.suite_dir, 'lib')
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200181 try:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200182 log.large_separator(self.trial.name(), self.name(), sublevel=2)
183 self.mark_start()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200184 util.import_path_prepend(suite_libdir)
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200185 MainLoop.register_poll_func(self.poll)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200186 if not self.reserved_resources:
187 self.reserve_resources()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100188 for t in self.tests:
189 if names and not t.name() in names:
190 t.set_skip()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200191 continue
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100192 self.current_test = t
193 t.run()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200194 self.stop_processes()
195 self.objects_cleanup()
196 self.reserved_resources.put_all()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200197 except Exception:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200198 log.log_exn()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200199 except BaseException as e:
200 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
201 self.err('SUITE RUN ABORTED: %s' % type(e).__name__)
202 raise
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200203 finally:
204 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
205 # base exception is raised. Make sure to stop processes in this
206 # finally section. Resources are automatically freed with 'atexit'.
207 self.stop_processes()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200208 self.objects_cleanup()
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200209 self.free_resources()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200210 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200211 self.test_import_modules_cleanup()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200212 util.import_path_remove(suite_libdir)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200213 self.duration = time.time() - self.start_timestamp
214
215 passed, skipped, failed = self.count_test_results()
216 # if no tests ran, count it as failure
217 if passed and not failed:
218 self.status = SuiteRun.PASS
219 else:
220 self.status = SuiteRun.FAIL
221
222 log.large_separator(self.trial.name(), self.name(), self.status, sublevel=2, space_above=False)
223
224 def passed(self):
225 return self.status == SuiteRun.PASS
226
227 def count_test_results(self):
228 passed = 0
229 skipped = 0
230 failed = 0
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100231 for t in self.tests:
232 if t.status == test.Test.PASS:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200233 passed += 1
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100234 elif t.status == test.Test.FAIL:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200235 failed += 1
236 else:
237 skipped += 1
238 return (passed, skipped, failed)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200239
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200240 def remember_to_stop(self, process, respawn=False):
241 '''Ask suite to monitor and manage lifecycle of the Process object. If a
242 process managed by suite finishes before cleanup time, the current test
243 will be marked as FAIL and end immediatelly. If respwan=True, then suite
244 will respawn() the process instead.'''
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200245 self._processes.insert(0, (process, respawn))
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200246
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200247 def stop_processes(self):
Pau Espin Pedrol04096552019-04-04 16:43:12 +0200248 if len(self._processes) == 0:
249 return
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000250 strategy = process.ParallelTerminationStrategy()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200251 while self._processes:
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000252 proc, _ = self._processes.pop()
253 strategy.add_process(proc)
254 strategy.terminate_all()
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200255
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100256 def stop_process(self, process):
257 'Remove process from monitored list and stop it'
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200258 for proc_respawn in self._processes:
259 proc, respawn = proc_respawn
260 if proc == process:
261 self._processes.remove(proc_respawn)
262 proc.terminate()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100263
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200264 def free_resources(self):
265 if self.reserved_resources is None:
266 return
267 self.reserved_resources.free()
268
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200269 def ip_address(self, specifics=None):
270 return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200271
Neels Hofmeyr76d81032017-05-18 18:35:32 +0200272 def nitb(self, ip_address=None):
273 if ip_address is None:
274 ip_address = self.ip_address()
275 return osmo_nitb.OsmoNitb(self, ip_address)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200276
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200277 def hlr(self, ip_address=None):
278 if ip_address is None:
279 ip_address = self.ip_address()
280 return osmo_hlr.OsmoHlr(self, ip_address)
281
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200282 def ggsn(self, ip_address=None):
283 if ip_address is None:
284 ip_address = self.ip_address()
285 return osmo_ggsn.OsmoGgsn(self, ip_address)
286
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100287 def sgsn(self, hlr, ggsn, ip_address=None):
288 if ip_address is None:
289 ip_address = self.ip_address()
290 return osmo_sgsn.OsmoSgsn(self, hlr, ggsn, ip_address)
291
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200292 def mgcpgw(self, ip_address=None, bts_ip=None):
293 if ip_address is None:
294 ip_address = self.ip_address()
295 return osmo_mgcpgw.OsmoMgcpgw(self, ip_address, bts_ip)
296
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100297 def mgw(self, ip_address=None):
298 if ip_address is None:
299 ip_address = self.ip_address()
300 return osmo_mgw.OsmoMgw(self, ip_address)
301
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100302 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200303 if ip_address is None:
304 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100305 return osmo_msc.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200306
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100307 def bsc(self, msc, mgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200308 if ip_address is None:
309 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100310 return osmo_bsc.OsmoBsc(self, msc, mgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200311
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200312 def stp(self, ip_address=None):
313 if ip_address is None:
314 ip_address = self.ip_address()
315 return osmo_stp.OsmoStp(self, ip_address)
316
Holger Hans Peter Freytherb484aab2018-08-29 04:28:33 +0100317 def ms_driver(self):
318 ms = ms_driver.MsDriver(self)
319 self.register_for_cleanup(ms)
320 return ms
321
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200322 def bts(self, specifics=None):
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200323 bts = bts_obj(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100324 bts.set_lac(self.lac())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100325 bts.set_rac(self.rac())
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100326 bts.set_cellid(self.cellid())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100327 bts.set_bvci(self.bvci())
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200328 self.register_for_cleanup(bts)
329 return bts
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200330
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200331 def modem(self, specifics=None):
332 conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
Holger Hans Peter Freyther74243012019-02-27 08:18:38 +0000333 ms_type = conf.get('type')
334 ms_class = resource.KNOWN_MS_TYPES.get(ms_type)
335 if ms_class is None:
336 raise RuntimeError('No such Modem type is defined: %r' % ms_type)
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200337 self.dbg('create Modem object', conf=conf)
Holger Hans Peter Freyther74243012019-02-27 08:18:38 +0000338 ms = ms_class(self, conf)
Pau Espin Pedrol6cdd2fd2017-11-07 11:57:42 +0100339 self.register_for_cleanup(ms)
340 return ms
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200341
Neels Hofmeyrf2d279c2017-05-06 15:05:02 +0200342 def modems(self, count):
343 l = []
344 for i in range(count):
345 l.append(self.modem())
346 return l
347
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +0000348 def all_resources(self, resource_func):
349 """Returns all yielded resource."""
350 l = []
351 while True:
352 try:
353 l.append(resource_func())
354 except resource.NoResourceExn:
355 return l
356
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200357 def esme(self):
358 esme_obj = esme.Esme(self.msisdn())
Pau Espin Pedrolac9c1bb2017-08-10 10:59:40 +0200359 self.register_for_cleanup(esme_obj)
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200360 return esme_obj
361
Pau Espin Pedrol116a2c42020-02-11 17:41:13 +0100362 def run_node(self, specifics=None):
363 return run_node.RunNode.from_conf(self.reserved_resources.get(resource.R_RUN_NODE, specifics=specifics))
364
Pau Espin Pedrolbc1ed882018-05-17 16:59:58 +0200365 def osmocon(self, specifics=None):
366 conf = self.reserved_resources.get(resource.R_OSMOCON, specifics=specifics)
367 osmocon_obj = osmocon.Osmocon(self, conf=conf)
368 self.register_for_cleanup(osmocon_obj)
369 return osmocon_obj
370
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200371 def iperf3srv(self, ip_address=None):
372 if ip_address is None:
373 ip_address = self.ip_address()
374 iperf3srv_obj = iperf3.IPerf3Server(self, ip_address)
375 return iperf3srv_obj
376
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200377 def msisdn(self):
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200378 msisdn = self.resources_pool.next_msisdn(self)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200379 self.log('using MSISDN', msisdn)
380 return msisdn
381
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100382 def lac(self):
383 lac = self.resources_pool.next_lac(self)
384 self.log('using LAC', lac)
385 return lac
386
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100387 def rac(self):
388 rac = self.resources_pool.next_rac(self)
389 self.log('using RAC', rac)
390 return rac
391
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100392 def cellid(self):
393 cellid = self.resources_pool.next_cellid(self)
394 self.log('using CellId', cellid)
395 return cellid
396
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100397 def bvci(self):
398 bvci = self.resources_pool.next_bvci(self)
399 self.log('using BVCI', bvci)
400 return bvci
401
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200402 def poll(self):
Pau Espin Pedrol806aae82019-04-04 17:44:33 +0200403 for proc, respawn in self._processes:
404 if proc.terminated():
405 if respawn == True:
406 proc.respawn()
407 else:
408 proc.log_stdout_tail()
409 proc.log_stderr_tail()
410 log.ctx(proc)
411 raise log.Error('Process ended prematurely: %s' % proc.name())
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200412
413 def prompt(self, *msgs, **msg_details):
414 'ask for user interaction. Do not use in tests that should run automatically!'
415 if msg_details:
416 msgs = list(msgs)
417 msgs.append('{%s}' %
418 (', '.join(['%s=%r' % (k,v)
419 for k,v in sorted(msg_details.items())])))
420 msg = ' '.join(msgs) or 'Hit Enter to continue'
421 self.log('prompt:', msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200422 sys.__stdout__.write('\n\n--- PROMPT ---\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200423 sys.__stdout__.write(msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200424 sys.__stdout__.write('\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200425 sys.__stdout__.flush()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200426 entered = util.input_polling('> ', MainLoop.poll)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200427 self.log('prompt entered:', repr(entered))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200428 return entered
429
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +0200430 def resource_status_str(self):
431 return '\n'.join(('',
432 'SUITE RUN: %s' % self.origin_id(),
433 'ASKED FOR:', pprint.pformat(self._resource_requirements),
434 'RESERVED COUNT:', pprint.pformat(self.reserved_resources.counts()),
435 'RESOURCES STATE:', repr(self.reserved_resources)))
436
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200437loaded_suite_definitions = {}
438
439def load(suite_name):
440 global loaded_suite_definitions
441
442 suite = loaded_suite_definitions.get(suite_name)
443 if suite is not None:
444 return suite
445
446 suites_dir = config.get_suites_dir()
447 suite_dir = suites_dir.child(suite_name)
448 if not suites_dir.exists(suite_name):
449 raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))
450 if not suites_dir.isdir(suite_name):
451 raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))
452
453 suite_def = SuiteDefinition(suite_dir)
454 loaded_suite_definitions[suite_name] = suite_def
455 return suite_def
456
457def parse_suite_scenario_str(suite_scenario_str):
458 tokens = suite_scenario_str.split(':')
459 if len(tokens) > 2:
460 raise RuntimeError('invalid combination string: %r' % suite_scenario_str)
461
462 suite_name = tokens[0]
463 if len(tokens) <= 1:
464 scenario_names = []
465 else:
466 scenario_names = tokens[1].split('+')
467
468 return suite_name, scenario_names
469
470def load_suite_scenario_str(suite_scenario_str):
471 suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
472 suite = load(suite_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200473 scenarios = [config.get_scenario(scenario_name, resource.CONF_SCHEMA) for scenario_name in scenario_names]
Your Name44af3412017-04-13 03:11:59 +0200474 return (suite_scenario_str, suite, scenarios)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200475
476def bts_obj(suite_run, conf):
477 bts_type = conf.get('type')
Neels Hofmeyrd28d1a72017-06-14 02:59:55 +0200478 log.dbg('create BTS object', type=bts_type)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200479 bts_class = resource.KNOWN_BTS_TYPES.get(bts_type)
480 if bts_class is None:
481 raise RuntimeError('No such BTS type is defined: %r' % bts_type)
482 return bts_class(suite_run, conf)
483
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200484# vim: expandtab tabstop=4 shiftwidth=4