diff --git a/example.devices.router.json b/example.devices.router.json new file mode 100644 index 0000000..7a366e8 --- /dev/null +++ b/example.devices.router.json @@ -0,0 +1,26 @@ +{ + "devices":[ + { + "name": "Rathaus2Bahnhof", + "mac": "18:E8:29:8E:C6:4D", + "gateway_nexthop": "18e8292f7de6", + "gateway": "a28cae6ff604", + "domain": "other", + "location": { + "longitude":7.148406208, + "latitude":50.817093402 + } + }, + { + "name": "Bahnhof2Rathaus", + "mac": "18:e8:29:dc:c3:7e", + "gateway_nexthop": "18e8292f7de6", + "gateway": "a28cae6ff604", + "domain": "other", + "location": { + "longitude":7.150436640, + "latitude":50.814456507 + } + } + ] +} \ No newline at end of file diff --git a/go.mod b/go.mod index 185622d..e71c768 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,8 @@ module git.freifunk-rhein-sieg.net/Freifunk-Troisdorf/ubnt-freifunk-map-api go 1.16 -require git.nils.zone/nils/prettify v0.0.4 +require ( + git.nils.zone/nils/prettify v0.0.4 + github.com/fatih/structs v1.1.0 + github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c +) diff --git a/go.sum b/go.sum index 8ec57a6..42ee048 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,12 @@ github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkH github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e h1:0aewS5NTyxftZHSnFaJmWE5oCCrj4DyEXkAiMa1iZJM= github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= diff --git a/main.go b/main.go index 2209f8d..14505d2 100644 --- a/main.go +++ b/main.go @@ -31,12 +31,13 @@ func main() { flag.Parse() // check if flags are set - //if *configPath == "" { - // log.Fatalln("Please specify path to config.json flag '-configPath'") - //} + if *configPath == "" { + log.Fatalln("Please specify path to config.json flag '-configPath'") + } // start API processing (runs in a loop) go processAPIs() - + //processUNMSAPIRouter() + //createMetrics(influxDBClient()) // start webserver on Port 3000 serveJSON() } @@ -52,7 +53,7 @@ func loadconfig(file string) config { return config } -//int to bool converter +// int to bool converter func itob(i int) bool { if i == 1 { return true @@ -69,7 +70,9 @@ func processAPIs() { if conf.Unms.Enabled { log.Println("Processing UNMS") unmsNodes, unmsLinks := processUNMSAPI() + unmsRouters := processUNMSAPIRouter() nodes = append(nodes, unmsNodes...) + nodes = append(nodes, unmsRouters...) links = append(links, unmsLinks...) } if conf.Unifi.Enabled { @@ -120,7 +123,7 @@ func getFile(url string) []byte { return byteValue } -//get devices from devices file on webserver (config) +// get devices from devices file on webserver (config) func getDevices(url string) devices { // get devices from JSON file jsonFile := getFile(url) @@ -136,7 +139,7 @@ func getDevices(url string) devices { return d } -//check for MAC Adress in current Devices +// check for MAC Adress in current Devices func isRemoteMACpublished(mac string, devices []device) bool { for i := range devices { if devices[i].MAC == mac { diff --git a/types.go b/types.go index bc6541c..994df7e 100644 --- a/types.go +++ b/types.go @@ -15,6 +15,7 @@ type config struct { UnmsAPIURL string `json:"unmsAPIUrl"` APItoken string `json:"APItoken"` DevicesURL string `json:"devicesURL"` + RouterURL string `json:"routerURL"` } `json:"unms"` Unifi struct { Enabled bool `json:"enabled"` @@ -240,7 +241,7 @@ type mvDevices struct { } `json:"links"` } -//switch Unifi AP Mod IDs to Names +// switch Unifi AP Mod IDs to Names func lookupModels(model string) string { switch model { case "BZ2", "U2S48", "U2Sv2": @@ -289,3 +290,40 @@ type UNMSLogResponse struct { } `json:"device"` } `json:"items"` } + +type UNMSstatistics struct { + Period int `json:"period"` + Interval struct { + Start int `json:"start"` + End int `json:"end"` + } `json:"interval"` + CPU []struct { + X int `json:"x"` + Y int `json:"y"` + } `json:"cpu"` + RAM []struct { + X int `json:"x"` + Y int `json:"y"` + } `json:"ram"` + Ping []struct { + X int `json:"x"` + Y int `json:"y"` + } `json:"ping"` + Errors []struct { + X int `json:"x"` + Y int `json:"y"` + } `json:"errors"` + Interfaces []struct { + ID string `json:"id"` + Priority int `json:"priority"` + Name string `json:"name"` + Receive []struct { + X int `json:"x"` + Y int `json:"y"` + } `json:"receive"` + Transmit []struct { + X int `json:"x"` + Y int `json:"y"` + } `json:"transmit"` + } `json:"interfaces"` +} diff --git a/unms.go b/unms.go index 61dfc8b..57d9ac5 100644 --- a/unms.go +++ b/unms.go @@ -7,10 +7,17 @@ import ( "io/ioutil" "log" "net/http" + "strconv" "strings" + "time" + + _ "github.com/fatih/structs" + + //_ "github.com/influxdata/influxdb1-client" // this is important because of the bug in go mod + client "github.com/influxdata/influxdb1-client/v2" ) -//UNMS API processing (Richtfunk) +// UNMS API processing (Richtfunk) func processUNMSAPI() ([]node, []link) { // Variables for runtime var links []link @@ -55,7 +62,7 @@ func processUNMSAPI() ([]node, []link) { // check if remote mac address is part of our published network for i := range airmaxes { if isRemoteMACpublished(airmaxes[i].DeviceIdentification.MAC, d.Devices) { - links = UnmsAddLink(dev, airmaxes[i], links) + //links = UnmsAddLink(dev, airmaxes[i], links) } } // END OF API CALL 3 @@ -98,6 +105,193 @@ func processUNMSAPI() ([]node, []link) { return nodes, links } +func processUNMSAPIRouter() []node { + // Variables for runtime + var nodes []node + + d := getDevices(conf.Unms.RouterURL) + + // API CALL 1 + log.Println("calling API 1") + var u []unifiAPIResponse + err := UnmsCallAPI("/devices", &u) + if err != nil { + log.Fatalln(err) + } + + for i := range d.Devices { + var dev unifiAPIResponse + var currentDevice device + for j := range u { + if strings.ToUpper(u[j].Identification.MAC) == strings.ToUpper(d.Devices[i].MAC) { + dev = u[j] + currentDevice = d.Devices[i] + } + } + + var isOnline bool = false + if dev.Overview.Status == "active" { + isOnline = true + } + // END OF API CALL 1 + + // API CALL 2 + log.Println("calling API 2 for device", d.Devices[i].Name) + var details unifiAPIDetails + UnmsCallAPI("/devices/erouters/"+dev.Identification.ID, &details) + // END OF API CALL 2 + + // API CALL 3 STATISTICS! + bp, err := client.NewBatchPoints(client.BatchPointsConfig{ + Database: "freifunk", + Precision: "s", + }) + if err != nil { + log.Fatalln("Error: ", err) + } + for i := range d.Devices { + log.Println("calling API 3 for device", d.Devices[i].Name) + var statistics UNMSstatistics + UnmsCallAPI("/devices/"+dev.Identification.ID+"/statistics?interval=hour", &statistics) + for t := range statistics.CPU { + if t > 9 { + break + } + tags := map[string]string{ + "hostname": d.Devices[i].Name, + "nodeid": strings.ReplaceAll(dev.Identification.MAC, ":", ""), + } + fields := map[string]interface{}{ + "cpu": statistics.CPU[t].Y, + "ram": statistics.RAM[t].Y, + //hier fehlen noch die interfaces + } + slice := strconv.Itoa(statistics.CPU[t].X) + timeunix, err := strconv.Atoi(slice[:10]) + if err != nil { + log.Fatalln("Error: ", err) + } + tm := time.Unix(int64(timeunix), 0) + fmt.Println(tm) + point, err := client.NewPoint( + "node", + tags, + fields, + tm, + ) + if err != nil { + log.Fatalln("Error: ", err) + } + + bp.AddPoint(point) + c := influxDBClient() + err = c.Write(bp) + if err != nil { + log.Fatal(err) + } + } + // ab hier Interfaces + for i := range d.Devices { + log.Println("calling API 3 for device", d.Devices[i].Name) + var statistics UNMSstatistics + UnmsCallAPI("/devices/"+dev.Identification.ID+"/statistics?interval=hour", &statistics) + for eth := range statistics.Interfaces { + for t := range statistics.Interfaces[eth].Transmit { + if t > 9 { + break + } + tags := map[string]string{ + "hostname": d.Devices[i].Name, + "nodeid": strings.ReplaceAll(dev.Identification.MAC, ":", ""), + } + interface_name_rx := ("traffic.rx.bytes" + "_" + statistics.Interfaces[eth].ID) + interface_name_tx := ("traffic.tx.bytes" + "_" + statistics.Interfaces[eth].ID) + if statistics.Interfaces[eth].ID == "eth0" { + interface_name_rx = "traffic.rx.bytes" + interface_name_tx = "traffic.tx.bytes" + } + fields := map[string]interface{}{ + interface_name_rx: statistics.Interfaces[eth].Receive[t].Y * 100, + interface_name_tx: statistics.Interfaces[eth].Transmit[t].Y * 100, + } + slice := strconv.Itoa(statistics.CPU[t].X) + timeunix, err := strconv.Atoi(slice[:10]) + if err != nil { + log.Fatalln("Error: ", err) + } + tm := time.Unix(int64(timeunix), 0) + fmt.Println(tm) + point, err := client.NewPoint( + "node", + tags, + fields, + tm, + ) + if err != nil { + log.Fatalln("Error: ", err) + } + + bp.AddPoint(point) + c := influxDBClient() + err = c.Write(bp) + if err != nil { + log.Fatal(err) + } + } + } + } + } + + // END OF API CALL 3 + + // Get info from json file (static) + nodes = append(nodes, node{ + Firstseen: dev.Overview.CreatedAt.Format(iso8601), + Lastseen: dev.Overview.LastSeen.Format(iso8601), + IsOnline: isOnline, + IsGateway: false, + Clients: 0, + ClientsWifi24: 0, + ClientsWifi5: 0, + ClientsOther: 0, + RootFSUsage: 0, + LoadAVG: details.Overview.CPU / 100, + MemoryUsage: details.Overview.RAM / 100, + Uptime: dev.Identification.Started.Format(iso8601), + GatewayNexthop: currentDevice.GatewayNexthop, + Gateway: currentDevice.Gateway, + Location: ¤tDevice.Location, + NodeID: strings.ReplaceAll(dev.Identification.MAC, ":", ""), + MAC: dev.Identification.MAC, + Adresses: UnmsGetAddresses(details.IPAddress), + Domain: currentDevice.Domain, + Hostname: "[RiFu] " + details.Identification.Name, + Owner: "Freifunk Rhein-Sieg", + Firmware: firmware{ + Base: "Ubiquiti - Stock", + Release: details.Firmware.Current, + }, + Autoupdater: autoupdater{ + Enabled: false, + Branch: "stable", + }, + NProc: 1, + Model: details.Identification.Model, + }) + } + return nodes +} + +func influxDBClient() client.Client { + c, err := client.NewHTTPClient(client.HTTPConfig{ + Addr: "http://statistik.freifunk-troisdorf.de:8886", + }) + if err != nil { + log.Fatalln("Error: ", err) + } + return c +} + func UnmsCallAPI(url string, i interface{}) error { request, err := http.NewRequest(http.MethodGet, conf.Unms.UnmsAPIURL+url, nil) if err != nil { @@ -122,25 +316,6 @@ func UnmsCallAPI(url string, i interface{}) error { return nil } -func UnmsAddLink(dev unifiAPIResponse, airmaxes unifiAPIAirmax, links []link) []link { - for i := range links { - if links[i].SourceAddr == airmaxes.DeviceIdentification.MAC { - // link already exists - return links - } - } - links = append(links, link{ - Type: "wifi", - Source: strings.ReplaceAll(dev.Identification.MAC, ":", ""), - Target: strings.ReplaceAll(airmaxes.DeviceIdentification.MAC, ":", ""), - SourceTQ: airmaxes.Statistics.LinkScore, - TargetTQ: airmaxes.Statistics.LinkScore, - SourceAddr: dev.Identification.MAC, - TargetAddr: airmaxes.DeviceIdentification.MAC, - }) - return links -} - func UnmsGetAddresses(ip string) []string { var adresses []string adresses = append(adresses, strings.Split(ip, "/")[0])