blob: 6d2916c153c67a4d0079d36d2f2425caf0ad1474 [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 Pedrolea8c3d42020-05-04 12:05:05 +020024from .core import config, log, util, process, schema
Pau Espin Pedrole8bbcbf2020-04-10 19:51:31 +020025from .core.event_loop import MainLoop
Pau Espin Pedrole1a58bd2020-04-10 20:46:07 +020026from .obj import nitb_osmo, hlr_osmo, mgcpgw_osmo, mgw_osmo, msc_osmo, bsc_osmo, stp_osmo, ggsn_osmo, sgsn_osmo, esme, osmocon, ms_driver, iperf3
27from .obj import run_node
Pau Espin Pedrole8bbcbf2020-04-10 19:51:31 +020028from . import resource, test
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020029
Neels Hofmeyr1ffc3fe2017-05-07 02:15:21 +020030class Timeout(Exception):
31 pass
32
Neels Hofmeyr3531a192017-03-28 14:30:28 +020033class SuiteDefinition(log.Origin):
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020034 '''A test suite reserves resources for a number of tests.
35 Each test requires a specific number of modems, BTSs etc., which are
36 reserved beforehand by a test suite. This way several test suites can be
37 scheduled dynamically without resource conflicts arising halfway through
38 the tests.'''
39
40 CONF_FILENAME = 'suite.conf'
41
Neels Hofmeyr3531a192017-03-28 14:30:28 +020042 def __init__(self, suite_dir):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020043 self.suite_dir = suite_dir
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020044 super().__init__(log.C_CNF, os.path.basename(self.suite_dir))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020045 self.read_conf()
46
47 def read_conf(self):
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020048 self.dbg('reading %s' % SuiteDefinition.CONF_FILENAME)
49 if not os.path.isdir(self.suite_dir):
50 raise RuntimeError('No such directory: %r' % self.suite_dir)
51 self.conf = config.read(os.path.join(self.suite_dir,
52 SuiteDefinition.CONF_FILENAME),
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020053 schema.get_all_schema())
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020054 self.load_test_basenames()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020055
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020056 def load_test_basenames(self):
57 self.test_basenames = []
58 for basename in sorted(os.listdir(self.suite_dir)):
59 if not basename.endswith('.py'):
60 continue
61 self.test_basenames.append(basename)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020062
Neels Hofmeyr3531a192017-03-28 14:30:28 +020063class SuiteRun(log.Origin):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020064 UNKNOWN = 'UNKNOWN'
65 PASS = 'PASS'
66 FAIL = 'FAIL'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020067
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020068 def __init__(self, trial, suite_scenario_str, suite_definition, scenarios=[]):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020069 super().__init__(log.C_TST, suite_scenario_str)
Pau Espin Pedrol58603672018-08-09 13:45:55 +020070 self.start_timestamp = None
71 self.duration = None
72 self.reserved_resources = None
73 self.objects_to_clean_up = None
74 self.test_import_modules_to_clean_up = []
75 self._resource_requirements = None
Pau Espin Pedrolaab56922018-08-21 14:58:29 +020076 self._resource_modifiers = None
Pau Espin Pedrol58603672018-08-09 13:45:55 +020077 self._config = None
Pau Espin Pedrol806aae82019-04-04 17:44:33 +020078 self._processes = []
Pau Espin Pedrol58603672018-08-09 13:45:55 +020079 self._run_dir = None
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020080 self.trial = trial
Neels Hofmeyr3531a192017-03-28 14:30:28 +020081 self.definition = suite_definition
82 self.scenarios = scenarios
Neels Hofmeyr3531a192017-03-28 14:30:28 +020083 self.resources_pool = resource.ResourcesPool()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020084 self.status = SuiteRun.UNKNOWN
85 self.load_tests()
86
87 def load_tests(self):
88 self.tests = []
89 for test_basename in self.definition.test_basenames:
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010090 self.tests.append(test.Test(self, test_basename))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020091
Neels Hofmeyr4d688c22017-05-29 04:13:58 +020092 def register_for_cleanup(self, *obj):
93 assert all([hasattr(o, 'cleanup') for o in obj])
94 self.objects_to_clean_up = self.objects_to_clean_up or []
95 self.objects_to_clean_up.extend(obj)
96
97 def objects_cleanup(self):
98 while self.objects_to_clean_up:
99 obj = self.objects_to_clean_up.pop()
Pau Espin Pedrol6100b622017-07-31 18:19:06 +0200100 try:
101 obj.cleanup()
102 except Exception:
103 log.log_exn()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200104
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200105 def test_import_modules_register_for_cleanup(self, mod):
106 '''
107 Tests are required to call this API for any module loaded from its own
108 lib subdir, because they are loaded in the global namespace. Otherwise
109 later tests importing modules with the same name will re-use an already
110 loaded module.
111 '''
112 if mod not in self.test_import_modules_to_clean_up:
113 self.dbg('registering module %r for cleanup' % mod)
114 self.test_import_modules_to_clean_up.append(mod)
115
116 def test_import_modules_cleanup(self):
117 while self.test_import_modules_to_clean_up:
118 mod = self.test_import_modules_to_clean_up.pop()
119 try:
120 self.dbg('Cleaning up module %r' % mod)
121 del sys.modules[mod.__name__]
122 del mod
123 except Exception:
124 log.log_exn()
125
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200126 def mark_start(self):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200127 self.start_timestamp = time.time()
128 self.duration = 0
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200129 self.status = SuiteRun.UNKNOWN
130
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200131 def combined(self, conf_name, replicate_times=True):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200132 log.dbg(combining=conf_name)
133 log.ctx(combining_scenarios=conf_name)
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200134 combination = self.definition.conf.get(conf_name, {})
135 if replicate_times:
136 combination = config.replicate_times(combination)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200137 log.dbg(definition_conf=combination)
138 for scenario in self.scenarios:
139 log.ctx(combining_scenarios=conf_name, scenario=scenario.name())
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200140 c = scenario.get(conf_name, {})
141 if replicate_times:
142 c = config.replicate_times(c)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200143 log.dbg(scenario=scenario.name(), conf=c)
144 if c is None:
145 continue
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +0200146 schema.combine(combination, c)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200147 return combination
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200148
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200149 def get_run_dir(self):
150 if self._run_dir is None:
151 self._run_dir = util.Dir(self.trial.get_run_dir().new_dir(self.name()))
152 return self._run_dir
153
154 def get_test_run_dir(self):
155 if self.current_test:
156 return self.current_test.get_run_dir()
157 return self.get_run_dir()
158
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200159 def resource_requirements(self):
160 if self._resource_requirements is None:
161 self._resource_requirements = self.combined('resources')
162 return self._resource_requirements
163
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200164 def resource_modifiers(self):
165 if self._resource_modifiers is None:
166 self._resource_modifiers = self.combined('modifiers')
167 return self._resource_modifiers
168
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200169 def config(self):
170 if self._config is None:
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200171 self._config = self.combined('config', False)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200172 return self._config
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200173
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200174 def reserve_resources(self):
175 if self.reserved_resources:
176 raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')
Neels Hofmeyr7e2e8f12017-05-14 03:37:13 +0200177 self.log('reserving resources in', self.resources_pool.state_dir, '...')
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200178 self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements(), self.resource_modifiers())
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200179
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200180 def run_tests(self, names=None):
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200181 suite_libdir = os.path.join(self.definition.suite_dir, 'lib')
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200182 try:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200183 log.large_separator(self.trial.name(), self.name(), sublevel=2)
184 self.mark_start()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200185 util.import_path_prepend(suite_libdir)
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200186 MainLoop.register_poll_func(self.poll)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200187 if not self.reserved_resources:
188 self.reserve_resources()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100189 for t in self.tests:
190 if names and not t.name() in names:
191 t.set_skip()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200192 continue
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100193 self.current_test = t
194 t.run()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200195 self.stop_processes()
196 self.objects_cleanup()
197 self.reserved_resources.put_all()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200198 except Exception:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200199 log.log_exn()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200200 except BaseException as e:
201 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
202 self.err('SUITE RUN ABORTED: %s' % type(e).__name__)
203 raise
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200204 finally:
205 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
206 # base exception is raised. Make sure to stop processes in this
207 # finally section. Resources are automatically freed with 'atexit'.
208 self.stop_processes()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200209 self.objects_cleanup()
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200210 self.free_resources()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200211 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200212 self.test_import_modules_cleanup()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200213 util.import_path_remove(suite_libdir)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200214 self.duration = time.time() - self.start_timestamp
215
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100216 passed, skipped, failed, errors = self.count_test_results()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200217 # if no tests ran, count it as failure
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100218 if passed and not failed and not errors:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200219 self.status = SuiteRun.PASS
220 else:
221 self.status = SuiteRun.FAIL
222
223 log.large_separator(self.trial.name(), self.name(), self.status, sublevel=2, space_above=False)
224
225 def passed(self):
226 return self.status == SuiteRun.PASS
227
228 def count_test_results(self):
229 passed = 0
230 skipped = 0
231 failed = 0
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100232 errors = 0
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100233 for t in self.tests:
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100234 if t.status == test.Test.SKIP:
235 skipped += 1
236 elif t.status == test.Test.PASS:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200237 passed += 1
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100238 elif t.status == test.Test.FAIL:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200239 failed += 1
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100240 else: # error, could not run
241 errors += 1
242 return (passed, skipped, failed, errors)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200243
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200244 def remember_to_stop(self, process, respawn=False):
245 '''Ask suite to monitor and manage lifecycle of the Process object. If a
246 process managed by suite finishes before cleanup time, the current test
247 will be marked as FAIL and end immediatelly. If respwan=True, then suite
248 will respawn() the process instead.'''
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200249 self._processes.insert(0, (process, respawn))
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200250
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200251 def stop_processes(self):
Pau Espin Pedrol04096552019-04-04 16:43:12 +0200252 if len(self._processes) == 0:
253 return
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000254 strategy = process.ParallelTerminationStrategy()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200255 while self._processes:
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000256 proc, _ = self._processes.pop()
257 strategy.add_process(proc)
258 strategy.terminate_all()
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200259
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100260 def stop_process(self, process):
261 'Remove process from monitored list and stop it'
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200262 for proc_respawn in self._processes:
263 proc, respawn = proc_respawn
264 if proc == process:
265 self._processes.remove(proc_respawn)
266 proc.terminate()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100267
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200268 def free_resources(self):
269 if self.reserved_resources is None:
270 return
271 self.reserved_resources.free()
272
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200273 def ip_address(self, specifics=None):
274 return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200275
Neels Hofmeyr76d81032017-05-18 18:35:32 +0200276 def nitb(self, ip_address=None):
277 if ip_address is None:
278 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200279 return nitb_osmo.OsmoNitb(self, ip_address)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200280
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200281 def hlr(self, ip_address=None):
282 if ip_address is None:
283 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200284 return hlr_osmo.OsmoHlr(self, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200285
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200286 def ggsn(self, ip_address=None):
287 if ip_address is None:
288 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200289 return ggsn_osmo.OsmoGgsn(self, ip_address)
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200290
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100291 def sgsn(self, hlr, ggsn, ip_address=None):
292 if ip_address is None:
293 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200294 return sgsn_osmo.OsmoSgsn(self, hlr, ggsn, ip_address)
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100295
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200296 def mgcpgw(self, ip_address=None, bts_ip=None):
297 if ip_address is None:
298 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200299 return mgcpgw_osmo.OsmoMgcpgw(self, ip_address, bts_ip)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200300
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100301 def mgw(self, ip_address=None):
302 if ip_address is None:
303 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200304 return mgw_osmo.OsmoMgw(self, ip_address)
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100305
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100306 def msc(self, hlr, mgcpgw, 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 Pedrol9b486ee2020-04-10 19:41:06 +0200309 return msc_osmo.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200310
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100311 def bsc(self, msc, mgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200312 if ip_address is None:
313 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200314 return bsc_osmo.OsmoBsc(self, msc, mgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200315
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200316 def stp(self, ip_address=None):
317 if ip_address is None:
318 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200319 return stp_osmo.OsmoStp(self, ip_address)
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200320
Holger Hans Peter Freytherb484aab2018-08-29 04:28:33 +0100321 def ms_driver(self):
322 ms = ms_driver.MsDriver(self)
323 self.register_for_cleanup(ms)
324 return ms
325
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200326 def bts(self, specifics=None):
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200327 bts = bts_obj(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100328 bts.set_lac(self.lac())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100329 bts.set_rac(self.rac())
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100330 bts.set_cellid(self.cellid())
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100331 bts.set_bvci(self.bvci())
Pau Espin Pedrol15aae982017-09-08 13:55:54 +0200332 self.register_for_cleanup(bts)
333 return bts
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200334
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200335 def modem(self, specifics=None):
336 conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
Holger Hans Peter Freyther74243012019-02-27 08:18:38 +0000337 ms_type = conf.get('type')
338 ms_class = resource.KNOWN_MS_TYPES.get(ms_type)
339 if ms_class is None:
340 raise RuntimeError('No such Modem type is defined: %r' % ms_type)
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200341 self.dbg('create Modem object', conf=conf)
Holger Hans Peter Freyther74243012019-02-27 08:18:38 +0000342 ms = ms_class(self, conf)
Pau Espin Pedrol6cdd2fd2017-11-07 11:57:42 +0100343 self.register_for_cleanup(ms)
344 return ms
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200345
Neels Hofmeyrf2d279c2017-05-06 15:05:02 +0200346 def modems(self, count):
347 l = []
348 for i in range(count):
349 l.append(self.modem())
350 return l
351
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +0000352 def all_resources(self, resource_func):
353 """Returns all yielded resource."""
354 l = []
355 while True:
356 try:
357 l.append(resource_func())
358 except resource.NoResourceExn:
359 return l
360
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200361 def esme(self):
362 esme_obj = esme.Esme(self.msisdn())
Pau Espin Pedrolac9c1bb2017-08-10 10:59:40 +0200363 self.register_for_cleanup(esme_obj)
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200364 return esme_obj
365
Pau Espin Pedrol116a2c42020-02-11 17:41:13 +0100366 def run_node(self, specifics=None):
367 return run_node.RunNode.from_conf(self.reserved_resources.get(resource.R_RUN_NODE, specifics=specifics))
368
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100369 def enb(self, specifics=None):
370 enb = enb_obj(self, self.reserved_resources.get(resource.R_ENB, specifics=specifics))
371 self.register_for_cleanup(enb)
372 return enb
373
374 def epc(self, run_node=None):
375 if run_node is None:
376 run_node = self.run_node()
Pau Espin Pedrolda2e31f2020-03-31 13:45:01 +0200377 epc = epc_obj(self, run_node)
378 self.register_for_cleanup(epc)
379 return epc
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100380
Pau Espin Pedrolbc1ed882018-05-17 16:59:58 +0200381 def osmocon(self, specifics=None):
382 conf = self.reserved_resources.get(resource.R_OSMOCON, specifics=specifics)
383 osmocon_obj = osmocon.Osmocon(self, conf=conf)
384 self.register_for_cleanup(osmocon_obj)
385 return osmocon_obj
386
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200387 def iperf3srv(self, ip_address=None):
388 if ip_address is None:
389 ip_address = self.ip_address()
390 iperf3srv_obj = iperf3.IPerf3Server(self, ip_address)
391 return iperf3srv_obj
392
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200393 def msisdn(self):
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200394 msisdn = self.resources_pool.next_msisdn(self)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200395 self.log('using MSISDN', msisdn)
396 return msisdn
397
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100398 def lac(self):
399 lac = self.resources_pool.next_lac(self)
400 self.log('using LAC', lac)
401 return lac
402
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100403 def rac(self):
404 rac = self.resources_pool.next_rac(self)
405 self.log('using RAC', rac)
406 return rac
407
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100408 def cellid(self):
409 cellid = self.resources_pool.next_cellid(self)
410 self.log('using CellId', cellid)
411 return cellid
412
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100413 def bvci(self):
414 bvci = self.resources_pool.next_bvci(self)
415 self.log('using BVCI', bvci)
416 return bvci
417
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200418 def poll(self):
Pau Espin Pedrol806aae82019-04-04 17:44:33 +0200419 for proc, respawn in self._processes:
420 if proc.terminated():
421 if respawn == True:
422 proc.respawn()
423 else:
424 proc.log_stdout_tail()
425 proc.log_stderr_tail()
426 log.ctx(proc)
427 raise log.Error('Process ended prematurely: %s' % proc.name())
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200428
429 def prompt(self, *msgs, **msg_details):
430 'ask for user interaction. Do not use in tests that should run automatically!'
431 if msg_details:
432 msgs = list(msgs)
433 msgs.append('{%s}' %
434 (', '.join(['%s=%r' % (k,v)
435 for k,v in sorted(msg_details.items())])))
436 msg = ' '.join(msgs) or 'Hit Enter to continue'
437 self.log('prompt:', msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200438 sys.__stdout__.write('\n\n--- PROMPT ---\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200439 sys.__stdout__.write(msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200440 sys.__stdout__.write('\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200441 sys.__stdout__.flush()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200442 entered = util.input_polling('> ', MainLoop.poll)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200443 self.log('prompt entered:', repr(entered))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200444 return entered
445
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +0200446 def resource_status_str(self):
447 return '\n'.join(('',
448 'SUITE RUN: %s' % self.origin_id(),
449 'ASKED FOR:', pprint.pformat(self._resource_requirements),
450 'RESERVED COUNT:', pprint.pformat(self.reserved_resources.counts()),
451 'RESOURCES STATE:', repr(self.reserved_resources)))
452
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200453loaded_suite_definitions = {}
454
455def load(suite_name):
456 global loaded_suite_definitions
457
458 suite = loaded_suite_definitions.get(suite_name)
459 if suite is not None:
460 return suite
461
462 suites_dir = config.get_suites_dir()
463 suite_dir = suites_dir.child(suite_name)
464 if not suites_dir.exists(suite_name):
465 raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))
466 if not suites_dir.isdir(suite_name):
467 raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))
468
469 suite_def = SuiteDefinition(suite_dir)
470 loaded_suite_definitions[suite_name] = suite_def
471 return suite_def
472
473def parse_suite_scenario_str(suite_scenario_str):
474 tokens = suite_scenario_str.split(':')
475 if len(tokens) > 2:
476 raise RuntimeError('invalid combination string: %r' % suite_scenario_str)
477
478 suite_name = tokens[0]
479 if len(tokens) <= 1:
480 scenario_names = []
481 else:
482 scenario_names = tokens[1].split('+')
483
484 return suite_name, scenario_names
485
486def load_suite_scenario_str(suite_scenario_str):
487 suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
488 suite = load(suite_name)
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +0200489 scenarios = [config.get_scenario(scenario_name, schema.get_all_schema()) for scenario_name in scenario_names]
Your Name44af3412017-04-13 03:11:59 +0200490 return (suite_scenario_str, suite, scenarios)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200491
492def bts_obj(suite_run, conf):
493 bts_type = conf.get('type')
Neels Hofmeyrd28d1a72017-06-14 02:59:55 +0200494 log.dbg('create BTS object', type=bts_type)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200495 bts_class = resource.KNOWN_BTS_TYPES.get(bts_type)
496 if bts_class is None:
497 raise RuntimeError('No such BTS type is defined: %r' % bts_type)
498 return bts_class(suite_run, conf)
499
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100500def enb_obj(suite_run, conf):
501 enb_type = conf.get('type')
502 log.dbg('create ENB object', type=enb_type)
503 enb_class = resource.KNOWN_ENB_TYPES.get(enb_type)
504 if enb_class is None:
505 raise RuntimeError('No such ENB type is defined: %r' % enb_type)
506 return enb_class(suite_run, conf)
507
Pau Espin Pedrolda2e31f2020-03-31 13:45:01 +0200508def epc_obj(suite_run, run_node):
509 values = dict(epc=config.get_defaults('epc'))
510 config.overlay(values, dict(epc=suite_run.config().get('epc', {})))
511 epc_type = values['epc'].get('type', None)
512 if epc_type is None:
513 raise RuntimeError('EPC type is not defined!')
514 log.dbg('create EPC object', type=epc_type)
515 epc_class = resource.KNOWN_EPC_TYPES.get(epc_type)
516 if epc_class is None:
517 raise RuntimeError('No such EPC type is defined: %r' % epc_type)
518 return epc_class(suite_run, run_node)
519
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200520# vim: expandtab tabstop=4 shiftwidth=4