<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/19581">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  laforge: Looks good to me, approved
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add parsing and checking of StatsD metrics<br><br>Change-Id: Icd1317b5f192d98e6cdc6635788d450501992bf1<br>Related: SYS#4877<br>---<br>A library/StatsD_Checker.ttcn<br>A library/StatsD_CodecPort.ttcn<br>A library/StatsD_CodecPort_CtrlFunct.ttcn<br>A library/StatsD_CodecPort_CtrlFunctdef.cc<br>A library/StatsD_Types.ttcn<br>5 files changed, 496 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/library/StatsD_Checker.ttcn b/library/StatsD_Checker.ttcn</span><br><span>new file mode 100644</span><br><span>index 0000000..d287b74</span><br><span>--- /dev/null</span><br><span>+++ b/library/StatsD_Checker.ttcn</span><br><span>@@ -0,0 +1,272 @@</span><br><span style="color: hsl(120, 100%, 40%);">+module StatsD_Checker {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Verifies that  StatsD metrics in a test match the expected values</span><br><span style="color: hsl(120, 100%, 40%);">+ * Uses StatsD_CodecPort to receive the statsd messages from the DUT</span><br><span style="color: hsl(120, 100%, 40%);">+ * and a separate VTY connection to reset and trigger the stats.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * When using this you should configure your stats reporter to disable</span><br><span style="color: hsl(120, 100%, 40%);">+ * interval-based reports and always send all metrics:</span><br><span style="color: hsl(120, 100%, 40%);">+ * > stats interval 0</span><br><span style="color: hsl(120, 100%, 40%);">+ * > stats reporter statsd</span><br><span style="color: hsl(120, 100%, 40%);">+ * >  remote-ip a.b.c.d</span><br><span style="color: hsl(120, 100%, 40%);">+ * >  remote-port 8125</span><br><span style="color: hsl(120, 100%, 40%);">+ * >  level subscriber</span><br><span style="color: hsl(120, 100%, 40%);">+ * >  flush-period 1</span><br><span style="color: hsl(120, 100%, 40%);">+ * >  mtu 1024</span><br><span style="color: hsl(120, 100%, 40%);">+ * >  enable</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * All rights reserved.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Daniel Willmann <dwillmann@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Released under the terms of GNU General Public License, Version 2 or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0-or-later</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import from StatsD_Types all;</span><br><span style="color: hsl(120, 100%, 40%);">+import from StatsD_CodecPort all;</span><br><span style="color: hsl(120, 100%, 40%);">+import from StatsD_CodecPort_CtrlFunct all;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import from Osmocom_Types all;</span><br><span style="color: hsl(120, 100%, 40%);">+import from Osmocom_VTY_Functions all;</span><br><span style="color: hsl(120, 100%, 40%);">+import from TELNETasp_PortType all;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+modulepar {</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Whether to test stats values */</span><br><span style="color: hsl(120, 100%, 40%);">+    boolean mp_enable_stats := false;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type record StatsDExpect {</span><br><span style="color: hsl(120, 100%, 40%);">+    MetricName      name,</span><br><span style="color: hsl(120, 100%, 40%);">+ MetricType      mtype,</span><br><span style="color: hsl(120, 100%, 40%);">+        MetricValue     min,</span><br><span style="color: hsl(120, 100%, 40%);">+  MetricValue     max</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type set of StatsDExpect StatsDExpects;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type record StatsDExpectPriv {</span><br><span style="color: hsl(120, 100%, 40%);">+  StatsDExpect expect,</span><br><span style="color: hsl(120, 100%, 40%);">+  integer seen</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type set of StatsDExpectPriv StatsDExpectPrivs;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type enumerated StatsDResultType {</span><br><span style="color: hsl(120, 100%, 40%);">+      e_Matched,</span><br><span style="color: hsl(120, 100%, 40%);">+    e_Mismatched,</span><br><span style="color: hsl(120, 100%, 40%);">+ e_NotFound</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type record StatsDExpectResult {</span><br><span style="color: hsl(120, 100%, 40%);">+     StatsDResultType kind,</span><br><span style="color: hsl(120, 100%, 40%);">+        integer idx</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type component StatsD_Checker_CT {</span><br><span style="color: hsl(120, 100%, 40%);">+  port TELNETasp_PT STATSVTY;</span><br><span style="color: hsl(120, 100%, 40%);">+   port STATSD_PROC_PT STATSD_PROC;</span><br><span style="color: hsl(120, 100%, 40%);">+      port STATSD_CODEC_PT STATS;</span><br><span style="color: hsl(120, 100%, 40%);">+   timer T_statsd := 5.0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type component StatsD_ConnHdlr {</span><br><span style="color: hsl(120, 100%, 40%);">+ port STATSD_PROC_PT STATSD_PROC;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+signature STATSD_reset();</span><br><span style="color: hsl(120, 100%, 40%);">+signature STATSD_expect(in StatsDExpects expects) return boolean;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type port STATSD_PROC_PT procedure {</span><br><span style="color: hsl(120, 100%, 40%);">+   inout STATSD_reset, STATSD_expect;</span><br><span style="color: hsl(120, 100%, 40%);">+} with {extension "internal"};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Expect templates and functions */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* StatsD checker component */</span><br><span style="color: hsl(120, 100%, 40%);">+function main(charstring statsd_host, integer statsd_port) runs on StatsD_Checker_CT {</span><br><span style="color: hsl(120, 100%, 40%);">+      var StatsD_ConnHdlr vc_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+  var StatsDExpects expects;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  while (not mp_enable_stats) {</span><br><span style="color: hsl(120, 100%, 40%);">+         log("StatsD checker disabled by modulepar");</span><br><span style="color: hsl(120, 100%, 40%);">+                f_sleep(3600.0);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   map(self:STATS, system:STATS);</span><br><span style="color: hsl(120, 100%, 40%);">+        StatsD_CodecPort_CtrlFunct.f_IPL4_listen(STATS, statsd_host, statsd_port, { udp := {} }, {});</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Connect to VTY and reset stats */</span><br><span style="color: hsl(120, 100%, 40%);">+  map(self:STATSVTY, system:STATSVTY);</span><br><span style="color: hsl(120, 100%, 40%);">+  f_vty_set_prompts(STATSVTY);</span><br><span style="color: hsl(120, 100%, 40%);">+  f_vty_transceive(STATSVTY, "enable");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Reset the stats system at start */</span><br><span style="color: hsl(120, 100%, 40%);">+ f_vty_transceive(STATSVTY, "stats reset");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        while (true) {</span><br><span style="color: hsl(120, 100%, 40%);">+                alt {</span><br><span style="color: hsl(120, 100%, 40%);">+         [] STATSD_PROC.getcall(STATSD_reset:{}) -> sender vc_conn {</span><br><span style="color: hsl(120, 100%, 40%);">+                        f_vty_transceive(STATSVTY, "stats reset");</span><br><span style="color: hsl(120, 100%, 40%);">+                  STATSD_PROC.reply(STATSD_reset:{}) to vc_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             [] STATSD_PROC.getcall(STATSD_expect:{?}) -> param(expects) sender vc_conn {</span><br><span style="color: hsl(120, 100%, 40%);">+                       var boolean success := f_statsd_checker_expect(expects);</span><br><span style="color: hsl(120, 100%, 40%);">+                      STATSD_PROC.reply(STATSD_expect:{expects} value success) to vc_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+                  }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Return false if the expectation doesn't match the metric, otherwise return true */</span><br><span style="color: hsl(120, 100%, 40%);">+private function f_compare_expect(StatsDMetric metric, StatsDExpect expect) return boolean {</span><br><span style="color: hsl(120, 100%, 40%);">+     if ((metric.name == expect.name) and (metric.mtype == expect.mtype)</span><br><span style="color: hsl(120, 100%, 40%);">+           and (metric.val >= expect.min) and (metric.val <= expect.max)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return true;</span><br><span style="color: hsl(120, 100%, 40%);">+  } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+private function f_statsd_checker_metric_expects(StatsDExpectPrivs exp_seen, StatsDMetric metric)</span><br><span style="color: hsl(120, 100%, 40%);">+return StatsDExpectResult {</span><br><span style="color: hsl(120, 100%, 40%);">+        var StatsDExpectResult result := {</span><br><span style="color: hsl(120, 100%, 40%);">+            kind := e_NotFound,</span><br><span style="color: hsl(120, 100%, 40%);">+           idx := -1</span><br><span style="color: hsl(120, 100%, 40%);">+     };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  for (var integer i := 0; i < lengthof(exp_seen); i := i + 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+             var StatsDExpectPriv exp := exp_seen[i];</span><br><span style="color: hsl(120, 100%, 40%);">+              if (exp.expect.name != metric.name) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (not f_compare_expect(metric, exp.expect)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       log("EXP mismatch: ", metric, exp.expect);</span><br><span style="color: hsl(120, 100%, 40%);">+                  result := {</span><br><span style="color: hsl(120, 100%, 40%);">+                           kind := e_Mismatched,</span><br><span style="color: hsl(120, 100%, 40%);">+                         idx := i</span><br><span style="color: hsl(120, 100%, 40%);">+                      };</span><br><span style="color: hsl(120, 100%, 40%);">+                    break;</span><br><span style="color: hsl(120, 100%, 40%);">+                } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      log("EXP match: ", metric, exp.expect);</span><br><span style="color: hsl(120, 100%, 40%);">+                     result := {</span><br><span style="color: hsl(120, 100%, 40%);">+                           kind := e_Matched,</span><br><span style="color: hsl(120, 100%, 40%);">+                            idx := i</span><br><span style="color: hsl(120, 100%, 40%);">+                      };</span><br><span style="color: hsl(120, 100%, 40%);">+                    break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return result;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+template StatsDExpectPriv t_statsd_expect_priv(template StatsDExpect expect) := {</span><br><span style="color: hsl(120, 100%, 40%);">+        expect := expect,</span><br><span style="color: hsl(120, 100%, 40%);">+     seen := 0</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+private function f_statsd_checker_expect(StatsDExpects expects) runs on StatsD_Checker_CT return boolean {</span><br><span style="color: hsl(120, 100%, 40%);">+    var default t;</span><br><span style="color: hsl(120, 100%, 40%);">+        var StatsDMessage msg;</span><br><span style="color: hsl(120, 100%, 40%);">+        var StatsDExpectResult res;</span><br><span style="color: hsl(120, 100%, 40%);">+   var StatsDExpectPrivs exp_seen := {};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       for (var integer i := 0; i < lengthof(expects); i := i + 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+              exp_seen := exp_seen & {valueof(t_statsd_expect_priv(expects[i]))};</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Dismiss any messages we might have skipped from the last report */</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS.clear;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        f_vty_transceive(STATSVTY, "stats report");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       var boolean seen_all := false;</span><br><span style="color: hsl(120, 100%, 40%);">+        T_statsd.start;</span><br><span style="color: hsl(120, 100%, 40%);">+       while (not seen_all) {</span><br><span style="color: hsl(120, 100%, 40%);">+                var StatsD_RecvFrom rf;</span><br><span style="color: hsl(120, 100%, 40%);">+               alt {</span><br><span style="color: hsl(120, 100%, 40%);">+         [] STATS.receive(tr_StatsD_RecvFrom(?, ?)) -> value rf {</span><br><span style="color: hsl(120, 100%, 40%);">+                   msg := rf.msg;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             [] T_statsd.timeout {</span><br><span style="color: hsl(120, 100%, 40%);">+                 for (var integer i := 0; i < lengthof(exp_seen); i := i + 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             /* We're still missing some expects, keep looking */</span><br><span style="color: hsl(120, 100%, 40%);">+                              if (exp_seen[i].seen == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                  log("Timeout waiting for ", exp_seen[i].expect.name, " (min: ", exp_seen[i].expect.min,</span><br><span style="color: hsl(120, 100%, 40%);">+                                           ", max: ", exp_seen[i].expect.max, ")");</span><br><span style="color: hsl(120, 100%, 40%);">+                          }</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     setverdict(fail, "Timeout waiting for metrics");</span><br><span style="color: hsl(120, 100%, 40%);">+                    return false;</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           for (var integer i := 0; i < lengthof(msg); i := i + 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  var StatsDMetric metric := msg[i];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                  res := f_statsd_checker_metric_expects(exp_seen, metric);</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (res.kind == e_NotFound) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (res.kind == e_Mismatched) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               log("Metric: ", metric);</span><br><span style="color: hsl(120, 100%, 40%);">+                            log("Expect: ", exp_seen[res.idx].expect);</span><br><span style="color: hsl(120, 100%, 40%);">+                          setverdict(fail, "Metric failed expectation ", metric, " vs ", exp_seen[res.idx].expect);</span><br><span style="color: hsl(120, 100%, 40%);">+                         return false;</span><br><span style="color: hsl(120, 100%, 40%);">+                 } else if (res.kind == e_Matched) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           exp_seen[res.idx].seen := exp_seen[res.idx].seen + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                 }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Check if all expected metrics were received */</span><br><span style="color: hsl(120, 100%, 40%);">+             seen_all := true;</span><br><span style="color: hsl(120, 100%, 40%);">+             for (var integer i := 0; i < lengthof(exp_seen); i := i + 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     /* We're still missing some expects, keep looking */</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (exp_seen[i].seen == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                          seen_all := false;</span><br><span style="color: hsl(120, 100%, 40%);">+                            break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   T_statsd.stop;</span><br><span style="color: hsl(120, 100%, 40%);">+        return seen_all;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+function f_init_statsd(charstring id, inout StatsD_Checker_CT vc_STATSD, charstring dst_addr, integer dst_port) {</span><br><span style="color: hsl(120, 100%, 40%);">+      id := id & "-STATS";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  vc_STATSD := StatsD_Checker_CT.create(id);</span><br><span style="color: hsl(120, 100%, 40%);">+    vc_STATSD.start(StatsD_Checker.main(dst_addr, dst_port));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* StatsD connhdlr */</span><br><span style="color: hsl(120, 100%, 40%);">+function f_statsd_reset() runs on StatsD_ConnHdlr {</span><br><span style="color: hsl(120, 100%, 40%);">+  if (not mp_enable_stats) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   STATSD_PROC.call(STATSD_reset:{}) {</span><br><span style="color: hsl(120, 100%, 40%);">+           [] STATSD_PROC.getreply(STATSD_reset:{}) {}</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+function f_statsd_expect(StatsDExpects expects) runs on StatsD_ConnHdlr return boolean {</span><br><span style="color: hsl(120, 100%, 40%);">+      var boolean res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (not mp_enable_stats) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return true;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   STATSD_PROC.call(STATSD_expect:{expects}) {</span><br><span style="color: hsl(120, 100%, 40%);">+           [] STATSD_PROC.getreply(STATSD_expect:{expects}) -> value res;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/library/StatsD_CodecPort.ttcn b/library/StatsD_CodecPort.ttcn</span><br><span>new file mode 100644</span><br><span>index 0000000..e7396b5</span><br><span>--- /dev/null</span><br><span>+++ b/library/StatsD_CodecPort.ttcn</span><br><span>@@ -0,0 +1,57 @@</span><br><span style="color: hsl(120, 100%, 40%);">+module StatsD_CodecPort {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import from StatsD_Types all;</span><br><span style="color: hsl(120, 100%, 40%);">+import from IPL4asp_PortType all;</span><br><span style="color: hsl(120, 100%, 40%);">+import from IPL4asp_Types all;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type record StatsD_RecvFrom {</span><br><span style="color: hsl(120, 100%, 40%);">+     ConnectionId    connId,</span><br><span style="color: hsl(120, 100%, 40%);">+       HostName                remName,</span><br><span style="color: hsl(120, 100%, 40%);">+      PortNumber              remPort,</span><br><span style="color: hsl(120, 100%, 40%);">+      HostName                locName,</span><br><span style="color: hsl(120, 100%, 40%);">+      PortNumber              locPort,</span><br><span style="color: hsl(120, 100%, 40%);">+      StatsDMessage   msg</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+template StatsD_RecvFrom tr_StatsD_RecvFrom(template ConnectionId cid, template StatsDMessage msg) := {</span><br><span style="color: hsl(120, 100%, 40%);">+     connId := cid,</span><br><span style="color: hsl(120, 100%, 40%);">+        remName := ?,</span><br><span style="color: hsl(120, 100%, 40%);">+ remPort := ?,</span><br><span style="color: hsl(120, 100%, 40%);">+ locName := ?,</span><br><span style="color: hsl(120, 100%, 40%);">+ locPort := ?,</span><br><span style="color: hsl(120, 100%, 40%);">+ msg := msg</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type record StatsD_Send {</span><br><span style="color: hsl(120, 100%, 40%);">+    ConnectionId    connId,</span><br><span style="color: hsl(120, 100%, 40%);">+       StatsDMessage   msg</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+private function IPL4_to_StatsD_RecvFrom(in ASP_RecvFrom pin, out StatsD_RecvFrom pout) {</span><br><span style="color: hsl(120, 100%, 40%);">+   pout.connId := pin.connId;</span><br><span style="color: hsl(120, 100%, 40%);">+    pout.remName := pin.remName;</span><br><span style="color: hsl(120, 100%, 40%);">+  pout.remPort := pin.remPort;</span><br><span style="color: hsl(120, 100%, 40%);">+  pout.locName := pin.locName;</span><br><span style="color: hsl(120, 100%, 40%);">+  pout.locPort := pin.locPort;</span><br><span style="color: hsl(120, 100%, 40%);">+  pout.msg := dec_StatsDMessage(oct2char(pin.msg));</span><br><span style="color: hsl(120, 100%, 40%);">+} with { extension "prototype(fast)" };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+private function StatsD_to_IPL4_Send(in StatsD_Send pin, out ASP_Send pout) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pout.connId := pin.connId;</span><br><span style="color: hsl(120, 100%, 40%);">+    pout.proto := { udp := {} };</span><br><span style="color: hsl(120, 100%, 40%);">+  pout.msg := char2oct(enc_StatsDMessage(pin.msg));</span><br><span style="color: hsl(120, 100%, 40%);">+} with { extension "prototype(fast)" };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type port STATSD_CODEC_PT message {</span><br><span style="color: hsl(120, 100%, 40%);">+   out     StatsD_Send;</span><br><span style="color: hsl(120, 100%, 40%);">+  in      StatsD_RecvFrom,</span><br><span style="color: hsl(120, 100%, 40%);">+              ASP_ConnId_ReadyToRelease,</span><br><span style="color: hsl(120, 100%, 40%);">+            ASP_Event;</span><br><span style="color: hsl(120, 100%, 40%);">+} with { extension "user IPL4asp_PT</span><br><span style="color: hsl(120, 100%, 40%);">+  out(StatsD_Send -> ASP_Send: function(StatsD_to_IPL4_Send))</span><br><span style="color: hsl(120, 100%, 40%);">+        in(ASP_RecvFrom -> StatsD_RecvFrom: function(IPL4_to_StatsD_RecvFrom);</span><br><span style="color: hsl(120, 100%, 40%);">+        ASP_ConnId_ReadyToRelease -> ASP_ConnId_ReadyToRelease: simple;</span><br><span style="color: hsl(120, 100%, 40%);">+    ASP_Event -> ASP_Event: simple)"</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/library/StatsD_CodecPort_CtrlFunct.ttcn b/library/StatsD_CodecPort_CtrlFunct.ttcn</span><br><span>new file mode 100644</span><br><span>index 0000000..b2927c3</span><br><span>--- /dev/null</span><br><span>+++ b/library/StatsD_CodecPort_CtrlFunct.ttcn</span><br><span>@@ -0,0 +1,43 @@</span><br><span style="color: hsl(120, 100%, 40%);">+module StatsD_CodecPort_CtrlFunct {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import from StatsD_CodecPort all;</span><br><span style="color: hsl(120, 100%, 40%);">+import from IPL4asp_Types all;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+external function f_IPL4_listen(</span><br><span style="color: hsl(120, 100%, 40%);">+ inout STATSD_CODEC_PT portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+        in HostName locName,</span><br><span style="color: hsl(120, 100%, 40%);">+  in PortNumber locPort,</span><br><span style="color: hsl(120, 100%, 40%);">+        in ProtoTuple proto,</span><br><span style="color: hsl(120, 100%, 40%);">+  in OptionList options := {}</span><br><span style="color: hsl(120, 100%, 40%);">+) return Result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+external function f_IPL4_connect(</span><br><span style="color: hsl(120, 100%, 40%);">+    inout STATSD_CODEC_PT portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+        in HostName remName,</span><br><span style="color: hsl(120, 100%, 40%);">+  in PortNumber remPort,</span><br><span style="color: hsl(120, 100%, 40%);">+        in HostName locName,</span><br><span style="color: hsl(120, 100%, 40%);">+  in PortNumber locPort,</span><br><span style="color: hsl(120, 100%, 40%);">+        in ConnectionId connId,</span><br><span style="color: hsl(120, 100%, 40%);">+       in ProtoTuple proto,</span><br><span style="color: hsl(120, 100%, 40%);">+  in OptionList options := {}</span><br><span style="color: hsl(120, 100%, 40%);">+) return Result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+external function f_IPL4_close(</span><br><span style="color: hsl(120, 100%, 40%);">+      inout STATSD_CODEC_PT portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+        in ConnectionId id,</span><br><span style="color: hsl(120, 100%, 40%);">+   in ProtoTuple proto := { unspecified := {} }</span><br><span style="color: hsl(120, 100%, 40%);">+) return Result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+external function f_IPL4_setUserData(</span><br><span style="color: hsl(120, 100%, 40%);">+       inout STATSD_CODEC_PT portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+        in ConnectionId id,</span><br><span style="color: hsl(120, 100%, 40%);">+   in UserData userData</span><br><span style="color: hsl(120, 100%, 40%);">+) return Result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+external function f_IPL4_getUserData(</span><br><span style="color: hsl(120, 100%, 40%);">+       inout STATSD_CODEC_PT portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+        in ConnectionId id,</span><br><span style="color: hsl(120, 100%, 40%);">+   out UserData userData</span><br><span style="color: hsl(120, 100%, 40%);">+) return Result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/library/StatsD_CodecPort_CtrlFunctdef.cc b/library/StatsD_CodecPort_CtrlFunctdef.cc</span><br><span>new file mode 100644</span><br><span>index 0000000..1b78a7e</span><br><span>--- /dev/null</span><br><span>+++ b/library/StatsD_CodecPort_CtrlFunctdef.cc</span><br><span>@@ -0,0 +1,55 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#include "IPL4asp_PortType.hh"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "IPL4asp_PT.hh"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "StatsD_CodecPort.hh"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+namespace StatsD__CodecPort__CtrlFunct {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     IPL4asp__Types::Result f__IPL4__listen(</span><br><span style="color: hsl(120, 100%, 40%);">+               StatsD__CodecPort::STATSD__CODEC__PT& portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+            const IPL4asp__Types::HostName& locName,</span><br><span style="color: hsl(120, 100%, 40%);">+          const IPL4asp__Types::PortNumber& locPort,</span><br><span style="color: hsl(120, 100%, 40%);">+                const IPL4asp__Types::ProtoTuple& proto,</span><br><span style="color: hsl(120, 100%, 40%);">+          const IPL4asp__Types::OptionList& options)</span><br><span style="color: hsl(120, 100%, 40%);">+        {</span><br><span style="color: hsl(120, 100%, 40%);">+             return f__IPL4__PROVIDER__listen(portRef, locName, locPort, proto, options);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   IPL4asp__Types::Result f__IPL4__connect(</span><br><span style="color: hsl(120, 100%, 40%);">+                      StatsD__CodecPort::STATSD__CODEC__PT& portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+                    const IPL4asp__Types::HostName& remName,</span><br><span style="color: hsl(120, 100%, 40%);">+                  const IPL4asp__Types::PortNumber& remPort,</span><br><span style="color: hsl(120, 100%, 40%);">+                        const IPL4asp__Types::HostName& locName,</span><br><span style="color: hsl(120, 100%, 40%);">+                  const IPL4asp__Types::PortNumber& locPort,</span><br><span style="color: hsl(120, 100%, 40%);">+                        const IPL4asp__Types::ConnectionId& connId,</span><br><span style="color: hsl(120, 100%, 40%);">+                       const IPL4asp__Types::ProtoTuple& proto,</span><br><span style="color: hsl(120, 100%, 40%);">+                  const IPL4asp__Types::OptionList& options)</span><br><span style="color: hsl(120, 100%, 40%);">+        {</span><br><span style="color: hsl(120, 100%, 40%);">+             return f__IPL4__PROVIDER__connect(portRef, remName, remPort,</span><br><span style="color: hsl(120, 100%, 40%);">+                          locName, locPort, connId, proto, options);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   IPL4asp__Types::Result f__IPL4__close(</span><br><span style="color: hsl(120, 100%, 40%);">+                        StatsD__CodecPort::STATSD__CODEC__PT& portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+                    const IPL4asp__Types::ConnectionId& connId,</span><br><span style="color: hsl(120, 100%, 40%);">+                       const IPL4asp__Types::ProtoTuple& proto)</span><br><span style="color: hsl(120, 100%, 40%);">+  {</span><br><span style="color: hsl(120, 100%, 40%);">+             return f__IPL4__PROVIDER__close(portRef, connId, proto);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   IPL4asp__Types::Result f__IPL4__setUserData(</span><br><span style="color: hsl(120, 100%, 40%);">+                  StatsD__CodecPort::STATSD__CODEC__PT& portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+                    const IPL4asp__Types::ConnectionId& connId,</span><br><span style="color: hsl(120, 100%, 40%);">+                       const IPL4asp__Types::UserData& userData)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+             return f__IPL4__PROVIDER__setUserData(portRef, connId, userData);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   IPL4asp__Types::Result f__IPL4__getUserData(</span><br><span style="color: hsl(120, 100%, 40%);">+                  StatsD__CodecPort::STATSD__CODEC__PT& portRef,</span><br><span style="color: hsl(120, 100%, 40%);">+                    const IPL4asp__Types::ConnectionId& connId,</span><br><span style="color: hsl(120, 100%, 40%);">+                       IPL4asp__Types::UserData& userData)</span><br><span style="color: hsl(120, 100%, 40%);">+       {</span><br><span style="color: hsl(120, 100%, 40%);">+             return f__IPL4__PROVIDER__getUserData(portRef, connId, userData);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/library/StatsD_Types.ttcn b/library/StatsD_Types.ttcn</span><br><span>new file mode 100644</span><br><span>index 0000000..2142b8c</span><br><span>--- /dev/null</span><br><span>+++ b/library/StatsD_Types.ttcn</span><br><span>@@ -0,0 +1,69 @@</span><br><span style="color: hsl(120, 100%, 40%);">+module StatsD_Types {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Definition of abstract types for the StatsD protocol. USes the TITAN "TEXT"</span><br><span style="color: hsl(120, 100%, 40%);">+ * codec to auto-generate encoder/decoder functions</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * All rights reserved.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Daniel Willmann <dwillmann@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Released under the terms of GNU General Public License, Version 2 or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0-or-later</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type charstring MetricName with {</span><br><span style="color: hsl(120, 100%, 40%);">+      variant "END(':')";</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type integer MetricValue with {</span><br><span style="color: hsl(120, 100%, 40%);">+  variant "END('|', '\|#(1)')";</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type charstring MetricType (pattern "(g|c|ms|h|m)");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type charstring MetricSampleRate (pattern "\d.\d+") with {</span><br><span style="color: hsl(120, 100%, 40%);">+ variant "BEGIN('|@')"</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type record StatsDMetric {</span><br><span style="color: hsl(120, 100%, 40%);">+     MetricName      name,</span><br><span style="color: hsl(120, 100%, 40%);">+ MetricValue     val,</span><br><span style="color: hsl(120, 100%, 40%);">+  MetricType      mtype,</span><br><span style="color: hsl(120, 100%, 40%);">+        MetricSampleRate srate optional</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+type record of StatsDMetric StatsDMessage with {</span><br><span style="color: hsl(120, 100%, 40%);">+       variant "SEPARATOR('\n')";</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+external function enc_StatsDMessage(in StatsDMessage id) return charstring</span><br><span style="color: hsl(120, 100%, 40%);">+with { extension "prototype(convert) encode(TEXT)"};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+external function dec_StatsDMessage(in charstring id) return StatsDMessage</span><br><span style="color: hsl(120, 100%, 40%);">+with { extension "prototype(convert) decode(TEXT)"};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+template StatsDMessage tr_StatsDMsg1(template StatsDMetric metric) := {</span><br><span style="color: hsl(120, 100%, 40%);">+       [0] := metric</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+template StatsDMetric tr_StatsDMetric(template MetricName name, template MetricValue val := ?, template MetricType mtype) := {</span><br><span style="color: hsl(120, 100%, 40%);">+    name := name,</span><br><span style="color: hsl(120, 100%, 40%);">+ val := val,</span><br><span style="color: hsl(120, 100%, 40%);">+   mtype := mtype</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+template StatsDMetric tr_StatsDMetricCounter(template MetricName name, template MetricValue val := ?) := {</span><br><span style="color: hsl(120, 100%, 40%);">+       name := name,</span><br><span style="color: hsl(120, 100%, 40%);">+ val := val,</span><br><span style="color: hsl(120, 100%, 40%);">+   mtype := "c"</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+template StatsDMetric tr_StatsDMetricGauge(template MetricName name, template MetricValue val := ?) := {</span><br><span style="color: hsl(120, 100%, 40%);">+ name := name,</span><br><span style="color: hsl(120, 100%, 40%);">+ val := val,</span><br><span style="color: hsl(120, 100%, 40%);">+   mtype := "g"</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+} with { encode "TEXT" }</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/19581">change 19581</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/19581"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-ttcn3-hacks </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Icd1317b5f192d98e6cdc6635788d450501992bf1 </div>
<div style="display:none"> Gerrit-Change-Number: 19581 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>