feat(promunifi): add band label to per-VAP and per-radio metrics

unifi_device_vap_* and unifi_device_radio_* expose the radio identifier
(radio=ng/na/6e) but not the frequency band, so downstream consumers must
hard-code the ng/na/6e -> 2.4/5/6 GHz mapping to group or join by band.

UniFi does not carry band as its own field on the radio/VAP structs, so this
derives it from the radio identifier via a small radioBand() helper and adds
it as a "band" label (GHz: 2.4/5/6) on every series exportVAPtable and
exportRADtable emit. This mirrors the band label already present on the
RogueAP metrics, giving the per-radio/per-VAP series the same dimension.

Derived internally from p.Radio / v.Radio, so the exportVAPtable /
exportRADtable signatures are unchanged and the UAP/UDM/UDB/UBB call sites
need no edits.
This commit is contained in:
Miguel Deus
2026-06-17 14:38:30 -05:00
committed by Cody Lee
parent 9fd23b66e7
commit df0baba630
+21 -4
View File
@@ -112,8 +112,8 @@ func descRogueAP(ns string) *rogueap {
func descUAP(ns string) *uap { // nolint: funlen
labelA := []string{"stat", "site_name", "name", "source", "tag"} // stat + labels[1:]
labelV := []string{"vap_name", "bssid", "radio", "radio_name", "essid", "usage", "mac", "site_name", "name", "source", "tag"}
labelR := []string{"radio_name", "radio", "mac", "site_name", "name", "source", "tag"}
labelV := []string{"vap_name", "bssid", "radio", "band", "radio_name", "essid", "usage", "mac", "site_name", "name", "source", "tag"}
labelR := []string{"radio_name", "radio", "band", "mac", "site_name", "name", "source", "tag"}
nd := prometheus.NewDesc
return &uap{
@@ -291,6 +291,23 @@ func (u *promUnifi) exportUAPstats(r report, labels []string, ap *unifi.Ap, byte
})
}
// radioBand maps a UniFi radio identifier to its frequency band in GHz.
// UniFi reports the radio as ng (2.4 GHz), na (5 GHz) or 6e (6 GHz); the band
// is not carried as its own field on the radio/VAP structs, so it is derived
// here. Unknown identifiers yield an empty band label.
func radioBand(radio string) string {
switch radio {
case "ng":
return "2.4"
case "na":
return "5"
case "6e":
return "6"
default:
return ""
}
}
// UAP VAP Table.
func (u *promUnifi) exportVAPtable(r report, labels []string, vt unifi.VapTable, mac string) {
// vap table stats
@@ -299,7 +316,7 @@ func (u *promUnifi) exportVAPtable(r report, labels []string, vt unifi.VapTable,
continue
}
labelV := []string{v.Name, v.Bssid, v.Radio, v.RadioName, v.Essid, v.Usage, mac, labels[1], labels[2], labels[3], labels[4]}
labelV := []string{v.Name, v.Bssid, v.Radio, radioBand(v.Radio), v.RadioName, v.Essid, v.Usage, mac, labels[1], labels[2], labels[3], labels[4]}
r.send([]*metric{
{u.UAP.VAPCcq, gauge, float64(v.Ccq) / 1000.0, labelV},
{u.UAP.VAPMacFilterRejections, counter, v.MacFilterRejections, labelV},
@@ -346,7 +363,7 @@ func (u *promUnifi) exportVAPtable(r report, labels []string, vt unifi.VapTable,
func (u *promUnifi) exportRADtable(r report, labels []string, rt unifi.RadioTable, rts unifi.RadioTableStats, mac string) {
// radio table
for _, p := range rt {
labelR := []string{p.Name, p.Radio, mac, labels[1], labels[2], labels[3], labels[4]}
labelR := []string{p.Name, p.Radio, radioBand(p.Radio), mac, labels[1], labels[2], labels[3], labels[4]}
labelRUser := append(labelR, "user")
labelRGuest := append(labelR, "guest")