pespin has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/37956?usp=email )


Change subject: StatsD_Checker: Allow building and running without VTY support
......................................................................

StatsD_Checker: Allow building and running without VTY support

Some programs may support exporting to statsd, but may not support the
Osmocom VTY set of commands to send reports, or not have a VTY at all.

New features are added to the public API of StatsD_Checker which make it
possible to use it...

* without "stats reset":
Feature to take snapshots (f_statsd_snapshot()) which can later be
used to validate expectancies with values relative to the snapshot,
using API f_statsd_expect_from_snapshot().
This way, one can do:
"""
var StatsDExpects statsd_exp := { /* relative expectancies here... */ };
var StatsDMetrics statsd_snapshot := 
f_statsd_snapshot(f_statsd_keys_from_expect(statsd_exp));
/* do some test stuff here changing the state of the IUT... */
f_statsd_expect_from_snapshot(statsd_exp, snapshot := statsd_snapshot);
"""

* without polling ("stats report"), aka with periodict reporting:
New parameter wait_converge in f_statsd_expect(), which allows
overcoming race conditions with StatsD server processing older incoming
metrics due to periodic reporting.
This feature also allows a test to wait until a state changes in the
IUT.

Change-Id: I5421c76e4f303fd16d4db945a1c69910e40ac820
---
M bsc/gen_links.sh
M bsc/regen_makefile.sh
M hnbgw/gen_links.sh
M hnbgw/regen_makefile.sh
M hnodeb/gen_links.sh
M hnodeb/regen_makefile.sh
D library/StatsD_Checker.ttcn
A library/StatsD_Checker.ttcnpp
M library/StatsD_Types.ttcn
M mgw/gen_links.sh
M mgw/regen_makefile.sh
M ns/gen_links.sh
M ns/regen_makefile.sh
M pcu/gen_links.sh
M pcu/regen_makefile.sh
M upf/gen_links.sh
M upf/regen_makefile.sh
17 files changed, 483 insertions(+), 290 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks 
refs/changes/56/37956/1

diff --git a/bsc/gen_links.sh b/bsc/gen_links.sh
index cb9f12f..05c1e51 100755
--- a/bsc/gen_links.sh
+++ b/bsc/gen_links.sh
@@ -70,7 +70,7 @@
 FILES="Misc_Helpers.ttcn General_Types.ttcn Osmocom_Types.ttcn GSM_Types.ttcn 
Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc 
IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn 
IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp L3_Templates.ttcn 
BSSMAP_Templates.ttcn RAN_Emulation.ttcnpp RLCMAC_CSN1_Templates.ttcn 
RLCMAC_CSN1_Types.ttcn GSM_RR_Types.ttcn RSL_Types.ttcn RSL_Emulation.ttcn 
MGCP_Emulation.ttcn SDP_Templates.ttcn MGCP_Types.ttcn MGCP_Templates.ttcn 
MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunct.ttcn 
MGCP_CodecPort_CtrlFunctDef.cc BSSAP_CodecPort.ttcn RAN_Adapter.ttcnpp 
Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn 
RTP_CodecPort.ttcn RTP_CodecPort_CtrlFunct.ttcn RTP_CodecPort_CtrlFunctDef.cc 
RTP_Emulation.ttcn IuUP_Types.ttcn IuUP_EncDec.cc IuUP_Emulation.ttcn 
SCCP_Templates.ttcn IPA_Testing.ttcn GSM_SystemInformation.ttcn 
GSM_RestOctets.ttcn "
 FILES+="CBSP_Types.ttcn CBSP_Templates.ttcn "
 FILES+="CBSP_CodecPort.ttcn CBSP_CodecPort_CtrlFunct.ttcn 
CBSP_CodecPort_CtrlFunctdef.cc CBSP_Adapter.ttcn "
-FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcn "
+FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcnpp "
 FILES+="BSSAP_LE_CodecPort.ttcn BSSAP_LE_Emulation.ttcn BSSAP_LE_Types.ttcn 
BSSAP_LE_Adapter.ttcn BSSLAP_Types.ttcn BSSMAP_LE_Templates.ttcn "
 FILES+="AbisOML_Types.ttcn"

diff --git a/bsc/regen_makefile.sh b/bsc/regen_makefile.sh
index fc53e30..6683f29 100755
--- a/bsc/regen_makefile.sh
+++ b/bsc/regen_makefile.sh
@@ -32,6 +32,7 @@
        -DRAN_EMULATION_BSSAP
        -DRAN_EMULATION_CTRL
        -DRAN_EMULATION_MGCP
+       -DSTATSD_HAVE_VTY
        -DUSE_MTP3_DISTRIBUTOR
 "

diff --git a/hnbgw/gen_links.sh b/hnbgw/gen_links.sh
index e11b915..51ce602 100755
--- a/hnbgw/gen_links.sh
+++ b/hnbgw/gen_links.sh
@@ -102,7 +102,7 @@
 FILES+="RAN_Adapter.ttcnpp RAN_Emulation.ttcnpp BSSAP_CodecPort.ttcn 
SCCP_Templates.ttcn "
 FILES+="PFCP_CodecPort.ttcn PFCP_CodecPort_CtrlFunct.ttcn 
