prometheus-node-exporter-lua: normalize metric names in hostapd_stations
authorMartin Weinelt <hexa@darmstadt.ccc.de>
Thu, 24 Jun 2021 16:34:47 +0000 (18:34 +0200)
committerEtienne Champetier <champetier.etienne@gmail.com>
Tue, 6 Jul 2021 17:37:36 +0000 (13:37 -0400)
Normalizes metrics according to the Prometheus upstream metric
guidelines available at https://prometheus.io/docs/practices/naming/.

Drops the `hostapd_station_wpa` metric, because it is misleading, as it
is not a differentiator between WPA versions, like one could be led to
assume.

Exposes more flags in a more consistent manner. Their metric was
previously only exposed if the flag was present, but not if it wasn't.
The same applies issue was fixed with regards to vht caps.

After this commit the following breaking changes are in place:

- All flags have been moved below `hostapd_station_flags_$flagname`:
  - `hostapd_station_ht` is now `hostapd_station_flag_ht`
  - `hostapd_station_mfp` is now `hostapd_station_flag_mfp`
  - `hostapd_station_vht` is now `hostapd_station_flag_vht`
  - `hostapd_station_wmm` is now `hostapd_station_flag_wmm`

- New flags have been exposed:
  - `hostapd_station_flag_he` for high-efficency connections
  - `hostapd_station_flag_short_preamble` for short preamble connections
  - `hostapd_station_flag_auth` for authentication state
  - `hostapd_station_flag_assoc` for association state

- Some metrics have had their unit normalized to the SI base unit or
  embedded into the metrics name:
  - `hostapd_station_inactive_msec` is now
    `hostapd_station_inactive_seconds`, the value is still float64 and
    as such has enough precision anyway, but becomes easier to reason
    about
  - `hostapd_station_connected_time` has been renamed to
    `hostapd_station_connected_seconds_total` so the unit, as well as
    the nature of the counter is reflected
  - `hostapd_station_signal` now includes its unit and is therefore
    named `hostapd_station_signal_dbm`

- The packet counter metrics have been normalized to what the node
  exporter uses, so it is more in line with the defaults in the
  Prometheus ecosystem:
  - `hostapd_station_rx_packets` is now
    `hostapd_station_receive_packets_total`
  - `hostapd_station_rx_bytes` is now
    `hostapd_station_receive_bytes_total`
  - `hostapd_station_tx_packets` is now
    `hostapd_station_transmit_packets_total`
  - `hostapd_station_tx_bytes` is now
    `hostapd_station_transmit_bytes_total`

Signed-off-by: Martin Weinelt <hexa@darmstadt.ccc.de>
utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/hostapd_stations.lua

index 39d1415cc8b9c0895815ddf5aebf092be2aa0e3c..9f7f6e4ad37ace4443dc52e8a0c5cc52f09156f2 100644 (file)
@@ -48,65 +48,88 @@ local function get_wifi_interface_labels()
 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_receive_packets_total", "counter")
+  local metric_hostapd_station_rx_bytes = metric("hostapd_station_receive_bytes_total", "counter")
+  local metric_hostapd_station_tx_packets = metric("hostapd_station_transmit_packets_total", "counter")
+  local metric_hostapd_station_tx_bytes = metric("hostapd_station_transmit_bytes_total", "counter")
 
-  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_signal = metric("hostapd_station_signal_dbm", "gauge")
+  local metric_hostapd_station_connected_time = metric("hostapd_station_connected_seconds_total", "counter")
+  local metric_hostapd_station_inactive_msec = metric("hostapd_station_inactive_seconds", "gauge")
 
-  local metric_hostapd_station_inactive_msec = metric("hostapd_station_inactive_msec", "counter")
+  local metric_hostapd_station_flag_auth = metric("hostapd_station_flag_auth", "gauge")
+  local metric_hostapd_station_flag_assoc = metric("hostapd_station_flag_assoc", "gauge")
 
-  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_flag_short_preamble = metric("hostapd_station_flag_short_preamble", "gauge")
+
+  local metric_hostapd_station_flag_ht = metric("hostapd_station_flag_ht", "gauge")
+  local metric_hostapd_station_flag_vht = metric("hostapd_station_flag_vht", "gauge")
+  local metric_hostapd_station_flag_he = metric("hostapd_station_flag_he", "gauge")
+
+  local metric_hostapd_station_flag_mfp = metric("hostapd_station_flag_mfp", "gauge")
+  local metric_hostapd_station_flag_wmm = metric("hostapd_station_flag_wmm", "gauge")
 
