blob: 75f8646902337c4f9b38ff4dada05cb38aeab9b2 [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
Neels Hofmeyrbd94fe72021-10-26 22:04:53 +020026 type record of charstring charstring_list;
27
Harald Welte03c0e562017-12-09 02:55:12 +010028 private function f_gen_rand_id() return CtrlId {
29 return int2str(float2int(rnd()*999999999.0));
30 }
31
32 /* perform a given GET Operation */
33 function f_ctrl_get(IPA_CTRL_PT pt, CtrlVariable variable) return CtrlValue {
34 timer T := 2.0;
35 var CtrlMessage rx;
36 var CtrlId id := f_gen_rand_id();
37 pt.send(ts_CtrlMsgGet(id, variable));
38 T.start;
39 alt {
Harald Weltefdfa0462017-12-10 18:09:40 +010040 [] pt.receive(tr_CtrlMsgGetRepl(id, variable)) -> value rx {
41 }
Harald Welte95a47812017-12-09 14:19:03 +010042 [] pt.receive(tr_CtrlMsgTrap) { repeat; }
Harald Welte03c0e562017-12-09 02:55:12 +010043 [] pt.receive(tr_CtrlMsgError) -> value rx {
44 setverdict(fail, "Error in CTRL GET ", variable, ": ", rx.err.reason);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020045 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010046 }
47 [] T.timeout {
48 setverdict(fail, "Timeout waiting for CTRL GET REPLY ", variable);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020049 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010050 }
51 }
52 return rx.resp.val;
53 }
54
55 /* perform a given SET Operation */
56 function f_ctrl_set(IPA_CTRL_PT pt, CtrlVariable variable, CtrlValue val) {
57 timer T := 2.0;
58 var CtrlMessage rx;
59 var CtrlId id := f_gen_rand_id();
60 pt.send(ts_CtrlMsgSet(id, variable, val));
61 T.start;
62 alt {
63 [] pt.receive(tr_CtrlMsgSetRepl(id, variable, val)) { }
Harald Welte95a47812017-12-09 14:19:03 +010064 [] pt.receive(tr_CtrlMsgTrap) { repeat; }
Harald Welte03c0e562017-12-09 02:55:12 +010065 [] pt.receive(tr_CtrlMsgError) -> value rx {
Neels Hofmeyr8f17e052021-09-07 14:45:31 +020066 setverdict(fail, "Error in CTRL SET ", variable, ": ", rx.err.reason);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020067 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010068 }
69 [] T.timeout {
70 setverdict(fail, "Timeout waiting for CTRL SET REPLY ", variable);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020071 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010072 }
73 }
74 }
75
Pau Espin Pedrol579cd7a2019-06-10 21:01:30 +020076 /* send a TRAP */
77 function f_ctrl_trap(IPA_CTRL_PT pt, CtrlVariable variable, CtrlValue val) {
78 pt.send(ts_CtrlMsgTrap(variable, val));
79 }
80
Harald Welte03c0e562017-12-09 02:55:12 +010081 /* Expect a matching TRAP */
82 function f_ctrl_exp_trap(IPA_CTRL_PT pt, template CtrlVariable variable,
Pau Espin Pedrol579cd7a2019-06-10 21:01:30 +020083 template CtrlValue val := ?, float timeout_val := 2.0)
84 return CtrlValue {
85 timer T := timeout_val;
Harald Welte03c0e562017-12-09 02:55:12 +010086 var CtrlMessage rx;
87 T.start;
88 alt {
Harald Weltefdfa0462017-12-10 18:09:40 +010089 [] pt.receive(tr_CtrlMsgTrap(variable, val)) -> value rx {
90 }
Harald Welte03c0e562017-12-09 02:55:12 +010091 [] T.timeout {
92 setverdict(fail, "Timeout waiting for TRAP ", variable);
Daniel Willmanne4ff5372018-07-05 17:35:03 +020093 mtc.stop;
Harald Welte03c0e562017-12-09 02:55:12 +010094 }
95 }
96 return rx.trap.val;
97 }
Harald Welte852a3842017-12-09 14:19:36 +010098
Pau Espin Pedrol579cd7a2019-06-10 21:01:30 +020099 /* Expect a matching SET, optionally answer */
100 function f_ctrl_exp_set(IPA_CTRL_PT pt, template CtrlVariable variable,
101 template CtrlValue val := ?,
102 template (omit) CtrlValue rsp := omit,
103 float timeout_val := 2.0)
104 return CtrlValue {
105 timer T := timeout_val;
106 var CtrlMessage rx;
107 T.start;
108 alt {
109 [] pt.receive(tr_CtrlMsgSet(?, variable, val)) -> value rx {
110 if (ispresent(rsp)) {
111 pt.send(ts_CtrlMsgSetRepl(rx.cmd.id, valueof(variable), valueof(rsp)));
112 }
113 }
114 [] T.timeout {
115 setverdict(fail, "Timeout waiting for SET ", variable);
116 mtc.stop;
117 }
118 }
119 return rx.cmd.val;
120 }
121
Pau Espin Pedrol9a5b8ff2021-01-04 19:01:31 +0100122 /* Expect a matching SET, optionally answer */
123 function f_ctrl_exp_get(IPA_CTRL_PT pt, template CtrlVariable variable,
124 template (omit) CtrlValue rsp := omit,
125 float timeout_val := 2.0) {
126 timer T := timeout_val;
127 var CtrlMessage rx;
128 T.start;
129 alt {
130 [] pt.receive(tr_CtrlMsgGet(?, variable)) -> value rx {
131 if (ispresent(rsp)) {
132 pt.send(ts_CtrlMsgGetRepl(rx.cmd.id, valueof(variable), valueof(rsp)));
133 }
134 }
135 [] T.timeout {
136 setverdict(fail, "Timeout waiting for GET ", variable);
137 mtc.stop;
138 }
139 }
140 }
141
Harald Welte852a3842017-12-09 14:19:36 +0100142 /* Expect a matching GET result */
143 function f_ctrl_get_exp(IPA_CTRL_PT pt, CtrlVariable variable, template CtrlValue exp) {
144 var charstring ctrl_resp;
145 ctrl_resp := f_ctrl_get(pt, variable);
146 if (not match(ctrl_resp, exp)) {
147 setverdict(fail, "Unexpected " & variable & ":" & ctrl_resp);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200148 mtc.stop;
Harald Welte852a3842017-12-09 14:19:36 +0100149 }
150 }
151
Harald Welte3ec493f2017-12-09 16:25:29 +0100152 template charstring ts_ctrl_ratectr(CtrlVariable grp, integer instance, CtrlVariable name,
153 CtrlVariable kind := "abs") :=
154 "rate_ctr." & kind & "." & grp & "." & int2str(instance) & "." & name;
155
156 function f_ctrl_get_ratectr_abs(IPA_CTRL_PT pt, CtrlVariable grp, integer instance,
157 CtrlVariable name) return integer {
158 return str2int(f_ctrl_get(pt, valueof(ts_ctrl_ratectr(grp, instance, name))));
159 }
160
161 function f_ctrl_get_exp_ratectr_abs(IPA_CTRL_PT pt, CtrlVariable grp, integer instance,
162 CtrlVariable name, template integer exp) {
163 var charstring ctrl_resp;
164 var CtrlVariable variable := valueof(ts_ctrl_ratectr(grp, instance, name));
165 ctrl_resp := f_ctrl_get(pt, variable);
166 if (not match(str2int(ctrl_resp), exp)) {
167 setverdict(fail, variable & " value " & ctrl_resp & " didn't match ", exp);
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200168 mtc.stop;
Harald Welte3ec493f2017-12-09 16:25:29 +0100169 }
170 }
171
Harald Welte852a3842017-12-09 14:19:36 +0100172
Neels Hofmeyr74180592020-06-20 21:40:52 +0200173 /* --- Retrieve and verify rate counter values in bulk ---
174 *
175 * BSC_Tests.ttcn shows a nice way to conveniently shorten the code needed to use these functions, see
176 * f_ctrs_msc_init() and f_ctrs_msc_expect().
177 *
178 * Here also a full usage example:
179 *
180 * const CounterNameVals my_counternames := {
181 * { "mscpool:subscr:new", 0 },
182 * { "mscpool:subscr:known", 0 },
183 * { "mscpool:subscr:attach_lost", 0 },
184 * };
185 *
186 * var CounterNameValsList my_counters := f_counter_name_vals_get_n(instance_name := "msc", instance_count := 3,
187 * counternames := my_counternames);
188 *
189 * // run some tests that increment rate counters in the program,
190 * // and increment expected counters accordingly:
Neels Hofmeyr1393d022020-08-29 01:17:33 +0000191 * f_counter_name_vals_list_add(my_counters, instance_nr := 1, "mscpool:subscr:new", 7);
192 * f_counter_name_vals_list_add(my_counters, instance_nr := 2, "mscpool:subscr:attach_lost", 3);
Neels Hofmeyr74180592020-06-20 21:40:52 +0200193 *
194 * // verify that the program reflects the expected counters:
195 * f_counter_name_vals_expect_n(instance_name := "msc", my_counters);
196 *
197 * // run some more tests...
Neels Hofmeyr1393d022020-08-29 01:17:33 +0000198 * f_counter_name_vals_list_add(my_counters, instance_nr := 0, "mscpool:subscr:known");
Neels Hofmeyr74180592020-06-20 21:40:52 +0200199 * // and verify again
200 * f_counter_name_vals_expect_n(instance_name := "msc", my_counters);
201 */
202
203 /* One counter value, e.g. { "name", 23 } */
204 type record CounterNameVal {
205 charstring name,
206 integer val
207 }
208
209 /* List of one instance's counters,
210 * e.g. { {"foo",23}, {"bar",42} }
211 */
212 type record of CounterNameVal CounterNameVals;
213
214 /* List of numerous instances' counters,
215 * e.g. { { {"foo",23}, {"bar",42} },
216 * { {"foo",23}, {"bar",42} } }
217 */
218 type record of CounterNameVals CounterNameValsList;
219
220 /* Retrieve one instance's rate counter values of the given names. */
221 function f_counter_name_vals_get(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr,
222 CounterNameVals counternames)
223 return CounterNameVals {
224 var CounterNameVals vals;
225 for (var integer i := 0; i < lengthof(counternames); i := i + 1) {
226 vals[i] := {
227 name := counternames[i].name,
228 val := f_ctrl_get_ratectr_abs(pt, instance_name, instance_nr, counternames[i].name)
229 };
230 }
231 return vals;
232 }
233
234 /* Retrieve the first N instances' rate counter values of the given names */
235 function f_counter_name_vals_get_n(IPA_CTRL_PT pt, charstring instance_name := "msc",
Neels Hofmeyr7afb13f2023-06-27 01:26:31 +0200236 integer instance_count, CounterNameVals counternames,
237 integer start_idx := 0)
Neels Hofmeyr74180592020-06-20 21:40:52 +0200238 return CounterNameValsList {
239 var CounterNameValsList valslist;
Neels Hofmeyr7afb13f2023-06-27 01:26:31 +0200240 for (var integer instance_nr := start_idx; instance_nr < start_idx + instance_count; instance_nr := instance_nr + 1) {
Neels Hofmeyr74180592020-06-20 21:40:52 +0200241 valslist[instance_nr] := f_counter_name_vals_get(pt, instance_name, instance_nr, counternames);
242 }
243 log("retrieved rate counters: ", instance_name, ": ", valslist);
244 return valslist;
245 }
246
247 /* In a list of one instance's counters, increment a specifically named counter. */
Neels Hofmeyr9656e922020-06-30 01:27:01 +0200248 function f_counter_name_vals_add(inout CounterNameVals vals, charstring countername, integer val := 1)
249 {
Neels Hofmeyr74180592020-06-20 21:40:52 +0200250 for (var integer i := 0; i < lengthof(vals); i := i + 1) {
251 if (vals[i].name == countername) {
252 vals[i].val := vals[i].val + val;
Neels Hofmeyr9656e922020-06-30 01:27:01 +0200253 return;
Neels Hofmeyr74180592020-06-20 21:40:52 +0200254 }
255 }
256 /* name not found, append */
257 vals[lengthof(vals)] := {
258 name := countername,
259 val := val
260 }
Neels Hofmeyr74180592020-06-20 21:40:52 +0200261 }
262
263 /* In a list of several instances' counters, increment a specific instance's specifically named counter. */
Neels Hofmeyr9656e922020-06-30 01:27:01 +0200264 function f_counter_name_vals_list_add(inout CounterNameValsList vals, integer instance_nr,
Neels Hofmeyr74180592020-06-20 21:40:52 +0200265 charstring countername, integer val := 1)
Neels Hofmeyr9656e922020-06-30 01:27:01 +0200266 {
267 f_counter_name_vals_add(vals[instance_nr], countername, val);
Neels Hofmeyr74180592020-06-20 21:40:52 +0200268 }
269
270 /* For a specific instance, call f_counter_name_vals_get() and compare with expected counter values.
271 * Set the test verdict accordingly. */
272 function f_counter_name_vals_expect(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr,
273 CounterNameVals vals) {
Pau Espin Pedrol40a02952021-02-05 12:18:06 +0100274 var CounterNameVals last := f_counter_name_vals_get(pt, instance_name, instance_nr, vals);
Neels Hofmeyr74180592020-06-20 21:40:52 +0200275 for (var integer i := 0; i < lengthof(vals); i := i + 1) {
Pau Espin Pedrol40a02952021-02-05 12:18:06 +0100276 if (last[i].name != vals[i].name) {
Neels Hofmeyr74180592020-06-20 21:40:52 +0200277 setverdict(fail, "Internal error");
278 }
Pau Espin Pedrol40a02952021-02-05 12:18:06 +0100279 if (last[i].val != vals[i].val) {
Neels Hofmeyr74180592020-06-20 21:40:52 +0200280 setverdict(fail, "Rate counter mismatch: ", instance_name, " ", instance_nr,
Pau Espin Pedrol40a02952021-02-05 12:18:06 +0100281 " ", vals[i].name, " is at ", last[i].val, " but expected ", vals[i].val);
Neels Hofmeyr74180592020-06-20 21:40:52 +0200282 }
283 }
284 setverdict(pass);
285 }
286
287 /* For N instances, call f_counter_name_vals_get() and compare with expected counter values.
288 * Set the test verdict accordingly. The number of instances is given by lengthof(valslist). */
289 function f_counter_name_vals_expect_n(IPA_CTRL_PT pt, charstring instance_name, CounterNameValsList valslist) {
290 for (var integer instance_nr := 0; instance_nr < lengthof(valslist); instance_nr := instance_nr + 1) {
291 f_counter_name_vals_expect(pt, instance_name, instance_nr, valslist[instance_nr]);
292 }
293 }
294
Neels Hofmeyrbd94fe72021-10-26 22:04:53 +0200295 /* For a specific instance, call f_counter_name_vals_get() and indentify counters that have changed with respect
296 * to 'vals'. Return list of the changed counter names in the order they appear in 'vals'. */
297 function f_counter_name_vals_get_changed(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr,
298 CounterNameVals vals)
299 return charstring_list {
300 var charstring_list changed := {};
301 var CounterNameVals last := f_counter_name_vals_get(pt, instance_name, instance_nr, vals);
302 for (var integer i := 0; i < lengthof(vals); i := i + 1) {
303 if (last[i].name != vals[i].name) {
304 setverdict(fail, "Internal error");
305 }
306 if (last[i].val != vals[i].val) {
307 changed := changed & { instance_name & "." & int2str(instance_nr) & "." & vals[i].name };
308 }
309 }
310 return changed;
311 }
312
313 /* For N instances, call f_counter_name_vals_get() and indentify counters that have changed with respect
314 * to 'vals'. Return list of the changed counter names in the order they appear in 'vals'. */
315 function f_counter_name_vals_get_changed_n(IPA_CTRL_PT pt, charstring instance_name, CounterNameValsList valslist)
316 return charstring_list {
317 var charstring_list changed := {};
318 for (var integer instance_nr := 0; instance_nr < lengthof(valslist); instance_nr := instance_nr + 1) {
319 changed := changed & f_counter_name_vals_get_changed(pt, instance_name, instance_nr, valslist[instance_nr]);
320 }
321 return changed;
322 }
323
324 function f_counter_name_vals_expect_changed(IPA_CTRL_PT pt, charstring instance_name, CounterNameValsList valslist,
325 charstring_list expect_changed) {
326 var charstring_list changed := f_counter_name_vals_get_changed_n(pt, instance_name, valslist);
327 f_counter_name_vals_expect_changed_list(changed, expect_changed);
328 }
329
330 function f_counter_name_vals_expect_changed_list(charstring_list got_list, charstring_list expect_list) {
331 var charstring unexpected_change := "";
332 for (var integer i := 0; i < lengthof(got_list); i := i + 1) {
333 var boolean found := false;
334 for (var integer j := 0; j < lengthof(expect_list); j := j + 1) {
335 if (got_list[i] == expect_list[j]) {
336 found := true;
337 break;
338 }
339 }
340 if (not found) {
341 unexpected_change := unexpected_change & " " & got_list[i];
342 }
343 }
344 var charstring missing_change := "";
345 for (var integer i := 0; i < lengthof(expect_list); i := i + 1) {
346 var boolean found := false;
347 for (var integer j := 0; j < lengthof(got_list); j := j + 1) {
348 if (expect_list[i] == got_list[j]) {
349 found := true;
350 break;
351 }
352 }
353 if (not found) {
354 missing_change := missing_change & " " & expect_list[i];
355 }
356 }
357 var charstring diff := "";
358 if (lengthof(unexpected_change) > 0) {
359 diff := diff & " Unexpected changes in" & unexpected_change & ";";
360 }
361 if (lengthof(missing_change) > 0) {
362 diff := diff & " Missing changes in" & missing_change & ";";
363 }
364 if (lengthof(diff) > 0) {
365 log("ERROR\nExpected: ", expect_list, "\nGot: ", got_list);
366 setverdict(fail, "Rate counters did not change as expected:" & diff);
367 } else {
368 setverdict(pass);
369 }
370 }
371
Harald Welte03c0e562017-12-09 02:55:12 +0100372}