blob: c712a753e2598148dd736ff3e608d03eeb68c958 [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
74 self._config = None
75 self._processes = None
76 self._run_dir = None
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020077 self.trial = trial
Neels Hofmeyr3531a192017-03-28 14:30:28 +020078 self.definition = suite_definition
79 self.scenarios = scenarios
Neels Hofmeyr3531a192017-03-28 14:30:28 +020080 self.resources_pool = resource.ResourcesPool()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020081 self.status = SuiteRun.UNKNOWN
82 self.load_tests()
83
84 def load_tests(self):
85 self.tests = []
86 for test_basename in self.definition.test_basenames:
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010087 self.tests.append(test.Test(self, test_basename))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020088
Neels Hofmeyr4d688c22017-05-29 04:13:58 +020089 def register_for_cleanup(self, *obj):
90 assert all([hasattr(o, 'cleanup') for o in obj])
91 self.objects_to_clean_up = self.objects_to_clean_up or []
92 self.objects_to_clean_up.extend(obj)
93
94 def objects_cleanup(self):
95 while self.objects_to_clean_up:
96 obj = self.objects_to_clean_up.pop()
Pau Espin Pedrol6100b622017-07-31 18:19:06 +020097 try:
98 obj.cleanup()
99 except Exception:
100 log.log_exn()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200101
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200102 def test_import_modules_register_for_cleanup(self, mod):
103 '''
104 Tests are required to call this API for any module loaded from its own
105 lib subdir, because they are loaded in the global namespace. Otherwise
106 later tests importing modules with the same name will re-use an already
107 loaded module.
108 '''
109 if mod not in self.test_import_modules_to_clean_up:
110 self.dbg('registering module %r for cleanup' % mod)
111 self.test_import_modules_to_clean_up.append(mod)
112
113 def test_import_modules_cleanup(self):
114 while self.test_import_modules_to_clean_up:
115 mod = self.test_import_modules_to_clean_up.pop()
116 try:
117 self.dbg('Cleaning up module %r' % mod)
118 del sys.modules[mod.__name__]
119 del mod
120 except Exception:
121 log.log_exn()
122
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200123 def mark_start(self):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200124 self.start_timestamp = time.time()
125 self.duration = 0
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200126 self.status = SuiteRun.UNKNOWN
127
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200128 def combined(self, conf_name):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200129 log.dbg(combining=conf_name)
130 log.ctx(combining_scenarios=conf_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200131 combination = config.replicate_times(self.definition.conf.get(conf_name, {}))
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200132 log.dbg(definition_conf=combination)
133 for scenario in self.scenarios:
134 log.ctx(combining_scenarios=conf_name, scenario=scenario.name())
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200135 c = config.replicate_times(scenario.get(conf_name, {}))
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200136 log.dbg(scenario=scenario.name(), conf=c)
137 if c is None:
138 continue
139 config.combine(combination, c)
140 return combination
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200141
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200142 def get_run_dir(self):
143 if self._run_dir is None:
144 self._run_dir = util.Dir(self.trial.get_run_dir().new_dir(self.name()))
145 return self._run_dir
146
147 def get_test_run_dir(self):
148 if self.current_test:
149 return self.current_test.get_run_dir()
150 return self.get_run_dir()
151
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200152 def resource_requirements(self):
153 if self._resource_requirements is None:
154 self._resource_requirements = self.combined('resources')
155 return self._resource_requirements
156
157 def config(self):
158 if self._config is None:
159 self._config = self.combined('config')
160 return self._config
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200161
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200162 def reserve_resources(self):
163 if self.reserved_resources:
164 raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')
Neels Hofmeyr7e2e8f12017-05-14 03:37:13 +0200165 self.log('reserving resources in', self.resources_pool.state_dir, '...')
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200166 self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements())
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200167
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200168 def run_tests(self, names=None):
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200169 suite_libdir = os.path.join(self.definition.suite_dir, 'lib')
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200170 try:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200171 log.large_separator(self.trial.name(), self.name(), sublevel=2)
172 self.mark_start()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200173 util.import_path_prepend(suite_libdir)
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200174 MainLoop.register_poll_func(self.poll)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200175 if not self.reserved_resources:
176 self.reserve_resources()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100177 for t in self.tests:
178 if names and not t.name() in names:
179 t.set_skip()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200180 continue
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100181 self.current_test = t
182 t.run()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200183 self.stop_processes()
184 self.objects_cleanup()
185 self.reserved_resources.put_all()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200186 except Exception:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200187 log.log_exn()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200188 except BaseException as e:
189 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
190 self.err('SUITE RUN ABORTED: %s' % type(e).__name__)
191 raise
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200192 finally:
193 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
194 # base exception is raised. Make sure to stop processes in this
195 # finally section. Resources are automatically freed with 'atexit'.
196 self.stop_processes()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200197 self.objects_cleanup()
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200198 self.free_resources()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200199 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200200 self.test_import_modules_cleanup()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200201 util.import_path_remove(suite_libdir)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200202 self.duration = time.time() - self.start_timestamp
203
204 passed, skipped, failed = self.count_test_results()
205 # if no tests ran, count it as failure
206 if passed and not failed:
207 self.status = SuiteRun.PASS
208 else:
209 self.status = SuiteRun.FAIL
210
211 log.large_separator(self.trial.name(), self.name(), self.status, sublevel=2, space_above=False)
212
213 def passed(self):
214 return self.status == SuiteRun.PASS
215
216 def count_test_results(self):
217 passed = 0
218 skipped = 0
219 failed = 0
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100220 for t in self.tests:
221 if t.status == test.Test.PASS:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200222 passed += 1
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100223 elif t.status == test.Test.FAIL:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200224 failed += 1
225 else:
226 skipped += 1
227 return (passed, skipped, failed)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200228
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200229 def remember_to_stop(self, process, respawn=False):
230 '''Ask suite to monitor and manage lifecycle of the Process object. If a
231 process managed by suite finishes before cleanup time, the current test
232 will be marked as FAIL and end immediatelly. If respwan=True, then suite
233 will respawn() the process instead.'''
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200234 if self._processes is None:
235 self._processes = []
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200236 self._processes.insert(0, (process, respawn))
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200237
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200238 def stop_processes(self):
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200239 while self._processes:
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200240 process, respawn = self._processes.pop()
241 process.terminate()
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200242
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100243 def stop_process(self, process):
244 'Remove process from monitored list and stop it'
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200245 for proc_respawn in self._processes:
246 proc, respawn = proc_respawn
247 if proc == process:
248 self._processes.remove(proc_respawn)
249 proc.terminate()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100250
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200251 def free_resources(self):
252 if self.reserved_resources is None:
253 return
254 self.reserved_resources.free()
255
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200256 def ip_address(self, specifics=None):
257 return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200258
Neels Hofmeyr76d81032017-05-18 18:35:32 +0200259 def nitb(self, ip_address=None):
260 if ip_address is None:
261 ip_address = self.ip_address()
262 return osmo_nitb.OsmoNitb(self, ip_address)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200263
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200264 def hlr(self, ip_address=None):
265 if ip_address is None:
266 ip_address = self.ip_address()
267 return osmo_hlr.OsmoHlr(self, ip_address)
268
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200269 def ggsn(self, ip_address=None):
270 if ip_address is None:
271 ip_address = self.ip_address()
272 return osmo_ggsn.OsmoGgsn(self, ip_address)
273
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100274 def sgsn(self, hlr, ggsn, ip_address=None):
275 if ip_address is None:
276 ip_address = self.ip_address()
277 return osmo_sgsn.OsmoSgsn(self, hlr, ggsn, ip_address)
278
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200279 def mgcpgw(self, ip_address=None, bts_ip=None):
280 if ip_address is None:
281 ip_address = self.ip_address()
282 return osmo_mgcpgw.OsmoMgcpgw(self, ip_address, bts_ip)
283
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100284 def mgw(self, ip_address=None):
285 if ip_address is None:
286 ip_address = self.ip_address()
287 return osmo_mgw.OsmoMgw(self, ip_address)
288
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100289 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200290 if ip_address is None:
291 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100292 return osmo_msc.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200293
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100294 def bsc(self, msc, mgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200295 if ip_address is None:
296 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100297 return osmo_bsc.OsmoBsc(self, msc, mgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200298
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200299 def stp(self, ip_address=None):
300 if ip_address is None:
301 ip_address = self.ip_address()
302 return osmo_stp.OsmoStp(self, ip_address)
303
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200304 def bts(self, specifics=None):
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200305 bts = bts_obj(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100306 bts.set_lac(self.lac())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100307 bts.set_rac(self.rac())
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100308 bts.set_cellid(self.cellid())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100309 bts.set_bvci(self.bvci())
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200310 self.register_for_cleanup(bts)
311 return bts
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200312
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200313 def modem(self, specifics=None):
314 conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200315 self.dbg('create Modem object', conf=conf)
Pau Espin Pedrol6cdd2fd2017-11-07 11:57:42 +0100316 ms = modem.Modem(conf)
317 self.register_for_cleanup(ms)
318 return ms
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200319
Neels Hofmeyrf2d279c2017-05-06 15:05:02 +0200320 def modems(self, count):
321 l = []
322 for i in range(count):
323 l.append(self.modem())
324 return l
325
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200326 def esme(self):
327 esme_obj = esme.Esme(self.msisdn())
Pau Espin Pedrolac9c1bb2017-08-10 10:59:40 +0200328 self.register_for_cleanup(esme_obj)
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200329 return esme_obj
330
Pau Espin Pedrolbc1ed882018-05-17 16:59:58 +0200331 def osmocon(self, specifics=None):
332 conf = self.reserved_resources.get(resource.R_OSMOCON, specifics=specifics)
333 osmocon_obj = osmocon.Osmocon(self, conf=conf)
334 self.register_for_cleanup(osmocon_obj)
335 return osmocon_obj
336
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200337 def msisdn(self):
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200338 msisdn = self.resources_pool.next_msisdn(self)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200339 self.log('using MSISDN', msisdn)
340 return msisdn
341
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100342 def lac(self):
343 lac = self.resources_pool.next_lac(self)
344 self.log('using LAC', lac)
345 return lac
346
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100347 def rac(self):
348 rac = self.resources_pool.next_rac(self)
349 self.log('using RAC', rac)
350 return rac
351
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100352 def cellid(self):
353 cellid = self.resources_pool.next_cellid(self)
354 self.log('using CellId', cellid)
355 return cellid
356
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100357 def bvci(self):
358 bvci = self.resources_pool.next_bvci(self)
359 self.log('using BVCI', bvci)
360 return bvci
361
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200362 def poll(self):
363 if self._processes:
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200364 for process, respawn in self._processes:
Neels Hofmeyr5356d0a2017-04-10 03:45:30 +0200365 if process.terminated():
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200366 if respawn == True:
367 process.respawn()
368 else:
369 process.log_stdout_tail()
370 process.log_stderr_tail()
371 log.ctx(process)
372 raise log.Error('Process ended prematurely: %s' % process.name())
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200373
374 def prompt(self, *msgs, **msg_details):
375 'ask for user interaction. Do not use in tests that should run automatically!'
376 if msg_details:
377 msgs = list(msgs)
378 msgs.append('{%s}' %
379 (', '.join(['%s=%r' % (k,v)
380 for k,v in sorted(msg_details.items())])))
381 msg = ' '.join(msgs) or 'Hit Enter to continue'
382 self.log('prompt:', msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200383 sys.__stdout__.write('\n\n--- PROMPT ---\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200384 sys.__stdout__.write(msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200385 sys.__stdout__.write('\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200386 sys.__stdout__.flush()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200387 entered = util.input_polling('> ', MainLoop.poll)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200388 self.log('prompt entered:', repr(entered))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200389 return entered
390
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +0200391 def resource_status_str(self):
392 return '\n'.join(('',
393 'SUITE RUN: %s' % self.origin_id(),
394 'ASKED FOR:', pprint.pformat(self._resource_requirements),
395 'RESERVED COUNT:', pprint.pformat(self.reserved_resources.counts()),
396 'RESOURCES STATE:', repr(self.reserved_resources)))
397
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200398loaded_suite_definitions = {}
399
400def load(suite_name):
401 global loaded_suite_definitions
402
403 suite = loaded_suite_definitions.get(suite_name)
404 if suite is not None:
405 return suite
406
407 suites_dir = config.get_suites_dir()
408 suite_dir = suites_dir.child(suite_name)
409 if not suites_dir.exists(suite_name):
410 raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))
411 if not suites_dir.isdir(suite_name):
412 raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))
413
414 suite_def = SuiteDefinition(suite_dir)
415 loaded_suite_definitions[suite_name] = suite_def
416 return suite_def
417
418def parse_suite_scenario_str(suite_scenario_str):
419 tokens = suite_scenario_str.split(':')
420 if len(tokens) > 2:
421 raise RuntimeError('invalid combination string: %r' % suite_scenario_str)
422
423 suite_name = tokens[0]
424 if len(tokens) <= 1:
425 scenario_names = []
426 else:
427 scenario_names = tokens[1].split('+')
428
429 return suite_name, scenario_names
430
431def load_suite_scenario_str(suite_scenario_str):
432 suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
433 suite = load(suite_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200434 scenarios = [config.get_scenario(scenario_name, resource.CONF_SCHEMA) for scenario_name in scenario_names]
Your Name44af3412017-04-13 03:11:59 +0200435 return (suite_scenario_str, suite, scenarios)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200436
437def bts_obj(suite_run, conf):
438 bts_type = conf.get('type')
Neels Hofmeyrd28d1a72017-06-14 02:59:55 +0200439 log.dbg('create BTS object', type=bts_type)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200440 bts_class = resource.KNOWN_BTS_TYPES.get(bts_type)
441 if bts_class is None:
442 raise RuntimeError('No such BTS type is defined: %r' % bts_type)
443 return bts_class(suite_run, conf)
444
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200445# vim: expandtab tabstop=4 shiftwidth=4