mirror of
https://github.com/unpoller/unpoller.git
synced 2026-03-31 06:24:21 -04:00
feat: Add WAN metrics export to Prometheus
Add comprehensive WAN metrics support to unpoller: WAN Configuration Metrics: - wan_failover_priority: WAN failover priority - wan_load_balance_weight: Load balancing weight - wan_provider_download_kbps: Configured ISP download speed - wan_provider_upload_kbps: Configured ISP upload speed - wan_smartq_enabled: SmartQueue QoS status - wan_magic_enabled: Magic WAN status - wan_vlan_enabled: VLAN configuration status WAN Statistics Metrics: - wan_uptime_percentage: WAN uptime percentage - wan_peak_download_percent: Peak download utilization - wan_peak_upload_percent: Peak upload utilization - wan_max_rx_bytes_rate: Maximum receive rate - wan_max_tx_bytes_rate: Maximum transmit rate WAN Service Provider Metrics: - wan_service_provider_asn: ISP autonomous system number Labels include: - wan_id, wan_name, wan_networkgroup - wan_type (dhcp, static, pppoe) - wan_load_balance_type (weighted, failover-only) - isp_name, isp_city (service provider metrics) - site_name, source Changes: - pkg/poller/config.go: Add WANConfigs field to Metrics struct - pkg/poller/inputs.go: Append WAN configs in metric aggregation - pkg/inputunifi/input.go: Add WANConfigs field to Metrics struct - pkg/inputunifi/collector.go: Fetch WAN enriched configuration - pkg/promunifi/wan.go: New WAN metrics exporter - pkg/promunifi/collector.go: Initialize and export WAN metrics Depends on: unpoller/unifi PR (WAN API support) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2
go.mod
2
go.mod
@@ -47,4 +47,4 @@ require (
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
// replace github.com/unpoller/unifi/v5 => ../unifi
|
||||
replace github.com/unpoller/unifi/v5 => ../unifi
|
||||
|
||||
@@ -190,6 +190,14 @@ func (u *InputUnifi) pollController(c *Controller) (*poller.Metrics, error) {
|
||||
u.LogDebugf("Found %d DHCPLeases entries", len(m.DHCPLeases))
|
||||
}
|
||||
|
||||
// Get WAN enriched configuration
|
||||
if m.WANConfigs, err = c.Unifi.GetWANEnrichedConfiguration(sites); err != nil {
|
||||
// Don't fail collection if WAN config fails - older controllers may not have this endpoint
|
||||
u.LogDebugf("unifi.GetWANEnrichedConfiguration(%s): %v (continuing)", c.URL, err)
|
||||
} else {
|
||||
u.LogDebugf("Found %d WAN configuration entries", len(m.WANConfigs))
|
||||
}
|
||||
|
||||
return u.augmentMetrics(c, m), nil
|
||||
}
|
||||
|
||||
@@ -383,6 +391,12 @@ func (u *InputUnifi) augmentMetrics(c *Controller, metrics *Metrics) *poller.Met
|
||||
m.DHCPLeases = append(m.DHCPLeases, lease)
|
||||
}
|
||||
|
||||
for _, wanConfig := range metrics.WANConfigs {
|
||||
// WANEnrichedConfiguration doesn't have a SiteName field by default
|
||||
// The site context is preserved via the collector's site list
|
||||
m.WANConfigs = append(m.WANConfigs, wanConfig)
|
||||
}
|
||||
|
||||
// Apply default_site_name_override to all metrics if configured.
|
||||
// This must be done AFTER all metrics are added to m, so everything is included.
|
||||
// This allows us to use the console name for Cloud Gateways while keeping
|
||||
@@ -487,6 +501,14 @@ func applySiteNameOverride(m *poller.Metrics, overrideName string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply to WAN configs
|
||||
for i := range m.WANConfigs {
|
||||
if wanConfig, ok := m.WANConfigs[i].(*unifi.WANEnrichedConfiguration); ok {
|
||||
// WAN configs don't have SiteName field, but we'll add it in the exporter
|
||||
_ = wanConfig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is a helper function for augmentMetrics.
|
||||
|
||||
@@ -87,6 +87,7 @@ type Metrics struct {
|
||||
SpeedTests []*unifi.SpeedTestResult
|
||||
Devices *unifi.Devices
|
||||
DHCPLeases []*unifi.DHCPLease
|
||||
WANConfigs []*unifi.WANEnrichedConfiguration
|
||||
}
|
||||
|
||||
func init() { // nolint: gochecknoinits
|
||||
|
||||
@@ -89,6 +89,7 @@ type Metrics struct {
|
||||
SpeedTests []any
|
||||
CountryTraffic []any
|
||||
DHCPLeases []any
|
||||
WANConfigs []any
|
||||
}
|
||||
|
||||
// Events defines the type for log entries.
|
||||
|
||||
@@ -270,6 +270,7 @@ func AppendMetrics(existing *Metrics, m *Metrics) *Metrics {
|
||||
existing.Devices = append(existing.Devices, m.Devices...)
|
||||
existing.CountryTraffic = append(existing.CountryTraffic, m.CountryTraffic...)
|
||||
existing.DHCPLeases = append(existing.DHCPLeases, m.DHCPLeases...)
|
||||
existing.WANConfigs = append(existing.WANConfigs, m.WANConfigs...)
|
||||
|
||||
return existing
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ type promUnifi struct {
|
||||
SpeedTest *speedtest
|
||||
CountryTraffic *ucountrytraffic
|
||||
DHCPLease *dhcplease
|
||||
WAN *wan
|
||||
// This interface is passed to the Collect() method. The Collect method uses
|
||||
// this interface to retrieve the latest UniFi measurements and export them.
|
||||
Collector poller.Collect
|
||||
@@ -208,6 +209,7 @@ func (u *promUnifi) Run(c poller.Collect) error {
|
||||
u.SpeedTest = descSpeedTest(u.Namespace + "_speedtest_")
|
||||
u.CountryTraffic = descCountryTraffic(u.Namespace + "_countrytraffic_")
|
||||
u.DHCPLease = descDHCPLease(u.Namespace + "_")
|
||||
u.WAN = descWAN(u.Namespace + "_")
|
||||
|
||||
mux := http.NewServeMux()
|
||||
promver.Version = version.Version
|
||||
@@ -291,7 +293,7 @@ func (t *target) Describe(ch chan<- *prometheus.Desc) {
|
||||
// Describe satisfies the prometheus Collector. This returns all of the
|
||||
// metric descriptions that this packages produces.
|
||||
func (u *promUnifi) Describe(ch chan<- *prometheus.Desc) {
|
||||
for _, f := range []any{u.Client, u.Device, u.UAP, u.USG, u.USW, u.PDU, u.Site, u.SpeedTest, u.DHCPLease} {
|
||||
for _, f := range []any{u.Client, u.Device, u.UAP, u.USG, u.USW, u.PDU, u.Site, u.SpeedTest, u.DHCPLease, u.WAN} {
|
||||
v := reflect.Indirect(reflect.ValueOf(f))
|
||||
|
||||
// Loop each struct member and send it to the provided channel.
|
||||
@@ -432,6 +434,13 @@ func (u *promUnifi) loopExports(r report) {
|
||||
}
|
||||
}
|
||||
|
||||
// Export WAN metrics
|
||||
for _, wanConfig := range m.WANConfigs {
|
||||
if w, ok := wanConfig.(*unifi.WANEnrichedConfiguration); ok {
|
||||
u.exportWAN(r, w)
|
||||
}
|
||||
}
|
||||
|
||||
u.exportClientDPItotals(r, appTotal, catTotal)
|
||||
}
|
||||
|
||||
|
||||
140
pkg/promunifi/wan.go
Normal file
140
pkg/promunifi/wan.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package promunifi
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/unpoller/unifi/v5"
|
||||
)
|
||||
|
||||
type wan struct {
|
||||
// WAN Configuration metrics
|
||||
FailoverPriority *prometheus.Desc
|
||||
LoadBalanceWeight *prometheus.Desc
|
||||
ProviderDownloadKbps *prometheus.Desc
|
||||
ProviderUploadKbps *prometheus.Desc
|
||||
SmartQEnabled *prometheus.Desc
|
||||
MagicEnabled *prometheus.Desc
|
||||
VlanEnabled *prometheus.Desc
|
||||
// WAN Statistics metrics
|
||||
UptimePercentage *prometheus.Desc
|
||||
PeakDownloadPercent *prometheus.Desc
|
||||
PeakUploadPercent *prometheus.Desc
|
||||
MaxRxBytesR *prometheus.Desc
|
||||
MaxTxBytesR *prometheus.Desc
|
||||
// WAN Service Provider metrics
|
||||
ServiceProviderASN *prometheus.Desc
|
||||
// WAN Creation timestamp
|
||||
CreationTimestamp *prometheus.Desc
|
||||
}
|
||||
|
||||
func descWAN(ns string) *wan {
|
||||
labels := []string{
|
||||
"wan_id",
|
||||
"wan_name",
|
||||
"wan_networkgroup",
|
||||
"wan_type",
|
||||
"wan_load_balance_type",
|
||||
"site_name",
|
||||
"source",
|
||||
}
|
||||
|
||||
providerLabels := []string{
|
||||
"wan_id",
|
||||
"wan_name",
|
||||
"wan_networkgroup",
|
||||
"isp_name",
|
||||
"isp_city",
|
||||
"site_name",
|
||||
"source",
|
||||
}
|
||||
|
||||
nd := prometheus.NewDesc
|
||||
|
||||
return &wan{
|
||||
// Configuration
|
||||
FailoverPriority: nd(ns+"wan_failover_priority", "WAN failover priority (lower is higher priority)", labels, nil),
|
||||
LoadBalanceWeight: nd(ns+"wan_load_balance_weight", "WAN load balancing weight", labels, nil),
|
||||
ProviderDownloadKbps: nd(ns+"wan_provider_download_kbps", "Configured ISP download speed in Kbps", labels, nil),
|
||||
ProviderUploadKbps: nd(ns+"wan_provider_upload_kbps", "Configured ISP upload speed in Kbps", labels, nil),
|
||||
SmartQEnabled: nd(ns+"wan_smartq_enabled", "SmartQueue QoS enabled (1) or disabled (0)", labels, nil),
|
||||
MagicEnabled: nd(ns+"wan_magic_enabled", "Magic WAN enabled (1) or disabled (0)", labels, nil),
|
||||
VlanEnabled: nd(ns+"wan_vlan_enabled", "VLAN enabled for WAN (1) or disabled (0)", labels, nil),
|
||||
// Statistics
|
||||
UptimePercentage: nd(ns+"wan_uptime_percentage", "WAN uptime percentage", labels, nil),
|
||||
PeakDownloadPercent: nd(ns+"wan_peak_download_percent", "Peak download usage as percentage of configured capacity", labels, nil),
|
||||
PeakUploadPercent: nd(ns+"wan_peak_upload_percent", "Peak upload usage as percentage of configured capacity", labels, nil),
|
||||
MaxRxBytesR: nd(ns+"wan_max_rx_bytes_rate", "Maximum receive bytes rate", labels, nil),
|
||||
MaxTxBytesR: nd(ns+"wan_max_tx_bytes_rate", "Maximum transmit bytes rate", labels, nil),
|
||||
// Service Provider
|
||||
ServiceProviderASN: nd(ns+"wan_service_provider_asn", "Service provider autonomous system number", providerLabels, nil),
|
||||
// Creation
|
||||
CreationTimestamp: nd(ns+"wan_creation_timestamp", "WAN configuration creation timestamp", labels, nil),
|
||||
}
|
||||
}
|
||||
|
||||
func (u *promUnifi) exportWAN(r report, w *unifi.WANEnrichedConfiguration) {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfg := w.Configuration
|
||||
stats := w.Statistics
|
||||
details := w.Details
|
||||
|
||||
// Base labels
|
||||
labels := []string{
|
||||
cfg.ID,
|
||||
cfg.Name,
|
||||
cfg.WANNetworkgroup,
|
||||
cfg.WANType,
|
||||
cfg.WANLoadBalanceType,
|
||||
"", // site_name - will be set by caller if available
|
||||
"", // source - will be set by caller if available
|
||||
}
|
||||
|
||||
// Convert boolean FlexBool values to float64
|
||||
smartQEnabled := 0.0
|
||||
if cfg.WANSmartqEnabled.Val {
|
||||
smartQEnabled = 1.0
|
||||
}
|
||||
|
||||
magicEnabled := 0.0
|
||||
if cfg.WANMagicEnabled.Val {
|
||||
magicEnabled = 1.0
|
||||
}
|
||||
|
||||
vlanEnabled := 0.0
|
||||
if cfg.WANVlanEnabled.Val {
|
||||
vlanEnabled = 1.0
|
||||
}
|
||||
|
||||
metrics := []*metric{
|
||||
{u.WAN.FailoverPriority, gauge, cfg.WANFailoverPriority.Val, labels},
|
||||
{u.WAN.LoadBalanceWeight, gauge, cfg.WANLoadBalanceWeight.Val, labels},
|
||||
{u.WAN.ProviderDownloadKbps, gauge, cfg.WANProviderCapabilities.DownloadKbps.Val, labels},
|
||||
{u.WAN.ProviderUploadKbps, gauge, cfg.WANProviderCapabilities.UploadKbps.Val, labels},
|
||||
{u.WAN.SmartQEnabled, gauge, smartQEnabled, labels},
|
||||
{u.WAN.MagicEnabled, gauge, magicEnabled, labels},
|
||||
{u.WAN.VlanEnabled, gauge, vlanEnabled, labels},
|
||||
{u.WAN.UptimePercentage, gauge, stats.UptimePercentage, labels},
|
||||
{u.WAN.PeakDownloadPercent, gauge, stats.PeakUsage.DownloadPercentage, labels},
|
||||
{u.WAN.PeakUploadPercent, gauge, stats.PeakUsage.UploadPercentage, labels},
|
||||
{u.WAN.MaxRxBytesR, gauge, stats.PeakUsage.MaxRxBytesR.Val, labels},
|
||||
{u.WAN.MaxTxBytesR, gauge, stats.PeakUsage.MaxTxBytesR.Val, labels},
|
||||
{u.WAN.CreationTimestamp, gauge, details.CreationTimestamp.Val, labels},
|
||||
}
|
||||
|
||||
// Service provider info (uses different labels)
|
||||
providerLabels := []string{
|
||||
cfg.ID,
|
||||
cfg.Name,
|
||||
cfg.WANNetworkgroup,
|
||||
details.ServiceProvider.Name,
|
||||
details.ServiceProvider.City,
|
||||
"", // site_name
|
||||
"", // source
|
||||
}
|
||||
|
||||
metrics = append(metrics, &metric{u.WAN.ServiceProviderASN, gauge, details.ServiceProvider.ASN.Val, providerLabels})
|
||||
|
||||
r.send(metrics)
|
||||
}
|
||||
Reference in New Issue
Block a user