diff --git a/example.config.json b/example.config.json index d1f81e6..15fb8d5 100644 --- a/example.config.json +++ b/example.config.json @@ -1,14 +1,14 @@ { "unms": { "enabled": false, - "unmsAPIUrl": "https://unifi.freifunk-troisdorf.de/v2.1", + "unmsAPIUrl": "https://uisp.freifunk-troisdorf.de/v2.1", "APItoken": "UNMS API TOKEN", "devicesURL": "https://git.freifunk-rhein-sieg.net/Freifunk-Troisdorf/ubnt-freifunk-map-api/raw/branch/master/example.devices.json" }, "unifi": { "enabled": false, "displayusers": true, - "APIUrl": "https://unifi.freifunk-troisdorf.de:8443", + "APIUrl": "https://unifi.freifunk-troisdorf.de", "user": "APIuser", "password": "PASSWORD", "ucDevicesURL": "https://git.freifunk-rhein-sieg.net/Freifunk-Troisdorf/ubnt-freifunk-map-api/raw/branch/master/example.ucDevices.json" diff --git a/go.mod b/go.mod index e71c768..56c13f0 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,17 @@ module git.freifunk-rhein-sieg.net/Freifunk-Troisdorf/ubnt-freifunk-map-api -go 1.16 +go 1.20 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 ) + +require ( + github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect + github.com/fatih/color v1.9.0 // indirect + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.11 // indirect + golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect +) diff --git a/main.go b/main.go index f5e5aa0..ec3803b 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,7 @@ import ( "encoding/json" "flag" "fmt" - "io/ioutil" + "io" "log" "net/http" "net/http/cookiejar" @@ -34,8 +34,19 @@ func main() { if *configPath == "" { log.Fatalln("Please specify path to config.json flag '-configPath'") } + // start API processing (runs in a loop) - go processAPIs() + go func() { + if err := processAPIs(); err != nil { + log.Fatalln("API processing failed, error is", err) + } + tick := time.Tick(delay) + for range tick { + if err := processAPIs(); err != nil { + log.Fatalln("API processing failed, error is", err) + } + } + }() //processUNMSAPIRouter() //createMetrics(influxDBClient()) // start webserver on Port 3000 @@ -55,59 +66,57 @@ func loadconfig(file string) config { // int to bool converter func itob(i int) bool { - if i == 1 { - return true - } - return false + return i == 1 } -func processAPIs() { - tick := time.Tick(delay) - for range tick { - var nodes []node - var links []link +func processAPIs() error { + var nodes []node + var links []link - 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 { - log.Println("Processing Unifi") - ucNodes, _ := processUcAPIs() - nodes = append(nodes, ucNodes...) - } - if conf.Meshviewer.Enabled { - log.Println("Processing Meshviewer") - mvNodes, mvLinks := getMeshviewer() - nodes = append(nodes, mvNodes...) - links = append(links, mvLinks...) - } - // assemble final struct - o := output{ - Timestamp: time.Now().Format(iso8601), - Nodes: nodes, - Links: links, - } - - // create file output - log.Println("writing json file") - - if err := o.writeToFile(); err != nil { - log.Fatalln(err) - } - // get outages to serve as .csv - l := getUNMSLogs() - err := writeOutagesToCSV(l) + if conf.Unms.Enabled { + log.Println("Processing UNMS") + unmsNodes, unmsLinks := processUNMSAPI() + unmsRouters, err := processUNMSAPIRouter() if err != nil { - log.Println("Error writing outages.csv") + return err } - // we're done here - log.Println("...done") + nodes = append(nodes, unmsNodes...) + nodes = append(nodes, unmsRouters...) + links = append(links, unmsLinks...) } + if conf.Unifi.Enabled { + log.Println("Processing Unifi") + ucNodes, _ := processUcAPIs() + nodes = append(nodes, ucNodes...) + } + if conf.Meshviewer.Enabled { + log.Println("Processing Meshviewer") + mvNodes, mvLinks := getMeshviewer() + nodes = append(nodes, mvNodes...) + links = append(links, mvLinks...) + } + // assemble final struct + o := output{ + Timestamp: time.Now().Format(iso8601), + Nodes: nodes, + Links: links, + } + + // create file output + log.Println("writing json file") + + if err := o.writeToFile(); err != nil { + log.Fatalln(err) + } + // get outages to serve as .csv + l := getUNMSLogs() + err := writeOutagesToCSV(l) + if err != nil { + log.Println("Error writing outages.csv") + } + // we're done here + log.Println("...done") + return nil } // function to get file from meshviewer @@ -117,7 +126,7 @@ func getFile(url string) []byte { log.Println("Error getting file from:", url) } data := resp.Body - byteValue, _ := ioutil.ReadAll(data) + byteValue, _ := io.ReadAll(data) return byteValue } diff --git a/types.go b/types.go index befda90..14d7281 100644 --- a/types.go +++ b/types.go @@ -314,40 +314,26 @@ type UNMSLogResponse struct { } `json:"items"` } +type XY struct { + X int `json:"x"` + Y int `json:"y"` +} + 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"` + CPU []XY `json:"cpu"` + RAM []XY `json:"ram"` + Errors []XY `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"` + Receive []XY `json:"receive"` + Transmit []XY `json:"transmit"` } `json:"interfaces"` } diff --git a/unifi.go b/unifi.go index f31052f..6d3c165 100644 --- a/unifi.go +++ b/unifi.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "io" "log" "net/http" "strconv" @@ -126,10 +126,10 @@ func (u *UnifiAPIData) ucCallAPI(url string, method string, body *bytes.Buffer, } defer response.Body.Close() if response.StatusCode != 200 { - return fmt.Errorf("Login failed %s", u.baseURL+url) + return fmt.Errorf("login failed %s", u.baseURL+url) } - data, err := ioutil.ReadAll(response.Body) + data, err := io.ReadAll(response.Body) if err != nil { return err } diff --git a/unms.go b/unms.go index 4a96415..e58d6e4 100644 --- a/unms.go +++ b/unms.go @@ -42,10 +42,7 @@ func processUNMSAPI() ([]node, []link) { } } - var isOnline bool = false - if dev.Overview.Status == "active" { - isOnline = true - } + isOnline := dev.Overview.Status == "active" // END OF API CALL 1 // API CALL 2 @@ -104,7 +101,7 @@ func processUNMSAPI() ([]node, []link) { return nodes, links } -func processUNMSAPIRouter() []node { +func processUNMSAPIRouter() ([]node, error) { // Variables for runtime var nodes []node @@ -115,7 +112,7 @@ func processUNMSAPIRouter() []node { var u []unifiAPIResponse err := UnmsCallAPI("/devices", &u) if err != nil { - log.Fatalln(err) + return nil, err } // END OF API CALL 1 @@ -130,26 +127,29 @@ func processUNMSAPIRouter() []node { } } - var isOnline bool = false - if dev.Overview.Status == "active" { - isOnline = true - } + isOnline := dev.Overview.Status == "active" // API CALL FOR ROUTER DETAILS (Interface RX/TX) log.Println("Getting details of ", d.Devices[i].Name, "from UNMS API") var details unifiAPIDetails - UnmsCallAPI("/devices/erouters/"+dev.Identification.ID, &details) + if err := UnmsCallAPI("/devices/erouters/"+dev.Identification.ID, &details); err != nil { + return nil, err + } // API CALL FOR DEVICE STATISTICS (CPU, RAM) log.Println("Getting statistics of ", d.Devices[i].Name, "from UNMS API") var statistics UNMSstatistics - UnmsCallAPI("/devices/"+dev.Identification.ID+"/statistics?interval=hour", &statistics) + if err := UnmsCallAPI("/devices/"+dev.Identification.ID+"/statistics?interval=hour", &statistics); err != nil { + return nil, err + } // API CALL FOR DHCP LEASES log.Println("Getting DHCP Leases of ", d.Devices[i].Name, "from UNMS API") var dhcpleases UNMSdhcp if isOnline { - UnmsCallAPI("/devices/erouters/"+dev.Identification.ID+"/dhcp/leases", &dhcpleases) + if err := UnmsCallAPI("/devices/erouters/"+dev.Identification.ID+"/dhcp/leases", &dhcpleases); err != nil { + return nil, err + } } else { log.Println("Router ist offline, skipping DHCP Leases") } @@ -164,7 +164,8 @@ func processUNMSAPIRouter() []node { } // - fields := map[string]interface{}{} + // fields := map[string]interface{}{} + fields := make(map[string]any) tags := map[string]string{ "hostname": strings.ReplaceAll(d.Devices[i].Name, " ", "-"), "nodeid": strings.ReplaceAll(dev.Identification.MAC, ":", ""), @@ -177,15 +178,21 @@ func processUNMSAPIRouter() []node { fields[interface_name_tx] = details.Interfaces[eth].Statistics.Txrate } - // Generate fields for all Statistics - load := (float64(statistics.CPU[0].Y) / float64(100)) - fields["cpu"] = statistics.CPU[0].Y - fields["load"] = load - fields["ram"] = statistics.RAM[0].Y + // set default values if we can't get statistics + fields["cpu"] = 0 + fields["load"] = float64(0) + fields["ram"] = 0 + + if isOnline { + // Generate fields for all Statistics + load := (float64(statistics.CPU[0].Y) / float64(100)) + fields["cpu"] = statistics.CPU[0].Y + fields["load"] = load + fields["ram"] = statistics.RAM[0].Y + } // Generate field for DHCP Leases - leases := len(dhcpleases) - fields["clients.total"] = leases + fields["clients.total"] = len(dhcpleases) // Generate Dataponts point, err := client.NewPoint( @@ -240,7 +247,7 @@ func processUNMSAPIRouter() []node { Model: details.Identification.Model, }) } - return nodes + return nodes, nil } func influxDBClient() client.Client { @@ -253,7 +260,7 @@ func influxDBClient() client.Client { return c } -func UnmsCallAPI(url string, i interface{}) error { +func UnmsCallAPI(url string, i any) 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)) @@ -265,7 +272,7 @@ func UnmsCallAPI(url string, i interface{}) error { 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) + 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() @@ -273,8 +280,7 @@ func UnmsCallAPI(url string, i interface{}) error { return fmt.Errorf("can't read response body: %+v", response.Body) } // no error occurred, unmarshal to struct - json.Unmarshal(data, &i) - return nil + return json.Unmarshal(data, &i) } func UnmsGetAddresses(ip string) []string {