PFCP_CodecPort_CtrlFunctDef.cc PFCP_Emulation.ttcn PFCP_Templates.ttcn "
 FILES+="Misc_Helpers.ttcn General_Types.ttcn Osmocom_Types.ttcn GSM_Types.ttcn 
Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc 
IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn 
IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp Osmocom_CTRL_Types.ttcn 
Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn RTP_CodecPort.ttcn 
RTP_CodecPort_CtrlFunct.ttcn RTP_CodecPort_CtrlFunctDef.cc RTP_Emulation.ttcn 
IuUP_Types.ttcn IuUP_EncDec.cc IuUP_Emulation.ttcn "
-FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcn "
+FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcnpp "
 FILES+="L3_Templates.ttcn L3_Common.ttcn "
 FILES+="SCTP_Templates.ttcn "
 gen_links $DIR $FILES
diff --git a/hnbgw/regen_makefile.sh b/hnbgw/regen_makefile.sh
index 13db985..c67323e 100755
--- a/hnbgw/regen_makefile.sh
+++ b/hnbgw/regen_makefile.sh
@@ -33,6 +33,7 @@
 export CPPFLAGS_TTCN3="
        -DIPA_EMULATION_CTRL
        -DRAN_EMULATION_RANAP
+       -DSTATSD_HAVE_VTY
        -DUSE_MTP3_DISTRIBUTOR
 "

diff --git a/hnodeb/gen_links.sh b/hnodeb/gen_links.sh
index 35aa039..71265b1 100755
--- a/hnodeb/gen_links.sh
+++ b/hnodeb/gen_links.sh
@@ -62,7 +62,7 @@
 FILES="HNBLLIF_Types.ttcn HNBLLIF_Templates.ttcn HNBLLIF_CodecPort.ttcn "
 FILES+="Iuh_Types.ttcn Iuh_CodecPort.ttcn Iuh_CodecPort_CtrlFunctDef.cc 
Iuh_CodecPort_CtrlFunct.ttcn Iuh_Emulation.ttcn DNS_Helpers.ttcn "
 FILES+="Misc_Helpers.ttcn General_Types.ttcn Osmocom_Types.ttcn 
Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc 
IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn 
IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp Osmocom_CTRL_Types.ttcn 
Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn RTP_CodecPort.ttcn 
RTP_CodecPort_CtrlFunct.ttcn RTP_CodecPort_CtrlFunctDef.cc RTP_Emulation.ttcn 
IuUP_Types.ttcn IuUP_EncDec.cc IuUP_Emulation.ttcn "
-FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcn "
+FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcnpp "
 FILES+="GTPv1C_CodecPort.ttcn GTPv1C_CodecPort_CtrlFunct.ttcn 
GTPv1C_CodecPort_CtrlFunctDef.cc GTPv1C_Templates.ttcn "
 FILES+="GTPv1U_CodecPort.ttcn GTPv1U_CodecPort_CtrlFunct.ttcn 
GTPv1U_CodecPort_CtrlFunctDef.cc GTPv1U_Templates.ttcn "
 FILES+="GTP_Emulation.ttcn IPCP_Types.ttcn GSM_Types.ttcn "
diff --git a/hnodeb/regen_makefile.sh b/hnodeb/regen_makefile.sh
index cf5fed3..be7dec5 100755
--- a/hnodeb/regen_makefile.sh
+++ b/hnodeb/regen_makefile.sh
@@ -33,6 +33,7 @@

 export CPPFLAGS_TTCN3="
        -DIPA_EMULATION_CTRL
+       -DSTATSD_HAVE_VTY
 "

 ../regen-makefile.sh -e $NAME $FILES
