diff --git a/pkg/inputunifi/collectevents.go b/pkg/inputunifi/collectevents.go index 53deceda..05ec4f47 100644 --- a/pkg/inputunifi/collectevents.go +++ b/pkg/inputunifi/collectevents.go @@ -64,46 +64,55 @@ func (u *InputUnifi) collectAlarms(logs []any, sites []*unifi.Site, c *Controlle devices, err := c.Unifi.GetDevices(sites) if err != nil { u.LogDebugf("Failed to get devices for alarm enrichment: %v (continuing without device names)", err) + devices = &unifi.Devices{} // Empty devices struct, alarms will not have device names } // Build MAC address to device name lookup map macToName := make(map[string]string) + for _, d := range devices.UAPs { if d.Mac != "" && d.Name != "" { macToName[strings.ToLower(d.Mac)] = d.Name } } + for _, d := range devices.USGs { if d.Mac != "" && d.Name != "" { macToName[strings.ToLower(d.Mac)] = d.Name } } + for _, d := range devices.USWs { if d.Mac != "" && d.Name != "" { macToName[strings.ToLower(d.Mac)] = d.Name } } + for _, d := range devices.UDMs { if d.Mac != "" && d.Name != "" { macToName[strings.ToLower(d.Mac)] = d.Name } } + for _, d := range devices.UXGs { if d.Mac != "" && d.Name != "" { macToName[strings.ToLower(d.Mac)] = d.Name } } + for _, d := range devices.PDUs { if d.Mac != "" && d.Name != "" { macToName[strings.ToLower(d.Mac)] = d.Name } } + for _, d := range devices.UBBs { if d.Mac != "" && d.Name != "" { macToName[strings.ToLower(d.Mac)] = d.Name } } + for _, d := range devices.UCIs { if d.Mac != "" && d.Name != "" { macToName[strings.ToLower(d.Mac)] = d.Name @@ -153,7 +162,7 @@ func (u *InputUnifi) collectAnomalies(logs []any, sites []*unifi.Site, c *Contro e.SiteName = c.DefaultSiteNameOverride } } - + logs = append(logs, e) webserver.NewInputEvent(PluginName, s.ID+"_anomalies", &webserver.Event{ @@ -201,6 +210,7 @@ func (u *InputUnifi) collectSyslog(logs []any, sites []*unifi.Site, c *Controlle // Use v2 system-log API req := unifi.DefaultSystemLogRequest(time.Hour) + entries, err := c.Unifi.GetSystemLog(sites, req) if err != nil { return logs, fmt.Errorf("unifi.GetSystemLog(): %w", err) @@ -229,6 +239,7 @@ func (u *InputUnifi) collectProtectLogs(logs []any, _ []*unifi.Site, c *Controll u.LogDebugf("Collecting Protect logs: %s (%s)", c.URL, c.ID) req := unifi.DefaultProtectLogRequest(0) // Uses default 24-hour window + entries, err := c.Unifi.GetProtectLogs(req) if err != nil { return logs, fmt.Errorf("unifi.GetProtectLogs(): %w", err) @@ -245,6 +256,7 @@ func (u *InputUnifi) collectProtectLogs(logs []any, _ []*unifi.Site, c *Controll if len(thumbID) > 2 && thumbID[:2] == "e-" { thumbID = thumbID[2:] } + if thumbData, err := c.Unifi.GetProtectEventThumbnail(thumbID); err == nil { e.ThumbnailBase64 = base64.StdEncoding.EncodeToString(thumbData) } else { @@ -343,6 +355,7 @@ func redactSystemLogEntry(e *unifi.SystemLogEntry, hash *bool, dropPII *bool) *u client.ID = RedactMacPII(client.ID, hash, dropPII) client.IP = RedactIPPII(client.IP, hash, dropPII) } + e.Parameters["CLIENT"] = client } @@ -355,6 +368,7 @@ func redactSystemLogEntry(e *unifi.SystemLogEntry, hash *bool, dropPII *bool) *u ip.ID = RedactIPPII(ip.ID, hash, dropPII) ip.Name = RedactIPPII(ip.Name, hash, dropPII) } + e.Parameters["IP"] = ip } @@ -365,6 +379,7 @@ func redactSystemLogEntry(e *unifi.SystemLogEntry, hash *bool, dropPII *bool) *u } else { admin.Name = RedactNamePII(admin.Name, hash, dropPII) } + e.Parameters["ADMIN"] = admin } @@ -415,6 +430,7 @@ func (u *InputUnifi) extractDeviceNameFromAlarm(alarm *unifi.Alarm, macToName ma // Simple regex-like search for MAC address in brackets start := strings.Index(msg, "[") + end := strings.Index(msg, "]") if start >= 0 && end > start { potentialMAC := msg[start+1 : end] diff --git a/pkg/inputunifi/interface.go b/pkg/inputunifi/interface.go index 896ca7ba..9fbfb03b 100644 --- a/pkg/inputunifi/interface.go +++ b/pkg/inputunifi/interface.go @@ -55,17 +55,21 @@ func (u *InputUnifi) Initialize(l poller.Logger) error { discovered, err := u.discoverRemoteControllers(c.APIKey) if err != nil { u.LogErrorf("Failed to discover remote controllers for controller: %v", err) + continue } + if len(discovered) > 0 { // Replace this controller with discovered ones // Remove the current one and add discovered newControllers := []*Controller{} + for _, existing := range u.Controllers { if existing != c { newControllers = append(newControllers, existing) } } + newControllers = append(newControllers, discovered...) u.Controllers = newControllers } @@ -214,6 +218,7 @@ func (u *InputUnifi) Events(filter *poller.Filter) (*poller.Events, error) { // Log error but continue to next controller u.LogErrorf("Failed to collect events from controller %s: %v", c.URL, err) collectionErrors = append(collectionErrors, fmt.Errorf("%s: %w", c.URL, err)) + continue } diff --git a/pkg/inputunifi/remote.go b/pkg/inputunifi/remote.go index 4766b4a4..998d2362 100644 --- a/pkg/inputunifi/remote.go +++ b/pkg/inputunifi/remote.go @@ -34,6 +34,7 @@ func (u *InputUnifi) discoverRemoteControllers(apiKey string) ([]*Controller, er if len(consoles) == 0 { u.Logf("No Network-capable consoles found via remote API (NVR/Protect/display-only consoles are excluded)") + return nil, nil } @@ -46,33 +47,43 @@ func (u *InputUnifi) discoverRemoteControllers(apiKey string) ([]*Controller, er if consoleName == "" { consoleName = console.ReportedState.Name } + if consoleName == "" { consoleName = console.ReportedState.Hostname } + if consoleName == "" { consoleName = console.ID } + u.LogDebugf("Discovering sites for console: %s (%s)", console.ID, consoleName) - var sites []unifi.RemoteSite - var lastErr error + var ( + sites []unifi.RemoteSite + lastErr error + ) + for attempt := 1; attempt <= discoverMaxAttempts; attempt++ { sites, lastErr = client.DiscoverSites(console.ID) if lastErr == nil { break } + if attempt < discoverMaxAttempts { u.LogDebugf("Discover sites attempt %d/%d failed for %s: %v; retrying in %v", attempt, discoverMaxAttempts, consoleName, lastErr, discoverRetryDelay) time.Sleep(discoverRetryDelay) } } + if lastErr != nil { u.Logf("Excluding controller %s: api key does not have permissions (after %d attempts)", consoleName, discoverMaxAttempts) + continue } if len(sites) == 0 { u.LogDebugf("No sites found for console %s", console.ID) + continue } @@ -99,6 +110,7 @@ func (u *InputUnifi) discoverRemoteControllers(apiKey string) ([]*Controller, er // Set remote-specific defaults and ensure all boolean pointers are initialized t := true f := false + if controller.VerifySSL == nil { controller.VerifySSL = &t // Remote API should verify SSL } @@ -106,39 +118,51 @@ func (u *InputUnifi) discoverRemoteControllers(apiKey string) ([]*Controller, er if controller.HashPII == nil { controller.HashPII = &f } + if controller.DropPII == nil { controller.DropPII = &f } + if controller.SaveSites == nil { controller.SaveSites = &t } + if controller.SaveDPI == nil { controller.SaveDPI = &f } + if controller.SaveEvents == nil { controller.SaveEvents = &f } + if controller.SaveAlarms == nil { controller.SaveAlarms = &f } + if controller.SaveAnomal == nil { controller.SaveAnomal = &f } + if controller.SaveIDs == nil { controller.SaveIDs = &f } + if controller.SaveTraffic == nil { controller.SaveTraffic = &f } + if controller.SaveRogue == nil { controller.SaveRogue = &f } + if controller.SaveSyslog == nil { controller.SaveSyslog = &f } + if controller.SaveProtectLogs == nil { controller.SaveProtectLogs = &f } + if controller.ProtectThumbnails == nil { controller.ProtectThumbnails = &f } @@ -162,6 +186,7 @@ func (u *InputUnifi) discoverRemoteControllers(apiKey string) ([]*Controller, er controller.DefaultSiteNameOverride = consoleName // Keep the actual site name for API calls controller.Sites = siteNames + u.LogDebugf("Using console name '%s' as default site name override for Cloud Gateway (API will use 'default')", consoleName) } else if len(siteNames) > 0 { controller.Sites = siteNames diff --git a/pkg/inputunifi/updateweb.go b/pkg/inputunifi/updateweb.go index b36f5fc4..3ffe0175 100644 --- a/pkg/inputunifi/updateweb.go +++ b/pkg/inputunifi/updateweb.go @@ -18,10 +18,12 @@ func controllerID(c *Controller) string { if c == nil { return "" } + client := c.Unifi if client == nil { return "" } + return client.UUID } @@ -32,12 +34,15 @@ func updateWeb(c *Controller, metrics *Metrics) { log.Printf("[ERROR] updateWeb panic recovered: %v", r) } }() + if c == nil || metrics == nil { return } + if c.Unifi == nil { return } + webserver.UpdateInput(&webserver.Input{ Name: PluginName, // Forgetting this leads to 3 hours of head scratching. Sites: formatSites(c, metrics.Sites), @@ -97,6 +102,7 @@ func formatSites(c *Controller, sites []*unifi.Site) (s webserver.Sites) { if site == nil { continue } + s = append(s, &webserver.Site{ ID: site.ID, Name: site.Name, @@ -114,6 +120,7 @@ func formatClients(c *Controller, clients []*unifi.Client) (d webserver.Clients) if client == nil { continue } + clientType, deviceMAC := "unknown", "unknown" if client.ApMac != "" { clientType = "wireless" @@ -157,6 +164,7 @@ func formatDevices(c *Controller, devices *unifi.Devices) (d webserver.Devices) if device == nil { continue } + d = append(d, &webserver.Device{ Name: device.Name, SiteID: device.SiteID, @@ -177,6 +185,7 @@ func formatDevices(c *Controller, devices *unifi.Devices) (d webserver.Devices) if device == nil { continue } + d = append(d, &webserver.Device{ Name: device.Name, SiteID: device.SiteID, @@ -197,6 +206,7 @@ func formatDevices(c *Controller, devices *unifi.Devices) (d webserver.Devices) if device == nil { continue } + d = append(d, &webserver.Device{ Name: device.Name, SiteID: device.SiteID, @@ -217,6 +227,7 @@ func formatDevices(c *Controller, devices *unifi.Devices) (d webserver.Devices) if device == nil { continue } + d = append(d, &webserver.Device{ Name: device.Name, SiteID: device.SiteID, diff --git a/pkg/lokiunifi/loki.go b/pkg/lokiunifi/loki.go index e94c4d3a..43d516d4 100644 --- a/pkg/lokiunifi/loki.go +++ b/pkg/lokiunifi/loki.go @@ -28,15 +28,15 @@ const ( // Config is the plugin's input data. type Config struct { - Disable bool `json:"disable" toml:"disable" xml:"disable" yaml:"disable"` - VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"` - URL string `json:"url" toml:"url" xml:"url" yaml:"url"` - Username string `json:"user" toml:"user" xml:"user" yaml:"user"` - Password string `json:"pass" toml:"pass" xml:"pass" yaml:"pass"` - TenantID string `json:"tenant_id" toml:"tenant_id" xml:"tenant_id" yaml:"tenant_id"` - Interval cnfg.Duration `json:"interval" toml:"interval" xml:"interval" yaml:"interval"` - Timeout cnfg.Duration `json:"timeout" toml:"timeout" xml:"timeout" yaml:"timeout"` - ExtraLabels map[string]string `json:"extra_labels" toml:"extra_labels" xml:"extra_labels" yaml:"extra_labels"` + Disable bool `json:"disable" toml:"disable" xml:"disable" yaml:"disable"` + VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"` + URL string `json:"url" toml:"url" xml:"url" yaml:"url"` + Username string `json:"user" toml:"user" xml:"user" yaml:"user"` + Password string `json:"pass" toml:"pass" xml:"pass" yaml:"pass"` + TenantID string `json:"tenant_id" toml:"tenant_id" xml:"tenant_id" yaml:"tenant_id"` + Interval cnfg.Duration `json:"interval" toml:"interval" xml:"interval" yaml:"interval"` + Timeout cnfg.Duration `json:"timeout" toml:"timeout" xml:"timeout" yaml:"timeout"` + ExtraLabels map[string]string `json:"extra_labels" toml:"extra_labels" xml:"extra_labels" yaml:"extra_labels"` } // Loki is the main library struct. This satisfies the poller.Output interface. diff --git a/pkg/lokiunifi/report.go b/pkg/lokiunifi/report.go index f74c95b7..07210048 100644 --- a/pkg/lokiunifi/report.go +++ b/pkg/lokiunifi/report.go @@ -84,6 +84,7 @@ func (r *Report) String() string { } s += fmt.Sprintf(", Dur: %v", time.Since(r.Start).Round(time.Millisecond)) + return s } diff --git a/pkg/lokiunifi/report_protect.go b/pkg/lokiunifi/report_protect.go index 9334c48d..e608d4b8 100644 --- a/pkg/lokiunifi/report_protect.go +++ b/pkg/lokiunifi/report_protect.go @@ -25,10 +25,12 @@ func (r *Report) ProtectLogEvent(event *unifi.ProtectLogEntry, logs *Logs) { // Marshal event to JSON for the log line (without thumbnail to keep it small) event.ThumbnailBase64 = "" // Temporarily clear for marshaling + msg, err := json.Marshal(event) if err != nil { msg = []byte(event.Msg()) } + event.ThumbnailBase64 = thumbnailBase64 // Restore // Add event log line @@ -68,4 +70,3 @@ func (r *Report) ProtectLogEvent(event *unifi.ProtectLogEntry, logs *Logs) { }) } } - diff --git a/pkg/otelunifi/firewall_policies.go b/pkg/otelunifi/firewall_policies.go index 4463fadb..703b5943 100644 --- a/pkg/otelunifi/firewall_policies.go +++ b/pkg/otelunifi/firewall_policies.go @@ -13,6 +13,7 @@ import ( // exportFirewallPolicies emits per-rule and per-site aggregate firewall policy metrics. func (u *OtelOutput) exportFirewallPolicies(ctx context.Context, meter metric.Meter, m *poller.Metrics, r *Report) { type siteKey struct{ site, source string } + type siteStats struct { total int enabled int diff --git a/pkg/otelunifi/otelunifi.go b/pkg/otelunifi/otelunifi.go index 4d7d0465..07ff6030 100644 --- a/pkg/otelunifi/otelunifi.go +++ b/pkg/otelunifi/otelunifi.go @@ -65,9 +65,9 @@ type OtelUnifi struct { // OtelOutput is the working struct for this plugin. type OtelOutput struct { - Collector poller.Collect - LastCheck time.Time - provider *sdkmetric.MeterProvider + Collector poller.Collect + LastCheck time.Time + provider *sdkmetric.MeterProvider *OtelUnifi } @@ -154,7 +154,7 @@ func (u *OtelOutput) Run(c poller.Collect) error { // pollController runs the ticker loop, pushing metrics on each tick. func (u *OtelOutput) pollController() { - interval := u.Interval.Duration.Round(time.Second) + interval := u.Interval.Round(time.Second) ticker := time.NewTicker(interval) defer ticker.Stop() @@ -290,7 +290,7 @@ func (u *OtelOutput) setConfigDefaults() { u.Interval = cnfg.Duration{Duration: minimumInterval} } - u.Interval = cnfg.Duration{Duration: u.Interval.Duration.Round(time.Second)} + u.Interval = cnfg.Duration{Duration: u.Interval.Round(time.Second)} if u.Timeout.Duration == 0 { u.Timeout = cnfg.Duration{Duration: 10 * time.Second} diff --git a/pkg/otelunifi/usw.go b/pkg/otelunifi/usw.go index 17854973..4eb0a8bf 100644 --- a/pkg/otelunifi/usw.go +++ b/pkg/otelunifi/usw.go @@ -42,9 +42,9 @@ func (u *OtelOutput) exportUSW(ctx context.Context, meter metric.Meter, r *Repor u.recordGauge(ctx, meter, r, "unifi_device_usw_load_avg_1", "USW load average 1-minute", s.SysStats.Loadavg1.Val, attrs) u.recordGauge(ctx, meter, r, "unifi_device_usw_rx_bytes", - "USW total receive bytes", s.Stat.Sw.RxBytes.Val, attrs) + "USW total receive bytes", s.Stat.RxBytes.Val, attrs) u.recordGauge(ctx, meter, r, "unifi_device_usw_tx_bytes", - "USW total transmit bytes", s.Stat.Sw.TxBytes.Val, attrs) + "USW total transmit bytes", s.Stat.TxBytes.Val, attrs) if !u.DeadPorts { for _, p := range s.PortTable { diff --git a/pkg/poller/commands.go b/pkg/poller/commands.go index 53f15346..ee002b0b 100644 --- a/pkg/poller/commands.go +++ b/pkg/poller/commands.go @@ -163,6 +163,7 @@ func (u *UnifiPoller) HealthCheck() error { // Check if at least one output is enabled. hasEnabledOutput := false + for _, output := range outputs { if output.Enabled() { hasEnabledOutput = true diff --git a/pkg/promunifi/controller.go b/pkg/promunifi/controller.go index ef15b9a6..72b083a5 100644 --- a/pkg/promunifi/controller.go +++ b/pkg/promunifi/controller.go @@ -6,22 +6,22 @@ import ( ) 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 + 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 + UnsupportedDeviceCount *prometheus.Desc + InformPort *prometheus.Desc + HTTPSPort *prometheus.Desc + PortalHTTPPort *prometheus.Desc } func descController(ns string) *controller { @@ -31,22 +31,22 @@ func descController(ns string) *controller { 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), + 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), + 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), } } @@ -55,9 +55,11 @@ func (u *promUnifi) exportSysinfo(r report, s *unifi.Sysinfo) { 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} @@ -65,18 +67,22 @@ func (u *promUnifi) exportSysinfo(r report, s *unifi.Sysinfo) { 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 diff --git a/pkg/promunifi/firewall_policies.go b/pkg/promunifi/firewall_policies.go index 3d0efae2..fa43fd3b 100644 --- a/pkg/promunifi/firewall_policies.go +++ b/pkg/promunifi/firewall_policies.go @@ -58,6 +58,7 @@ func (u *promUnifi) exportFirewallPolicies(r report, policies []*unifi.FirewallP // Per-site aggregate counters, keyed by "siteName|source" type siteKey struct{ site, source string } + type siteStats struct { total int enabled int diff --git a/pkg/promunifi/pdu.go b/pkg/promunifi/pdu.go index b9b42b63..646d0bf9 100644 --- a/pkg/promunifi/pdu.go +++ b/pkg/promunifi/pdu.go @@ -59,18 +59,18 @@ type pdu struct { OutletPowerFactor *prometheus.Desc OutletVoltage *prometheus.Desc // UPS battery health (vbms_table) - BatteryLevelPercent *prometheus.Desc - BatteryTimeRemaining *prometheus.Desc - BatteryCharging *prometheus.Desc - BatteryMode *prometheus.Desc - BatteriesAvailable *prometheus.Desc - BatteriesReady *prometheus.Desc - PowerBudgetWatts *prometheus.Desc - PowerOutputWatts *prometheus.Desc - PowerFactor *prometheus.Desc - OutputVoltage *prometheus.Desc - OutputCurrentAmps *prometheus.Desc - LoadPercent *prometheus.Desc + BatteryLevelPercent *prometheus.Desc + BatteryTimeRemaining *prometheus.Desc + BatteryCharging *prometheus.Desc + BatteryMode *prometheus.Desc + BatteriesAvailable *prometheus.Desc + BatteriesReady *prometheus.Desc + PowerBudgetWatts *prometheus.Desc + PowerOutputWatts *prometheus.Desc + PowerFactor *prometheus.Desc + OutputVoltage *prometheus.Desc + OutputCurrentAmps *prometheus.Desc + LoadPercent *prometheus.Desc BMSAnomalyCount *prometheus.Desc PowerCycleOnRecoveryEnabled *prometheus.Desc } @@ -182,6 +182,7 @@ func (u *promUnifi) exportPDU(r report, d *unifi.PDU) { if d.VBMSTable != nil { u.exportPDUVBMS(r, d, append([]string{d.SiteName, d.Name, d.Mac, d.SourceName}, tag)) } + u.exportBYTstats(r, labels, d.TxBytes, d.RxBytes) u.exportSYSstats(r, labels, d.SysStats, d.SystemStats) u.exportSTAcount(r, labels, d.UserNumSta, d.GuestNumSta) diff --git a/pkg/promunifi/report.go b/pkg/promunifi/report.go index fff6f13e..eaefab35 100644 --- a/pkg/promunifi/report.go +++ b/pkg/promunifi/report.go @@ -72,6 +72,7 @@ func (r *Report) export(m *metric, v float64) prometheus.Metric { for _, label := range m.Labels { bytes += len(label) + 3 // label value + quotes and comma/equals overhead } + bytes += 20 // approximate size for value and timestamp r.Bytes += bytes