Compare commits

...

5 Commits

Author SHA1 Message Date
Stefan Hoffmann
631a5e398f Merge branch 'master' of ssh://git.freifunk-rhein-sieg.net:2222/Freifunk-Troisdorf/ubnt-freifunk-map-api
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-09-25 18:34:32 +02:00
Stefan Hoffmann
e6583918c1 Fixing ghost devices in Unifi 2024-09-25 18:33:41 +02:00
6f4fc76812 Delete .drone.jsonnet
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
2024-09-25 16:00:47 +00:00
Stefan Hoffmann
ff5cf755aa Bugfixing UISP 503 Errors.
Finetuning API Calls
2024-09-25 17:53:38 +02:00
f9fa5fe26a
Naming changes 2024-03-19 18:43:09 +01:00
8 changed files with 128 additions and 175 deletions

View File

@ -1,53 +0,0 @@
local pipeline(os, arch) = {
kind: "pipeline",
name: os + "/" + arch,
platform: {
"os": os,
"arch": arch,
},
steps: [{
name: "compile " + os + "/" + arch,
image: "golang:1.20.3-alpine3.17",
environment: {
"GOOS": os,
"GOARCH": arch,
"CGO_ENABLED": "0",
},
commands: [
'go build -ldflags "-s -w -X main.version=${DRONE_TAG##v}" -trimpath -o release/' + os + "/" + arch + "/ubnt-freifunk-map-api .",
"tar -cvzf release/ubnt-freifunk-map-api_"+ os + "-" + arch + ".tar.gz -C release/" + os + "/" + arch + " ubnt-freifunk-map-api"
],
},
{
name: "gitea_release " + os + "/" + arch,
image: "plugins/gitea-release",
settings: {
api_key: { "from_secret": "gitea_api_key" },
base_url: "https://git.freifunk-rhein-sieg.net",
files: "release/*.tar.gz"
},
when: {
event: "tag"
},
},
{
name: "upload to gitea container registry",
image: "plugins/docker:latest",
settings: {
repo: "git.freifunk-rhein-sieg.net/freifunk-troisdorf/ubnt-freifunk-map-api",
registry: "git.freifunk-rhein-sieg.net",
username: { "from_secret": "docker_username" },
password: { "from_secret": "docker_password" },
tags: ["latest"],
auto_tag: true,
},
when: {
event: "tag"
},
},
],
};
[
pipeline("linux", "amd64")
]

View File

