Files
unpoller-unpoller-3/pkg/otelunifi/firewall_policies.go
Cody Lee 6b33b6b97b feat: firewall policy metrics across all output plugins (closes #928) (#979)
* feat(promunifi): add firewall policy metrics (closes #928)

Bump unifi client to v5.22.0 and wire up firewall policy data end-to-end:

- poller.Metrics: add FirewallPolicies []any slice
- inputunifi: collect GetFirewallPolicies() per poll cycle; apply
  DefaultSiteNameOverride; augment into poller.Metrics
- promunifi: export per-rule (rule_enabled, rule_index) and per-site
  aggregate metrics (rules_total, rules_enabled, rules_disabled,
  rules_by_action, rules_predefined, rules_custom, rules_logging_enabled)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* feat: export firewall policies to influx, datadog, and otel outputs

Extends firewall policy support (PR #979) to all remaining output plugins:

- influxunifi: batchFirewallPolicy() writes measurement "firewall_policy"
  with tags (rule_name, action, protocol, ip_version, source/dest zone,
  site_name, source) and fields (enabled, index, predefined, logging)
- datadogunifi: batchFirewallPolicy() emits the same data as Datadog gauges
  under the "firewall_policy.*" namespace
- otelunifi: exportFirewallPolicies() emits per-rule gauges
  (unifi_firewall_rule_enabled, unifi_firewall_rule_index) and per-site
  aggregates (rules_total, rules_enabled, rules_disabled, rules_by_action,
  rules_predefined, rules_custom, rules_logging_enabled)

Also rebases onto master to pick up the otelunifi plugin (PR #978).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:26:27 -05:00

117 lines
3.2 KiB
Go

package otelunifi
import (
"context"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"github.com/unpoller/unifi/v5"
"github.com/unpoller/unpoller/pkg/poller"
)
// 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
disabled int
predef int
custom int
logging int
byAction map[string]int
}
sites := make(map[siteKey]*siteStats)
for _, item := range m.FirewallPolicies {
p, ok := item.(*unifi.FirewallPolicy)
if !ok {
continue
}
attrs := attribute.NewSet(
attribute.String("rule_name", p.Name),
attribute.String("action", p.Action),
attribute.String("protocol", p.Protocol),
attribute.String("ip_version", p.IPVersion),
attribute.String("source_zone", p.Source.ZoneID),
attribute.String("dest_zone", p.Destination.ZoneID),
attribute.String("site_name", p.SiteName),
attribute.String("source", p.SourceName),
)
enabled := 0.0
if p.Enabled.Val {
enabled = 1.0
}
u.recordGauge(ctx, meter, r, "unifi_firewall_rule_enabled",
"Firewall rule enabled status (1=enabled, 0=disabled)", enabled, attrs)
u.recordGauge(ctx, meter, r, "unifi_firewall_rule_index",
"Firewall rule priority index", p.Index.Val, attrs)
// Accumulate site-level stats
key := siteKey{p.SiteName, p.SourceName}
if _, ok := sites[key]; !ok {
sites[key] = &siteStats{byAction: make(map[string]int)}
}
s := sites[key]
s.total++
if p.Enabled.Val {
s.enabled++
} else {
s.disabled++
}
if p.Predefined.Val {
s.predef++
} else {
s.custom++
}
if p.Logging.Val {
s.logging++
}
if p.Action != "" {
s.byAction[p.Action]++
}
}
// Emit per-site aggregate metrics
for key, s := range sites {
siteAttrs := attribute.NewSet(
attribute.String("site_name", key.site),
attribute.String("source", key.source),
)
u.recordGauge(ctx, meter, r, "unifi_firewall_rules_total",
"Total number of firewall rules", float64(s.total), siteAttrs)
u.recordGauge(ctx, meter, r, "unifi_firewall_rules_enabled",
"Number of enabled firewall rules", float64(s.enabled), siteAttrs)
u.recordGauge(ctx, meter, r, "unifi_firewall_rules_disabled",
"Number of disabled firewall rules", float64(s.disabled), siteAttrs)
u.recordGauge(ctx, meter, r, "unifi_firewall_rules_predefined",
"Number of predefined firewall rules", float64(s.predef), siteAttrs)
u.recordGauge(ctx, meter, r, "unifi_firewall_rules_custom",
"Number of custom firewall rules", float64(s.custom), siteAttrs)
u.recordGauge(ctx, meter, r, "unifi_firewall_rules_logging_enabled",
"Number of firewall rules with logging enabled", float64(s.logging), siteAttrs)
for action, count := range s.byAction {
actionAttrs := attribute.NewSet(
attribute.String("action", action),
attribute.String("site_name", key.site),
attribute.String("source", key.source),
)
u.recordGauge(ctx, meter, r, "unifi_firewall_rules_by_action",
"Number of firewall rules grouped by action", float64(count), actionAttrs)
}
}
}