livebox-exporter/main.go
2023-05-03 21:17:27 +02:00

155 lines
4 KiB
Go

package main
import (
"context"
"errors"
"flag"
"fmt"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/Tomy2e/livebox-api-client"
"github.com/Tomy2e/livebox-exporter/internal/collector"
"github.com/Tomy2e/livebox-exporter/internal/poller"
exporterLivebox "github.com/Tomy2e/livebox-exporter/pkg/livebox"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/exp/slices"
)
const defaultPollingFrequency = 30
const (
ExperimentalMetricsInterfaceHomeLan = "livebox_interface_homelan"
ExperimentalMetricsInterfaceNetDev = "livebox_interface_netdev"
ExperimentalMetricsWAN = "livebox_wan"
)
var experimentalMetrics = []string{
ExperimentalMetricsInterfaceHomeLan,
ExperimentalMetricsInterfaceNetDev,
ExperimentalMetricsWAN,
}
func parseExperimentalFlag(
ctx context.Context,
client livebox.Client,
experimental string,
pollingFrequency *uint,
) (pollers []poller.Poller) {
var (
interfaces []*exporterLivebox.Interface
err error
enabled = make(map[string]bool)
)
for _, exp := range strings.Split(experimental, ",") {
exp = strings.TrimSpace(exp)
if !slices.Contains(experimentalMetrics, exp) {
log.Printf("WARN: Unknown experimental metrics: %s", exp)
continue
}
if enabled[exp] {
continue
}
// Discover interfaces for experimental pollers that require interfaces.
switch exp {
case ExperimentalMetricsInterfaceHomeLan, ExperimentalMetricsInterfaceNetDev:
if interfaces == nil {
interfaces, err = exporterLivebox.DiscoverInterfaces(ctx, client)
if err != nil {
log.Fatalf("Failed to discover Livebox interfaces: %s\n", err)
}
}
}
switch exp {
case ExperimentalMetricsInterfaceHomeLan:
pollers = append(pollers, poller.NewInterfaceHomeLanMbits(client, interfaces))
case ExperimentalMetricsInterfaceNetDev:
pollers = append(pollers, poller.NewInterfaceNetDevMbits(client, interfaces))
if *pollingFrequency > 5 {
log.Printf(
"WARN: The %s experimental metrics require a lower polling frequency, "+
"setting polling frequency to 5 seconds\n",
ExperimentalMetricsInterfaceNetDev,
)
*pollingFrequency = 5
}
case ExperimentalMetricsWAN:
pollers = append(pollers, poller.NewWANMbits(client))
}
log.Printf("INFO: enabled experimental metrics: %s\n", exp)
enabled[exp] = true
}
return
}
func main() {
pollingFrequency := flag.Uint("polling-frequency", defaultPollingFrequency, "Polling frequency")
listen := flag.String("listen", ":8080", "Listening address")
experimental := flag.String("experimental", "", fmt.Sprintf(
"Comma separated list of experimental metrics to enable (available metrics: %s)",
strings.Join(experimentalMetrics, ","),
))
flag.Parse()
adminPassword := os.Getenv("ADMIN_PASSWORD")
if adminPassword == "" {
log.Fatal("ADMIN_PASSWORD environment variable must be set")
}
var (
ctx = context.Background()
registry = prometheus.NewRegistry()
client = livebox.NewClient(adminPassword)
pollers = poller.Pollers{
poller.NewDevicesTotal(client),
poller.NewInterfaceMbits(client),
}
)
// Add experimental pollers.
pollers = append(pollers, parseExperimentalFlag(ctx, client, *experimental, pollingFrequency)...)
registry.MustRegister(
append(
pollers.Collectors(),
collectors.NewGoCollector(),
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
)...,
)
registry.MustRegister(collector.NewDeviceInfo(client))
go func() {
for {
if err := pollers.Poll(ctx); err != nil {
if errors.Is(err, livebox.ErrInvalidPassword) {
log.Fatal(err)
}
log.Printf("WARN: polling failed: %s\n", err)
}
time.Sleep(time.Duration(*pollingFrequency) * time.Second)
}
}()
http.Handle("/metrics", promhttp.InstrumentMetricHandler(
registry, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}),
))
log.Printf("Listening on %s\n", *listen)
log.Fatal(http.ListenAndServe(*listen, nil))
}