diff --git a/utils/prometheus-node-exporter-lua/Makefile b/utils/prometheus-node-exporter-lua/Makefile index 31583a6cf8df13..bbfa13ca8a744b 100644 --- a/utils/prometheus-node-exporter-lua/Makefile +++ b/utils/prometheus-node-exporter-lua/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=prometheus-node-exporter-lua -PKG_VERSION:=2025.11.22 +PKG_VERSION:=2026.04.07 PKG_RELEASE:=1 PKG_MAINTAINER:=Etienne CHAMPETIER @@ -279,6 +279,22 @@ define Package/prometheus-node-exporter-lua-nft-counters/install $(INSTALL_DATA) ./files/usr/lib/lua/prometheus-collectors/nft_counters.lua $(1)/usr/lib/lua/prometheus-collectors/ endef +define Package/prometheus-node-exporter-lua-unbound + $(call Package/prometheus-node-exporter-lua/Default) + TITLE+= (unbound stats collector) + DEPENDS:=prometheus-node-exporter-lua +unbound-control +endef + +define Package/prometheus-node-exporter-lua-unbound/description + Adds a Unbound stats collector to the Prometheus node exporter. + Refer to https://github.com/marinierb/prometheus-node-exporter-lua-unbound for configuration details. +endef + +define Package/prometheus-node-exporter-lua-unbound/install + $(INSTALL_DIR) $(1)/usr/lib/lua/prometheus-collectors + $(INSTALL_DATA) ./files/usr/lib/lua/prometheus-collectors/unbound.lua $(1)/usr/lib/lua/prometheus-collectors/ +endef + $(eval $(call BuildPackage,prometheus-node-exporter-lua)) $(eval $(call BuildPackage,prometheus-node-exporter-lua-bmx7)) $(eval $(call BuildPackage,prometheus-node-exporter-lua-dawn)) @@ -300,3 +316,4 @@ $(eval $(call BuildPackage,prometheus-node-exporter-lua-realtek-poe)) $(eval $(call BuildPackage,prometheus-node-exporter-lua-mwan3)) $(eval $(call BuildPackage,prometheus-node-exporter-lua-ethtool)) $(eval $(call BuildPackage,prometheus-node-exporter-lua-nft-counters)) +$(eval $(call BuildPackage,prometheus-node-exporter-lua-unbound)) diff --git a/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/unbound.lua b/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/unbound.lua new file mode 100644 index 00000000000000..2cf3b1c5adcf1b --- /dev/null +++ b/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/unbound.lua @@ -0,0 +1,66 @@ +-- Unbound stats exporter + +local socket = require("socket") +local unix = require("socket.unix") + +local function scrape() + local metrics = { + ["total.num.queries" ] = metric("unbound_num_queries_total", "counter"), + ["total.num.queries_ip_ratelimited" ] = metric("unbound_num_queries_ip_ratelimited_total", "counter"), + ["total.num.queries_cookie_valid" ] = metric("unbound_num_queries_cookie_valid_total", "counter"), + ["total.num.queries_cookie_client" ] = metric("unbound_num_queries_cookie_client_total", "counter"), + ["total.num.queries_cookie_invalid" ] = metric("unbound_num_queries_cookie_invalid_total", "counter"), + ["total.num.queries_discard_timeout"] = metric("unbound_num_queries_discard_timeout_total","counter"), + ["total.num.queries_wait_limit" ] = metric("unbound_num_queries_wait_limit_total", "counter"), + ["total.num.cachehits" ] = metric("unbound_cachehits_total", "counter"), + ["total.num.cachemiss" ] = metric("unbound_cachemiss_total", "counter"), + ["total.num.prefetch" ] = metric("unbound_prefetch_total", "counter"), + ["total.num.queries_timed_out" ] = metric("unbound_num_queries_timed_out_total", "counter"), + ["total.query.queue_time_us.max" ] = metric("unbound_query_queue_time_us_max", "gauge"), + ["total.num.expired" ] = metric("unbound_num_expired_total", "counter"), + ["total.num.recursivereplies" ] = metric("unbound_num_recursivereplies_total", "counter"), + ["total.num.dns_error_reports" ] = metric("unbound_num_dns_error_reports_total", "counter"), + ["total.requestlist.avg" ] = metric("unbound_requestlist_avg", "gauge"), + ["total.requestlist.max" ] = metric("unbound_requestlist_max", "gauge"), + ["total.requestlist.overwritten" ] = metric("unbound_requestlist_overwritten_total", "counter"), + ["total.requestlist.exceeded" ] = metric("unbound_requestlist_exceeded_total", "counter"), + ["total.requestlist.current.all" ] = metric("unbound_requestlist_current_all", "gauge"), + ["total.requestlist.current.user" ] = metric("unbound_requestlist_current_user", "gauge"), + ["total.recursion.time.avg" ] = metric("unbound_recursion_time_avg", "gauge"), + ["total.recursion.time.median" ] = metric("unbound_recursion_time_median", "gauge"), + } + + local sock = unix() + local ok, err = sock:connect("/run/unbound.ctl") + if not ok then + return nil, "failed to connect to unbound socket: " .. (err or "unknown") + end + + sock:settimeout(1) + sock:send("UBCT1 stats_noreset\n") + + local chunks = {} + while true do + local chunk, err, partial = sock:receive(4096) + if partial and partial ~= "" then + chunks[#chunks + 1] = partial + end + if not chunk then break end + chunks[#chunks + 1] = chunk + end + sock:close() + + local out = table.concat(chunks) + + for line in out:gmatch("[^\r\n]+") do + local key, val = line:match("^([%w%._]+)=(%-?[%d%.]+)$") + if key and val then + local n = tonumber(val) + if metrics[key] then + metrics[key]({}, n) + end + end + end +end + +return { scrape = scrape }