Introduce scenario modifiers

Before this patch, scenarios were only used to select resources with
specific attributes. This commit introduces "modifiers" in scenarios,
which allows setting or modifing config attributes of resources once
they have been reserved. This way same test can be run selecting same
resources but modifying its configuration, allowing for instance running
different number of TRX, different timeslot configuration, etc.

Modifiers are described by placing a "modifiers" dictionary in any
scenario file, similar to the current "resources" one used to select
requird resources. The "modifiers" dictionary is overlaid on top of the
"resources" one resulting from combining all the "resources" dictionary
of all scenario files.

Change-Id: If8c422c67d9a971d9ce2c72594f55cde2db7550d
This commit is contained in:
Pau Espin 2018-08-21 14:58:29 +02:00
parent 635f4c8d8f
commit aab56921f1
8 changed files with 226 additions and 12 deletions

View File

@ -0,0 +1,3 @@
modifiers:
bts:
- num_trx: 2

View File

@ -222,6 +222,54 @@ modem:
~~~ end: currently reserved
~~~ with modifiers:
resources(testowner)={'arfcn': [{'_hash': 'e620569450f8259b3f0212ec19c285dd07df063c',
'_reserved_by': 'testowner-123-1490837279',
'arfcn': '512',
'band': 'GSM-1800'},
{'_hash': '022621e513c5a5bf33b77430a1e9c886be676fa1',
'_reserved_by': 'testowner-123-1490837279',
'arfcn': '514',
'band': 'GSM-1800'}],
'bts': [{'_hash': 'd2aa7c1124943de352351b650ca0c751784da6b6',
'_reserved_by': 'testowner-123-1490837279',
'addr': '10.42.42.114',
'band': 'GSM-1800',
'ciphers': ['a5_0', 'a5_1'],
'direct_pcu': 'True',
'ipa_unit_id': '1',
'label': 'sysmoBTS 1002',
'type': 'osmo-bts-sysmo'},
{'_hash': '6a9c9fbd364e1563a5b9f0826030a7888fd19575',
'_reserved_by': 'testowner-123-1490837279',
'addr': '10.42.42.50',
'band': 'GSM-1800',
'ciphers': ['a5_0', 'a5_1'],
'ipa_unit_id': '6',
'label': 'Ettus B200',
'launch_trx': 'True',
'num_trx': 2,
'type': 'osmo-bts-trx'}],
'ip_address': [{'_hash': 'fd103b22c7cf2480d609150e06f4bbd92ac78d8c',
'_reserved_by': 'testowner-123-1490837279',
'addr': '10.42.42.2'}],
'modem': [{'_hash': '0b538cb6ad799fbd7c2953fd3b4463a76c7cc9c0',
'_reserved_by': 'testowner-123-1490837279',
'auth_algo': 'comp128v1',
'ciphers': ['a5_0', 'a5_1'],
'imsi': '901700000009031',
'ki': '80A37E6FDEA931EAC92FFA5F671EFEAD',
'label': 'sierra_1',
'path': '/sierra_1'},
{'_hash': '3a6e7747dfe7dfdf817bd3351031bd08051605c3',
'_reserved_by': 'testowner-123-1490837279',
'auth_algo': 'comp128v1',
'ciphers': ['a5_0', 'a5_1'],
'imsi': '901700000009029',
'ki': '00969E283349D354A8239E877F2E0866',
'label': 'sierra_2',
'path': '/sierra_2'}]}
~~~ end: with modifiers:
~~~ currently reserved:
{}

View File

@ -77,16 +77,22 @@ want = {
'arfcn': [ { 'band': 'GSM-1800', 'times': 2 } ],
'modem': [ { 'times': 2 , 'ciphers': ['a5_0', 'a5_1']} ],
}
modifiers = {
'bts': [ {}, {'num_trx': 2 }],
}
origin = log.Origin(None, 'testowner')
resources = pool.reserve(origin, config.replicate_times(want))
resources = pool.reserve(origin, config.replicate_times(want), config.replicate_times(modifiers))
print('~~~ currently reserved:')
with open(rrfile, 'r') as f:
print(f.read())
print('~~~ end: currently reserved\n')
print('~~~ with modifiers:')
print(repr(resources))
print('~~~ end: with modifiers:')
resources.free()
print('~~~ currently reserved:')

