prometheus-node-exporter-lua: Add ethtool exporter
authorKevin Jilissen <redacted>
Wed, 8 May 2024 23:51:32 +0000 (01:51 +0200)
committerEtienne Champetier <redacted>
Sun, 8 Jun 2025 21:04:34 +0000 (00:04 +0300)
Based on ethtool-lua library, add interface statistics to the
prometheus-node-exporter.

As closely as possible, the behaviour of the implementation at
https://github.com/prometheus/node_exporter/blob/400c3979931613db930ea035f39ce7b377cdbb5b/collector/ethtool_linux.go#L208
is replicated.

Signed-off-by: Kevin Jilissen <redacted>
[squash commits, bump version, fix permission, use untyped, rename to ethtool]
Signed-off-by: Etienne Champetier <redacted>
utils/prometheus-node-exporter-lua/Makefile
utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/ethtool.lua [new file with mode: 0644]

index 9d68c20bc702402e6b360f37ee890ee8104a4bc9..5d5bbf802e6b3396f0ad330073112c14dc25522c 100644 (file)
@@ -4,8 +4,8 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=prometheus-node-exporter-lua
-PKG_VERSION:=2024.06.16
-PKG_RELEASE:=2
+PKG_VERSION:=2025.06.08
+PKG_RELEASE:=1
 
 PKG_MAINTAINER:=Etienne CHAMPETIER <champetier.etienne@gmail.com>
 PKG_LICENSE:=Apache-2.0
@@ -257,6 +257,17 @@ define Package/prometheus-node-exporter-lua-mwan3/install
        $(INSTALL_BIN) ./files/usr/lib/lua/prometheus-collectors/mwan3.lua $(1)/usr/lib/lua/prometheus-collectors/
 endef
 
+define Package/prometheus-node-exporter-lua-ethtool
+  $(call Package/prometheus-node-exporter-lua/Default)
+  TITLE+= (ethtool collector)
+  DEPENDS:=prometheus-node-exporter-lua +ethtool-lua
+endef
+
+define Package/prometheus-node-exporter-lua-ethtool/install
+       $(INSTALL_DIR) $(1)/usr/lib/lua/prometheus-collectors
+       $(INSTALL_BIN) ./files/usr/lib/lua/prometheus-collectors/ethtool.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))
@@ -276,3 +287,4 @@ $(eval $(call BuildPackage,prometheus-node-exporter-lua-wifi_stations))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-snmp6))
 $(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))
diff --git a/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/ethtool.lua b/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/ethtool.lua
new file mode 100644 (file)
index 0000000..5080728
--- /dev/null
@@ -0,0 +1,83 @@
+local ethtool = require "ethtool"
+
+local pattern_device = "^%s*([^%s:]+):"
+local pattern_metric = "_*[^0-9A-Za-z_]+_*"
+
+local metric_fqnames = {
+  rx_bytes = "node_ethtool_received_bytes_total",
+  rx_dropped = "node_ethtool_received_dropped_total",
+  rx_errors = "node_ethtool_received_errors_total",
+  rx_packets = "node_ethtool_received_packets_total",
+  tx_bytes = "node_ethtool_transmitted_bytes_total",
+  tx_errors = "node_ethtool_transmitted_errors_total",
+  tx_packets = "node_ethtool_transmitted_packets_total",
+  -- link info
+  supported_port = "node_network_supported_port_info",
+  supported_speed = "node_network_supported_speed_bytes",
+  supported_autonegotiate = "node_network_autonegotiate_supported",
+  supported_pause = "node_network_pause_supported",
+  supported_asymmetricpause = "node_network_asymmetricpause_supported",
+  advertised_speed = "node_network_advertised_speed_bytes",
+  advertised_autonegotiate = "node_network_autonegotiate_advertised",
+  advertised_pause = "node_network_pause_advertised",
+  advertised_asymmetricpause = "node_network_asymmetricpause_advertised",
+  autonegotiate = "node_network_autonegotiate",
+}
+local metric_replacement_words = {
+  rx = "received",
+  tx = "transmitted",
+}
+
+local function get_devices()
+  local devices = {}
+  for line in io.lines("/proc/net/dev") do
+    local dev = string.match(line, pattern_device)
+    if dev then
+      table.insert(devices, dev)
+    end
+  end
+  return devices
+end
+
+local function replace_words(metric)
+  return string.gsub(metric, "(%a+)", function(word)
+    local replacement = metric_replacement_words[word]
+    if replacement then
+      return replacement
+    end
+    return word
+  end)
+end
+
+local function build_ethtool_fqname(metric)
+  local metricName = metric:gsub(pattern_metric, "_")
+  metricName = metricName:lower():gsub("^[%s_]+", "")
+  metricName = replace_words(metricName)
+  return "node_ethtool_" .. metricName
+end
+
+local function scrape()
+  local eth = ethtool.open()
+  local metrics = {}
+  for _, dev in ipairs(get_devices()) do
+    local stats = eth:statistics(dev)
+    if stats then
+      for m_name, m_value in pairs(stats) do
+        fqname = metric_fqnames[m_name]
+        if fqname == nil then
+          fqname = build_ethtool_fqname(m_name)
+          metric_fqnames[m_name] = fqname
+        end
+        local m = metrics[fqname]
+        if m == nil then
+          m = metric(fqname, "untyped")
+          metrics[fqname] = m
+        end
+        m({ device = dev }, m_value)
+      end
+    end
+  end
+  eth:close()
+end
+
+return { scrape = scrape }
git clone https://git.99rst.org/PROJECT