diff --git a/library/StatsD_Checker.ttcn b/library/StatsD_Checker.ttcn
deleted file mode 100644
index 2ad4c9f..0000000
--- a/library/StatsD_Checker.ttcn
+++ /dev/null
@@ -1,280 +0,0 @@
-module StatsD_Checker {
-
-/* Verifies that  StatsD metrics in a test match the expected values
- * Uses StatsD_CodecPort to receive the statsd messages from the DUT
- * and a separate VTY connection to reset and trigger the stats.
- *
- * When using this you should configure your stats reporter to disable
- * interval-based reports and always send all metrics:
- * > stats interval 0
- * > stats reporter statsd
- * >  remote-ip a.b.c.d
- * >  remote-port 8125
- * >  level subscriber
- * >  flush-period 1
- * >  mtu 1024
- * >  enable
- *
- * (C) 2020 by sysmocom s.f.m.c. GmbH <[email protected]>
- * All rights reserved.
- *
- * Author: Daniel Willmann <[email protected]>
- *
- * Released under the terms of GNU General Public License, Version 2 or
- * (at your option) any later version.
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-import from Misc_Helpers all;
-import from Socket_API_Definitions all;
-
-import from StatsD_Types all;
-import from StatsD_CodecPort all;
-import from StatsD_CodecPort_CtrlFunct all;
-
-import from Osmocom_Types all;
-import from Osmocom_VTY_Functions all;
-import from TELNETasp_PortType all;
-
-modulepar {
-       /* Whether to test stats values */
-       boolean mp_enable_stats := true;
-}
-
-type record StatsDExpect {
-       MetricName      name,
-       MetricType      mtype,
-       MetricValue     min,
-       MetricValue     max
-};
-
-type set of StatsDExpect StatsDExpects;
-
-type record StatsDExpectPriv {
-       StatsDExpect expect,
-       integer seen
-}
-
-type set of StatsDExpectPriv StatsDExpectPrivs;
-
-type enumerated StatsDResultType {
-       e_Matched,
-       e_Mismatched,
-       e_NotFound
-}
-
-type record StatsDExpectResult {
-       StatsDResultType kind,
-       integer idx
-}
-
-type component StatsD_Checker_CT {
-       port TELNETasp_PT STATSVTY;
-       port STATSD_PROC_PT STATSD_PROC;
-       port STATSD_CODEC_PT STATS;
-       timer T_statsd := 5.0;
-}
-
-type component StatsD_ConnHdlr {
-       port STATSD_PROC_PT STATSD_PROC;
-}
-
-signature STATSD_reset();
-signature STATSD_expect(in StatsDExpects expects) return boolean;
-
-type port STATSD_PROC_PT procedure {
-       inout STATSD_reset, STATSD_expect;
-} with {extension "internal"};
-
-/* Expect templates and functions */
-
-
-/* StatsD checker component */
-function main(charstring statsd_host, integer statsd_port) runs on 
StatsD_Checker_CT {
-       var StatsD_ConnHdlr vc_conn;
-       var StatsDExpects expects;
-       var Result res;
-
-       while (not mp_enable_stats) {
-               log("StatsD checker disabled by modulepar");
-               f_sleep(3600.0);
-       }
-
-       map(self:STATS, system:STATS);
-       res := StatsD_CodecPort_CtrlFunct.f_IPL4_listen(STATS, statsd_host, 
statsd_port, { udp := {} }, {});
-       if (not ispresent(res.connId)) {
-               Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
-                                       "Could not bind StatsD socket, check 
your configuration");
-       }
-
-       /* Connect to VTY and reset stats */
-       map(self:STATSVTY, system:STATSVTY);
-       f_vty_set_prompts(STATSVTY);
-       f_vty_transceive(STATSVTY, "enable");
-
-       /* Reset the stats system at start */
-       f_vty_transceive(STATSVTY, "stats reset");
-
-       while (true) {
-               alt {
-               [] STATSD_PROC.getcall(STATSD_reset:{}) -> sender vc_conn {
-                       f_vty_transceive(STATSVTY, "stats reset");
-                       STATSD_PROC.reply(STATSD_reset:{}) to vc_conn;
-                       }
-               [] STATSD_PROC.getcall(STATSD_expect:{?}) -> param(expects) 
sender vc_conn {
-                       var boolean success := f_statsd_checker_expect(expects);
-                       STATSD_PROC.reply(STATSD_expect:{expects} value 
success) to vc_conn;
-                       }
-               }
-       }
-}
-
-
-/* Return false if the expectation doesn't match the metric, otherwise return 
true */
-private function f_compare_expect(StatsDMetric metric, StatsDExpect expect) 
return boolean {
-       if ((metric.name == expect.name) and (metric.mtype == expect.mtype)
-               and (metric.val >= expect.min) and (metric.val <= expect.max)) {
-               return true;
-       } else {
-               return false;
-       }
-}
-
-private function f_statsd_checker_metric_expects(StatsDExpectPrivs exp_seen, 
StatsDMetric metric)
-return StatsDExpectResult {
-       var StatsDExpectResult result := {
-               kind := e_NotFound,
-               idx := -1
-       };
-
-       for (var integer i := 0; i < lengthof(exp_seen); i := i + 1) {
-               var StatsDExpectPriv exp := exp_seen[i];
-               if (exp.expect.name != metric.name) {
-                       continue;
-               }
-               if (not f_compare_expect(metric, exp.expect)) {
-                       log("EXP mismatch: ", metric, " vs ", exp.expect);
-                       result := {
-                               kind := e_Mismatched,
-                               idx := i
-                       };
-                       break;
-               } else {
-                       log("EXP match: ", metric, " vs ", exp.expect);
-                       result := {
-                               kind := e_Matched,
-                               idx := i
-                       };
-                       break;
-               }
-       }
-       return result;
-}
-
-template StatsDExpectPriv t_statsd_expect_priv(template StatsDExpect expect) 
:= {
-       expect := expect,
-       seen := 0
-}
-
-private function f_statsd_checker_expect(StatsDExpects expects) runs on 
StatsD_Checker_CT return boolean {
-       var default t;
-       var StatsDMessage msg;
-       var StatsDExpectResult res;
-       var StatsDExpectPrivs exp_seen := {};
-
-       for (var integer i := 0; i < lengthof(expects); i := i + 1) {
-               exp_seen := exp_seen & 
{valueof(t_statsd_expect_priv(expects[i]))};
-       }
-
-       /* Dismiss any messages we might have skipped from the last report */
-       STATS.clear;
-
-       f_vty_transceive(STATSVTY, "stats report");
-
-       var boolean seen_all := false;
-       T_statsd.start;
-       while (not seen_all) {
-               var StatsD_RecvFrom rf;
-               alt {
-               [] STATS.receive(tr_StatsD_RecvFrom(?, ?)) -> value rf {
-                       msg := rf.msg;
-                       }
-               [] T_statsd.timeout {
-                       for (var integer i := 0; i < lengthof(exp_seen); i := i 
+ 1) {
-                               /* We're still missing some expects, keep 
looking */
-                               if (exp_seen[i].seen == 0) {
-                                       log("Timeout waiting for ", 
exp_seen[i].expect.name, " (min: ", exp_seen[i].expect.min,
-                                               ", max: ", 
exp_seen[i].expect.max, ")");
-                               }
-                       }
-                       setverdict(fail, "Timeout waiting for metrics");
-                       return false;
-               }
-               }
-
-               for (var integer i := 0; i < lengthof(msg); i := i + 1) {
-                       var StatsDMetric metric := msg[i];
-
-                       res := f_statsd_checker_metric_expects(exp_seen, 
metric);
-                       if (res.kind == e_NotFound) {
-                               continue;
-                       }
-
-                       if (res.kind == e_Mismatched) {
-                               log("Metric: ", metric);
-                               log("Expect: ", exp_seen[res.idx].expect);
-                               setverdict(fail, "Metric failed expectation ", 
metric, " vs ", exp_seen[res.idx].expect);
-                               return false;
-                       } else if (res.kind == e_Matched) {
-                               exp_seen[res.idx].seen := 
exp_seen[res.idx].seen + 1;
-                       }
-               }
-
-               /* Check if all expected metrics were received */
-               seen_all := true;
-               for (var integer i := 0; i < lengthof(exp_seen); i := i + 1) {
-                       /* We're still missing some expects, keep looking */
-                       if (exp_seen[i].seen == 0) {
-                               seen_all := false;
-                               break;
-                       }
-               }
-       }
-
-       T_statsd.stop;
-       return seen_all;
-}
-
-function f_init_statsd(charstring id, inout StatsD_Checker_CT vc_STATSD, 
charstring local_addr, integer local_port) {
-       id := id & "-STATS";
-
-       vc_STATSD := StatsD_Checker_CT.create(id);
-       vc_STATSD.start(StatsD_Checker.main(local_addr, local_port));
-}
-
-
-/* StatsD connhdlr */
-function f_statsd_reset() runs on StatsD_ConnHdlr {
-       if (not mp_enable_stats) {
-               return;
-       }
-
-       STATSD_PROC.call(STATSD_reset:{}) {
-               [] STATSD_PROC.getreply(STATSD_reset:{}) {}
-       }
-}
-
-function f_statsd_expect(StatsDExpects expects) runs on StatsD_ConnHdlr return 
boolean {
-       var boolean res;
-
-       if (not mp_enable_stats) {
-               return true;
-       }
-
-       STATSD_PROC.call(STATSD_expect:{expects}) {
-               [] STATSD_PROC.getreply(STATSD_expect:{expects}) -> value res;
-       }
-       return res;
-}
-
-}
diff --git a/library/StatsD_Checker.ttcnpp b/library/StatsD_Checker.ttcnpp
new file mode 100644
index 0000000..ecadee3
--- /dev/null
+++ b/library/StatsD_Checker.ttcnpp
@@ -0,0 +1,444 @@
+module StatsD_Checker {
+
+/* Verifies that  StatsD metrics in a test match the expected values
+ * Uses StatsD_CodecPort to receive the statsd messages from the DUT
+ * and a separate VTY connection to reset and trigger the stats.
+ *
+ * When using this you should configure your stats reporter to disable
+ * interval-based reports and always send all metrics:
+ * > stats interval 0
+ * > stats reporter statsd
+ * >  remote-ip a.b.c.d
+ * >  remote-port 8125
+ * >  level subscriber
+ * >  flush-period 1
+ * >  mtu 1024
+ * >  enable
+ *
+ * (C) 2020 by sysmocom s.f.m.c. GmbH <[email protected]>
+ * All rights reserved.
+ *
+ * Author: Daniel Willmann <[email protected]>
+ *
+ * Released under the terms of GNU General Public License, Version 2 or
+ * (at your option) any later version.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+import from Misc_Helpers all;
+import from Socket_API_Definitions all;
+
+import from StatsD_Types all;
+import from StatsD_CodecPort all;
+import from StatsD_CodecPort_CtrlFunct all;
+
+import from General_Types all;
+import from Osmocom_Types all;
+#ifdef STATSD_HAVE_VTY
+import from Osmocom_VTY_Functions all;
+import from TELNETasp_PortType all;
+#endif
+
+modulepar {
+       /* Whether to test stats values */
+       boolean mp_enable_stats := true;
+}
+
+type record StatsDMetricKey {
+       MetricName      name,
+       MetricType      mtype
+};
+type set of StatsDMetricKey StatsDMetricKeys;
+
+template (value) StatsDMetricKey ts_StatsDMetricKey(template (value) 
MetricName        name, template (value) MetricType mtype) := {
+       name := name,
+       mtype := mtype
+}
+
+type record StatsDExpect {
+       MetricName      name,
+       MetricType      mtype,
+       MetricValue     min,
+       MetricValue     max
+};
+type set of StatsDExpect StatsDExpects;
+
+type enumerated StatsDResultType {
+       e_Matched,
+       e_Mismatched,
+       e_NotFound
+}
+
+type record StatsDExpectResult {
+       StatsDResultType kind,
+       integer idx
+}
+
+type component StatsD_Checker_CT {
+#ifdef STATSD_HAVE_VTY
+       port TELNETasp_PT STATSVTY;
+#endif
+       port STATSD_PROC_PT STATSD_PROC;
+       port STATSD_CODEC_PT STATS;
+       timer T_statsd := 5.0;
+}
+
+type component StatsD_ConnHdlr {
+       port STATSD_PROC_PT STATSD_PROC;
+}
+
+signature STATSD_reset();
+signature STATSD_snapshot(in StatsDMetricKeys keys, in boolean 
since_last_snapshot) return StatsDMetrics;
+signature STATSD_expect(in StatsDExpects expects, in boolean wait_converge, in 
boolean use_snapshot, in StatsDMetrics snapshot) return boolean;
+
+type port STATSD_PROC_PT procedure {
+       inout STATSD_reset, STATSD_snapshot, STATSD_expect;
+} with {extension "internal"};
+
+/* Expect templates and functions */
+
+
+/* StatsD checker component */
+function main(charstring statsd_host, integer statsd_port) runs on 
StatsD_Checker_CT {
+       var StatsD_ConnHdlr vc_conn;
+       var StatsDMetricKeys keys;
+       var boolean since_last_snapshot;
+       var StatsDExpects expects;
+       var boolean wait_converge;
+       var boolean use_snapshot;
+       var StatsDMetrics snapshot;
+       var Result res;
+
+       while (not mp_enable_stats) {
+               log("StatsD checker disabled by modulepar");
+               f_sleep(3600.0);
+       }
+
+       map(self:STATS, system:STATS);
+       res := StatsD_CodecPort_CtrlFunct.f_IPL4_listen(STATS, statsd_host, 
statsd_port, { udp := {} }, {});
+       if (not ispresent(res.connId)) {
+               Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+                                       "Could not bind StatsD socket, check 
your configuration");
+       }
+
+#ifdef STATSD_HAVE_VTY
+       /* Connect to VTY and reset stats */
+       map(self:STATSVTY, system:STATSVTY);
+       f_vty_set_prompts(STATSVTY);
+       f_vty_transceive(STATSVTY, "enable");
+
+       /* Reset the stats system at start */
+       f_vty_transceive(STATSVTY, "stats reset");
+#endif
+
+       while (true) {
+               alt {
+               [] STATSD_PROC.getcall(STATSD_reset:{}) -> sender vc_conn {
+#ifdef STATSD_HAVE_VTY
+                       f_vty_transceive(STATSVTY, "stats reset");
+                       STATSD_PROC.reply(STATSD_reset:{}) to vc_conn;
+#else
+                       Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+                                               "STATSD_reset not supported, 
StatsD_Checker was built without VTY support");
+#endif
+                       }
+               [] STATSD_PROC.getcall(STATSD_snapshot:{?, ?}) -> param(keys, 
since_last_snapshot) sender vc_conn {
+                       snapshot := f_statsd_checker_snapshot(keys, 
since_last_snapshot);
+                       STATSD_PROC.reply(STATSD_snapshot:{keys, 
since_last_snapshot} value snapshot) to vc_conn;
+                       }
+               [] STATSD_PROC.getcall(STATSD_expect:{?, ?, ?, ?}) -> 
param(expects, wait_converge, use_snapshot, snapshot) sender vc_conn {
+                       var boolean success := f_statsd_checker_expect(expects, 
wait_converge, use_snapshot, snapshot);
+                       STATSD_PROC.reply(STATSD_expect:{expects, 
wait_converge, use_snapshot, snapshot} value success) to vc_conn;
+                       }
+               }
+       }
+}
+
+/* Updates "metrics" & "seen" with content from "it". Returns true if the 
metric becomes known (for first time) as a result. */
+private function f_statsd_metrics_update_value(inout StatsDMetrics metrics, 
inout Booleans seen, StatsDMetric it) return boolean
+{
+       for (var integer i := 0; i < lengthof(metrics); i := i + 1) {
+               log("Rx new metric ", it);
+               if (it.name != metrics[i].name or it.mtype != metrics[i].mtype) 
{
+                       log("PESPIN: metric[",i,"] ", it, " doesn't match ", 
metrics[i]);
+                       continue;
+               }
+               metrics[i] := it;
+               log("PESPIN: metric[",i,"] seen =", seen[i]);
+               if (seen[i]) {
+                       return false;
+               } else {
+                       seen[i] := true;
+                       return true;
+               }
+       }
+       return false;
+}
+
+
+private function f_statsd_checker_snapshot(StatsDMetricKeys keys, boolean 
since_last_snapshot := true) runs on StatsD_Checker_CT return StatsDMetrics {
+       var default t;
+       var StatsDMessage msg;
+       var StatsDMetrics metrics := {};
+       var Booleans seen := {};
+       var integer seen_remain := 0;
+
+       for (var integer i := 0; i < lengthof(keys); i := i + 1) {
+               metrics := metrics & {valueof(ts_StatsDMetric(keys[i].name, 0, 
keys[i].mtype))};
+               seen := seen & {false};
+               seen_remain := seen_remain + 1;
+       }
+
+       if (not since_last_snapshot) {
+               STATS.clear;
+       }
+
+       T_statsd.start;
+       while (seen_remain > 0) {
+               var StatsD_RecvFrom rf;
+               alt {
+               [] STATS.receive(tr_StatsD_RecvFrom(?, ?)) -> value rf {
+                       msg := rf.msg;
+                       }
+               [] T_statsd.timeout {
+                       for (var integer i := 0; i < lengthof(metrics); i := i 
+ 1) {
+                               /* We're still missing some expects, keep 
looking */
+                               if (not seen[i]) {
+                                       log("Timeout waiting for ", 
metrics[i].name);
+                               }
+                       }
+                       Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+                                               log2str("Timeout waiting for 
metrics: ", keys, seen));
+               }
+               }
+
+               for (var integer i := 0; i < lengthof(msg); i := i + 1) {
+                       var StatsDMetric metric := msg[i];
+                       if (f_statsd_metrics_update_value(metrics, seen, 
metric)) {
+                               seen_remain := seen_remain - 1;
+                       }
+               }
+       }
+       T_statsd.stop;
+
+       return metrics;
+}
+
+private function get_val_from_snapshot(inout integer val, StatsDMetric metric, 
StatsDMetrics snapshot) return boolean
+{
+       for (var integer i := 0; i < lengthof(snapshot); i := i + 1) {
+               if (metric.name != snapshot[i].name or metric.mtype != 
snapshot[i].mtype) {
+                       continue;
+               }
+               val := snapshot[i].val;
+               return true;
+       }
+       return false;
+}
+
+/* Return false if the expectation doesn't match the metric, otherwise return 
true */
+private function f_compare_expect(StatsDMetric metric,
+                                 StatsDExpect expect,
+                                 boolean use_snapshot := false,
+                                 StatsDMetrics snapshot := {}) return boolean {
+       var integer val := 0;
+       if ((metric.name != expect.name) or (metric.mtype != expect.mtype)) {
+               return false;
+       }
+       if (use_snapshot) {
+               var integer prev_val := 0;
+               if (not get_val_from_snapshot(prev_val, metric, snapshot)) {
+                       Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail,
+                                               log2str("Metric ", metric.name, 
" not found in snapshot ", snapshot));
+               }
+               val := metric.val - prev_val;
+       } else {
+               val := metric.val;
+       }
+
+       if ((val < expect.min) or (val > expect.max)) {
+               return false;
+       }
+       return true;
+}
+
+private function f_statsd_checker_metric_expects(StatsDExpects expects,
+                                                StatsDMetric metric,
+                                                boolean use_snapshot := false,
+                                                StatsDMetrics snapshot := {})
+return StatsDExpectResult {
+       var StatsDExpectResult result := {
+               kind := e_NotFound,
+               idx := -1
+       };
+
+       for (var integer i := 0; i < lengthof(expects); i := i + 1) {
+               var StatsDExpect exp := expects[i];
+               if (exp.name != metric.name) {
+                       continue;
+               }
+               if (not f_compare_expect(metric, exp, use_snapshot, snapshot)) {
+                       log("EXP mismatch: ", metric, " vs exp ", exp, " | 
use_snapshot=", use_snapshot, ", snapshot=", snapshot);
+                       result := {
+                               kind := e_Mismatched,
+                               idx := i
+                       };
+                       break;
+               } else {
+                       log("EXP match: ", metric, " vs exp ", exp);
+                       result := {
+                               kind := e_Matched,
+                               idx := i
+                       };
+                       break;
+               }
+       }
+       return result;
+}
+
+private function f_statsd_checker_expect(StatsDExpects expects,
+                                        boolean wait_converge := false,
+                                        boolean use_snapshot := false,
+                                        StatsDMetrics snapshot := {}) runs on 
StatsD_Checker_CT return boolean {
+       var default t;
+       var StatsDMessage msg;
+       var StatsDExpectResult res;
+       var Booleans matched := {};
+       var integer matched_remain := 0;
+
+       for (var integer i := 0; i < lengthof(expects); i := i + 1) {
+               matched := matched & {false};
+               matched_remain := matched_remain + 1;
+       }
+
+       /* Dismiss any messages we might have skipped from the last report */
+       STATS.clear;
+
+       if (not use_snapshot) {
+#ifdef STATSD_HAVE_VTY
+               f_vty_transceive(STATSVTY, "stats report");
+#else
+               /* Assume caller knows previous state, eg. gauges may have been 
0 due to IUT being reset */
+#endif
+       }
+
+       T_statsd.start;
+       while (matched_remain > 0) {
+               var StatsD_RecvFrom rf;
+               alt {
+               [] STATS.receive(tr_StatsD_RecvFrom(?, ?)) -> value rf {
+                       msg := rf.msg;
+                       }
+               [] T_statsd.timeout {
+                       for (var integer i := 0; i < lengthof(expects); i := i 
+ 1) {
+                               /* We're still missing some expects, keep 
looking */
+                               if (not matched[i]) {
+                                       log("Timeout waiting for ", 
expects[i].name,
+                                           " (min: ", expects[i].min, ", max: 
", expects[i].max, ")");
+                               }
+                       }
+                       setverdict(fail, "Timeout waiting for metrics");
+                       return false;
+               }
+               }
+
+               for (var integer i := 0; i < lengthof(msg); i := i + 1) {
+                       var StatsDMetric metric := msg[i];
+                       res := f_statsd_checker_metric_expects(expects, metric, 
use_snapshot, snapshot);
+                       if (res.kind == e_NotFound) {
+                               continue;
+                       }
+                       if (res.kind == e_Mismatched) {
+                               if (wait_converge and not matched[res.idx]) {
+                                       log("Waiting convergence: Ignoring 
metric mismatch metric=", metric, " expect=", expects[res.idx])
+                                       continue;
+                               }
+                               log("Metric: ", metric);
+                               log("Expect: ", expects[res.idx]);
+                               setverdict(fail, "Metric failed expectation ", 
metric, " vs ", expects[res.idx]);
+                               return false;
+                       }
+                       if (res.kind == e_Matched) {
+                               if (not matched[res.idx]) {
+                                       matched[res.idx] := true;
+                                       matched_remain := matched_remain - 1;
+                               }
+                               continue;
+                       }
+               }
+       }
+       T_statsd.stop;
+       return true;
+}
+
+function f_init_statsd(charstring id, inout StatsD_Checker_CT vc_STATSD, 
charstring local_addr, integer local_port) {
+       id := id & "-STATS";
+
+       vc_STATSD := StatsD_Checker_CT.create(id);
+       vc_STATSD.start(StatsD_Checker.main(local_addr, local_port));
+}
+
+
+/* StatsD connhdlr */
+function f_statsd_reset() runs on StatsD_ConnHdlr {
+       if (not mp_enable_stats) {
+               return;
+       }
+
+       STATSD_PROC.call(STATSD_reset:{}) {
+               [] STATSD_PROC.getreply(STATSD_reset:{}) {}
+       }
+}
+
+/* Useful to automatically generate param for f_statsd_snapshot() from 
StatsDExpects used in f_statsd_expect_from_snapshot() */
+function f_statsd_keys_from_expect(StatsDExpects expects) return 
StatsDMetricKeys
+{
+       var StatsDMetricKeys keys := {}
+       for (var integer i := 0; i < lengthof(expects); i := i + 1) {
+               keys := keys & {valueof(ts_StatsDMetricKey(expects[i].name, 
expects[i].mtype))}
+       }
+       return keys;
+}
+
+/* Retrieve current values obtained at statsd server.
+* If since_last_snapshot is false, then clear the received packets in port. */
+function f_statsd_snapshot(StatsDMetricKeys keys, boolean since_last_snapshot 
:= true) runs on StatsD_ConnHdlr return StatsDMetrics {
+       var StatsDMetrics snapshot;
+       if (not mp_enable_stats) {
+               return {};
+       }
+
+       STATSD_PROC.call(STATSD_snapshot:{keys, since_last_snapshot}) {
+               [] STATSD_PROC.getreply(STATSD_snapshot:{keys, 
since_last_snapshot}) -> value snapshot;
+       }
+       return snapshot;
+}
+
+function f_statsd_expect(StatsDExpects expects, boolean wait_converge := 
false) runs on StatsD_ConnHdlr return boolean {
+       var boolean res;
+
+       if (not mp_enable_stats) {
+               return true;
+       }
+
+       STATSD_PROC.call(STATSD_expect:{expects, wait_converge, false, {}}) {
+               [] STATSD_PROC.getreply(STATSD_expect:{expects, wait_converge, 
false, {}}) -> value res;
+       }
+       return res;
+}
+
+function f_statsd_expect_from_snapshot(StatsDExpects expects, boolean 
wait_converge := false,
+                                      StatsDMetrics snapshot := {}) runs on 
StatsD_ConnHdlr return boolean {
+       var boolean res;
+
+       if (not mp_enable_stats) {
+               return true;
+       }
+
+       STATSD_PROC.call(STATSD_expect:{expects, wait_converge, true, 
snapshot}) {
+               [] STATSD_PROC.getreply(STATSD_expect:{expects, wait_converge, 
true, snapshot}) -> value res;
+       }
+       return res;
+}
+
+}
diff --git a/library/StatsD_Types.ttcn b/library/StatsD_Types.ttcn
index 8938571..e60b8e8 100644
--- a/library/StatsD_Types.ttcn
+++ b/library/StatsD_Types.ttcn
@@ -33,6 +33,7 @@
        MetricType      mtype,
        MetricSampleRate srate optional
 };
