Skip to content

Commit

Permalink
Cleanup Exporter a bit
Browse files Browse the repository at this point in the history
- move unwieldy prom.MustNewConstMetric into a helper func
- move fetching UNMS data into a helper func
- reduce unnecessay allocation in intoDesc
- make API tokens configurable via environment
- run `go mod tidy`
  • Loading branch information
dmke committed Apr 9, 2022
1 parent b5021ae commit 8f20b7e
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 283 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,22 @@ Log verbosity level. Defaults to `info`. Use `debug` to get more details.
### UNMS API Tokens

- Config: `token`
- Env: `UNMS_EXPORTER_TOKEN`
- use a comma-separated list of `instance=token` values

Configures an API token per UNMS instance.

Example:

```yaml
token:
"my-unms-instance.example.org": "my token"
my-unms-instance.example.org: "my token"
unms.example.com: "token123"
```
```console
$ UNMS_EXPORTER_TOKEN="my-unms-instance.example.org=my token,unms.example.com=token123" \
unms-exporter
```

## Prometheus Scrape Setup
Expand Down Expand Up @@ -76,6 +84,7 @@ scrape_configs:
## Available Metrics
### Device wide
- `device_cpu`: CPU load average in percent
- `device_ram`: RAM usage in percent
- `device_enabled`: Indicating if device is enabled in UNMS
Expand Down
33 changes: 33 additions & 0 deletions exporter/device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package exporter

import (
"context"
"time"

"github.com/ffddorf/unms-exporter/client/devices"
"github.com/ffddorf/unms-exporter/models"
)

func (e *Exporter) fetchDeviceData() ([]*models.DeviceStatusOverview, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

params := &devices.GetDevicesParams{
WithInterfaces: &defaultWithInterfaces,
Context: ctx,
}
devicesResponse, err := e.api.Devices.GetDevices(params)
if err != nil {
return nil, err
}

data := make([]*models.DeviceStatusOverview, 0, len(devicesResponse.Payload))
for _, overview := range devicesResponse.Payload {
if overview.Identification == nil {
continue
}
data = append(data, overview)
}

return data, nil
}
75 changes: 36 additions & 39 deletions exporter/exporter.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package exporter