View File

@ -34,6 +34,8 @@ trial test_suite
tst test_suite: reserving resources in [PATH]/selftest/suite_test/test_work/state_dir ...
tst test_suite: DBG: {combining='resources'}
tst {combining_scenarios='resources'}: DBG: {definition_conf={bts=[{'label': 'sysmoCell 5000'}, {'label': 'sysmoCell 5000'}, {'type': 'sysmo'}], ip_address=[{}], modem=[{}, {}]}} [test_suite↪{combining_scenarios='resources'}]
tst test_suite: DBG: {combining='modifiers'}
tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}} [test_suite↪{combining_scenarios='modifiers'}]
tst test_suite: Reserving 3 x bts (candidates: 6)
tst test_suite: DBG: Picked - _hash: 076ff06a4b719e61779492d3fb99f42a6635bb72
addr: 10.42.42.53
@ -177,6 +179,9 @@ tst test_suite: reserving resources in [PATH]/selftest/suite_test/test_work/stat
tst test_suite: DBG: {combining='resources'} [suite.py:[LINENR]]
tst {combining_scenarios='resources'}: DBG: {definition_conf={bts=[{'label': 'sysmoCell 5000'}, {'label': 'sysmoCell 5000'}, {'type': 'sysmo'}], ip_address=[{}], modem=[{}, {}]}} [test_suite↪{combining_scenarios='resources'}] [suite.py:[LINENR]]
tst {combining_scenarios='resources', scenario='foo'}: [RESOURCE_DICT]
tst test_suite: DBG: {combining='modifiers'} [suite.py:[LINENR]]
tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}} [test_suite↪{combining_scenarios='modifiers'}] [suite.py:[LINENR]]
tst {combining_scenarios='modifiers', scenario='foo'}: DBG: {conf={}, scenario='foo'} [test_suite↪{combining_scenarios='modifiers', scenario='foo'}] [suite.py:[LINENR]]
tst test_suite: Reserving 3 x bts (candidates: 6) [resource.py:[LINENR]]
tst test_suite: DBG: Picked - _hash: 076ff06a4b719e61779492d3fb99f42a6635bb72
addr: 10.42.42.53
@ -255,6 +260,9 @@ tst test_suite: reserving resources in [PATH]/selftest/suite_test/test_work/stat
tst test_suite: DBG: {combining='resources'} [suite.py:[LINENR]]
tst {combining_scenarios='resources'}: DBG: {definition_conf={bts=[{'label': 'sysmoCell 5000'}, {'label': 'sysmoCell 5000'}, {'type': 'sysmo'}], ip_address=[{}], modem=[{}, {}]}} [test_suite↪{combining_scenarios='resources'}] [suite.py:[LINENR]]
tst {combining_scenarios='resources', scenario='foo'}: [RESOURCE_DICT]
tst test_suite: DBG: {combining='modifiers'} [suite.py:[LINENR]]
tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}} [test_suite↪{combining_scenarios='modifiers'}] [suite.py:[LINENR]]
tst {combining_scenarios='modifiers', scenario='foo'}: DBG: {conf={}, scenario='foo'} [test_suite↪{combining_scenarios='modifiers', scenario='foo'}] [suite.py:[LINENR]]
tst test_suite: Reserving 3 x bts (candidates: 6) [resource.py:[LINENR]]
tst test_suite: DBG: Picked - _hash: 076ff06a4b719e61779492d3fb99f42a6635bb72
addr: 10.42.42.53
@ -303,6 +311,129 @@ tst test_suite: DBG: Picked - _hash: 19c69e45aa090fb511446bd00797690aa82ff52f
path: /wavecom_1
[resource.py:[LINENR]]
----------------------------------------------
trial test_suite hello_world.py
----------------------------------------------
tst hello_world.py:[LINENR]: hello world [test_suite↪hello_world.py:[LINENR]] [hello_world.py:[LINENR]]
tst hello_world.py:[LINENR]: I am 'test_suite' / 'hello_world.py:[LINENR]' [test_suite↪hello_world.py:[LINENR]] [hello_world.py:[LINENR]]
tst hello_world.py:[LINENR]: one [test_suite↪hello_world.py:[LINENR]] [hello_world.py:[LINENR]]
tst hello_world.py:[LINENR]: two [test_suite↪hello_world.py:[LINENR]] [hello_world.py:[LINENR]]
tst hello_world.py:[LINENR]: three [test_suite↪hello_world.py:[LINENR]] [hello_world.py:[LINENR]]
tst hello_world.py:[LINENR] Test passed (N.N sec) [test_suite↪hello_world.py] [test.py:[LINENR]]
---------------------------------------------------------------------
trial test_suite PASS
---------------------------------------------------------------------
PASS: test_suite (pass: 1, skip: 5)
pass: hello_world.py (N.N sec)
skip: mo_mt_sms.py
skip: mo_sms.py
skip: test_error.py
skip: test_fail.py
skip: test_fail_raise.py
- test with scenario and modifiers
cnf ResourcesPool: DBG: Found config file resources.conf as [PATH]/selftest/suite_test/resources.conf in ./suite_test which is [PATH]/selftest/suite_test [config.py:[LINENR]]
cnf ResourcesPool: DBG: Found path state_dir as [PATH]/selftest/suite_test/test_work/state_dir [config.py:[LINENR]]
tst test_suite: reserving resources in [PATH]/selftest/suite_test/test_work/state_dir ... [suite.py:[LINENR]]
tst test_suite: DBG: {combining='resources'} [suite.py:[LINENR]]
tst {combining_scenarios='resources'}: DBG: {definition_conf={bts=[{'label': 'sysmoCell 5000'}, {'label': 'sysmoCell 5000'}, {'type': 'sysmo'}], ip_address=[{}], modem=[{}, {}]}} [test_suite↪{combining_scenarios='resources'}] [suite.py:[LINENR]]
tst {combining_scenarios='resources', scenario='foo'}: [RESOURCE_DICT]
tst test_suite: DBG: {combining='modifiers'} [suite.py:[LINENR]]
tst {combining_scenarios='modifiers'}: DBG: {definition_conf={}} [test_suite↪{combining_scenarios='modifiers'}] [suite.py:[LINENR]]
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]]
tst test_suite: Reserving 3 x bts (candidates: 6) [resource.py:[LINENR]]
tst test_suite: DBG: Picked - _hash: 076ff06a4b719e61779492d3fb99f42a6635bb72
addr: 10.42.42.53
band: GSM-1800
ipa_unit_id: '7'
label: sysmoCell 5000
trx_list:
- max_power_red: '3'
nominal_power: '10'
- max_power_red: '0'
nominal_power: '12'
trx_remote_ip: 10.42.42.112
type: osmo-bts-trx
- _hash: 9eaa928b04ce04b19dbae972f9bfc3eea6f5e249
addr: 10.42.42.53
band: GSM-1800
ipa_unit_id: '7'
label: sysmoCell 5000
trx_list:
- nominal_power: '10'
- max_power_red: '1'
nominal_power: '12'
trx_remote_ip: 10.42.42.112
type: osmo-bts-trx
- _hash: 07d9c8aaa940b674efcbbabdd69f58a6ce4e94f9
addr: 10.42.42.114
band: GSM-1800
ipa_unit_id: '1'
label: sysmoBTS 1002
type: sysmo
[resource.py:[LINENR]]
tst test_suite: Reserving 1 x ip_address (candidates: 3) [resource.py:[LINENR]]
tst test_suite: DBG: Picked - _hash: cde1debf28f07f94f92c761b4b7c6bf35785ced4
addr: 10.42.42.1
[resource.py:[LINENR]]
tst test_suite: Reserving 2 x modem (candidates: 16) [resource.py:[LINENR]]
tst test_suite: DBG: Picked - _hash: 19c69e45aa090fb511446bd00797690aa82ff52f
imsi: '901700000007801'
ki: D620F48487B1B782DA55DF6717F08FF9
label: m7801
path: /wavecom_0
- _hash: e1a46516a1fb493b2617ab14fc1693a9a45ec254
imsi: '901700000007802'
ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3
label: m7802
path: /wavecom_1
[resource.py:[LINENR]]
resources(test_suite)={'bts': [{'_hash': '076ff06a4b719e61779492d3fb99f42a6635bb72',
'_reserved_by': 'test_suite-[ID_NUM]-[ID_NUM]',
'addr': '10.42.42.53',
'band': 'GSM-1800',
'ipa_unit_id': '7',
'label': 'sysmoCell 5000',
'trx_list': [{'max_power_red': '3', 'nominal_power': '20'},
{'max_power_red': '0', 'nominal_power': '20'}],
'trx_remote_ip': '10.42.42.112',
'type': 'osmo-bts-trx'},
{'_hash': '9eaa928b04ce04b19dbae972f9bfc3eea6f5e249',
'_reserved_by': 'test_suite-[ID_NUM]-[ID_NUM]',
'addr': '10.42.42.53',
'band': 'GSM-1800',
'ipa_unit_id': '7',
'label': 'sysmoCell 5000',
'trx_list': [{'nominal_power': '20'},
{'max_power_red': '1', 'nominal_power': '20'}],
'trx_remote_ip': '10.42.42.112',
'type': 'osmo-bts-trx'},
{'_hash': '07d9c8aaa940b674efcbbabdd69f58a6ce4e94f9',
'_reserved_by': 'test_suite-[ID_NUM]-[ID_NUM]',
'addr': '10.42.42.114',
'band': 'GSM-1800',
'ipa_unit_id': '1',
'label': 'sysmoBTS 1002',
'type': 'sysmo'}],
'ip_address': [{'_hash': 'cde1debf28f07f94f92c761b4b7c6bf35785ced4',
'_reserved_by': 'test_suite-[ID_NUM]-[ID_NUM]',
'addr': '10.42.42.1'}],
'modem': [{'_hash': '19c69e45aa090fb511446bd00797690aa82ff52f',
'_reserved_by': 'test_suite-[ID_NUM]-[ID_NUM]',
'imsi': '901700000007801',
'ki': 'D620F48487B1B782DA55DF6717F08FF9',
'label': 'm7801',
'path': '/wavecom_0'},
{'_hash': 'e1a46516a1fb493b2617ab14fc1693a9a45ec254',
'_reserved_by': 'test_suite-[ID_NUM]-[ID_NUM]',
'imsi': '901700000007802',
'ki': '47FDB2D55CE6A10A85ABDAD034A5B7B3',
'label': 'm7802',
'path': '/wavecom_1'}]}
---------------------------------------------------------------------
trial test_suite
---------------------------------------------------------------------
----------------------------------------------
trial test_suite hello_world.py
----------------------------------------------

