Change in osmo-ttcn3-hacks[master]: Add parsing and checking of StatsD metrics

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

daniel gerrit-no-reply at lists.osmocom.org
Wed Aug 12 16:54:50 UTC 2020


daniel has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/19581 )


Change subject: Add parsing and checking of StatsD metrics
......................................................................

Add parsing and checking of StatsD metrics

Change-Id: Icd1317b5f192d98e6cdc6635788d450501992bf1
Related: SYS#4877
---
A library/StatsD_Checker.ttcn
A library/StatsD_CodecPort.ttcn
A library/StatsD_CodecPort_CtrlFunct.ttcn
A library/StatsD_CodecPort_CtrlFunctdef.cc
A library/StatsD_Types.ttcn
5 files changed, 488 insertions(+), 0 deletions(-)



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

diff --git a/library/StatsD_Checker.ttcn b/library/StatsD_Checker.ttcn
new file mode 100644
index 0000000..9328d21
--- /dev/null
+++ b/library/StatsD_Checker.ttcn
@@ -0,0 +1,264 @@
+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 <info at sysmocom.de>
+ * All rights reserved.
+ *
+ * Author: Daniel Willmann <dwillmann at sysmocom.de>
+ *
+ * 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 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 := false;
+}
+
+type record StatsDExpect {
+	MetricName	name,
+	MetricType	mtype,
+	MetricValue	min,
+	MetricValue	max
+};
+
+type record of StatsDExpect StatsDExpects;
+
+type record StatsDExpectPriv {
+	StatsDExpect expect,
+	integer seen
+}
+
+type record 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;
+
+	while (not mp_enable_stats) {
+		log("StatsD checker disabled by modulepar");
+		f_sleep(3600.0);
+	}
+
+    map(self:STATS, system:STATS);
+    StatsD_CodecPort_CtrlFunct.f_IPL4_listen(STATS, statsd_host, statsd_port, { udp := {} }, {});
+
+	/* 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, exp.expect);
+			result := {
+				kind := e_Mismatched,
+				idx := i
+			};
+			break;
+		} else {
+			log("EXP match: ", metric, 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;
+}
+
+/* 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_CodecPort.ttcn b/library/StatsD_CodecPort.ttcn
new file mode 100644
index 0000000..e7396b5
--- /dev/null
+++ b/library/StatsD_CodecPort.ttcn
@@ -0,0 +1,57 @@
+module StatsD_CodecPort {
+
+import from StatsD_Types all;
+import from IPL4asp_PortType all;
+import from IPL4asp_Types all;
+
+type record StatsD_RecvFrom {
+	ConnectionId	connId,
+	HostName		remName,
+	PortNumber		remPort,
+	HostName		locName,
+	PortNumber		locPort,
+	StatsDMessage	msg
+}
+
+template StatsD_RecvFrom tr_StatsD_RecvFrom(template ConnectionId cid, template StatsDMessage msg) := {
+	connId := cid,
+	remName := ?,
+	remPort := ?,
+	locName := ?,
+	locPort := ?,
+	msg := msg
+}
+
+type record StatsD_Send {
+	ConnectionId	connId,
+	StatsDMessage	msg
+}
+
+private function IPL4_to_StatsD_RecvFrom(in ASP_RecvFrom pin, out StatsD_RecvFrom pout) {
+	pout.connId := pin.connId;
+	pout.remName := pin.remName;
+	pout.remPort := pin.remPort;
+	pout.locName := pin.locName;
+	pout.locPort := pin.locPort;
+	pout.msg := dec_StatsDMessage(oct2char(pin.msg));
+} with { extension "prototype(fast)" };
+
+private function StatsD_to_IPL4_Send(in StatsD_Send pin, out ASP_Send pout) {
+	pout.connId := pin.connId;
+	pout.proto := { udp := {} };
+	pout.msg := char2oct(enc_StatsDMessage(pin.msg));
+} with { extension "prototype(fast)" };
+
+type port STATSD_CODEC_PT message {
+	out	StatsD_Send;
+	in	StatsD_RecvFrom,
+		ASP_ConnId_ReadyToRelease,
+		ASP_Event;
+} with { extension "user IPL4asp_PT
+	out(StatsD_Send -> ASP_Send: function(StatsD_to_IPL4_Send))
+	in(ASP_RecvFrom -> StatsD_RecvFrom: function(IPL4_to_StatsD_RecvFrom);
+	   ASP_ConnId_ReadyToRelease -> ASP_ConnId_ReadyToRelease: simple;
+	   ASP_Event -> ASP_Event: simple)"
+}
+
+}
diff --git a/library/StatsD_CodecPort_CtrlFunct.ttcn b/library/StatsD_CodecPort_CtrlFunct.ttcn
new file mode 100644
index 0000000..b2927c3
--- /dev/null
+++ b/library/StatsD_CodecPort_CtrlFunct.ttcn
@@ -0,0 +1,43 @@
+module StatsD_CodecPort_CtrlFunct {
+
+import from StatsD_CodecPort all;
+import from IPL4asp_Types all;
+
+external function f_IPL4_listen(
+	inout STATSD_CODEC_PT portRef,
+	in HostName locName,
+	in PortNumber locPort,
+	in ProtoTuple proto,
+	in OptionList options := {}
+) return Result;
+
+external function f_IPL4_connect(
+	inout STATSD_CODEC_PT portRef,
+	in HostName remName,
+	in PortNumber remPort,
+	in HostName locName,
+	in PortNumber locPort,
+	in ConnectionId connId,
+	in ProtoTuple proto,
+	in OptionList options := {}
+) return Result;
+
+external function f_IPL4_close(
+	inout STATSD_CODEC_PT portRef,
+	in ConnectionId id,
+	in ProtoTuple proto := { unspecified := {} }
+) return Result;
+
+external function f_IPL4_setUserData(
+	inout STATSD_CODEC_PT portRef,
+	in ConnectionId id,
+	in UserData userData
+) return Result;
+
+external function f_IPL4_getUserData(
+	inout STATSD_CODEC_PT portRef,
+	in ConnectionId id,
+	out UserData userData
+) return Result;
+
+}
diff --git a/library/StatsD_CodecPort_CtrlFunctdef.cc b/library/StatsD_CodecPort_CtrlFunctdef.cc
new file mode 100644
index 0000000..1b78a7e
--- /dev/null
+++ b/library/StatsD_CodecPort_CtrlFunctdef.cc
@@ -0,0 +1,55 @@
+#include "IPL4asp_PortType.hh"
+#include "IPL4asp_PT.hh"
+#include "StatsD_CodecPort.hh"
+
+namespace StatsD__CodecPort__CtrlFunct {
+
+	IPL4asp__Types::Result f__IPL4__listen(
+		StatsD__CodecPort::STATSD__CODEC__PT& portRef,
+		const IPL4asp__Types::HostName& locName,
+		const IPL4asp__Types::PortNumber& locPort,
+		const IPL4asp__Types::ProtoTuple& proto,
+		const IPL4asp__Types::OptionList& options)
+	{
+		return f__IPL4__PROVIDER__listen(portRef, locName, locPort, proto, options);
+	}
+
+	IPL4asp__Types::Result f__IPL4__connect(
+			StatsD__CodecPort::STATSD__CODEC__PT& portRef,
+			const IPL4asp__Types::HostName& remName,
+			const IPL4asp__Types::PortNumber& remPort,
+			const IPL4asp__Types::HostName& locName,
+			const IPL4asp__Types::PortNumber& locPort,
+			const IPL4asp__Types::ConnectionId& connId,
+			const IPL4asp__Types::ProtoTuple& proto,
+			const IPL4asp__Types::OptionList& options)
+	{
+		return f__IPL4__PROVIDER__connect(portRef, remName, remPort,
+				locName, locPort, connId, proto, options);
+	}
+
+	IPL4asp__Types::Result f__IPL4__close(
+			StatsD__CodecPort::STATSD__CODEC__PT& portRef,
+			const IPL4asp__Types::ConnectionId& connId,
+			const IPL4asp__Types::ProtoTuple& proto)
+	{
+		return f__IPL4__PROVIDER__close(portRef, connId, proto);
+	}
+
+	IPL4asp__Types::Result f__IPL4__setUserData(
+			StatsD__CodecPort::STATSD__CODEC__PT& portRef,
+			const IPL4asp__Types::ConnectionId& connId,
+			const IPL4asp__Types::UserData& userData)
+	{
+		return f__IPL4__PROVIDER__setUserData(portRef, connId, userData);
+	}
+
+	IPL4asp__Types::Result f__IPL4__getUserData(
+			StatsD__CodecPort::STATSD__CODEC__PT& portRef,
+			const IPL4asp__Types::ConnectionId& connId,
+			IPL4asp__Types::UserData& userData)
+	{
+		return f__IPL4__PROVIDER__getUserData(portRef, connId, userData);
+	}
+
+}
diff --git a/library/StatsD_Types.ttcn b/library/StatsD_Types.ttcn
new file mode 100644
index 0000000..71023c4
--- /dev/null
+++ b/library/StatsD_Types.ttcn
@@ -0,0 +1,69 @@
+module StatsD_Types {
+
+/* Definition of abstract types for the StatsD protocol. USes the TITAN "TEXT"
+ * codec to auto-generate encoder/decoder functions
+ *
+ * (C) 2020 by sysmocom s.f.m.c. GmbH <info at sysmocom.de>
+ * All rights reserved.
+ *
+ * Author: Daniel Willmann <dwillmann at sysmocom.de>
+ *
+ * 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
+ */
+
+type charstring MetricName with {
+  variant "END(':')";
+};
+
+type integer MetricValue with {
+	variant "END('|', '\|#(1)')";
+};
+
+type charstring MetricType (pattern "(g|c|ms|h|m)");
+
+type charstring MetricSampleRate (pattern "\d.\d+") with {
+	variant "BEGIN('|@')"
+};
+
+type record StatsDMetric {
+	MetricName	name,
+	MetricValue	val,
+	MetricType	mtype,
+	MetricSampleRate srate optional
+};
+
+type record of StatsDMetric StatsDMessage with {
+	variant "SEPARATOR('\n')";
+};
+
+external function enc_StatsDMessage(in StatsDMessage id) return charstring
+with { extension "prototype(convert) encode(TEXT)"};
+
+external function dec_StatsDMessage(in charstring id) return StatsDMessage
+with { extension "prototype(convert) decode(TEXT)"};
+
+template StatsDMessage tr_StatsDMsg1(template StatsDMetric metric) := {
+	[0] := metric
+}
+
+template StatsDMetric tr_StatsDMetric(template MetricName name, template MetricValue val := ?, template MetricType mtype) := {
+	name := name,
+	val := val,
+	mtype := mtype
+}
+
+template StatsDMetric tr_StatsDMetricCounter(template MetricName name, template MetricValue val := ?) := {
+	name := name,
+	val := val,
+	mtype := "c"
+}
+
+template StatsDMetric tr_StatsDMetricGauge(template MetricName name, template MetricValue val := ?) := {
+	name := name,
+	val := val,
+	mtype := "g"
+}
+
+} with { encode "TEXT" }

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

Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: Icd1317b5f192d98e6cdc6635788d450501992bf1
Gerrit-Change-Number: 19581
Gerrit-PatchSet: 1
Gerrit-Owner: daniel <dwillmann at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200812/433a8c6a/attachment.htm>


More information about the gerrit-log mailing list