blob: 76cd2484cdd2413f2504d372d09cacad5fe1e084 [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
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200233 def remember_to_stop(self, process):
234 if self._processes is None:
235 self._processes = []
Pau Espin Pedrolecf10792017-05-08 16:56:38 +0200236 self._processes.insert(0, process)
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:
240 self._processes.pop().terminate()
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200241
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100242 def stop_process(self, process):
243 'Remove process from monitored list and stop it'
244 self._processes.remove(process)
245 process.terminate()
246
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200247 def free_resources(self):
248 if self.reserved_resources is None:
249 return
250 self.reserved_resources.free()
251
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200252 def ip_address(self, specifics=None):
253 return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200254
Neels Hofmeyr76d81032017-05-18 18:35:32 +0200255 def nitb(self, ip_address=None):
256 if ip_address is None:
257 ip_address = self.ip_address()
258 return osmo_nitb.OsmoNitb(self, ip_address)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200259
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200260 def hlr(self, ip_address=None):
261 if ip_address is None:
262 ip_address = self.ip_address()
263 return osmo_hlr.OsmoHlr(self, ip_address)
264
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200265 def ggsn(self, ip_address=None):
266 if ip_address is None:
267 ip_address = self.ip_address()
268 return osmo_ggsn.OsmoGgsn(self, ip_address)
269
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100270 def sgsn(self, hlr, ggsn, ip_address=None):
271 if ip_address is None:
272 ip_address = self.ip_address()
273 return osmo_sgsn.OsmoSgsn(self, hlr, ggsn, ip_address)
274
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200275 def mgcpgw(self, ip_address=None, bts_ip=None):
276 if ip_address is None:
277 ip_address = self.ip_address()
278 return osmo_mgcpgw.OsmoMgcpgw(self, ip_address, bts_ip)
279
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100280 def mgw(self, ip_address=None):
281 if ip_address is None:
282 ip_address = self.ip_address()
283 return osmo_mgw.OsmoMgw(self, ip_address)
284
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100285 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200286 if ip_address is None:
287 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100288 return osmo_msc.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200289
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100290 def bsc(self, msc, mgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200291 if ip_address is None:
292 ip_address = self.ip_address()
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100293 return osmo_bsc.OsmoBsc(self, msc, mgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200294
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200295 def stp(self, ip_address=None):
296 if ip_address is None:
297 ip_address = self.ip_address()
298 return osmo_stp.OsmoStp(self, ip_address)
299
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200300 def bts(self, specifics=None):
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200301 bts = bts_obj(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100302 bts.set_lac(self.lac())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100303 bts.set_rac(self.rac())
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100304 bts.set_cellid(self.cellid())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100305 bts.set_bvci(self.bvci())
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200306 self.register_for_cleanup(bts)
307 return bts
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200308
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200309 def modem(self, specifics=None):
310 conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200311 self.dbg('create Modem object', conf=conf)
Pau Espin Pedrol6cdd2fd2017-11-07 11:57:42 +0100312 ms = modem.Modem(conf)
313 self.register_for_cleanup(ms)
314 return ms
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200315
Neels Hofmeyrf2d279c2017-05-06 15:05:02 +0200316 def modems(self, count):
317 l = []
318 for i in range(count):
319 l.append(self.modem())
320 return l
321
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200322 def esme(self):
323 esme_obj = esme.Esme(self.msisdn())
Pau Espin Pedrolac9c1bb2017-08-10 10:59:40 +0200324 self.register_for_cleanup(esme_obj)
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200325 return esme_obj
326
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200327 def msisdn(self):
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200328 msisdn = self.resources_pool.next_msisdn(self)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200329 self.log('using MSISDN', msisdn)
330 return msisdn
331
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100332 def lac(self):
333 lac = self.resources_pool.next_lac(self)
334 self.log('using LAC', lac)
335 return lac
336
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100337 def rac(self):
338 rac = self.resources_pool.next_rac(self)
339 self.log('using RAC', rac)
340 return rac
341
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100342 def cellid(self):
343 cellid = self.resources_pool.next_cellid(self)
344 self.log('using CellId', cellid)
345 return cellid
346
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100347 def bvci(self):
348 bvci = self.resources_pool.next_bvci(self)
349 self.log('using BVCI', bvci)
350 return bvci
351
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200352 def poll(self):
353 if self._processes:
354 for process in self._processes:
Neels Hofmeyr5356d0a2017-04-10 03:45:30 +0200355 if process.terminated():
Neels Hofmeyr85eb3242017-04-09 22:01:16 +0200356 process.log_stdout_tail()
357 process.log_stderr_tail()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200358 log.ctx(process)
Pau Espin Pedrol800a6972017-07-03 18:34:09 +0200359 raise log.Error('Process ended prematurely: %s' % process.name())
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200360
361 def prompt(self, *msgs, **msg_details):
362 'ask for user interaction. Do not use in tests that should run automatically!'
363 if msg_details:
364 msgs = list(msgs)
365 msgs.append('{%s}' %
366 (', '.join(['%s=%r' % (k,v)
367 for k,v in sorted(msg_details.items())])))
368 msg = ' '.join(msgs) or 'Hit Enter to continue'
369 self.log('prompt:', msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200370 sys.__stdout__.write('\n\n--- PROMPT ---\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200371 sys.__stdout__.write(msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200372 sys.__stdout__.write('\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200373 sys.__stdout__.flush()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200374 entered = util.input_polling('> ', MainLoop.poll)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200375 self.log('prompt entered:', repr(entered))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200376 return entered
377
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +0200378 def resource_status_str(self):
379 return '\n'.join(('',
380 'SUITE RUN: %s' % self.origin_id(),
381 'ASKED FOR:', pprint.pformat(self._resource_requirements),
382 'RESERVED COUNT:', pprint.pformat(self.reserved_resources.counts()),
383 'RESOURCES STATE:', repr(self.reserved_resources)))
384
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200385loaded_suite_definitions = {}
386
387def load(suite_name):
388 global loaded_suite_definitions
389
390 suite = loaded_suite_definitions.get(suite_name)
391 if suite is not None:
392 return suite
393
394 suites_dir = config.get_suites_dir()
395 suite_dir = suites_dir.child(suite_name)
396 if not suites_dir.exists(suite_name):
397 raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))
398 if not suites_dir.isdir(suite_name):
399 raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))
400
401 suite_def = SuiteDefinition(suite_dir)
402 loaded_suite_definitions[suite_name] = suite_def
403 return suite_def
404
405def parse_suite_scenario_str(suite_scenario_str):
406 tokens = suite_scenario_str.split(':')
407 if len(tokens) > 2:
408 raise RuntimeError('invalid combination string: %r' % suite_scenario_str)
409
410 suite_name = tokens[0]
411 if len(tokens) <= 1:
412 scenario_names = []
413 else:
414 scenario_names = tokens[1].split('+')
415
416 return suite_name, scenario_names
417
418def load_suite_scenario_str(suite_scenario_str):
419 suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
420 suite = load(suite_name)
Pau Espin Pedrol0b302792017-09-10 16:33:10 +0200421 scenarios = [config.get_scenario(scenario_name, resource.CONF_SCHEMA) for scenario_name in scenario_names]
Your Name44af3412017-04-13 03:11:59 +0200422 return (suite_scenario_str, suite, scenarios)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200423
424def bts_obj(suite_run, conf):
425 bts_type = conf.get('type')
Neels Hofmeyrd28d1a72017-06-14 02:59:55 +0200426 log.dbg('create BTS object', type=bts_type)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200427 bts_class = resource.KNOWN_BTS_TYPES.get(bts_type)
428 if bts_class is None:
429 raise RuntimeError('No such BTS type is defined: %r' % bts_type)
430 return bts_class(suite_run, conf)
431
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200432# vim: expandtab tabstop=4 shiftwidth=4