Compare commits

..

No commits in common. "master" and "v1.3.3" have entirely different histories.

9 changed files with 179 additions and 138 deletions

53
.drone.jsonnet Normal file
View File

@ -0,0 +1,53 @@
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,17 +5,14 @@
"APItoken": "UNMS API TOKEN", "APItoken": "UNMS API TOKEN",
"devicesURL": "https://git.freifunk-rhein-sieg.net/Freifunk-Troisdorf/ubnt-freifunk-map-api/raw/branch/master/example.devices.json" "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, "enabled": false,
"displayusers": true, "displayusers": true,
"APIUrl": "https://unifi.freifunk-troisdorf.de", "APIUrl": "https://unifi.freifunk-troisdorf.de",
"user": "APIuser", "user": "APIuser",
"password": "PASSWORD", "password": "PASSWORD",
"ucDevicesURL": "https://git.freifunk-rhein-sieg.net/Freifunk-Troisdorf/ubnt-freifunk-map-api/raw/branch/master/example.ucDevices.json" "ucDevicesURL": "https://git.freifunk-rhein-sieg.net/Freifunk-Troisdorf/ubnt-freifunk-map-api/raw/branch/master/example.ucDevices.json"
} },
],
"meshviewer": { "meshviewer": {
"enabled": false, "enabled": false,
"files": [ "files": [

47
main.go
View File

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

View File

@ -3,7 +3,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"log" "log"
"time"
) )
func getMeshviewerJSON(url string) (mvDevices, error) { func getMeshviewerJSON(url string) (mvDevices, error) {
@ -130,31 +129,17 @@ func addmvDevices(d mvDevices) ([]node, []link) {
} }
func getMeshviewer() ([]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 nodes []node
var links []link var links []link
for i := range conf.Meshviewer.Files { for i := range conf.Meshviewer.Files {
m, err := getMeshviewerJSON(conf.Meshviewer.Files[i].URL) m, err := getMeshviewerJSON(conf.Meshviewer.Files[i].URL)
if err != nil { if err != nil {
return cacheNodes, cacheLinks return nodes, links
} }
mvNodes, mvLinks := addmvDevices(m) mvNodes, mvLinks := addmvDevices(m)
nodes = append(nodes, mvNodes...) nodes = append(nodes, mvNodes...)
links = append(links, mvLinks...) links = append(links, mvLinks...)
} }
return nodes, links
// Cache aktualisieren
cacheNodes = nodes
cacheLinks = links
lastFetchTime = time.Now()
return cacheNodes, cacheLinks
} }

37
outages.go Normal file
View File

@ -0,0 +1,37 @@
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

@ -57,9 +57,7 @@ func processGateways() []node {
if err != nil { if err != nil {
log.Fatalln("Error: ", err) log.Fatalln("Error: ", err)
} }
if conf.General.InfluxEnabled {
sendInfluxBatchDataPoint(point, conf.General.FreifunkInfluxPort) sendInfluxBatchDataPoint(point, conf.General.FreifunkInfluxPort)
}
//Build Nodes //Build Nodes
nodes = append(nodes, node{ nodes = append(nodes, node{

View File

@ -11,19 +11,25 @@ import (
type config struct { type config struct {
General struct { General struct {
InfluxEnabled bool `json:"influx_enabled"`
FreifunkInfluxPort string `json:"freifunk_influx_port"` FreifunkInfluxPort string `json:"freifunk_influx_port"`
ProxmoxInfluxPort string `json:"proxmox_influx_port"` ProxmoxInfluxPort string `json:"proxmox_influx_port"`
InfluxURL string `json:"influx_url"` InfluxURL string `json:"influx_url"`
} }
UISP struct { Unms struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
UnmsAPIURL string `json:"unmsAPIUrl"` UnmsAPIURL string `json:"unmsAPIUrl"`
APItoken string `json:"APItoken"` APItoken string `json:"APItoken"`
DevicesURL string `json:"devicesURL"` DevicesURL string `json:"devicesURL"`
RouterURL string `json:"routerURL"` RouterURL string `json:"routerURL"`
} `json:"unms"` } `json:"unms"`
Unifi []UnifiServer `json:"unifi"` 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"`
Meshviewer struct { Meshviewer struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
Files []struct { Files []struct {
@ -37,16 +43,6 @@ type config struct {
} `json:"gateways"` } `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 { type device struct {
Name string `json:"name"` Name string `json:"name"`
FQDN string `json:"fqdn"` FQDN string `json:"fqdn"`

View File

@ -15,14 +15,14 @@ import (
) )
// Unifi Controller API processing // Unifi Controller API processing
func processUnifiAPI(s int) ([]node, []link, error) { func processUcAPIs() ([]node, []link, error) {
//get list of Unifi devices to display //get list of Unifi devices to display
var nodes []node var nodes []node
var links []link var links []link
d := getDevices(conf.Unifi[s].UCDevicesURL) d := getDevices(conf.Unifi.UCDevicesURL)
//call Unifi Controller //call Unifi Controller
ucAPI := UnifiNewAPI(conf.Unifi[s].User, conf.Unifi[s].Password, conf.Unifi[s].APIURL) ucAPI := UnifiNewAPI(conf.Unifi.User, conf.Unifi.Password, conf.Unifi.APIURL)
//login //login
if err := ucAPI.ucLogin(); err != nil { if err := ucAPI.ucLogin(); err != nil {
return nil, nil, err return nil, nil, err
@ -49,14 +49,12 @@ func processUnifiAPI(s int) ([]node, []link, error) {
currentJSONDevice = jsonDevice currentJSONDevice = jsonDevice
} }
} }
if isRemoteMACpublished(jsonDevice.MAC, d.Devices) { if isRemoteMACpublished(jsonDevice.MAC, d.Devices) {
//hier muss gecheckt werden ob der link valide ist //hier muss gecheckt werden ob der link valide ist
if checkMeshviewerLink(jsonDevice.LinkedTo) { if checkMeshviewerLink(jsonDevice.LinkedTo) {
links = UnifiAddLink(jsonDevice, links) links = UnifiAddLink(jsonDevice, links)
} }
} }
isOnline := currentDevice.State == 1 isOnline := currentDevice.State == 1
var load float64 var load float64
var mem float64 var mem float64
@ -80,7 +78,7 @@ func processUnifiAPI(s int) ([]node, []link, error) {
var model = lookupModels(currentDevice.Model) var model = lookupModels(currentDevice.Model)
var clients int var clients int
if conf.Unifi[s].DisplayUsers { if conf.Unifi.DisplayUsers {
clients = currentDevice.Users clients = currentDevice.Users
} }
@ -127,13 +125,10 @@ func processUnifiAPI(s int) ([]node, []link, error) {
log.Fatalln("Error: ", err) log.Fatalln("Error: ", err)
} }
if conf.General.InfluxEnabled {
sendInfluxBatchDataPoint(point, conf.General.FreifunkInfluxPort) sendInfluxBatchDataPoint(point, conf.General.FreifunkInfluxPort)
}
// INFLUX STOP // INFLUX STOP
//log.Println(currentDevice.Mac)
if currentDevice.Mac != "" {
nodes = append(nodes, node{ nodes = append(nodes, node{
Firstseen: "0", Firstseen: "0",
Lastseen: time.Unix(int64(currentDevice.LastSeen), 0).Format(iso8601), Lastseen: time.Unix(int64(currentDevice.LastSeen), 0).Format(iso8601),
@ -168,7 +163,6 @@ func processUnifiAPI(s int) ([]node, []link, error) {
Model: model, Model: model,
}) })
} }
}
return nodes, links, err return nodes, links, err
} }
@ -272,13 +266,10 @@ func UnifiAddLink(dev device, links []link) []link {
} }
func findNodeID(NodeID string) bool { func findNodeID(NodeID string) bool {
for s := range conf.Unifi {
ucDev := getDevices(conf.Unifi[s].UCDevicesURL)
for i := range ucDev.Devices { for i := range ucDev.Devices {
if ucDev.Devices[i].GatewayNexthop == NodeID { if ucDev.Devices[i].GatewayNexthop == NodeID {
return true return true
} }
} }
}
return false return false
} }

27
unms.go
View File

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