ubnt-freifunk-map-api/unms.go
Stefan 75c24e389b
All checks were successful
continuous-integration/drone/tag Build is passing
Add VPN-Router Prefix to Routers instead of RiFu
2023-03-20 16:26:16 +01:00

344 lines
9.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"errors"
"fmt"
"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)
func processUNMSAPI() ([]node, []link) {
// Variables for runtime
var links []link
var nodes []node
d := getDevices(conf.Unms.DevicesURL)
// 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
log.Println("calling API 3 for device", d.Devices[i].Name)
var airmaxes []unifiAPIAirmax
UnmsCallAPI("/devices/airmaxes/"+dev.Identification.ID+"/stations", &airmaxes)
// 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)
}
}
// 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: &currentDevice.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, 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,
"load": 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)
time_local := tm.Add(1 * time.Hour)
point, err := client.NewPoint(
"node",
tags,
fields,
time_local,
)
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)
time_local := tm.Add(1 * time.Hour)
point, err := client.NewPoint(
"node",
tags,
fields,
time_local,
)
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: &currentDevice.Location,
NodeID: strings.ReplaceAll(dev.Identification.MAC, ":", ""),
MAC: dev.Identification.MAC,
Adresses: UnmsGetAddresses(details.IPAddress),
Domain: currentDevice.Domain,
Hostname: "[VPN-Router] " + 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 {
return errors.New(fmt.Sprint("can't set request", conf.Unms.UnmsAPIURL+url))
}
request.Header.Set("x-auth-token", conf.Unms.APItoken)
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
return fmt.Errorf("can't get request %s with x-auth-token %s", conf.Unms.UnmsAPIURL+url, conf.Unms.APItoken)
}
if response.StatusCode != 200 {
log.Fatalln("Can´t call UNMS API, check token and URL. HTTP Status: ", response.StatusCode)
}
data, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
return fmt.Errorf("can't read response body: %+v", response.Body)
}
// no error occurred, unmarshal to struct
json.Unmarshal(data, &i)
return nil
}
func UnmsGetAddresses(ip string) []string {
var adresses []string
adresses = append(adresses, strings.Split(ip, "/")[0])
return adresses
}
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
}