2017-03-28 10:16:58 +00:00
|
|
|
# osmo_gsm_tester: automated cellular network hardware tests
|
|
|
|
# Proxy to templating engine to handle files
|
|
|
|
#
|
|
|
|
# Copyright (C) 2016-2017 by sysmocom - s.f.m.c. GmbH
|
|
|
|
#
|
|
|
|
# Author: Neels Hofmeyr <neels@hofmeyr.de>
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
2017-06-03 07:51:45 +00:00
|
|
|
# it under the terms of the GNU General Public License as
|
2017-03-28 10:16:58 +00:00
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2017-06-03 07:51:45 +00:00
|
|
|
# GNU General Public License for more details.
|
2017-03-28 10:16:58 +00:00
|
|
|
#
|
2017-06-03 07:51:45 +00:00
|
|
|
# You should have received a copy of the GNU General Public License
|
2017-03-28 10:16:58 +00:00
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2019-02-27 04:33:21 +00:00
|
|
|
import os
|
Introduce parametrized scenario files support
The idea is to have something similar to systemd template unit files:
https://fedoramagazine.org/systemd-template-unit-files/
Specially for modifiers, one finds the situation where same scenario structure
has to be created with lots of different values.
For instance, let's say we want to test with different eNodeB num_prb values:
[6, 15, 25, 50, 75,100]
Right now we'd need to create one scenario file for each of them, for instance:
mod-enb-nprb6.conf
mod-enb-nprb15.conf
mod-enb-nprb25.conf
mod-enb-nprb50.conf
mod-enb-nprb75.conf
mod-enb-nprb100.conf
And each of them containing something like (changing the num_prb value):
"""
modifiers:
enb:
- num_prb: 75
"""
Instead, we can now have one unique file mod-enb-nprb@.conf:
"""
modifiers:
enb:
- num_prb: ${param1}
"""
The general syntax is: "scenario-name@param1,param2,param3".
So "@" splits between scenario name and parameter list, and "," splits
between parameters.
For instance, one can now run following suite with scenario:
"4g:srsenb-rftype-uhd+srsue-rftype-uhd+mod-enb-nprb@75"
Related: OS#4424
Change-Id: Icfcba15b937225aa4b1f322a8005fcd57db1d1ca
2020-02-27 16:03:15 +00:00
|
|
|
from mako.lookup import TemplateLookup, Template
|
2017-03-28 10:16:58 +00:00
|
|
|
|
|
|
|
from . import log
|
2017-03-28 12:30:28 +00:00
|
|
|
from .util import dict2obj
|
2017-03-28 10:16:58 +00:00
|
|
|
|
|
|
|
_lookup = None
|
fix and refactor logging: drop 'with', simplify
With the recent fix of the junit report related issues, another issue arose:
the 'with log.Origin' was changed to disallow __enter__ing an object twice to
fix problems, now still code would fail because it tries to do 'with' on the
same object twice. The only reason is to ensure that logging is associated with
a given object. Instead of complicating even more, implement differently.
Refactor logging to simplify use: drop the 'with Origin' style completely, and
instead use the python stack to determine which objects are created by which,
and which object to associate a log statement with.
The new way: we rely on the convention that each class instance has a local
'self' referencing the object instance. If we need to find an origin as a new
object's parent, or to associate a log message with, we traverse each stack
frame, fetching the first local 'self' object that is a log.Origin class
instance.
How to use:
Simply call log.log() anywhere, and it finds an Origin object to log for, from
the stack. Alternatively call self.log() for any Origin() object to skip the
lookup.
Create classes as child class of log.Origin and make sure to call
super().__init__(category, name). This constructor will magically find a parent
Origin on the stack.
When an exception happens, we first escalate the exception up through call
scopes to where ever it is handled by log.log_exn(). This then finds an Origin
object in the traceback's stack frames, no need to nest in 'with' scopes.
Hence the 'with log.Origin' now "happens implicitly", we can write pure natural
python code, no more hassles with scope ordering.
Furthermore, any frame can place additional logging information in a frame by
calling log.ctx(). This is automatically inserted in the ancestry associated
with a log statement / exception.
Change-Id: I5f9b53150f2bb6fa9d63ce27f0806f0ca6a45e90
2017-06-09 23:18:27 +00:00
|
|
|
_logger = log.Origin(log.C_CNF, 'no templates dir set')
|
2017-03-28 10:16:58 +00:00
|
|
|
|
|
|
|
def set_templates_dir(*templates_dirs):
|
|
|
|
global _lookup
|
|
|
|
global _logger
|
|
|
|
if not templates_dirs:
|
|
|
|
# default templates dir is relative to this source file
|
|
|
|
templates_dirs = [os.path.join(os.path.dirname(__file__), 'templates')]
|
|
|
|
for d in templates_dirs:
|
|
|
|
if not os.path.isdir(d):
|
|
|
|
raise RuntimeError('templates dir is not a dir: %r'
|
|
|
|
% os.path.abspath(d))
|
|
|
|
_lookup = TemplateLookup(directories=templates_dirs)
|
fix and refactor logging: drop 'with', simplify
With the recent fix of the junit report related issues, another issue arose:
the 'with log.Origin' was changed to disallow __enter__ing an object twice to
fix problems, now still code would fail because it tries to do 'with' on the
same object twice. The only reason is to ensure that logging is associated with
a given object. Instead of complicating even more, implement differently.
Refactor logging to simplify use: drop the 'with Origin' style completely, and
instead use the python stack to determine which objects are created by which,
and which object to associate a log statement with.
The new way: we rely on the convention that each class instance has a local
'self' referencing the object instance. If we need to find an origin as a new
object's parent, or to associate a log message with, we traverse each stack
frame, fetching the first local 'self' object that is a log.Origin class
instance.
How to use:
Simply call log.log() anywhere, and it finds an Origin object to log for, from
the stack. Alternatively call self.log() for any Origin() object to skip the
lookup.
Create classes as child class of log.Origin and make sure to call
super().__init__(category, name). This constructor will magically find a parent
Origin on the stack.
When an exception happens, we first escalate the exception up through call
scopes to where ever it is handled by log.log_exn(). This then finds an Origin
object in the traceback's stack frames, no need to nest in 'with' scopes.
Hence the 'with log.Origin' now "happens implicitly", we can write pure natural
python code, no more hassles with scope ordering.
Furthermore, any frame can place additional logging information in a frame by
calling log.ctx(). This is automatically inserted in the ancestry associated
with a log statement / exception.
Change-Id: I5f9b53150f2bb6fa9d63ce27f0806f0ca6a45e90
2017-06-09 23:18:27 +00:00
|
|
|
_logger = log.Origin(log.C_CNF, 'Templates')
|
2017-03-28 10:16:58 +00:00
|
|
|
|
|
|
|
def render(name, values):
|
|
|
|
'''feed values dict into template and return rendered result.
|
|
|
|
".tmpl" is added to the name to look it up in the templates dir.'''
|
|
|
|
global _lookup
|
|
|
|
if _lookup is None:
|
|
|
|
set_templates_dir()
|
2017-03-28 12:30:28 +00:00
|
|
|
tmpl_name = name + '.tmpl'
|
fix and refactor logging: drop 'with', simplify
With the recent fix of the junit report related issues, another issue arose:
the 'with log.Origin' was changed to disallow __enter__ing an object twice to
fix problems, now still code would fail because it tries to do 'with' on the
same object twice. The only reason is to ensure that logging is associated with
a given object. Instead of complicating even more, implement differently.
Refactor logging to simplify use: drop the 'with Origin' style completely, and
instead use the python stack to determine which objects are created by which,
and which object to associate a log statement with.
The new way: we rely on the convention that each class instance has a local
'self' referencing the object instance. If we need to find an origin as a new
object's parent, or to associate a log message with, we traverse each stack
frame, fetching the first local 'self' object that is a log.Origin class
instance.
How to use:
Simply call log.log() anywhere, and it finds an Origin object to log for, from
the stack. Alternatively call self.log() for any Origin() object to skip the
lookup.
Create classes as child class of log.Origin and make sure to call
super().__init__(category, name). This constructor will magically find a parent
Origin on the stack.
When an exception happens, we first escalate the exception up through call
scopes to where ever it is handled by log.log_exn(). This then finds an Origin
object in the traceback's stack frames, no need to nest in 'with' scopes.
Hence the 'with log.Origin' now "happens implicitly", we can write pure natural
python code, no more hassles with scope ordering.
Furthermore, any frame can place additional logging information in a frame by
calling log.ctx(). This is automatically inserted in the ancestry associated
with a log statement / exception.
Change-Id: I5f9b53150f2bb6fa9d63ce27f0806f0ca6a45e90
2017-06-09 23:18:27 +00:00
|
|
|
log.ctx(tmpl_name)
|
|
|
|
template = _lookup.get_template(tmpl_name)
|
|
|
|
_logger.dbg('rendering', tmpl_name)
|
2017-03-28 12:30:28 +00:00
|
|
|
|
fix and refactor logging: drop 'with', simplify
With the recent fix of the junit report related issues, another issue arose:
the 'with log.Origin' was changed to disallow __enter__ing an object twice to
fix problems, now still code would fail because it tries to do 'with' on the
same object twice. The only reason is to ensure that logging is associated with
a given object. Instead of complicating even more, implement differently.
Refactor logging to simplify use: drop the 'with Origin' style completely, and
instead use the python stack to determine which objects are created by which,
and which object to associate a log statement with.
The new way: we rely on the convention that each class instance has a local
'self' referencing the object instance. If we need to find an origin as a new
object's parent, or to associate a log message with, we traverse each stack
frame, fetching the first local 'self' object that is a log.Origin class
instance.
How to use:
Simply call log.log() anywhere, and it finds an Origin object to log for, from
the stack. Alternatively call self.log() for any Origin() object to skip the
lookup.
Create classes as child class of log.Origin and make sure to call
super().__init__(category, name). This constructor will magically find a parent
Origin on the stack.
When an exception happens, we first escalate the exception up through call
scopes to where ever it is handled by log.log_exn(). This then finds an Origin
object in the traceback's stack frames, no need to nest in 'with' scopes.
Hence the 'with log.Origin' now "happens implicitly", we can write pure natural
python code, no more hassles with scope ordering.
Furthermore, any frame can place additional logging information in a frame by
calling log.ctx(). This is automatically inserted in the ancestry associated
with a log statement / exception.
Change-Id: I5f9b53150f2bb6fa9d63ce27f0806f0ca6a45e90
2017-06-09 23:18:27 +00:00
|
|
|
return template.render(**dict2obj(values))
|
2017-03-28 10:16:58 +00:00
|
|
|
|
Introduce parametrized scenario files support
The idea is to have something similar to systemd template unit files:
https://fedoramagazine.org/systemd-template-unit-files/
Specially for modifiers, one finds the situation where same scenario structure
has to be created with lots of different values.
For instance, let's say we want to test with different eNodeB num_prb values:
[6, 15, 25, 50, 75,100]
Right now we'd need to create one scenario file for each of them, for instance:
mod-enb-nprb6.conf
mod-enb-nprb15.conf
mod-enb-nprb25.conf
mod-enb-nprb50.conf
mod-enb-nprb75.conf
mod-enb-nprb100.conf
And each of them containing something like (changing the num_prb value):
"""
modifiers:
enb:
- num_prb: 75
"""
Instead, we can now have one unique file mod-enb-nprb@.conf:
"""
modifiers:
enb:
- num_prb: ${param1}
"""
The general syntax is: "scenario-name@param1,param2,param3".
So "@" splits between scenario name and parameter list, and "," splits
between parameters.
For instance, one can now run following suite with scenario:
"4g:srsenb-rftype-uhd+srsue-rftype-uhd+mod-enb-nprb@75"
Related: OS#4424
Change-Id: Icfcba15b937225aa4b1f322a8005fcd57db1d1ca
2020-02-27 16:03:15 +00:00
|
|
|
def render_strbuf_inline(strbuf, values):
|
|
|
|
'''Receive a string containing template syntax, and generate output using
|
|
|
|
passed values.'''
|
|
|
|
mytemplate = Template(strbuf)
|
|
|
|
return mytemplate.render(**dict2obj(values))
|
|
|
|
|
2017-03-28 10:16:58 +00:00
|
|
|
# vim: expandtab tabstop=4 shiftwidth=4
|