mirror of
https://github.com/unpoller/unpoller.git
synced 2026-03-31 06:24:21 -04:00
* 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>
This commit is contained in:
4
go.mod
4
go.mod
@@ -12,7 +12,7 @@ require (
|
||||
github.com/prometheus/common v0.67.5
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/unpoller/unifi/v5 v5.20.1-0.20260323223726-f363f61cdbe3
|
||||
github.com/unpoller/unifi/v5 v5.22.0
|
||||
go.opentelemetry.io/otel v1.42.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.42.0
|
||||
@@ -36,7 +36,7 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@@ -89,6 +89,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/unpoller/unifi/v5 v5.20.1-0.20260323223726-f363f61cdbe3 h1:gBBSmwjzzTAVN56mthkm7J7sN/M6bgjR0RUMTtBwPO0=
|
||||
github.com/unpoller/unifi/v5 v5.20.1-0.20260323223726-f363f61cdbe3/go.mod h1:vSIXIclPG9dpKxUp+pavfgENHWaTZXvDg7F036R1YCo=
|
||||
github.com/unpoller/unifi/v5 v5.21.0 h1:rVmZjiKDwu35JYuFhhJTfCU2itcFy9uEfySmjOf5JFU=
|
||||
github.com/unpoller/unifi/v5 v5.21.0/go.mod h1:0R6t/SKaS8eoOrTkSYwzVb292KG5eQfbKEuevuES0So=
|
||||
github.com/unpoller/unifi/v5 v5.22.0 h1:ftLZcdXCtSfmd1a9nytajVCPuUoDxB1JyOPqoxPt8cI=
|
||||
github.com/unpoller/unifi/v5 v5.22.0/go.mod h1:0R6t/SKaS8eoOrTkSYwzVb292KG5eQfbKEuevuES0So=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
@@ -122,6 +126,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
||||
@@ -321,6 +321,10 @@ func (u *DatadogUnifi) loopPoints(r report) {
|
||||
for _, w := range m.WANConfigs {
|
||||
u.switchExport(r, w)
|
||||
}
|
||||
|
||||
for _, p := range m.FirewallPolicies {
|
||||
u.switchExport(r, p)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) switchExport(r report, v any) { //nolint:cyclop
|
||||
@@ -361,6 +365,8 @@ func (u *DatadogUnifi) switchExport(r report, v any) { //nolint:cyclop
|
||||
u.batchSpeedTest(r, v)
|
||||
case *unifi.WANEnrichedConfiguration:
|
||||
u.batchWAN(r, v)
|
||||
case *unifi.FirewallPolicy:
|
||||
u.batchFirewallPolicy(r, v)
|
||||
default:
|
||||
if u.Collector != nil && u.Collector.Poller().LogUnknownTypes {
|
||||
u.LogDebugf("unknown export type: %T", v)
|
||||
|
||||
51
pkg/datadogunifi/firewall_policies.go
Normal file
51
pkg/datadogunifi/firewall_policies.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package datadogunifi
|
||||
|
||||
import (
|
||||
"github.com/unpoller/unifi/v5"
|
||||
)
|
||||
|
||||
// batchFirewallPolicy generates firewall policy datapoints for Datadog.
|
||||
func (u *DatadogUnifi) batchFirewallPolicy(r report, p *unifi.FirewallPolicy) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
metricName := metricNamespace("firewall_policy")
|
||||
|
||||
tags := []string{
|
||||
tag("rule_name", p.Name),
|
||||
tag("action", p.Action),
|
||||
tag("protocol", p.Protocol),
|
||||
tag("ip_version", p.IPVersion),
|
||||
tag("source_zone", p.Source.ZoneID),
|
||||
tag("dest_zone", p.Destination.ZoneID),
|
||||
tag("site_name", p.SiteName),
|
||||
tag("source", p.SourceName),
|
||||
}
|
||||
|
||||
enabled := 0.0
|
||||
if p.Enabled.Val {
|
||||
enabled = 1.0
|
||||
}
|
||||
|
||||
predefined := 0.0
|
||||
if p.Predefined.Val {
|
||||
predefined = 1.0
|
||||
}
|
||||
|
||||
logging := 0.0
|
||||
if p.Logging.Val {
|
||||
logging = 1.0
|
||||
}
|
||||
|
||||
data := map[string]float64{
|
||||
"enabled": enabled,
|
||||
"index": p.Index.Val,
|
||||
"predefined": predefined,
|
||||
"logging": logging,
|
||||
}
|
||||
|
||||
for name, value := range data {
|
||||
_ = r.reportGauge(metricName(name), value, tags)
|
||||
}
|
||||
}
|
||||
47
pkg/influxunifi/firewall_policies.go
Normal file
47
pkg/influxunifi/firewall_policies.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package influxunifi
|
||||
|
||||
import (
|
||||
"github.com/unpoller/unifi/v5"
|
||||
)
|
||||
|
||||
// batchFirewallPolicy generates a firewall policy datapoint for InfluxDB.
|
||||
func (u *InfluxUnifi) batchFirewallPolicy(r report, p *unifi.FirewallPolicy) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"rule_name": p.Name,
|
||||
"action": p.Action,
|
||||
"protocol": p.Protocol,
|
||||
"ip_version": p.IPVersion,
|
||||
"source_zone": p.Source.ZoneID,
|
||||
"dest_zone": p.Destination.ZoneID,
|
||||
"site_name": p.SiteName,
|
||||
"source": p.SourceName,
|
||||
}
|
||||
|
||||
enabled := 0
|
||||
if p.Enabled.Val {
|
||||
enabled = 1
|
||||
}
|
||||
|
||||
predefined := 0
|
||||
if p.Predefined.Val {
|
||||
predefined = 1
|
||||
}
|
||||
|
||||
logging := 0
|
||||
if p.Logging.Val {
|
||||
logging = 1
|
||||
}
|
||||
|
||||
fields := map[string]any{
|
||||
"enabled": enabled,
|
||||
"index": p.Index.Val,
|
||||
"predefined": predefined,
|
||||
"logging": logging,
|
||||
}
|
||||
|
||||
r.send(&metric{Table: "firewall_policy", Tags: tags, Fields: fields})
|
||||
}
|
||||
@@ -434,6 +434,10 @@ func (u *InfluxUnifi) loopPoints(r report) {
|
||||
for _, w := range m.WANConfigs {
|
||||
u.switchExport(r, w)
|
||||
}
|
||||
|
||||
for _, p := range m.FirewallPolicies {
|
||||
u.switchExport(r, p)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *InfluxUnifi) switchExport(r report, v any) { //nolint:cyclop
|
||||
@@ -474,6 +478,8 @@ func (u *InfluxUnifi) switchExport(r report, v any) { //nolint:cyclop
|
||||
u.batchSpeedTest(r, v)
|
||||
case *unifi.WANEnrichedConfiguration:
|
||||
u.batchWAN(r, v)
|
||||
case *unifi.FirewallPolicy:
|
||||
u.batchFirewallPolicy(r, v)
|
||||
default:
|
||||
if u.Collector.Poller().LogUnknownTypes {
|
||||
u.LogDebugf("unknown export type: %T", v)
|
||||
|
||||
@@ -214,6 +214,14 @@ func (u *InputUnifi) pollController(c *Controller) (*poller.Metrics, error) {
|
||||
u.LogDebugf("Found %d WAN configuration entries", len(m.WANConfigs))
|
||||
}
|
||||
|
||||
// Get firewall policies
|
||||
if m.FirewallPolicies, err = c.Unifi.GetFirewallPolicies(sites); err != nil {
|
||||
// Don't fail collection if firewall policies fail - older controllers may not have this endpoint
|
||||
u.LogDebugf("unifi.GetFirewallPolicies(%s): %v (continuing)", c.URL, err)
|
||||
} else {
|
||||
u.LogDebugf("Found %d FirewallPolicies entries", len(m.FirewallPolicies))
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -437,6 +445,15 @@ func (u *InputUnifi) augmentMetrics(c *Controller, metrics *Metrics) *poller.Met
|
||||
m.Sysinfos = append(m.Sysinfos, sysinfo)
|
||||
}
|
||||
|
||||
for _, policy := range metrics.FirewallPolicies {
|
||||
// Apply site name override for firewall policies if configured
|
||||
if c.DefaultSiteNameOverride != "" && isDefaultSiteName(policy.SiteName) {
|
||||
policy.SiteName = c.DefaultSiteNameOverride
|
||||
}
|
||||
|
||||
m.FirewallPolicies = append(m.FirewallPolicies, policy)
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -562,6 +579,15 @@ func applySiteNameOverride(m *poller.Metrics, overrideName string) {
|
||||
_ = wanConfig
|
||||
}
|
||||
}
|
||||
|
||||
// Apply to firewall policies
|
||||
for i := range m.FirewallPolicies {
|
||||
if policy, ok := m.FirewallPolicies[i].(*unifi.FirewallPolicy); ok {
|
||||
if isDefaultSiteName(policy.SiteName) {
|
||||
policy.SiteName = overrideName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is a helper function for augmentMetrics.
|
||||
|
||||
@@ -87,9 +87,10 @@ type Metrics struct {
|
||||
RogueAPs []*unifi.RogueAP
|
||||
SpeedTests []*unifi.SpeedTestResult
|
||||
Devices *unifi.Devices
|
||||
DHCPLeases []*unifi.DHCPLease
|
||||
WANConfigs []*unifi.WANEnrichedConfiguration
|
||||
Sysinfos []*unifi.Sysinfo
|
||||
DHCPLeases []*unifi.DHCPLease
|
||||
WANConfigs []*unifi.WANEnrichedConfiguration
|
||||
Sysinfos []*unifi.Sysinfo
|
||||
FirewallPolicies []*unifi.FirewallPolicy
|
||||
}
|
||||
|
||||
func init() { // nolint: gochecknoinits
|
||||
|
||||
116
pkg/otelunifi/firewall_policies.go
Normal file
116
pkg/otelunifi/firewall_policies.go
Normal file
@@ -0,0 +1,116 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ func (u *OtelOutput) reportMetrics(m *poller.Metrics, _ *poller.Events) (*Report
|
||||
u.exportSites(ctx, meter, m, r)
|
||||
u.exportClients(ctx, meter, m, r)
|
||||
u.exportDevices(ctx, meter, m, r)
|
||||
u.exportFirewallPolicies(ctx, meter, m, r)
|
||||
|
||||
r.Elapsed = time.Since(start)
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ type Metrics struct {
|
||||
DHCPLeases []any
|
||||
WANConfigs []any
|
||||
Sysinfos []any
|
||||
FirewallPolicies []any
|
||||
ControllerStatuses []ControllerStatus
|
||||
}
|
||||
|
||||
|
||||
@@ -277,6 +277,7 @@ func AppendMetrics(existing *Metrics, m *Metrics) *Metrics {
|
||||
existing.DHCPLeases = append(existing.DHCPLeases, m.DHCPLeases...)
|
||||
existing.WANConfigs = append(existing.WANConfigs, m.WANConfigs...)
|
||||
existing.Sysinfos = append(existing.Sysinfos, m.Sysinfos...)
|
||||
existing.FirewallPolicies = append(existing.FirewallPolicies, m.FirewallPolicies...)
|
||||
existing.ControllerStatuses = append(existing.ControllerStatuses, m.ControllerStatuses...)
|
||||
|
||||
return existing
|
||||
|
||||
@@ -50,6 +50,7 @@ type promUnifi struct {
|
||||
DHCPLease *dhcplease
|
||||
WAN *wan
|
||||
Controller *controller
|
||||
FirewallPolicy *firewallpolicy
|
||||
// controllerUp tracks per-controller poll success (1) or failure (0).
|
||||
controllerUp *prometheus.GaugeVec
|
||||
// This interface is passed to the Collect() method. The Collect method uses
|
||||
@@ -215,6 +216,7 @@ func (u *promUnifi) Run(c poller.Collect) error {
|
||||
u.DHCPLease = descDHCPLease(u.Namespace + "_")
|
||||
u.WAN = descWAN(u.Namespace + "_")
|
||||
u.Controller = descController(u.Namespace + "_")
|
||||
u.FirewallPolicy = descFirewallPolicy(u.Namespace + "_")
|
||||
u.controllerUp = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: u.Namespace + "_controller_up",
|
||||
Help: "Whether the last poll of the UniFi controller succeeded (1) or failed (0).",
|
||||
@@ -303,7 +305,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, u.WAN} {
|
||||
for _, f := range []any{u.Client, u.Device, u.UAP, u.USG, u.USW, u.PDU, u.Site, u.SpeedTest, u.DHCPLease, u.WAN, u.FirewallPolicy} {
|
||||
v := reflect.Indirect(reflect.ValueOf(f))
|
||||
|
||||
// Loop each struct member and send it to the provided channel.
|
||||
@@ -470,6 +472,16 @@ func (u *promUnifi) loopExports(r report) {
|
||||
}
|
||||
}
|
||||
|
||||
// Export firewall policy metrics
|
||||
firewallPolicies := make([]*unifi.FirewallPolicy, 0, len(m.FirewallPolicies))
|
||||
for _, p := range m.FirewallPolicies {
|
||||
if policy, ok := p.(*unifi.FirewallPolicy); ok {
|
||||
firewallPolicies = append(firewallPolicies, policy)
|
||||
}
|
||||
}
|
||||
|
||||
u.exportFirewallPolicies(r, firewallPolicies)
|
||||
|
||||
u.exportClientDPItotals(r, appTotal, catTotal)
|
||||
}
|
||||
|
||||
|
||||
150
pkg/promunifi/firewall_policies.go
Normal file
150
pkg/promunifi/firewall_policies.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package promunifi
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/unpoller/unifi/v5"
|
||||
)
|
||||
|
||||
type firewallpolicy struct {
|
||||
// Per-rule metrics
|
||||
RuleEnabled *prometheus.Desc
|
||||
RuleIndex *prometheus.Desc
|
||||
// Aggregate metrics
|
||||
RulesTotal *prometheus.Desc
|
||||
RulesEnabled *prometheus.Desc
|
||||
RulesDisabled *prometheus.Desc
|
||||
RulesByAction *prometheus.Desc
|
||||
RulesPredefined *prometheus.Desc
|
||||
RulesCustom *prometheus.Desc
|
||||
RulesLoggingEnabled *prometheus.Desc
|
||||
}
|
||||
|
||||
func descFirewallPolicy(ns string) *firewallpolicy {
|
||||
// Per-rule labels
|
||||
ruleLabels := []string{
|
||||
"rule_name",
|
||||
"action",
|
||||
"protocol",
|
||||
"ip_version",
|
||||
"source_zone",
|
||||
"dest_zone",
|
||||
"site_name",
|
||||
"source",
|
||||
}
|
||||
// Site-level labels
|
||||
siteLabels := []string{"site_name", "source"}
|
||||
// Action-level labels
|
||||
actionLabels := []string{"action", "site_name", "source"}
|
||||
|
||||
nd := prometheus.NewDesc
|
||||
|
||||
return &firewallpolicy{
|
||||
RuleEnabled: nd(ns+"firewall_rule_enabled", "Firewall rule enabled status (1=enabled, 0=disabled)", ruleLabels, nil),
|
||||
RuleIndex: nd(ns+"firewall_rule_index", "Firewall rule priority index", ruleLabels, nil),
|
||||
RulesTotal: nd(ns+"firewall_rules_total", "Total number of firewall rules", siteLabels, nil),
|
||||
RulesEnabled: nd(ns+"firewall_rules_enabled", "Number of enabled firewall rules", siteLabels, nil),
|
||||
RulesDisabled: nd(ns+"firewall_rules_disabled", "Number of disabled firewall rules", siteLabels, nil),
|
||||
RulesByAction: nd(ns+"firewall_rules_by_action", "Number of firewall rules grouped by action", actionLabels, nil),
|
||||
RulesPredefined: nd(ns+"firewall_rules_predefined", "Number of predefined firewall rules", siteLabels, nil),
|
||||
RulesCustom: nd(ns+"firewall_rules_custom", "Number of custom firewall rules", siteLabels, nil),
|
||||
RulesLoggingEnabled: nd(ns+"firewall_rules_logging_enabled", "Number of firewall rules with logging enabled", siteLabels, nil),
|
||||
}
|
||||
}
|
||||
|
||||
func (u *promUnifi) exportFirewallPolicies(r report, policies []*unifi.FirewallPolicy) {
|
||||
if len(policies) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Per-site aggregate counters, keyed by "siteName|source"
|
||||
type siteKey struct{ site, source string }
|
||||
type siteStats struct {
|
||||
total int
|
||||
enabled int
|
||||
disabled int
|
||||
predef int
|
||||
custom int
|
||||
logging int
|
||||
site string
|
||||
source string
|
||||
byAction map[string]int
|
||||
}
|
||||
|
||||
sites := make(map[siteKey]*siteStats)
|
||||
|
||||
for _, p := range policies {
|
||||
key := siteKey{p.SiteName, p.SourceName}
|
||||
if _, ok := sites[key]; !ok {
|
||||
sites[key] = &siteStats{
|
||||
site: p.SiteName,
|
||||
source: p.SourceName,
|
||||
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]++
|
||||
}
|
||||
|
||||
// Per-rule metrics
|
||||
ruleLabels := []string{
|
||||
p.Name,
|
||||
p.Action,
|
||||
p.Protocol,
|
||||
p.IPVersion,
|
||||
p.Source.ZoneID,
|
||||
p.Destination.ZoneID,
|
||||
p.SiteName,
|
||||
p.SourceName,
|
||||
}
|
||||
|
||||
enabledVal := 0.0
|
||||
if p.Enabled.Val {
|
||||
enabledVal = 1.0
|
||||
}
|
||||
|
||||
r.send([]*metric{
|
||||
{u.FirewallPolicy.RuleEnabled, gauge, enabledVal, ruleLabels},
|
||||
{u.FirewallPolicy.RuleIndex, gauge, p.Index.Val, ruleLabels},
|
||||
})
|
||||
}
|
||||
|
||||
// Site-level aggregate metrics
|
||||
for _, s := range sites {
|
||||
siteLabels := []string{s.site, s.source}
|
||||
|
||||
r.send([]*metric{
|
||||
{u.FirewallPolicy.RulesTotal, gauge, float64(s.total), siteLabels},
|
||||
{u.FirewallPolicy.RulesEnabled, gauge, float64(s.enabled), siteLabels},
|
||||
{u.FirewallPolicy.RulesDisabled, gauge, float64(s.disabled), siteLabels},
|
||||
{u.FirewallPolicy.RulesPredefined, gauge, float64(s.predef), siteLabels},
|
||||
{u.FirewallPolicy.RulesCustom, gauge, float64(s.custom), siteLabels},
|
||||
{u.FirewallPolicy.RulesLoggingEnabled, gauge, float64(s.logging), siteLabels},
|
||||
})
|
||||
|
||||
for action, count := range s.byAction {
|
||||
r.send([]*metric{
|
||||
{u.FirewallPolicy.RulesByAction, gauge, float64(count), []string{action, s.site, s.source}},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user