This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
pespin gerrit-no-reply at lists.osmocom.orgpespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18023 )
Change subject: Generate schemas dynamically from pieces provided by each object class
......................................................................
Generate schemas dynamically from pieces provided by each object class
This way we benefit from:
* knowing which attributes are used/required by each object class and
subclass
* Having validation function definitions near the class going to use them
Change-Id: I8fd6773c51d19405a585977af4ed72cad2b21db1
---
M selftest/config_test.py
M selftest/resource_test.py
M selftest/suite_test.py
M src/osmo-gsm-tester.py
M src/osmo_gsm_tester/core/config.py
M src/osmo_gsm_tester/core/schema.py
M src/osmo_gsm_tester/core/util.py
M src/osmo_gsm_tester/obj/bsc_osmo.py
M src/osmo_gsm_tester/obj/bts.py
M src/osmo_gsm_tester/obj/bts_osmo.py
M src/osmo_gsm_tester/obj/bts_osmotrx.py
M src/osmo_gsm_tester/obj/enb.py
M src/osmo_gsm_tester/obj/enb_amarisoft.py
M src/osmo_gsm_tester/obj/enb_srs.py
M src/osmo_gsm_tester/obj/epc.py
M src/osmo_gsm_tester/obj/epc_amarisoft.py
M src/osmo_gsm_tester/obj/epc_srs.py
M src/osmo_gsm_tester/obj/iperf3.py
M src/osmo_gsm_tester/obj/ms.py
M src/osmo_gsm_tester/obj/ms_amarisoft.py
M src/osmo_gsm_tester/obj/ms_srs.py
M src/osmo_gsm_tester/obj/msc_osmo.py
M src/osmo_gsm_tester/obj/osmocon.py
M src/osmo_gsm_tester/obj/run_node.py
M src/osmo_gsm_tester/resource.py
M src/osmo_gsm_tester/suite.py
26 files changed, 418 insertions(+), 208 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-gsm-tester refs/changes/23/18023/1
diff --git a/selftest/config_test.py b/selftest/config_test.py
index 83a8d06..c26ebd1 100755
--- a/selftest/config_test.py
+++ b/selftest/config_test.py
@@ -116,35 +116,35 @@
a = {'times': '2'}
b = {'type': 'osmo-bts-trx'}
res = {'times': '2', 'type': 'osmo-bts-trx'}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine dicts 2:')
a = {'times': '1', 'label': 'foo', 'type': 'osmo-bts-trx'}
b = {'type': 'osmo-bts-trx'}
res = {'times': '1', 'label': 'foo', 'type': 'osmo-bts-trx'}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine lists:')
a = { 'a_list': ['x', 'y', 'z'] }
b = { 'a_list': ['y'] }
res = {'a_list': ['x', 'y', 'z']}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine lists 2:')
a = { 'a_list': ['x'] }
b = { 'a_list': ['w', 'u', 'x', 'y', 'z'] }
res = {'a_list': ['x', 'w', 'u', 'y', 'z']}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine lists 3:')
a = { 'a_list': ['x', 3] }
b = { 'a_list': ['y', 'z'] }
try:
- config.combine(a, b)
+ schema.combine(a, b)
except ValueError:
print("ValueError expected")
@@ -152,7 +152,7 @@
a = { 'a_list': [2, 3] }
b = { 'a_list': ['y', 'z'] }
try:
- config.combine(a, b)
+ schema.combine(a, b)
except ValueError:
print("ValueError expected")
@@ -160,7 +160,7 @@
a = { 'a_list': [{}, {}] }
b = { 'a_list': ['y', 'z'] }
try:
- config.combine(a, b)
+ schema.combine(a, b)
except ValueError:
print("ValueError expected")
@@ -168,49 +168,49 @@
a = { 'a_list': [{}, {}] }
b = { 'a_list': [{}] }
res = {'a_list': [{}, {}]}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine lists 7:')
a = { 'times': '1', 'label': 'foo', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}] }
b = { 'type': 'osmo-bts-trx', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}] }
res = {'times': '1', 'label': 'foo', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}], 'type': 'osmo-bts-trx'}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine lists 8:')
a = { 'times': '1', 'label': 'foo', 'trx': [{'nominal power': '10'}] }
b = { 'type': 'osmo-bts-trx', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}] }
res = {'times': '1', 'label': 'foo', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}], 'type': 'osmo-bts-trx'}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine lists 9:')
a = { 'times': '1', 'label': 'foo', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}] }
b = { 'type': 'osmo-bts-trx', 'trx': [{'nominal power': '10'}] }
res = {'times': '1', 'label': 'foo', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}], 'type': 'osmo-bts-trx'}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine lists 10:')
a = { 'times': '1', 'label': 'foo', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}] }
b = { 'type': 'osmo-bts-trx', 'trx': [{}, {'nominal power': '12'}] }
res = {'times': '1', 'label': 'foo', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}], 'type': 'osmo-bts-trx'}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine lists 13:')
a = { 'times': '1', 'label': 'foo', 'trx': [{}, {'nominal power': '12'}] }
b = { 'type': 'osmo-bts-trx', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}] }
res = {'times': '1', 'label': 'foo', 'trx': [{'nominal power': '10'}, {'nominal power': '12'}], 'type': 'osmo-bts-trx'}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
print('- Combine lists 14:')
a = { 'times': '1', 'label': 'foo', 'trx': [] }
b = { 'type': 'osmo-bts-trx', 'trx': [] }
res = {'times': '1', 'label': 'foo', 'trx': [], 'type': 'osmo-bts-trx'}
-config.combine(a, b)
+schema.combine(a, b)
assert a == res
# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/selftest/resource_test.py b/selftest/resource_test.py
index f399e20..ecbeb24 100755
--- a/selftest/resource_test.py
+++ b/selftest/resource_test.py
@@ -7,6 +7,7 @@
import atexit
import _prep
from osmo_gsm_tester.core import config, log, util
+from osmo_gsm_tester.core.schema import generate_schemas
from osmo_gsm_tester import resource
workdir = util.get_tempdir()
@@ -16,6 +17,9 @@
log.get_process_id = lambda: '123-1490837279'
+# Generate supported schemas dynamically from objects:
+generate_schemas()
+
print('- expect solutions:')
pprint.pprint(
resource.solve([ [0, 1, 2],
diff --git a/selftest/suite_test.py b/selftest/suite_test.py
index c4dd5bf..1fb95ec 100755
--- a/selftest/suite_test.py
+++ b/selftest/suite_test.py
@@ -3,6 +3,7 @@
import _prep
import shutil
from osmo_gsm_tester.core import log, config, util
+from osmo_gsm_tester.core.schema import generate_schemas
from osmo_gsm_tester import suite, report
config.ENV_CONF = './suite_test'
@@ -24,6 +25,9 @@
#log.style_change(trace=True)
+# Generate supported schemas dynamically from objects:
+generate_schemas()
+
print('- non-existing suite dir')
assert(log.run_logging_exceptions(suite.load, 'does_not_exist') == None)
diff --git a/src/osmo-gsm-tester.py b/src/osmo-gsm-tester.py
index df87957..af66b32 100755
--- a/src/osmo-gsm-tester.py
+++ b/src/osmo-gsm-tester.py
@@ -70,7 +70,9 @@
import argparse
from signal import *
from osmo_gsm_tester import __version__
-from osmo_gsm_tester.core import log, config
+from osmo_gsm_tester.core import log
+from osmo_gsm_tester.core.config import read_config_file, DEFAULT_SUITES_CONF
+from osmo_gsm_tester.core.schema import generate_schemas
from osmo_gsm_tester import trial, suite
def sig_handler_cleanup(signum, frame):
@@ -162,6 +164,9 @@
if not combination_strs:
raise RuntimeError('Need at least one suite:scenario or series to run')
+ # Generate supported schemas dynamically from objects:
+ generate_schemas()
+
# make sure all suite:scenarios exist
suite_scenarios = []
for combination_str in combination_strs:
diff --git a/src/osmo_gsm_tester/core/config.py b/src/osmo_gsm_tester/core/config.py
index 9333601..6730807 100644
--- a/src/osmo_gsm_tester/core/config.py
+++ b/src/osmo_gsm_tester/core/config.py
@@ -54,7 +54,8 @@
import os
import copy
-from . import log, schema, util, template
+from . import log, util, template
+from . import schema
from .util import is_dict, is_list, Dir, get_tempdir
ENV_PREFIX = 'OSMO_GSM_TESTER_'
@@ -288,68 +289,6 @@
sc.read_from_file(validation_schema)
return sc
-def add(dest, src):
- if is_dict(dest):
- if not is_dict(src):
- raise ValueError('cannot add to dict a value of type: %r' % type(src))
-
- for key, val in src.items():
- dest_val = dest.get(key)
- if dest_val is None:
- dest[key] = val
- else:
- log.ctx(key=key)
- add(dest_val, val)
- return
- if is_list(dest):
- if not is_list(src):
- raise ValueError('cannot add to list a value of type: %r' % type(src))
- dest.extend(src)
- return
- if dest == src:
- return
- raise ValueError('cannot add dicts, conflicting items (values %r and %r)'
- % (dest, src))
-
-def combine(dest, src):
- if is_dict(dest):
- if not is_dict(src):
- raise ValueError('cannot combine dict with a value of type: %r' % type(src))
-
- for key, val in src.items():
- log.ctx(key=key)
- dest_val = dest.get(key)
- if dest_val is None:
- dest[key] = val
- else:
- combine(dest_val, val)
- return
- if is_list(dest):
- if not is_list(src):
- raise ValueError('cannot combine list with a value of type: %r' % type(src))
- # Validate that all elements in both lists are of the same type:
- t = util.list_validate_same_elem_type(src + dest)
- if t is None:
- return # both lists are empty, return
- # For lists of complex objects, we expect them to be sorted lists:
- if t in (dict, list, tuple):
- for i in range(len(dest)):
- log.ctx(idx=i)
- src_it = src[i] if i < len(src) else util.empty_instance_type(t)
- combine(dest[i], src_it)
- for i in range(len(dest), len(src)):
- log.ctx(idx=i)
- dest.append(src[i])
- else: # for lists of basic elements, we handle them as unsorted sets:
- for elem in src:
- if elem not in dest:
- dest.append(elem)
- return
- if dest == src:
- return
- raise ValueError('cannot combine dicts, conflicting items (values %r and %r)'
- % (dest, src))
-
def overlay(dest, src):
if is_dict(dest):
if not is_dict(src):
diff --git a/src/osmo_gsm_tester/core/schema.py b/src/osmo_gsm_tester/core/schema.py
index d343bef..588c432 100644
--- a/src/osmo_gsm_tester/core/schema.py
+++ b/src/osmo_gsm_tester/core/schema.py
@@ -18,9 +18,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
+import os
from . import log
-from .util import is_dict, is_list, str2bool, ENUM_OSMO_AUTH_ALGO
+from . import util
KEY_RE = re.compile('[a-zA-Z][a-zA-Z0-9_]*')
IPV4_RE = re.compile('([0-9]{1,3}.){3}[0-9]{1,3}')
@@ -62,7 +63,7 @@
match_re('MSISDN', MSISDN_RE, val)
def auth_algo(val):
- if val not in ENUM_OSMO_AUTH_ALGO:
+ if val not in util.ENUM_OSMO_AUTH_ALGO:
raise ValueError('Unknown Authentication Algorithm: %r' % val)
def uint(val):
@@ -162,7 +163,7 @@
INT: int,
STR: str,
UINT: uint,
- BOOL_STR: str2bool,
+ BOOL_STR: util.str2bool,
BAND: band,
IPV4: ipv4,
HWADDR: hwaddr,
@@ -182,6 +183,87 @@
DURATION: duration,
}
+def add(dest, src):
+ if util.is_dict(dest):
+ if not util.is_dict(src):
+ raise ValueError('cannot add to dict a value of type: %r' % type(src))
+
+ for key, val in src.items():
+ dest_val = dest.get(key)
+ if dest_val is None:
+ dest[key] = val
+ else:
+ log.ctx(key=key)
+ add(dest_val, val)
+ return
+ if util.is_list(dest):
+ if not util.is_list(src):
+ raise ValueError('cannot add to list a value of type: %r' % type(src))
+ dest.extend(src)
+ return
+ if dest == src:
+ return
+ raise ValueError('cannot add dicts, conflicting items (values %r and %r)'
+ % (dest, src))
+
+def combine(dest, src):
+ if util.is_dict(dest):
+ if not util.is_dict(src):
+ raise ValueError('cannot combine dict with a value of type: %r' % type(src))
+
+ for key, val in src.items():
+ log.ctx(key=key)
+ dest_val = dest.get(key)
+ if dest_val is None:
+ dest[key] = val
+ else:
+ combine(dest_val, val)
+ return
+ if util.is_list(dest):
+ if not util.is_list(src):
+ raise ValueError('cannot combine list with a value of type: %r' % type(src))
+ # Validate that all elements in both lists are of the same type:
+ t = util.list_validate_same_elem_type(src + dest)
+ if t is None:
+ return # both lists are empty, return
+ # For lists of complex objects, we expect them to be sorted lists:
+ if t in (dict, list, tuple):
+ for i in range(len(dest)):
+ log.ctx(idx=i)
+ src_it = src[i] if i < len(src) else util.empty_instance_type(t)
+ combine(dest[i], src_it)
+ for i in range(len(dest), len(src)):
+ log.ctx(idx=i)
+ dest.append(src[i])
+ else: # for lists of basic elements, we handle them as unsorted sets:
+ for elem in src:
+ if elem not in dest:
+ dest.append(elem)
+ return
+ if dest == src:
+ return
+ raise ValueError('cannot combine dicts, conflicting items (values %r and %r)'
+ % (dest, src))
+
+def replicate_times(d):
+ '''
+ replicate items that have a "times" > 1
+
+ 'd' is a dict matching WANT_SCHEMA, which is the same as
+ the RESOURCES_SCHEMA, except each entity that can be reserved has a 'times'
+ field added, to indicate how many of those should be reserved.
+ '''
+ d = copy.deepcopy(d)
+ for key, item_list in d.items():
+ idx = 0
+ while idx < len(item_list):
+ item = item_list[idx]
+ times = int(item.pop('times', 1))
+ for j in range(1, times):
+ item_list.insert(idx + j, copy.deepcopy(item))
+ idx += times
+ return d
+
def validate(config, schema):
'''Make sure the given config dict adheres to the schema.
The schema is a dict of 'dict paths' in dot-notation with permitted
@@ -198,17 +280,17 @@
def validate_item(path, value, schema):
want_type = schema.get(path)
- if is_list(value):
+ if util.is_list(value):
if want_type:
raise ValueError('config item is a list, should be %r: %r' % (want_type, path))
path = path + '[]'
want_type = schema.get(path)
if not want_type:
- if is_dict(value):
+ if util.is_dict(value):
nest(path, value, schema)
return
- if is_list(value) and value:
+ if util.is_list(value) and value:
for list_v in value:
validate_item(path, list_v, schema)
return
@@ -217,11 +299,11 @@
if want_type not in SCHEMA_TYPES:
raise ValueError('unknown type %r at %r' % (want_type, path))
- if is_dict(value):
+ if util.is_dict(value):
raise ValueError('config item is dict but should be a leaf node of type %r: %r'
% (want_type, path))
- if is_list(value):
+ if util.is_list(value):
for list_v in value:
validate_item(path, list_v, schema)
return
@@ -243,4 +325,73 @@
nest(None, config, schema)
+def generate_schemas():
+ "Generate supported schemas dynamically from objects"
+ obj_dir = '%s/../obj/' % os.path.dirname(os.path.abspath(__file__))
+ for filename in os.listdir(obj_dir):
+ if not filename.endswith(".py"):
+ continue
+ module_name = 'osmo_gsm_tester.obj.%s' % filename[:-3]
+ util.run_python_file_method(module_name, 'on_register_schemas', False)
+
+
+_RESOURCE_TYPES = ['ip_address', 'arfcn']
+
+_RESOURCES_SCHEMA = {
+ 'ip_address[].addr': IPV4,
+ 'arfcn[].arfcn': INT,
+ 'arfcn[].band': BAND,
+ }
+
+_CONFIG_SCHEMA = {}
+
+_WANT_SCHEMA = None
+_ALL_SCHEMA = None
+
+def register_resource_schema(obj_class_str, obj_attr_dict):
+ """Register schema attributes for a resource type.
+ For instance: register_resource_schema_attributes('modem', {'type': schema.STR, 'ki': schema.KI})
+ """
+ global _RESOURCES_SCHEMA
+ global _RESOURCE_TYPES
+ tmpdict = {}
+ for key, val in obj_attr_dict.items():
+ new_key = '%s[].%s' % (obj_class_str, key)
+ tmpdict[new_key] = val
+ combine(_RESOURCES_SCHEMA, tmpdict)
+ if obj_class_str not in _RESOURCE_TYPES:
+ _RESOURCE_TYPES.append(obj_class_str)
+
+def register_config_schema(obj_class_str, obj_attr_dict):
+ """Register schema attributes to configure all instances of an object class.
+ For instance: register_resource_schema_attributes('bsc', {'net.codec_list[]': schema.CODEC})
+ """
+ global _CONFIG_SCHEMA
+ tmpdict = {}
+ for key, val in obj_attr_dict.items():
+ new_key = '%s.%s' % (obj_class_str, key)
+ tmpdict[new_key] = val
+ combine(_CONFIG_SCHEMA, tmpdict)
+
+def get_resources_schema():
+ return _RESOURCES_SCHEMA;
+
+def get_want_schema():
+ global _WANT_SCHEMA
+ if _WANT_SCHEMA is None:
+ _WANT_SCHEMA = util.dict_add(
+ dict([('%s[].times' % r, TIMES) for r in _RESOURCE_TYPES]),
+ get_resources_schema())
+ return _WANT_SCHEMA
+
+def get_all_schema():
+ global _ALL_SCHEMA
+ if _ALL_SCHEMA is None:
+ want_schema = get_want_schema()
+ _ALL_SCHEMA = util.dict_add({ 'defaults.timeout': STR },
+ dict([('config.%s' % key, val) for key, val in _CONFIG_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()]))
+ return _ALL_SCHEMA
+
# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/core/util.py b/src/osmo_gsm_tester/core/util.py
index a5b2bbf..4c7b1dd 100644
--- a/src/osmo_gsm_tester/core/util.py
+++ b/src/osmo_gsm_tester/core/util.py
@@ -370,6 +370,17 @@
def run_python_file(module_name, path):
SourceFileLoader(module_name, path).load_module()
+def run_python_file_method(module_name, func_name, fail_if_missing=True):
+ module_obj = __import__(module_name, globals(), locals(), [func_name])
+ try:
+ func = getattr(module_obj, func_name)
+ except AttributeError as e:
+ if fail_if_missing:
+ raise RuntimeError('function %s not found in %s (%s)' % (func_name, module_name))
+ else:
+ return None
+ return func()
+
def msisdn_inc(msisdn_str):
'add 1 and preserve leading zeros'
return ('%%0%dd' % len(msisdn_str)) % (int(msisdn_str) + 1)
diff --git a/src/osmo_gsm_tester/obj/bsc_osmo.py b/src/osmo_gsm_tester/obj/bsc_osmo.py
index 25cc780..046ef94 100644
--- a/src/osmo_gsm_tester/obj/bsc_osmo.py
+++ b/src/osmo_gsm_tester/obj/bsc_osmo.py
@@ -22,8 +22,16 @@
import pprint
from ..core import log, util, config, template, process
+from ..core import schema
from . import osmo_ctrl, pcap_recorder
+def on_register_schemas():
+ config_schema = {
+ 'net.codec_list[]': schema.CODEC,
+ }
+ schema.register_config_schema('bsc', config_schema)
+
+
class OsmoBsc(log.Origin):
def __init__(self, suite_run, msc, mgw, stp, ip_address):
diff --git a/src/osmo_gsm_tester/obj/bts.py b/src/osmo_gsm_tester/obj/bts.py
index 515b42b..8b05ea0 100644
--- a/src/osmo_gsm_tester/obj/bts.py
+++ b/src/osmo_gsm_tester/obj/bts.py
@@ -21,6 +21,30 @@
from abc import ABCMeta, abstractmethod
from ..core import log, config, schema
+def on_register_schemas():
+ resource_schema = {
+ 'label': schema.STR,
+ 'type': schema.STR,
+ 'addr': schema.IPV4,
+ 'band': schema.BAND,
+ 'direct_pcu': schema.BOOL_STR,
+ 'ciphers[]': schema.CIPHER,
+ 'channel_allocator': schema.CHAN_ALLOCATOR,
+ 'gprs_mode': schema.GPRS_MODE,
+ 'num_trx': schema.UINT,
+ 'max_trx': schema.UINT,
+ 'trx_list[].addr': schema.IPV4,
+ 'trx_list[].hw_addr': schema.HWADDR,
+ 'trx_list[].net_device': schema.STR,
+ 'trx_list[].nominal_power': schema.UINT,
+ 'trx_list[].max_power_red': schema.UINT,
+ 'trx_list[].timeslot_list[].phys_chan_config': schema.PHY_CHAN,
+ 'trx_list[].power_supply.type': schema.STR,
+ 'trx_list[].power_supply.device': schema.STR,
+ 'trx_list[].power_supply.port': schema.STR,
+ }
+ schema.register_resource_schema('bts', resource_schema)
+
class Bts(log.Origin, metaclass=ABCMeta):
##############
diff --git a/src/osmo_gsm_tester/obj/bts_osmo.py b/src/osmo_gsm_tester/obj/bts_osmo.py
index 74f3ec7..a182c47 100644
--- a/src/osmo_gsm_tester/obj/bts_osmo.py
+++ b/src/osmo_gsm_tester/obj/bts_osmo.py
@@ -21,8 +21,18 @@
import tempfile
from abc import ABCMeta, abstractmethod
from ..core import log
+from ..core import schema
from . import bts, pcu_osmo
+def on_register_schemas():
+ resource_schema = {
+ 'ipa_unit_id': schema.UINT,
+ 'direct_pcu': schema.BOOL_STR,
+ 'channel_allocator': schema.CHAN_ALLOCATOR,
+ 'gprs_mode': schema.GPRS_MODE,
+ }
+ schema.register_resource_schema('bts', resource_schema)
+
class OsmoBts(bts.Bts, metaclass=ABCMeta):
##############
diff --git a/src/osmo_gsm_tester/obj/bts_osmotrx.py b/src/osmo_gsm_tester/obj/bts_osmotrx.py
index 5339946..9234fc8 100644
--- a/src/osmo_gsm_tester/obj/bts_osmotrx.py
+++ b/src/osmo_gsm_tester/obj/bts_osmotrx.py
@@ -21,9 +21,25 @@
import pprint
from abc import ABCMeta, abstractmethod
from ..core import log, config, util, template, process, remote
+from ..core import schema
from ..core.event_loop import MainLoop
from . import powersupply, bts_osmo
+def on_register_schemas():
+ resource_schema = {
+ 'osmo_trx.launch_trx': schema.BOOL_STR,
+ 'osmo_trx.type': schema.STR,
+ 'osmo_trx.clock_reference': schema.OSMO_TRX_CLOCK_REF,
+ 'osmo_trx.trx_ip': schema.IPV4,
+ 'osmo_trx.remote_user': schema.STR,
+ 'osmo_trx.dev_args': schema.STR,
+ 'osmo_trx.multi_arfcn': schema.BOOL_STR,
+ 'osmo_trx.max_trxd_version': schema.UINT,
+ 'osmo_trx.channels[].rx_path': schema.STR,
+ 'osmo_trx.channels[].tx_path': schema.STR,
+ }
+ schema.register_resource_schema('bts', resource_schema)
+
class OsmoBtsTrx(bts_osmo.OsmoBtsMainUnit):
##############
# PROTECTED
diff --git a/src/osmo_gsm_tester/obj/enb.py b/src/osmo_gsm_tester/obj/enb.py
index 3c1f771..c652761 100644
--- a/src/osmo_gsm_tester/obj/enb.py
+++ b/src/osmo_gsm_tester/obj/enb.py
@@ -19,7 +19,47 @@
from abc import ABCMeta, abstractmethod
from ..core import log, config
+from ..core import schema
+def on_register_schemas():
+ resource_schema = {
+ 'label': schema.STR,
+ 'type': schema.STR,
+ 'remote_user': schema.STR,
+ 'addr': schema.IPV4,
+ 'gtp_bind_addr': schema.IPV4,
+ 'id': schema.UINT,
+ 'num_prb': schema.UINT,
+ 'transmission_mode': schema.LTE_TRANSMISSION_MODE,
+ 'tx_gain': schema.UINT,
+ 'rx_gain': schema.UINT,
+ 'rf_dev_type': schema.STR,
+ 'rf_dev_args': schema.STR,
+ 'additional_args': schema.STR,
+ 'enable_measurements': schema.BOOL_STR,
+ 'a1_report_type': schema.STR,
+ 'a1_report_value': schema.INT,
+ 'a1_hysteresis': schema.INT,
+ 'a1_time_to_trigger': schema.INT,
+ 'a2_report_type': schema.STR,
+ 'a2_report_value': schema.INT,
+ 'a2_hysteresis': schema.INT,
+ 'a2_time_to_trigger': schema.INT,
+ 'a3_report_type': schema.STR,
+ 'a3_report_value': schema.INT,
+ 'a3_hysteresis': schema.INT,
+ 'a3_time_to_trigger': schema.INT,
+ 'num_cells': schema.UINT,
+ 'cell_list[].cell_id': schema.UINT,
+ 'cell_list[].pci': schema.UINT,
+ 'cell_list[].ncell_list[]': schema.UINT,
+ 'cell_list[].scell_list[]': schema.UINT,
+ 'cell_list[].dl_earfcn': schema.UINT,
+ 'cell_list[].dl_rfemu.type': schema.STR,
+ 'cell_list[].dl_rfemu.addr': schema.IPV4,
+ 'cell_list[].dl_rfemu.ports[]': schema.UINT,
+ }
+ schema.register_resource_schema('enb', resource_schema)
class eNodeB(log.Origin, metaclass=ABCMeta):
diff --git a/src/osmo_gsm_tester/obj/enb_amarisoft.py b/src/osmo_gsm_tester/obj/enb_amarisoft.py
index ec7063c..6f7080a 100644
--- a/src/osmo_gsm_tester/obj/enb_amarisoft.py
+++ b/src/osmo_gsm_tester/obj/enb_amarisoft.py
@@ -21,9 +21,16 @@
import pprint
from ..core import log, util, config, template, process, remote
+from ..core import schema
from . import enb
from . import rfemu
+def on_register_schemas():
+ config_schema = {
+ 'license_server_addr': schema.IPV4,
+ }
+ schema.register_config_schema('amarisoft', config_schema)
+
def rf_type_valid(rf_type_str):
return rf_type_str in ('uhd', 'zmq')
diff --git a/src/osmo_gsm_tester/obj/enb_srs.py b/src/osmo_gsm_tester/obj/enb_srs.py
index 243ffaa..0816ca6 100644
--- a/src/osmo_gsm_tester/obj/enb_srs.py
+++ b/src/osmo_gsm_tester/obj/enb_srs.py
@@ -24,6 +24,14 @@
from . import enb
from . import rfemu
+from ..core import schema
+
+def on_register_schemas():
+ config_schema = {
+ 'enable_pcap': schema.BOOL_STR,
+ }
+ schema.register_config_schema('enb', config_schema)
+
def rf_type_valid(rf_type_str):
return rf_type_str in ('zmq', 'uhd', 'soapy', 'bladerf')
diff --git a/src/osmo_gsm_tester/obj/epc.py b/src/osmo_gsm_tester/obj/epc.py
index f6bddea..c725f76 100644
--- a/src/osmo_gsm_tester/obj/epc.py
+++ b/src/osmo_gsm_tester/obj/epc.py
@@ -19,7 +19,14 @@
from abc import ABCMeta, abstractmethod
from ..core import log, config
+from ..core import schema
+def on_register_schemas():
+ config_schema = {
+ 'type': schema.STR,
+ 'qci': schema.UINT,
+ }
+ schema.register_config_schema('epc', config_schema)
class EPC(log.Origin, metaclass=ABCMeta):
diff --git a/src/osmo_gsm_tester/obj/epc_amarisoft.py b/src/osmo_gsm_tester/obj/epc_amarisoft.py
index afd8aa4..8606e57 100644
--- a/src/osmo_gsm_tester/obj/epc_amarisoft.py
+++ b/src/osmo_gsm_tester/obj/epc_amarisoft.py
@@ -21,8 +21,15 @@
import pprint
from ..core import log, util, config, template, process, remote
+from ..core import schema
from . import epc
+def on_register_schemas():
+ config_schema = {
+ 'license_server_addr': schema.IPV4,
+ }
+ schema.register_config_schema('amarisoft', config_schema)
+
class AmarisoftEPC(epc.EPC):
REMOTE_DIR = '/osmo-gsm-tester-amarisoftepc'
diff --git a/src/osmo_gsm_tester/obj/epc_srs.py b/src/osmo_gsm_tester/obj/epc_srs.py
index ec9dc44..f859df0 100644
--- a/src/osmo_gsm_tester/obj/epc_srs.py
+++ b/src/osmo_gsm_tester/obj/epc_srs.py
@@ -21,8 +21,15 @@
import pprint
from ..core import log, util, config, template, process, remote
+from ..core import schema
from . import epc
+def on_register_schemas():
+ config_schema = {
+ 'enable_pcap': schema.BOOL_STR,
+ }
+ schema.register_config_schema('epc', config_schema)
+
class srsEPC(epc.EPC):
REMOTE_DIR = '/osmo-gsm-tester-srsepc'
diff --git a/src/osmo_gsm_tester/obj/iperf3.py b/src/osmo_gsm_tester/obj/iperf3.py
index 9427770..b9fdfe8 100644
--- a/src/osmo_gsm_tester/obj/iperf3.py
+++ b/src/osmo_gsm_tester/obj/iperf3.py
@@ -21,8 +21,15 @@
import json
from ..core import log, util, config, process, remote
+from ..core import schema
from . import pcap_recorder, run_node
+def on_register_schemas():
+ config_schema = {
+ 'time': schema.DURATION,
+ }
+ schema.register_config_schema('iperf3cli', config_schema)
+
def iperf3_result_to_json(log_obj, data):
try:
# Drop non-interesting self-generated output before json:
diff --git a/src/osmo_gsm_tester/obj/ms.py b/src/osmo_gsm_tester/obj/ms.py
index 3dcea7b..b72333a 100644
--- a/src/osmo_gsm_tester/obj/ms.py
+++ b/src/osmo_gsm_tester/obj/ms.py
@@ -19,6 +19,21 @@
from abc import ABCMeta, abstractmethod
from ..core import log
+from ..core import schema
+
+def on_register_schemas():
+ resource_schema = {
+ 'type': schema.STR,
+ 'label': schema.STR,
+ 'path': schema.STR,
+ 'imsi': schema.IMSI,
+ 'ki': schema.KI,
+ 'auth_algo': schema.AUTH_ALGO,
+ 'apn_ipaddr': schema.IPV4,
+ 'ciphers[]': schema.CIPHER,
+ 'features[]': schema.MODEM_FEATURE
+ }
+ schema.register_resource_schema('modem', resource_schema)
class MS(log.Origin, metaclass=ABCMeta):
"""Base for everything about mobile/modem and SIMs."""
diff --git a/src/osmo_gsm_tester/obj/ms_amarisoft.py b/src/osmo_gsm_tester/obj/ms_amarisoft.py
index 6fd80ee..46a92dc 100644
--- a/src/osmo_gsm_tester/obj/ms_amarisoft.py
+++ b/src/osmo_gsm_tester/obj/ms_amarisoft.py
@@ -21,10 +21,17 @@
import pprint
from ..core import log, util, config, template, process, remote
+from ..core import schema
from ..core.event_loop import MainLoop
from .run_node import RunNode
from .ms import MS
+def on_register_schemas():
+ config_schema = {
+ 'license_server_addr': schema.IPV4,
+ }
+ schema.register_config_schema('amarisoft', config_schema)
+
def rf_type_valid(rf_type_str):
return rf_type_str in ('uhd', 'zmq')
diff --git a/src/osmo_gsm_tester/obj/ms_srs.py b/src/osmo_gsm_tester/obj/ms_srs.py
index cdc8d18..7d08b66 100644
--- a/src/osmo_gsm_tester/obj/ms_srs.py
+++ b/src/osmo_gsm_tester/obj/ms_srs.py
@@ -21,6 +21,7 @@
import pprint
from ..core import log, util, config, template, process, remote
+from ..core import schema
from .run_node import RunNode
from ..core.event_loop import MainLoop
from .ms import MS
@@ -28,6 +29,26 @@
def rf_type_valid(rf_type_str):
return rf_type_str in ('zmq', 'uhd', 'soapy', 'bladerf')
+def on_register_schemas():
+ resource_schema = {
+ 'remote_user': schema.STR,
+ 'addr': schema.IPV4,
+ 'rf_dev_type': schema.STR,
+ 'rf_dev_args': schema.STR,
+ 'num_carriers': schema.UINT,
+ 'additional_args': schema.STR,
+ 'airplane_t_on_ms': schema.INT,
+ 'airplane_t_off_ms': schema.INT,
+ 'tx_gain': schema.UINT,
+ 'rx_gain': schema.UINT,
+ }
+ schema.register_resource_schema('modem', resource_schema)
+
+ config_schema = {
+ 'enable_pcap': schema.BOOL_STR,
+ }
+ schema.register_config_schema('modem', config_schema)
+
#reference: srsLTE.git srslte_symbol_sz()
def num_prb2symbol_sz(num_prb):
if num_prb <= 6:
diff --git a/src/osmo_gsm_tester/obj/msc_osmo.py b/src/osmo_gsm_tester/obj/msc_osmo.py
index cb8894f..048934e 100644
--- a/src/osmo_gsm_tester/obj/msc_osmo.py
+++ b/src/osmo_gsm_tester/obj/msc_osmo.py
@@ -21,8 +21,15 @@
import pprint
from ..core import log, util, config, template, process
+from ..core import schema
from . import osmo_ctrl, pcap_recorder, smsc
+def on_register_schemas():
+ resource_schema = {
+ 'path': schema.STR,
+ }
+ schema.register_resource_schema('modem', resource_schema)
+
class OsmoMsc(log.Origin):
def __init__(self, suite_run, hlr, mgw, stp, ip_address):
diff --git a/src/osmo_gsm_tester/obj/osmocon.py b/src/osmo_gsm_tester/obj/osmocon.py
index 1fad239..6f6ac2a 100644
--- a/src/osmo_gsm_tester/obj/osmocon.py
+++ b/src/osmo_gsm_tester/obj/osmocon.py
@@ -21,8 +21,16 @@
import tempfile
from ..core import log, util, process
+from ..core import schema
from ..core.event_loop import MainLoop
+def on_register_schemas():
+ resource_schema = {
+ 'serial_device': schema.STR,
+ }
+ schema.register_resource_schema('osmocon_phone', resource_schema)
+
+
class Osmocon(log.Origin):
FIRMWARE_FILE="opt/osmocom-bb/target/firmware/board/compal_e88/layer1.compalram.bin"
diff --git a/src/osmo_gsm_tester/obj/run_node.py b/src/osmo_gsm_tester/obj/run_node.py
index e9f43a1..26c85df 100644
--- a/src/osmo_gsm_tester/obj/run_node.py
+++ b/src/osmo_gsm_tester/obj/run_node.py
@@ -18,6 +18,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ..core import log
+from ..core import schema
+
+def on_register_schemas():
+ resource_schema = {
+ 'run_type': schema.STR,
+ 'run_addr': schema.IPV4,
+ 'ssh_user': schema.STR,
+ 'ssh_addr': schema.IPV4,
+ }
+ schema.register_resource_schema('run_node', resource_schema)
+
class RunNode(log.Origin):
diff --git a/src/osmo_gsm_tester/resource.py b/src/osmo_gsm_tester/resource.py
index 20302d3..b216c50 100644
--- a/src/osmo_gsm_tester/resource.py
+++ b/src/osmo_gsm_tester/resource.py
@@ -46,120 +46,6 @@
R_MODEM = 'modem'
R_OSMOCON = 'osmocon_phone'
R_ENB = 'enb'
-R_ALL = (R_IP_ADDRESS, R_RUN_NODE, R_BTS, R_ARFCN, R_MODEM, R_OSMOCON, R_ENB)
-
-RESOURCES_SCHEMA = {
- 'ip_address[].addr': schema.IPV4,
- 'run_node[].run_type': schema.STR,
- 'run_node[].run_addr': schema.IPV4,
- 'run_node[].ssh_user': schema.STR,
- 'run_node[].ssh_addr': schema.IPV4,
- 'bts[].label': schema.STR,
- 'bts[].type': schema.STR,
- 'bts[].ipa_unit_id': schema.UINT,
- 'bts[].addr': schema.IPV4,
- 'bts[].band': schema.BAND,
- 'bts[].direct_pcu': schema.BOOL_STR,
- 'bts[].ciphers[]': schema.CIPHER,
- 'bts[].channel_allocator': schema.CHAN_ALLOCATOR,
- 'bts[].gprs_mode': schema.GPRS_MODE,
- 'bts[].num_trx': schema.UINT,
- 'bts[].max_trx': schema.UINT,
- 'bts[].trx_list[].addr': schema.IPV4,
- 'bts[].trx_list[].hw_addr': schema.HWADDR,
- 'bts[].trx_list[].net_device': schema.STR,
- 'bts[].trx_list[].nominal_power': schema.UINT,
- 'bts[].trx_list[].max_power_red': schema.UINT,
- 'bts[].trx_list[].timeslot_list[].phys_chan_config': schema.PHY_CHAN,
- 'bts[].trx_list[].power_supply.type': schema.STR,
- 'bts[].trx_list[].power_supply.device': schema.STR,
- 'bts[].trx_list[].power_supply.port': schema.STR,
- 'bts[].osmo_trx.launch_trx': schema.BOOL_STR,
- 'bts[].osmo_trx.type': schema.STR,
- 'bts[].osmo_trx.clock_reference': schema.OSMO_TRX_CLOCK_REF,
- 'bts[].osmo_trx.trx_ip': schema.IPV4,
- 'bts[].osmo_trx.remote_user': schema.STR,
- 'bts[].osmo_trx.dev_args': schema.STR,
- 'bts[].osmo_trx.multi_arfcn': schema.BOOL_STR,
- 'bts[].osmo_trx.max_trxd_version': schema.UINT,
- 'bts[].osmo_trx.channels[].rx_path': schema.STR,
- 'bts[].osmo_trx.channels[].tx_path': schema.STR,
- 'enb[].label': schema.STR,
- 'enb[].type': schema.STR,
- 'enb[].remote_user': schema.STR,
- 'enb[].addr': schema.IPV4,
- 'enb[].gtp_bind_addr': schema.IPV4,
- 'enb[].id': schema.UINT,
- 'enb[].num_prb': schema.UINT,
- 'enb[].transmission_mode': schema.LTE_TRANSMISSION_MODE,
- 'enb[].tx_gain': schema.UINT,
- 'enb[].rx_gain': schema.UINT,
- 'enb[].rf_dev_type': schema.STR,
- 'enb[].rf_dev_args': schema.STR,
- 'enb[].additional_args': schema.STR,
- 'enb[].enable_measurements': schema.BOOL_STR,
- 'enb[].a1_report_type': schema.STR,
- 'enb[].a1_report_value': schema.INT,
- 'enb[].a1_hysteresis': schema.INT,
- 'enb[].a1_time_to_trigger': schema.INT,
- 'enb[].a2_report_type': schema.STR,
- 'enb[].a2_report_value': schema.INT,
- 'enb[].a2_hysteresis': schema.INT,
- 'enb[].a2_time_to_trigger': schema.INT,
- 'enb[].a3_report_type': schema.STR,
- 'enb[].a3_report_value': schema.INT,
- 'enb[].a3_hysteresis': schema.INT,
- 'enb[].a3_time_to_trigger': schema.INT,
- 'enb[].num_cells': schema.UINT,
- 'enb[].cell_list[].cell_id': schema.UINT,
- 'enb[].cell_list[].pci': schema.UINT,
- 'enb[].cell_list[].ncell_list[]': schema.UINT,
- 'enb[].cell_list[].scell_list[]': schema.UINT,
- 'enb[].cell_list[].dl_earfcn': schema.UINT,
- 'enb[].cell_list[].dl_rfemu.type': schema.STR,
- 'enb[].cell_list[].dl_rfemu.addr': schema.IPV4,
- 'enb[].cell_list[].dl_rfemu.ports[]': schema.UINT,
- 'arfcn[].arfcn': schema.INT,
- 'arfcn[].band': schema.BAND,
- 'modem[].type': schema.STR,
- 'modem[].label': schema.STR,
- 'modem[].path': schema.STR,
- 'modem[].imsi': schema.IMSI,
- 'modem[].ki': schema.KI,
- 'modem[].auth_algo': schema.AUTH_ALGO,
- 'modem[].apn_ipaddr': schema.IPV4,
- 'modem[].remote_user': schema.STR,
- 'modem[].addr': schema.IPV4,
- 'modem[].ciphers[]': schema.CIPHER,
- 'modem[].features[]': schema.MODEM_FEATURE,
- 'modem[].rf_dev_type': schema.STR,
- 'modem[].rf_dev_args': schema.STR,
- 'modem[].num_carriers': schema.UINT,
- 'modem[].additional_args': schema.STR,
- 'modem[].airplane_t_on_ms': schema.INT,
- 'modem[].airplane_t_off_ms': schema.INT,
- 'modem[].tx_gain': schema.UINT,
- 'modem[].rx_gain': schema.UINT,
- 'osmocon_phone[].serial_device': schema.STR,
- }
-
-WANT_SCHEMA = util.dict_add(
- dict([('%s[].times' % r, schema.TIMES) for r in R_ALL]),
- RESOURCES_SCHEMA)
-
-CONF_SCHEMA = util.dict_add(
- { 'defaults.timeout': schema.STR,
- 'config.bsc.net.codec_list[]': schema.CODEC,
- 'config.enb.enable_pcap': schema.BOOL_STR,
- 'config.epc.type': schema.STR,
- 'config.epc.qci': schema.UINT,
- 'config.epc.enable_pcap': schema.BOOL_STR,
- 'config.modem.enable_pcap': schema.BOOL_STR,
- 'config.amarisoft.license_server_addr': schema.IPV4,
- 'config.iperf3cli.time': schema.DURATION,
- },
- 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,
@@ -204,7 +90,7 @@
self.read_conf()
def read_conf(self):
- self.all_resources = Resources(config.read(self.config_path, RESOURCES_SCHEMA))
+ self.all_resources = Resources(config.read(self.config_path, schema.get_resources_schema()))
self.all_resources.set_hashes()
def reserve(self, origin, want, modifiers):
@@ -239,8 +125,8 @@
'modem': [ {}, {} ],
}
'''
- schema.validate(want, RESOURCES_SCHEMA)
- schema.validate(modifiers, RESOURCES_SCHEMA)
+ schema.validate(want, schema.get_resources_schema())
+ schema.validate(modifiers, schema.get_resources_schema())
origin_id = origin.origin_id()
@@ -480,7 +366,7 @@
def add(self, more):
if more is self:
raise RuntimeError('adding a list of resources to itself?')
- config.add(self, copy.deepcopy(more))
+ schema.add(self, copy.deepcopy(more))
def mark_reserved_by(self, origin_id):
for key, item_list in self.items():
diff --git a/src/osmo_gsm_tester/suite.py b/src/osmo_gsm_tester/suite.py
index fecb7a6..6d2916c 100644
--- a/src/osmo_gsm_tester/suite.py
+++ b/src/osmo_gsm_tester/suite.py
@@ -21,7 +21,7 @@
import sys
import time
import pprint
-from .core import config, log, util, process
+from .core import config, log, util, process, schema
from .core.event_loop import MainLoop
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
from .obj import run_node
@@ -50,7 +50,7 @@
raise RuntimeError('No such directory: %r' % self.suite_dir)
self.conf = config.read(os.path.join(self.suite_dir,
SuiteDefinition.CONF_FILENAME),
- resource.CONF_SCHEMA)
+ schema.get_all_schema())
self.load_test_basenames()
def load_test_basenames(self):
@@ -143,7 +143,7 @@
log.dbg(scenario=scenario.name(), conf=c)
if c is None:
continue
- config.combine(combination, c)
+ schema.combine(combination, c)
return combination
def get_run_dir(self):
@@ -486,7 +486,7 @@
def load_suite_scenario_str(suite_scenario_str):
suite_name, scenario_names = parse_suite_scenario_str(suite_scenario_str)
suite = load(suite_name)
- scenarios = [config.get_scenario(scenario_name, resource.CONF_SCHEMA) for scenario_name in scenario_names]
+ scenarios = [config.get_scenario(scenario_name, schema.get_all_schema()) for scenario_name in scenario_names]
return (suite_scenario_str, suite, scenarios)
def bts_obj(suite_run, conf):
--
To view, visit https://gerrit.osmocom.org/c/osmo-gsm-tester/+/18023
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-gsm-tester
Gerrit-Branch: master
Gerrit-Change-Id: I8fd6773c51d19405a585977af4ed72cad2b21db1
Gerrit-Change-Number: 18023
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200504/6e8a0907/attachment.htm>