prometheus-node-exporter-lua: add hostapd exporter 11221/head
authorDavid Bauer <mail@david-bauer.net>
Tue, 3 Dec 2019 23:40:08 +0000 (00:40 +0100)
committerDavid Bauer <mail@david-bauer.net>
Mon, 3 Feb 2020 14:42:35 +0000 (15:42 +0100)
This exporter exposes information of the connected stations acquired
from hostapd. These contain additional information compared to the
existing station exporter, however they require a full build of hostapd
/ wpad.

Signed-off-by: David Bauer <mail@david-bauer.net>
utils/prometheus-node-exporter-lua/Makefile
utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/hostapd_stations.lua [new file with mode: 0644]

index 06b28ee9de9e1aea12606449102dfe36b976f20f..3d676c385e564a4db09f23323d81cfbd40beba95 100644 (file)
@@ -4,8 +4,8 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=prometheus-node-exporter-lua
-PKG_VERSION:=2019.11.17
-PKG_RELEASE:=2
+PKG_VERSION:=2020.02.03
+PKG_RELEASE:=1
 
 PKG_MAINTAINER:=Etienne CHAMPETIER <champetier.etienne@gmail.com>
 PKG_LICENSE:=Apache-2.0
@@ -58,6 +58,12 @@ define Package/prometheus-node-exporter-lua-wifi_stations
   DEPENDS:=prometheus-node-exporter-lua +libiwinfo-lua +libubus-lua
 endef
 
+define Package/prometheus-node-exporter-lua-hostapd_stations
+  $(call Package/prometheus-node-exporter-lua/Default)
+  TITLE+= (hostapd_stations collector) - Requires a full hostapd / wpad build
+  DEPENDS:=prometheus-node-exporter-lua +hostapd-utils +lua-bit32 +libubus-lua
+endef
+
 define Package/prometheus-node-exporter-lua-bmx6
   $(call Package/prometheus-node-exporter-lua/Default)
   TITLE+= (bmx6 links collector)
@@ -128,6 +134,11 @@ define Package/prometheus-node-exporter-lua-wifi_stations/install
        $(INSTALL_BIN) ./files/usr/lib/lua/prometheus-collectors/wifi_stations.lua $(1)/usr/lib/lua/prometheus-collectors/
 endef
 
+define Package/prometheus-node-exporter-lua-hostapd_stations/install
+       $(INSTALL_DIR) $(1)/usr/lib/lua/prometheus-collectors
+       $(INSTALL_BIN) ./files/usr/lib/lua/prometheus-collectors/hostapd_stations.lua $(1)/usr/lib/lua/prometheus-collectors/
+endef
+
 define Package/prometheus-node-exporter-lua-bmx6/install
        $(INSTALL_DIR) $(1)/usr/lib/lua/prometheus-collectors
        $(INSTALL_BIN) ./files/usr/lib/lua/prometheus-collectors/bmx6.lua $(1)/usr/lib/lua/prometheus-collectors/