@ -5,14 +5,17 @@
"APItoken": "UNMS API TOKEN",
"devicesURL": "https://git.freifunk-rhein-sieg.net/Freifunk-Troisdorf/ubnt-freifunk-map-api/raw/branch/master/example.devices.json"
},
"unifi": {
"unifi": [
{
"name": "Unifi Freifunk Troisdorf",
"enabled": false,
"displayusers": true,
"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"
},
}
],
"meshviewer": {
"enabled": false,
"files": [

53
main.go
View File

@ -9,21 +9,28 @@ import (
"net/http"
"net/http/cookiejar"
"os"
"sync"
"time"
_ "git.nils.zone/nils/prettify"
)
const (
iso8601 = "2006-01-02T15:04:05-0700"
iso8601 = "2006-01-02T15:04:05-0700"
fetchInterval = 1 * time.Minute
)
// flags
var (
lastFetchTime time.Time
cacheMutex sync.Mutex
cacheNodes []node
cacheLinks []link
)
var configPath = flag.String("configPath", "config.json", "Path to config.json")
var version = "development"
var delay time.Duration = 60 * time.Second
var conf = loadconfig(*configPath)
var ucDev = getDevices(conf.Unifi.UCDevicesURL)
func main() {
log.Printf("starting version %s...\n", version)
@ -55,27 +62,35 @@ func processAPIs() error {
var nodes []node
var links []link
if conf.Unms.Enabled {
log.Println("Processing UNMS")
unmsNodes, unmsLinks, err := processUNMSAPI()
if conf.UISP.Enabled {
log.Println("Processing UISP")
//Process UISP RiFu Nodes
uispNodes, uispLinks, err := processUISPRiFu()
if err != nil {
return err
}
unmsRouters, err := processUNMSAPIRouter()
//Process UISP Routers (like EDGE Router)
uispRouters, err := processUISPRouter()
if err != nil {
return err
}
nodes = append(nodes, unmsNodes...)
nodes = append(nodes, unmsRouters...)
links = append(links, unmsLinks...)
nodes = append(nodes, uispNodes...)
nodes = append(nodes, uispRouters...)
links = append(links, uispLinks...)
}
if conf.Unifi.Enabled {
log.Println("Processing Unifi")
ucNodes, _, err := processUcAPIs()
if err != nil {
return err
if len(conf.Unifi) > 0 {
log.Println("Anazahl der Unifi Server:", len(conf.Unifi))
for i := range conf.Unifi {
if conf.Unifi[i].Enabled {
log.Println("Processing Unifi-Server: ", conf.Unifi[i].Name)
//Process Unifi Nodes
unifiNodes, _, err := processUnifiAPI(i)
if err != nil {
return err
}
nodes = append(nodes, unifiNodes...)
}
}
nodes = append(nodes, ucNodes...)
}
if conf.Meshviewer.Enabled {
log.Println("Processing Meshviewer")
@ -85,6 +100,7 @@ func processAPIs() error {
}
if conf.Gateways.Enabled {
log.Println("Processing Gateways")
//Process Static Gateways from Json
gwNodes := processGateways()
nodes = append(nodes, gwNodes...)
}
@ -101,12 +117,7 @@ func processAPIs() error {
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

View File

@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"log"
"time"
)
func getMeshviewerJSON(url string) (mvDevices, error) {
@ -129,17 +130,31 @@ func addmvDevices(d mvDevices) ([]node, []link) {
}
func getMeshviewer() ([]node, []link) {
cacheMutex.Lock()
defer cacheMutex.Unlock()
// Überprüfen, ob die Daten kürzlich aktualisiert wurden
if time.Since(lastFetchTime) < fetchInterval {
return cacheNodes, cacheLinks
}
var nodes []node
var links []link
for i := range conf.Meshviewer.Files {
m, err := getMeshviewerJSON(conf.Meshviewer.Files[i].URL)
if err != nil {
return nodes, links
return cacheNodes, cacheLinks
}
mvNodes, mvLinks := addmvDevices(m)
nodes = append(nodes, mvNodes...)
links = append(links, mvLinks...)
}
return nodes, links
// Cache aktualisieren
cacheNodes = nodes
cacheLinks = links
lastFetchTime = time.Now()
return cacheNodes, cacheLinks
}

View File

@ -1,37 +0,0 @@
package main
import (
"encoding/csv"
"log"
"os"
)
func getUNMSLogs() UNMSLogResponse {
var l UNMSLogResponse
log.Println("Get Outages from UNMS")
err := UnmsCallAPI("/outages?count=100&page=1&type=outage", &l)
if err != nil {
log.Fatalln("Error calling Outages API")
}
return l
}
func writeOutagesToCSV(l UNMSLogResponse) error {
csvFile, err := os.Create("output/outages.csv")
if err != nil {
return err
}
writer := csv.NewWriter(csvFile)
for _, o := range l.Items {
var row []string
row = append(row, o.StartTime.Format("02.01.2006 15:04:05"))
row = append(row, o.EndTime.Format("02.01.2006 15:04:05"))
row = append(row, o.Site.Name)
row = append(row, o.Device.DisplayName)
writer.Write(row)
}
writer.Flush()
return nil
}

View File

@ -16,21 +16,14 @@ type config struct {
ProxmoxInfluxPort string `json:"proxmox_influx_port"`
InfluxURL string `json:"influx_url"`
}
Unms struct {
UISP struct {
Enabled bool `json:"enabled"`
UnmsAPIURL string `json:"unmsAPIUrl"`
APItoken string `json:"APItoken"`
DevicesURL string `json:"devicesURL"`
RouterURL string `json:"routerURL"`
} `json:"unms"`
Unifi struct {
Enabled bool `json:"enabled"`
DisplayUsers bool `json:"displayusers"`
APIURL string `json:"APIUrl"`
User string `json:"user"`
Password string `json:"password"`
UCDevicesURL string `json:"ucDevicesURL"`
} `json:"unifi"`
Unifi []UnifiServer `json:"unifi"`
Meshviewer struct {
Enabled bool `json:"enabled"`
Files []struct {
@ -44,6 +37,16 @@ type config struct {
} `json:"gateways"`
}
type UnifiServer struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
DisplayUsers bool `json:"displayusers"`
APIURL string `json:"APIUrl"`
User string `json:"user"`
Password string `json:"password"`
UCDevicesURL string `json:"ucDevicesURL"`
}
type device struct {
Name string `json:"name"`
FQDN string `json:"fqdn"`

View File

@ -15,14 +15,14 @@ import (
)
// Unifi Controller API processing
func processUcAPIs() ([]node, []link, error) {
func processUnifiAPI(s int) ([]node, []link, error) {
//get list of Unifi devices to display
var nodes []node
var links []link
d := getDevices(conf.Unifi.UCDevicesURL)
d := getDevices(conf.Unifi[s].UCDevicesURL)
//call Unifi Controller
ucAPI := UnifiNewAPI(conf.Unifi.User, conf.Unifi.Password, conf.Unifi.APIURL)
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
@ -49,12 +49,14 @@ func processUcAPIs() ([]node, []link, error) {
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
@ -78,7 +80,7 @@ func processUcAPIs() ([]node, []link, error) {
var model = lookupModels(currentDevice.Model)
var clients int
if conf.Unifi.DisplayUsers {
if conf.Unifi[s].DisplayUsers {
clients = currentDevice.Users
}
@ -130,40 +132,42 @@ func processUcAPIs() ([]node, []link, error) {
}
// INFLUX STOP
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: &currentJSONDevice.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,
})
//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: &currentJSONDevice.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
}
@ -268,9 +272,12 @@ func UnifiAddLink(dev device, links []link) []link {
}
func findNodeID(NodeID string) bool {
for i := range ucDev.Devices {
if ucDev.Devices[i].GatewayNexthop == NodeID {
return true
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

24
unms.go
View File

@ -17,12 +17,12 @@ import (
)
// UNMS API processing (Richtfunk)
func processUNMSAPI() ([]node, []link, error) {
func processUISPRiFu() ([]node, []link, error) {
// Variables for runtime
var links []link
var nodes []node
d := getDevices(conf.Unms.DevicesURL)
d := getDevices(conf.UISP.DevicesURL)
// API CALL 1 (get Device overview)
log.Println("Starting UISP API Crawler for Rifu devices")
@ -33,6 +33,7 @@ func processUNMSAPI() ([]node, []link, error) {
}
for i := range d.Devices {
time.Sleep(time.Second)
var dev unifiAPIResponse
var currentDevice device
for j := range u {
@ -103,10 +104,11 @@ func processUNMSAPI() ([]node, []link, error) {
return nodes, links, nil
}
func processUNMSAPIRouter() ([]node, error) {
func processUISPRouter() ([]node, error) {
time.Sleep(time.Second)
// Variables for runtime
var nodes []node
d := getDevices(conf.Unms.RouterURL)
d := getDevices(conf.UISP.RouterURL)
// API CALL 1, get all devices list from UNMS
log.Println("Get all Routers from UISP")
@ -234,19 +236,21 @@ func processUNMSAPIRouter() ([]node, error) {
}
func UnmsCallAPI(url string, i any) error {
request, err := http.NewRequest(http.MethodGet, conf.Unms.UnmsAPIURL+url, nil)
time.Sleep(time.Second)
request, err := http.NewRequest(http.MethodGet, conf.UISP.UnmsAPIURL+url, nil)
if err != nil {
return errors.New(fmt.Sprint("can't set request", conf.Unms.UnmsAPIURL+url))
return errors.New(fmt.Sprint("can't set request", conf.UISP.UnmsAPIURL+url))
}
log.Println(conf.Unms.UnmsAPIURL + url)
request.Header.Set("x-auth-token", conf.Unms.APItoken)
//log.Println(conf.UISP.UnmsAPIURL + url)
request.Header.Set("x-auth-token", conf.UISP.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)
return fmt.Errorf("can't get request %s with x-auth-token %s", conf.UISP.UnmsAPIURL+url, conf.UISP.APItoken)
}
if response.StatusCode != 200 {
log.Fatalln("Can't call UNMS API, check token and URL. HTTP Status: ", response.StatusCode)
log.Println("Can't call UNMS API, check token and URL. Skipping device. HTTP Status: ", response.StatusCode)
return nil
}
data, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()