new files
This commit is contained in:
parent
2fc90393de
commit
493f687793
|
|
@ -0,0 +1,48 @@
|
||||||
|
package poller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/davidnewhall/unifi-poller/influxunifi"
|
||||||
|
"github.com/davidnewhall/unifi-poller/metrics"
|
||||||
|
client "github.com/influxdata/influxdb1-client/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReportMetrics batches all the metrics and writes them to InfluxDB.
|
||||||
|
// This creates an InfluxDB writer, and returns an error if the write fails.
|
||||||
|
func (u *UnifiPoller) ReportMetrics(metrics *metrics.Metrics) error {
|
||||||
|
// Batch (and send) all the points.
|
||||||
|
m := &influxunifi.Metrics{Metrics: metrics}
|
||||||
|
// Make a new Influx Points Batcher.
|
||||||
|
var err error
|
||||||
|
m.BatchPoints, err = client.NewBatchPoints(client.BatchPointsConfig{Database: u.Config.InfluxDB})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("influx.NewBatchPoints: %v", err)
|
||||||
|
}
|
||||||
|
for _, err := range m.ProcessPoints() {
|
||||||
|
u.LogError(err, "influx.ProcessPoints")
|
||||||
|
}
|
||||||
|
if err = u.Influx.Write(m.BatchPoints); err != nil {
|
||||||
|
return fmt.Errorf("influxdb.Write(points): %v", err)
|
||||||
|
}
|
||||||
|
u.LogInfluxReport(m)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogInfluxReport writes a log message after exporting to influxdb.
|
||||||
|
func (u *UnifiPoller) LogInfluxReport(m *influxunifi.Metrics) {
|
||||||
|
var fields, points int
|
||||||
|
for _, p := range m.Points() {
|
||||||
|
points++
|
||||||
|
i, _ := p.Fields()
|
||||||
|
fields += len(i)
|
||||||
|
}
|
||||||
|
idsMsg := ""
|
||||||
|
if u.Config.CollectIDS {
|
||||||
|
idsMsg = fmt.Sprintf("IDS Events: %d, ", len(m.IDSList))
|
||||||
|
}
|
||||||
|
u.Logf("UniFi Measurements Recorded. Sites: %d, Clients: %d, "+
|
||||||
|
"Wireless APs: %d, Gateways: %d, Switches: %d, %sPoints: %d, Fields: %d",
|
||||||
|
len(m.Sites), len(m.Clients), len(m.UAPs),
|
||||||
|
len(m.UDMs)+len(m.USGs), len(m.USWs), idsMsg, points, fields)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package poller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/davidnewhall/unifi-poller/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExportMetrics updates the internal metrics provided via
|
||||||
|
// HTTP at /metrics for prometheus collection. This is run by Prometheus.
|
||||||
|
func (u *UnifiPoller) ExportMetrics() *metrics.Metrics {
|
||||||
|
if u.Config.ReAuth {
|
||||||
|
u.LogDebugf("Re-authenticating to UniFi Controller")
|
||||||
|
// Some users need to re-auth every interval because the cookie times out.
|
||||||
|
if err := u.Unifi.Login(); err != nil {
|
||||||
|
u.LogError(err, "re-authenticating")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.LastCheck = time.Now()
|
||||||
|
m, err := u.CollectMetrics()
|
||||||
|
if err != nil {
|
||||||
|
u.LogErrorf("collecting metrics: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
u.AugmentMetrics(m)
|
||||||
|
|
||||||
|
idsMsg := ""
|
||||||
|
if u.Config.CollectIDS {
|
||||||
|
idsMsg = fmt.Sprintf(", IDS Events: %d, ", len(m.IDSList))
|
||||||
|
}
|
||||||
|
u.Logf("UniFi Measurements Exported. Sites: %d, Clients: %d, "+
|
||||||
|
"Wireless APs: %d, Gateways: %d, Switches: %d%s",
|
||||||
|
len(m.Sites), len(m.Clients), len(m.UAPs),
|
||||||
|
len(m.UDMs)+len(m.USGs), len(m.USWs), idsMsg)
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/davidnewhall/unifi-poller/promunifi"
|
"github.com/davidnewhall/unifi-poller/promunifi"
|
||||||
client "github.com/influxdata/influxdb1-client/v2"
|
influx "github.com/influxdata/influxdb1-client/v2"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
@ -100,7 +100,7 @@ func (u *UnifiPoller) Run() (err error) {
|
||||||
case "prometheus", "exporter":
|
case "prometheus", "exporter":
|
||||||
u.Logf("Exporting Measurements at https://%s/metrics for Prometheus", u.Config.HTTPListen)
|
u.Logf("Exporting Measurements at https://%s/metrics for Prometheus", u.Config.HTTPListen)
|
||||||
http.Handle("/metrics", promhttp.Handler())
|
http.Handle("/metrics", promhttp.Handler())
|
||||||
prometheus.MustRegister(promunifi.NewUnifiCollector(promunifi.UnifiCollectorOpts{
|
prometheus.MustRegister(promunifi.NewUnifiCollector(promunifi.UnifiCollectorCnfg{
|
||||||
Namespace: "unifi",
|
Namespace: "unifi",
|
||||||
CollectFn: u.ExportMetrics,
|
CollectFn: u.ExportMetrics,
|
||||||
ReportErrors: true,
|
ReportErrors: true,
|
||||||
|
|
@ -121,7 +121,7 @@ func (u *UnifiPoller) Run() (err error) {
|
||||||
|
|
||||||
// GetInfluxDB returns an InfluxDB interface.
|
// GetInfluxDB returns an InfluxDB interface.
|
||||||
func (u *UnifiPoller) GetInfluxDB() (err error) {
|
func (u *UnifiPoller) GetInfluxDB() (err error) {
|
||||||
u.Influx, err = client.NewHTTPClient(client.HTTPConfig{
|
u.Influx, err = influx.NewHTTPClient(influx.HTTPConfig{
|
||||||
Addr: u.Config.InfluxURL,
|
Addr: u.Config.InfluxURL,
|
||||||
Username: u.Config.InfluxUser,
|
Username: u.Config.InfluxUser,
|
||||||
Password: u.Config.InfluxPass,
|
Password: u.Config.InfluxPass,
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,37 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/davidnewhall/unifi-poller/influxunifi"
|
|
||||||
"github.com/davidnewhall/unifi-poller/metrics"
|
"github.com/davidnewhall/unifi-poller/metrics"
|
||||||
client "github.com/influxdata/influxdb1-client/v2"
|
|
||||||
"golift.io/unifi"
|
"golift.io/unifi"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PollController runs forever, polling UniFi
|
||||||
|
// and pushing to influx OR exporting for prometheus.
|
||||||
|
// This is started by Run() after everything checks out.
|
||||||
|
func (u *UnifiPoller) PollController(process func(*metrics.Metrics) error) error {
|
||||||
|
interval := u.Config.Interval.Round(time.Second)
|
||||||
|
log.Printf("[INFO] Everything checks out! Poller started in %v mode, interval: %v", u.Config.Mode, interval)
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
for u.LastCheck = range ticker.C {
|
||||||
|
var err error
|
||||||
|
if u.Config.ReAuth {
|
||||||
|
u.LogDebugf("Re-authenticating to UniFi Controller")
|
||||||
|
// Some users need to re-auth every interval because the cookie times out.
|
||||||
|
if err = u.Unifi.Login(); err != nil {
|
||||||
|
u.LogError(err, "re-authenticating")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// Only run this if the authentication procedure didn't return error.
|
||||||
|
_ = u.CollectAndProcess(process)
|
||||||
|
}
|
||||||
|
if u.errorCount > 0 {
|
||||||
|
return fmt.Errorf("too many errors, stopping poller")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CheckSites makes sure the list of provided sites exists on the controller.
|
// CheckSites makes sure the list of provided sites exists on the controller.
|
||||||
// This does not run in Lambda (run-once) mode.
|
// This does not run in Lambda (run-once) mode.
|
||||||
func (u *UnifiPoller) CheckSites() error {
|
func (u *UnifiPoller) CheckSites() error {
|
||||||
|
|
@ -45,33 +70,6 @@ FIRST:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PollController runs forever, polling UniFi
|
|
||||||
// and pushing to influx OR exporting for prometheus.
|
|
||||||
// This is started by Run() after everything checks out.
|
|
||||||
func (u *UnifiPoller) PollController(process func(*metrics.Metrics) error) error {
|
|
||||||
interval := u.Config.Interval.Round(time.Second)
|
|
||||||
log.Printf("[INFO] Everything checks out! Poller started in %v mode, interval: %v", u.Config.Mode, interval)
|
|
||||||
ticker := time.NewTicker(interval)
|
|
||||||
for u.LastCheck = range ticker.C {
|
|
||||||
var err error
|
|
||||||
if u.Config.ReAuth {
|
|
||||||
u.LogDebugf("Re-authenticating to UniFi Controller")
|
|
||||||
// Some users need to re-auth every interval because the cookie times out.
|
|
||||||
if err = u.Unifi.Login(); err != nil {
|
|
||||||
u.LogError(err, "re-authenticating")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
// Only run this if the authentication procedure didn't return error.
|
|
||||||
_ = u.CollectAndProcess(process)
|
|
||||||
}
|
|
||||||
if u.errorCount > 0 {
|
|
||||||
return fmt.Errorf("too many errors, stopping poller")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CollectAndProcess collects measurements and then passese them into the
|
// CollectAndProcess collects measurements and then passese them into the
|
||||||
// provided method. The method is either an http exporter or an influxdb update.
|
// provided method. The method is either an http exporter or an influxdb update.
|
||||||
// Can be called once or in a ticker loop. This function and all the ones below
|
// Can be called once or in a ticker loop. This function and all the ones below
|
||||||
|
|
@ -89,37 +87,6 @@ func (u *UnifiPoller) CollectAndProcess(process func(*metrics.Metrics) error) er
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportMetrics updates the internal metrics provided via
|
|
||||||
// HTTP at /metrics for prometheus collection. This is run by Prometheus.
|
|
||||||
func (u *UnifiPoller) ExportMetrics() *metrics.Metrics {
|
|
||||||
if u.Config.ReAuth {
|
|
||||||
u.LogDebugf("Re-authenticating to UniFi Controller")
|
|
||||||
// Some users need to re-auth every interval because the cookie times out.
|
|
||||||
if err := u.Unifi.Login(); err != nil {
|
|
||||||
u.LogError(err, "re-authenticating")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
u.LastCheck = time.Now()
|
|
||||||
m, err := u.CollectMetrics()
|
|
||||||
if err != nil {
|
|
||||||
u.LogErrorf("collecting metrics: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
u.AugmentMetrics(m)
|
|
||||||
|
|
||||||
idsMsg := ""
|
|
||||||
if u.Config.CollectIDS {
|
|
||||||
idsMsg = fmt.Sprintf(", IDS Events: %d, ", len(m.IDSList))
|
|
||||||
}
|
|
||||||
u.Logf("UniFi Measurements Exported. Sites: %d, Clients: %d, "+
|
|
||||||
"Wireless APs: %d, Gateways: %d, Switches: %d%s",
|
|
||||||
len(m.Sites), len(m.Clients), len(m.UAPs),
|
|
||||||
len(m.UDMs)+len(m.USGs), len(m.USWs), idsMsg)
|
|
||||||
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// CollectMetrics grabs all the measurements from a UniFi controller and returns them.
|
// CollectMetrics grabs all the measurements from a UniFi controller and returns them.
|
||||||
func (u *UnifiPoller) CollectMetrics() (*metrics.Metrics, error) {
|
func (u *UnifiPoller) CollectMetrics() (*metrics.Metrics, error) {
|
||||||
m := &metrics.Metrics{TS: u.LastCheck} // At this point, it's the Current Check.
|
m := &metrics.Metrics{TS: u.LastCheck} // At this point, it's the Current Check.
|
||||||
|
|
@ -173,45 +140,6 @@ func (u *UnifiPoller) AugmentMetrics(metrics *metrics.Metrics) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReportMetrics batches all the metrics and writes them to InfluxDB.
|
|
||||||
// This creates an InfluxDB writer, and returns an error if the write fails.
|
|
||||||
func (u *UnifiPoller) ReportMetrics(metrics *metrics.Metrics) error {
|
|
||||||
// Batch (and send) all the points.
|
|
||||||
m := &influxunifi.Metrics{Metrics: metrics}
|
|
||||||
// Make a new Influx Points Batcher.
|
|
||||||
var err error
|
|
||||||
m.BatchPoints, err = client.NewBatchPoints(client.BatchPointsConfig{Database: u.Config.InfluxDB})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("influx.NewBatchPoints: %v", err)
|
|
||||||
}
|
|
||||||
for _, err := range m.ProcessPoints() {
|
|
||||||
u.LogError(err, "influx.ProcessPoints")
|
|
||||||
}
|
|
||||||
if err = u.Influx.Write(m.BatchPoints); err != nil {
|
|
||||||
return fmt.Errorf("influxdb.Write(points): %v", err)
|
|
||||||
}
|
|
||||||
u.LogInfluxReport(m)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogInfluxReport writes a log message after exporting to influxdb.
|
|
||||||
func (u *UnifiPoller) LogInfluxReport(m *influxunifi.Metrics) {
|
|
||||||
var fields, points int
|
|
||||||
for _, p := range m.Points() {
|
|
||||||
points++
|
|
||||||
i, _ := p.Fields()
|
|
||||||
fields += len(i)
|
|
||||||
}
|
|
||||||
idsMsg := ""
|
|
||||||
if u.Config.CollectIDS {
|
|
||||||
idsMsg = fmt.Sprintf("IDS Events: %d, ", len(m.IDSList))
|
|
||||||
}
|
|
||||||
u.Logf("UniFi Measurements Recorded. Sites: %d, Clients: %d, "+
|
|
||||||
"Wireless APs: %d, Gateways: %d, Switches: %d, %sPoints: %d, Fields: %d",
|
|
||||||
len(m.Sites), len(m.Clients), len(m.UAPs),
|
|
||||||
len(m.UDMs)+len(m.USGs), len(m.USWs), idsMsg, points, fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFilteredSites returns a list of sites to fetch data for.
|
// GetFilteredSites returns a list of sites to fetch data for.
|
||||||
// Omits requested but unconfigured sites. Grabs the full list from the
|
// Omits requested but unconfigured sites. Grabs the full list from the
|
||||||
// controller and returns the sites provided in the config file.
|
// controller and returns the sites provided in the config file.
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UnifiCollectorOpts defines the data needed to collect and report UniFi Metrics.
|
// UnifiCollectorCnfg defines the data needed to collect and report UniFi Metrics.
|
||||||
type UnifiCollectorOpts struct {
|
type UnifiCollectorCnfg struct {
|
||||||
// If non-empty, each of the collected metrics is prefixed by the
|
// If non-empty, each of the collected metrics is prefixed by the
|
||||||
// provided string and an underscore ("_").
|
// provided string and an underscore ("_").
|
||||||
Namespace string
|
Namespace string
|
||||||
|
|
@ -27,7 +27,7 @@ type UnifiCollectorOpts struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type unifiCollector struct {
|
type unifiCollector struct {
|
||||||
opts UnifiCollectorOpts
|
Config UnifiCollectorCnfg
|
||||||
Client *client
|
Client *client
|
||||||
UAP *uap
|
UAP *uap
|
||||||
USG *usg
|
USG *usg
|
||||||
|
|
@ -46,13 +46,13 @@ type metricExports struct {
|
||||||
|
|
||||||
// NewUnifiCollector returns a prometheus collector that will export any available
|
// NewUnifiCollector returns a prometheus collector that will export any available
|
||||||
// UniFi metrics. You must provide a collection function in the opts.
|
// UniFi metrics. You must provide a collection function in the opts.
|
||||||
func NewUnifiCollector(opts UnifiCollectorOpts) prometheus.Collector {
|
func NewUnifiCollector(opts UnifiCollectorCnfg) prometheus.Collector {
|
||||||
if opts.CollectFn == nil {
|
if opts.CollectFn == nil {
|
||||||
panic("nil collector function")
|
panic("nil collector function")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &unifiCollector{
|
return &unifiCollector{
|
||||||
opts: opts,
|
Config: opts,
|
||||||
Client: descClient(opts.Namespace),
|
Client: descClient(opts.Namespace),
|
||||||
UAP: descUAP(opts.Namespace),
|
UAP: descUAP(opts.Namespace),
|
||||||
USG: descUSG(opts.Namespace),
|
USG: descUSG(opts.Namespace),
|
||||||
|
|
@ -84,7 +84,7 @@ func (u *unifiCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||||
describe(u.USW)
|
describe(u.USW)
|
||||||
describe(u.UDM)
|
describe(u.UDM)
|
||||||
describe(u.Site)
|
describe(u.Site)
|
||||||
if u.opts.CollectIDS {
|
if u.Config.CollectIDS {
|
||||||
describe(u.IDS)
|
describe(u.IDS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +92,7 @@ func (u *unifiCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||||
// Collect satisifes the prometheus Collector. This runs the input method to get
|
// Collect satisifes the prometheus Collector. This runs the input method to get
|
||||||
// the current metrics (from another package) then exports them for prometheus.
|
// the current metrics (from another package) then exports them for prometheus.
|
||||||
func (u *unifiCollector) Collect(ch chan<- prometheus.Metric) {
|
func (u *unifiCollector) Collect(ch chan<- prometheus.Metric) {
|
||||||
m := u.opts.CollectFn()
|
m := u.Config.CollectFn()
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +103,7 @@ func (u *unifiCollector) Collect(ch chan<- prometheus.Metric) {
|
||||||
for _, asset := range m.Sites {
|
for _, asset := range m.Sites {
|
||||||
u.export(ch, u.exportSite(asset), m.TS)
|
u.export(ch, u.exportSite(asset), m.TS)
|
||||||
}
|
}
|
||||||
if u.opts.CollectIDS {
|
if u.Config.CollectIDS {
|
||||||
for _, asset := range m.IDSList {
|
for _, asset := range m.IDSList {
|
||||||
u.export(ch, u.exportIDS(asset), m.TS)
|
u.export(ch, u.exportIDS(asset), m.TS)
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +131,7 @@ func (u *unifiCollector) export(ch chan<- prometheus.Metric, exports []*metricEx
|
||||||
for _, e := range exports {
|
for _, e := range exports {
|
||||||
v, ok := e.Value.(float64)
|
v, ok := e.Value.(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
if u.opts.ReportErrors {
|
if u.Config.ReportErrors {
|
||||||
ch <- prometheus.NewInvalidMetric(e.Desc, fmt.Errorf("not a number"))
|
ch <- prometheus.NewInvalidMetric(e.Desc, fmt.Errorf("not a number"))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue