blob: 618a39ba21c53cc608e6e8afd05e49d4a28df8cd [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 Pedrol4796b352017-11-23 11:07:42 +010026from . import osmo_nitb, osmo_hlr, osmo_mgcpgw, osmo_mgw, osmo_msc, osmo_bsc, osmo_stp, osmo_ggsn, osmo_sgsn, modem, esme
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
66 trial = None
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020067 status = None
Neels Hofmeyrf8e61862017-06-06 23:08:07 +020068 start_timestamp = None
69 duration = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020070 resources_pool = None
71 reserved_resources = None
Neels Hofmeyr4d688c22017-05-29 04:13:58 +020072 objects_to_clean_up = None
Pau Espin Pedrol155a3552018-05-09 11:24:23 +020073 test_import_modules_to_clean_up = []
Neels Hofmeyr3531a192017-03-28 14:30:28 +020074 _resource_requirements = None
75 _config = None
76 _processes = None
Pau Espin Pedrold0912332017-06-14 13:27:08 +020077 _run_dir = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020078
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020079 def __init__(self, trial, suite_scenario_str, suite_definition, scenarios=[]):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020080 super().__init__(log.C_TST, suite_scenario_str)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020081 self.trial = trial
Neels Hofmeyr3531a192017-03-28 14:30:28 +020082 self.definition = suite_definition
83 self.scenarios = scenarios
Neels Hofmeyr3531a192017-03-28 14:30:28 +020084 self.resources_pool = resource.ResourcesPool()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020085 self.status = SuiteRun.UNKNOWN
86 self.load_tests()
87
88 def load_tests(self):
89 self.tests = []
90 for test_basename in self.definition.test_basenames:
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010091 self.tests.append(test.Test(self, test_basename))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020092
Neels Hofmeyr4d688c22017-05-29 04:13:58 +020093 def register_for_cleanup(self, *obj):
94 assert all([hasattr(o, 'cleanup') for o in obj])
95 self.objects_to_clean_up = self.objects_to_clean_up or []
96 self.objects_to_clean_up.extend(obj)
97
98 def objects_cleanup(self):
99 while self.objects_to_clean_up:
100 obj = self.objects_to_clean_up.pop()
Pau Espin Pedrol6100b622017-07-31 18:19:06 +0200101 try:
102 obj.cleanup()
103 except Exception:
104 log.log_exn()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200105
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200106 def test_import_modules_register_for_cleanup(self, mod):
107 '''
108 Tests are required to call this API for any module loaded from its own
109 lib subdir, because they are loaded in the global namespace. Otherwise
110 later tests importing modules with the same name will re-use an already
111 loaded module.
112 '''
113 if mod not in self.test_import_modules_to_clean_up:
114 self.dbg('registering module %r for cleanup' % mod)
115 self.test_import_modules_to_clean_up.append(mod)
116
117 def test_import_modules_cleanup(self):
118 while self.test_import_modules_to_clean_up:
119 mod = self.test_import_modules_to_clean_up.pop()
120 try:
121 self.dbg('Cleaning up module %r' % mod)
122 del sys.modules[mod.__name__]
123 del mod
124 except Exception:
125 log.log_exn()
126
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200127 def mark_start(self):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200128 self.start_timestamp = time.time()
129 self.duration = 0
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200130 self.status = SuiteRun.UNKNOWN
131
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200132 def combined(self, conf_name):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200133 log.dbg(combining=conf_name)
134 log.ctx(combining_scenarios=conf_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200135 combination = config.replicate_times(self.definition.conf.get(conf_name, {}))
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 Pedrol0b302792017-09-10 16:33:10 +0200139 c = config.replicate_times(scenario.get(conf_name, {}))
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200140 log.dbg(scenario=scenario.name(), conf=c)
141 if c is None:
142 continue
143 config.combine(combination, c)
144 return combination
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200145
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200146 def get_run_dir(self):
147 if self._run_dir is None:
148 self._run_dir = util.Dir(self.trial.get_run_dir().new_dir(self.name()))
149 return self._run_dir
150
151 def get_test_run_dir(self):
152 if self.current_test:
153 return self.current_test.get_run_dir()
154 return self.get_run_dir()
155
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200156 def resource_requirements(self):
157 if self._resource_requirements is None:
158 self._resource_requirements = self.combined('resources')
159 return self._resource_requirements
160
161 def config(self):
162 if self._config is None:
163 self._config = self.combined('config')
164 return self._config
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200165
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200166 def reserve_resources(self):
167 if self.reserved_resources:
168 raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')
Neels Hofmeyr7e2e8f12017-05-14 03:37:13 +0200169 self.log('reserving resources in', self.resources_pool.state_dir, '...')
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200170 self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements())
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200171
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200172 def run_tests(self, names=None):
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200173 suite_libdir = os.path.join(self.definition.suite_dir, 'lib')
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200174 try:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200175 log.large_separator(self.trial.name(), self.name(), sublevel=2)
176 self.mark_start()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200177 util.import_path_prepend(suite_libdir)
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200178 MainLoop.register_poll_func(self.poll)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200179 if not self.reserved_resources:
180 self.reserve_resources()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100181 for t in self.tests:
182 if names and not t.name() in names:
183 t.set_skip()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200184 continue
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100185 self.current_test = t
186 t.run()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200187 self.stop_processes()
188 self.objects_cleanup()
189 self.reserved_resources.put_all()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200190 except Exception:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200191 log.log_exn()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200192 except BaseException as e:
193 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
194 self.err('SUITE RUN ABORTED: %s' % type(e).__name__)
195 raise
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200196 finally:
197 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
198 # base exception is raised. Make sure to stop processes in this
199 # finally section. Resources are automatically freed with 'atexit'.
200 self.stop_processes()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200201 self.objects_cleanup()
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200202 self.free_resources()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200203 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200204 self.test_import_modules_cleanup()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200205 util.import_path_remove(suite_libdir)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200206 self.duration = time.time() - self.start_timestamp
207
208 passed, skipped, failed = self.count_test_results()
209 # if no tests ran, count it as failure
210 if passed and not failed:
211 self.status = SuiteRun.PASS
212 else:
213 self.status = SuiteRun.FAIL
214
215 log.large_separator(self.trial.name(), self.name(), self.status, sublevel=2, space_above=False)
216
217 def passed(self):
218 return self.status == SuiteRun.PASS
219
220 def count_test_results(self):
221 passed = 0
222 skipped = 0
223 failed = 0
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100224 for t in self.tests:
225 if t.status == test.Test.PASS:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200226 passed += 1
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100227 elif t.status == test.Test.FAIL:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200228 failed += 1
229 else:
230 skipped += 1
231 return (passed, skipped, failed)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200232
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200233 def remember_to_stop(self, process, respawn=False):
234 '''Ask suite to monitor and manage lifecycle of the Process object. If a
235 process managed by suite finishes before cleanup time, the current test
236 will be marked as FAIL and end immediatelly. If respwan=True, then suite
237 will respawn() the process instead.'''
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200238 if self._processes is None:
239 self._processes = []
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200240 self._processes.insert(0, (process, respawn))
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200241
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200242 def stop_processes(self):
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200243 while self._processes:
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200244 process, respawn = self._processes.pop()
245 process.terminate()
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200246
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100247 def stop_process(self, process):
248 'Remove process from monitored list and stop it'
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200249 for proc_respawn in self._processes:
250 proc, respawn = proc_respawn
251 if proc == process:
252 self._processes.remove(proc_respawn)
253 proc.terminate()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100254
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200255 def free_resources(self):
256 if self.reserved_resources is None:
257 return
258 self.reserved_resources.free()
259
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200260 def ip_address(self, specifics=None):
261 return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200262
Neels Hofmeyr76d81032017-05-18 18:35:32 +0200263 def nitb(self, ip_address=None):
264 if ip_address is None:
265 ip_address = self.ip_address()
266 return osmo_nitb.OsmoNitb(self, ip_address)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200267
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200268 def hlr(self, ip_address=None):
269 if ip_address is None:
270 ip_address = self.ip_address()
271 return osmo_hlr.OsmoHlr(self, ip_address)
272
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200273 def ggsn(self, ip_address=None):
274 if ip_address is None:
275 ip_address = self.ip_address()
276 return osmo_ggsn.OsmoGgsn(self, ip_address)
277
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100278 def sgsn(self, hlr, ggsn, ip_address=None):
279 if ip_address is None:
280 ip_address = self.ip_address()
281 return osmo_sgsn.OsmoSgsn(self, hlr, ggsn, ip_address)
282
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200283 def mgcpgw(self, ip_address=None, bts_ip=None):
284 if ip_address is None:
285 ip_address = self.ip_address()
286 return osmo_mgcpgw.OsmoMgcpgw(self, ip_address, bts_ip)
287
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100288 def mgw(self, ip_address=None):
289 if ip_address is None:
290 ip_address = self.ip_address()
291 return osmo_mgw.OsmoMgw(self, ip_address)
292
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100293 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200294 if ip_address is None:
295 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100296 return osmo_msc.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200297
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100298 def bsc(self, msc, mgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200299 if ip_address is None:
300 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100301 return osmo_bsc.OsmoBsc(self, msc, mgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200302
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200303 def stp(self, ip_address=None):
304 if ip_address is None:
305 ip_address = self.ip_address()
306 return osmo_stp.OsmoStp(self, ip_address)
307
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200308 def bts(self, specifics=None):
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200309 bts = bts_obj(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100310 bts.set_lac(self.lac())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100311 bts.set_rac(self.rac())
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100312 bts.set_cellid(self.cellid())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100313 bts.set_bvci(self.bvci())
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200314 self.register_for_cleanup(bts)
315 return bts
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200316
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200317 def modem(self, specifics=None):
318 conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200319 self.dbg('create Modem object', conf=conf)
Pau Espin Pedrol6cdd2fd2017-11-07 11:57:42 +0100320 ms = modem.Modem(conf)
321 self.register_for_cleanup(ms)
322 return ms
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200323
Neels Hofmeyrf2d279c2017-05-06 15:05:02 +0200324 def modems(self, count):
325 l = []
326 for i in range(count):
327 l.append(self.modem())
328 return l
329
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200330 def esme(self):
331 esme_obj = esme.Esme(self.msisdn())
Pau Espin Pedrolac9c1bb2017-08-10 10:59:40 +0200332 self.register_for_cleanup(esme_obj)
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200333 return esme_obj
334
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200335 def msisdn(self):
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200336 msisdn = self.resources_pool.next_msisdn(self)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200337 self.log('using MSISDN', msisdn)
338 return msisdn
339
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100340 def lac(self):
341 lac = self.resources_pool.next_lac(self)
342 self.log('using LAC', lac)
343 return lac
344
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100345 def rac(self):
346 rac = self.resources_pool.next_rac(self)
347 self.log('using RAC', rac)
348 return rac
349
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100350 def cellid(self):
351 cellid = self.resources_pool.next_cellid(self)
352 self.log('using CellId', cellid)
353 return cellid
354
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100355 def bvci(self):
356 bvci = self.resources_pool.next_bvci(self)
357 self.log('using BVCI', bvci)
358 return bvci
359
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200360 def poll(self):
361 if self._processes:
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200362 for process, respawn in self._processes:
Neels Hofmeyr5356d0a2017-04-10 03:45:30 +0200363 if process.terminated():
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200364 if respawn == True:
365 process.respawn()
366 else:
367 process.log_stdout_tail()
368 process.log_stderr_tail()
369 log.ctx(process)
370 raise log.Error('Process ended prematurely: %s' % process.name())
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200371
372 def prompt(self, *msgs, **msg_details):
373 'ask for user interaction. Do not use in tests that should run automatically!'
374 if msg_details:
375 msgs = list(msgs)
376 msgs.append('{%s}' %
377 (', '.join(['%s=%r' % (k,v)
378 for k,v in sorted(msg_details.items())])))
379 msg = ' '.join(msgs) or 'Hit Enter to continue'
380 self.log('prompt:', msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200381 sys.__stdout__.write('\n\n--- PROMPT ---\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200382 sys.__stdout__.write(msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200383 sys.__stdout__.write('\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200384 sys.__stdout__.flush()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200385 entered = util.input_polling('> ', MainLoop.poll)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200386 self.log('prompt entered:', repr(entered))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200387 return entered
388
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +0200389 def resource_status_str(self):
390 return '\n'.join(('',
391 'SUITE RUN: %s' % self.origin_id(),
392 'ASKED FOR:', pprint.pformat(self._resource_requirements),
393 'RESERVED COUNT:', pprint.pformat(self.reserved_resources.counts()),
394 'RESOURCES STATE:', repr(self.reserved_resources)))
395
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200396loaded_suite_definitions = {}
397
398def load(suite_name):
399 global loaded_suite_definitions
400
401 suite = loaded_suite_definitions.get(suite_name)
402 if suite is not None:
403 return suite
404
405 suites_dir = config.get_suites_dir()
406 suite_dir = suites_dir.child(suite_name)
407 if not suites_dir.exists(suite_name):
408 raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))
409 if not suites_dir.isdir(suite_name):
410 raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))
411
412 suite_def = SuiteDefinition(suite_dir)
413 loaded_suite_definitions[suite_name] = suite_def
414 return suite_def
415
416def parse_suite_scenario_str(suite_scenario_str):
417 tokens = suite_scenario_str.split(':')
418 if len(tokens) > 2:
419 raise RuntimeError('invalid combination string: %r' % suite_scenario_str)
420
421 suite_name = tokens[0]
422 if len(tokens) <= 1:
423 scenario_names = []
424 else:
425 scenario_names = tokens[1].split('+')
426
427 return suite_name, scenario_names
428
429def load_suite_scenario_str(suite_scenario_str):
430 suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
431 suite = load(suite_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200432 scenarios = [config.get_scenario(scenario_name, resource.CONF_SCHEMA) for scenario_name in scenario_names]
Your Name44af3412017-04-13 03:11:59 +0200433 return (suite_scenario_str, suite, scenarios)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200434
435def bts_obj(suite_run, conf):
436 bts_type = conf.get('type')
Neels Hofmeyrd28d1a72017-06-14 02:59:55 +0200437 log.dbg('create BTS object', type=bts_type)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200438 bts_class = resource.KNOWN_BTS_TYPES.get(bts_type)
439 if bts_class is None:
440 raise RuntimeError('No such BTS type is defined: %r' % bts_type)
441 return bts_class(suite_run, conf)
442
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200443# vim: expandtab tabstop=4 shiftwidth=4