blob: 15cb26f9a7d3aa4fe7afe51673ac01a8a56826d5 [file] [log] [blame]
Harald Welte03c0e562017-12-09 02:55:12 +01001module Osmocom_CTRL_Functions {
Harald Welte35bb7162018-01-03 21:07:52 +01002
3/* Definition of helper functions for the Osmocom CTRL interface.
4 *
5 * As opposed to many other parts of the Osmocom TTCN-3 code base, this module
6 * implements blocking functions, instead of asynchronous functions. The
7 * rationale for this is simple: One normally wants to inquire a value or set
8 * a value and not continue the main program until that operation is complete.
Pau Espin Pedrol76ba5412019-06-10 11:00:33 +02009 *
Harald Welte35bb7162018-01-03 21:07:52 +010010 * CTRL is a machine-type protocol on how external programs can interact with
11 * an Osmocom program in a structured way. It is intended for programmatic
12 * access (by other software), as opposed to the VTY interface intended for
13 * human consumption.
14 *
15 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
16 * All rights reserved.
17 *
18 * Released under the terms of GNU General Public License, Version 2 or
19 * (at your option) any later version.
20 */
21
22
Harald Welte03c0e562017-12-09 02:55:12 +010023 import from Osmocom_CTRL_Types all;
24 import from IPA_Emulation all;
25
26 private function f_gen_rand_id() return CtrlId {
27 return int2str(float2int(rnd()*999999999.0));
28 }
29
30 /* perform a given GET Operation */
31 function f_ctrl_get(IPA_CTRL_PT pt, CtrlVariable variable) return CtrlValue {
32 timer T := 2.0;
33 var CtrlMessage rx;
34 var CtrlId id := f_gen_rand_id();
35 pt.send(ts_CtrlMsgGet(id, variable));
36 T.start;
37 alt {
Harald Weltefdfa0462017-12-10 18:09:40 +010038 [] pt.receive(tr_CtrlMsgGetRepl(id, variable)) -> value rx {
39 }
Harald Welte95a47812017-12-09 14:19:03 +010040 [] pt.receive(tr_CtrlMsgTrap) { repeat; }
Harald Welte03c0e562017-12-09 02:55:12 +010041 [] pt.receive(tr_CtrlMsgError) -> value rx {
42 setverdict(fail, "Error in CTRL GET ", variable, ": ", rx.err.reason);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020043 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010044 }
45 [] T.timeout {
46 setverdict(fail, "Timeout waiting for CTRL GET REPLY ", variable);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020047 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010048 }
49 }
50 return rx.resp.val;
51 }
52
53 /* perform a given SET Operation */
54 function f_ctrl_set(IPA_CTRL_PT pt, CtrlVariable variable, CtrlValue val) {
55 timer T := 2.0;
56 var CtrlMessage rx;
57 var CtrlId id := f_gen_rand_id();
58 pt.send(ts_CtrlMsgSet(id, variable, val));
59 T.start;
60 alt {
61 [] pt.receive(tr_CtrlMsgSetRepl(id, variable, val)) { }
Harald Welte95a47812017-12-09 14:19:03 +010062 [] pt.receive(tr_CtrlMsgTrap) { repeat; }
Harald Welte03c0e562017-12-09 02:55:12 +010063 [] pt.receive(tr_CtrlMsgError) -> value rx {
64 setverdict(fail, "Error in CTRL GET ", variable, ": ", rx.err.reason);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020065 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010066 }
67 [] T.timeout {
68 setverdict(fail, "Timeout waiting for CTRL SET REPLY ", variable);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020069 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010070 }
71 }
72 }
73
Pau Espin Pedrol579cd7a2019-06-10 21:01:30 +020074 /* send a TRAP */
75 function f_ctrl_trap(IPA_CTRL_PT pt, CtrlVariable variable, CtrlValue val) {
76 pt.send(ts_CtrlMsgTrap(variable, val));
77 }
78
Harald Welte03c0e562017-12-09 02:55:12 +010079 /* Expect a matching TRAP */
80 function f_ctrl_exp_trap(IPA_CTRL_PT pt, template CtrlVariable variable,
Pau Espin Pedrol579cd7a2019-06-10 21:01:30 +020081 template CtrlValue val := ?, float timeout_val := 2.0)
82 return CtrlValue {
83 timer T := timeout_val;
Harald Welte03c0e562017-12-09 02:55:12 +010084 var CtrlMessage rx;
85 T.start;
86 alt {
Harald Weltefdfa0462017-12-10 18:09:40 +010087 [] pt.receive(tr_CtrlMsgTrap(variable, val)) -> value rx {
88 }
Harald Welte03c0e562017-12-09 02:55:12 +010089 [] T.timeout {
90 setverdict(fail, "Timeout waiting for TRAP ", variable);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020091 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010092 }
93 }
94 return rx.trap.val;
95 }
Harald Welte852a3842017-12-09 14:19:36 +010096
Pau Espin Pedrol579cd7a2019-06-10 21:01:30 +020097 /* Expect a matching SET, optionally answer */
98 function f_ctrl_exp_set(IPA_CTRL_PT pt, template CtrlVariable variable,
99 template CtrlValue val := ?,
100 template (omit) CtrlValue rsp := omit,
101 float timeout_val := 2.0)
102 return CtrlValue {
103 timer T := timeout_val;
104 var CtrlMessage rx;
105 T.start;
106 alt {
107 [] pt.receive(tr_CtrlMsgSet(?, variable, val)) -> value rx {
108 if (ispresent(rsp)) {
109 pt.send(ts_CtrlMsgSetRepl(rx.cmd.id, valueof(variable), valueof(rsp)));
110 }
111 }
112 [] T.timeout {
113 setverdict(fail, "Timeout waiting for SET ", variable);
114 mtc.stop;
115 }
116 }
117 return rx.cmd.val;
118 }
119
Pau Espin Pedrol9a5b8ff2021-01-04 19:01:31 +0100120 /* Expect a matching SET, optionally answer */
121 function f_ctrl_exp_get(IPA_CTRL_PT pt, template CtrlVariable variable,
122 template (omit) CtrlValue rsp := omit,
123 float timeout_val := 2.0) {
124 timer T := timeout_val;
125 var CtrlMessage rx;
126 T.start;
127 alt {
128 [] pt.receive(tr_CtrlMsgGet(?, variable)) -> value rx {
129 if (ispresent(rsp)) {
130 pt.send(ts_CtrlMsgGetRepl(rx.cmd.id, valueof(variable), valueof(rsp)));
131 }
132 }
133 [] T.timeout {
134 setverdict(fail, "Timeout waiting for GET ", variable);
135 mtc.stop;
136 }
137 }
138 }
139
Harald Welte852a3842017-12-09 14:19:36 +0100140 /* Expect a matching GET result */
141 function f_ctrl_get_exp(IPA_CTRL_PT pt, CtrlVariable variable, template CtrlValue exp) {
142 var charstring ctrl_resp;
143 ctrl_resp := f_ctrl_get(pt, variable);
144 if (not match(ctrl_resp, exp)) {
145 setverdict(fail, "Unexpected " & variable & ":" & ctrl_resp);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200146 mtc.stop;
Harald Welte852a3842017-12-09 14:19:36 +0100147 }
148 }
149
Harald Welte3ec493f2017-12-09 16:25:29 +0100150 template charstring ts_ctrl_ratectr(CtrlVariable grp, integer instance, CtrlVariable name,
151 CtrlVariable kind := "abs") :=
152 "rate_ctr." & kind & "." & grp & "." & int2str(instance) & "." & name;
153
154 function f_ctrl_get_ratectr_abs(IPA_CTRL_PT pt, CtrlVariable grp, integer instance,
155 CtrlVariable name) return integer {
156 return str2int(f_ctrl_get(pt, valueof(ts_ctrl_ratectr(grp, instance, name))));
157 }
158
159 function f_ctrl_get_exp_ratectr_abs(IPA_CTRL_PT pt, CtrlVariable grp, integer instance,
160 CtrlVariable name, template integer exp) {
161 var charstring ctrl_resp;
162 var CtrlVariable variable := valueof(ts_ctrl_ratectr(grp, instance, name));
163 ctrl_resp := f_ctrl_get(pt, variable);
164 if (not match(str2int(ctrl_resp), exp)) {
165 setverdict(fail, variable & " value " & ctrl_resp & " didn't match ", exp);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200166 mtc.stop;
Harald Welte3ec493f2017-12-09 16:25:29 +0100167 }
168 }
169
Harald Welte852a3842017-12-09 14:19:36 +0100170
Neels Hofmeyr74180592020-06-20 21:40:52 +0200171 /* --- Retrieve and verify rate counter values in bulk ---
172 *
173 * BSC_Tests.ttcn shows a nice way to conveniently shorten the code needed to use these functions, see
174 * f_ctrs_msc_init() and f_ctrs_msc_expect().
175 *
176 * Here also a full usage example:
177 *
178 * const CounterNameVals my_counternames := {
179 * { "mscpool:subscr:new", 0 },
180 * { "mscpool:subscr:known", 0 },
181 * { "mscpool:subscr:attach_lost", 0 },
182 * };
183 *
184 * var CounterNameValsList my_counters := f_counter_name_vals_get_n(instance_name := "msc", instance_count := 3,
185 * counternames := my_counternames);
186 *
187 * // run some tests that increment rate counters in the program,
188 * // and increment expected counters accordingly:
Neels Hofmeyr1393d022020-08-29 01:17:33 +0000189 * f_counter_name_vals_list_add(my_counters, instance_nr := 1, "mscpool:subscr:new", 7);
190 * f_counter_name_vals_list_add(my_counters, instance_nr := 2, "mscpool:subscr:attach_lost", 3);
Neels Hofmeyr74180592020-06-20 21:40:52 +0200191 *
192 * // verify that the program reflects the expected counters:
193 * f_counter_name_vals_expect_n(instance_name := "msc", my_counters);
194 *
195 * // run some more tests...
Neels Hofmeyr1393d022020-08-29 01:17:33 +0000196 * f_counter_name_vals_list_add(my_counters, instance_nr := 0, "mscpool:subscr:known");
Neels Hofmeyr74180592020-06-20 21:40:52 +0200197 * // and verify again
198 * f_counter_name_vals_expect_n(instance_name := "msc", my_counters);
199 */
200
201 /* One counter value, e.g. { "name", 23 } */
202 type record CounterNameVal {
203 charstring name,
204 integer val
205 }
206
207 /* List of one instance's counters,
208 * e.g. { {"foo",23}, {"bar",42} }
209 */
210 type record of CounterNameVal CounterNameVals;
211
212 /* List of numerous instances' counters,
213 * e.g. { { {"foo",23}, {"bar",42} },
214 * { {"foo",23}, {"bar",42} } }
215 */
216 type record of CounterNameVals CounterNameValsList;
217
218 /* Retrieve one instance's rate counter values of the given names. */
219 function f_counter_name_vals_get(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr,
220 CounterNameVals counternames)
221 return CounterNameVals {
222 var CounterNameVals vals;
223 for (var integer i := 0; i < lengthof(counternames); i := i + 1) {
224 vals[i] := {
225 name := counternames[i].name,
226 val := f_ctrl_get_ratectr_abs(pt, instance_name, instance_nr, counternames[i].name)
227 };
228 }
229 return vals;
230 }
231
232 /* Retrieve the first N instances' rate counter values of the given names */
233 function f_counter_name_vals_get_n(IPA_CTRL_PT pt, charstring instance_name := "msc",
234 integer instance_count, CounterNameVals counternames)
235 return CounterNameValsList {
236 var CounterNameValsList valslist;
237 for (var integer instance_nr := 0; instance_nr < instance_count; instance_nr := instance_nr + 1) {
238 valslist[instance_nr] := f_counter_name_vals_get(pt, instance_name, instance_nr, counternames);
239 }
240 log("retrieved rate counters: ", instance_name, ": ", valslist);
241 return valslist;
242 }
243
244 /* In a list of one instance's counters, increment a specifically named counter. */
Neels Hofmeyr9656e922020-06-30 01:27:01 +0200245 function f_counter_name_vals_add(inout CounterNameVals vals, charstring countername, integer val := 1)
246 {
Neels Hofmeyr74180592020-06-20 21:40:52 +0200247 for (var integer i := 0; i < lengthof(vals); i := i + 1) {
248 if (vals[i].name == countername) {
249 vals[i].val := vals[i].val + val;
Neels Hofmeyr9656e922020-06-30 01:27:01 +0200250 return;
Neels Hofmeyr74180592020-06-20 21:40:52 +0200251 }
252 }
253 /* name not found, append */
254 vals[lengthof(vals)] := {
255 name := countername,
256 val := val
257 }
Neels Hofmeyr74180592020-06-20 21:40:52 +0200258 }
259
260 /* In a list of several instances' counters, increment a specific instance's specifically named counter. */
Neels Hofmeyr9656e922020-06-30 01:27:01 +0200261 function f_counter_name_vals_list_add(inout CounterNameValsList vals, integer instance_nr,
Neels Hofmeyr74180592020-06-20 21:40:52 +0200262 charstring countername, integer val := 1)
Neels Hofmeyr9656e922020-06-30 01:27:01 +0200263 {
264 f_counter_name_vals_add(vals[instance_nr], countername, val);
Neels Hofmeyr74180592020-06-20 21:40:52 +0200265 }
266
267 /* For a specific instance, call f_counter_name_vals_get() and compare with expected counter values.
268 * Set the test verdict accordingly. */
269 function f_counter_name_vals_expect(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr,
270 CounterNameVals vals) {
Pau Espin Pedrol40a02952021-02-05 12:18:06 +0100271 var CounterNameVals last := f_counter_name_vals_get(pt, instance_name, instance_nr, vals);
Neels Hofmeyr74180592020-06-20 21:40:52 +0200272 for (var integer i := 0; i < lengthof(vals); i := i + 1) {
Pau Espin Pedrol40a02952021-02-05 12:18:06 +0100273 if (last[i].name != vals[i].name) {
Neels Hofmeyr74180592020-06-20 21:40:52 +0200274 setverdict(fail, "Internal error");
275 }
Pau Espin Pedrol40a02952021-02-05 12:18:06 +0100276 if (last[i].val != vals[i].val) {
Neels Hofmeyr74180592020-06-20 21:40:52 +0200277 setverdict(fail, "Rate counter mismatch: ", instance_name, " ", instance_nr,
Pau Espin Pedrol40a02952021-02-05 12:18:06 +0100278 " ", vals[i].name, " is at ", last[i].val, " but expected ", vals[i].val);
Neels Hofmeyr74180592020-06-20 21:40:52 +0200279 }
280 }
281 setverdict(pass);
282 }
283
284 /* For N instances, call f_counter_name_vals_get() and compare with expected counter values.
285 * Set the test verdict accordingly. The number of instances is given by lengthof(valslist). */
286 function f_counter_name_vals_expect_n(IPA_CTRL_PT pt, charstring instance_name, CounterNameValsList valslist) {
287 for (var integer instance_nr := 0; instance_nr < lengthof(valslist); instance_nr := instance_nr + 1) {
288 f_counter_name_vals_expect(pt, instance_name, instance_nr, valslist[instance_nr]);
289 }
290 }
291
Harald Welte03c0e562017-12-09 02:55:12 +0100292}