285 lines
7.0 KiB
Go
285 lines
7.0 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
client "github.com/influxdata/influxdb1-client/v2"
|
|
)
|
|
|
|
// Unifi Controller API processing
|
|
func processUnifiAPI(s int) ([]node, []link, error) {
|
|
//get list of Unifi devices to display
|
|
var nodes []node
|
|
var links []link
|
|
d := getDevices(conf.Unifi[s].UCDevicesURL)
|
|
|
|
//call Unifi Controller
|
|
ucAPI := UnifiNewAPI(conf.Unifi[s].User, conf.Unifi[s].Password, conf.Unifi[s].APIURL)
|
|
//login
|
|
if err := ucAPI.ucLogin(); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
//get all Sites from Controller
|
|
sites, err := ucAPI.ucGetSites()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
//get all devices in all sites
|
|
devices, err := ucAPI.ucGetDevices(sites)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
//build nodes struct
|
|
//mvJson, err := getMeshviewerJSON()
|
|
for _, jsonDevice := range d.Devices {
|
|
var currentDevice ucDevice
|
|
var currentJSONDevice device
|
|
for _, device := range devices {
|
|
if strings.EqualFold(device.Mac, jsonDevice.MAC) {
|
|
currentDevice = device
|
|
currentJSONDevice = jsonDevice
|
|
}
|
|
}
|
|
|
|
if isRemoteMACpublished(jsonDevice.MAC, d.Devices) {
|
|
//hier muss gecheckt werden ob der link valide ist
|
|
if checkMeshviewerLink(jsonDevice.LinkedTo) {
|
|
links = UnifiAddLink(jsonDevice, links)
|
|
}
|
|
}
|
|
|
|
isOnline := currentDevice.State == 1
|
|
var load float64
|
|
var mem float64
|
|
var cpu float64
|
|
if isOnline {
|
|
load, err = strconv.ParseFloat(currentDevice.Sysstats.CPU, 64)
|
|
cpu = load * 100
|
|
if err != nil {
|
|
log.Println("Error psrsing CPU of device ", currentDevice.Name)
|
|
log.Println(err)
|
|
load = 0
|
|
cpu = 0
|
|
}
|
|
mem, err = strconv.ParseFloat(currentDevice.Sysstats.Memory, 64)
|
|
if err != nil {
|
|
log.Println("Error parsing Memory of device ", currentDevice.Name)
|
|
log.Println(err)
|
|
mem = 0
|
|
}
|
|
}
|
|
|
|
var model = lookupModels(currentDevice.Model)
|
|
var clients int
|
|
if conf.Unifi[s].DisplayUsers {
|
|
clients = currentDevice.Users
|
|
}
|
|
|
|
//// INFLUX START
|
|
// fields := map[string]interface{}{}
|
|
fields := make(map[string]any)
|
|
tags := map[string]string{
|
|
"hostname": strings.ReplaceAll(currentDevice.Name, " ", "-"),
|
|
"nodeid": strings.ReplaceAll(currentDevice.Mac, ":", ""),
|
|
}
|
|
// Generate fields for all network interfaces (not availible for Unifi Nodes)
|
|
//for eth := range details.Interfaces {
|
|
// interface_name_rx := ("rate.rx" + "_" + details.Interfaces[eth].Identification.Name)
|
|
// interface_name_tx := ("rate.tx" + "_" + details.Interfaces[eth].Identification.Name)
|
|
// fields[interface_name_rx] = details.Interfaces[eth].Statistics.Rxrate
|
|
// fields[interface_name_tx] = details.Interfaces[eth].Statistics.Txrate
|
|
//}
|
|
|
|
// 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(load) / float64(100))
|
|
fields["cpu"] = int(cpu)
|
|
fields["load"] = load
|
|
fields["ram"] = int(mem)
|
|
}
|
|
|
|
// Generate field for DHCP Leases
|
|
fields["clients.total"] = clients
|
|
fields["time.up"] = currentDevice.Uptime
|
|
|
|
// Generate Dataponts
|
|
point, err := client.NewPoint(
|
|
"node",
|
|
tags,
|
|
fields,
|
|
time.Now(),
|
|
)
|
|
if err != nil {
|
|
log.Fatalln("Error: ", err)
|
|
}
|
|
|
|
if conf.General.InfluxEnabled {
|
|
sendInfluxBatchDataPoint(point, conf.General.FreifunkInfluxPort)
|
|
}
|
|
|
|
// INFLUX STOP
|
|
//log.Println(currentDevice.Mac)
|
|
if currentDevice.Mac != "" {
|
|
nodes = append(nodes, node{
|
|
Firstseen: "0",
|
|
Lastseen: time.Unix(int64(currentDevice.LastSeen), 0).Format(iso8601),
|
|
IsOnline: itob(currentDevice.State),
|
|
IsGateway: false,
|
|
Clients: clients,
|
|
ClientsWifi24: 0,
|
|
ClientsWifi5: 0,
|
|
ClientsOther: clients,
|
|
RootFSUsage: 0,
|
|
LoadAVG: load / 100,
|
|
MemoryUsage: mem / 100,
|
|
Uptime: time.Now().Add(-1 * time.Second * time.Duration(currentDevice.Uptime)).Format(iso8601),
|
|
GatewayNexthop: currentJSONDevice.GatewayNexthop,
|
|
Gateway: currentJSONDevice.Gateway,
|
|
Location: ¤tJSONDevice.Location,
|
|
NodeID: strings.ReplaceAll(currentDevice.Mac, ":", ""),
|
|
MAC: currentDevice.Mac,
|
|
Adresses: []string{currentDevice.IP},
|
|
Domain: currentJSONDevice.Domain,
|
|
Hostname: "[Unifi] " + currentDevice.Name,
|
|
Owner: "Freifunk Rhein-Sieg",
|
|
Firmware: firmware{
|
|
Base: "Ubiquiti - Stock",
|
|
Release: currentDevice.Version,
|
|
},
|
|
Autoupdater: autoupdater{
|
|
Enabled: false,
|
|
Branch: "stable",
|
|
},
|
|
NProc: 1,
|
|
Model: model,
|
|
})
|
|
}
|
|
}
|
|
return nodes, links, err
|
|
}
|
|
|
|
func UnifiNewAPI(user string, pass string, baseURL string) UnifiAPIData {
|
|
return UnifiAPIData{
|
|
user: user,
|
|
pass: pass,
|
|
baseURL: baseURL,
|
|
client: httpClient(),
|
|
}
|
|
}
|
|
|
|
func (u *UnifiAPIData) ucCallAPI(url string, method string, body *bytes.Buffer, output interface{}) error {
|
|
req, err := http.NewRequest(method, u.baseURL+url, body)
|
|
if err != nil {
|
|
return fmt.Errorf("can't set request %s", u.baseURL+url)
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
response, err := u.client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("can't login %s", u.baseURL+url)
|
|
}
|
|
defer response.Body.Close()
|
|
if response.StatusCode != 200 {
|
|
return fmt.Errorf("login failed %s", u.baseURL+url)
|
|
}
|
|
|
|
data, err := io.ReadAll(response.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = json.Unmarshal(data, &output)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (u *UnifiAPIData) ucLogin() error {
|
|
var loginData = []byte(`{"username":"` + u.user + `","password":"` + u.pass + `"}`)
|
|
|
|
url := "/api/login"
|
|
err := u.ucCallAPI(url, http.MethodPost, bytes.NewBuffer(loginData), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (u *UnifiAPIData) ucGetSites() ([]ucSite, error) {
|
|
var d struct {
|
|
Data []ucSite `json:"data"`
|
|
}
|
|
|
|
url := "/api/self/sites"
|
|
err := u.ucCallAPI(url, http.MethodGet, bytes.NewBuffer([]byte{}), &d)
|
|
if err != nil {
|
|
return []ucSite{}, err
|
|
}
|
|
return d.Data, nil
|
|
}
|
|
|
|
func (u *UnifiAPIData) ucGetDevices(sites []ucSite) ([]ucDevice, error) {
|
|
var d struct {
|
|
Data []ucDevice `json:"data"`
|
|
}
|
|
var s []ucDevice
|
|
|
|
for _, site := range sites {
|
|
url := "/api/s/" + site.ID + "/stat/device"
|
|
u.ucCallAPI(url, http.MethodGet, bytes.NewBuffer([]byte{}), &d)
|
|
s = append(s, d.Data...)
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
func UnifiAddLink(dev device, links []link) []link {
|
|
for i := range links {
|
|
if links[i].SourceAddr == dev.MAC {
|
|
// link already exists
|
|
return links
|
|
}
|
|
}
|
|
if dev.LinkedTo == "" {
|
|
//no LinkedTo in ucDevices.json
|
|
return links
|
|
}
|
|
links = append(links, link{
|
|
Type: "cable",
|
|
Source: strings.ReplaceAll(dev.MAC, ":", ""),
|
|
Target: strings.ReplaceAll(dev.GatewayNexthop, ":", ""),
|
|
SourceTQ: 1,
|
|
TargetTQ: 1,
|
|
SourceAddr: dev.MAC,
|
|
TargetAddr: dev.LinkedTo,
|
|
})
|
|
return links
|
|
}
|
|
|
|
func findNodeID(NodeID string) bool {
|
|
for s := range conf.Unifi {
|
|
ucDev := getDevices(conf.Unifi[s].UCDevicesURL)
|
|
for i := range ucDev.Devices {
|
|
if ucDev.Devices[i].GatewayNexthop == NodeID {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|