Enhance InfluxDB and Datadog UBB outputs with comprehensive metrics

This change significantly expands the metrics exported for UBB devices
to InfluxDB and Datadog, matching the comprehensive coverage added to
the Prometheus output.

Changes to InfluxDB (pkg/influxunifi/ubb.go):
- Added batchUBBstats() to export comprehensive statistics separated
  by radio (total, wifi0, terra2, user-wifi0, user-terra2)
- Added VAP table export via processVAPTable()
- Added Radio table export via processRadTable()
- Added P2P stats (rx_rate, tx_rate, throughput)
- Added link quality metrics (link_quality, link_quality_current,
  link_capacity)
- Comprehensive stats exported to new "ubb_stats" table with full
  breakdown of traffic per radio

Changes to Datadog (pkg/datadogunifi/ubb.go):
- Added batchUBBstats() to export comprehensive statistics separated
  by radio (total, wifi0, terra2, user-wifi0, user-terra2)
- Added VAP table export via processVAPTable()
- Added Radio table export via processRadTable()
- Added P2P stats (rx_rate, tx_rate, throughput)
- Added link quality metrics (link_quality, link_quality_current,
  link_capacity)
- Comprehensive stats exported with namespace "ubb.stats"

All implementations now fully support:
- 5GHz radio (wifi0) metrics
- 60GHz radio (terra2/ad) metrics - Full 802.11ad support!
- Per-radio RX/TX packets, bytes, errors, dropped, retries
- User-specific metrics for each radio
- Interface-specific metrics (ath0 for 5GHz, wlan0 for 60GHz)
- Point-to-point link statistics and quality metrics

Fixes: #409

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Cody Lee
2025-12-09 10:53:39 -06:00
parent ab2c677f30
commit c61d2651a2
4 changed files with 450 additions and 85 deletions

View File

