Add livebox_devices_total metric

This commit is contained in:
Tomy Guichard 2023-03-04 13:34:10 +01:00
parent 3b01929958
commit 5d2b77a38c
7 changed files with 239 additions and 72 deletions

View file

@ -0,0 +1,63 @@
package poller
import (
"context"
"fmt"
"github.com/Tomy2e/livebox-api-client"
"github.com/Tomy2e/livebox-api-client/api/request"
"github.com/prometheus/client_golang/prometheus"
)
var _ Poller = &DevicesTotal{}
// DevicesTotal allows to poll the total number of active devices.
type DevicesTotal struct {
client livebox.Client
devicesTotal *prometheus.GaugeVec
}
// NewDevicesTotal returns a new DevicesTotal poller.
func NewDevicesTotal(client livebox.Client) *DevicesTotal {
return &DevicesTotal{
client: client,
devicesTotal: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "livebox_devices_total",
Help: "The total number of active devices",
}, []string{
// Type of the device (dongle, ethernet, printer or wifi).
"type",
}),
}
}
// Collectors returns all metrics.
func (dt *DevicesTotal) Collectors() []prometheus.Collector {
return []prometheus.Collector{dt.devicesTotal}
}
// Poll polls the current number of active devices.
func (dt *DevicesTotal) Poll(ctx context.Context) error {
var devices struct {
Status map[string][]struct{} `json:"status"`
}
if err := dt.client.Request(ctx, request.New("Devices", "get", map[string]interface{}{
"expression": map[string]string{
"ethernet": "not interface and not self and eth and .Active==true",
"wifi": "not interface and not self and wifi and .Active==true",
"printer": "printer and .Active==true",
"dongle": "usb && wwan and .Active==true",
},
}), &devices); err != nil {
return fmt.Errorf("failed to get active devices: %w", err)
}
for t, d := range devices.Status {
dt.devicesTotal.
With(prometheus.Labels{"type": t}).
Set(float64(len(d)))
}
return nil
}

View file

@ -0,0 +1,97 @@
package poller
import (
"context"
"fmt"
"github.com/Tomy2e/livebox-api-client"
"github.com/Tomy2e/livebox-api-client/api/request"
"github.com/prometheus/client_golang/prometheus"
)
var _ Poller = &InterfaceMbits{}
// InterfaceMbits allows to poll the current bandwidth usage on the Livebox
// interfaces.
type InterfaceMbits struct {
client livebox.Client
txMbits, rxMbits *prometheus.GaugeVec
}
// NewInterfaceMbits returns a new InterfaceMbits poller.
func NewInterfaceMbits(client livebox.Client) *InterfaceMbits {
return &InterfaceMbits{
client: client,
txMbits: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "livebox_interface_tx_mbits",
Help: "Transmitted Mbits per second.",
}, []string{
// Name of the interface.
"interface",
}),
rxMbits: prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "livebox_interface_rx_mbits",
Help: "Received Mbits per second.",
}, []string{
// Name of the interface.
"interface",
}),
}
}
// Collectors returns all metrics.
func (im *InterfaceMbits) Collectors() []prometheus.Collector {
return []prometheus.Collector{im.txMbits, im.rxMbits}
}
func bitsPer30SecsToMbitsPerSec(v int) float64 {
return float64(v) / 30000000
}
// Poll polls the current bandwidth usage.
func (im *InterfaceMbits) Poll(ctx context.Context) error {
var counters struct {
Status map[string]struct {
Traffic []struct {
Timestamp int `json:"Timestamp"`
RxCounter int `json:"Rx_Counter"`
TxCounter int `json:"Tx_Counter"`
} `json:"Traffic"`
} `json:"status"`
}
// Request latest rx/tx counters.
if err := im.client.Request(
ctx,
request.New(
"HomeLan",
"getResults",
map[string]interface{}{
"Seconds": 0,
"NumberOfReadings": 1,
},
),
&counters,
); err != nil {
return fmt.Errorf("failed to get interfaces: %w", err)
}
for iface, traffic := range counters.Status {
rxCounter := 0
txCounter := 0
if len(traffic.Traffic) > 0 {
rxCounter = traffic.Traffic[0].RxCounter
txCounter = traffic.Traffic[0].TxCounter
}
im.rxMbits.
With(prometheus.Labels{"interface": iface}).
Set(bitsPer30SecsToMbitsPerSec(rxCounter))
im.txMbits.
With(prometheus.Labels{"interface": iface}).
Set(bitsPer30SecsToMbitsPerSec(txCounter))
}
return nil
}

41
internal/poller/poller.go Normal file
View file

@ -0,0 +1,41 @@
package poller
import (
"context"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/errgroup"
)
// Poller is an interface that allows polling a system and updating Prometheus
// metrics.
type Poller interface {
Poll(ctx context.Context) error
Collectors() []prometheus.Collector
}
// Pollers is a list of pollers.
type Pollers []Poller
// Collectors returns the collectors of all pollers.
func (p Pollers) Collectors() (c []prometheus.Collector) {
for _, poller := range p {
c = append(c, poller.Collectors()...)
}
return
}
// Poll runs all pollers in parallel.
func (p Pollers) Poll(ctx context.Context) error {
eg, ctx := errgroup.WithContext(ctx)
for _, poller := range p {
poller := poller
eg.Go(func() error {
return poller.Poll(ctx)
})
}
return eg.Wait()
}