commit d553dd436aa7dd7ccc15397bc69c80e05c892009 Author: Martin Weinelt Date: Mon Mar 27 20:05:09 2017 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6a5112 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +fastd-exporter +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f815986 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Martin Weinelt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..975d685 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# fastd-exporter + +We are building a prometheus exporter for the fastd vpn daemon. We don't have a working version yet, stay tuned + +For now run + +``` +$ go build +``` + +and + +``` +$ ./fastd-exporter --instance ffda +``` + +It will print json data received from the socket and maybe also show metrics on `http://127.0.0.1:9099/metrics`. \ No newline at end of file diff --git a/fastd-exporter.go b/fastd-exporter.go new file mode 100644 index 0000000..4921831 --- /dev/null +++ b/fastd-exporter.go @@ -0,0 +1,133 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "net" + "net/http" + "regexp" + "strings" + "time" + "errors" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +var ( + address = flag.String("listen-address", ":9099", "The address to listen on for HTTP requests.") + instances = flag.String("instances", "", "The fastd instances to Update on, comma separated.") +) + +// These are the structs necessary for unmarshalling the data that is being +// received on fastds unix socket. +type PacketStatistics struct { + Count int `json:"packets"` + Bytes int `json:"bytes"` +} + +type Statistics struct { + RX PacketStatistics `json:"rx"` + RX_Reordered PacketStatistics `json:"rx_reordered"` + TX PacketStatistics `json:"tx"` + TX_Dropped PacketStatistics `json:"tx_dropped"` + TX_Error PacketStatistics `json:"tx_error"` +} + +type Message struct { + Uptime float64 `json:"uptime"` + Interface string `json:"interface"` + Statistics Statistics `json:"statistics"` + Peers map[string]Peer `json:"peers"` +} + +type Peer struct { + Name string `json:"name"` + Address string `json:"address"` + Connection *struct { + Established time.Duration `json:"established"` + Method string `json:"method"` + Statistics Statistics `json:"statistics"` + } `json:"connection"` + MAC []string `json:"mac_addresses"` +} + + +func init() { + // register metrics +} + +func Update(sock string) { + data := data_from_sock(sock) + + // TODO: continue here mapping json data to metrics + // prometheus.MustNewConstMetric(, , data.Uptime) +} + +func data_from_sock(sock string) Message { + conn, err := net.DialTimeout("unix", sock, 2*time.Second) + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + decoder := json.NewDecoder(conn) + msg := Message{} + err = decoder.Decode(&msg) + if err != nil { + log.Fatal(err) + } + + dat, err := json.MarshalIndent(msg, "", "\t") + if err != nil { + log.Fatal(err) + } + + log.Print(string(dat)) + return msg +} + +func sock_from_instance(instance string) (string, error) { + data, err := ioutil.ReadFile(fmt.Sprintf("/etc/fastd/%s/fastd.conf", instance)) + if err != nil { + return "", err + } + + pattern := regexp.MustCompile("status socket \"([^\"]+)\";") + matches := pattern.FindSubmatch(data) + + if len(matches) > 1 { + return string(matches[1]), nil + } else { + return "", errors.New(fmt.Sprintf("Instance %s has no status socket configured.", instance)) + } +} + +func main() { + flag.Parse() + + if *instances == "" { + log.Fatal("No instance given, exiting.") + } + + instances := strings.Split(*instances, ",") + for i := 0; i < len(instances); i++ { + sock, err := sock_from_instance(instances[i]) + if err != nil { + log.Fatal(err) + } + go func() { + for { + Update(sock) + time.Sleep(time.Duration(15 * time.Second)) + } + }() + } + + // Expose the registered metrics via HTTP. + http.Handle("/metrics", promhttp.Handler()) + log.Fatal(http.ListenAndServe(*address, nil)) +}