mirror of
https://github.com/unpoller/unpoller.git
synced 2026-04-05 08:54:00 -04:00
Merge pull request #940 from brngates98/feat/sysinfo-metrics
feat: add controller sysinfo metrics (unpoller#927)
This commit is contained in:
2
go.mod
2
go.mod
@@ -12,7 +12,7 @@ require (
|
|||||||
github.com/prometheus/common v0.67.5
|
github.com/prometheus/common v0.67.5
|
||||||
github.com/spf13/pflag v1.0.10
|
github.com/spf13/pflag v1.0.10
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/unpoller/unifi/v5 v5.14.0
|
github.com/unpoller/unifi/v5 v5.15.0
|
||||||
golang.org/x/crypto v0.47.0
|
golang.org/x/crypto v0.47.0
|
||||||
golang.org/x/term v0.39.0
|
golang.org/x/term v0.39.0
|
||||||
golift.io/cnfg v0.2.3
|
golift.io/cnfg v0.2.3
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -77,8 +77,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/unpoller/unifi/v5 v5.14.0 h1:mKz1GJhkYStJLgP6CpHAvMl8YTqBFJDCM8TNvC62Ois=
|
github.com/unpoller/unifi/v5 v5.15.0 h1:9xYBmboWBcY4Cv8ARbWMjBlAUNVlG7TIuX+aRf6mcUE=
|
||||||
github.com/unpoller/unifi/v5 v5.14.0/go.mod h1:vSIXIclPG9dpKxUp+pavfgENHWaTZXvDg7F036R1YCo=
|
github.com/unpoller/unifi/v5 v5.15.0/go.mod h1:vSIXIclPG9dpKxUp+pavfgENHWaTZXvDg7F036R1YCo=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
|||||||
@@ -198,6 +198,14 @@ func (u *InputUnifi) pollController(c *Controller) (*poller.Metrics, error) {
|
|||||||
u.LogDebugf("Found %d WAN configuration entries", len(m.WANConfigs))
|
u.LogDebugf("Found %d WAN configuration entries", len(m.WANConfigs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get controller system info (UniFi OS only)
|
||||||
|
if m.Sysinfos, err = c.Unifi.GetSysinfo(sites); err != nil {
|
||||||
|
// Don't fail collection if sysinfo fails - older controllers may not have this endpoint
|
||||||
|
u.LogDebugf("unifi.GetSysinfo(%s): %v (continuing)", c.URL, err)
|
||||||
|
} else {
|
||||||
|
u.LogDebugf("Found %d Sysinfo entries", len(m.Sysinfos))
|
||||||
|
}
|
||||||
|
|
||||||
return u.augmentMetrics(c, m), nil
|
return u.augmentMetrics(c, m), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,6 +405,10 @@ func (u *InputUnifi) augmentMetrics(c *Controller, metrics *Metrics) *poller.Met
|
|||||||
m.WANConfigs = append(m.WANConfigs, wanConfig)
|
m.WANConfigs = append(m.WANConfigs, wanConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, sysinfo := range metrics.Sysinfos {
|
||||||
|
m.Sysinfos = append(m.Sysinfos, sysinfo)
|
||||||
|
}
|
||||||
|
|
||||||
// Apply default_site_name_override to all metrics if configured.
|
// 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 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
|
// This allows us to use the console name for Cloud Gateways while keeping
|
||||||
@@ -502,6 +514,15 @@ func applySiteNameOverride(m *poller.Metrics, overrideName string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply to sysinfo (controller metrics)
|
||||||
|
for i := range m.Sysinfos {
|
||||||
|
if s, ok := m.Sysinfos[i].(*unifi.Sysinfo); ok {
|
||||||
|
if isDefaultSiteName(s.SiteName) {
|
||||||
|
s.SiteName = overrideName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply to WAN configs
|
// Apply to WAN configs
|
||||||
for i := range m.WANConfigs {
|
for i := range m.WANConfigs {
|
||||||
if wanConfig, ok := m.WANConfigs[i].(*unifi.WANEnrichedConfiguration); ok {
|
if wanConfig, ok := m.WANConfigs[i].(*unifi.WANEnrichedConfiguration); ok {
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ type Metrics struct {
|
|||||||
Devices *unifi.Devices
|
Devices *unifi.Devices
|
||||||
DHCPLeases []*unifi.DHCPLease
|
DHCPLeases []*unifi.DHCPLease
|
||||||
WANConfigs []*unifi.WANEnrichedConfiguration
|
WANConfigs []*unifi.WANEnrichedConfiguration
|
||||||
|
Sysinfos []*unifi.Sysinfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { // nolint: gochecknoinits
|
func init() { // nolint: gochecknoinits
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ type Metrics struct {
|
|||||||
CountryTraffic []any
|
CountryTraffic []any
|
||||||
DHCPLeases []any
|
DHCPLeases []any
|
||||||
WANConfigs []any
|
WANConfigs []any
|
||||||
|
Sysinfos []any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Events defines the type for log entries.
|
// Events defines the type for log entries.
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ type Input interface {
|
|||||||
DebugInput() (bool, error)
|
DebugInput() (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discoverer is an optional interface for inputs that can discover API endpoints
|
// Discoverer is an optional interface for inputs that can discover API endpoints.
|
||||||
// on a controller and write a shareable report (e.g. for support/debugging).
|
|
||||||
type Discoverer interface {
|
type Discoverer interface {
|
||||||
Discover(outputPath string) error
|
Discover(outputPath string) error
|
||||||
}
|
}
|
||||||
@@ -277,6 +276,7 @@ func AppendMetrics(existing *Metrics, m *Metrics) *Metrics {
|
|||||||
existing.CountryTraffic = append(existing.CountryTraffic, m.CountryTraffic...)
|
existing.CountryTraffic = append(existing.CountryTraffic, m.CountryTraffic...)
|
||||||
existing.DHCPLeases = append(existing.DHCPLeases, m.DHCPLeases...)
|
existing.DHCPLeases = append(existing.DHCPLeases, m.DHCPLeases...)
|
||||||
existing.WANConfigs = append(existing.WANConfigs, m.WANConfigs...)
|
existing.WANConfigs = append(existing.WANConfigs, m.WANConfigs...)
|
||||||
|
existing.Sysinfos = append(existing.Sysinfos, m.Sysinfos...)
|
||||||
|
|
||||||
return existing
|
return existing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ type promUnifi struct {
|
|||||||
CountryTraffic *ucountrytraffic
|
CountryTraffic *ucountrytraffic
|
||||||
DHCPLease *dhcplease
|
DHCPLease *dhcplease
|
||||||
WAN *wan
|
WAN *wan
|
||||||
|
Controller *controller
|
||||||
// This interface is passed to the Collect() method. The Collect method uses
|
// This interface is passed to the Collect() method. The Collect method uses
|
||||||
// this interface to retrieve the latest UniFi measurements and export them.
|
// this interface to retrieve the latest UniFi measurements and export them.
|
||||||
Collector poller.Collect
|
Collector poller.Collect
|
||||||
@@ -210,6 +211,7 @@ func (u *promUnifi) Run(c poller.Collect) error {
|
|||||||
u.CountryTraffic = descCountryTraffic(u.Namespace + "_countrytraffic_")
|
u.CountryTraffic = descCountryTraffic(u.Namespace + "_countrytraffic_")
|
||||||
u.DHCPLease = descDHCPLease(u.Namespace + "_")
|
u.DHCPLease = descDHCPLease(u.Namespace + "_")
|
||||||
u.WAN = descWAN(u.Namespace + "_")
|
u.WAN = descWAN(u.Namespace + "_")
|
||||||
|
u.Controller = descController(u.Namespace + "_")
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
promver.Version = version.Version
|
promver.Version = version.Version
|
||||||
@@ -441,6 +443,13 @@ func (u *promUnifi) loopExports(r report) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export controller sysinfo metrics
|
||||||
|
for _, s := range m.Sysinfos {
|
||||||
|
if sysinfo, ok := s.(*unifi.Sysinfo); ok {
|
||||||
|
u.exportSysinfo(r, sysinfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u.exportClientDPItotals(r, appTotal, catTotal)
|
u.exportClientDPItotals(r, appTotal, catTotal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
103
pkg/promunifi/controller.go
Normal file
103
pkg/promunifi/controller.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package promunifi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/unpoller/unifi/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type controller struct {
|
||||||
|
Info *prometheus.Desc
|
||||||
|
UptimeSeconds *prometheus.Desc
|
||||||
|
UpdateAvailable *prometheus.Desc
|
||||||
|
UpdateDownloaded *prometheus.Desc
|
||||||
|
AutobackupEnabled *prometheus.Desc
|
||||||
|
WebRTCSupport *prometheus.Desc
|
||||||
|
IsCloudConsole *prometheus.Desc
|
||||||
|
DataRetentionDays *prometheus.Desc
|
||||||
|
DataRetention5minHours *prometheus.Desc
|
||||||
|
DataRetentionHourlyHours *prometheus.Desc
|
||||||
|
DataRetentionDailyHours *prometheus.Desc
|
||||||
|
DataRetentionMonthlyHours *prometheus.Desc
|
||||||
|
UnsupportedDeviceCount *prometheus.Desc
|
||||||
|
InformPort *prometheus.Desc
|
||||||
|
HTTPSPort *prometheus.Desc
|
||||||
|
PortalHTTPPort *prometheus.Desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func descController(ns string) *controller {
|
||||||
|
labels := []string{"hostname", "site_name", "source"}
|
||||||
|
infoLabels := []string{"version", "build", "device_type", "console_version", "hostname", "site_name", "source"}
|
||||||
|
|
||||||
|
nd := prometheus.NewDesc
|
||||||
|
|
||||||
|
return &controller{
|
||||||
|
Info: nd(ns+"controller_info", "Controller information (always 1)", infoLabels, nil),
|
||||||
|
UptimeSeconds: nd(ns+"controller_uptime_seconds", "Controller uptime in seconds", labels, nil),
|
||||||
|
UpdateAvailable: nd(ns+"controller_update_available", "Update available (1/0)", labels, nil),
|
||||||
|
UpdateDownloaded: nd(ns+"controller_update_downloaded", "Update downloaded (1/0)", labels, nil),
|
||||||
|
AutobackupEnabled: nd(ns+"controller_autobackup_enabled", "Auto backup enabled (1/0)", labels, nil),
|
||||||
|
WebRTCSupport: nd(ns+"controller_webrtc_support", "WebRTC supported (1/0)", labels, nil),
|
||||||
|
IsCloudConsole: nd(ns+"controller_is_cloud_console", "Is cloud console (1/0)", labels, nil),
|
||||||
|
DataRetentionDays: nd(ns+"controller_data_retention_days", "Data retention in days", labels, nil),
|
||||||
|
DataRetention5minHours: nd(ns+"controller_data_retention_5min_hours", "5-minute scale retention hours", labels, nil),
|
||||||
|
DataRetentionHourlyHours: nd(ns+"controller_data_retention_hourly_hours", "Hourly scale retention hours", labels, nil),
|
||||||
|
DataRetentionDailyHours: nd(ns+"controller_data_retention_daily_hours", "Daily scale retention hours", labels, nil),
|
||||||
|
DataRetentionMonthlyHours: nd(ns+"controller_data_retention_monthly_hours", "Monthly scale retention hours", labels, nil),
|
||||||
|
UnsupportedDeviceCount: nd(ns+"controller_unsupported_device_count", "Number of unsupported devices", labels, nil),
|
||||||
|
InformPort: nd(ns+"controller_inform_port", "Inform port number", labels, nil),
|
||||||
|
HTTPSPort: nd(ns+"controller_https_port", "HTTPS port number", labels, nil),
|
||||||
|
PortalHTTPPort: nd(ns+"controller_portal_http_port", "Portal HTTP port number", labels, nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *promUnifi) exportSysinfo(r report, s *unifi.Sysinfo) {
|
||||||
|
hostname := s.Hostname
|
||||||
|
if hostname == "" {
|
||||||
|
hostname = s.Name
|
||||||
|
}
|
||||||
|
if hostname == "" {
|
||||||
|
hostname = s.SiteName // fallback when API omits both (e.g. remote/cloud)
|
||||||
|
}
|
||||||
|
labels := []string{hostname, s.SiteName, s.SourceName}
|
||||||
|
infoLabels := []string{s.Version, s.Build, s.DeviceType, s.ConsoleVer, hostname, s.SiteName, s.SourceName}
|
||||||
|
|
||||||
|
updateAvail := 0
|
||||||
|
if s.UpdateAvail {
|
||||||
|
updateAvail = 1
|
||||||
|
}
|
||||||
|
updateDown := 0
|
||||||
|
if s.UpdateDown {
|
||||||
|
updateDown = 1
|
||||||
|
}
|
||||||
|
autobackup := 0
|
||||||
|
if s.Autobackup {
|
||||||
|
autobackup = 1
|
||||||
|
}
|
||||||
|
webrtc := 0
|
||||||
|
if s.HasWebRTC {
|
||||||
|
webrtc = 1
|
||||||
|
}
|
||||||
|
cloud := 0
|
||||||
|
if s.IsCloud {
|
||||||
|
cloud = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
r.send([]*metric{
|
||||||
|
{u.Controller.Info, gauge, 1, infoLabels},
|
||||||
|
{u.Controller.UptimeSeconds, gauge, s.Uptime, labels},
|
||||||
|
{u.Controller.UpdateAvailable, gauge, updateAvail, labels},
|
||||||
|
{u.Controller.UpdateDownloaded, gauge, updateDown, labels},
|
||||||
|
{u.Controller.AutobackupEnabled, gauge, autobackup, labels},
|
||||||
|
{u.Controller.WebRTCSupport, gauge, webrtc, labels},
|
||||||
|
{u.Controller.IsCloudConsole, gauge, cloud, labels},
|
||||||
|
{u.Controller.DataRetentionDays, gauge, s.DataRetDays, labels},
|
||||||
|
{u.Controller.DataRetention5minHours, gauge, s.DataRet5min, labels},
|
||||||
|
{u.Controller.DataRetentionHourlyHours, gauge, s.DataRetHour, labels},
|
||||||
|
{u.Controller.DataRetentionDailyHours, gauge, s.DataRetDay, labels},
|
||||||
|
{u.Controller.DataRetentionMonthlyHours, gauge, s.DataRetMonth, labels},
|
||||||
|
{u.Controller.UnsupportedDeviceCount, gauge, s.Unsupported, labels},
|
||||||
|
{u.Controller.InformPort, gauge, s.InformPort, labels},
|
||||||
|
{u.Controller.HTTPSPort, gauge, s.HTTPSPort, labels},
|
||||||
|
{u.Controller.PortalHTTPPort, gauge, s.PortalPort, labels},
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user