blob: aefdc24a24a5698363fc4d09dd0b8aefbc0a8a57 [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
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020027
Neels Hofmeyr1ffc3fe2017-05-07 02:15:21 +020028class Timeout(Exception):
29 pass
30
Neels Hofmeyr3531a192017-03-28 14:30:28 +020031class SuiteDefinition(log.Origin):
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020032 '''A test suite reserves resources for a number of tests.
33 Each test requires a specific number of modems, BTSs etc., which are
34 reserved beforehand by a test suite. This way several test suites can be
35 scheduled dynamically without resource conflicts arising halfway through
36 the tests.'''
37
38 CONF_FILENAME = 'suite.conf'
39
Neels Hofmeyr3531a192017-03-28 14:30:28 +020040 def __init__(self, suite_dir):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020041 self.suite_dir = suite_dir
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020042 super().__init__(log.C_CNF, os.path.basename(self.suite_dir))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020043 self.read_conf()
44
45 def read_conf(self):
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020046 self.dbg('reading %s' % SuiteDefinition.CONF_FILENAME)
47 if not os.path.isdir(self.suite_dir):
48 raise RuntimeError('No such directory: %r' % self.suite_dir)
49 self.conf = config.read(os.path.join(self.suite_dir,
50 SuiteDefinition.CONF_FILENAME),
Pau Espin Pedrol0b302792017-09-10 16:33:10 +020051 resource.CONF_SCHEMA)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020052 self.load_test_basenames()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020053
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020054 def load_test_basenames(self):
55 self.test_basenames = []
56 for basename in sorted(os.listdir(self.suite_dir)):
57 if not basename.endswith('.py'):
58 continue
59 self.test_basenames.append(basename)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020060
Neels Hofmeyr3531a192017-03-28 14:30:28 +020061class SuiteRun(log.Origin):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020062 UNKNOWN = 'UNKNOWN'
63 PASS = 'PASS'
64 FAIL = 'FAIL'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020065
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020066 def __init__(self, trial, suite_scenario_str, suite_definition, scenarios=[]):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020067 super().__init__(log.C_TST, suite_scenario_str)
Pau Espin Pedrol58603672018-08-09 13:45:55 +020068 self.start_timestamp = None
69 self.duration = None
70 self.reserved_resources = None
71 self.objects_to_clean_up = None
72 self.test_import_modules_to_clean_up = []
73 self._resource_requirements = None
Pau Espin Pedrolaab56922018-08-21 14:58:29 +020074 self._resource_modifiers = None
Pau Espin Pedrol58603672018-08-09 13:45:55 +020075 self._config = None
76 self._processes = None
77 self._run_dir = None
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020078 self.trial = trial
Neels Hofmeyr3531a192017-03-28 14:30:28 +020079 self.definition = suite_definition
80 self.scenarios = scenarios
Neels Hofmeyr3531a192017-03-28 14:30:28 +020081 self.resources_pool = resource.ResourcesPool()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020082 self.status = SuiteRun.UNKNOWN
83 self.load_tests()
84
85 def load_tests(self):
86 self.tests = []
87 for test_basename in self.definition.test_basenames:
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010088 self.tests.append(test.Test(self, test_basename))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020089
Neels Hofmeyr4d688c22017-05-29 04:13:58 +020090 def register_for_cleanup(self, *obj):
91 assert all([hasattr(o, 'cleanup') for o in obj])
92 self.objects_to_clean_up = self.objects_to_clean_up or []
93 self.objects_to_clean_up.extend(obj)
94
95 def objects_cleanup(self):
96 while self.objects_to_clean_up:
97 obj = self.objects_to_clean_up.pop()
Pau Espin Pedrol6100b622017-07-31 18:19:06 +020098 try:
99 obj.cleanup()
100 except Exception:
101 log.log_exn()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200102
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200103 def test_import_modules_register_for_cleanup(self, mod):
104 '''
105 Tests are required to call this API for any module loaded from its own
106 lib subdir, because they are loaded in the global namespace. Otherwise
107 later tests importing modules with the same name will re-use an already
108 loaded module.
109 '''
110 if mod not in self.test_import_modules_to_clean_up:
111 self.dbg('registering module %r for cleanup' % mod)
112 self.test_import_modules_to_clean_up.append(mod)
113
114 def test_import_modules_cleanup(self):
115 while self.test_import_modules_to_clean_up:
116 mod = self.test_import_modules_to_clean_up.pop()
117 try:
118 self.dbg('Cleaning up module %r' % mod)
119 del sys.modules[mod.__name__]
120 del mod
121 except Exception:
122 log.log_exn()
123
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200124 def mark_start(self):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200125 self.start_timestamp = time.time()
126 self.duration = 0
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200127 self.status = SuiteRun.UNKNOWN
128
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200129 def combined(self, conf_name, replicate_times=True):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200130 log.dbg(combining=conf_name)
131 log.ctx(combining_scenarios=conf_name)
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200132 combination = self.definition.conf.get(conf_name, {})
133 if replicate_times:
134 combination = config.replicate_times(combination)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200135 log.dbg(definition_conf=combination)
136 for scenario in self.scenarios:
137 log.ctx(combining_scenarios=conf_name, scenario=scenario.name())
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200138 c = scenario.get(conf_name, {})
139 if replicate_times:
140 c = config.replicate_times(c)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200141 log.dbg(scenario=scenario.name(), conf=c)
142 if c is None:
143 continue
144 config.combine(combination, c)
145 return combination
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200146
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200147 def get_run_dir(self):
148 if self._run_dir is None:
149 self._run_dir = util.Dir(self.trial.get_run_dir().new_dir(self.name()))
150 return self._run_dir
151
152 def get_test_run_dir(self):
153 if self.current_test:
154 return self.current_test.get_run_dir()
155 return self.get_run_dir()
156
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200157 def resource_requirements(self):
158 if self._resource_requirements is None:
159 self._resource_requirements = self.combined('resources')
160 return self._resource_requirements
161
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200162 def resource_modifiers(self):
163 if self._resource_modifiers is None:
164 self._resource_modifiers = self.combined('modifiers')
165 return self._resource_modifiers
166
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200167 def config(self):
168 if self._config is None:
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200169 self._config = self.combined('config', False)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200170 return self._config
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200171
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200172 def reserve_resources(self):
173 if self.reserved_resources:
174 raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')
Neels Hofmeyr7e2e8f12017-05-14 03:37:13 +0200175 self.log('reserving resources in', self.resources_pool.state_dir, '...')
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200176 self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements(), self.resource_modifiers())
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200177
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200178 def run_tests(self, names=None):
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200179 suite_libdir = os.path.join(self.definition.suite_dir, 'lib')
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200180 try:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200181 log.large_separator(self.trial.name(), self.name(), sublevel=2)
182 self.mark_start()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200183 util.import_path_prepend(suite_libdir)
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200184 MainLoop.register_poll_func(self.poll)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200185 if not self.reserved_resources:
186 self.reserve_resources()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100187 for t in self.tests:
188 if names and not t.name() in names:
189 t.set_skip()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200190 continue
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100191 self.current_test = t
192 t.run()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200193 self.stop_processes()
194 self.objects_cleanup()
195 self.reserved_resources.put_all()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200196 except Exception:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200197 log.log_exn()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200198 except BaseException as e:
199 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
200 self.err('SUITE RUN ABORTED: %s' % type(e).__name__)
201 raise
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200202 finally:
203 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
204 # base exception is raised. Make sure to stop processes in this
205 # finally section. Resources are automatically freed with 'atexit'.
206 self.stop_processes()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200207 self.objects_cleanup()
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200208 self.free_resources()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200209 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200210 self.test_import_modules_cleanup()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200211 util.import_path_remove(suite_libdir)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200212 self.duration = time.time() - self.start_timestamp
213
214 passed, skipped, failed = self.count_test_results()
215 # if no tests ran, count it as failure
216 if passed and not failed:
217 self.status = SuiteRun.PASS
218 else:
219 self.status = SuiteRun.FAIL
220
221 log.large_separator(self.trial.name(), self.name(), self.status, sublevel=2, space_above=False)
222
223 def passed(self):
224 return self.status == SuiteRun.PASS
225
226 def count_test_results(self):
227 passed = 0
228 skipped = 0
229 failed = 0
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100230 for t in self.tests:
231 if t.status == test.Test.PASS:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200232 passed += 1
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100233 elif t.status == test.Test.FAIL:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200234 failed += 1
235 else:
236 skipped += 1
237 return (passed, skipped, failed)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200238
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200239 def remember_to_stop(self, process, respawn=False):
240 '''Ask suite to monitor and manage lifecycle of the Process object. If a
241 process managed by suite finishes before cleanup time, the current test
242 will be marked as FAIL and end immediatelly. If respwan=True, then suite
243 will respawn() the process instead.'''
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200244 if self._processes is None:
245 self._processes = []
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200246 self._processes.insert(0, (process, respawn))
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200247
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200248 def stop_processes(self):
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000249 strategy = process.ParallelTerminationStrategy()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200250 while self._processes:
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000251 proc, _ = self._processes.pop()
252 strategy.add_process(proc)
253 strategy.terminate_all()
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200254
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100255 def stop_process(self, process):
256 'Remove process from monitored list and stop it'
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200257 for proc_respawn in self._processes:
258 proc, respawn = proc_respawn
259 if proc == process:
260 self._processes.remove(proc_respawn)
261 proc.terminate()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100262
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200263 def free_resources(self):
264 if self.reserved_resources is None:
265 return
266 self.reserved_resources.free()
267
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200268 def ip_address(self, specifics=None):
269 return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200270
Neels Hofmeyr76d81032017-05-18 18:35:32 +0200271 def nitb(self, ip_address=None):
272 if ip_address is None:
273 ip_address = self.ip_address()
274 return osmo_nitb.OsmoNitb(self, ip_address)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200275
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200276 def hlr(self, ip_address=None):
277 if ip_address is None:
278 ip_address = self.ip_address()
279 return osmo_hlr.OsmoHlr(self, ip_address)
280
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200281 def ggsn(self, ip_address=None):
282 if ip_address is None:
283 ip_address = self.ip_address()
284 return osmo_ggsn.OsmoGgsn(self, ip_address)
285
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100286 def sgsn(self, hlr, ggsn, ip_address=None):
287 if ip_address is None:
288 ip_address = self.ip_address()
289 return osmo_sgsn.OsmoSgsn(self, hlr, ggsn, ip_address)
290
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200291 def mgcpgw(self, ip_address=None, bts_ip=None):
292 if ip_address is None:
293 ip_address = self.ip_address()
294 return osmo_mgcpgw.OsmoMgcpgw(self, ip_address, bts_ip)
295
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100296 def mgw(self, ip_address=None):
297 if ip_address is None:
298 ip_address = self.ip_address()
299 return osmo_mgw.OsmoMgw(self, ip_address)
300
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100301 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200302 if ip_address is None:
303 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100304 return osmo_msc.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200305
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100306 def bsc(self, msc, mgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200307 if ip_address is None:
308 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100309 return osmo_bsc.OsmoBsc(self, msc, mgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200310
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200311 def stp(self, ip_address=None):
312 if ip_address is None:
313 ip_address = self.ip_address()
314 return osmo_stp.OsmoStp(self, ip_address)
315
Holger Hans Peter Freytherb484aab2018-08-29 04:28:33 +0100316 def ms_driver(self):
317 ms = ms_driver.MsDriver(self)
318 self.register_for_cleanup(ms)
319 return ms
320
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200321 def bts(self, specifics=None):
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200322 bts = bts_obj(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100323 bts.set_lac(self.lac())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100324 bts.set_rac(self.rac())
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100325 bts.set_cellid(self.cellid())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100326 bts.set_bvci(self.bvci())
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200327 self.register_for_cleanup(bts)
328 return bts
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200329
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200330 def modem(self, specifics=None):
331 conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
Holger Hans Peter Freyther74243012019-02-27 08:18:38 +0000332 ms_type = conf.get('type')
333 ms_class = resource.KNOWN_MS_TYPES.get(ms_type)
334 if ms_class is None:
335 raise RuntimeError('No such Modem type is defined: %r' % ms_type)
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200336 self.dbg('create Modem object', conf=conf)
Holger Hans Peter Freyther74243012019-02-27 08:18:38 +0000337 ms = ms_class(self, conf)
Pau Espin Pedrol6cdd2fd2017-11-07 11:57:42 +0100338 self.register_for_cleanup(ms)
339 return ms
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200340
Neels Hofmeyrf2d279c2017-05-06 15:05:02 +0200341 def modems(self, count):
342 l = []
343 for i in range(count):
344 l.append(self.modem())
345 return l
346
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +0000347 def all_resources(self, resource_func):
348 """Returns all yielded resource."""
349 l = []
350 while True:
351 try:
352 l.append(resource_func())
353 except resource.NoResourceExn:
354 return l
355
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200356 def esme(self):
357 esme_obj = esme.Esme(self.msisdn())
Pau Espin Pedrolac9c1bb2017-08-10 10:59:40 +0200358 self.register_for_cleanup(esme_obj)
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200359 return esme_obj
360
Pau Espin Pedrolbc1ed882018-05-17 16:59:58 +0200361 def osmocon(self, specifics=None):
362 conf = self.reserved_resources.get(resource.R_OSMOCON, specifics=specifics)
363 osmocon_obj = osmocon.Osmocon(self, conf=conf)
364 self.register_for_cleanup(osmocon_obj)
365 return osmocon_obj
366
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200367 def iperf3srv(self, ip_address=None):
368 if ip_address is None:
369 ip_address = self.ip_address()
370 iperf3srv_obj = iperf3.IPerf3Server(self, ip_address)
371 return iperf3srv_obj
372
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200373 def msisdn(self):
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200374 msisdn = self.resources_pool.next_msisdn(self)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200375 self.log('using MSISDN', msisdn)
376 return msisdn
377
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100378 def lac(self):
379 lac = self.resources_pool.next_lac(self)
380 self.log('using LAC', lac)
381 return lac
382
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100383 def rac(self):
384 rac = self.resources_pool.next_rac(self)
385 self.log('using RAC', rac)
386 return rac
387
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100388 def cellid(self):
389 cellid = self.resources_pool.next_cellid(self)
390 self.log('using CellId', cellid)
391 return cellid
392
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100393 def bvci(self):
394 bvci = self.resources_pool.next_bvci(self)
395 self.log('using BVCI', bvci)
396 return bvci
397
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200398 def poll(self):
399 if self._processes:
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000400 for proc, respawn in self._processes:
401 if proc.terminated():
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200402 if respawn == True:
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000403 proc.respawn()
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200404 else:
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000405 proc.log_stdout_tail()
406 proc.log_stderr_tail()
407 log.ctx(proc)
408 raise log.Error('Process ended prematurely: %s' % proc.name())
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200409
410 def prompt(self, *msgs, **msg_details):
411 'ask for user interaction. Do not use in tests that should run automatically!'
412 if msg_details:
413 msgs = list(msgs)
414 msgs.append('{%s}' %
415 (', '.join(['%s=%r' % (k,v)
416 for k,v in sorted(msg_details.items())])))
417 msg = ' '.join(msgs) or 'Hit Enter to continue'
418 self.log('prompt:', msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200419 sys.__stdout__.write('\n\n--- PROMPT ---\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200420 sys.__stdout__.write(msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200421 sys.__stdout__.write('\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200422 sys.__stdout__.flush()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200423 entered = util.input_polling('> ', MainLoop.poll)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200424 self.log('prompt entered:', repr(entered))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200425 return entered
426
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +0200427 def resource_status_str(self):
428 return '\n'.join(('',
429 'SUITE RUN: %s' % self.origin_id(),
430 'ASKED FOR:', pprint.pformat(self._resource_requirements),
431 'RESERVED COUNT:', pprint.pformat(self.reserved_resources.counts()),
432 'RESOURCES STATE:', repr(self.reserved_resources)))
433
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200434loaded_suite_definitions = {}
435
436def load(suite_name):
437 global loaded_suite_definitions
438
439 suite = loaded_suite_definitions.get(suite_name)
440 if suite is not None:
441 return suite
442
443 suites_dir = config.get_suites_dir()
444 suite_dir = suites_dir.child(suite_name)
445 if not suites_dir.exists(suite_name):
446 raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))
447 if not suites_dir.isdir(suite_name):
448 raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))
449
450 suite_def = SuiteDefinition(suite_dir)
451 loaded_suite_definitions[suite_name] = suite_def
452 return suite_def
453
454def parse_suite_scenario_str(suite_scenario_str):
455 tokens = suite_scenario_str.split(':')
456 if len(tokens) > 2:
457 raise RuntimeError('invalid combination string: %r' % suite_scenario_str)
458
459 suite_name = tokens[0]
460 if len(tokens) <= 1:
461 scenario_names = []
462 else:
463 scenario_names = tokens[1].split('+')
464
465 return suite_name, scenario_names
466
467def load_suite_scenario_str(suite_scenario_str):
468 suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
469 suite = load(suite_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200470 scenarios = [config.get_scenario(scenario_name, resource.CONF_SCHEMA) for scenario_name in scenario_names]
Your Name44af3412017-04-13 03:11:59 +0200471 return (suite_scenario_str, suite, scenarios)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200472
473def bts_obj(suite_run, conf):
474 bts_type = conf.get('type')
Neels Hofmeyrd28d1a72017-06-14 02:59:55 +0200475 log.dbg('create BTS object', type=bts_type)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200476 bts_class = resource.KNOWN_BTS_TYPES.get(bts_type)
477 if bts_class is None:
478 raise RuntimeError('No such BTS type is defined: %r' % bts_type)
479 return bts_class(suite_run, conf)
480
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200481# vim: expandtab tabstop=4 shiftwidth=4