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