+type set of StatsDMetric StatsDMetrics;

 type record of StatsDMetric StatsDMessage with {
        variant "SEPARATOR('\n')";
@@ -48,7 +49,8 @@
        [0] := metric
 }

-template (present) StatsDMetric tr_StatsDMetric(template (present) MetricName 
name, template (present) MetricValue val := ?,
+template (present) StatsDMetric tr_StatsDMetric(template (present) MetricName 
name,
+                                               template (present) MetricValue 
val := ?,
                                                template (present) MetricType 
mtype) := {
        name := name,
        val := val,
@@ -56,10 +58,30 @@
        srate := *
 }

-template (present) StatsDMetric tr_StatsDMetricCounter(template (present) 
MetricName name, template (present) MetricValue val := ?) :=
+template (present) StatsDMetric tr_StatsDMetricCounter(template (present) 
MetricName name,
+                                                      template (present) 
MetricValue val := ?) :=
        tr_StatsDMetric(name, val, "c");

-template (present) StatsDMetric tr_StatsDMetricGauge(template (present) 
MetricName name, template (present) MetricValue val := ?) :=
+template (present) StatsDMetric tr_StatsDMetricGauge(template (present) 
MetricName name,
+                                                    template (present) 
MetricValue val := ?) :=
        tr_StatsDMetric(name, val, "g");

+template (value) StatsDMetric ts_StatsDMetric(template (value) MetricName name,
+                                             template (value) MetricValue val 
:= 0,
+                                             template (value) MetricType mtype 
:= "c",
+                                             template (omit) MetricSampleRate 
srate := omit) := {
+       name := name,
+       val := val,
+       mtype := mtype,
+       srate := srate
+}
+
+template (value) StatsDMetric ts_StatsDMetricCounter(template (value) 
MetricName name,
+                                                    template (value) 
MetricValue val := 0) :=
+       ts_StatsDMetric(name, val, "c");
+
+template (value) StatsDMetric ts_StatsDMetricGauge(template (value) MetricName 
name,
+                                                  template (value) MetricValue 
val := 0) :=
+       ts_StatsDMetric(name, val, "g");
+
 } with { encode "TEXT" }
