]> git.ipfire.org Git - collecty.git/commitdiff
sources: Collect metrics for Knot Resolver
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 29 May 2026 11:11:13 +0000 (11:11 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 29 May 2026 11:11:13 +0000 (11:11 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
configure.ac
src/daemon/sources.c
src/daemon/sources/knot-resolver.c [new file with mode: 0644]
src/daemon/sources/knot-resolver.h [new file with mode: 0644]

index fccdcb16178c7c611b896eae4ec5a9e4d714d2ba..851a354fc27796bb9cef313bf08c4bdc96b47b6b 100644 (file)
@@ -206,6 +206,8 @@ dist_telemetryd_SOURCES = \
        src/daemon/sources/ipfrag4.h \
        src/daemon/sources/iptables.c \
        src/daemon/sources/iptables.h \
+       src/daemon/sources/knot-resolver.c \
+       src/daemon/sources/knot-resolver.h \
        src/daemon/sources/legacy-gateway-latency4.c \
        src/daemon/sources/legacy-gateway-latency4.h \
        src/daemon/sources/loadavg.c \
index 863c9c56559d8dbabdd4220678a1d2ab57c3326c..6f17f99ffa5db56d94051c9018a1dd6be01d78e7 100644 (file)
@@ -227,6 +227,7 @@ AC_SOURCE([hostapd])
 AC_SOURCE([interface], [$have_libnl3 $have_libnl3_route])
 AC_SOURCE([ipfrag4])
 AC_SOURCE([iptables], [$have_libiptc])
+AC_SOURCE([knot-resolver])
 AC_SOURCE([legacy-gateway-latency4], [$have_libnl3 $have_libnl3_route])
 AC_SOURCE([loadavg])
 AC_SOURCE([memory])
index 0dfb0fab77f5835f119bbc21308f639625670a03..63348773f1e28ef3639e0b8cbcd495322aaad86e 100644 (file)
@@ -38,6 +38,7 @@
 #include "sources/interface.h"
 #include "sources/ipfrag4.h"
 #include "sources/iptables.h"
+#include "sources/knot-resolver.h"
 #include "sources/legacy-gateway-latency4.h"
 #include "sources/loadavg.h"
 #include "sources/memory.h"
@@ -99,6 +100,10 @@ static const td_source_impl* source_impls[] = {
        &iptables_source,
 #endif /* BUILD_SOURCE_IPTABLES */
 
+#ifdef BUILD_SOURCE_KNOT_RESOLVER
+       &knot_resolver_source,
+#endif /* BUILD_SOURCE_KNOT_RESOLVER */
+
 #ifdef BUILD_SOURCE_LEGACY_GATEWAY_LATENCY4
        &legacy_gateway_latency4_source,
 #endif /* BUILD_SOURCE_LEGACY_GATEWAY_LATENCY4 */
diff --git a/src/daemon/sources/knot-resolver.c b/src/daemon/sources/knot-resolver.c
new file mode 100644 (file)
index 0000000..933968d
--- /dev/null
@@ -0,0 +1,276 @@
+/*#############################################################################
+#                                                                             #
+# telemetryd - The IPFire Telemetry Collection Service                        #
+# Copyright (C) 2025 IPFire Development Team                                  #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#ifdef BUILD_SOURCE_KNOT_RESOLVER
+
+#include <errno.h>
+#include <limits.h>
+
+#include "../command.h"
+#include "../ctx.h"
+#include "../source.h"
+#include "../string.h"
+#include "knot-resolver.h"
+
+typedef struct knot_resolver_metric {
+       sd_json_variant* json;
+       const char* key;
+       const char* field;
+} knot_resolver_metric;
+
+static int knot_resolver_parse_worker(td_ctx* ctx,
+               td_metrics* metrics, sd_json_variant* json) {
+       sd_json_variant* request = NULL;
+       sd_json_variant* answer = NULL;
+       sd_json_variant* query = NULL;
+       int r = 0;
+
+       // Fetch the request object
+       request = sd_json_variant_by_key(json, "request");
+       if (!request)
+               return -EBADMSG;
+
+       // Fetch the answer object
+       answer = sd_json_variant_by_key(json, "answer");
+       if (!answer)
+               return -EBADMSG;
+
+       // Fetch the query object
+       query = sd_json_variant_by_key(json, "query");
+       if (!query)
+               return -EBADMSG;
+
+       const knot_resolver_metric keys[] = {
+               // Query
+               { query, "dnssec",     "query_dnssec", },
+               { query, "edns",       "query_edns", },
+
+               // Request
+               { request, "total",    "request_total", },
+               { request, "total6",   "request_total6", },
+               { request, "total4",   "request_total4", },
+               { request, "internal", "request_internal", },
+               { request, "udp",      "request_udp", },
+               { request, "udp6",     "request_udp6", },
+               { request, "udp4",     "request_udp4", },
+               { request, "tcp",      "request_tcp", },
+               { request, "tcp6",     "request_tcp6", },
+               { request, "tcp4",     "request_tcp4", },
+               { request, "dot",      "request_dot", },
+               { request, "dot6",     "request_dot6", },
+               { request, "dot4",     "request_dot4", },
+               { request, "doh",      "request_doh", },
+               { request, "doh6",     "request_doh6", },
+               { request, "doh4",     "request_doh4", },
+               { request, "doq",      "request_doq", },
+               { request, "doq6",     "request_doq6", },
+               { request, "doq4",     "request_doq4", },
+               { request, "xdp",      "request_xdp", },
+               { request, "xdp6",     "request_xdp6", },
+               { request, "xdp4",     "request_xdp4", },
+
+               // Answer
+               { answer, "total",     "answer_total", },
+               { answer, "noerror",   "answer_noerror", },
+               { answer, "nxdomain",  "answer_nxdomain", },
+               { answer, "servfail",  "answer_servfail", },
+               { answer, "nodata",    "answer_nodata", },
+               { answer, "cached",    "answer_cached", },
+               { answer, "stale",     "answer_stale", },
+
+               // Answer Flags
+               { answer, "aa",        "answer_aa", },
+               { answer, "ad",        "answer_ad", },
+               { answer, "cd",        "answer_cd", },
+               { answer, "do",        "answer_do", },
+               { answer, "ra",        "answer_ra", },
+               { answer, "rd",        "answer_rd", },
+               { answer, "tc",        "answer_tc", },
+               { answer, "edns0",     "answer_edns0", },
+
+               // Answer Response Time
+               { answer, "1ms",       "answer_1ms", },
+               { answer, "10ms",      "answer_10ms", },
+               { answer, "50ms",      "answer_50ms", },
+               { answer, "100ms",     "answer_100ms", },
+               { answer, "250ms",     "answer_250ms", },
+               { answer, "500ms",     "answer_500ms", },
+               { answer, "1000ms",    "answer_1000ms", },
+               { answer, "1500ms",    "answer_1500ms", },
+               { answer, "slow",      "answer_slow", },
+               { answer, "sum_ms",    "answer_sum_ms", },
+
+               { NULL },
+       };
+
+       // Extract all metrics from JSON and push them into our metrics object
+       for (const knot_resolver_metric* key = keys; key->field; key++) {
+               r = td_metrics_push_uint64_from_json(metrics,
+                               key->field, key->json, key->key);
+               if (r < 0)
+                       return r;
+       }
+
+       return 0;
+}
+
+static int knot_resolver_parse_metrics(td_ctx* ctx,
+               td_metrics* metrics, sd_json_variant* json) {
+       sd_json_variant* worker = NULL;
+       char name[NAME_MAX];
+       int r;
+
+       int i = 0;
+
+       for (;;) {
+               // Format the field name
+               r = td_string_format(name, "kresd:kresd%d", i++);
+               if (r < 0)
+                       return r;
+
+               // Fetch the worker object
+               worker = sd_json_variant_by_key(json, name);
+               if (!worker)
+                       break;
+
+               // Parse the worker
+               r = knot_resolver_parse_worker(ctx, metrics, worker);
+               if (r < 0)
+                       return r;
+       }
+
+       return 0;
+}
+
+static int knot_resolver_on_success(td_ctx* ctx,
+               int rc, td_file* stdout, void* data) {
+       sd_json_variant* json = NULL;
+       td_metrics* metrics = NULL;
+       td_source* source = data;
+       int r;
+
+       // Parse the output as JSON
+       r = td_file_parse_json(stdout, &json, 0);
+       if (r < 0)
+               goto ERROR;
+
+       // Create a new metrics object
+       r = td_source_create_metrics(source, &metrics, NULL);
+       if (r < 0)
+               goto ERROR;
+
+       // Parse the message
+       r = knot_resolver_parse_metrics(ctx, metrics, json);
+       if (r < 0)
+               goto ERROR;
+
+       // Submit all collected metrics
+       r = td_source_submit_metrics(source, metrics);
+
+ERROR:
+       if (json)
+               sd_json_variant_unref(json);
+       if (metrics)
+               td_metrics_unref(metrics);
+
+       return r;
+}
+
+static int knot_resolver_heartbeat(td_ctx* ctx, td_source* source) {
+       // Run kresctl to fetch metrics
+       const char* argv[] = {
+               "kresctl", "metrics", NULL,
+       };
+
+       return td_source_run_command(source, NULL, argv, knot_resolver_on_success, source);
+}
+
+const td_source_impl knot_resolver_source = {
+       .name = "knot-resolver",
+
+       // RRD Data Sources
+       .rrd_dss = {
+               // Query
+               { "query_dnssec",     "DERIVE", 0, -1, },
+               { "query_edns",       "DERIVE", 0, -1, },
+
+               // Request
+               { "request_total",    "DERIVE", 0, -1, },
+               { "request_total6",   "DERIVE", 0, -1, },
+               { "request_total4",   "DERIVE", 0, -1, },
+               { "request_internal", "DERIVE", 0, -1, },
+               { "request_udp",      "DERIVE", 0, -1, },
+               { "request_udp6",     "DERIVE", 0, -1, },
+               { "request_udp4",     "DERIVE", 0, -1, },
+               { "request_tcp",      "DERIVE", 0, -1, },
+               { "request_tcp6",     "DERIVE", 0, -1, },
+               { "request_tcp4",     "DERIVE", 0, -1, },
+               { "request_dot",      "DERIVE", 0, -1, },
+               { "request_dot6",     "DERIVE", 0, -1, },
+               { "request_dot4",     "DERIVE", 0, -1, },
+               { "request_doh",      "DERIVE", 0, -1, },
+               { "request_doh6",     "DERIVE", 0, -1, },
+               { "request_doh4",     "DERIVE", 0, -1, },
+               { "request_doq",      "DERIVE", 0, -1, },
+               { "request_doq6",     "DERIVE", 0, -1, },
+               { "request_doq4",     "DERIVE", 0, -1, },
+               { "request_xdp",      "DERIVE", 0, -1, },
+               { "request_xdp6",     "DERIVE", 0, -1, },
+               { "request_xdp4",     "DERIVE", 0, -1, },
+
+               // Answer
+               { "answer_total",     "DERIVE", 0, -1, },
+               { "answer_noerror",   "DERIVE", 0, -1, },
+               { "answer_nxdomain",  "DERIVE", 0, -1, },
+               { "answer_servfail",  "DERIVE", 0, -1, },
+               { "answer_nodata",    "DERIVE", 0, -1, },
+               { "answer_cached",    "DERIVE", 0, -1, },
+               { "answer_stale",     "DERIVE", 0, -1, },
+
+               // Answer Flags
+               { "answer_aa",        "DERIVE", 0, -1, },
+               { "answer_ad",        "DERIVE", 0, -1, },
+               { "answer_cd",        "DERIVE", 0, -1, },
+               { "answer_do",        "DERIVE", 0, -1, },
+               { "answer_ra",        "DERIVE", 0, -1, },
+               { "answer_rd",        "DERIVE", 0, -1, },
+               { "answer_tc",        "DERIVE", 0, -1, },
+               { "answer_edns0",     "DERIVE", 0, -1, },
+
+               // Answer Response Time
+               { "answer_1ms",       "DERIVE", 0, -1, },
+               { "answer_10ms",      "DERIVE", 0, -1, },
+               { "answer_50ms",      "DERIVE", 0, -1, },
+               { "answer_100ms",     "DERIVE", 0, -1, },
+               { "answer_250ms",     "DERIVE", 0, -1, },
+               { "answer_500ms",     "DERIVE", 0, -1, },
+               { "answer_1000ms",    "DERIVE", 0, -1, },
+               { "answer_1500ms",    "DERIVE", 0, -1, },
+               { "answer_slow",      "DERIVE", 0, -1, },
+               { "answer_sum_ms",    "DERIVE", 0, -1, },
+
+               { NULL },
+       },
+
+       // Methods
+       .heartbeat = knot_resolver_heartbeat,
+};
+
+#endif /* BUILD_SOURCE_KNOT_RESOLVER */
diff --git a/src/daemon/sources/knot-resolver.h b/src/daemon/sources/knot-resolver.h
new file mode 100644 (file)
index 0000000..4806774
--- /dev/null
@@ -0,0 +1,30 @@
+/*#############################################################################
+#                                                                             #
+# telemetryd - The IPFire Telemetry Collection Service                        #
+# Copyright (C) 2025 IPFire Development Team                                  #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#ifndef TELEMETRY_SOURCE_KNOT_RESOLVER_H
+#define TELEMETRY_SOURCE_KNOT_RESOLVER_H
+#ifdef BUILD_SOURCE_KNOT_RESOLVER
+
+#include "../source.h"
+
+extern const td_source_impl knot_resolver_source;
+
+#endif /* BUILD_SOURCE_KNOT_RESOLVER */
+#endif /* TELEMETRY_SOURCE_KNOT_RESOLVER_H */