package main import ( "encoding/json" "errors" "flag" "fmt" "io/ioutil" "log" "net/http" "os" "strings" "time" ) // types const ( baseURL = "https://unifi.freifunk-troisdorf.de/v2.1" iso8601 = "2006-01-02T15:04:05-0700" ) // flags var token = flag.String("token", "", "Defines the x-auth-token") func main() { // parse all flags flag.Parse() // check if token is set if *token == "" { log.Fatalln("Please specify an API token via the flag '-token'") } log.Println("starting...") // Variables for runtime var links []link var nodes []node d, err := getDevices() if err != nil { log.Fatalln(err) } // API CALL 1 log.Println("calling API 1") var u []unifiAPIResponse err = callUnifiAPI("/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 callUnifiAPI("/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 callUnifiAPI("/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) == true { links = addLink(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: getAddresses(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, }) } // assemble final struct o := output{ Timestamp: time.Now().Format(iso8601), Nodes: nodes, Links: links, } // create file output log.Println("writing json file") writeJSONtoFile(o) // we're done here log.Println("...done") } func getDevices() (devices, error) { // get devices from JSON file jsonFile, err := os.Open("devices.json") if err != nil { return devices{}, errors.New("can't open devices.json") } defer jsonFile.Close() // read file to bytes byteValue, _ := ioutil.ReadAll(jsonFile) // variable for d var d devices // unmarshal to struct json.Unmarshal(byteValue, &d) return d, nil } func callUnifiAPI(url string, i interface{}) error { request, err := http.NewRequest(http.MethodGet, baseURL+url, nil) if err != nil { return errors.New(fmt.Sprint("can't set request", baseURL+url)) } request.Header.Set("x-auth-token", *token) client := &http.Client{} response, err := client.Do(request) if err != nil { return fmt.Errorf("can't get request %s with x-auth-token %s", baseURL+url, *token) } data, err := ioutil.ReadAll(response.Body) defer response.Body.Close() if err != nil { return fmt.Errorf("can't read response body: %+v", response.Body) } // try to fetch errors var a apiResponse json.Unmarshal(data, &a) if a.StatusCode != 0 { return fmt.Errorf("got following errorcode from API: %d %s %s", a.StatusCode, a.Error, a.Message) } // no error occurred, unmarshal to struct json.Unmarshal(data, &i) return nil } func isRemoteMACpublished(mac string, devices []device) bool { for i := range devices { if devices[i].MAC == mac { return true } } return false } func addLink(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 writeJSONtoFile(o output) error { file, err := json.MarshalIndent(o, "", " ") if err != nil { return fmt.Errorf("can't marshal to json: %+v", o) } // write to file err = ioutil.WriteFile("example.json", file, 0644) if err != nil { return fmt.Errorf("can't write to json file example.json") } return nil } func getAddresses(ip string) []string { var adresses []string adresses = append(adresses, strings.Split(ip, "/")[0]) return adresses }