@@ -158,6 +169,7 @@ $(eval $(call BuildPackage,prometheus-node-exporter-lua-nat_traffic))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-netstat))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-wifi))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-wifi_stations))
+$(eval $(call BuildPackage,prometheus-node-exporter-lua-hostapd_stations))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-bmx6))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-bmx7))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-textfile))
diff --git a/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/hostapd_stations.lua b/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/hostapd_stations.lua
new file mode 100644 (file)
index 0000000..47ae9a5
--- /dev/null
@@ -0,0 +1,110 @@
+local ubus = require "ubus"
+local bit32 = require "bit32"
+
+local function get_wifi_interfaces()
+  local u = ubus.connect()
+  local status = u:call("network.wireless", "status", {})
+  local interfaces = {}
+
+  for dev, dev_table in pairs(status) do
+    for _, intf in ipairs(dev_table['interfaces']) do
+      table.insert(interfaces, intf['config']['ifname'])
+    end
+  end
+
+  return interfaces
+end
+
+local function scrape()
+  local metric_hostapd_station_vht = metric("hostapd_station_vht", "gauge")
+  local metric_hostapd_station_ht = metric("hostapd_station_ht", "gauge")
+  local metric_hostapd_station_wmm = metric("hostapd_station_wmm", "gauge")
+  local metric_hostapd_station_mfp = metric("hostapd_station_mfp", "gauge")
+
+  local metric_hostapd_station_rx_packets = metric("hostapd_station_rx_packets", "counter")
+  local metric_hostapd_station_rx_bytes = metric("hostapd_station_rx_bytes", "counter")
+  local metric_hostapd_station_tx_packets = metric("hostapd_station_tx_packets", "counter")
+  local metric_hostapd_station_tx_bytes = metric("hostapd_station_tx_bytes", "counter")
+  
+  local metric_hostapd_station_inactive_msec = metric("hostapd_station_inactive_msec", "counter")
+
+  local metric_hostapd_station_signal = metric("hostapd_station_signal", "gauge")
+  local metric_hostapd_station_connected_time = metric("hostapd_station_connected_time", "counter")
+
+  local metric_hostapd_station_wpa = metric("hostapd_station_wpa", "gauge")
+  local metric_hostapd_station_sae_group = metric("hostapd_station_sae_group", "gauge")
+
+  local metric_hostapd_station_vht_capb_su_beamformee = metric("hostapd_station_vht_capb_su_beamformee", "gauge")
+  local metric_hostapd_station_vht_capb_mu_beamformee = metric("hostapd_station_vht_capb_mu_beamformee", "gauge")
+
+  local function evaluate_metrics(ifname, station, vals)
+    local label_station = {
+      ifname = ifname,
+      station = station,
+    }
+
+    for k, v in pairs(vals) do
+      if k == "flags" then
+        if string.match(v, "[VHT]") then
+          metric_hostapd_station_vht(label_station, 1)
+        end
+        if string.match(v, "[HT]") then
+          metric_hostapd_station_ht(label_station, 1)
+        end
+        if string.match(v, "[WMM]") then
+          metric_hostapd_station_wmm(label_station, 1)
+        end
+        if string.match(v, "[MFP]") then
+          metric_hostapd_station_mfp(label_station, 1)
+        end
+      elseif k == "wpa" then
+        metric_hostapd_station_wpa(label_station, v)
+      elseif k == "rx_packets" then
+        metric_hostapd_station_rx_packets(label_station, v)
+      elseif k == "rx_bytes" then
+        metric_hostapd_station_rx_bytes(label_station, v)
+      elseif k == "tx_packets" then
+        metric_hostapd_station_tx_packets(label_station, v)
+      elseif k == "tx_bytes" then
+        metric_hostapd_station_tx_bytes(label_station, v)
+      elseif k == "inactive_msec" then
+        metric_hostapd_station_inactive_msec(label_station, v)
+      elseif k == "signal" then
+        metric_hostapd_station_signal(label_station, v)
+      elseif k == "connected_time" then
+        metric_hostapd_station_connected_time(label_station, v)
+      elseif k == "sae_group" then
+        metric_hostapd_station_sae_group(label_station, v)
+      elseif k == "vht_caps_info" then
+             local caps = tonumber(string.gsub(v, "0x", ""), 16)
+             metric_hostapd_station_vht_capb_su_beamformee(label_station, bit32.band(bit32.lshift(1, 12), caps) > 0 and 1 or 0)
+             metric_hostapd_station_vht_capb_mu_beamformee(label_station, bit32.band(bit32.lshift(1, 20), caps) > 0 and 1 or 0)
+      end
+    end
+  end
+
+  for _, ifname in ipairs(get_wifi_interfaces()) do
+    local handle = io.popen("hostapd_cli -i " .. ifname .." all_sta")
+    local all_sta = handle:read("*a")
+    handle:close()
+
+    local current_station = nil
+    local current_station_values = {}
+
+    for line in all_sta:gmatch("[^\r\n]+") do
+      if string.match(line, "^%x[0123456789aAbBcCdDeE]:%x%x:%x%x:%x%x:%x%x:%x%x$") then
+        if current_station ~= nil then
+          evaluate_metrics(ifname, current_station, current_station_values)
+        end
+        current_station = line
+        current_station_values = {}
+      else
+        local name, value = string.match(line, "(.+)=(.+)")
+        current_station_values[name] = value
+      end
+    end
+    evaluate_metrics(ifname, current_station, current_station_values)
+  end
+end
+
+return { scrape = scrape }