@@ -370,6 +370,98 @@ gauges:
- unifi.ubb.uplink_latency
- unifi.ubb.uplink_max_speed
- unifi.ubb.uplink_uptime
- unifi.ubb.upgradeable
- unifi.ubb.link_capacity
- unifi.ubb.link_quality
- unifi.ubb.link_quality_current
- unifi.ubb.p2p_rx_rate
- unifi.ubb.p2p_throughput
- unifi.ubb.p2p_tx_rate
- unifi.ubb.stat_duration
- unifi.ubb.stat_mac_filter_rejections
- unifi.ubb.stat_terra2_mac_filter_rejections
- unifi.ubb.stat_terra2_rx_bytes
- unifi.ubb.stat_terra2_rx_crypts
- unifi.ubb.stat_terra2_rx_dropped
- unifi.ubb.stat_terra2_rx_errors
- unifi.ubb.stat_terra2_rx_frags
- unifi.ubb.stat_terra2_rx_packets
- unifi.ubb.stat_terra2_tx_bytes
- unifi.ubb.stat_terra2_tx_dropped
- unifi.ubb.stat_terra2_tx_errors
- unifi.ubb.stat_terra2_tx_packets
- unifi.ubb.stat_terra2_tx_retries
- unifi.ubb.stat_terra2_wifi_tx_attempts
- unifi.ubb.stat_terra2_wifi_tx_dropped
- unifi.ubb.stat_user_mac_filter_rejections
- unifi.ubb.stat_user_rx_bytes
- unifi.ubb.stat_user_rx_crypts
- unifi.ubb.stat_user_rx_dropped
- unifi.ubb.stat_user_rx_errors
- unifi.ubb.stat_user_rx_frags
- unifi.ubb.stat_user_rx_packets
- unifi.ubb.stat_user_terra2_mac_filter_rejections
- unifi.ubb.stat_user_terra2_rx_bytes
- unifi.ubb.stat_user_terra2_rx_crypts
- unifi.ubb.stat_user_terra2_rx_dropped
- unifi.ubb.stat_user_terra2_rx_errors
- unifi.ubb.stat_user_terra2_rx_frags
- unifi.ubb.stat_user_terra2_rx_packets
- unifi.ubb.stat_user_terra2_tx_bytes
- unifi.ubb.stat_user_terra2_tx_dropped
- unifi.ubb.stat_user_terra2_tx_errors
- unifi.ubb.stat_user_terra2_tx_packets
- unifi.ubb.stat_user_terra2_tx_retries
- unifi.ubb.stat_user_terra2_wifi_tx_attempts
- unifi.ubb.stat_user_terra2_wifi_tx_dropped
- unifi.ubb.stat_user_terra2_wlan0_rx_bytes
- unifi.ubb.stat_user_terra2_wlan0_rx_errors
- unifi.ubb.stat_user_terra2_wlan0_rx_packets
- unifi.ubb.stat_user_terra2_wlan0_tx_bytes
- unifi.ubb.stat_user_terra2_wlan0_tx_dropped
- unifi.ubb.stat_user_terra2_wlan0_tx_errors
- unifi.ubb.stat_user_terra2_wlan0_tx_packets
- unifi.ubb.stat_user_tx_bytes
- unifi.ubb.stat_user_tx_dropped
- unifi.ubb.stat_user_tx_errors
- unifi.ubb.stat_user_tx_packets
- unifi.ubb.stat_user_tx_retries
- unifi.ubb.stat_user_wifi0_ath0_rx_bytes
- unifi.ubb.stat_user_wifi0_ath0_rx_packets
- unifi.ubb.stat_user_wifi0_ath0_tx_bytes
- unifi.ubb.stat_user_wifi0_ath0_tx_packets
- unifi.ubb.stat_user_wifi0_mac_filter_rejections
- unifi.ubb.stat_user_wifi0_rx_bytes
- unifi.ubb.stat_user_wifi0_rx_crypts
- unifi.ubb.stat_user_wifi0_rx_dropped
- unifi.ubb.stat_user_wifi0_rx_errors
- unifi.ubb.stat_user_wifi0_rx_frags
- unifi.ubb.stat_user_wifi0_rx_packets
- unifi.ubb.stat_user_wifi0_tx_bytes
- unifi.ubb.stat_user_wifi0_tx_dropped
- unifi.ubb.stat_user_wifi0_tx_errors
- unifi.ubb.stat_user_wifi0_tx_packets
- unifi.ubb.stat_user_wifi0_tx_retries
- unifi.ubb.stat_user_wifi0_wifi_tx_attempts
- unifi.ubb.stat_user_wifi0_wifi_tx_dropped
- unifi.ubb.stat_user_wifi_tx_attempts
- unifi.ubb.stat_user_wifi_tx_dropped
- unifi.ubb.stat_wifi0_mac_filter_rejections
- unifi.ubb.stat_wifi0_rx_bytes
- unifi.ubb.stat_wifi0_rx_crypts
- unifi.ubb.stat_wifi0_rx_dropped
- unifi.ubb.stat_wifi0_rx_errors
- unifi.ubb.stat_wifi0_rx_frags
- unifi.ubb.stat_wifi0_rx_packets
- unifi.ubb.stat_wifi0_tx_bytes
- unifi.ubb.stat_wifi0_tx_dropped
- unifi.ubb.stat_wifi0_tx_errors
- unifi.ubb.stat_wifi0_tx_packets
- unifi.ubb.stat_wifi0_tx_retries
- unifi.ubb.stat_wifi0_wifi_tx_attempts
- unifi.ubb.stat_wifi0_wifi_tx_dropped
- unifi.ubb.stat_wifi_tx_attempts
- unifi.ubb.stat_wifi_tx_dropped
- unifi.usg.uplink_max_speed
- unifi.usg.uplink_uptime
- unifi.ubb.stat_rx_frags

View File

