mDNS discovery (HTTP is optional)

This commit is contained in:
Alexandre Blazart 2021-02-28 11:44:31 +01:00
parent 9a97bea4b2
commit b98278d506
6 changed files with 136 additions and 21 deletions

View file

@ -100,7 +100,7 @@ var (
promDescWifiApChannel = prometheus.NewDesc(
metricPrefix+"wifi_ap_channel",
"channel number",
[]string{"ap_id", "ap_band", "ap_name", "state", "channel_type"}, nil)
[]string{"ap_id", "ap_band", "ap_name", "channel_type"}, nil)
promDescWifiApStations = prometheus.NewDesc(
metricPrefix+"wifi_stations",
"number of stations connected to the AP",
@ -294,7 +294,10 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) {
if capabilities, ok := ap.Capabilities[ap.Config.Band]; ok {
labels := prometheus.Labels{}
labels["band"] = ap.Config.Band
labels["ap_id"] = apID
labels["ap_band"] = ap.Config.Band
labels["ap_name"] = ap.Name
labels["ap_state"] = ap.Status.State
for k, v := range capabilities {
labels[k] = strconv.FormatBool(v)
@ -312,13 +315,11 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) {
apID,
ap.Config.Band,
ap.Name,
ap.Status.State,
"primary")
c.collectGauge(ch, ap.Status.SecondaryChannel, promDescWifiApChannel,
apID,
ap.Config.Band,
ap.Name,
ap.Status.State,
"secondary")
ch <- prometheus.MustNewConstMetric(promDescWifiApStations, prometheus.GaugeValue, float64(len(ap.Stations)),
apID,
@ -500,7 +501,7 @@ func (c *Collector) toBool(b *bool) bool {
return b != nil && *b
}
func NewCollector(filename string, hostDetails, debug bool) *Collector {
func NewCollector(filename string, discovery fbx.FreeboxDiscovery, hostDetails, debug bool) *Collector {
result := &Collector{
hostDetails: hostDetails,
}
@ -515,7 +516,7 @@ func NewCollector(filename string, hostDetails, debug bool) *Collector {
} else {
log.Info.Println("Could not find the configuration file", filename)
newConfig = true
result.freebox, err = fbx.NewFreeboxConnection(debug)
result.freebox, err = fbx.NewFreeboxConnectionFromServiceDiscovery(debug, discovery)
if err != nil {
panic(err)
}

View file

@ -4,8 +4,10 @@ import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"github.com/hashicorp/mdns"
"github.com/trazfr/freebox-exporter/log"
)
@ -22,22 +24,25 @@ type FreeboxAPIVersion struct {
const (
apiVersionURL = "http://mafreebox.freebox.fr/api_version"
mdnsService = "_fbx-api._tcp"
)
func NewFreeboxAPIVersionHTTP(client *FreeboxHttpClient) (*FreeboxAPIVersion, error) {
log.Debug.Println("GET", apiVersionURL)
type FreeboxDiscovery int
// get api version
r, err := client.client.Get(apiVersionURL)
if err != nil {
return nil, err
}
defer r.Body.Close()
const (
// FreeboxDiscoveryHTTP Freebox discovery by call to http://mafreebox.freebox.fr/api_version
FreeboxDiscoveryHTTP FreeboxDiscovery = iota
// FreeboxDiscoveryMDNS Freebox discovery by mDNS on service _fbx-api._tcp
FreeboxDiscoveryMDNS
)
func NewFreeboxAPIVersion(client *FreeboxHttpClient, discovery FreeboxDiscovery) (*FreeboxAPIVersion, error) {
result := &FreeboxAPIVersion{}
if err := json.NewDecoder(r.Body).Decode(result); err != nil {
if err := result.getDiscovery(discovery)(client); err != nil {
return nil, err
}
if result.IsValid() == false {
return nil, errors.New("Could not get the API version")
}
@ -77,3 +82,92 @@ func (f *FreeboxAPIVersion) GetURL(path string, miscPath ...interface{}) (string
}
return fmt.Sprintf("https://%s:%d%sv%s/"+path, args...), nil
}
func (f *FreeboxAPIVersion) getDiscovery(discovery FreeboxDiscovery) func(client *FreeboxHttpClient) error {
function := func(*FreeboxHttpClient) error {
return errors.New("Wrong discovery argument")
}
switch discovery {
case FreeboxDiscoveryHTTP:
function = f.newFreeboxAPIVersionHTTP
case FreeboxDiscoveryMDNS:
function = f.newFreeboxAPIVersionMDNS
default:
}
return function
}
func (f *FreeboxAPIVersion) newFreeboxAPIVersionHTTP(client *FreeboxHttpClient) error {
log.Info.Println("Freebox discovery: GET", apiVersionURL)
// HTTP GET api version
r, err := client.client.Get(apiVersionURL)
if err != nil {
return err
}
defer r.Body.Close()
if err := json.NewDecoder(r.Body).Decode(f); err != nil {
return err
}
return nil
}
func (f *FreeboxAPIVersion) newFreeboxAPIVersionMDNS(*FreeboxHttpClient) error {
log.Info.Println("Freebox discovery: mDNS")
entries := make(chan *mdns.ServiceEntry, 4)
// mDNS lookup
go func() {
defer close(entries)
if err := mdns.Lookup(mdnsService, entries); err != nil {
log.Error.Println("mDNS lookup failed:", err)
}
log.Debug.Println("End of mDNS lookup")
}()
for entry := range entries {
deviceName := entry.Name
idx := strings.Index(deviceName, ".")
if idx >= 0 {
deviceName = deviceName[0:idx]
}
deviceName = strings.ReplaceAll(deviceName, "\\", "")
*f = FreeboxAPIVersion{
DeviceName: deviceName,
}
for i := range entry.InfoFields {
kv := strings.SplitN(entry.InfoFields[i], "=", 2)
if len(kv) != 2 {
break
}
switch kv[0] {
case "api_domain":
f.APIDomain = kv[1]
case "uid":
f.UID = kv[1]
case "https_available":
f.HTTPSAvailable = (kv[1] == "1")
case "https_port":
port, _ := strconv.ParseUint(kv[1], 10, 16)
f.HTTPSPort = uint16(port)
case "api_version":
f.APIVersion = kv[1]
case "api_base_url":
f.APIBaseURL = kv[1]
case "device_type":
f.DeviceType = kv[1]
default:
break
}
}
if f.IsValid() {
return nil
}
}
return errors.New("MDNS timeout")
}

View file

@ -21,9 +21,9 @@ type FreeboxConnection struct {
* FreeboxConnection
*/
func NewFreeboxConnection(debug bool) (*FreeboxConnection, error) {
func NewFreeboxConnectionFromServiceDiscovery(debug bool, discovery FreeboxDiscovery) (*FreeboxConnection, error) {
client := NewFreeboxHttpClient(debug)
apiVersion, err := NewFreeboxAPIVersionHTTP(client)
apiVersion, err := NewFreeboxAPIVersion(client, discovery)
if err != nil {
return nil, err
}

5
go.mod
View file

@ -2,4 +2,7 @@ module github.com/trazfr/freebox-exporter
go 1.16
require github.com/prometheus/client_golang v1.9.0
require (
github.com/hashicorp/mdns v1.0.3
github.com/prometheus/client_golang v1.9.0
)

10
go.sum
View file

@ -119,6 +119,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/mdns v1.0.3 h1:hPneYJlzSjxFBmUlnDGXRykxBZ++dQAJhU57gCO7TzI=
github.com/hashicorp/mdns v1.0.3/go.mod h1:P9sIDVQGUBr2GtS4qS2QCBdtgqP7TBt6d8looU5l5r4=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@ -153,6 +155,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
@ -274,6 +278,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -299,6 +304,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -307,6 +314,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -322,6 +330,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -345,6 +354,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

13
main.go
View file

@ -11,6 +11,7 @@ import (
"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"
)
@ -18,7 +19,7 @@ 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, "Usage:", os.Args[0], "[options] <api_token_file>")
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, "Options:")
flag.PrintDefaults()
@ -28,12 +29,13 @@ func usage(err error) {
func main() {
debugPtr := flag.Bool("debug", false, "enable the debug mode")
hostDetailsPtr := flag.Bool("hostDetails", false, "get details about the hosts connected to wifi and ethernet. This increases the number of metrics")
httpDiscoveryPtr := flag.Bool("httpDiscovery", false, "use http://mafreebox.freebox.fr/api_version to discover the Freebox at the first run (default: mDNS)")
listenPtr := flag.String("listen", ":9091", "listen to address")
flag.Parse()
args := flag.Args()
if len(args) < 1 {
usage(errors.New("configfile not defined"))
usage(errors.New("api_token_file not defined"))
} else if len(args) > 1 {
usage(errors.New("too many arguments"))
}
@ -42,7 +44,12 @@ func main() {
} else {
log.Init(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr)
}
collector := NewCollector(args[0], *hostDetailsPtr, *debugPtr)
discovery := fbx.FreeboxDiscoveryMDNS
if *httpDiscoveryPtr {
discovery = fbx.FreeboxDiscoveryHTTP
}
collector := NewCollector(args[0], discovery, *hostDetailsPtr, *debugPtr)
defer collector.Close()
prometheus.MustRegister(collector)