blob: c5421c45fc5549cd9bf40ab4df529c8ffdfd8d1c [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 Pedrol06cb5362020-05-04 18:58:53 +020024from .core import config, log, util, process, schema, resource
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 Pedrol1ee5ec52020-05-04 17:16:39 +020028from .obj import epc
29from .obj import enb
30from .obj import bts
31from .obj import ms
Pau Espin Pedrol06cb5362020-05-04 18:58:53 +020032from . import test
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020033
Neels Hofmeyr1ffc3fe2017-05-07 02:15:21 +020034class Timeout(Exception):
35 pass
36
Neels Hofmeyr3531a192017-03-28 14:30:28 +020037class SuiteDefinition(log.Origin):
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +020038 '''A test suite reserves resources for a number of tests.
39 Each test requires a specific number of modems, BTSs etc., which are
40 reserved beforehand by a test suite. This way several test suites can be
41 scheduled dynamically without resource conflicts arising halfway through
42 the tests.'''
43
44 CONF_FILENAME = 'suite.conf'
45
Neels Hofmeyr3531a192017-03-28 14:30:28 +020046 def __init__(self, suite_dir):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020047 self.suite_dir = suite_dir
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020048 super().__init__(log.C_CNF, os.path.basename(self.suite_dir))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020049 self.read_conf()
50
51 def read_conf(self):
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020052 self.dbg('reading %s' % SuiteDefinition.CONF_FILENAME)
53 if not os.path.isdir(self.suite_dir):
54 raise RuntimeError('No such directory: %r' % self.suite_dir)
55 self.conf = config.read(os.path.join(self.suite_dir,
56 SuiteDefinition.CONF_FILENAME),
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020057 schema.get_all_schema())
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020058 self.load_test_basenames()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020059
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020060 def load_test_basenames(self):
61 self.test_basenames = []
62 for basename in sorted(os.listdir(self.suite_dir)):
63 if not basename.endswith('.py'):
64 continue
65 self.test_basenames.append(basename)
Neels Hofmeyr3531a192017-03-28 14:30:28 +020066
Neels Hofmeyr3531a192017-03-28 14:30:28 +020067class SuiteRun(log.Origin):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +020068 UNKNOWN = 'UNKNOWN'
69 PASS = 'PASS'
70 FAIL = 'FAIL'
Neels Hofmeyr3531a192017-03-28 14:30:28 +020071
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020072 def __init__(self, trial, suite_scenario_str, suite_definition, scenarios=[]):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +020073 super().__init__(log.C_TST, suite_scenario_str)
Pau Espin Pedrol58603672018-08-09 13:45:55 +020074 self.start_timestamp = None
75 self.duration = None
76 self.reserved_resources = None
77 self.objects_to_clean_up = None
78 self.test_import_modules_to_clean_up = []
79 self._resource_requirements = None
Pau Espin Pedrolaab56922018-08-21 14:58:29 +020080 self._resource_modifiers = None
Pau Espin Pedrol58603672018-08-09 13:45:55 +020081 self._config = None
Pau Espin Pedrol806aae82019-04-04 17:44:33 +020082 self._processes = []
Pau Espin Pedrol58603672018-08-09 13:45:55 +020083 self._run_dir = None
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020084 self.trial = trial
Neels Hofmeyr3531a192017-03-28 14:30:28 +020085 self.definition = suite_definition
86 self.scenarios = scenarios
Neels Hofmeyr3531a192017-03-28 14:30:28 +020087 self.resources_pool = resource.ResourcesPool()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +020088 self.status = SuiteRun.UNKNOWN
89 self.load_tests()
90
91 def load_tests(self):
92 self.tests = []
93 for test_basename in self.definition.test_basenames:
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +010094 self.tests.append(test.Test(self, test_basename))
Neels Hofmeyr3531a192017-03-28 14:30:28 +020095
Neels Hofmeyr4d688c22017-05-29 04:13:58 +020096 def register_for_cleanup(self, *obj):
97 assert all([hasattr(o, 'cleanup') for o in obj])
98 self.objects_to_clean_up = self.objects_to_clean_up or []
99 self.objects_to_clean_up.extend(obj)
100
101 def objects_cleanup(self):
102 while self.objects_to_clean_up:
103 obj = self.objects_to_clean_up.pop()
Pau Espin Pedrol6100b622017-07-31 18:19:06 +0200104 try:
105 obj.cleanup()
106 except Exception:
107 log.log_exn()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200108
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200109 def test_import_modules_register_for_cleanup(self, mod):
110 '''
111 Tests are required to call this API for any module loaded from its own
112 lib subdir, because they are loaded in the global namespace. Otherwise
113 later tests importing modules with the same name will re-use an already
114 loaded module.
115 '''
116 if mod not in self.test_import_modules_to_clean_up:
117 self.dbg('registering module %r for cleanup' % mod)
118 self.test_import_modules_to_clean_up.append(mod)
119
120 def test_import_modules_cleanup(self):
121 while self.test_import_modules_to_clean_up:
122 mod = self.test_import_modules_to_clean_up.pop()
123 try:
124 self.dbg('Cleaning up module %r' % mod)
125 del sys.modules[mod.__name__]
126 del mod
127 except Exception:
128 log.log_exn()
129
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200130 def mark_start(self):
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200131 self.start_timestamp = time.time()
132 self.duration = 0
Pau Espin Pedrol0ffb4142017-05-15 18:24:35 +0200133 self.status = SuiteRun.UNKNOWN
134
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200135 def combined(self, conf_name, replicate_times=True):
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200136 log.dbg(combining=conf_name)
137 log.ctx(combining_scenarios=conf_name)
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200138 combination = self.definition.conf.get(conf_name, {})
139 if replicate_times:
140 combination = config.replicate_times(combination)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200141 log.dbg(definition_conf=combination)
142 for scenario in self.scenarios:
143 log.ctx(combining_scenarios=conf_name, scenario=scenario.name())
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200144 c = scenario.get(conf_name, {})
145 if replicate_times:
146 c = config.replicate_times(c)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200147 log.dbg(scenario=scenario.name(), conf=c)
148 if c is None:
149 continue
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +0200150 schema.combine(combination, c)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200151 return combination
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200152
Pau Espin Pedrold0912332017-06-14 13:27:08 +0200153 def get_run_dir(self):
154 if self._run_dir is None:
155 self._run_dir = util.Dir(self.trial.get_run_dir().new_dir(self.name()))
156 return self._run_dir
157
158 def get_test_run_dir(self):
159 if self.current_test:
160 return self.current_test.get_run_dir()
161 return self.get_run_dir()
162
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200163 def resource_requirements(self):
164 if self._resource_requirements is None:
165 self._resource_requirements = self.combined('resources')
166 return self._resource_requirements
167
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200168 def resource_modifiers(self):
169 if self._resource_modifiers is None:
170 self._resource_modifiers = self.combined('modifiers')
171 return self._resource_modifiers
172
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200173 def config(self):
174 if self._config is None:
Pau Espin Pedrolc264d3d2018-08-27 12:49:35 +0200175 self._config = self.combined('config', False)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200176 return self._config
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200177
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200178 def reserve_resources(self):
179 if self.reserved_resources:
180 raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')
Neels Hofmeyr7e2e8f12017-05-14 03:37:13 +0200181 self.log('reserving resources in', self.resources_pool.state_dir, '...')
Pau Espin Pedrolaab56922018-08-21 14:58:29 +0200182 self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements(), self.resource_modifiers())
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200183
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200184 def run_tests(self, names=None):
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200185 suite_libdir = os.path.join(self.definition.suite_dir, 'lib')
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200186 try:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200187 log.large_separator(self.trial.name(), self.name(), sublevel=2)
188 self.mark_start()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200189 util.import_path_prepend(suite_libdir)
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200190 MainLoop.register_poll_func(self.poll)
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200191 if not self.reserved_resources:
192 self.reserve_resources()
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100193 for t in self.tests:
194 if names and not t.name() in names:
195 t.set_skip()
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200196 continue
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100197 self.current_test = t
198 t.run()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200199 self.stop_processes()
200 self.objects_cleanup()
201 self.reserved_resources.put_all()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200202 except Exception:
Neels Hofmeyr1a7a3f02017-06-10 01:18:27 +0200203 log.log_exn()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200204 except BaseException as e:
205 # when the program is aborted by a signal (like Ctrl-C), escalate to abort all.
206 self.err('SUITE RUN ABORTED: %s' % type(e).__name__)
207 raise
Pau Espin Pedrol469316f2017-05-17 14:51:31 +0200208 finally:
209 # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit
210 # base exception is raised. Make sure to stop processes in this
211 # finally section. Resources are automatically freed with 'atexit'.
212 self.stop_processes()
Neels Hofmeyr4d688c22017-05-29 04:13:58 +0200213 self.objects_cleanup()
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200214 self.free_resources()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200215 MainLoop.unregister_poll_func(self.poll)
Pau Espin Pedrol155a3552018-05-09 11:24:23 +0200216 self.test_import_modules_cleanup()
Pau Espin Pedrol7e02d202018-05-08 15:28:48 +0200217 util.import_path_remove(suite_libdir)
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200218 self.duration = time.time() - self.start_timestamp
219
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100220 passed, skipped, failed, errors = self.count_test_results()
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200221 # if no tests ran, count it as failure
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100222 if passed and not failed and not errors:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200223 self.status = SuiteRun.PASS
224 else:
225 self.status = SuiteRun.FAIL
226
227 log.large_separator(self.trial.name(), self.name(), self.status, sublevel=2, space_above=False)
228
229 def passed(self):
230 return self.status == SuiteRun.PASS
231
232 def count_test_results(self):
233 passed = 0
234 skipped = 0
235 failed = 0
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100236 errors = 0
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100237 for t in self.tests:
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100238 if t.status == test.Test.SKIP:
239 skipped += 1
240 elif t.status == test.Test.PASS:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200241 passed += 1
Pau Espin Pedrolfd5de3d2017-11-09 14:26:35 +0100242 elif t.status == test.Test.FAIL:
Neels Hofmeyr6ccda112017-06-06 19:41:17 +0200243 failed += 1
Pau Espin Pedrol02e8a8d2020-03-05 17:22:40 +0100244 else: # error, could not run
245 errors += 1
246 return (passed, skipped, failed, errors)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200247
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200248 def remember_to_stop(self, process, respawn=False):
249 '''Ask suite to monitor and manage lifecycle of the Process object. If a
250 process managed by suite finishes before cleanup time, the current test
251 will be marked as FAIL and end immediatelly. If respwan=True, then suite
252 will respawn() the process instead.'''
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200253 self._processes.insert(0, (process, respawn))
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200254
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200255 def stop_processes(self):
Pau Espin Pedrol04096552019-04-04 16:43:12 +0200256 if len(self._processes) == 0:
257 return
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000258 strategy = process.ParallelTerminationStrategy()
Pau Espin Pedrol1dd29552017-06-13 18:07:57 +0200259 while self._processes:
Holger Hans Peter Freyther20b52c12019-02-27 02:31:50 +0000260 proc, _ = self._processes.pop()
261 strategy.add_process(proc)
262 strategy.terminate_all()
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200263
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100264 def stop_process(self, process):
265 'Remove process from monitored list and stop it'
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200266 for proc_respawn in self._processes:
267 proc, respawn = proc_respawn
268 if proc == process:
269 self._processes.remove(proc_respawn)
270 proc.terminate()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100271
Neels Hofmeyred4e5282017-05-29 02:53:54 +0200272 def free_resources(self):
273 if self.reserved_resources is None:
274 return
275 self.reserved_resources.free()
276
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200277 def ip_address(self, specifics=None):
278 return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200279
Neels Hofmeyr76d81032017-05-18 18:35:32 +0200280 def nitb(self, ip_address=None):
281 if ip_address is None:
282 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200283 return nitb_osmo.OsmoNitb(self, ip_address)
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200284
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200285 def hlr(self, ip_address=None):
286 if ip_address is None:
287 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200288 return hlr_osmo.OsmoHlr(self, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200289
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200290 def ggsn(self, ip_address=None):
291 if ip_address is None:
292 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200293 return ggsn_osmo.OsmoGgsn(self, ip_address)
Pau Espin Pedrol30ceb5c2017-08-31 18:30:11 +0200294
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100295 def sgsn(self, hlr, ggsn, ip_address=None):
296 if ip_address is None:
297 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200298 return sgsn_osmo.OsmoSgsn(self, hlr, ggsn, ip_address)
Pau Espin Pedrol4796b352017-11-23 11:07:42 +0100299
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200300 def mgcpgw(self, ip_address=None, bts_ip=None):
301 if ip_address is None:
302 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200303 return mgcpgw_osmo.OsmoMgcpgw(self, ip_address, bts_ip)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200304
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100305 def mgw(self, ip_address=None):
306 if ip_address is None:
307 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200308 return mgw_osmo.OsmoMgw(self, ip_address)
Pau Espin Pedrol386b78d2017-11-09 13:02:09 +0100309
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100310 def msc(self, hlr, mgcpgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200311 if ip_address is None:
312 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200313 return msc_osmo.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200314
Pau Espin Pedrol1e1d3812017-11-16 18:06:37 +0100315 def bsc(self, msc, mgw, stp, ip_address=None):
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200316 if ip_address is None:
317 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200318 return bsc_osmo.OsmoBsc(self, msc, mgw, stp, ip_address)
Neels Hofmeyr798e5922017-05-18 15:24:02 +0200319
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200320 def stp(self, ip_address=None):
321 if ip_address is None:
322 ip_address = self.ip_address()
Pau Espin Pedrol9b486ee2020-04-10 19:41:06 +0200323 return stp_osmo.OsmoStp(self, ip_address)
Neels Hofmeyr38b051c2017-06-13 16:26:06 +0200324
Holger Hans Peter Freytherb484aab2018-08-29 04:28:33 +0100325 def ms_driver(self):
326 ms = ms_driver.MsDriver(self)
327 self.register_for_cleanup(ms)
328 return ms
329
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200330 def bts(self, specifics=None):
Pau Espin Pedrol1ee5ec52020-05-04 17:16:39 +0200331 bts_obj = bts.Bts.get_instance_by_type(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))
332 bts_obj.set_lac(self.lac())
333 bts_obj.set_rac(self.rac())
334 bts_obj.set_cellid(self.cellid())
335 bts_obj.set_bvci(self.bvci())
336 self.register_for_cleanup(bts_obj)
337 return bts_obj
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200338
Neels Hofmeyrb902b292017-06-06 21:52:03 +0200339 def modem(self, specifics=None):
340 conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)
Pau Espin Pedrol1ee5ec52020-05-04 17:16:39 +0200341 ms_obj = ms.MS.get_instance_by_type(self, conf)
342 self.register_for_cleanup(ms_obj)
343 return ms_obj
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200344
Neels Hofmeyrf2d279c2017-05-06 15:05:02 +0200345 def modems(self, count):
346 l = []
347 for i in range(count):
348 l.append(self.modem())
349 return l
350
Holger Hans Peter Freyther5e67ed42019-02-25 09:48:50 +0000351 def all_resources(self, resource_func):
352 """Returns all yielded resource."""
353 l = []
354 while True:
355 try:
356 l.append(resource_func())
357 except resource.NoResourceExn:
358 return l
359
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200360 def esme(self):
361 esme_obj = esme.Esme(self.msisdn())
Pau Espin Pedrolac9c1bb2017-08-10 10:59:40 +0200362 self.register_for_cleanup(esme_obj)
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200363 return esme_obj
364
Pau Espin Pedrol116a2c42020-02-11 17:41:13 +0100365 def run_node(self, specifics=None):
366 return run_node.RunNode.from_conf(self.reserved_resources.get(resource.R_RUN_NODE, specifics=specifics))
367
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100368 def enb(self, specifics=None):
Pau Espin Pedrol1ee5ec52020-05-04 17:16:39 +0200369 enb_obj = enb.eNodeB.get_instance_by_type(self, self.reserved_resources.get(resource.R_ENB, specifics=specifics))
370 self.register_for_cleanup(enb_obj)
371 return enb_obj
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100372
373 def epc(self, run_node=None):
374 if run_node is None:
375 run_node = self.run_node()
Pau Espin Pedrol1ee5ec52020-05-04 17:16:39 +0200376 epc_obj = epc.EPC.get_instance_by_type(self, run_node)
377 self.register_for_cleanup(epc_obj)
378 return epc_obj
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100379
Pau Espin Pedrolbc1ed882018-05-17 16:59:58 +0200380 def osmocon(self, specifics=None):
381 conf = self.reserved_resources.get(resource.R_OSMOCON, specifics=specifics)
382 osmocon_obj = osmocon.Osmocon(self, conf=conf)
383 self.register_for_cleanup(osmocon_obj)
384 return osmocon_obj
385
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200386 def iperf3srv(self, ip_address=None):
387 if ip_address is None:
388 ip_address = self.ip_address()
389 iperf3srv_obj = iperf3.IPerf3Server(self, ip_address)
390 return iperf3srv_obj
391
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200392 def msisdn(self):
Pau Espin Pedrol2d16f6f2017-05-30 15:33:57 +0200393 msisdn = self.resources_pool.next_msisdn(self)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200394 self.log('using MSISDN', msisdn)
395 return msisdn
396
Pau Espin Pedrol5e0c2512017-11-06 18:40:23 +0100397 def lac(self):
398 lac = self.resources_pool.next_lac(self)
399 self.log('using LAC', lac)
400 return lac
401
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100402 def rac(self):
403 rac = self.resources_pool.next_rac(self)
404 self.log('using RAC', rac)
405 return rac
406
Pau Espin Pedrol4ccce7c2017-11-07 11:13:20 +0100407 def cellid(self):
408 cellid = self.resources_pool.next_cellid(self)
409 self.log('using CellId', cellid)
410 return cellid
411
Pau Espin Pedrol8a3a7b52017-11-28 15:50:02 +0100412 def bvci(self):
413 bvci = self.resources_pool.next_bvci(self)
414 self.log('using BVCI', bvci)
415 return bvci
416
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200417 def poll(self):
Pau Espin Pedrol806aae82019-04-04 17:44:33 +0200418 for proc, respawn in self._processes:
419 if proc.terminated():
420 if respawn == True:
421 proc.respawn()
422 else:
423 proc.log_stdout_tail()
424 proc.log_stderr_tail()
425 log.ctx(proc)
426 raise log.Error('Process ended prematurely: %s' % proc.name())
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200427
428 def prompt(self, *msgs, **msg_details):
429 'ask for user interaction. Do not use in tests that should run automatically!'
430 if msg_details:
431 msgs = list(msgs)
432 msgs.append('{%s}' %
433 (', '.join(['%s=%r' % (k,v)
434 for k,v in sorted(msg_details.items())])))
435 msg = ' '.join(msgs) or 'Hit Enter to continue'
436 self.log('prompt:', msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200437 sys.__stdout__.write('\n\n--- PROMPT ---\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200438 sys.__stdout__.write(msg)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200439 sys.__stdout__.write('\n')
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200440 sys.__stdout__.flush()
Pau Espin Pedrol9a4631c2018-03-28 19:17:34 +0200441 entered = util.input_polling('> ', MainLoop.poll)
Neels Hofmeyracf0c932017-05-06 16:05:33 +0200442 self.log('prompt entered:', repr(entered))
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200443 return entered
444
Neels Hofmeyr2d1d5612017-05-22 20:02:41 +0200445 def resource_status_str(self):
446 return '\n'.join(('',
447 'SUITE RUN: %s' % self.origin_id(),
448 'ASKED FOR:', pprint.pformat(self._resource_requirements),
449 'RESERVED COUNT:', pprint.pformat(self.reserved_resources.counts()),
450 'RESOURCES STATE:', repr(self.reserved_resources)))
451
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200452loaded_suite_definitions = {}
453
454def load(suite_name):
455 global loaded_suite_definitions
456
457 suite = loaded_suite_definitions.get(suite_name)
458 if suite is not None:
459 return suite
460
461 suites_dir = config.get_suites_dir()
462 suite_dir = suites_dir.child(suite_name)
463 if not suites_dir.exists(suite_name):
464 raise RuntimeError('Suite not found: %r in %r' % (suite_name, suites_dir))
465 if not suites_dir.isdir(suite_name):
466 raise RuntimeError('Suite name found, but not a directory: %r' % (suite_dir))
467
468 suite_def = SuiteDefinition(suite_dir)
469 loaded_suite_definitions[suite_name] = suite_def
470 return suite_def
471
472def parse_suite_scenario_str(suite_scenario_str):
473 tokens = suite_scenario_str.split(':')
474 if len(tokens) > 2:
475 raise RuntimeError('invalid combination string: %r' % suite_scenario_str)
476
477 suite_name = tokens[0]
478 if len(tokens) <= 1:
479 scenario_names = []
480 else:
481 scenario_names = tokens[1].split('+')
482
483 return suite_name, scenario_names
484
485def load_suite_scenario_str(suite_scenario_str):
486 suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
487 suite = load(suite_name)
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +0200488 scenarios = [config.get_scenario(scenario_name, schema.get_all_schema()) for scenario_name in scenario_names]
Your Name44af3412017-04-13 03:11:59 +0200489 return (suite_scenario_str, suite, scenarios)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200490
Neels Hofmeyrdae3d3c2017-03-28 12:16:58 +0200491# vim: expandtab tabstop=4 shiftwidth=4