View File

@ -2,3 +2,4 @@
\.py:[0-9]* .py:[LINENR]
\([0-9.]+ sec\) (N.N sec)
{combining_scenarios='resources', scenario='foo'}:.* {combining_scenarios='resources', scenario='foo'}: [RESOURCE_DICT]
test_suite-[0-9]*-[0-9]* test_suite-[ID_NUM]-[ID_NUM]

View File

@ -58,5 +58,16 @@ s = suite.SuiteRun(trial, 'test_suite', s_def, [scenario])
results = s.run_tests('hello_world.py')
print(report.suite_to_text(s))
print('- test with scenario and modifiers')
trial = log.Origin(log.C_TST, 'trial')
scenario = config.Scenario('foo', 'bar')
scenario['resources'] = { 'bts': [{ 'times': '2', 'type': 'osmo-bts-trx', 'trx_list': [{'nominal_power': '10'}, {'nominal_power': '12'}]}, {'type': 'sysmo'}] }
scenario['modifiers'] = { 'bts': [{ 'times': '2', 'trx_list': [{'nominal_power': '20'}, {'nominal_power': '20'}]}, {'type': 'sysmo'}] }
s = suite.SuiteRun(trial, 'test_suite', s_def, [scenario])
s.reserve_resources()
print(repr(s.reserved_resources))
results = s.run_tests('hello_world.py')
print(report.suite_to_text(s))
print('\n- graceful exit.')
# vim: expandtab tabstop=4 shiftwidth=4

