freebox-exporter/main.go

323 lines
9.9 KiB
Go
Raw Normal View History

2018-09-05 03:08:48 +02:00
package main
import (
"encoding/json"
2018-09-06 01:06:16 +02:00
"errors"
"flag"
2018-09-05 03:08:48 +02:00
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"sync"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/trazfr/freebox-exporter/fbx"
"github.com/trazfr/freebox-exporter/log"
)
const (
namespace = "freebox"
)
var (
info = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: namespace,
Name: "info",
Help: "A constant metric with value=0. Various information about the Freebox",
}, []string{"firmware", "mac", "serial", "boardname", "box_flavor", "state", "media", "ipv4", "ipv6"})
collectorSystemUptimeValue = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "system_uptime",
Help: "freebox uptime (in seconds)",
})
collectorVecSystemTemp = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "system_temp_degrees",
Help: "temperature (°C)",
}, []string{"probe"})
collectorSystemTempCpum = collectorVecSystemTemp.WithLabelValues("cpum")
collectorSystemTempCpub = collectorVecSystemTemp.WithLabelValues("cpub")
collectorSystemTempSw = collectorVecSystemTemp.WithLabelValues("sw")
collectorSystemFanRpm = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "system_fan_rpm",
Help: "fan rpm",
})
collectorVecConnectionRate = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "connection_rate",
Help: "current upload/download rate in byte/s",
}, []string{"dir"})
collectorConnectionRateUp = collectorVecConnectionRate.WithLabelValues("up")
collectorConnectionRateDown = collectorVecConnectionRate.WithLabelValues("down")
collectorVecConnectionBandwith = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "connection_bandwith",
Help: " available upload/download bandwidth in bit/s",
}, []string{"dir"})
collectorConnectionBandwithUp = collectorVecConnectionBandwith.WithLabelValues("up")
collectorConnectionBandwithDown = collectorVecConnectionBandwith.WithLabelValues("down")
collectorVecConnectionBytes = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "connection_bytes",
Help: "total uploaded/downloaded bytes since last connection",
}, []string{"dir"})
collectorConnectionBytesUp = collectorVecConnectionBytes.WithLabelValues("up")
collectorConnectionBytesDown = collectorVecConnectionBytes.WithLabelValues("down")
collectorVecConnectionUptime = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "connection_uptime",
Help: "Uptime in seconds",
}, []string{"media"})
collectorConnectionUptimeXdsl = collectorVecConnectionUptime.WithLabelValues("xdsl")
collectorVecConnectionXdslMaxrate = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "connection_xdsl_maxrate_kbps",
Help: "ATM max rate in kbit/s",
}, []string{"dir"})
collectorConnectionXdslMaxrateUp = collectorVecConnectionXdslMaxrate.WithLabelValues("up")
collectorConnectionXdslMaxrateDown = collectorVecConnectionXdslMaxrate.WithLabelValues("down")
collectorVecConnectionXdslRate = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "connection_xdsl_rate_kbps",
Help: "ATM rate in kbit/s",
}, []string{"dir"})
collectorConnectionXdslRateUp = collectorVecConnectionXdslRate.WithLabelValues("up")
collectorConnectionXdslRateDown = collectorVecConnectionXdslRate.WithLabelValues("down")
collectorVecConnectionXdslSnr = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "connection_xdsl_snr_db",
Help: "in Db",
}, []string{"dir"})
collectorConnectionXdslSnrUp = collectorVecConnectionXdslSnr.WithLabelValues("up")
collectorConnectionXdslSnrDown = collectorVecConnectionXdslSnr.WithLabelValues("down")
collectorVecConnectionXdslAttn = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "connection_xdsl_attn_db",
Help: "in Db",
}, []string{"dir"})
collectorConnectionXdslAttnUp = collectorVecConnectionXdslAttn.WithLabelValues("up")
collectorConnectionXdslAttnDown = collectorVecConnectionXdslAttn.WithLabelValues("down")
)
type context struct {
client *http.Client
freebox *fbx.FreeboxConnection
used map[prometheus.Metric]bool
}
func (c *context) Describe(ch chan<- *prometheus.Desc) {
log.Debug.Println("Describe")
ch2 := make(chan prometheus.Metric)
go func() {
c.Collect(ch2)
close(ch2)
}()
metrics := make([]prometheus.Metric, 16)
for v := range ch2 {
metrics = append(metrics, v)
ch <- v.Desc()
}
for _, v := range metrics {
c.used[v] = true
}
}
func (c *context) Collect(ch chan<- prometheus.Metric) {
log.Debug.Println("Collect")
wg := sync.WaitGroup{}
wg.Add(2)
var firmwareVersion string
var mac string
var serial string
var boardName string
var boxFlavor string
go func() {
defer wg.Done()
log.Debug.Println("Collect system")
if m, err := c.freebox.GetMetricsSystem(); err == nil {
firmwareVersion = m.FirmwareVersion
mac = m.Mac
serial = m.Serial
boardName = m.BoardName
boxFlavor = m.BoxFlavor
c.collectGauge(ch, collectorSystemUptimeValue, m.UptimeValue)
c.collectGauge(ch, collectorSystemTempCpum, m.TempCPUM)
c.collectGauge(ch, collectorSystemTempCpub, m.TempCPUB)
c.collectGauge(ch, collectorSystemTempSw, m.TempSW)
c.collectGauge(ch, collectorSystemFanRpm, m.FanRpm)
} else {
log.Info.Println(err)
}
}()
var cnxState string
var cnxMedia string
var cnxIPv4 string
var cnxIPv6 string
go func() {
defer wg.Done()
log.Debug.Println("Collect connection")
if m, err := c.freebox.GetMetricsConnection(); err == nil {
cnxState = m.State
cnxMedia = m.Media
cnxIPv4 = m.IPv4
cnxIPv6 = m.IPv6
c.collectGauge(ch, collectorConnectionRateUp, m.RateUp)
c.collectGauge(ch, collectorConnectionRateDown, m.RateDown)
c.collectGauge(ch, collectorConnectionBandwithUp, m.BandwithUp)
c.collectGauge(ch, collectorConnectionBandwithDown, m.BandwithDown)
c.collectGauge(ch, collectorConnectionBytesUp, m.BytesUp)
c.collectGauge(ch, collectorConnectionBytesDown, m.BytesDown)
if m.Xdsl != nil {
if m.Xdsl.Status != nil {
c.collectGauge(ch, collectorConnectionUptimeXdsl, m.Xdsl.Status.Uptime)
}
if m.Xdsl.Up != nil {
x := m.Xdsl.Up
c.collectGauge(ch, collectorConnectionXdslMaxrateUp, x.Maxrate)
c.collectGauge(ch, collectorConnectionXdslRateUp, x.Rate)
if c.use(collectorConnectionXdslSnrUp) && x.Snr10 != nil {
collectorConnectionXdslSnrUp.Set(float64(*x.Snr10) / 10)
collectorConnectionXdslSnrUp.Collect(ch)
} else {
c.collectGauge(ch, collectorConnectionXdslSnrUp, x.Snr)
}
if c.use(collectorConnectionXdslAttnUp) && x.Attn10 != nil {
collectorConnectionXdslAttnUp.Set(float64(*x.Attn10) / 10)
collectorConnectionXdslAttnUp.Collect(ch)
} else {
c.collectGauge(ch, collectorConnectionXdslAttnUp, x.Attn)
}
}
if m.Xdsl.Down != nil {
x := m.Xdsl.Down
c.collectGauge(ch, collectorConnectionXdslMaxrateDown, x.Maxrate)
c.collectGauge(ch, collectorConnectionXdslRateDown, x.Rate)
if c.use(collectorConnectionXdslSnrDown) && x.Snr10 != nil {
collectorConnectionXdslSnrDown.Set(float64(*x.Snr10) / 10)
collectorConnectionXdslSnrDown.Collect(ch)
} else {
c.collectGauge(ch, collectorConnectionXdslSnrDown, x.Snr)
}
if c.use(collectorConnectionXdslAttnDown) && x.Attn10 != nil {
collectorConnectionXdslAttnDown.Set(float64(*x.Attn10) / 10)
collectorConnectionXdslAttnDown.Collect(ch)
} else {
c.collectGauge(ch, collectorConnectionXdslAttnDown, x.Attn)
}
}
}
} else {
log.Info.Println(err)
}
}()
wg.Wait()
info.WithLabelValues(firmwareVersion,
mac,
serial,
boardName,
boxFlavor,
cnxState,
cnxMedia,
cnxIPv4,
cnxIPv6).Collect(ch)
}
func (c *context) collectGauge(ch chan<- prometheus.Metric, i prometheus.Gauge, value *int64) {
if c.use(i) && value != nil {
i.Set(float64(*value))
i.Collect(ch)
}
}
func (c *context) use(i prometheus.Metric) bool {
found, _ := c.used[i]
return found || len(c.used) == 0
}
func decodeFromFile(file io.Reader, fb *fbx.FreeboxConnection) bool {
if err := json.NewDecoder(file).Decode(fb); err != nil {
return false
}
if fb.AppToken == "" {
return false
}
if err := fb.Login(); err != nil {
log.Debug.Println("Decoding file:", err)
return false
}
return true
}
func getContext(filename string) context {
connection := fbx.NewFreeboxConnection()
tryConnect := true
if r, err := os.Open(filename); err == nil {
defer r.Close()
tryConnect = !decodeFromFile(r, connection)
}
if tryConnect {
if err := connection.Login(); err != nil {
panic(err)
}
w, err := os.Create(filename)
if err != nil {
panic(err)
}
defer w.Close()
if err := json.NewEncoder(w).Encode(connection); err != nil {
panic(err)
}
}
return context{
freebox: connection,
used: make(map[prometheus.Metric]bool),
}
}
2018-09-06 01:06:16 +02:00
func usage(err error) {
if err != nil {
fmt.Fprintln(os.Stderr, "Error:", err)
}
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[options] <configfile>")
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, "Options:")
flag.PrintDefaults()
os.Exit(-1)
}
2018-09-05 03:08:48 +02:00
func main() {
2018-09-06 01:06:16 +02:00
debugPtr := flag.Bool("debug", false, "enable the debug mode")
flag.Parse()
args := flag.Args()
if len(args) < 1 {
usage(errors.New("configfile not defined"))
} else if len(args) > 1 {
usage(errors.New("invalid configfile"))
2018-09-05 03:08:48 +02:00
}
2018-09-06 01:06:16 +02:00
if *debugPtr {
2018-09-05 03:08:48 +02:00
log.Init(os.Stdout, os.Stdout, os.Stdout, os.Stderr)
2018-09-06 01:06:16 +02:00
} else {
log.Init(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr)
2018-09-05 03:08:48 +02:00
}
context := getContext(os.Args[1])
defer func() { context.freebox.Logout() }()
prometheus.MustRegister(&context)
http.Handle("/metrics", promhttp.Handler())
log.Error.Println(http.ListenAndServe(":9091", nil))
}