diff --git a/mgw/gen_links.sh b/mgw/gen_links.sh
index 2fd2b0d..7662b55 100755
--- a/mgw/gen_links.sh
+++ b/mgw/gen_links.sh
@@ -46,7 +46,7 @@
 FILES+="Native_Functions.ttcn Native_FunctionDefs.cc IPCP_Types.ttcn "
 FILES+="Osmocom_VTY_Functions.ttcn "
 FILES+="RTP_CodecPort_CtrlFunct.ttcn RTP_CodecPort_CtrlFunctDef.cc "
-FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcn "
+FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcnpp "
 FILES+="IPA_Types.ttcn IPA_Emulation.ttcnpp IPA_CodecPort.ttcn 
IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc "
 FILES+="Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn 
Osmocom_CTRL_Adapter.ttcn "

diff --git a/mgw/regen_makefile.sh b/mgw/regen_makefile.sh
index 921956c..2b78cf0 100755
--- a/mgw/regen_makefile.sh
+++ b/mgw/regen_makefile.sh
@@ -25,6 +25,7 @@

 export CPPFLAGS_TTCN3="
        -DIPA_EMULATION_CTRL
+       -DSTATSD_HAVE_VTY
 "

 ../regen-makefile.sh -e $NAME $FILES
diff --git a/ns/gen_links.sh b/ns/gen_links.sh
index 8d78ddf..fde0a67 100755
--- a/ns/gen_links.sh
+++ b/ns/gen_links.sh
@@ -51,7 +51,7 @@

 DIR=../library
 FILES="Misc_Helpers.ttcn General_Types.ttcn Osmocom_VTY_Functions.ttcn 
