<p>pespin has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/21089">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">resource: Support waiting for reserved resources until available<br><br>Before this patch, almost everything was in place to support concurrent<br>osmo-gsm-tester instances sharing a common state dir. However, during<br>resource reservation, if the reservation couldn't be done due to too<br>many resources being in use, osmo-gsm-tester would fail and skip the<br>test suite.<br>With this patch, OGT will wait until some reserved resources are<br>released and then try requesting the reservation again.<br><br>Change-Id: I938602ee890712fda82fd3f812d8edb1bcd05e08<br>---<br>M doc/manuals/chapters/install.adoc<br>M selftest/resource_test/resource_test.ok<br>M selftest/resource_test/resource_test.py<br>M selftest/suite_test/suite_test.ok<br>M src/osmo_gsm_tester/core/resource.py<br>M src/osmo_gsm_tester/core/util.py<br>6 files changed, 470 insertions(+), 11 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-gsm-tester refs/changes/89/21089/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/doc/manuals/chapters/install.adoc b/doc/manuals/chapters/install.adoc</span><br><span>index 0e48e9d..c94c596 100644</span><br><span>--- a/doc/manuals/chapters/install.adoc</span><br><span>+++ b/doc/manuals/chapters/install.adoc</span><br><span>@@ -340,6 +340,7 @@</span><br><span>         python3-yaml \</span><br><span>         python3-mako \</span><br><span>         python3-gi \</span><br><span style="color: hsl(120, 100%, 40%);">+        python3-watchdog \</span><br><span>         locales</span><br><span> ----</span><br><span> </span><br><span>diff --git a/selftest/resource_test/resource_test.ok b/selftest/resource_test/resource_test.ok</span><br><span>index 91acaaf..5f7d5f6 100644</span><br><span>--- a/selftest/resource_test/resource_test.ok</span><br><span>+++ b/selftest/resource_test/resource_test.ok</span><br><span>@@ -116,6 +116,59 @@</span><br><span> *** end: all resources</span><br><span> </span><br><span> - request some resources</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: Verifying 2 x arfcn (candidates: 10)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: DBG: Picked - _hash: e620569450f8259b3f0212ec19c285dd07df063c</span><br><span style="color: hsl(120, 100%, 40%);">+  arfcn: '512'</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: 022621e513c5a5bf33b77430a1e9c886be676fa1</span><br><span style="color: hsl(120, 100%, 40%);">+  arfcn: '514'</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: Verifying 2 x bts (candidates: 3)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: DBG: Picked - _hash: d2aa7c1124943de352351b650ca0c751784da6b6</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.114</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ciphers:</span><br><span style="color: hsl(120, 100%, 40%);">+  - a5_0</span><br><span style="color: hsl(120, 100%, 40%);">+  - a5_1</span><br><span style="color: hsl(120, 100%, 40%);">+  direct_pcu: 'True'</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoBTS 1002</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-sysmo</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: 2158317d5e0055070e7174c2498dedf53a2957e9</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.50</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ciphers:</span><br><span style="color: hsl(120, 100%, 40%);">+  - a5_0</span><br><span style="color: hsl(120, 100%, 40%);">+  - a5_1</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '6'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: Ettus B200</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'True'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: Verifying 1 x ip_address (candidates: 5)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: DBG: Picked - _hash: fd103b22c7cf2480d609150e06f4bbd92ac78d8c</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.2</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: Verifying 2 x modem (candidates: 4)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: DBG: Picked - _hash: 0b538cb6ad799fbd7c2953fd3b4463a76c7cc9c0</span><br><span style="color: hsl(120, 100%, 40%);">+  auth_algo: comp128v1</span><br><span style="color: hsl(120, 100%, 40%);">+  ciphers:</span><br><span style="color: hsl(120, 100%, 40%);">+  - a5_0</span><br><span style="color: hsl(120, 100%, 40%);">+  - a5_1</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000009031'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: 80A37E6FDEA931EAC92FFA5F671EFEAD</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sierra_1</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /sierra_1</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: 3a6e7747dfe7dfdf817bd3351031bd08051605c3</span><br><span style="color: hsl(120, 100%, 40%);">+  auth_algo: comp128v1</span><br><span style="color: hsl(120, 100%, 40%);">+  ciphers:</span><br><span style="color: hsl(120, 100%, 40%);">+  - a5_0</span><br><span style="color: hsl(120, 100%, 40%);">+  - a5_1</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000009029'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: 00969E283349D354A8239E877F2E0866</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sierra_2</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /sierra_2</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: DBG: FileWatch: scheduling watch for directory [PATH]/selftest/resource_test/conf/test_work/state_dir</span><br><span> --- testowner: Reserving 2 x arfcn (candidates: 10)</span><br><span> --- testowner: DBG: Picked - _hash: e620569450f8259b3f0212ec19c285dd07df063c</span><br><span>   arfcn: '512'</span><br><span>@@ -168,6 +221,7 @@</span><br><span>   ki: 00969E283349D354A8239E877F2E0866</span><br><span>   label: sierra_2</span><br><span>   path: /sierra_2</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner: DBG: FileWatch: unscheduling watch <ObservedWatch: path=[PATH]/selftest/resource_test/conf/test_work/state_dir, is_recursive=False></span><br><span> ~~~ currently reserved:</span><br><span> arfcn:</span><br><span> - _hash: e620569450f8259b3f0212ec19c285dd07df063c</span><br><span>@@ -290,3 +344,27 @@</span><br><span> 3rd subset should not match, pass</span><br><span> 3rd subset should not match, pass</span><br><span> 4th subset should not match, pass</span><br><span style="color: hsl(120, 100%, 40%);">+*** concurrent allocation:</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner1: Verifying 2 x arfcn (candidates: 10)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner1: Verifying 2 x bts (candidates: 3)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner1: Verifying 1 x ip_address (candidates: 5)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner1: Verifying 2 x modem (candidates: 4)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner1: Reserving 2 x arfcn (candidates: 10)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner1: Reserving 2 x bts (candidates: 3)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner1: Reserving 1 x ip_address (candidates: 5)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner1: Reserving 2 x modem (candidates: 4)</span><br><span style="color: hsl(120, 100%, 40%);">+- 2nd instance reserve() start</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Verifying 2 x arfcn (candidates: 10)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Verifying 2 x bts (candidates: 3)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Verifying 1 x ip_address (candidates: 5)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Verifying 2 x modem (candidates: 4)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Reserving 2 x arfcn (candidates: 8)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Reserving 2 x bts (candidates: 1)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Unable to reserve resources, too many currently reserved. Waiting until some are available again</span><br><span style="color: hsl(120, 100%, 40%);">+- 1st instance free()</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Reserving 2 x arfcn (candidates: 10)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Reserving 2 x bts (candidates: 3)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Reserving 1 x ip_address (candidates: 5)</span><br><span style="color: hsl(120, 100%, 40%);">+--- testowner2: Reserving 2 x modem (candidates: 4)</span><br><span style="color: hsl(120, 100%, 40%);">+- 2nd instance reserve() done</span><br><span style="color: hsl(120, 100%, 40%);">+*** end: concurrent allocation</span><br><span>diff --git a/selftest/resource_test/resource_test.py b/selftest/resource_test/resource_test.py</span><br><span>index f18aa73..13cce97 100755</span><br><span>--- a/selftest/resource_test/resource_test.py</span><br><span>+++ b/selftest/resource_test/resource_test.py</span><br><span>@@ -6,6 +6,8 @@</span><br><span> import pprint</span><br><span> import shutil</span><br><span> import atexit</span><br><span style="color: hsl(120, 100%, 40%);">+import time</span><br><span style="color: hsl(120, 100%, 40%);">+import threading</span><br><span> import _prep</span><br><span> from osmo_gsm_tester.core import config, log, util, resource</span><br><span> from osmo_gsm_tester.core.schema import generate_schemas</span><br><span>@@ -128,4 +130,26 @@</span><br><span> if not resource.item_matches(superset, subset):</span><br><span>     print('4th subset should not match, pass')</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+print('*** concurrent allocation:')</span><br><span style="color: hsl(120, 100%, 40%);">+origin1 = log.Origin(None, 'testowner1')</span><br><span style="color: hsl(120, 100%, 40%);">+origin2 = log.Origin(None, 'testowner2')</span><br><span style="color: hsl(120, 100%, 40%);">+# We disable dbg() for second thread since FileWatch output result is</span><br><span style="color: hsl(120, 100%, 40%);">+# non-deterministic, since sometimes 1 Modiffied event is triggered, sometimes 2.</span><br><span style="color: hsl(120, 100%, 40%);">+origin1.dbg = origin2.dbg = lambda obj, *messages, _src=3, **named_items: None</span><br><span style="color: hsl(120, 100%, 40%);">+resources2 = None</span><br><span style="color: hsl(120, 100%, 40%);">+def second_ogt_instance():</span><br><span style="color: hsl(120, 100%, 40%);">+    # should block here until "resources" are freed.</span><br><span style="color: hsl(120, 100%, 40%);">+    print('- 2nd instance reserve() start')</span><br><span style="color: hsl(120, 100%, 40%);">+    resources2 = pool.reserve(origin2, config.replicate_times(want), config.replicate_times(modifiers))</span><br><span style="color: hsl(120, 100%, 40%);">+    print('- 2nd instance reserve() done')</span><br><span style="color: hsl(120, 100%, 40%);">+    resources2.free()</span><br><span style="color: hsl(120, 100%, 40%);">+resources = pool.reserve(origin1, config.replicate_times(want), config.replicate_times(modifiers))</span><br><span style="color: hsl(120, 100%, 40%);">+th = threading.Thread(target=second_ogt_instance)</span><br><span style="color: hsl(120, 100%, 40%);">+th.start()</span><br><span style="color: hsl(120, 100%, 40%);">+time.sleep(1.0)</span><br><span style="color: hsl(120, 100%, 40%);">+print('- 1st instance free()')</span><br><span style="color: hsl(120, 100%, 40%);">+resources.free()</span><br><span style="color: hsl(120, 100%, 40%);">+th.join()</span><br><span style="color: hsl(120, 100%, 40%);">+print('*** end: concurrent allocation')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # vim: expandtab tabstop=4 shiftwidth=4</span><br><span>diff --git a/selftest/suite_test/suite_test.ok b/selftest/suite_test/suite_test.ok</span><br><span>index 58593fd..150a4e9 100644</span><br><span>--- a/selftest/suite_test/suite_test.ok</span><br><span>+++ b/selftest/suite_test/suite_test.ok</span><br><span>@@ -43,6 +43,57 @@</span><br><span> tst {combining_scenarios='resources'}: DBG: {definition_conf={bts=[{'label': 'sysmoCell 5000'}, {'label': 'sysmoCell 5000'}, {'type': 'sysmo'}], ip_address=[{}], modem=[{}, {}]}}  [test_suite↪{combining_scenarios='resources'}]</span><br><span> tst test_suite: DBG: {combining='modifiers'}</span><br><span> tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}}  [test_suite↪{combining_scenarios='modifiers'}]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 3 x bts (candidates: 6)</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '3'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '0'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: c2feabd082c36a1cdeccb9a5237dfff7dbadb009</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: 07d9c8aaa940b674efcbbabdd69f58a6ce4e94f9</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.114</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoBTS 1002</span><br><span style="color: hsl(120, 100%, 40%);">+  type: sysmo</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 1 x ip_address (candidates: 3)</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: cde1debf28f07f94f92c761b4b7c6bf35785ced4</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.1</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 2 x modem (candidates: 16)</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: 19c69e45aa090fb511446bd00797690aa82ff52f</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007801'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: D620F48487B1B782DA55DF6717F08FF9</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7801</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_0</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: e1a46516a1fb493b2617ab14fc1693a9a45ec254</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007802'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7802</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_1</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: scheduling watch for directory [PATH]/selftest/suite_test/test_work/state_dir</span><br><span> tst test_suite: Reserving 3 x bts (candidates: 6)</span><br><span> tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span>   addr: 10.42.42.53</span><br><span>@@ -93,6 +144,7 @@</span><br><span>   ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3</span><br><span>   label: m7802</span><br><span>   path: /wavecom_1</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: unscheduling watch <ObservedWatch: path=[PATH]/selftest/suite_test/test_work/state_dir, is_recursive=False></span><br><span> </span><br><span> ----------------------------------------------</span><br><span> trial test_suite hello_world.py</span><br><span>@@ -204,6 +256,60 @@</span><br><span> tst test_suite: DBG: {combining='modifiers'}  [suite.py:[LINENR]]</span><br><span> tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}}  [test_suite↪{combining_scenarios='modifiers'}]  [suite.py:[LINENR]]</span><br><span> tst {combining_scenarios='modifiers', scenario='foo'}: DBG: {conf={}, scenario='foo'}  [test_suite↪{combining_scenarios='modifiers', scenario='foo'}]  [suite.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 3 x bts (candidates: 6)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '3'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '0'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: c2feabd082c36a1cdeccb9a5237dfff7dbadb009</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: 07d9c8aaa940b674efcbbabdd69f58a6ce4e94f9</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.114</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoBTS 1002</span><br><span style="color: hsl(120, 100%, 40%);">+  type: sysmo</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 1 x ip_address (candidates: 3)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: cde1debf28f07f94f92c761b4b7c6bf35785ced4</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.1</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 2 x modem (candidates: 16)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: 19c69e45aa090fb511446bd00797690aa82ff52f</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007801'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: D620F48487B1B782DA55DF6717F08FF9</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7801</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_0</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: e1a46516a1fb493b2617ab14fc1693a9a45ec254</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007802'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7802</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_1</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: scheduling watch for directory [PATH]/selftest/suite_test/test_work/state_dir  [util.py:[LINENR]]</span><br><span> tst test_suite: Reserving 3 x bts (candidates: 6)  [resource.py:[LINENR]]</span><br><span> tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span>   addr: 10.42.42.53</span><br><span>@@ -257,6 +363,7 @@</span><br><span>   label: m7802</span><br><span>   path: /wavecom_1</span><br><span>   [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: unscheduling watch <ObservedWatch: path=[PATH]/selftest/suite_test/test_work/state_dir, is_recursive=False>  [util.py:[LINENR]]</span><br><span> </span><br><span> ----------------------------------------------</span><br><span> trial test_suite hello_world.py</span><br><span>@@ -294,6 +401,60 @@</span><br><span> tst test_suite: DBG: {combining='modifiers'}  [suite.py:[LINENR]]</span><br><span> tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}}  [test_suite↪{combining_scenarios='modifiers'}]  [suite.py:[LINENR]]</span><br><span> tst {combining_scenarios='modifiers', scenario='foo'}: DBG: {conf={}, scenario='foo'}  [test_suite↪{combining_scenarios='modifiers', scenario='foo'}]  [suite.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 3 x bts (candidates: 6)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '3'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '0'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: c2feabd082c36a1cdeccb9a5237dfff7dbadb009</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: 07d9c8aaa940b674efcbbabdd69f58a6ce4e94f9</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.114</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoBTS 1002</span><br><span style="color: hsl(120, 100%, 40%);">+  type: sysmo</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 1 x ip_address (candidates: 3)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: cde1debf28f07f94f92c761b4b7c6bf35785ced4</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.1</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 2 x modem (candidates: 16)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: 19c69e45aa090fb511446bd00797690aa82ff52f</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007801'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: D620F48487B1B782DA55DF6717F08FF9</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7801</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_0</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: e1a46516a1fb493b2617ab14fc1693a9a45ec254</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007802'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7802</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_1</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: scheduling watch for directory [PATH]/selftest/suite_test/test_work/state_dir  [util.py:[LINENR]]</span><br><span> tst test_suite: Reserving 3 x bts (candidates: 6)  [resource.py:[LINENR]]</span><br><span> tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span>   addr: 10.42.42.53</span><br><span>@@ -347,6 +508,7 @@</span><br><span>   label: m7802</span><br><span>   path: /wavecom_1</span><br><span>   [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: unscheduling watch <ObservedWatch: path=[PATH]/selftest/suite_test/test_work/state_dir, is_recursive=False>  [util.py:[LINENR]]</span><br><span> </span><br><span> ----------------------------------------------</span><br><span> trial test_suite hello_world.py</span><br><span>@@ -380,6 +542,60 @@</span><br><span> tst test_suite: DBG: {combining='modifiers'}  [suite.py:[LINENR]]</span><br><span> tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}}  [test_suite↪{combining_scenarios='modifiers'}]  [suite.py:[LINENR]]</span><br><span> tst {combining_scenarios='modifiers', scenario='foo'}: DBG: {conf={bts=[{'trx_list': [{'nominal_power': '20'}, {'nominal_power': '20'}]}, {'trx_list': [{'nominal_power': '20'}, {'nominal_power': '20'}]}, {'type': 'sysmo'}]}, scenario='foo'}  [test_suite↪{combining_scenarios='modifiers', scenario='foo'}]  [suite.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 3 x bts (candidates: 6)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '3'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '0'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: c2feabd082c36a1cdeccb9a5237dfff7dbadb009</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: 07d9c8aaa940b674efcbbabdd69f58a6ce4e94f9</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.114</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoBTS 1002</span><br><span style="color: hsl(120, 100%, 40%);">+  type: sysmo</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 1 x ip_address (candidates: 3)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: cde1debf28f07f94f92c761b4b7c6bf35785ced4</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.1</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 2 x modem (candidates: 16)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: 19c69e45aa090fb511446bd00797690aa82ff52f</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007801'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: D620F48487B1B782DA55DF6717F08FF9</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7801</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_0</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: e1a46516a1fb493b2617ab14fc1693a9a45ec254</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007802'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7802</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_1</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: scheduling watch for directory [PATH]/selftest/suite_test/test_work/state_dir  [util.py:[LINENR]]</span><br><span> tst test_suite: Reserving 3 x bts (candidates: 6)  [resource.py:[LINENR]]</span><br><span> tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span>   addr: 10.42.42.53</span><br><span>@@ -433,6 +649,7 @@</span><br><span>   label: m7802</span><br><span>   path: /wavecom_1</span><br><span>   [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: unscheduling watch <ObservedWatch: path=[PATH]/selftest/suite_test/test_work/state_dir, is_recursive=False>  [util.py:[LINENR]]</span><br><span> resources(test_suite)={'bts': [{'_hash': 'a59640b8ba6a373552b24a6f9f65cadd2347bace',</span><br><span>           '_reserved_by': 'test_suite-[ID_NUM]-[ID_NUM]',</span><br><span>           'addr': '10.42.42.53',</span><br><span>@@ -516,6 +733,60 @@</span><br><span> tst test_suite: DBG: {combining='modifiers'}  [suite.py:[LINENR]]</span><br><span> tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}}  [test_suite↪{combining_scenarios='modifiers'}]  [suite.py:[LINENR]]</span><br><span> tst {combining_scenarios='modifiers', scenario='foo'}: DBG: {conf={}, scenario='foo'}  [test_suite↪{combining_scenarios='modifiers', scenario='foo'}]  [suite.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 3 x bts (candidates: 6)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '3'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '0'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: c2feabd082c36a1cdeccb9a5237dfff7dbadb009</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.53</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '7'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoCell 5000</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_trx:</span><br><span style="color: hsl(120, 100%, 40%);">+    clock_reference: external</span><br><span style="color: hsl(120, 100%, 40%);">+    launch_trx: 'False'</span><br><span style="color: hsl(120, 100%, 40%);">+    trx_ip: 10.42.42.112</span><br><span style="color: hsl(120, 100%, 40%);">+  trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+  - nominal_power: '10'</span><br><span style="color: hsl(120, 100%, 40%);">+  - max_power_red: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+    nominal_power: '12'</span><br><span style="color: hsl(120, 100%, 40%);">+  type: osmo-bts-trx</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: 07d9c8aaa940b674efcbbabdd69f58a6ce4e94f9</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.114</span><br><span style="color: hsl(120, 100%, 40%);">+  band: GSM-1800</span><br><span style="color: hsl(120, 100%, 40%);">+  ipa_unit_id: '1'</span><br><span style="color: hsl(120, 100%, 40%);">+  label: sysmoBTS 1002</span><br><span style="color: hsl(120, 100%, 40%);">+  type: sysmo</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 1 x ip_address (candidates: 3)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: cde1debf28f07f94f92c761b4b7c6bf35785ced4</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.1</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: Verifying 2 x modem (candidates: 16)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: Picked - _hash: 19c69e45aa090fb511446bd00797690aa82ff52f</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007801'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: D620F48487B1B782DA55DF6717F08FF9</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7801</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_0</span><br><span style="color: hsl(120, 100%, 40%);">+- _hash: e1a46516a1fb493b2617ab14fc1693a9a45ec254</span><br><span style="color: hsl(120, 100%, 40%);">+  imsi: '901700000007802'</span><br><span style="color: hsl(120, 100%, 40%);">+  ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3</span><br><span style="color: hsl(120, 100%, 40%);">+  label: m7802</span><br><span style="color: hsl(120, 100%, 40%);">+  path: /wavecom_1</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: scheduling watch for directory [PATH]/selftest/suite_test/test_work/state_dir  [util.py:[LINENR]]</span><br><span> tst test_suite: Reserving 3 x bts (candidates: 6)  [resource.py:[LINENR]]</span><br><span> tst test_suite: DBG: Picked - _hash: a59640b8ba6a373552b24a6f9f65cadd2347bace</span><br><span>   addr: 10.42.42.53</span><br><span>@@ -569,6 +840,7 @@</span><br><span>   label: m7802</span><br><span>   path: /wavecom_1</span><br><span>   [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst test_suite: DBG: FileWatch: unscheduling watch <ObservedWatch: path=[PATH]/selftest/suite_test/test_work/state_dir, is_recursive=False>  [util.py:[LINENR]]</span><br><span> resources(test_suite)={'bts': [{'_hash': 'a59640b8ba6a373552b24a6f9f65cadd2347bace',</span><br><span>           '_reserved_by': 'test_suite-[ID_NUM]-[ID_NUM]',</span><br><span>           'addr': '10.42.42.53',</span><br><span>@@ -662,10 +934,16 @@</span><br><span> tst {combining_scenarios='resources'}: DBG: {definition_conf={ip_address=[{'addr': '10.42.42.2'}]}}  [suiteC↪{combining_scenarios='resources'}]  [suite.py:[LINENR]]</span><br><span> tst suiteC: DBG: {combining='modifiers'}  [suite.py:[LINENR]]</span><br><span> tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}}  [suiteC↪{combining_scenarios='modifiers'}]  [suite.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst suiteC: Verifying 1 x ip_address (candidates: 3)  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst suiteC: DBG: Picked - _hash: fd103b22c7cf2480d609150e06f4bbd92ac78d8c</span><br><span style="color: hsl(120, 100%, 40%);">+  addr: 10.42.42.2</span><br><span style="color: hsl(120, 100%, 40%);">+  [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst suiteC: DBG: FileWatch: scheduling watch for directory [PATH]/selftest/suite_test/test_work/state_dir  [util.py:[LINENR]]</span><br><span> tst suiteC: Reserving 1 x ip_address (candidates: 3)  [resource.py:[LINENR]]</span><br><span> tst suiteC: DBG: Picked - _hash: fd103b22c7cf2480d609150e06f4bbd92ac78d8c</span><br><span>   addr: 10.42.42.2</span><br><span>   [resource.py:[LINENR]]</span><br><span style="color: hsl(120, 100%, 40%);">+tst suiteC: DBG: FileWatch: unscheduling watch <ObservedWatch: path=[PATH]/selftest/suite_test/test_work/state_dir, is_recursive=False>  [util.py:[LINENR]]</span><br><span> </span><br><span> ----------------------------------------------</span><br><span> trial suiteC test_template_overlay.py</span><br><span>diff --git a/src/osmo_gsm_tester/core/resource.py b/src/osmo_gsm_tester/core/resource.py</span><br><span>index 61a73aa..621522b 100644</span><br><span>--- a/src/osmo_gsm_tester/core/resource.py</span><br><span>+++ b/src/osmo_gsm_tester/core/resource.py</span><br><span>@@ -26,6 +26,7 @@</span><br><span> from . import config</span><br><span> from . import util</span><br><span> from . import schema</span><br><span style="color: hsl(120, 100%, 40%);">+from .event_loop import MainLoop</span><br><span> </span><br><span> from .util import is_dict, is_list</span><br><span> </span><br><span>@@ -48,6 +49,7 @@</span><br><span>     _registered_exit_handler = False</span><br><span> </span><br><span>     def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.reserved_modified = False</span><br><span>         self.config_path = config.get_main_config_value(config.CFG_RESOURCES_CONF)</span><br><span>         self.state_dir = config.get_state_dir()</span><br><span>         super().__init__(log.C_CNF, conf=self.config_path, state=self.state_dir.path)</span><br><span>@@ -57,6 +59,11 @@</span><br><span>         self.all_resources = Resources(config.read(self.config_path, schema.get_resources_schema()) or {})</span><br><span>         self.all_resources.set_hashes()</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    # Used by FileWatch in reserve() method below</span><br><span style="color: hsl(120, 100%, 40%);">+    def reserve_resources_fw_cb(self, event):</span><br><span style="color: hsl(120, 100%, 40%);">+        if event.event_type == 'modified':</span><br><span style="color: hsl(120, 100%, 40%);">+            self.reserved_modified = True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     def reserve(self, origin, want, modifiers):</span><br><span>         '''</span><br><span>         attempt to reserve the resources specified in the dict 'want' for</span><br><span>@@ -94,18 +101,42 @@</span><br><span> </span><br><span>         origin_id = origin.origin_id()</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        with self.state_dir.lock(origin_id):</span><br><span style="color: hsl(0, 100%, 40%);">-            rrfile_path = self.state_dir.mk_parentdir(RESERVED_RESOURCES_FILE)</span><br><span style="color: hsl(0, 100%, 40%);">-            reserved = Resources(config.read(rrfile_path, if_missing_return={}))</span><br><span style="color: hsl(0, 100%, 40%);">-            to_be_reserved = self.all_resources.without(reserved).find(origin, want)</span><br><span style="color: hsl(120, 100%, 40%);">+        # Make sure wanted resources can ever be reserved, even if all</span><br><span style="color: hsl(120, 100%, 40%);">+        # resources are unallocated. It will throw an exception if not</span><br><span style="color: hsl(120, 100%, 40%);">+        # possible:</span><br><span style="color: hsl(120, 100%, 40%);">+        self.all_resources.find(origin, want, None, False, True, 'Verifying')</span><br><span style="color: hsl(120, 100%, 40%);">+        self.reserved_modified = True # go through on first attempt</span><br><span style="color: hsl(120, 100%, 40%);">+        rrfile_path = self.state_dir.mk_parentdir(RESERVED_RESOURCES_FILE)</span><br><span style="color: hsl(120, 100%, 40%);">+        fw = util.FileWatch(origin, rrfile_path, self.reserve_resources_fw_cb)</span><br><span style="color: hsl(120, 100%, 40%);">+        fw.start()</span><br><span style="color: hsl(120, 100%, 40%);">+        while True:</span><br><span style="color: hsl(120, 100%, 40%);">+            # First, figure out if  RESERVED_RESOURCES_FILE was modified since last time we checked:</span><br><span style="color: hsl(120, 100%, 40%);">+            modified = False</span><br><span style="color: hsl(120, 100%, 40%);">+            try:</span><br><span style="color: hsl(120, 100%, 40%);">+                fw.get_lock().acquire()</span><br><span style="color: hsl(120, 100%, 40%);">+                if self.reserved_modified:</span><br><span style="color: hsl(120, 100%, 40%);">+                    modified = True</span><br><span style="color: hsl(120, 100%, 40%);">+                    self.reserved_modified = False</span><br><span style="color: hsl(120, 100%, 40%);">+            finally:</span><br><span style="color: hsl(120, 100%, 40%);">+                fw.get_lock().release()</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-            to_be_reserved.mark_reserved_by(origin_id)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-            reserved.add(to_be_reserved)</span><br><span style="color: hsl(0, 100%, 40%);">-            config.write(rrfile_path, reserved)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-            self.remember_to_free(to_be_reserved)</span><br><span style="color: hsl(0, 100%, 40%);">-            return ReservedResources(self, origin, to_be_reserved, modifiers)</span><br><span style="color: hsl(120, 100%, 40%);">+            if modified: # file was modified, attempt to reserve resources</span><br><span style="color: hsl(120, 100%, 40%);">+                # It should be possible at some point to reserve the wanted</span><br><span style="color: hsl(120, 100%, 40%);">+                # resources, so try and wait for some to be released if it's not</span><br><span style="color: hsl(120, 100%, 40%);">+                # possible to allocate them now:</span><br><span style="color: hsl(120, 100%, 40%);">+                try:</span><br><span style="color: hsl(120, 100%, 40%);">+                    with self.state_dir.lock(origin_id):</span><br><span style="color: hsl(120, 100%, 40%);">+                        reserved = Resources(config.read(rrfile_path, if_missing_return={}))</span><br><span style="color: hsl(120, 100%, 40%);">+                        to_be_reserved = self.all_resources.without(reserved).find(origin, want)</span><br><span style="color: hsl(120, 100%, 40%);">+                        to_be_reserved.mark_reserved_by(origin_id)</span><br><span style="color: hsl(120, 100%, 40%);">+                        reserved.add(to_be_reserved)</span><br><span style="color: hsl(120, 100%, 40%);">+                        fw.stop()</span><br><span style="color: hsl(120, 100%, 40%);">+                        config.write(rrfile_path, reserved)</span><br><span style="color: hsl(120, 100%, 40%);">+                        self.remember_to_free(to_be_reserved)</span><br><span style="color: hsl(120, 100%, 40%);">+                        return ReservedResources(self, origin, to_be_reserved, modifiers)</span><br><span style="color: hsl(120, 100%, 40%);">+                except NoResourceExn:</span><br><span style="color: hsl(120, 100%, 40%);">+                    origin.log('Unable to reserve resources, too many currently reserved. Waiting until some are available again')</span><br><span style="color: hsl(120, 100%, 40%);">+            MainLoop.sleep(1)</span><br><span> </span><br><span>     def free(self, origin, to_be_freed):</span><br><span>         log.ctx(origin)</span><br><span>diff --git a/src/osmo_gsm_tester/core/util.py b/src/osmo_gsm_tester/core/util.py</span><br><span>index e035a72..691b489 100644</span><br><span>--- a/src/osmo_gsm_tester/core/util.py</span><br><span>+++ b/src/osmo_gsm_tester/core/util.py</span><br><span>@@ -28,6 +28,8 @@</span><br><span> import threading</span><br><span> import importlib.util</span><br><span> import subprocess</span><br><span style="color: hsl(120, 100%, 40%);">+from watchdog.observers import Observer</span><br><span style="color: hsl(120, 100%, 40%);">+from watchdog.events import FileSystemEventHandler</span><br><span> </span><br><span> # This mirrors enum osmo_auth_algo in libosmocore/include/osmocom/crypt/auth.h</span><br><span> # so that the index within the tuple matches the enum value.</span><br><span>@@ -302,6 +304,51 @@</span><br><span>     def __repr__(self):</span><br><span>         return self.path</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+class FileWatch(FileSystemEventHandler):</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, origin, watch_path, event_func):</span><br><span style="color: hsl(120, 100%, 40%);">+        FileSystemEventHandler.__init__(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.origin = origin</span><br><span style="color: hsl(120, 100%, 40%);">+        self.watch_path = watch_path</span><br><span style="color: hsl(120, 100%, 40%);">+        self.event_func = event_func</span><br><span style="color: hsl(120, 100%, 40%);">+        self.observer = Observer()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.watch = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.mutex = threading.Lock()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_lock(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.mutex</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def start(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        dir = os.path.abspath(os.path.dirname(self.watch_path))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.origin.dbg('FileWatch: scheduling watch for directory %s' % dir)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.watch = self.observer.schedule(self, dir, recursive = False)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.observer.start()</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 self.watch:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.origin.dbg('FileWatch: unscheduling watch %r' % self.watch)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.observer.unschedule(self.watch)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.watch = None</span><br><span style="color: hsl(120, 100%, 40%);">+        if self.observer.is_alive():</span><br><span style="color: hsl(120, 100%, 40%);">+            self.observer.stop()</span><br><span style="color: hsl(120, 100%, 40%);">+            self.observer.join()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __del__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.stop()</span><br><span style="color: hsl(120, 100%, 40%);">+        self.observer = None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # Override from FileSystemEventHandler</span><br><span style="color: hsl(120, 100%, 40%);">+    def on_any_event(self, event):</span><br><span style="color: hsl(120, 100%, 40%);">+        if event.is_directory:</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</span><br><span style="color: hsl(120, 100%, 40%);">+        if os.path.abspath(event.src_path) != os.path.abspath(self.watch_path):</span><br><span style="color: hsl(120, 100%, 40%);">+            return None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.origin.dbg('FileWatch: received event %r' % event)</span><br><span style="color: hsl(120, 100%, 40%);">+        try:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.mutex.acquire()</span><br><span style="color: hsl(120, 100%, 40%);">+            self.event_func(event)</span><br><span style="color: hsl(120, 100%, 40%);">+        finally:</span><br><span style="color: hsl(120, 100%, 40%);">+             self.mutex.release()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def touch_file(path):</span><br><span>     with open(path, 'a') as f:</span><br><span>         f.close()</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/21089">change 21089</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/+/21089"/><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: I938602ee890712fda82fd3f812d8edb1bcd05e08 </div>
<div style="display:none"> Gerrit-Change-Number: 21089 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>