273 lines
9.3 KiB
Plaintext
273 lines
9.3 KiB
Plaintext
module Osmocom_CTRL_Functions {
|
|
|
|
/* Definition of helper functions for the Osmocom CTRL interface.
|
|
*
|
|
* As opposed to many other parts of the Osmocom TTCN-3 code base, this module
|
|
* implements blocking functions, instead of asynchronous functions. The
|
|
* rationale for this is simple: One normally wants to inquire a value or set
|
|
* a value and not continue the main program until that operation is complete.
|
|
*
|
|
* CTRL is a machine-type protocol on how external programs can interact with
|
|
* an Osmocom program in a structured way. It is intended for programmatic
|
|
* access (by other software), as opposed to the VTY interface intended for
|
|
* human consumption.
|
|
*
|
|
* (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
|
* All rights reserved.
|
|
*
|
|
* Released under the terms of GNU General Public License, Version 2 or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
|
|
import from Osmocom_CTRL_Types all;
|
|
import from IPA_Emulation all;
|
|
|
|
private function f_gen_rand_id() return CtrlId {
|
|
return int2str(float2int(rnd()*999999999.0));
|
|
}
|
|
|
|
/* perform a given GET Operation */
|
|
function f_ctrl_get(IPA_CTRL_PT pt, CtrlVariable variable) return CtrlValue {
|
|
timer T := 2.0;
|
|
var CtrlMessage rx;
|
|
var CtrlId id := f_gen_rand_id();
|
|
pt.send(ts_CtrlMsgGet(id, variable));
|
|
T.start;
|
|
alt {
|
|
[] pt.receive(tr_CtrlMsgGetRepl(id, variable)) -> value rx {
|
|
}
|
|
[] pt.receive(tr_CtrlMsgTrap) { repeat; }
|
|
[] pt.receive(tr_CtrlMsgError) -> value rx {
|
|
setverdict(fail, "Error in CTRL GET ", variable, ": ", rx.err.reason);
|
|
mtc.stop;
|
|
}
|
|
[] T.timeout {
|
|
setverdict(fail, "Timeout waiting for CTRL GET REPLY ", variable);
|
|
mtc.stop;
|
|
}
|
|
}
|
|
return rx.resp.val;
|
|
}
|
|
|
|
/* perform a given SET Operation */
|
|
function f_ctrl_set(IPA_CTRL_PT pt, CtrlVariable variable, CtrlValue val) {
|
|
timer T := 2.0;
|
|
var CtrlMessage rx;
|
|
var CtrlId id := f_gen_rand_id();
|
|
pt.send(ts_CtrlMsgSet(id, variable, val));
|
|
T.start;
|
|
alt {
|
|
[] pt.receive(tr_CtrlMsgSetRepl(id, variable, val)) { }
|
|
[] pt.receive(tr_CtrlMsgTrap) { repeat; }
|
|
[] pt.receive(tr_CtrlMsgError) -> value rx {
|
|
setverdict(fail, "Error in CTRL GET ", variable, ": ", rx.err.reason);
|
|
mtc.stop;
|
|
}
|
|
[] T.timeout {
|
|
setverdict(fail, "Timeout waiting for CTRL SET REPLY ", variable);
|
|
mtc.stop;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* send a TRAP */
|
|
function f_ctrl_trap(IPA_CTRL_PT pt, CtrlVariable variable, CtrlValue val) {
|
|
pt.send(ts_CtrlMsgTrap(variable, val));
|
|
}
|
|
|
|
/* Expect a matching TRAP */
|
|
function f_ctrl_exp_trap(IPA_CTRL_PT pt, template CtrlVariable variable,
|
|
template CtrlValue val := ?, float timeout_val := 2.0)
|
|
return CtrlValue {
|
|
timer T := timeout_val;
|
|
var CtrlMessage rx;
|
|
T.start;
|
|
alt {
|
|
[] pt.receive(tr_CtrlMsgTrap(variable, val)) -> value rx {
|
|
}
|
|
[] T.timeout {
|
|
setverdict(fail, "Timeout waiting for TRAP ", variable);
|
|
mtc.stop;
|
|
}
|
|
}
|
|
return rx.trap.val;
|
|
}
|
|
|
|
/* Expect a matching SET, optionally answer */
|
|
function f_ctrl_exp_set(IPA_CTRL_PT pt, template CtrlVariable variable,
|
|
template CtrlValue val := ?,
|
|
template (omit) CtrlValue rsp := omit,
|
|
float timeout_val := 2.0)
|
|
return CtrlValue {
|
|
timer T := timeout_val;
|
|
var CtrlMessage rx;
|
|
T.start;
|
|
alt {
|
|
[] pt.receive(tr_CtrlMsgSet(?, variable, val)) -> value rx {
|
|
if (ispresent(rsp)) {
|
|
pt.send(ts_CtrlMsgSetRepl(rx.cmd.id, valueof(variable), valueof(rsp)));
|
|
}
|
|
}
|
|
[] T.timeout {
|
|
setverdict(fail, "Timeout waiting for SET ", variable);
|
|
mtc.stop;
|
|
}
|
|
}
|
|
return rx.cmd.val;
|
|
}
|
|
|
|
/* Expect a matching GET result */
|
|
function f_ctrl_get_exp(IPA_CTRL_PT pt, CtrlVariable variable, template CtrlValue exp) {
|
|
var charstring ctrl_resp;
|
|
ctrl_resp := f_ctrl_get(pt, variable);
|
|
if (not match(ctrl_resp, exp)) {
|
|
setverdict(fail, "Unexpected " & variable & ":" & ctrl_resp);
|
|
mtc.stop;
|
|
}
|
|
}
|
|
|
|
template charstring ts_ctrl_ratectr(CtrlVariable grp, integer instance, CtrlVariable name,
|
|
CtrlVariable kind := "abs") :=
|
|
"rate_ctr." & kind & "." & grp & "." & int2str(instance) & "." & name;
|
|
|
|
function f_ctrl_get_ratectr_abs(IPA_CTRL_PT pt, CtrlVariable grp, integer instance,
|
|
CtrlVariable name) return integer {
|
|
return str2int(f_ctrl_get(pt, valueof(ts_ctrl_ratectr(grp, instance, name))));
|
|
}
|
|
|
|
function f_ctrl_get_exp_ratectr_abs(IPA_CTRL_PT pt, CtrlVariable grp, integer instance,
|
|
CtrlVariable name, template integer exp) {
|
|
var charstring ctrl_resp;
|
|
var CtrlVariable variable := valueof(ts_ctrl_ratectr(grp, instance, name));
|
|
ctrl_resp := f_ctrl_get(pt, variable);
|
|
if (not match(str2int(ctrl_resp), exp)) {
|
|
setverdict(fail, variable & " value " & ctrl_resp & " didn't match ", exp);
|
|
mtc.stop;
|
|
}
|
|
}
|
|
|
|
|
|
/* --- Retrieve and verify rate counter values in bulk ---
|
|
*
|
|
* BSC_Tests.ttcn shows a nice way to conveniently shorten the code needed to use these functions, see
|
|
* f_ctrs_msc_init() and f_ctrs_msc_expect().
|
|
*
|
|
* Here also a full usage example:
|
|
*
|
|
* const CounterNameVals my_counternames := {
|
|
* { "mscpool:subscr:new", 0 },
|
|
* { "mscpool:subscr:known", 0 },
|
|
* { "mscpool:subscr:attach_lost", 0 },
|
|
* };
|
|
*
|
|
* var CounterNameValsList my_counters := f_counter_name_vals_get_n(instance_name := "msc", instance_count := 3,
|
|
* counternames := my_counternames);
|
|
*
|
|
* // run some tests that increment rate counters in the program,
|
|
* // and increment expected counters accordingly:
|
|
* f_counter_name_vals_list_add(my_counters, instance_nr := 1, "mscpool:subscr:new", 7);
|
|
* f_counter_name_vals_list_add(my_counters, instance_nr := 2, "mscpool:subscr:attach_lost", 3);
|
|
*
|
|
* // verify that the program reflects the expected counters:
|
|
* f_counter_name_vals_expect_n(instance_name := "msc", my_counters);
|
|
*
|
|
* // run some more tests...
|
|
* f_counter_name_vals_list_add(my_counters, instance_nr := 0, "mscpool:subscr:known");
|
|
* // and verify again
|
|
* f_counter_name_vals_expect_n(instance_name := "msc", my_counters);
|
|
*/
|
|
|
|
/* One counter value, e.g. { "name", 23 } */
|
|
type record CounterNameVal {
|
|
charstring name,
|
|
integer val
|
|
}
|
|
|
|
/* List of one instance's counters,
|
|
* e.g. { {"foo",23}, {"bar",42} }
|
|
*/
|
|
type record of CounterNameVal CounterNameVals;
|
|
|
|
/* List of numerous instances' counters,
|
|
* e.g. { { {"foo",23}, {"bar",42} },
|
|
* { {"foo",23}, {"bar",42} } }
|
|
*/
|
|
type record of CounterNameVals CounterNameValsList;
|
|
|
|
/* Retrieve one instance's rate counter values of the given names. */
|
|
function f_counter_name_vals_get(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr,
|
|
CounterNameVals counternames)
|
|
return CounterNameVals {
|
|
var CounterNameVals vals;
|
|
for (var integer i := 0; i < lengthof(counternames); i := i + 1) {
|
|
vals[i] := {
|
|
name := counternames[i].name,
|
|
val := f_ctrl_get_ratectr_abs(pt, instance_name, instance_nr, counternames[i].name)
|
|
};
|
|
}
|
|
return vals;
|
|
}
|
|
|
|
/* Retrieve the first N instances' rate counter values of the given names */
|
|
function f_counter_name_vals_get_n(IPA_CTRL_PT pt, charstring instance_name := "msc",
|
|
integer instance_count, CounterNameVals counternames)
|
|
return CounterNameValsList {
|
|
var CounterNameValsList valslist;
|
|
for (var integer instance_nr := 0; instance_nr < instance_count; instance_nr := instance_nr + 1) {
|
|
valslist[instance_nr] := f_counter_name_vals_get(pt, instance_name, instance_nr, counternames);
|
|
}
|
|
log("retrieved rate counters: ", instance_name, ": ", valslist);
|
|
return valslist;
|
|
}
|
|
|
|
/* In a list of one instance's counters, increment a specifically named counter. */
|
|
function f_counter_name_vals_add(inout CounterNameVals vals, charstring countername, integer val := 1)
|
|
{
|
|
for (var integer i := 0; i < lengthof(vals); i := i + 1) {
|
|
if (vals[i].name == countername) {
|
|
vals[i].val := vals[i].val + val;
|
|
return;
|
|
}
|
|
}
|
|
/* name not found, append */
|
|
vals[lengthof(vals)] := {
|
|
name := countername,
|
|
val := val
|
|
}
|
|
}
|
|
|
|
/* In a list of several instances' counters, increment a specific instance's specifically named counter. */
|
|
function f_counter_name_vals_list_add(inout CounterNameValsList vals, integer instance_nr,
|
|
charstring countername, integer val := 1)
|
|
{
|
|
f_counter_name_vals_add(vals[instance_nr], countername, val);
|
|
}
|
|
|
|
/* For a specific instance, call f_counter_name_vals_get() and compare with expected counter values.
|
|
* Set the test verdict accordingly. */
|
|
function f_counter_name_vals_expect(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr,
|
|
CounterNameVals vals) {
|
|
var CounterNameVals now := f_counter_name_vals_get(pt, instance_name, instance_nr, vals);
|
|
for (var integer i := 0; i < lengthof(vals); i := i + 1) {
|
|
if (now[i].name != vals[i].name) {
|
|
setverdict(fail, "Internal error");
|
|
}
|
|
if (now[i].val != vals[i].val) {
|
|
setverdict(fail, "Rate counter mismatch: ", instance_name, " ", instance_nr,
|
|
" ", vals[i].name, " is at ", now[i].val, " but expected ", vals[i].val);
|
|
}
|
|
}
|
|
setverdict(pass);
|
|
}
|
|
|
|
/* For N instances, call f_counter_name_vals_get() and compare with expected counter values.
|
|
* Set the test verdict accordingly. The number of instances is given by lengthof(valslist). */
|
|
function f_counter_name_vals_expect_n(IPA_CTRL_PT pt, charstring instance_name, CounterNameValsList valslist) {
|
|
for (var integer instance_nr := 0; instance_nr < lengthof(valslist); instance_nr := instance_nr + 1) {
|
|
f_counter_name_vals_expect(pt, instance_name, instance_nr, valslist[instance_nr]);
|
|
}
|
|
}
|
|
|
|
}
|