Native_Functions.ttcn Native_FunctionDefs.cc GSM_Types.ttcn Osmocom_Types.ttcn "
-FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcn "
+FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcnpp "
 FILES+="RAW_NS.ttcnpp NS_Provider_IPL4.ttcn NS_Provider_FR.ttcn 
NS_Emulation.ttcnpp "
 FILES+="BSSGP_Emulation.ttcnpp Osmocom_Gb_Types.ttcn "
 FILES+="LLC_Templates.ttcn "
diff --git a/ns/regen_makefile.sh b/ns/regen_makefile.sh
index ad86d71..57dde2d 100755
--- a/ns/regen_makefile.sh
+++ b/ns/regen_makefile.sh
@@ -22,6 +22,7 @@
 export CPPFLAGS_TTCN3="
        -DBSSGP_EM_L3
        -DNS_EMULATION_FR
+       -DSTATSD_HAVE_VTY
 "

 ../regen-makefile.sh -e $NAME $FILES
diff --git a/pcu/gen_links.sh b/pcu/gen_links.sh
index 556f02a..146f538 100755
--- a/pcu/gen_links.sh
+++ b/pcu/gen_links.sh
@@ -50,7 +50,7 @@

 DIR=../library
 FILES="Misc_Helpers.ttcn General_Types.ttcn Osmocom_VTY_Functions.ttcn 