@@ -7,8 +7,10 @@ import (
// ubbT is used as a name for printed/logged counters.
const ubbT = item("UBB")
// batchUBB generates UBB datapoints for Datadog.
// These points can be passed directly to datadog.
// batchUBB generates UBB (UniFi Building Bridge) datapoints for Datadog.
// UBB devices are point-to-point wireless bridges with dual radios:
// - wifi0: 5GHz radio (802.11ac)
// - terra2/wlan0/ad: 60GHz radio (802.11ad - Terragraph/WiGig)
func (u *DatadogUnifi) batchUBB(r report, s *unifi.UBB) { // nolint: funlen
if !s.Adopted.Val || s.Locating.Val {
return
@@ -27,11 +29,6 @@ func (u *DatadogUnifi) batchUBB(r report, s *unifi.UBB) { // nolint: funlen
"license_state": s.LicenseState,
})
var sw *unifi.Bb
if s.Stat != nil {
sw = s.Stat.Bb
}
sysStats := unifi.SysStats{}
if s.SysStats != nil {
sysStats = *s.SysStats
@@ -44,6 +41,7 @@ func (u *DatadogUnifi) batchUBB(r report, s *unifi.UBB) { // nolint: funlen
data := CombineFloat64(
u.batchSysStats(sysStats, systemStats),
u.batchUBBstats(s.Stat),
map[string]float64{
"bytes": s.Bytes.Val,
"last_seen": s.LastSeen.Val,
@@ -59,53 +57,148 @@ func (u *DatadogUnifi) batchUBB(r report, s *unifi.UBB) { // nolint: funlen
},
)
// Add upgradeable as a float64 (0 or 1)
if s.Upgradable.Val {
data["upgradeable"] = 1.0
} else {
data["upgradeable"] = 0.0
}
// Add UBB-specific P2P and link quality metrics
if s.P2PStats != nil {
data["p2p_rx_rate"] = s.P2PStats.RXRate.Val
data["p2p_tx_rate"] = s.P2PStats.TXRate.Val
data["p2p_throughput"] = s.P2PStats.Throughput.Val
}
data["link_quality"] = s.LinkQuality.Val
data["link_quality_current"] = s.LinkQualityCurrent.Val
data["link_capacity"] = s.LinkCapacity.Val
r.addCount(ubbT)
metricName := metricNamespace("ubb")
reportGaugeForFloat64Map(r, metricName, data, tags)
tags = cleanTags(map[string]string{
"mac": s.Mac,
"site_name": s.SiteName,
"source": s.SourceName,
"name": s.Name,
"version": s.Version,
"model": s.Model,
"serial": s.Serial,
"type": s.Type,
"ip": s.IP,
})
data = CombineFloat64(
u.batchUBBstat(sw),
map[string]float64{
"bytes": s.Bytes.Val,
"last_seen": s.LastSeen.Val,
"rx_bytes": s.RxBytes.Val,
"tx_bytes": s.TxBytes.Val,
"uptime": s.Uptime.Val,
})
// Export VAP table (Virtual Access Point table - wireless interface stats)
u.processVAPTable(r, tags, s.VapTable)
metricName = metricNamespace("ubb")
reportGaugeForFloat64Map(r, metricName, data, tags)
// Export Radio tables (includes 5GHz wifi0 and 60GHz terra2/ad radios)
u.processRadTable(r, tags, s.RadioTable, s.RadioTableStats)
}
func (u *DatadogUnifi) batchUBBstat(sw *unifi.Bb) map[string]float64 {
if sw == nil {
// batchUBBstats generates UBB-specific statistics separated by radio.
// This includes metrics for total, wifi0 (5GHz), terra2 (60GHz), and user-specific stats.
func (u *DatadogUnifi) batchUBBstats(stat *unifi.UBBStat) map[string]float64 {
if stat == nil || stat.Bb == nil {
return map[string]float64{}
}
bb := stat.Bb
// Total aggregated stats across both radios
return map[string]float64{
"stat_bytes": sw.Bytes.Val,
"stat_rx_bytes": sw.RxBytes.Val,
"stat_rx_crypts": sw.RxCrypts.Val,
"stat_rx_dropped": sw.RxDropped.Val,
"stat_rx_errors": sw.RxErrors.Val,
"stat_rx_frags": sw.RxFrags.Val,
"stat_rx_packets": sw.TxPackets.Val,
"stat_tx_bytes": sw.TxBytes.Val,
"stat_tx_dropped": sw.TxDropped.Val,
"stat_tx_errors": sw.TxErrors.Val,
"stat_tx_packets": sw.TxPackets.Val,
"stat_tx_retries": sw.TxRetries.Val,
"stat_bytes": bb.Bytes.Val,
"stat_duration": bb.Duration.Val,
"stat_rx_packets": bb.RxPackets.Val,
"stat_rx_bytes": bb.RxBytes.Val,
"stat_rx_errors": bb.RxErrors.Val,
"stat_rx_dropped": bb.RxDropped.Val,
"stat_rx_crypts": bb.RxCrypts.Val,
"stat_rx_frags": bb.RxFrags.Val,
"stat_tx_packets": bb.TxPackets.Val,
"stat_tx_bytes": bb.TxBytes.Val,
"stat_tx_errors": bb.TxErrors.Val,
"stat_tx_dropped": bb.TxDropped.Val,
"stat_tx_retries": bb.TxRetries.Val,
"stat_mac_filter_rejections": bb.MacFilterRejections.Val,
"stat_wifi_tx_attempts": bb.WifiTxAttempts.Val,
"stat_wifi_tx_dropped": bb.WifiTxDropped.Val,
// User aggregated stats
"stat_user_rx_packets": bb.UserRxPackets.Val,
"stat_user_rx_bytes": bb.UserRxBytes.Val,
"stat_user_rx_errors": bb.UserRxErrors.Val,
"stat_user_rx_dropped": bb.UserRxDropped.Val,
"stat_user_rx_crypts": bb.UserRxCrypts.Val,
"stat_user_rx_frags": bb.UserRxFrags.Val,
"stat_user_tx_packets": bb.UserTxPackets.Val,
"stat_user_tx_bytes": bb.UserTxBytes.Val,
"stat_user_tx_errors": bb.UserTxErrors.Val,
"stat_user_tx_dropped": bb.UserTxDropped.Val,
"stat_user_tx_retries": bb.UserTxRetries.Val,
"stat_user_mac_filter_rejections": bb.UserMacFilterRejections.Val,
"stat_user_wifi_tx_attempts": bb.UserWifiTxAttempts.Val,
"stat_user_wifi_tx_dropped": bb.UserWifiTxDropped.Val,
// wifi0 radio stats (5GHz)
"stat_wifi0_rx_packets": bb.Wifi0RxPackets.Val,
"stat_wifi0_rx_bytes": bb.Wifi0RxBytes.Val,
"stat_wifi0_rx_errors": bb.Wifi0RxErrors.Val,
"stat_wifi0_rx_dropped": bb.Wifi0RxDropped.Val,
"stat_wifi0_rx_crypts": bb.Wifi0RxCrypts.Val,
"stat_wifi0_rx_frags": bb.Wifi0RxFrags.Val,
"stat_wifi0_tx_packets": bb.Wifi0TxPackets.Val,
"stat_wifi0_tx_bytes": bb.Wifi0TxBytes.Val,
"stat_wifi0_tx_errors": bb.Wifi0TxErrors.Val,
"stat_wifi0_tx_dropped": bb.Wifi0TxDropped.Val,
"stat_wifi0_tx_retries": bb.Wifi0TxRetries.Val,
"stat_wifi0_mac_filter_rejections": bb.Wifi0MacFilterRejections.Val,
"stat_wifi0_wifi_tx_attempts": bb.Wifi0WifiTxAttempts.Val,
"stat_wifi0_wifi_tx_dropped": bb.Wifi0WifiTxDropped.Val,
// terra2 radio stats (60GHz - 802.11ad)
"stat_terra2_rx_packets": bb.Terra2RxPackets.Val,
"stat_terra2_rx_bytes": bb.Terra2RxBytes.Val,
"stat_terra2_rx_errors": bb.Terra2RxErrors.Val,
"stat_terra2_rx_dropped": bb.Terra2RxDropped.Val,
"stat_terra2_rx_crypts": bb.Terra2RxCrypts.Val,
"stat_terra2_rx_frags": bb.Terra2RxFrags.Val,
"stat_terra2_tx_packets": bb.Terra2TxPackets.Val,
"stat_terra2_tx_bytes": bb.Terra2TxBytes.Val,
"stat_terra2_tx_errors": bb.Terra2TxErrors.Val,
"stat_terra2_tx_dropped": bb.Terra2TxDropped.Val,
"stat_terra2_tx_retries": bb.Terra2TxRetries.Val,
"stat_terra2_mac_filter_rejections": bb.Terra2MacFilterRejections.Val,
"stat_terra2_wifi_tx_attempts": bb.Terra2WifiTxAttempts.Val,
"stat_terra2_wifi_tx_dropped": bb.Terra2WifiTxDropped.Val,
// User wifi0 stats
"stat_user_wifi0_rx_packets": bb.UserWifi0RxPackets.Val,
"stat_user_wifi0_rx_bytes": bb.UserWifi0RxBytes.Val,
"stat_user_wifi0_rx_errors": bb.UserWifi0RxErrors.Val,
"stat_user_wifi0_rx_dropped": bb.UserWifi0RxDropped.Val,
"stat_user_wifi0_rx_crypts": bb.UserWifi0RxCrypts.Val,
"stat_user_wifi0_rx_frags": bb.UserWifi0RxFrags.Val,
"stat_user_wifi0_tx_packets": bb.UserWifi0TxPackets.Val,
"stat_user_wifi0_tx_bytes": bb.UserWifi0TxBytes.Val,
"stat_user_wifi0_tx_errors": bb.UserWifi0TxErrors.Val,
"stat_user_wifi0_tx_dropped": bb.UserWifi0TxDropped.Val,
"stat_user_wifi0_tx_retries": bb.UserWifi0TxRetries.Val,
"stat_user_wifi0_mac_filter_rejections": bb.UserWifi0MacFilterRejections.Val,
"stat_user_wifi0_wifi_tx_attempts": bb.UserWifi0WifiTxAttempts.Val,
"stat_user_wifi0_wifi_tx_dropped": bb.UserWifi0WifiTxDropped.Val,
// User terra2 stats (60GHz)
"stat_user_terra2_rx_packets": bb.UserTerra2RxPackets.Val,
"stat_user_terra2_rx_bytes": bb.UserTerra2RxBytes.Val,
"stat_user_terra2_rx_errors": bb.UserTerra2RxErrors.Val,
"stat_user_terra2_rx_dropped": bb.UserTerra2RxDropped.Val,
"stat_user_terra2_rx_crypts": bb.UserTerra2RxCrypts.Val,
"stat_user_terra2_rx_frags": bb.UserTerra2RxFrags.Val,
"stat_user_terra2_tx_packets": bb.UserTerra2TxPackets.Val,
"stat_user_terra2_tx_bytes": bb.UserTerra2TxBytes.Val,
"stat_user_terra2_tx_errors": bb.UserTerra2TxErrors.Val,
"stat_user_terra2_tx_dropped": bb.UserTerra2TxDropped.Val,
"stat_user_terra2_tx_retries": bb.UserTerra2TxRetries.Val,
"stat_user_terra2_mac_filter_rejections": bb.UserTerra2MacFilterRejections.Val,
"stat_user_terra2_wifi_tx_attempts": bb.UserTerra2WifiTxAttempts.Val,
"stat_user_terra2_wifi_tx_dropped": bb.UserTerra2WifiTxDropped.Val,
// Interface-specific stats
"stat_user_wifi0_ath0_rx_packets": bb.UserWifi0Ath0RxPackets.Val,
"stat_user_wifi0_ath0_rx_bytes": bb.UserWifi0Ath0RxBytes.Val,
"stat_user_wifi0_ath0_tx_packets": bb.UserWifi0Ath0TxPackets.Val,
"stat_user_wifi0_ath0_tx_bytes": bb.UserWifi0Ath0TxBytes.Val,
"stat_user_terra2_wlan0_rx_packets": bb.UserTerra2Wlan0RxPackets.Val,
"stat_user_terra2_wlan0_rx_bytes": bb.UserTerra2Wlan0RxBytes.Val,
"stat_user_terra2_wlan0_tx_packets": bb.UserTerra2Wlan0TxPackets.Val,
"stat_user_terra2_wlan0_tx_bytes": bb.UserTerra2Wlan0TxBytes.Val,
"stat_user_terra2_wlan0_tx_dropped": bb.UserTerra2Wlan0TxDropped.Val,
"stat_user_terra2_wlan0_rx_errors": bb.UserTerra2Wlan0RxErrors.Val,
"stat_user_terra2_wlan0_tx_errors": bb.UserTerra2Wlan0TxErrors.Val,
}
}