unpoller_unpoller/integrations/inputunifi/promunifi/collector.go

136 lines
3.7 KiB
Go

package promunifi
import (
"fmt"
"reflect"
"time"
"github.com/davidnewhall/unifi-poller/metrics"
"github.com/prometheus/client_golang/prometheus"
)
// UnifiCollectorOpts defines the data needed to collect and report UniFi Metrics.
type UnifiCollectorOpts struct {
// If non-empty, each of the collected metrics is prefixed by the
// provided string and an underscore ("_").
Namespace string
// If true, any error encountered during collection is reported as an
// invalid metric (see NewInvalidMetric). Otherwise, errors are ignored
// and the collected metrics will be incomplete. (Possibly, no metrics
// will be collected at all.)
ReportErrors bool
// This function is passed to the Collect() method. The Collect method runs This
// function to retreive the latest UniFi
CollectFn func() *metrics.Metrics
// Setting this to true will enable IDS exports.
CollectIDS bool
}
type unifiCollector struct {
opts UnifiCollectorOpts
Client *client
// UAP *UAP
// USG *USG
// USW *USW
// UDM *UDM
// IDS *IDS
// Site *Site
}
type metricExports struct {
Desc *prometheus.Desc
ValueType prometheus.ValueType
Value interface{}
Labels []string
}
// NewUnifiCollector returns a prometheus collector that will export any available
// UniFi metrics. You must provide a collection function in the opts.
func NewUnifiCollector(opts UnifiCollectorOpts) prometheus.Collector {
if opts.CollectFn == nil {
panic("nil collector function")
}
return &unifiCollector{
opts: opts,
Client: descClient(opts.Namespace),
// UAP: descUAP(opts.Namespace),
// USG: descUSG(opts.Namespace),
// USW: descUSW(opts.Namespace),
// UDM: descUDM(opts.Namespace),
// Site: descSite(opts.Namespace),
// IDS: descIDS(opts.Namespace),
}
}
// Describe satisfies the prometheus Collector. This returns all of the
// metric descriptions that this packages produces.
func (u *unifiCollector) Describe(ch chan<- *prometheus.Desc) {
describe := func(from interface{}) {
v := reflect.ValueOf(from)
// Loop each struct member and send it to the provided channel.
for i := 0; i < v.NumField(); i++ {
desc, ok := v.Field(i).Interface().(*prometheus.Desc)
if ok && desc != nil {
ch <- desc
}
}
}
describe(u.Client)
// describe(u.UAP)
// describe(u.USG)
// describe(u.USW)
// describe(u.UDM)
// describe(u.Site)
// if u.opts.CollectIDS {
// describe(u.IDS)
// }
}
// Collect satisifes the prometheus Collector. This runs the input method to get
// the current metrics (from another package) then exports them for prometheus.
func (u *unifiCollector) Collect(ch chan<- prometheus.Metric) {
m := u.opts.CollectFn()
for _, asset := range m.Clients {
u.export(ch, u.exportClient(asset), m.TS)
}
for _, asset := range m.Sites {
u.export(ch, u.exportSite(asset), m.TS)
}
if u.opts.CollectIDS {
for _, asset := range m.IDSList {
u.export(ch, u.exportIDS(asset), m.TS)
}
}
if m.Devices == nil {
return
}
for _, asset := range m.Devices.UAPs {
u.export(ch, u.exportUAP(asset), m.TS)
}
for _, asset := range m.Devices.USGs {
u.export(ch, u.exportUSG(asset), m.TS)
}
for _, asset := range m.Devices.USWs {
u.export(ch, u.exportUSW(asset), m.TS)
}
for _, asset := range m.Devices.UDMs {
u.export(ch, u.exportUDM(asset), m.TS)
}
}
func (u *unifiCollector) export(ch chan<- prometheus.Metric, exports []*metricExports, ts time.Time) {
for _, e := range exports {
v, ok := e.Value.(float64)
if !ok {
if u.opts.ReportErrors {
ch <- prometheus.NewInvalidMetric(e.Desc, fmt.Errorf("not a number"))
}
return
}
ch <- prometheus.NewMetricWithTimestamp(ts, prometheus.MustNewConstMetric(e.Desc, e.ValueType, v, e.Labels...))
}
}