Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 1 | module Osmocom_CTRL_Functions { |
Harald Welte | 35bb716 | 2018-01-03 21:07:52 +0100 | [diff] [blame] | 2 | |
| 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 Pedrol | 76ba541 | 2019-06-10 11:00:33 +0200 | [diff] [blame] | 9 | * |
Harald Welte | 35bb716 | 2018-01-03 21:07:52 +0100 | [diff] [blame] | 10 | * 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 Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 23 | 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 Welte | fdfa046 | 2017-12-10 18:09:40 +0100 | [diff] [blame] | 38 | [] pt.receive(tr_CtrlMsgGetRepl(id, variable)) -> value rx { |
| 39 | } |
Harald Welte | 95a4781 | 2017-12-09 14:19:03 +0100 | [diff] [blame] | 40 | [] pt.receive(tr_CtrlMsgTrap) { repeat; } |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 41 | [] pt.receive(tr_CtrlMsgError) -> value rx { |
| 42 | setverdict(fail, "Error in CTRL GET ", variable, ": ", rx.err.reason); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 43 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 44 | } |
| 45 | [] T.timeout { |
| 46 | setverdict(fail, "Timeout waiting for CTRL GET REPLY ", variable); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 47 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 48 | } |
| 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 Welte | 95a4781 | 2017-12-09 14:19:03 +0100 | [diff] [blame] | 62 | [] pt.receive(tr_CtrlMsgTrap) { repeat; } |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 63 | [] pt.receive(tr_CtrlMsgError) -> value rx { |
| 64 | setverdict(fail, "Error in CTRL GET ", variable, ": ", rx.err.reason); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 65 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 66 | } |
| 67 | [] T.timeout { |
| 68 | setverdict(fail, "Timeout waiting for CTRL SET REPLY ", variable); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 69 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 70 | } |
| 71 | } |
| 72 | } |
| 73 | |
Pau Espin Pedrol | 579cd7a | 2019-06-10 21:01:30 +0200 | [diff] [blame] | 74 | /* 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 Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 79 | /* Expect a matching TRAP */ |
| 80 | function f_ctrl_exp_trap(IPA_CTRL_PT pt, template CtrlVariable variable, |
Pau Espin Pedrol | 579cd7a | 2019-06-10 21:01:30 +0200 | [diff] [blame] | 81 | template CtrlValue val := ?, float timeout_val := 2.0) |
| 82 | return CtrlValue { |
| 83 | timer T := timeout_val; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 84 | var CtrlMessage rx; |
| 85 | T.start; |
| 86 | alt { |
Harald Welte | fdfa046 | 2017-12-10 18:09:40 +0100 | [diff] [blame] | 87 | [] pt.receive(tr_CtrlMsgTrap(variable, val)) -> value rx { |
| 88 | } |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 89 | [] T.timeout { |
| 90 | setverdict(fail, "Timeout waiting for TRAP ", variable); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 91 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 92 | } |
| 93 | } |
| 94 | return rx.trap.val; |
| 95 | } |
Harald Welte | 852a384 | 2017-12-09 14:19:36 +0100 | [diff] [blame] | 96 | |
Pau Espin Pedrol | 579cd7a | 2019-06-10 21:01:30 +0200 | [diff] [blame] | 97 | /* 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 Pedrol | 9a5b8ff | 2021-01-04 19:01:31 +0100 | [diff] [blame] | 120 | /* 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 Welte | 852a384 | 2017-12-09 14:19:36 +0100 | [diff] [blame] | 140 | /* 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 Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 146 | mtc.stop; |
Harald Welte | 852a384 | 2017-12-09 14:19:36 +0100 | [diff] [blame] | 147 | } |
| 148 | } |
| 149 | |
Harald Welte | 3ec493f | 2017-12-09 16:25:29 +0100 | [diff] [blame] | 150 | 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 Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 166 | mtc.stop; |
Harald Welte | 3ec493f | 2017-12-09 16:25:29 +0100 | [diff] [blame] | 167 | } |
| 168 | } |
| 169 | |
Harald Welte | 852a384 | 2017-12-09 14:19:36 +0100 | [diff] [blame] | 170 | |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 171 | /* --- 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 Hofmeyr | 1393d02 | 2020-08-29 01:17:33 +0000 | [diff] [blame] | 189 | * 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 Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 191 | * |
| 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 Hofmeyr | 1393d02 | 2020-08-29 01:17:33 +0000 | [diff] [blame] | 196 | * f_counter_name_vals_list_add(my_counters, instance_nr := 0, "mscpool:subscr:known"); |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 197 | * // 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 Hofmeyr | 9656e92 | 2020-06-30 01:27:01 +0200 | [diff] [blame] | 245 | function f_counter_name_vals_add(inout CounterNameVals vals, charstring countername, integer val := 1) |
| 246 | { |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 247 | 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 Hofmeyr | 9656e92 | 2020-06-30 01:27:01 +0200 | [diff] [blame] | 250 | return; |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 251 | } |
| 252 | } |
| 253 | /* name not found, append */ |
| 254 | vals[lengthof(vals)] := { |
| 255 | name := countername, |
| 256 | val := val |
| 257 | } |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 258 | } |
| 259 | |
| 260 | /* In a list of several instances' counters, increment a specific instance's specifically named counter. */ |
Neels Hofmeyr | 9656e92 | 2020-06-30 01:27:01 +0200 | [diff] [blame] | 261 | function f_counter_name_vals_list_add(inout CounterNameValsList vals, integer instance_nr, |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 262 | charstring countername, integer val := 1) |
Neels Hofmeyr | 9656e92 | 2020-06-30 01:27:01 +0200 | [diff] [blame] | 263 | { |
| 264 | f_counter_name_vals_add(vals[instance_nr], countername, val); |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 265 | } |
| 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) { |
| 271 | var CounterNameVals now := f_counter_name_vals_get(pt, instance_name, instance_nr, vals); |
| 272 | for (var integer i := 0; i < lengthof(vals); i := i + 1) { |
| 273 | if (now[i].name != vals[i].name) { |
| 274 | setverdict(fail, "Internal error"); |
| 275 | } |
| 276 | if (now[i].val != vals[i].val) { |
| 277 | setverdict(fail, "Rate counter mismatch: ", instance_name, " ", instance_nr, |
| 278 | " ", vals[i].name, " is at ", now[i].val, " but expected ", vals[i].val); |
| 279 | } |
| 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 Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 292 | } |