prometheus-node-exporter-ucode: add new package
This is a port of prometheus-node-exporter-lua to ucode. Signed-off-by: Andre Heider <a.heider@gmail.com>
This commit is contained in:
parent
c23789a576
commit
06c2ef2ce4
25 changed files with 910 additions and 0 deletions
74
utils/prometheus-node-exporter-ucode/Makefile
Normal file
74
utils/prometheus-node-exporter-ucode/Makefile
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Copyright (C) 2013-2017 OpenWrt.org
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=prometheus-node-exporter-ucode
|
||||||
|
PKG_VERSION:=2022.12.02
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
PKG_MAINTAINER:=Andre Heider <a.heider@gmail.com>
|
||||||
|
PKG_LICENSE:=Apache-2.0
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
Build/Compile=
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/Default
|
||||||
|
SECTION:=utils
|
||||||
|
CATEGORY:=Utilities
|
||||||
|
TITLE:=Prometheus node exporter
|
||||||
|
PKGARCH:=all
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)
|
||||||
|
$(call Package/$(PKG_NAME)/Default)
|
||||||
|
DEPENDS:=+uhttpd +uhttpd-mod-ucode +rpcd +ucode-mod-fs +ucode-mod-ubus
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/install
|
||||||
|
$(INSTALL_DIR) $(1)/etc/config
|
||||||
|
$(INSTALL_CONF) ./files/config $(1)/etc/config/$(PKG_NAME)
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
$(INSTALL_BIN) ./files/init $(1)/etc/init.d/$(PKG_NAME)
|
||||||
|
$(INSTALL_DIR) $(1)/usr/share/ucode/node-exporter/lib
|
||||||
|
$(INSTALL_DATA) ./files/metrics.uc $(1)/usr/share/ucode/node-exporter/
|
||||||
|
$(INSTALL_DATA) ./files/base/*.uc $(1)/usr/share/ucode/node-exporter/lib/
|
||||||
|
$(INSTALL_DIR) $(1)/usr/bin
|
||||||
|
$(INSTALL_BIN) ./files/run.sh $(1)/usr/bin/$(PKG_NAME)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/conffiles
|
||||||
|
/etc/config/$(PKG_NAME)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/description
|
||||||
|
Provides node metrics as Prometheus scraping endpoint.
|
||||||
|
|
||||||
|
This service is a lightweight rewrite in ucode of the offical Prometheus node_exporter.
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,prometheus-node-exporter-ucode))
|
||||||
|
|
||||||
|
define Collector
|
||||||
|
define Package/$(PKG_NAME)-$(1)
|
||||||
|
$$(call Package/$(PKG_NAME)/Default)
|
||||||
|
TITLE+= ($(2))
|
||||||
|
DEPENDS:=$(PKG_NAME) $(3)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)-$(1)/install
|
||||||
|
$$(INSTALL_DIR) $$(1)/usr/share/ucode/node-exporter/lib
|
||||||
|
$$(INSTALL_DATA) ./files/extra/$(1).uc $$(1)/usr/share/ucode/node-exporter/lib/
|
||||||
|
endef
|
||||||
|
|
||||||
|
$$(eval $$(call BuildPackage,$(PKG_NAME)-$(1)))
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call Collector,dnsmasq,Dnsmasq collector,@dnsmasq))
|
||||||
|
$(eval $(call Collector,ltq-dsl,Lantiq/Intel/MaxLinear DSL collector,@ltq-dsl-app))
|
||||||
|
$(eval $(call Collector,netstat,netstat collector,))
|
||||||
|
$(eval $(call Collector,openwrt,OpenWrt collector,))
|
||||||
|
$(eval $(call Collector,snmp6,snmp6 collector,))
|
||||||
|
$(eval $(call Collector,uci_dhcp_host,UCI DHCP host collector,))
|
||||||
|
$(eval $(call Collector,wifi,Wi-Fi collector,+ucode-mod-nl80211))
|
||||||
|
$(eval $(call Collector,wireguard,Wireguard collector,+rpcd-mod-wireguard))
|
|
@ -0,0 +1,4 @@
|
||||||
|
gauge("node_nf_conntrack_entries")
|
||||||
|
(null, oneline("/proc/sys/net/netfilter/nf_conntrack_count"));
|
||||||
|
gauge("node_nf_conntrack_entries_limit")
|
||||||
|
(null, oneline("/proc/sys/net/netfilter/nf_conntrack_max"));
|
44
utils/prometheus-node-exporter-ucode/files/base/cpu.uc
Normal file
44
utils/prometheus-node-exporter-ucode/files/base/cpu.uc
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
let f = fs.open("/proc/stat");
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const desc = [
|
||||||
|
null,
|
||||||
|
"user",
|
||||||
|
"nice",
|
||||||
|
"system",
|
||||||
|
"idle",
|
||||||
|
"iowait",
|
||||||
|
"irq",
|
||||||
|
"softirq",
|
||||||
|
"steal",
|
||||||
|
"guest",
|
||||||
|
"guest_nice",
|
||||||
|
];
|
||||||
|
const m_cpu = counter("node_cpu_seconds_total");
|
||||||
|
|
||||||
|
let line;
|
||||||
|
while (line = nextline(f)) {
|
||||||
|
const x = wsplit(line);
|
||||||
|
|
||||||
|
if (length(x) < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (match(x[0], /^cpu\d+/)) {
|
||||||
|
const count = min(length(x), length(desc));
|
||||||
|
for (let i = 1; i < count; i++)
|
||||||
|
m_cpu({ cpu: x[0], mode: desc[i] }, x[i] / 100.0);
|
||||||
|
} else if (x[0] == "intr")
|
||||||
|
counter("node_intr_total")(null, x[1]);
|
||||||
|
else if (x[0] == "ctxt")
|
||||||
|
counter("node_context_switches_total")(null, x[1]);
|
||||||
|
else if (x[0] == "btime")
|
||||||
|
gauge("node_boot_time_seconds")(null, x[1]);
|
||||||
|
else if (x[0] == "processes")
|
||||||
|
counter("node_forks_total")(null, x[1]);
|
||||||
|
else if (x[0] == "procs_running")
|
||||||
|
gauge("node_procs_running_total")(null, x[1]);
|
||||||
|
else if (x[0] == "procs_blocked")
|
||||||
|
gauge("node_procs_blocked_total")(null, x[1]);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
gauge("node_entropy_available_bits")
|
||||||
|
(null, oneline("/proc/sys/kernel/random/entropy_avail"));
|
||||||
|
gauge("node_entropy_pool_size_bits")
|
||||||
|
(null, oneline("/proc/sys/kernel/random/poolsize"));
|
|
@ -0,0 +1,7 @@
|
||||||
|
const x = wsplit(oneline("/proc/sys/fs/file-nr"));
|
||||||
|
|
||||||
|
if (length(x) < 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gauge("node_filefd_allocated")(null, x[0]);
|
||||||
|
gauge("node_filefd_maximum")(null, x[2]);
|
|
@ -0,0 +1,8 @@
|
||||||
|
const x = wsplit(oneline("/proc/loadavg"));
|
||||||
|
|
||||||
|
if (length(x) < 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gauge("node_load1")(null, x[0]);
|
||||||
|
gauge("node_load5")(null, x[1]);
|
||||||
|
gauge("node_load15")(null, x[2]);
|
24
utils/prometheus-node-exporter-ucode/files/base/meminfo.uc
Normal file
24
utils/prometheus-node-exporter-ucode/files/base/meminfo.uc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
let f = fs.open("/proc/meminfo");
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let line;
|
||||||
|
while (line = nextline(f)) {
|
||||||
|
const x = wsplit(line);
|
||||||
|
|
||||||
|
if (length(x) < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (substr(x[0], -1) != ":")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let name;
|
||||||
|
if (substr(x[0], -2) == "):")
|
||||||
|
name = replace(substr(x[0], 0, -2), "(", "_");
|
||||||
|
else
|
||||||
|
name = substr(x[0], 0, -1);
|
||||||
|
|
||||||
|
gauge(`node_memory_${name}_bytes`)
|
||||||
|
(null, x[2] == "kB" ? x[1] * 1024 : x[1]);
|
||||||
|
}
|
48
utils/prometheus-node-exporter-ucode/files/base/netclass.uc
Normal file
48
utils/prometheus-node-exporter-ucode/files/base/netclass.uc
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
const root = "/sys/class/net/";
|
||||||
|
const devices = fs.lsdir(root);
|
||||||
|
|
||||||
|
if (length(devices) < 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const m_info = gauge("node_network_info");
|
||||||
|
const m_speed = gauge("node_network_speed_bytes");
|
||||||
|
const metrics = {
|
||||||
|
addr_assign_type: gauge("node_network_address_assign_type"),
|
||||||
|
carrier: gauge("node_network_carrier"),
|
||||||
|
carrier_changes: counter("node_network_carrier_changes_total"),
|
||||||
|
carrier_down_count: counter("node_network_carrier_down_changes_total"),
|
||||||
|
carrier_up_count: counter("node_network_carrier_up_changes_total"),
|
||||||
|
dev_id: gauge("node_network_device_id"),
|
||||||
|
dormant: gauge("node_network_dormant"),
|
||||||
|
flags: gauge("node_network_flags"),
|
||||||
|
ifindex: gauge("node_network_iface_id"),
|
||||||
|
iflink: gauge("node_network_iface_link"),
|
||||||
|
link_mode: gauge("node_network_iface_link_mode"),
|
||||||
|
mtu: gauge("node_network_mtu_bytes"),
|
||||||
|
name_assign_type: gauge("node_network_name_assign_type"),
|
||||||
|
netdev_group: gauge("node_network_net_dev_group"),
|
||||||
|
type: gauge("node_network_protocol_type"),
|
||||||
|
tx_queue_len: gauge("node_network_transmit_queue_length"),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let device in devices) {
|
||||||
|
const devroot = root + device + "/";
|
||||||
|
|
||||||
|
m_info({
|
||||||
|
device,
|
||||||
|
address: oneline(devroot + "address"),
|
||||||
|
broadcast: oneline(devroot + "broadcast"),
|
||||||
|
duplex: oneline(devroot + "duplex"),
|
||||||
|
operstate: oneline(devroot + "operstate"),
|
||||||
|
ifalias: oneline(devroot + "ifalias"),
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
for (let m in metrics) {
|
||||||
|
let line = oneline(devroot + m);
|
||||||
|
metrics[m]({ device }, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
const speed = int(oneline(devroot + "speed"));
|
||||||
|
if (speed > 0)
|
||||||
|
m_speed({ device }, speed * 1000 * 1000 / 8);
|
||||||
|
}
|
40
utils/prometheus-node-exporter-ucode/files/base/netdev.uc
Normal file
40
utils/prometheus-node-exporter-ucode/files/base/netdev.uc
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
let f = fs.open("/proc/net/dev");
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const m = [
|
||||||
|
null,
|
||||||
|
counter("node_network_receive_bytes_total"),
|
||||||
|
counter("node_network_receive_packets_total"),
|
||||||
|
counter("node_network_receive_errs_total"),
|
||||||
|
counter("node_network_receive_drop_total"),
|
||||||
|
counter("node_network_receive_fifo_total"),
|
||||||
|
counter("node_network_receive_frame_total"),
|
||||||
|
counter("node_network_receive_compressed_total"),
|
||||||
|
counter("node_network_receive_multicast_total"),
|
||||||
|
counter("node_network_transmit_bytes_total"),
|
||||||
|
counter("node_network_transmit_packets_total"),
|
||||||
|
counter("node_network_transmit_errs_total"),
|
||||||
|
counter("node_network_transmit_drop_total"),
|
||||||
|
counter("node_network_transmit_fifo_total"),
|
||||||
|
counter("node_network_transmit_colls_total"),
|
||||||
|
counter("node_network_transmit_carrier_total"),
|
||||||
|
counter("node_network_transmit_compressed_total"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let line;
|
||||||
|
while (line = nextline(f)) {
|
||||||
|
const x = wsplit(ltrim(line), " ");
|
||||||
|
|
||||||
|
if (length(x) < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (substr(x[0], -1) != ":")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const count = min(length(x), length(m));
|
||||||
|
const labels = { device: substr(x[0], 0, -1) };
|
||||||
|
for (let i = 1; i < count; i++)
|
||||||
|
m[i](labels, x[i]);
|
||||||
|
}
|
10
utils/prometheus-node-exporter-ucode/files/base/selinux.uc
Normal file
10
utils/prometheus-node-exporter-ucode/files/base/selinux.uc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
const mode = oneline("/sys/fs/selinux/enforce");
|
||||||
|
const enabled = gauge("node_selinux_enabled");
|
||||||
|
|
||||||
|
if (mode == null) {
|
||||||
|
enabled(null, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled(null, 1);
|
||||||
|
gauge("node_selinux_current_mode")(null, mode);
|
1
utils/prometheus-node-exporter-ucode/files/base/time.uc
Normal file
1
utils/prometheus-node-exporter-ucode/files/base/time.uc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
gauge("node_time_seconds")(null, time());
|
8
utils/prometheus-node-exporter-ucode/files/base/uname.uc
Normal file
8
utils/prometheus-node-exporter-ucode/files/base/uname.uc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
gauge("node_uname_info")({
|
||||||
|
sysname: oneline("/proc/sys/kernel/ostype"),
|
||||||
|
nodename: oneline("/proc/sys/kernel/hostname"),
|
||||||
|
release: oneline("/proc/sys/kernel/osrelease"),
|
||||||
|
version: oneline("/proc/sys/kernel/version"),
|
||||||
|
machine: poneline("uname -m"), // TODO lame
|
||||||
|
domainname: oneline("/proc/sys/kernel/domainname"),
|
||||||
|
}, 1);
|
7
utils/prometheus-node-exporter-ucode/files/config
Normal file
7
utils/prometheus-node-exporter-ucode/files/config
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
config prometheus-node-exporter-ucode 'main'
|
||||||
|
option listen_interface 'loopback'
|
||||||
|
option listen_port '9101'
|
||||||
|
option http_keepalive '70'
|
||||||
|
|
||||||
|
config collector 'wifi'
|
||||||
|
option stations '1'
|
|
@ -0,0 +1,6 @@
|
||||||
|
const x = ubus.call("dnsmasq", "metrics");
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (let i in x)
|
||||||
|
gauge(`dnsmasq_${i}_total`)(null, x[i]);
|
72
utils/prometheus-node-exporter-ucode/files/extra/ltq-dsl.uc
Normal file
72
utils/prometheus-node-exporter-ucode/files/extra/ltq-dsl.uc
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
const x = ubus.call("dsl", "metrics");
|
||||||
|
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gauge("dsl_info")({
|
||||||
|
atuc_vendor: x.atu_c.vendor,
|
||||||
|
atuc_system_vendor: x.atu_c.system_vendor,
|
||||||
|
chipset: x.chipset,
|
||||||
|
firmware_version: x.firmware_version,
|
||||||
|
api_version: x.api_version,
|
||||||
|
driver_version: x.driver_version,
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
gauge("dsl_line_info")({
|
||||||
|
annex: x.annex,
|
||||||
|
standard: x.standard,
|
||||||
|
mode: x.mode,
|
||||||
|
profile: x.profile,
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
gauge("dsl_up")({ detail: x.state }, x.up);
|
||||||
|
gauge("dsl_uptime_seconds")(null, x.uptime);
|
||||||
|
|
||||||
|
gauge("dsl_line_attenuation_db")
|
||||||
|
({ direction: "down" }, x.downstream.latn)
|
||||||
|
({ direction: "up" }, x.upstream.latn);
|
||||||
|
gauge("dsl_signal_attenuation_db")
|
||||||
|
({ direction: "down" }, x.downstream.satn)
|
||||||
|
({ direction: "up" }, x.upstream.satn);
|
||||||
|
gauge("dsl_signal_to_noise_margin_db")
|
||||||
|
({ direction: "down" }, x.downstream.snr)
|
||||||
|
({ direction: "up" }, x.upstream.snr);
|
||||||
|
gauge("dsl_aggregated_transmit_power_db")
|
||||||
|
({ direction: "down" }, x.downstream.actatp)
|
||||||
|
({ direction: "up" }, x.upstream.actatp);
|
||||||
|
|
||||||
|
if (x.downstream.interleave_delay)
|
||||||
|
gauge("dsl_latency_seconds")
|
||||||
|
({ direction: "down" }, x.downstream.interleave_delay / 1000000.0)
|
||||||
|
({ direction: "up" }, x.upstream.interleave_delay / 1000000.0);
|
||||||
|
gauge("dsl_datarate")
|
||||||
|
({ direction: "down" }, x.downstream.data_rate)
|
||||||
|
({ direction: "up" }, x.upstream.data_rate);
|
||||||
|
gauge("dsl_max_datarate")
|
||||||
|
({ direction: "down" }, x.downstream.attndr)
|
||||||
|
({ direction: "up" }, x.upstream.attndr);
|
||||||
|
|
||||||
|
counter("dsl_error_seconds_total")
|
||||||
|
({ err: "forward error correction", loc: "near" }, x.errors.near.fecs)
|
||||||
|
({ err: "forward error correction", loc: "far" }, x.errors.far.fecs)
|
||||||
|
({ err: "errored", loc: "near" }, x.errors.near.es)
|
||||||
|
({ err: "errored", loc: "far" }, x.errors.far.es)
|
||||||
|
({ err: "severely errored", loc: "near" }, x.errors.near.ses)
|
||||||
|
({ err: "severely errored", loc: "far" }, x.errors.far.ses)
|
||||||
|
({ err: "loss of signal", loc: "near" }, x.errors.near.loss)
|
||||||
|
({ err: "loss of signal", loc: "far" }, x.errors.far.loss)
|
||||||
|
({ err: "unavailable", loc: "near" }, x.errors.near.uas)
|
||||||
|
({ err: "unavailable", loc: "far" }, x.errors.far.uas);
|
||||||
|
|
||||||
|
counter("dsl_errors_total")
|
||||||
|
({ err: "header error code error", loc: "near" }, x.errors.near.hec)
|
||||||
|
({ err: "header error code error", loc: "far" }, x.errors.far.hec)
|
||||||
|
({ err: "non pre-emptive crc error", loc: "near" }, x.errors.near.crc_p)
|
||||||
|
({ err: "non pre-emptive crc error", loc: "far" }, x.errors.far.crc_p)
|
||||||
|
({ err: "pre-emptive crc error", loc: "near" }, x.errors.near.crcp_p)
|
||||||
|
({ err: "pre-emptive crc error", loc: "far" }, x.errors.far.crcp_p);
|
||||||
|
|
||||||
|
if (x.erb)
|
||||||
|
counter("dsl_erb_total")
|
||||||
|
({ counter: "sent" }, x.erb.sent)
|
||||||
|
({ counter: "discarded" }, x.erb.discarded);
|
30
utils/prometheus-node-exporter-ucode/files/extra/netstat.uc
Normal file
30
utils/prometheus-node-exporter-ucode/files/extra/netstat.uc
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
function parse(fn) {
|
||||||
|
let f = fs.open(fn);
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let names, values;
|
||||||
|
while (names = nextline(f), values = nextline(f)) {
|
||||||
|
const name = wsplit(names);
|
||||||
|
const value = wsplit(values);
|
||||||
|
|
||||||
|
if (name[0] != value[0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (length(name) != length(value))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let prefix = substr(name[0], 0, -1);
|
||||||
|
for (let i = 1; i < length(name); i++)
|
||||||
|
gauge(`node_netstat_${prefix}_${name[i]}`)(null, value[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = parse("/proc/net/netstat");
|
||||||
|
let s = parse("/proc/net/snmp");
|
||||||
|
|
||||||
|
if (!n && !s)
|
||||||
|
return false;
|
14
utils/prometheus-node-exporter-ucode/files/extra/openwrt.uc
Normal file
14
utils/prometheus-node-exporter-ucode/files/extra/openwrt.uc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
const x = ubus.call("system", "board");
|
||||||
|
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gauge("node_openwrt_info")({
|
||||||
|
board_name: x.board_name,
|
||||||
|
id: x.release.distribution,
|
||||||
|
model: x.model,
|
||||||
|
release: x.release.version,
|
||||||
|
revision: x.release.revision,
|
||||||
|
system: x.system,
|
||||||
|
target: x.release.target,
|
||||||
|
}, 1);
|
23
utils/prometheus-node-exporter-ucode/files/extra/snmp6.uc
Normal file
23
utils/prometheus-node-exporter-ucode/files/extra/snmp6.uc
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
function parse(fn, device, skipdecl) {
|
||||||
|
let f = fs.open(fn);
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const labels = { device };
|
||||||
|
let line;
|
||||||
|
while (line = nextline(f)) {
|
||||||
|
const x = wsplit(line);
|
||||||
|
|
||||||
|
if (length(x) < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
counter(`snmp6_${x[0]}`, null, skipdecl)(labels, x[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parse("/proc/net/snmp6", "all");
|
||||||
|
|
||||||
|
const root = "/proc/net/dev_snmp6/";
|
||||||
|
for (let device in fs.lsdir(root))
|
||||||
|
parse(root + device, device, true);
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { cursor } from "uci";
|
||||||
|
|
||||||
|
const uci = cursor();
|
||||||
|
uci.load("dhcp");
|
||||||
|
|
||||||
|
let m = gauge("dhcp_host_info");
|
||||||
|
|
||||||
|
uci.foreach('dhcp', `host`, (s) => {
|
||||||
|
m({
|
||||||
|
name: s.name,
|
||||||
|
mac: s.mac,
|
||||||
|
ip: s.ip,
|
||||||
|
}, 1);
|
||||||
|
});
|
118
utils/prometheus-node-exporter-ucode/files/extra/wifi.uc
Normal file
118
utils/prometheus-node-exporter-ucode/files/extra/wifi.uc
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
import { request, 'const' as wlconst } from 'nl80211';
|
||||||
|
|
||||||
|
const x = ubus.call("network.wireless", "status");
|
||||||
|
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const iftypes = [
|
||||||
|
"Unknown",
|
||||||
|
"Ad-Hoc",
|
||||||
|
"Client",
|
||||||
|
"Master",
|
||||||
|
"Master (VLAN)",
|
||||||
|
"WDS",
|
||||||
|
"Monitor",
|
||||||
|
"Mesh Point",
|
||||||
|
"P2P Client",
|
||||||
|
"P2P Go",
|
||||||
|
"P2P Device",
|
||||||
|
"OCB",
|
||||||
|
];
|
||||||
|
|
||||||
|
let m_radio_info = gauge("wifi_radio_info");
|
||||||
|
let m_network_info = gauge("wifi_network_info");
|
||||||
|
let m_network_quality = gauge("wifi_network_quality");
|
||||||
|
let m_network_bitrate = gauge("wifi_network_bitrate");
|
||||||
|
let m_network_noise = gauge("wifi_network_noise_dbm");
|
||||||
|
let m_network_signal = gauge("wifi_network_signal_dbm");
|
||||||
|
let m_stations_total = counter("wifi_stations_total");
|
||||||
|
let m_station_inactive = gauge("wifi_station_inactive_milliseconds");
|
||||||
|
let m_station_rx_bytes = counter("wifi_station_receive_bytes_total");
|
||||||
|
let m_station_tx_bytes = counter("wifi_station_transmit_bytes_total");
|
||||||
|
let m_station_rx_packets = counter("wifi_station_receive_packets_total");
|
||||||
|
let m_station_tx_packets = counter("wifi_station_transmit_packets_total");
|
||||||
|
let m_station_signal = gauge("wifi_station_signal_dbm");
|
||||||
|
let m_station_rx_bitrate = gauge("wifi_station_receive_kilobits_per_second");
|
||||||
|
let m_station_tx_bitrate = gauge("wifi_station_transmit_kilobits_per_second");
|
||||||
|
let m_station_exp_tp = gauge("wifi_station_expected_throughput_kilobits_per_second");
|
||||||
|
|
||||||
|
for (let radio in x) {
|
||||||
|
const rc = x[radio]["config"];
|
||||||
|
|
||||||
|
m_radio_info({
|
||||||
|
radio,
|
||||||
|
htmode: rc["htmode"],
|
||||||
|
channel: rc["channel"],
|
||||||
|
country: rc["country"],
|
||||||
|
} ,1);
|
||||||
|
|
||||||
|
for (let iface in x[radio]["interfaces"]) {
|
||||||
|
const ifname = iface["ifname"];
|
||||||
|
const nc = iface["config"];
|
||||||
|
const wif = request(wlconst.NL80211_CMD_GET_INTERFACE, 0, { dev: ifname });
|
||||||
|
|
||||||
|
if (!wif)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_network_info({
|
||||||
|
radio,
|
||||||
|
ifname,
|
||||||
|
ssid: nc["ssid"] || nc["mesh_id"],
|
||||||
|
bssid: wif["mac"],
|
||||||
|
mode: iftypes[wif["iftype"]],
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
const wsta = request(wlconst.NL80211_CMD_GET_STATION, wlconst.NLM_F_DUMP, { dev: ifname });
|
||||||
|
let signal = 0;
|
||||||
|
let bitrate = 0;
|
||||||
|
const stations = length(wsta) || 0;
|
||||||
|
if (stations) {
|
||||||
|
for (let sta in wsta) {
|
||||||
|
signal += sta["sta_info"].signal;
|
||||||
|
bitrate += sta["sta_info"]["tx_bitrate"].bitrate32;
|
||||||
|
}
|
||||||
|
bitrate /= stations * 0.01;
|
||||||
|
signal /= stations;
|
||||||
|
}
|
||||||
|
|
||||||
|
let labels = { radio, ifname };
|
||||||
|
m_network_bitrate(labels, bitrate || NaN);
|
||||||
|
m_network_signal(labels, signal || NaN);
|
||||||
|
m_network_quality(labels, signal ? 100.0 / 70 * (signal + 110) : NaN);
|
||||||
|
|
||||||
|
const wsur = request(wlconst.NL80211_CMD_GET_SURVEY, wlconst.NLM_F_DUMP, { dev: ifname });
|
||||||
|
let noise = 0;
|
||||||
|
for (let i in wsur) {
|
||||||
|
if (i["survey_info"]["frequency"] != wif["wiphy_freq"])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
noise = i["survey_info"]["noise"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_network_noise(labels, noise || NaN);
|
||||||
|
|
||||||
|
if (config["stations"] != "1")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_stations_total(labels, stations);
|
||||||
|
if (!stations)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (let sta in wsta) {
|
||||||
|
labels["mac"] = sta["mac"];
|
||||||
|
const info = sta["sta_info"];
|
||||||
|
|
||||||
|
m_station_inactive(labels, info["inactive_time"]);
|
||||||
|
m_station_rx_bytes(labels, info["rx_bytes64"]);
|
||||||
|
m_station_tx_bytes(labels, info["tx_bytes64"]);
|
||||||
|
m_station_rx_packets(labels, info["rx_packets"]);
|
||||||
|
m_station_tx_packets(labels, info["tx_packets"]);
|
||||||
|
m_station_signal(labels, info["signal"]);
|
||||||
|
m_station_rx_bitrate(labels, info["rx_bitrate"]["bitrate32"] * 100);
|
||||||
|
m_station_tx_bitrate(labels, info["tx_bitrate"]["bitrate32"] * 100);
|
||||||
|
m_station_exp_tp(labels, info["expected_throughput"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { cursor } from "uci";
|
||||||
|
|
||||||
|
const x = ubus.call("wireguard", "status");
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const uci = cursor();
|
||||||
|
uci.load("network");
|
||||||
|
|
||||||
|
let m_wg_iface_info = gauge("wireguard_interface_info");
|
||||||
|
let m_wg_peer_info = gauge("wireguard_peer_info");
|
||||||
|
let m_wg_handshake = gauge ("wireguard_latest_handshake_seconds");
|
||||||
|
let m_wg_rx = gauge ("wireguard_received_bytes_total");
|
||||||
|
let m_wg_tx = gauge ("wireguard_sent_bytes_total");
|
||||||
|
|
||||||
|
for (let iface in x) {
|
||||||
|
const wc = x[iface];
|
||||||
|
|
||||||
|
m_wg_iface_info({
|
||||||
|
name: iface,
|
||||||
|
public_key: wc["public_key"],
|
||||||
|
listen_port: wc["listen_port"],
|
||||||
|
fwmark: wc["fwmark"] || NaN,
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
for (let peer in wc["peers"]) {
|
||||||
|
let description;
|
||||||
|
uci.foreach('network', `wireguard_${iface}`, (s) => {
|
||||||
|
if (s.public_key == peer)
|
||||||
|
description = s.description;
|
||||||
|
});
|
||||||
|
|
||||||
|
const pc = wc["peers"][peer];
|
||||||
|
|
||||||
|
m_wg_peer_info({
|
||||||
|
interface: iface,
|
||||||
|
public_key: peer,
|
||||||
|
description,
|
||||||
|
endpoint: pc["endpoint"],
|
||||||
|
persistent_keepalive_interval: pc["persistent_keepalive_interval"] || NaN,
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
const labels = { public_key: peer };
|
||||||
|
|
||||||
|
m_wg_handshake(labels, pc["last_handshake"]);
|
||||||
|
m_wg_rx(labels, pc["rx_bytes"]);
|
||||||
|
m_wg_tx(labels, pc["tx_bytes"]);
|
||||||
|
}
|
||||||
|
}
|
73
utils/prometheus-node-exporter-ucode/files/init
Normal file
73
utils/prometheus-node-exporter-ucode/files/init
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
# Copyright (C) 2013-2017 OpenWrt.org
|
||||||
|
|
||||||
|
START=60
|
||||||
|
USE_PROCD=1
|
||||||
|
|
||||||
|
_log() {
|
||||||
|
logger -p daemon.info -t prometheus-node-exporter-ucode "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
. /lib/functions/network.sh
|
||||||
|
|
||||||
|
local interface port bind4 bind6
|
||||||
|
|
||||||
|
config_load prometheus-node-exporter-ucode.main
|
||||||
|
config_get interface "main" listen_interface "loopback"
|
||||||
|
config_get port "main" listen_port 9101
|
||||||
|
config_get keepalive "main" http_keepalive 70
|
||||||
|
|
||||||
|
[ "$interface" = "*" ] || {
|
||||||
|
network_get_ipaddr bind4 "$interface"
|
||||||
|
network_get_ipaddr6 bind6 "$interface"
|
||||||
|
[ -n "$bind4$bind6" ] || {
|
||||||
|
_log "defering start until listen interface $interface becomes ready"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
procd_open_instance
|
||||||
|
|
||||||
|
procd_set_param command /usr/sbin/uhttpd -f -c /dev/null -h /dev/null -S -D -o /metrics -O /usr/share/ucode/node-exporter/metrics.uc
|
||||||
|
|
||||||
|
if [ "$interface" = "*" ]; then
|
||||||
|
procd_append_param command -p $port
|
||||||
|
else
|
||||||
|
[ -n "$bind4" ] && procd_append_param command -p $bind4:$port
|
||||||
|
[ -n "$bind6" ] && procd_append_param command -p [$bind6]:$port
|
||||||
|
fi
|
||||||
|
[ $keepalive -gt 0 ] && procd_append_param command -k $keepalive
|
||||||
|
|
||||||
|
procd_add_jail prometheus-node-exporter-ucode log procfs sysfs ubus
|
||||||
|
procd_add_jail_mount "/usr/lib/uhttpd_ucode.so"
|
||||||
|
procd_add_jail_mount "/lib/libubus.so*"
|
||||||
|
procd_add_jail_mount "/lib/libuci.so"
|
||||||
|
procd_add_jail_mount "/usr/lib/ucode"
|
||||||
|
procd_add_jail_mount "/usr/lib/libnl*.so*"
|
||||||
|
procd_add_jail_mount "/usr/share/ucode/node-exporter"
|
||||||
|
procd_add_jail_mount "/etc/config"
|
||||||
|
|
||||||
|
# TODO breaks the dsl collector?
|
||||||
|
#procd_set_param user nobody
|
||||||
|
#procd_set_param group nogroup
|
||||||
|
procd_set_param no_new_privs 1
|
||||||
|
|
||||||
|
procd_set_param stdout 1
|
||||||
|
procd_set_param stderr 1
|
||||||
|
procd_set_param respawn
|
||||||
|
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
service_triggers()
|
||||||
|
{
|
||||||
|
local interface
|
||||||
|
|
||||||
|
procd_add_reload_trigger "prometheus-node-exporter-ucode"
|
||||||
|
|
||||||
|
config_load prometheus-node-exporter-ucode.main
|
||||||
|
config_get interface "main" listen_interface "loopback"
|
||||||
|
|
||||||
|
[ "$interface" = "*" ] || procd_add_reload_interface_trigger "$interface"
|
||||||
|
}
|
227
utils/prometheus-node-exporter-ucode/files/metrics.uc
Normal file
227
utils/prometheus-node-exporter-ucode/files/metrics.uc
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
{%
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { connect } from "ubus";
|
||||||
|
import { cursor } from "uci";
|
||||||
|
|
||||||
|
function debug(...s) {
|
||||||
|
if (global.debug)
|
||||||
|
warn("DEBUG: ", ...s, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function puts(...s) {
|
||||||
|
return uhttpd.send(...s, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function govalue(value) {
|
||||||
|
if (value == Infinity)
|
||||||
|
return "+Inf";
|
||||||
|
else if (value == -Infinity)
|
||||||
|
return "-Inf";
|
||||||
|
else if (value != value)
|
||||||
|
return "NaN";
|
||||||
|
else if (type(value) in [ "int", "double" ])
|
||||||
|
return value;
|
||||||
|
else if (type(value) in [ "bool", "string" ])
|
||||||
|
return +value;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function metric(name, mtype, help, skipdecl) {
|
||||||
|
let func;
|
||||||
|
let decl = skipdecl == true ? false : true;
|
||||||
|
|
||||||
|
let yield = function(labels, value) {
|
||||||
|
let v = govalue(value);
|
||||||
|
|
||||||
|
if (v == null) {
|
||||||
|
debug(`skipping metric: unsupported value '${value}' (${name})`);
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
let labels_str = "";
|
||||||
|
if (length(labels)) {
|
||||||
|
let sep = "";
|
||||||
|
let s;
|
||||||
|
labels_str = "{";
|
||||||
|
for (let l in labels) {
|
||||||
|
if (labels[l] == null)
|
||||||
|
s = "";
|
||||||
|
else if (type(labels[l]) == "string") {
|
||||||
|
s = labels[l];
|
||||||
|
s = replace(labels[l], "\\", "\\\\");
|
||||||
|
s = replace(s, "\"", "\\\"");
|
||||||
|
s = replace(s, "\n", "\\n");
|
||||||
|
} else {
|
||||||
|
s = govalue(labels[l]);
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_str += sep + l + "=\"" + s + "\"";
|
||||||
|
sep = ",";
|
||||||
|
}
|
||||||
|
labels_str += "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decl) {
|
||||||
|
if (help)
|
||||||
|
puts("# HELP ", name, " ", help);
|
||||||
|
puts("# TYPE ", name, " ", mtype);
|
||||||
|
decl = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
puts(name, labels_str, " ", v);
|
||||||
|
return func;
|
||||||
|
};
|
||||||
|
|
||||||
|
func = yield;
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
function counter(name, help, skipdecl) {
|
||||||
|
return metric(name, "counter", help, skipdecl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gauge(name, help, skipdecl) {
|
||||||
|
return metric(name, "gauge", help, skipdecl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function httpstatus(status) {
|
||||||
|
puts("Status: ", status, "\nContent-Type: text/plain; version=0.0.4; charset=utf-8\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function clockdiff(t1, t2) {
|
||||||
|
return (t2[0] - t1[0]) * 1000000000 + t2[1] - t1[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
let collectors = {};
|
||||||
|
|
||||||
|
global.handle_request = function(env) {
|
||||||
|
let scope = {
|
||||||
|
config: null,
|
||||||
|
fs,
|
||||||
|
ubus: connect(),
|
||||||
|
counter,
|
||||||
|
gauge,
|
||||||
|
wsplit: function(line) {
|
||||||
|
return split(line, /\s+/);
|
||||||
|
},
|
||||||
|
nextline: function(f) {
|
||||||
|
return rtrim(f.read("line"), "\n");
|
||||||
|
},
|
||||||
|
oneline: function(fn) {
|
||||||
|
let f = fs.open(fn);
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return nextline(f);
|
||||||
|
},
|
||||||
|
poneline: function(cmd) {
|
||||||
|
let f = fs.popen(cmd);
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return nextline(f);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (length(collectors) < 1) {
|
||||||
|
httpstatus("404 No Collectors found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cols = [];
|
||||||
|
for (let q in split(env.QUERY_STRING, "&")) {
|
||||||
|
let s = split(q, "=", 2);
|
||||||
|
if (length(s) == 2 && s[0] == "collect") {
|
||||||
|
if (!(s[1] in collectors)) {
|
||||||
|
httpstatus(`404 Collector ${s[1]} not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
push(cols, s[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length(cols) > 0)
|
||||||
|
cols = uniq(cols);
|
||||||
|
else
|
||||||
|
cols = keys(collectors);
|
||||||
|
|
||||||
|
httpstatus("200 OK");
|
||||||
|
|
||||||
|
let duration = gauge("node_scrape_collector_duration_seconds");
|
||||||
|
let success = gauge("node_scrape_collector_success");
|
||||||
|
|
||||||
|
for (let col in cols) {
|
||||||
|
let ok = false;
|
||||||
|
let t1, t2;
|
||||||
|
|
||||||
|
scope["config"] = collectors[col].config;
|
||||||
|
t1 = clock(true);
|
||||||
|
try {
|
||||||
|
ok = call(collectors[col].func, null, scope) != false;
|
||||||
|
} catch(e) {
|
||||||
|
warn(`error running collector '${col}':\n${e.message}\n`);
|
||||||
|
}
|
||||||
|
t2 = clock(true);
|
||||||
|
|
||||||
|
duration({ collector: col }, clockdiff(t1, t2) / 1000000000.0);
|
||||||
|
success({ collector: col }, ok);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const lib = "/usr/share/ucode/node-exporter/lib";
|
||||||
|
const opts = {
|
||||||
|
strict_declarations: true,
|
||||||
|
raw_mode: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cols = fs.lsdir(lib, "*.uc");
|
||||||
|
for (let col in cols) {
|
||||||
|
let func;
|
||||||
|
let uci = cursor();
|
||||||
|
|
||||||
|
try {
|
||||||
|
func = loadfile(lib + "/" + col, opts);
|
||||||
|
} catch(e) {
|
||||||
|
warn(`error compiling collector '${col}':\n${e.message}\n`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = substr(col, 0, -3);
|
||||||
|
let config = uci.get_all("prometheus-node-exporter-ucode", name);
|
||||||
|
if (!config || config[".type"] != "collector")
|
||||||
|
config = {};
|
||||||
|
else {
|
||||||
|
delete config[".anonymous"];
|
||||||
|
delete config[".type"];
|
||||||
|
delete config[".name"];
|
||||||
|
}
|
||||||
|
|
||||||
|
collectors[name] = {
|
||||||
|
func,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(`prometheus-node-exporter-ucode now serving requests with ${length(collectors)} collectors\n`);
|
||||||
|
|
||||||
|
if (!("uhttpd" in global)) {
|
||||||
|
global.debug = true;
|
||||||
|
|
||||||
|
puts = function(...s) {
|
||||||
|
return print(...s, "\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_request({
|
||||||
|
QUERY_STRING: join("&", map(ARGV, v => "collect=" + v)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
%}
|
2
utils/prometheus-node-exporter-ucode/files/run.sh
Executable file
2
utils/prometheus-node-exporter-ucode/files/run.sh
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
exec /usr/bin/ucode -T /usr/share/ucode/node-exporter/metrics.uc $*
|
3
utils/prometheus-node-exporter-ucode/test.sh
Executable file
3
utils/prometheus-node-exporter-ucode/test.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
prometheus-node-exporter-ucode time
|
Loading…
Reference in a new issue