147 lines
3.8 KiB
Go
147 lines
3.8 KiB
Go
package promunifi
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/davidnewhall/unifi-poller/metrics"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
// UnifiCollectorCnfg defines the data needed to collect and report UniFi Metrics.
|
|
type UnifiCollectorCnfg 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 {
|
|
Config UnifiCollectorCnfg
|
|
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 UnifiCollectorCnfg) prometheus.Collector {
|
|
if opts.CollectFn == nil {
|
|
panic("nil collector function")
|
|
}
|
|
|
|
return &unifiCollector{
|
|
Config: 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.Indirect(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.Config.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.Config.CollectFn()
|
|
if m == nil {
|
|
return
|
|
}
|
|
|
|
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.Config.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 {
|
|
j, ok := e.Value.(int64)
|
|
v = float64(j)
|
|
if !ok {
|
|
// log.Printf("not a number: %v %v", e.Value, e.Desc.String())
|
|
if u.Config.ReportErrors {
|
|
ch <- prometheus.NewInvalidMetric(e.Desc, fmt.Errorf("not a number: %v", e.Value))
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
ch <- prometheus.NewMetricWithTimestamp(ts, prometheus.MustNewConstMetric(e.Desc, e.ValueType, v, e.Labels...))
|
|
}
|
|
}
|