import (
"context"
"fmt"
"strconv"
"time"

"github.com/ffddorf/unms-exporter/client"
"github.com/ffddorf/unms-exporter/client/devices"
"github.com/ffddorf/unms-exporter/models"
openapi "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
Expand All @@ -33,7 +31,7 @@ var defaultLabels = []string{
}

func (s metricSpec) intoDesc(name string) *prom.Desc {
labels := make([]string, 0, len(s.labels)+2)
labels := make([]string, 0, len(s.labels)+len(defaultLabels))
labels = append(labels, defaultLabels...)
labels = append(labels, s.labels...)
return prom.NewDesc(namespace+"_"+name, s.help, labels, prom.Labels{})
Expand Down Expand Up @@ -99,7 +97,12 @@ func New(log logrus.FieldLogger, host string, token string) *Exporter {

im := newInternalMetrics()

return &Exporter{api, metrics, im, log}
return &Exporter{
api: api,
metrics: metrics,
im: im,
log: log,
}
}

func (e *Exporter) Describe(out chan<- *prom.Desc) {
Expand All @@ -112,8 +115,7 @@ func (e *Exporter) Describe(out chan<- *prom.Desc) {
func (e *Exporter) Collect(out chan<- prom.Metric) {
defer e.im.Collect(out)

err := e.collectImpl(out)
if err != nil {
if err := e.collectImpl(out); err != nil {
e.log.WithError(err).Warn("Metric collection failed")
e.im.errors.Inc()
} else {
Expand All @@ -139,24 +141,19 @@ func timeToGauge(ts strfmt.DateTime) float64 {
return float64(time.Time(ts).Unix())
}

var (
defaultWithInterfaces = true
)
var defaultWithInterfaces = true

func (e *Exporter) collectImpl(out chan<- prom.Metric) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
func (e *Exporter) newMetric(name string, typ prom.ValueType, val float64, labels ...string) prom.Metric {
return prom.MustNewConstMetric(e.metrics[name], typ, val, labels...)
}

params := &devices.GetDevicesParams{
WithInterfaces: &defaultWithInterfaces,
Context: ctx,
}
devices, err := e.api.Devices.GetDevices(params)
func (e *Exporter) collectImpl(out chan<- prom.Metric) error {
devices, err := e.fetchDeviceData()
if err != nil {
return err
}

for _, device := range devices.Payload {
for _, device := range devices {
if device.Identification == nil {
continue
}
Expand All @@ -181,18 +178,18 @@ func (e *Exporter) collectImpl(out chan<- prom.Metric) error {
siteName,
}

out <- prom.MustNewConstMetric(e.metrics["device_enabled"], prom.GaugeValue, boolToGauge(derefOrFalse(device.Enabled)), deviceLabels...)
out <- e.newMetric("device_enabled", prom.GaugeValue, boolToGauge(derefOrFalse(device.Enabled)), deviceLabels...)
if device.Meta != nil {
out <- prom.MustNewConstMetric(e.metrics["device_maintenance"], prom.GaugeValue, boolToGauge(derefOrFalse(device.Meta.Maintenance)), deviceLabels...)
out <- e.newMetric("device_maintenance", prom.GaugeValue, boolToGauge(derefOrFalse(device.Meta.Maintenance)), deviceLabels...)
}
if device.Overview != nil {
out <- prom.MustNewConstMetric(e.metrics["device_cpu"], prom.GaugeValue, device.Overview.CPU, deviceLabels...)
out <- prom.MustNewConstMetric(e.metrics["device_ram"], prom.GaugeValue, device.Overview.RAM, deviceLabels...)
out <- prom.MustNewConstMetric(e.metrics["device_uptime"], prom.GaugeValue, device.Overview.Uptime, deviceLabels...)
out <- prom.MustNewConstMetric(e.metrics["device_last_seen"], prom.CounterValue, timeToGauge(device.Overview.LastSeen), deviceLabels...)
out <- e.newMetric("device_cpu", prom.GaugeValue, device.Overview.CPU, deviceLabels...)
out <- e.newMetric("device_ram", prom.GaugeValue, device.Overview.RAM, deviceLabels...)
out <- e.newMetric("device_uptime", prom.GaugeValue, device.Overview.Uptime, deviceLabels...)
out <- e.newMetric("device_last_seen", prom.CounterValue, timeToGauge(device.Overview.LastSeen), deviceLabels...)
}
if device.LatestBackup != nil && device.LatestBackup.Timestamp != nil {
out <- prom.MustNewConstMetric(e.metrics["device_last_backup"], prom.GaugeValue, timeToGauge(*device.LatestBackup.Timestamp), deviceLabels...)
out <- e.newMetric("device_last_backup", prom.GaugeValue, timeToGauge(*device.LatestBackup.Timestamp), deviceLabels...)
}

seenInterfaces := make(map[string]struct{})
Expand Down Expand Up @@ -223,29 +220,29 @@ func (e *Exporter) collectImpl(out chan<- prom.Metric) error {
intf.Identification.Type, // ifType
)

out <- prom.MustNewConstMetric(e.metrics["interface_enabled"], prom.GaugeValue, boolToGauge(intf.Enabled), intfLabels...)
out <- e.newMetric("interface_enabled", prom.GaugeValue, boolToGauge(intf.Enabled), intfLabels...)
if intf.Status != nil {
out <- prom.MustNewConstMetric(e.metrics["interface_plugged"], prom.GaugeValue, boolToGauge(intf.Status.Plugged), intfLabels...)
out <- prom.MustNewConstMetric(e.metrics["interface_up"], prom.GaugeValue, boolToGauge(intf.Status.Status == "active"), intfLabels...)
out <- e.newMetric("interface_plugged", prom.GaugeValue, boolToGauge(intf.Status.Plugged), intfLabels...)
out <- e.newMetric("interface_up", prom.GaugeValue, boolToGauge(intf.Status.Status == "active"), intfLabels...)
}

if intf.Statistics != nil {
out <- prom.MustNewConstMetric(e.metrics["interface_dropped"], prom.CounterValue, intf.Statistics.Dropped, intfLabels...)
out <- prom.MustNewConstMetric(e.metrics["interface_errors"], prom.CounterValue, intf.Statistics.Errors, intfLabels...)
out <- prom.MustNewConstMetric(e.metrics["interface_rx_bytes"], prom.CounterValue, intf.Statistics.Rxbytes, intfLabels...)
out <- prom.MustNewConstMetric(e.metrics["interface_tx_bytes"], prom.CounterValue, intf.Statistics.Txbytes, intfLabels...)
out <- prom.MustNewConstMetric(e.metrics["interface_rx_rate"], prom.GaugeValue, intf.Statistics.Rxrate, intfLabels...)
out <- prom.MustNewConstMetric(e.metrics["interface_tx_rate"], prom.GaugeValue, intf.Statistics.Txrate, intfLabels...)
out <- prom.MustNewConstMetric(e.metrics["interface_poe_power"], prom.GaugeValue, intf.Statistics.PoePower, intfLabels...)
out <- e.newMetric("interface_dropped", prom.CounterValue, intf.Statistics.Dropped, intfLabels...)
out <- e.newMetric("interface_errors", prom.CounterValue, intf.Statistics.Errors, intfLabels...)
out <- e.newMetric("interface_rx_bytes", prom.CounterValue, intf.Statistics.Rxbytes, intfLabels...)
out <- e.newMetric("interface_tx_bytes", prom.CounterValue, intf.Statistics.Txbytes, intfLabels...)
out <- e.newMetric("interface_rx_rate", prom.GaugeValue, intf.Statistics.Rxrate, intfLabels...)
out <- e.newMetric("interface_tx_rate", prom.GaugeValue, intf.Statistics.Txrate, intfLabels...)
out <- e.newMetric("interface_poe_power", prom.GaugeValue, intf.Statistics.PoePower, intfLabels...)
}
}

// WAN metrics
if wanIF != nil && wanIF.Statistics != nil {
out <- prom.MustNewConstMetric(e.metrics["wan_rx_bytes"], prom.CounterValue, wanIF.Statistics.Rxbytes, deviceLabels...)
out <- prom.MustNewConstMetric(e.metrics["wan_tx_bytes"], prom.CounterValue, wanIF.Statistics.Txbytes, deviceLabels...)
out <- prom.MustNewConstMetric(e.metrics["wan_rx_rate"], prom.GaugeValue, wanIF.Statistics.Rxrate, deviceLabels...)
out <- prom.MustNewConstMetric(e.metrics["wan_tx_rate"], prom.GaugeValue, wanIF.Statistics.Txrate, deviceLabels...)
out <- e.newMetric("wan_rx_bytes", prom.CounterValue, wanIF.Statistics.Rxbytes, deviceLabels...)
out <- e.newMetric("wan_tx_bytes", prom.CounterValue, wanIF.Statistics.Txbytes, deviceLabels...)
out <- e.newMetric("wan_rx_rate", prom.GaugeValue, wanIF.Statistics.Rxrate, deviceLabels...)
out <- e.newMetric("wan_tx_rate", prom.GaugeValue, wanIF.Statistics.Txrate, deviceLabels...)
}
}

Expand Down
Loading

0 comments on commit 8f20b7e

Please sign in to comment.