-  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(labels, vals)
-    for k, v in pairs(vals) do
-      if k == "flags" then
-        if string.match(v, "[VHT]") then
-          metric_hostapd_station_vht(labels, 1)
-        end
-        if string.match(v, "[HT]") then
-          metric_hostapd_station_ht(labels, 1)
-        end
-        if string.match(v, "[WMM]") then
-          metric_hostapd_station_wmm(labels, 1)
-        end
-        if string.match(v, "[MFP]") then
-          metric_hostapd_station_mfp(labels, 1)
-        end
-      elseif k == "wpa" then
-        metric_hostapd_station_wpa(labels, v)
-      elseif k == "rx_packets" then
-        metric_hostapd_station_rx_packets(labels, v)
-      elseif k == "rx_bytes" then
-        metric_hostapd_station_rx_bytes(labels, v)
-      elseif k == "tx_packets" then
-        metric_hostapd_station_tx_packets(labels, v)
-      elseif k == "tx_bytes" then
-        metric_hostapd_station_tx_bytes(labels, v)
-      elseif k == "inactive_msec" then
-        metric_hostapd_station_inactive_msec(labels, v)
-      elseif k == "signal" then
-        metric_hostapd_station_signal(labels, v)
-      elseif k == "connected_time" then
-        metric_hostapd_station_connected_time(labels, v)
-      elseif k == "sae_group" then
-        metric_hostapd_station_sae_group(labels, v)
-      elseif k == "vht_caps_info" then
-             local caps = tonumber(string.gsub(v, "0x", ""), 16)
-             metric_hostapd_station_vht_capb_su_beamformee(labels, bit32.band(bit32.lshift(1, 12), caps) > 0 and 1 or 0)
-             metric_hostapd_station_vht_capb_mu_beamformee(labels, bit32.band(bit32.lshift(1, 20), caps) > 0 and 1 or 0)
+  local function evaluate_metrics(labels, kv)
+    values = {}
+    for k, v in pairs(kv) do
+      values[k] = v
+    end
+
+    -- check if values exist, they may not due to race conditions while querying
+    if values["flags"] then
+      local flags = {}
+      for flag in string.gmatch(values["flags"], "%u+") do
+        flags[flag] = true
       end
+
+      metric_hostapd_station_flag_auth(labels, flags["AUTH"] and 1 or 0)
+      metric_hostapd_station_flag_assoc(labels, flags["ASSOC"] and 1 or 0)
+
+      metric_hostapd_station_flag_short_preamble(labels, flags["SHORT_PREAMBLE"] and 1 or 0)
+
+      metric_hostapd_station_flag_ht(labels, flags["HT"] and 1 or 0)
+      metric_hostapd_station_flag_vht(labels, flags["VHT"]and 1 or 0)
+      metric_hostapd_station_flag_he(labels, flags["HE"] and 1 or 0)
+
+      metric_hostapd_station_flag_wmm(labels, flags["WMM"] and 1 or 0)
+      metric_hostapd_station_flag_mfp(labels, flags["MFP"] and 1 or 0)
+    end
+
+    -- these metrics can reasonably default to zero, when missing
+    metric_hostapd_station_rx_packets(labels, values["rx_packets"] or 0)
+    metric_hostapd_station_rx_bytes(labels, values["rx_bytes"] or 0)
+    metric_hostapd_station_tx_packets(labels, values["tx_packets"] or 0)
+    metric_hostapd_station_tx_bytes(labels, values["tx_bytes"] or 0)
+
+    -- and these metrics can't be defaulted, so check again
+    if values["inactive_msec"] ~= nil then
+      metric_hostapd_station_inactive_msec(labels, values["inactive_msec"] / 1000)
+    end
+
+    if values["signal"] ~= nil then
+      metric_hostapd_station_signal(labels, values["signal"])
+    end
+
+    if values["connected_time"] ~= nil then
+      metric_hostapd_station_connected_time(labels, values["connected_time"])
+    end
+
+    if values["vht_caps_info"] ~= nil then
+      local caps = tonumber(string.gsub(values["vht_caps_info"], "0x", ""), 16)
+      metric_hostapd_station_vht_capb_su_beamformee(labels, bit32.band(bit32.lshift(1, 12), caps) > 0 and 1 or 0)
+      metric_hostapd_station_vht_capb_mu_beamformee(labels, bit32.band(bit32.lshift(1, 20), caps) > 0 and 1 or 0)
+    else
+      metric_hostapd_station_vht_capb_su_beamformee(labels, 0)
+      metric_hostapd_station_vht_capb_mu_beamformee(labels, 0)
+    end
+
+    if values["sae_group"] ~= nil then
+      metric_hostapd_station_sae_group(labels, values["sae_group"])
     end
   end