View File

@ -87,7 +87,8 @@ WANT_SCHEMA = util.dict_add(
CONF_SCHEMA = util.dict_add(
{ 'defaults.timeout': schema.STR },
dict([('resources.%s' % key, val) for key, val in WANT_SCHEMA.items()]))
dict([('resources.%s' % key, val) for key, val in WANT_SCHEMA.items()]),
dict([('modifiers.%s' % key, val) for key, val in WANT_SCHEMA.items()]))
KNOWN_BTS_TYPES = {
'osmo-bts-sysmo': bts_sysmo.SysmoBts,
@ -113,7 +114,7 @@ class ResourcesPool(log.Origin):
self.all_resources = Resources(config.read(self.config_path, RESOURCES_SCHEMA))
self.all_resources.set_hashes()
def reserve(self, origin, want):
def reserve(self, origin, want, modifiers):
'''
attempt to reserve the resources specified in the dict 'want' for
'origin'. Obtain a lock on the resources lock dir, verify that all
@ -125,7 +126,11 @@ class ResourcesPool(log.Origin):
'origin' should be an Origin() instance.
'want' is a dict matching RESOURCES_SCHEMA.
'want' is a dict matching RESOURCES_SCHEMA, used to specify what to
reserve.
'modifiers' is a dict matching RESOURCES_SCHEMA, it is overlaid on top
of 'want'.
If an entry has no attribute set, any of the resources may be
reserved without further limitations.
@ -142,6 +147,7 @@ class ResourcesPool(log.Origin):
}
'''
schema.validate(want, RESOURCES_SCHEMA)
schema.validate(modifiers, RESOURCES_SCHEMA)
origin_id = origin.origin_id()
@ -156,7 +162,7 @@ class ResourcesPool(log.Origin):
config.write(rrfile_path, reserved)
self.remember_to_free(to_be_reserved)
return ReservedResources(self, origin, to_be_reserved)
return ReservedResources(self, origin, to_be_reserved, modifiers)
def free(self, origin, to_be_freed):
log.ctx(origin)
@ -491,10 +497,12 @@ class ReservedResources(log.Origin):
dependencies from so far unused (but reserved) resource.
'''
def __init__(self, resources_pool, origin, reserved):
def __init__(self, resources_pool, origin, reserved, modifiers):
self.resources_pool = resources_pool
self.origin = origin
self.reserved = reserved
self.reserved_original = reserved
self.reserved = copy.deepcopy(self.reserved_original)
config.overlay(self.reserved, modifiers)
def __repr__(self):
return 'resources(%s)=%s' % (self.origin.name(), pprint.pformat(self.reserved))
@ -551,9 +559,9 @@ class ReservedResources(log.Origin):
item.pop(USED_KEY, None)
def free(self):
if self.reserved:
self.resources_pool.free(self.origin, self.reserved)
self.reserved = None
if self.reserved_original:
self.resources_pool.free(self.origin, self.reserved_original)
self.reserved_original = None
def counts(self):
counts = {}

View File

@ -71,6 +71,7 @@ class SuiteRun(log.Origin):
self.objects_to_clean_up = None
self.test_import_modules_to_clean_up = []
self._resource_requirements = None
self._resource_modifiers = None
self._config = None
self._processes = None
self._run_dir = None
@ -154,6 +155,11 @@ class SuiteRun(log.Origin):
self._resource_requirements = self.combined('resources')
return self._resource_requirements
def resource_modifiers(self):
if self._resource_modifiers is None:
self._resource_modifiers = self.combined('modifiers')
return self._resource_modifiers
def config(self):
if self._config is None:
self._config = self.combined('config')
@ -163,7 +169,7 @@ class SuiteRun(log.Origin):
if self.reserved_resources:
raise RuntimeError('Attempt to reserve resources twice for a SuiteRun')
self.log('reserving resources in', self.resources_pool.state_dir, '...')
self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements())
self.reserved_resources = self.resources_pool.reserve(self, self.resource_requirements(), self.resource_modifiers())
def run_tests(self, names=None):
suite_libdir = os.path.join(self.definition.suite_dir, 'lib')