blob: fa86f96cebcc025c41abec6a3500640e7b0a20d4 [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
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +020024from . import config, log, template, util, resource, schema, test
25from .event_loop import MainLoop
Pau Espin Pedrolbc1ed882018-05-17 16:59:58 +020026from . import osmo_nitb, osmo_hlr, osmo_mgcpgw, osmo_mgw, osmo_msc, osmo_bsc, osmo_stp, osmo_ggsn, osmo_sgsn, modem, esme, osmocon
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
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200129 def combined(self, conf_name):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200130 log.dbg(combining=conf_name)
131 log.ctx(combining_scenarios=conf_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200132 combination = config.replicate_times(self.definition.conf.get(conf_name, {}))
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200133 log.dbg(definition_conf=combination)
134 for scenario in self.scenarios:
135 log.ctx(combining_scenarios=conf_name, scenario=scenario.name())
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200136 c = config.replicate_times(scenario.get(conf_name, {}))
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200137 log.dbg(scenario=scenario.name(), conf=c)
138 if c is None:
139 continue
140 config.combine(combination, c)
141 return combination
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200142
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200143 def get_run_dir(self):
144 if self._run_dir is None:
145 self._run_dir = util.Dir(self.trial.get_run_dir().new_dir(self.name()))
146 return self._run_dir
147
148 def get_test_run_dir(self):
149 if self.current_test:
150 return self.current_test.get_run_dir()
151 return self.get_run_dir()
152
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200153 def resource_requirements(self):
154 if self._resource_requirements is None:
155 self._resource_requirements = self.combined('resources')
156 return self._resource_requirements
157
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200158 def resource_modifiers(self):
159 if self._resource_modifiers is None:
160 self._resource_modifiers = self.combined('modifiers')
161 return self._resource_modifiers
162
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200163 def config(self):
164 if self._config is None:
165 self._config = self.combined('config')
166 return self._config
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200167
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200168 def reserve_resources(self):
169 if self.reserved_resources:
170 raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')
Neels Hofmeyr7e2e8f12017-05-14 03:37:13 +0200171 self.log('reserving resources in', self.resources_pool.state_dir, '...')
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200172 self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements(), self.resource_modifiers())
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200173
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200174 def run_tests(self, names=None):
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200175 suite_libdir = os.path.join(self.definition.suite_dir, 'lib')
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200176 try:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200177 log.large_separator(self.trial.name(), self.name(), sublevel=2)
178 self.mark_start()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200179 util.import_path_prepend(suite_libdir)
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200180 MainLoop.register_poll_func(self.poll)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200181 if not self.reserved_resources:
182 self.reserve_resources()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100183 for t in self.tests:
184 if names and not t.name() in names:
185 t.set_skip()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200186 continue
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100187 self.current_test = t
188 t.run()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200189 self.stop_processes()
190 self.objects_cleanup()
191 self.reserved_resources.put_all()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200192 except Exception:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200193 log.log_exn()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200194 except BaseException as e:
195 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
196 self.err('SUITE RUN ABORTED: %s' % type(e).__name__)
197 raise
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200198 finally:
199 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
200 # base exception is raised. Make sure to stop processes in this
201 # finally section. Resources are automatically freed with 'atexit'.
202 self.stop_processes()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200203 self.objects_cleanup()
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200204 self.free_resources()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200205 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200206 self.test_import_modules_cleanup()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200207 util.import_path_remove(suite_libdir)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200208 self.duration = time.time() - self.start_timestamp
209
210 passed, skipped, failed = self.count_test_results()
211 # if no tests ran, count it as failure
212 if passed and not failed:
213 self.status = SuiteRun.PASS
214 else:
215 self.status = SuiteRun.FAIL
216
217 log.large_separator(self.trial.name(), self.name(), self.status, sublevel=2, space_above=False)
218
219 def passed(self):
220 return self.status == SuiteRun.PASS
221
222 def count_test_results(self):
223 passed = 0
224 skipped = 0
225 failed = 0
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100226 for t in self.tests:
227 if t.status == test.Test.PASS:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200228 passed += 1
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100229 elif t.status == test.Test.FAIL:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200230 failed += 1
231 else:
232 skipped += 1
233 return (passed, skipped, failed)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200234
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200235 def remember_to_stop(self, process, respawn=False):
236 '''Ask suite to monitor and manage lifecycle of the Process object. If a
237 process managed by suite finishes before cleanup time, the current test
238 will be marked as FAIL and end immediatelly. If respwan=True, then suite
239 will respawn() the process instead.'''
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200240 if self._processes is None:
241 self._processes = []
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200242 self._processes.insert(0, (process, respawn))
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200243
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200244 def stop_processes(self):
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200245 while self._processes:
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200246 process, respawn = self._processes.pop()
247 process.terminate()
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200248
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100249 def stop_process(self, process):
250 'Remove process from monitored list and stop it'
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200251 for proc_respawn in self._processes:
252 proc, respawn = proc_respawn
253 if proc == process:
254 self._processes.remove(proc_respawn)
255 proc.terminate()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100256
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200257 def free_resources(self):
258 if self.reserved_resources is None:
259 return
260 self.reserved_resources.free()
261
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200262 def ip_address(self, specifics=None):
263 return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200264
Neels Hofmeyr76d81032017-05-18 18:35:32 +0200265 def nitb(self, ip_address=None):
266 if ip_address is None:
267 ip_address = self.ip_address()
268 return osmo_nitb.OsmoNitb(self, ip_address)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200269
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200270 def hlr(self, ip_address=None):
271 if ip_address is None:
272 ip_address = self.ip_address()
273 return osmo_hlr.OsmoHlr(self, ip_address)
274
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200275 def ggsn(self, ip_address=None):
276 if ip_address is None:
277 ip_address = self.ip_address()
278 return osmo_ggsn.OsmoGgsn(self, ip_address)
279
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100280 def sgsn(self, hlr, ggsn, ip_address=None):
281 if ip_address is None:
282 ip_address = self.ip_address()
283 return osmo_sgsn.OsmoSgsn(self, hlr, ggsn, ip_address)
284
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200285 def mgcpgw(self, ip_address=None, bts_ip=None):
286 if ip_address is None:
287 ip_address = self.ip_address()
288 return osmo_mgcpgw.OsmoMgcpgw(self, ip_address, bts_ip)
289
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100290 def mgw(self, ip_address=None):
291 if ip_address is None:
292 ip_address = self.ip_address()
293 return osmo_mgw.OsmoMgw(self, ip_address)
294
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100295 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200296 if ip_address is None:
297 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100298 return osmo_msc.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200299
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100300 def bsc(self, msc, mgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200301 if ip_address is None:
302 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100303 return osmo_bsc.OsmoBsc(self, msc, mgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200304
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200305 def stp(self, ip_address=None):
306 if ip_address is None:
307 ip_address = self.ip_address()
308 return osmo_stp.OsmoStp(self, ip_address)
309
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200310 def bts(self, specifics=None):
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200311 bts = bts_obj(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100312 bts.set_lac(self.lac())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100313 bts.set_rac(self.rac())
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100314 bts.set_cellid(self.cellid())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100315 bts.set_bvci(self.bvci())
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200316 self.register_for_cleanup(bts)
317 return bts
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200318
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200319 def modem(self, specifics=None):
320 conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200321 self.dbg('create Modem object', conf=conf)
Pau Espin Pedrol6cdd2fd2017-11-07 11:57:42 +0100322 ms = modem.Modem(conf)
323 self.register_for_cleanup(ms)
324 return ms
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200325
Neels Hofmeyrf2d279c2017-05-06 15:05:02 +0200326 def modems(self, count):
327 l = []
328 for i in range(count):
329 l.append(self.modem())
330 return l
331
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200332 def esme(self):
333 esme_obj = esme.Esme(self.msisdn())
Pau Espin Pedrolac9c1bb2017-08-10 10:59:40 +0200334 self.register_for_cleanup(esme_obj)
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200335 return esme_obj
336
Pau Espin Pedrolbc1ed882018-05-17 16:59:58 +0200337 def osmocon(self, specifics=None):
338 conf = self.reserved_resources.get(resource.R_OSMOCON, specifics=specifics)
339 osmocon_obj = osmocon.Osmocon(self, conf=conf)
340 self.register_for_cleanup(osmocon_obj)
341 return osmocon_obj
342
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200343 def msisdn(self):
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200344 msisdn = self.resources_pool.next_msisdn(self)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200345 self.log('using MSISDN', msisdn)
346 return msisdn
347
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100348 def lac(self):
349 lac = self.resources_pool.next_lac(self)
350 self.log('using LAC', lac)
351 return lac
352
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100353 def rac(self):
354 rac = self.resources_pool.next_rac(self)
355 self.log('using RAC', rac)
356 return rac
357
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100358 def cellid(self):
359 cellid = self.resources_pool.next_cellid(self)
360 self.log('using CellId', cellid)
361 return cellid
362
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100363 def bvci(self):
364 bvci = self.resources_pool.next_bvci(self)
365 self.log('using BVCI', bvci)
366 return bvci
367
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200368 def poll(self):
369 if self._processes:
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200370 for process, respawn in self._processes:
Neels Hofmeyr5356d0a2017-04-10 03:45:30 +0200371 if process.terminated():
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200372 if respawn == True:
373 process.respawn()
374 else:
375 process.log_stdout_tail()
376 process.log_stderr_tail()
377 log.ctx(process)
378 raise log.Error('Process ended prematurely: %s' % process.name())
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200379
380 def prompt(self, *msgs, **msg_details):
381 'ask for user interaction. Do not use in tests that should run automatically!'
382 if msg_details:
383 msgs = list(msgs)
384 msgs.append('{%s}' %
385 (', '.join(['%s=%r' % (k,v)
386 for k,v in sorted(msg_details.items())])))
387 msg = ' '.join(msgs) or 'Hit Enter to continue'
388 self.log('prompt:', msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200389 sys.__stdout__.write('\n\n--- PROMPT ---\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200390 sys.__stdout__.write(msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200391 sys.__stdout__.write('\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200392 sys.__stdout__.flush()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200393 entered = util.input_polling('> ', MainLoop.poll)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200394 self.log('prompt entered:', repr(entered))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200395 return entered
396
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +0200397 def resource_status_str(self):
398 return '\n'.join(('',
399 'SUITE RUN: %s' % self.origin_id(),
400 'ASKED FOR:', pprint.pformat(self._resource_requirements),
401 'RESERVED COUNT:', pprint.pformat(self.reserved_resources.counts()),
402 'RESOURCES STATE:', repr(self.reserved_resources)))
403
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200404loaded_suite_definitions = {}
405
406def load(suite_name):
407 global loaded_suite_definitions
408
409 suite = loaded_suite_definitions.get(suite_name)
410 if suite is not None:
411 return suite
412
413 suites_dir = config.get_suites_dir()
414 suite_dir = suites_dir.child(suite_name)
415 if not suites_dir.exists(suite_name):
416 raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))
417 if not suites_dir.isdir(suite_name):
418 raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))
419
420 suite_def = SuiteDefinition(suite_dir)
421 loaded_suite_definitions[suite_name] = suite_def
422 return suite_def
423
424def parse_suite_scenario_str(suite_scenario_str):
425 tokens = suite_scenario_str.split(':')
426 if len(tokens) > 2:
427 raise RuntimeError('invalid combination string: %r' % suite_scenario_str)
428
429 suite_name = tokens[0]
430 if len(tokens) <= 1:
431 scenario_names = []
432 else:
433 scenario_names = tokens[1].split('+')
434
435 return suite_name, scenario_names
436
437def load_suite_scenario_str(suite_scenario_str):
438 suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
439 suite = load(suite_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200440 scenarios = [config.get_scenario(scenario_name, resource.CONF_SCHEMA) for scenario_name in scenario_names]
Your Name44af3412017-04-13 03:11:59 +0200441 return (suite_scenario_str, suite, scenarios)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200442
443def bts_obj(suite_run, conf):
444 bts_type = conf.get('type')
Neels Hofmeyrd28d1a72017-06-14 02:59:55 +0200445 log.dbg('create BTS object', type=bts_type)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200446 bts_class = resource.KNOWN_BTS_TYPES.get(bts_type)
447 if bts_class is None:
448 raise RuntimeError('No such BTS type is defined: %r' % bts_type)
449 return bts_class(suite_run, conf)
450
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200451# vim: expandtab tabstop=4 shiftwidth=4