<p>pespin <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18042">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  pespin: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Move all obj/ references in suite.py to testenv.py<br><br>Change-Id: If4ab39be7a97d33e82c5a34e2a10dfec38613a4e<br>---<br>M selftest/suite_test/test_suite/hello_world.py<br>M selftest/suite_test/test_suite/test_error.py<br>M selftest/suite_test/test_suite/test_fail.py<br>M src/osmo_gsm_tester/core/test.py<br>M src/osmo_gsm_tester/suite.py<br>M src/osmo_gsm_tester/testenv.py<br>M src/osmo_gsm_tester/trial.py<br>7 files changed, 315 insertions(+), 271 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/selftest/suite_test/test_suite/hello_world.py b/selftest/suite_test/test_suite/hello_world.py</span><br><span>index 073d07f..a69f95a 100644</span><br><span>--- a/selftest/suite_test/test_suite/hello_world.py</span><br><span>+++ b/selftest/suite_test/test_suite/hello_world.py</span><br><span>@@ -1,5 +1,5 @@</span><br><span> from osmo_gsm_tester.testenv import *</span><br><span> </span><br><span> print('hello world')</span><br><span style="color: hsl(0, 100%, 40%);">-print('I am %r / %r' % (suite.name(), test.name()))</span><br><span style="color: hsl(120, 100%, 40%);">+print('I am %r / %r' % (suite.suite().name(), test.name()))</span><br><span> print('one\ntwo\nthree')</span><br><span>diff --git a/selftest/suite_test/test_suite/test_error.py b/selftest/suite_test/test_suite/test_error.py</span><br><span>index c0583ff..70d14a1 100755</span><br><span>--- a/selftest/suite_test/test_suite/test_error.py</span><br><span>+++ b/selftest/suite_test/test_suite/test_error.py</span><br><span>@@ -1,5 +1,5 @@</span><br><span> from osmo_gsm_tester.testenv import *</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-print('I am %r / %r' % (suite.name(), test.name()))</span><br><span style="color: hsl(120, 100%, 40%);">+print('I am %r / %r' % (suite.suite().name(), test.name()))</span><br><span> </span><br><span> assert False</span><br><span>diff --git a/selftest/suite_test/test_suite/test_fail.py b/selftest/suite_test/test_suite/test_fail.py</span><br><span>index cbaeded..ffb7218 100755</span><br><span>--- a/selftest/suite_test/test_suite/test_fail.py</span><br><span>+++ b/selftest/suite_test/test_suite/test_fail.py</span><br><span>@@ -1,6 +1,6 @@</span><br><span> #!/usr/bin/env python3</span><br><span> from osmo_gsm_tester.testenv import *</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-print('I am %r / %r' % (suite.name(), test.name()))</span><br><span style="color: hsl(120, 100%, 40%);">+print('I am %r / %r' % (suite.suite().name(), test.name()))</span><br><span> </span><br><span> test.set_fail('EpicFail', 'This failure is expected')</span><br><span>diff --git a/src/osmo_gsm_tester/core/test.py b/src/osmo_gsm_tester/core/test.py</span><br><span>index 93dbf6a..76c9ce9 100644</span><br><span>--- a/src/osmo_gsm_tester/core/test.py</span><br><span>+++ b/src/osmo_gsm_tester/core/test.py</span><br><span>@@ -21,9 +21,13 @@</span><br><span> import sys</span><br><span> import time</span><br><span> import traceback</span><br><span style="color: hsl(0, 100%, 40%);">-from .. import testenv</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from . import log, util, resource</span><br><span style="color: hsl(120, 100%, 40%);">+from . import log</span><br><span style="color: hsl(120, 100%, 40%);">+from . import  util</span><br><span style="color: hsl(120, 100%, 40%);">+from . import resource</span><br><span style="color: hsl(120, 100%, 40%);">+from .event_loop import MainLoop</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from .. import testenv</span><br><span> </span><br><span> class Test(log.Origin):</span><br><span>     UNKNOWN = 'UNKNOWN' # matches junit 'error'</span><br><span>@@ -51,12 +55,13 @@</span><br><span>         return self._run_dir</span><br><span> </span><br><span>     def run(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        testenv_obj = None</span><br><span>         try:</span><br><span>             self.log_target = log.FileLogTarget(self.get_run_dir().new_child('log')).set_all_levels(log.L_DBG).style_change(trace=True)</span><br><span>             log.large_separator(self.suite_run.trial.name(), self.suite_run.name(), self.name(), sublevel=3)</span><br><span>             self.status = Test.UNKNOWN</span><br><span>             self.start_timestamp = time.time()</span><br><span style="color: hsl(0, 100%, 40%);">-            testenv.setup(self.suite_run, self)</span><br><span style="color: hsl(120, 100%, 40%);">+            testenv_obj = testenv.setup(self.suite_run, self)</span><br><span>             with self.redirect_stdout():</span><br><span>                 util.run_python_file('%s.%s' % (self.suite_run.definition.name(), self.basename),</span><br><span>                                      self.path)</span><br><span>@@ -83,6 +88,8 @@</span><br><span>             self.err('TEST RUN ABORTED: %s' % type(e).__name__)</span><br><span>             raise</span><br><span>         finally:</span><br><span style="color: hsl(120, 100%, 40%);">+            if testenv_obj:</span><br><span style="color: hsl(120, 100%, 40%);">+                testenv_obj.stop()</span><br><span>             if self.log_target:</span><br><span>                 self.log_target.remove()</span><br><span> </span><br><span>diff --git a/src/osmo_gsm_tester/suite.py b/src/osmo_gsm_tester/suite.py</span><br><span>index 6952fd2..3416ff5 100644</span><br><span>--- a/src/osmo_gsm_tester/suite.py</span><br><span>+++ b/src/osmo_gsm_tester/suite.py</span><br><span>@@ -21,18 +21,12 @@</span><br><span> import sys</span><br><span> import time</span><br><span> import pprint</span><br><span style="color: hsl(0, 100%, 40%);">-from .core import config, log, util, process, schema, resource</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import config</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import log</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import util</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import schema</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import resource</span><br><span> from .core import test</span><br><span style="color: hsl(0, 100%, 40%);">-from .core.event_loop import MainLoop</span><br><span style="color: hsl(0, 100%, 40%);">-from .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</span><br><span style="color: hsl(0, 100%, 40%);">-from .obj import run_node</span><br><span style="color: hsl(0, 100%, 40%);">-from .obj import epc</span><br><span style="color: hsl(0, 100%, 40%);">-from .obj import enb</span><br><span style="color: hsl(0, 100%, 40%);">-from .obj import bts</span><br><span style="color: hsl(0, 100%, 40%);">-from .obj import ms</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-class Timeout(Exception):</span><br><span style="color: hsl(0, 100%, 40%);">-    pass</span><br><span> </span><br><span> class SuiteDefinition(log.Origin):</span><br><span>     '''A test suite reserves resources for a number of tests.</span><br><span>@@ -74,12 +68,9 @@</span><br><span>         self.start_timestamp = None</span><br><span>         self.duration = None</span><br><span>         self.reserved_resources = None</span><br><span style="color: hsl(0, 100%, 40%);">-        self.objects_to_clean_up = None</span><br><span style="color: hsl(0, 100%, 40%);">-        self.test_import_modules_to_clean_up = []</span><br><span>         self._resource_requirements = None</span><br><span>         self._resource_modifiers = None</span><br><span>         self._config = None</span><br><span style="color: hsl(0, 100%, 40%);">-        self._processes = []</span><br><span>         self._run_dir = None</span><br><span>         self.trial = trial</span><br><span>         self.definition = suite_definition</span><br><span>@@ -93,40 +84,6 @@</span><br><span>         for test_basename in self.definition.test_basenames:</span><br><span>             self.tests.append(test.Test(self, test_basename))</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def register_for_cleanup(self, *obj):</span><br><span style="color: hsl(0, 100%, 40%);">-        assert all([hasattr(o, 'cleanup') for o in obj])</span><br><span style="color: hsl(0, 100%, 40%);">-        self.objects_to_clean_up = self.objects_to_clean_up or []</span><br><span style="color: hsl(0, 100%, 40%);">-        self.objects_to_clean_up.extend(obj)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def objects_cleanup(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        while self.objects_to_clean_up:</span><br><span style="color: hsl(0, 100%, 40%);">-            obj = self.objects_to_clean_up.pop()</span><br><span style="color: hsl(0, 100%, 40%);">-            try:</span><br><span style="color: hsl(0, 100%, 40%);">-                obj.cleanup()</span><br><span style="color: hsl(0, 100%, 40%);">-            except Exception:</span><br><span style="color: hsl(0, 100%, 40%);">-                log.log_exn()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_import_modules_register_for_cleanup(self, mod):</span><br><span style="color: hsl(0, 100%, 40%);">-        '''</span><br><span style="color: hsl(0, 100%, 40%);">-        Tests are required to call this API for any module loaded from its own</span><br><span style="color: hsl(0, 100%, 40%);">-        lib subdir, because they are loaded in the global namespace. Otherwise</span><br><span style="color: hsl(0, 100%, 40%);">-        later tests importing modules with the same name will re-use an already</span><br><span style="color: hsl(0, 100%, 40%);">-        loaded module.</span><br><span style="color: hsl(0, 100%, 40%);">-        '''</span><br><span style="color: hsl(0, 100%, 40%);">-        if mod not in self.test_import_modules_to_clean_up:</span><br><span style="color: hsl(0, 100%, 40%);">-            self.dbg('registering module %r for cleanup' % mod)</span><br><span style="color: hsl(0, 100%, 40%);">-            self.test_import_modules_to_clean_up.append(mod)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def test_import_modules_cleanup(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        while self.test_import_modules_to_clean_up:</span><br><span style="color: hsl(0, 100%, 40%);">-            mod = self.test_import_modules_to_clean_up.pop()</span><br><span style="color: hsl(0, 100%, 40%);">-            try:</span><br><span style="color: hsl(0, 100%, 40%);">-                self.dbg('Cleaning up module %r' % mod)</span><br><span style="color: hsl(0, 100%, 40%);">-                del sys.modules[mod.__name__]</span><br><span style="color: hsl(0, 100%, 40%);">-                del mod</span><br><span style="color: hsl(0, 100%, 40%);">-            except Exception:</span><br><span style="color: hsl(0, 100%, 40%);">-                log.log_exn()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>     def mark_start(self):</span><br><span>         self.start_timestamp = time.time()</span><br><span>         self.duration = 0</span><br><span>@@ -155,11 +112,6 @@</span><br><span>             self._run_dir = util.Dir(self.trial.get_run_dir().new_dir(self.name()))</span><br><span>         return self._run_dir</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def get_test_run_dir(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        if self.current_test:</span><br><span style="color: hsl(0, 100%, 40%);">-            return self.current_test.get_run_dir()</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.get_run_dir()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>     def resource_requirements(self):</span><br><span>         if self._resource_requirements is None:</span><br><span>             self._resource_requirements = self.combined('resources')</span><br><span>@@ -175,19 +127,24 @@</span><br><span>             self._config = self.combined('config', False)</span><br><span>         return self._config</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    def resource_pool(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.resources_pool</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     def reserve_resources(self):</span><br><span>         if self.reserved_resources:</span><br><span>             raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')</span><br><span>         self.log('reserving resources in', self.resources_pool.state_dir, '...')</span><br><span>         self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements(), self.resource_modifiers())</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    def get_reserved_resource(self, resource_class_str, specifics):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.reserved_resources.get(resource_class_str, specifics=specifics)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     def run_tests(self, names=None):</span><br><span>         suite_libdir = os.path.join(self.definition.suite_dir, 'lib')</span><br><span>         try:</span><br><span>             log.large_separator(self.trial.name(), self.name(), sublevel=2)</span><br><span>             self.mark_start()</span><br><span>             util.import_path_prepend(suite_libdir)</span><br><span style="color: hsl(0, 100%, 40%);">-            MainLoop.register_poll_func(self.poll)</span><br><span>             if not self.reserved_resources:</span><br><span>                 self.reserve_resources()</span><br><span>             for t in self.tests:</span><br><span>@@ -196,9 +153,6 @@</span><br><span>                     continue</span><br><span>                 self.current_test = t</span><br><span>                 t.run()</span><br><span style="color: hsl(0, 100%, 40%);">-                self.stop_processes()</span><br><span style="color: hsl(0, 100%, 40%);">-                self.objects_cleanup()</span><br><span style="color: hsl(0, 100%, 40%);">-                self.reserved_resources.put_all()</span><br><span>         except Exception:</span><br><span>             log.log_exn()</span><br><span>         except BaseException as e:</span><br><span>@@ -206,14 +160,7 @@</span><br><span>             self.err('SUITE RUN ABORTED: %s' % type(e).__name__)</span><br><span>             raise</span><br><span>         finally:</span><br><span style="color: hsl(0, 100%, 40%);">-            # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit</span><br><span style="color: hsl(0, 100%, 40%);">-            # base exception is raised. Make sure to stop processes in this</span><br><span style="color: hsl(0, 100%, 40%);">-            # finally section. Resources are automatically freed with 'atexit'.</span><br><span style="color: hsl(0, 100%, 40%);">-            self.stop_processes()</span><br><span style="color: hsl(0, 100%, 40%);">-            self.objects_cleanup()</span><br><span>             self.free_resources()</span><br><span style="color: hsl(0, 100%, 40%);">-            MainLoop.unregister_poll_func(self.poll)</span><br><span style="color: hsl(0, 100%, 40%);">-            self.test_import_modules_cleanup()</span><br><span>             util.import_path_remove(suite_libdir)</span><br><span>             self.duration = time.time() - self.start_timestamp</span><br><span> </span><br><span>@@ -245,203 +192,11 @@</span><br><span>                 errors += 1</span><br><span>         return (passed, skipped, failed, errors)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def remember_to_stop(self, process, respawn=False):</span><br><span style="color: hsl(0, 100%, 40%);">-        '''Ask suite to monitor and manage lifecycle of the Process object. If a</span><br><span style="color: hsl(0, 100%, 40%);">-        process managed by suite finishes before cleanup time, the current test</span><br><span style="color: hsl(0, 100%, 40%);">-        will be marked as FAIL and end immediatelly. If respwan=True, then suite</span><br><span style="color: hsl(0, 100%, 40%);">-        will respawn() the process instead.'''</span><br><span style="color: hsl(0, 100%, 40%);">-        self._processes.insert(0, (process, respawn))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def stop_processes(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        if len(self._processes) == 0:</span><br><span style="color: hsl(0, 100%, 40%);">-            return</span><br><span style="color: hsl(0, 100%, 40%);">-        strategy = process.ParallelTerminationStrategy()</span><br><span style="color: hsl(0, 100%, 40%);">-        while self._processes:</span><br><span style="color: hsl(0, 100%, 40%);">-            proc, _ = self._processes.pop()</span><br><span style="color: hsl(0, 100%, 40%);">-            strategy.add_process(proc)</span><br><span style="color: hsl(0, 100%, 40%);">-        strategy.terminate_all()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def stop_process(self, process):</span><br><span style="color: hsl(0, 100%, 40%);">-        'Remove process from monitored list and stop it'</span><br><span style="color: hsl(0, 100%, 40%);">-        for proc_respawn in self._processes:</span><br><span style="color: hsl(0, 100%, 40%);">-            proc, respawn = proc_respawn</span><br><span style="color: hsl(0, 100%, 40%);">-            if proc == process:</span><br><span style="color: hsl(0, 100%, 40%);">-                self._processes.remove(proc_respawn)</span><br><span style="color: hsl(0, 100%, 40%);">-                proc.terminate()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>     def free_resources(self):</span><br><span>         if self.reserved_resources is None:</span><br><span>             return</span><br><span>         self.reserved_resources.free()</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    def ip_address(self, specifics=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.reserved_resources.get(resource.R_IP_ADDRESS, specifics=specifics)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def nitb(self, ip_address=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        return nitb_osmo.OsmoNitb(self, ip_address)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def hlr(self, ip_address=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        return hlr_osmo.OsmoHlr(self, ip_address)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def ggsn(self, ip_address=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        return ggsn_osmo.OsmoGgsn(self, ip_address)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def sgsn(self, hlr, ggsn, ip_address=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        return sgsn_osmo.OsmoSgsn(self, hlr, ggsn, ip_address)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def mgcpgw(self, ip_address=None, bts_ip=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        return mgcpgw_osmo.OsmoMgcpgw(self, ip_address, bts_ip)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def mgw(self, ip_address=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        return mgw_osmo.OsmoMgw(self, ip_address)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def msc(self, hlr, mgcpgw, stp, ip_address=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        return msc_osmo.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def bsc(self, msc, mgw, stp, ip_address=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        return bsc_osmo.OsmoBsc(self, msc, mgw, stp, ip_address)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def stp(self, ip_address=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        return stp_osmo.OsmoStp(self, ip_address)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def ms_driver(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        ms = ms_driver.MsDriver(self)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.register_for_cleanup(ms)</span><br><span style="color: hsl(0, 100%, 40%);">-        return ms</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def bts(self, specifics=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        bts_obj = bts.Bts.get_instance_by_type(self, self.reserved_resources.get(resource.R_BTS, specifics=specifics))</span><br><span style="color: hsl(0, 100%, 40%);">-        bts_obj.set_lac(self.lac())</span><br><span style="color: hsl(0, 100%, 40%);">-        bts_obj.set_rac(self.rac())</span><br><span style="color: hsl(0, 100%, 40%);">-        bts_obj.set_cellid(self.cellid())</span><br><span style="color: hsl(0, 100%, 40%);">-        bts_obj.set_bvci(self.bvci())</span><br><span style="color: hsl(0, 100%, 40%);">-        self.register_for_cleanup(bts_obj)</span><br><span style="color: hsl(0, 100%, 40%);">-        return bts_obj</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def modem(self, specifics=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        conf = self.reserved_resources.get(resource.R_MODEM, specifics=specifics)</span><br><span style="color: hsl(0, 100%, 40%);">-        ms_obj = ms.MS.get_instance_by_type(self, conf)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.register_for_cleanup(ms_obj)</span><br><span style="color: hsl(0, 100%, 40%);">-        return ms_obj</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def modems(self, count):</span><br><span style="color: hsl(0, 100%, 40%);">-        l = []</span><br><span style="color: hsl(0, 100%, 40%);">-        for i in range(count):</span><br><span style="color: hsl(0, 100%, 40%);">-            l.append(self.modem())</span><br><span style="color: hsl(0, 100%, 40%);">-        return l</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def all_resources(self, resource_func):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Returns all yielded resource."""</span><br><span style="color: hsl(0, 100%, 40%);">-        l = []</span><br><span style="color: hsl(0, 100%, 40%);">-        while True:</span><br><span style="color: hsl(0, 100%, 40%);">-            try:</span><br><span style="color: hsl(0, 100%, 40%);">-                l.append(resource_func())</span><br><span style="color: hsl(0, 100%, 40%);">-            except resource.NoResourceExn:</span><br><span style="color: hsl(0, 100%, 40%);">-                return l</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def esme(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        esme_obj = esme.Esme(self.msisdn())</span><br><span style="color: hsl(0, 100%, 40%);">-        self.register_for_cleanup(esme_obj)</span><br><span style="color: hsl(0, 100%, 40%);">-        return esme_obj</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def run_node(self, specifics=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        return run_node.RunNode.from_conf(self.reserved_resources.get(resource.R_RUN_NODE, specifics=specifics))</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def enb(self, specifics=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        enb_obj = enb.eNodeB.get_instance_by_type(self, self.reserved_resources.get(resource.R_ENB, specifics=specifics))</span><br><span style="color: hsl(0, 100%, 40%);">-        self.register_for_cleanup(enb_obj)</span><br><span style="color: hsl(0, 100%, 40%);">-        return enb_obj</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def epc(self, run_node=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if run_node is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            run_node = self.run_node()</span><br><span style="color: hsl(0, 100%, 40%);">-        epc_obj = epc.EPC.get_instance_by_type(self, run_node)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.register_for_cleanup(epc_obj)</span><br><span style="color: hsl(0, 100%, 40%);">-        return epc_obj</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def osmocon(self, specifics=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        conf = self.reserved_resources.get(resource.R_OSMOCON, specifics=specifics)</span><br><span style="color: hsl(0, 100%, 40%);">-        osmocon_obj = osmocon.Osmocon(self, conf=conf)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.register_for_cleanup(osmocon_obj)</span><br><span style="color: hsl(0, 100%, 40%);">-        return osmocon_obj</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def iperf3srv(self, ip_address=None):</span><br><span style="color: hsl(0, 100%, 40%);">-        if ip_address is None:</span><br><span style="color: hsl(0, 100%, 40%);">-            ip_address = self.ip_address()</span><br><span style="color: hsl(0, 100%, 40%);">-        iperf3srv_obj = iperf3.IPerf3Server(self, ip_address)</span><br><span style="color: hsl(0, 100%, 40%);">-        return iperf3srv_obj</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def msisdn(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        msisdn = self.resources_pool.next_msisdn(self)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.log('using MSISDN', msisdn)</span><br><span style="color: hsl(0, 100%, 40%);">-        return msisdn</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def lac(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        lac = self.resources_pool.next_lac(self)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.log('using LAC', lac)</span><br><span style="color: hsl(0, 100%, 40%);">-        return lac</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def rac(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        rac = self.resources_pool.next_rac(self)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.log('using RAC', rac)</span><br><span style="color: hsl(0, 100%, 40%);">-        return rac</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def cellid(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        cellid = self.resources_pool.next_cellid(self)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.log('using CellId', cellid)</span><br><span style="color: hsl(0, 100%, 40%);">-        return cellid</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def bvci(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        bvci = self.resources_pool.next_bvci(self)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.log('using BVCI', bvci)</span><br><span style="color: hsl(0, 100%, 40%);">-        return bvci</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def poll(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        for proc, respawn in self._processes:</span><br><span style="color: hsl(0, 100%, 40%);">-            if proc.terminated():</span><br><span style="color: hsl(0, 100%, 40%);">-                if respawn == True:</span><br><span style="color: hsl(0, 100%, 40%);">-                    proc.respawn()</span><br><span style="color: hsl(0, 100%, 40%);">-                else:</span><br><span style="color: hsl(0, 100%, 40%);">-                    proc.log_stdout_tail()</span><br><span style="color: hsl(0, 100%, 40%);">-                    proc.log_stderr_tail()</span><br><span style="color: hsl(0, 100%, 40%);">-                    log.ctx(proc)</span><br><span style="color: hsl(0, 100%, 40%);">-                    raise log.Error('Process ended prematurely: %s' % proc.name())</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def prompt(self, *msgs, **msg_details):</span><br><span style="color: hsl(0, 100%, 40%);">-        'ask for user interaction. Do not use in tests that should run automatically!'</span><br><span style="color: hsl(0, 100%, 40%);">-        if msg_details:</span><br><span style="color: hsl(0, 100%, 40%);">-            msgs = list(msgs)</span><br><span style="color: hsl(0, 100%, 40%);">-            msgs.append('{%s}' %</span><br><span style="color: hsl(0, 100%, 40%);">-                        (', '.join(['%s=%r' % (k,v)</span><br><span style="color: hsl(0, 100%, 40%);">-                                    for k,v in sorted(msg_details.items())])))</span><br><span style="color: hsl(0, 100%, 40%);">-        msg = ' '.join(msgs) or 'Hit Enter to continue'</span><br><span style="color: hsl(0, 100%, 40%);">-        self.log('prompt:', msg)</span><br><span style="color: hsl(0, 100%, 40%);">-        sys.__stdout__.write('\n\n--- PROMPT ---\n')</span><br><span style="color: hsl(0, 100%, 40%);">-        sys.__stdout__.write(msg)</span><br><span style="color: hsl(0, 100%, 40%);">-        sys.__stdout__.write('\n')</span><br><span style="color: hsl(0, 100%, 40%);">-        sys.__stdout__.flush()</span><br><span style="color: hsl(0, 100%, 40%);">-        entered = util.input_polling('> ', MainLoop.poll)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.log('prompt entered:', repr(entered))</span><br><span style="color: hsl(0, 100%, 40%);">-        return entered</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>     def resource_status_str(self):</span><br><span>         return '\n'.join(('',</span><br><span>             'SUITE RUN: %s' % self.origin_id(),</span><br><span>diff --git a/src/osmo_gsm_tester/testenv.py b/src/osmo_gsm_tester/testenv.py</span><br><span>index 8c4743a..aa94f3c 100644</span><br><span>--- a/src/osmo_gsm_tester/testenv.py</span><br><span>+++ b/src/osmo_gsm_tester/testenv.py</span><br><span>@@ -20,6 +20,22 @@</span><br><span> # These will be initialized before each test run.</span><br><span> # A test script can thus establish its context by doing:</span><br><span> # from osmo_gsm_tester.testenv import *</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import sys</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import process</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import log as log_module</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import process as process_module</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import resource</span><br><span style="color: hsl(120, 100%, 40%);">+from .core.event_loop import MainLoop</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from .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</span><br><span style="color: hsl(120, 100%, 40%);">+from .obj import run_node</span><br><span style="color: hsl(120, 100%, 40%);">+from .obj import epc</span><br><span style="color: hsl(120, 100%, 40%);">+from .obj import enb</span><br><span style="color: hsl(120, 100%, 40%);">+from .obj import bts</span><br><span style="color: hsl(120, 100%, 40%);">+from .obj import ms</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> trial = None</span><br><span> suite = None</span><br><span> test = None</span><br><span>@@ -32,22 +48,284 @@</span><br><span> sleep = None</span><br><span> poll = None</span><br><span> prompt = None</span><br><span style="color: hsl(0, 100%, 40%);">-Timeout = None</span><br><span> Sms = None</span><br><span> process = None</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+class Timeout(Exception):</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TestEnv(log_module.Origin):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, suite_run, test):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(log_module.C_TST, test.name())</span><br><span style="color: hsl(120, 100%, 40%);">+        self.suite_run = suite_run</span><br><span style="color: hsl(120, 100%, 40%);">+        self._test = test</span><br><span style="color: hsl(120, 100%, 40%);">+        self.trial = suite_run.trial # backward compat with objects</span><br><span style="color: hsl(120, 100%, 40%);">+        self.resources_pool = suite_run.resource_pool() # backward compat with objects</span><br><span style="color: hsl(120, 100%, 40%);">+        self._processes = []</span><br><span style="color: hsl(120, 100%, 40%);">+        self.test_import_modules_to_clean_up = []</span><br><span style="color: hsl(120, 100%, 40%);">+        self.objects_to_clean_up = None</span><br><span style="color: hsl(120, 100%, 40%);">+        MainLoop.register_poll_func(self.poll)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def suite(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.suite_run</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # backward compat with objects</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_test_run_dir(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self._test.get_run_dir()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # backward compat with objects</span><br><span style="color: hsl(120, 100%, 40%);">+    def config(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.suite_run.config()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def remember_to_stop(self, process, respawn=False):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Ask suite to monitor and manage lifecycle of the Process object. If a</span><br><span style="color: hsl(120, 100%, 40%);">+        process managed by suite finishes before cleanup time, the current test</span><br><span style="color: hsl(120, 100%, 40%);">+        will be marked as FAIL and end immediatelly. If respwan=True, then suite</span><br><span style="color: hsl(120, 100%, 40%);">+        will respawn() the process instead.'''</span><br><span style="color: hsl(120, 100%, 40%);">+        self._processes.insert(0, (process, respawn))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def stop_processes(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        if len(self._processes) == 0:</span><br><span style="color: hsl(120, 100%, 40%);">+            return</span><br><span style="color: hsl(120, 100%, 40%);">+        strategy = process_module.ParallelTerminationStrategy()</span><br><span style="color: hsl(120, 100%, 40%);">+        while self._processes:</span><br><span style="color: hsl(120, 100%, 40%);">+            proc, _ = self._processes.pop()</span><br><span style="color: hsl(120, 100%, 40%);">+            strategy.add_process(proc)</span><br><span style="color: hsl(120, 100%, 40%);">+        strategy.terminate_all()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def stop_process(self, process):</span><br><span style="color: hsl(120, 100%, 40%);">+        'Remove process from monitored list and stop it'</span><br><span style="color: hsl(120, 100%, 40%);">+        for proc_respawn in self._processes:</span><br><span style="color: hsl(120, 100%, 40%);">+            proc, respawn = proc_respawn</span><br><span style="color: hsl(120, 100%, 40%);">+            if proc == process:</span><br><span style="color: hsl(120, 100%, 40%);">+                self._processes.remove(proc_respawn)</span><br><span style="color: hsl(120, 100%, 40%);">+                proc.terminate()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def register_for_cleanup(self, *obj):</span><br><span style="color: hsl(120, 100%, 40%);">+        assert all([hasattr(o, 'cleanup') for o in obj])</span><br><span style="color: hsl(120, 100%, 40%);">+        self.objects_to_clean_up = self.objects_to_clean_up or []</span><br><span style="color: hsl(120, 100%, 40%);">+        self.objects_to_clean_up.extend(obj)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def objects_cleanup(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        while self.objects_to_clean_up:</span><br><span style="color: hsl(120, 100%, 40%);">+            obj = self.objects_to_clean_up.pop()</span><br><span style="color: hsl(120, 100%, 40%);">+            try:</span><br><span style="color: hsl(120, 100%, 40%);">+                obj.cleanup()</span><br><span style="color: hsl(120, 100%, 40%);">+            except Exception:</span><br><span style="color: hsl(120, 100%, 40%);">+                log_module.log_exn()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_import_modules_register_for_cleanup(self, mod):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''</span><br><span style="color: hsl(120, 100%, 40%);">+        Tests are required to call this API for any module loaded from its own</span><br><span style="color: hsl(120, 100%, 40%);">+        lib subdir, because they are loaded in the global namespace. Otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+        later tests importing modules with the same name will re-use an already</span><br><span style="color: hsl(120, 100%, 40%);">+        loaded module.</span><br><span style="color: hsl(120, 100%, 40%);">+        '''</span><br><span style="color: hsl(120, 100%, 40%);">+        if mod not in self.test_import_modules_to_clean_up:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.dbg('registering module %r for cleanup' % mod)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.test_import_modules_to_clean_up.append(mod)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def test_import_modules_cleanup(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        while self.test_import_modules_to_clean_up:</span><br><span style="color: hsl(120, 100%, 40%);">+            mod = self.test_import_modules_to_clean_up.pop()</span><br><span style="color: hsl(120, 100%, 40%);">+            try:</span><br><span style="color: hsl(120, 100%, 40%);">+                self.dbg('Cleaning up module %r' % mod)</span><br><span style="color: hsl(120, 100%, 40%);">+                del sys.modules[mod.__name__]</span><br><span style="color: hsl(120, 100%, 40%);">+                del mod</span><br><span style="color: hsl(120, 100%, 40%);">+            except Exception:</span><br><span style="color: hsl(120, 100%, 40%);">+                log_module.log_exn()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def poll(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        for proc, respawn in self._processes:</span><br><span style="color: hsl(120, 100%, 40%);">+            if proc.terminated():</span><br><span style="color: hsl(120, 100%, 40%);">+                if respawn == True:</span><br><span style="color: hsl(120, 100%, 40%);">+                    proc.respawn()</span><br><span style="color: hsl(120, 100%, 40%);">+                else:</span><br><span style="color: hsl(120, 100%, 40%);">+                    proc.log_stdout_tail()</span><br><span style="color: hsl(120, 100%, 40%);">+                    proc.log_stderr_tail()</span><br><span style="color: hsl(120, 100%, 40%);">+                    log_module.ctx(proc)</span><br><span style="color: hsl(120, 100%, 40%);">+                    raise log_module.Error('Process ended prematurely: %s' % proc.name())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def stop(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        # if sys.exit() called from signal handler (e.g. SIGINT), SystemExit</span><br><span style="color: hsl(120, 100%, 40%);">+        # base exception is raised. Make sure to stop processes in this</span><br><span style="color: hsl(120, 100%, 40%);">+        # finally section. Resources are automatically freed with 'atexit'.</span><br><span style="color: hsl(120, 100%, 40%);">+        self.stop_processes()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.objects_cleanup()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.suite_run.reserved_resources.put_all()</span><br><span style="color: hsl(120, 100%, 40%);">+        MainLoop.unregister_poll_func(self.poll)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.test_import_modules_cleanup()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def prompt(self, *msgs, **msg_details):</span><br><span style="color: hsl(120, 100%, 40%);">+        'ask for user interaction. Do not use in tests that should run automatically!'</span><br><span style="color: hsl(120, 100%, 40%);">+        if msg_details:</span><br><span style="color: hsl(120, 100%, 40%);">+            msgs = list(msgs)</span><br><span style="color: hsl(120, 100%, 40%);">+            msgs.append('{%s}' %</span><br><span style="color: hsl(120, 100%, 40%);">+                        (', '.join(['%s=%r' % (k,v)</span><br><span style="color: hsl(120, 100%, 40%);">+                                    for k,v in sorted(msg_details.items())])))</span><br><span style="color: hsl(120, 100%, 40%);">+        msg = ' '.join(msgs) or 'Hit Enter to continue'</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log('prompt:', msg)</span><br><span style="color: hsl(120, 100%, 40%);">+        sys.__stdout__.write('\n\n--- PROMPT ---\n')</span><br><span style="color: hsl(120, 100%, 40%);">+        sys.__stdout__.write(msg)</span><br><span style="color: hsl(120, 100%, 40%);">+        sys.__stdout__.write('\n')</span><br><span style="color: hsl(120, 100%, 40%);">+        sys.__stdout__.flush()</span><br><span style="color: hsl(120, 100%, 40%);">+        entered = util.input_polling('> ', MainLoop.poll)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log('prompt entered:', repr(entered))</span><br><span style="color: hsl(120, 100%, 40%);">+        return entered</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_reserved_resource(self, resource_class_str, specifics=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.suite_run.get_reserved_resource(resource_class_str, specifics)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def ip_address(self, specifics=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.get_reserved_resource(resource.R_IP_ADDRESS, specifics)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def nitb(self, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        return nitb_osmo.OsmoNitb(self, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def hlr(self, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        return hlr_osmo.OsmoHlr(self, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def ggsn(self, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        return ggsn_osmo.OsmoGgsn(self, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def sgsn(self, hlr, ggsn, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        return sgsn_osmo.OsmoSgsn(self, hlr, ggsn, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def mgcpgw(self, ip_address=None, bts_ip=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        return mgcpgw_osmo.OsmoMgcpgw(self, ip_address, bts_ip)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def mgw(self, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        return mgw_osmo.OsmoMgw(self, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def msc(self, hlr, mgcpgw, stp, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        return msc_osmo.OsmoMsc(self, hlr, mgcpgw, stp, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def bsc(self, msc, mgw, stp, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        return bsc_osmo.OsmoBsc(self, msc, mgw, stp, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def stp(self, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        return stp_osmo.OsmoStp(self, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def ms_driver(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        ms = ms_driver.MsDriver(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.register_for_cleanup(ms)</span><br><span style="color: hsl(120, 100%, 40%);">+        return ms</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def bts(self, specifics=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        bts_obj = bts.Bts.get_instance_by_type(self, self.get_reserved_resource(resource.R_BTS, specifics=specifics))</span><br><span style="color: hsl(120, 100%, 40%);">+        bts_obj.set_lac(self.lac())</span><br><span style="color: hsl(120, 100%, 40%);">+        bts_obj.set_rac(self.rac())</span><br><span style="color: hsl(120, 100%, 40%);">+        bts_obj.set_cellid(self.cellid())</span><br><span style="color: hsl(120, 100%, 40%);">+        bts_obj.set_bvci(self.bvci())</span><br><span style="color: hsl(120, 100%, 40%);">+        self.register_for_cleanup(bts_obj)</span><br><span style="color: hsl(120, 100%, 40%);">+        return bts_obj</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def modem(self, specifics=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        conf = self.get_reserved_resource(resource.R_MODEM, specifics=specifics)</span><br><span style="color: hsl(120, 100%, 40%);">+        ms_obj = ms.MS.get_instance_by_type(self, conf)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.register_for_cleanup(ms_obj)</span><br><span style="color: hsl(120, 100%, 40%);">+        return ms_obj</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def modems(self, count):</span><br><span style="color: hsl(120, 100%, 40%);">+        l = []</span><br><span style="color: hsl(120, 100%, 40%);">+        for i in range(count):</span><br><span style="color: hsl(120, 100%, 40%);">+            l.append(self.modem())</span><br><span style="color: hsl(120, 100%, 40%);">+        return l</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def all_resources(self, resource_func):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Returns all yielded resource."""</span><br><span style="color: hsl(120, 100%, 40%);">+        l = []</span><br><span style="color: hsl(120, 100%, 40%);">+        while True:</span><br><span style="color: hsl(120, 100%, 40%);">+            try:</span><br><span style="color: hsl(120, 100%, 40%);">+                l.append(resource_func())</span><br><span style="color: hsl(120, 100%, 40%);">+            except resource.NoResourceExn:</span><br><span style="color: hsl(120, 100%, 40%);">+                return l</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def esme(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        esme_obj = esme.Esme(self.msisdn())</span><br><span style="color: hsl(120, 100%, 40%);">+        self.register_for_cleanup(esme_obj)</span><br><span style="color: hsl(120, 100%, 40%);">+        return esme_obj</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def run_node(self, specifics=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        return run_node.RunNode.from_conf(self.get_reserved_resource(resource.R_RUN_NODE, specifics=specifics))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def enb(self, specifics=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        enb_obj = enb.eNodeB.get_instance_by_type(self, self.get_reserved_resource(resource.R_ENB, specifics=specifics))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.register_for_cleanup(enb_obj)</span><br><span style="color: hsl(120, 100%, 40%);">+        return enb_obj</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def epc(self, run_node=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if run_node is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            run_node = self.run_node()</span><br><span style="color: hsl(120, 100%, 40%);">+        epc_obj = epc.EPC.get_instance_by_type(self, run_node)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.register_for_cleanup(epc_obj)</span><br><span style="color: hsl(120, 100%, 40%);">+        return epc_obj</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def osmocon(self, specifics=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        conf = self.get_reserved_resource(resource.R_OSMOCON, specifics=specifics)</span><br><span style="color: hsl(120, 100%, 40%);">+        osmocon_obj = osmocon.Osmocon(self, conf=conf)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.register_for_cleanup(osmocon_obj)</span><br><span style="color: hsl(120, 100%, 40%);">+        return osmocon_obj</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def iperf3srv(self, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        iperf3srv_obj = iperf3.IPerf3Server(self, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+        return iperf3srv_obj</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def msisdn(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        msisdn = self.suite_run.resource_pool().next_msisdn(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log('using MSISDN', msisdn)</span><br><span style="color: hsl(120, 100%, 40%);">+        return msisdn</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def lac(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        lac = self.suite_run.resource_pool().next_lac(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log('using LAC', lac)</span><br><span style="color: hsl(120, 100%, 40%);">+        return lac</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def rac(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        rac = self.suite_run.resource_pool().next_rac(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log('using RAC', rac)</span><br><span style="color: hsl(120, 100%, 40%);">+        return rac</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def cellid(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        cellid = self.suite_run.resource_pool().next_cellid(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log('using CellId', cellid)</span><br><span style="color: hsl(120, 100%, 40%);">+        return cellid</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def bvci(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        bvci = self.suite_run.resource_pool().next_bvci(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log('using BVCI', bvci)</span><br><span style="color: hsl(120, 100%, 40%);">+        return bvci</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def setup(suite_run, _test):</span><br><span style="color: hsl(0, 100%, 40%);">-    from .core import process as process_module</span><br><span>     from .core.event_loop import MainLoop</span><br><span>     from .obj.sms import Sms as Sms_class</span><br><span style="color: hsl(0, 100%, 40%);">-    from . import suite as suite_module</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    global trial, suite, test, resources, log, dbg, err, wait, wait_no_raise, sleep, poll, prompt, Timeout, Sms, process</span><br><span style="color: hsl(120, 100%, 40%);">+    global trial, suite, test, resources, log, dbg, err, wait, wait_no_raise, sleep, poll, prompt, Sms, process</span><br><span> </span><br><span>     trial = suite_run.trial</span><br><span style="color: hsl(0, 100%, 40%);">-    suite = suite_run</span><br><span>     test = _test</span><br><span style="color: hsl(0, 100%, 40%);">-    resources = suite_run.reserved_resources</span><br><span style="color: hsl(120, 100%, 40%);">+    resources = suite_run.reserved_resources # TODO: remove this global, only used in selftest</span><br><span>     log = test.log</span><br><span>     dbg = test.dbg</span><br><span>     err = test.err</span><br><span>@@ -55,9 +333,10 @@</span><br><span>     wait_no_raise = lambda *args, **kwargs: MainLoop.wait_no_raise(suite_run, *args, **kwargs)</span><br><span>     sleep = lambda *args, **kwargs: MainLoop.sleep(suite_run, *args, **kwargs)</span><br><span>     poll = MainLoop.poll</span><br><span style="color: hsl(0, 100%, 40%);">-    prompt = suite_run.prompt</span><br><span style="color: hsl(0, 100%, 40%);">-    Timeout = suite_module.Timeout</span><br><span>     Sms = Sms_class</span><br><span>     process = process_module</span><br><span style="color: hsl(120, 100%, 40%);">+    suite = TestEnv(suite_run, _test) # stored in "suite" for backward compatibility</span><br><span style="color: hsl(120, 100%, 40%);">+    prompt = suite.prompt</span><br><span style="color: hsl(120, 100%, 40%);">+    return suite</span><br><span> </span><br><span> # vim: expandtab tabstop=4 shiftwidth=4</span><br><span>diff --git a/src/osmo_gsm_tester/trial.py b/src/osmo_gsm_tester/trial.py</span><br><span>index fb94a59..08b7d8f 100644</span><br><span>--- a/src/osmo_gsm_tester/trial.py</span><br><span>+++ b/src/osmo_gsm_tester/trial.py</span><br><span>@@ -22,7 +22,10 @@</span><br><span> import shutil</span><br><span> import tarfile</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-from .core import log, util, report</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import log</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import util</span><br><span style="color: hsl(120, 100%, 40%);">+from .core import report</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> from . import suite</span><br><span> </span><br><span> FILE_MARK_TAKEN = 'taken'</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18042">change 18042</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18042"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-gsm-tester </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: If4ab39be7a97d33e82c5a34e2a10dfec38613a4e </div>
<div style="display:none"> Gerrit-Change-Number: 18042 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>