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>