Native_Functions.ttcn Native_FunctionDefs.cc GSM_Types.ttcn GSM_RR_Types.ttcn 
GSM_RestOctets.ttcn Osmocom_Types.ttcn RLCMAC_Templates.ttcn RLCMAC_Types.ttcn 
RLCMAC_CSN1_Templates.ttcn RLCMAC_CSN1_Types.ttcn RLCMAC_EncDec.cc "
-FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcn "
+FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcnpp "
 FILES+="RAW_NS.ttcnpp NS_Provider_IPL4.ttcn NS_Emulation.ttcnpp "
 FILES+="BSSGP_Emulation.ttcnpp Osmocom_Gb_Types.ttcn "
 FILES+="LLC_Templates.ttcn L3_Templates.ttcn L3_Common.ttcn "
diff --git a/pcu/regen_makefile.sh b/pcu/regen_makefile.sh
index 006c6ba..9cb1554 100755
--- a/pcu/regen_makefile.sh
+++ b/pcu/regen_makefile.sh
@@ -22,6 +22,7 @@
 export CPPFLAGS_TTCN3="
        -DBSSGP_EM_L3
        -DIPA_EMULATION_CTRL
+       -DSTATSD_HAVE_VTY
 "

 ../regen-makefile.sh -e $NAME $FILES
diff --git a/upf/gen_links.sh b/upf/gen_links.sh
index 3865110..6c710ee 100755
--- a/upf/gen_links.sh
+++ b/upf/gen_links.sh
@@ -29,7 +29,7 @@

 DIR=../library
 FILES="Misc_Helpers.ttcn General_Types.ttcn Osmocom_Types.ttcn 
Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc 
IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn 
IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp Osmocom_CTRL_Types.ttcn 
Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn "
-FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcn "
+FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn 
StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc 
StatsD_Checker.ttcnpp "
 FILES+="PFCP_CodecPort.ttcn PFCP_CodecPort_CtrlFunct.ttcn 
PFCP_CodecPort_CtrlFunctDef.cc PFCP_Emulation.ttcn PFCP_Templates.ttcn"
 gen_links $DIR $FILES

diff --git a/upf/regen_makefile.sh b/upf/regen_makefile.sh
index 953e10f..1368db1 100755
--- a/upf/regen_makefile.sh
+++ b/upf/regen_makefile.sh
@@ -19,6 +19,7 @@

 export CPPFLAGS_TTCN3="
        -DIPA_EMULATION_CTRL
+       -DSTATSD_HAVE_VTY
 "

 ../regen-makefile.sh -e $NAME $FILES

--
To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/37956?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: I5421c76e4f303fd16d4db945a1c69910e40ac820
Gerrit-Change-Number: 37956
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <[email protected]>

Reply via email to