improve complete code with comments and co (thanks linter)
This commit is contained in:
parent
798db6a063
commit
d855248f6a
@ -8,9 +8,8 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// 7 nachkommerstellen sollten genug sein (7cm genau)
|
||||
// GEOROUND : 7 nachkommerstellen sollten genug sein (7cm genau)
|
||||
// http://blog.3960.org/post/7309573249/genauigkeit-bei-geo-koordinaten
|
||||
|
||||
const GEOROUND = 0.0000001
|
||||
|
||||
func geoEqual(a, b float64) bool {
|
||||
@ -20,29 +19,33 @@ func geoEqual(a, b float64) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type ApiAliases struct {
|
||||
// AliasesAPI struct for API
|
||||
type AliasesAPI struct {
|
||||
aliases *models.Aliases
|
||||
config *models.Config
|
||||
nodes *models.Nodes
|
||||
}
|
||||
|
||||
// NewAliases Bind to API
|
||||
func NewAliases(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) {
|
||||
api := &ApiAliases{
|
||||
api := &AliasesAPI{
|
||||
aliases: models.NewAliases(config),
|
||||
nodes: nodes,
|
||||
config: config,
|
||||
}
|
||||
router.GET(prefix, api.GetAll)
|
||||
router.GET(prefix+"/ansible", api.AnsibleDiff)
|
||||
router.GET(prefix+"/ansible", api.Ansible)
|
||||
router.GET(prefix+"/alias/:nodeid", api.GetOne)
|
||||
router.POST(prefix+"/alias/:nodeid", BasicAuth(api.SaveOne, []byte(config.Webserver.Api.Passphrase)))
|
||||
router.POST(prefix+"/alias/:nodeid", BasicAuth(api.SaveOne, []byte(config.Webserver.API.Passphrase)))
|
||||
}
|
||||
|
||||
func (api *ApiAliases) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
// GetAll request for get all aliases
|
||||
func (api *AliasesAPI) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
jsonOutput(w, r, api.aliases.List)
|
||||
}
|
||||
|
||||
func (api *ApiAliases) GetOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
// GetOne request for get one alias
|
||||
func (api *AliasesAPI) GetOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if alias := api.aliases.List[ps.ByName("nodeid")]; alias != nil {
|
||||
jsonOutput(w, r, alias)
|
||||
return
|
||||
@ -50,7 +53,8 @@ func (api *ApiAliases) GetOne(w http.ResponseWriter, r *http.Request, ps httprou
|
||||
fmt.Fprint(w, "Not found: ", ps.ByName("nodeid"), "\n")
|
||||
}
|
||||
|
||||
func (api *ApiAliases) SaveOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
// SaveOne request for save a alias
|
||||
func (api *AliasesAPI) SaveOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
var alias models.Alias
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&alias)
|
||||
@ -63,7 +67,9 @@ func (api *ApiAliases) SaveOne(w http.ResponseWriter, r *http.Request, ps httpro
|
||||
fmt.Print("[api] node updated '", ps.ByName("nodeid"), "'\n")
|
||||
jsonOutput(w, r, alias)
|
||||
}
|
||||
func (api *ApiAliases) AnsibleDiff(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
|
||||
// Ansible json output
|
||||
func (api *AliasesAPI) Ansible(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
fmt.Print("[api] ansible\n")
|
||||
jsonOutput(w, r, models.GenerateAnsible(api.nodes, api.aliases.List))
|
||||
}
|
||||
|
72
api/lib.go
72
api/lib.go
@ -2,15 +2,15 @@ package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
func jsonOutput(w http.ResponseWriter, r *http.Request,data interface{}){
|
||||
func jsonOutput(w http.ResponseWriter, r *http.Request, data interface{}) {
|
||||
js, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -22,40 +22,42 @@ func jsonOutput(w http.ResponseWriter, r *http.Request,data interface{}){
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
|
||||
w.Header().Set("Access-Control-Allow-Headers","Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
// BasicAuth for API request
|
||||
func BasicAuth(h httprouter.Handle, pass []byte) httprouter.Handle {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if origin := r.Header.Get("Origin"); origin != "" {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
|
||||
w.Header().Set("Access-Control-Allow-Headers","Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
const basicAuthPrefix string = "Basic "
|
||||
|
||||
// Get the Basic Authentication credentials
|
||||
auth := r.Header.Get("Authorization")
|
||||
if strings.HasPrefix(auth, basicAuthPrefix) {
|
||||
// Check credentials
|
||||
payload, err := base64.StdEncoding.DecodeString(auth[len(basicAuthPrefix):])
|
||||
if err == nil {
|
||||
pair := bytes.SplitN(payload, []byte(":"), 2)
|
||||
if len(pair) == 2 &&
|
||||
bytes.Equal(pair[1], pass) {
|
||||
|
||||
// Delegate request to the given handle
|
||||
h(w, r, ps)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request Basic Authentication otherwise
|
||||
w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if origin := r.Header.Get("Origin"); origin != "" {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
const basicAuthPrefix string = "Basic "
|
||||
|
||||
// Get the Basic Authentication credentials
|
||||
auth := r.Header.Get("Authorization")
|
||||
if strings.HasPrefix(auth, basicAuthPrefix) {
|
||||
// Check credentials
|
||||
payload, err := base64.StdEncoding.DecodeString(auth[len(basicAuthPrefix):])
|
||||
if err == nil {
|
||||
pair := bytes.SplitN(payload, []byte(":"), 2)
|
||||
if len(pair) == 2 &&
|
||||
bytes.Equal(pair[1], pass) {
|
||||
|
||||
// Delegate request to the given handle
|
||||
h(w, r, ps)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request Basic Authentication otherwise
|
||||
w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
|
@ -6,19 +6,22 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ApiNodes struct {
|
||||
// NodesAPI struct for API
|
||||
type NodesAPI struct {
|
||||
config *models.Config
|
||||
nodes *models.Nodes
|
||||
}
|
||||
|
||||
// NewNodes Bind to API
|
||||
func NewNodes(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) {
|
||||
api := &ApiNodes{
|
||||
api := &NodesAPI{
|
||||
nodes: nodes,
|
||||
config: config,
|
||||
}
|
||||
router.GET(prefix, api.GetAll)
|
||||
}
|
||||
|
||||
func (api *ApiNodes) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
// GetAll request for get all nodes
|
||||
func (api *NodesAPI) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
jsonOutput(w, r, api.nodes.List)
|
||||
}
|
||||
|
@ -57,11 +57,11 @@ func main() {
|
||||
|
||||
if config.Webserver.Enable {
|
||||
router := httprouter.New()
|
||||
if config.Webserver.Api.NewNodes {
|
||||
if config.Webserver.API.NewNodes {
|
||||
api.NewNodes(config, router, "/api/nodes", nodes)
|
||||
log.Println("api nodes started")
|
||||
}
|
||||
if config.Webserver.Api.Aliases {
|
||||
if config.Webserver.API.Aliases {
|
||||
api.NewAliases(config, router, "/api/aliases", nodes)
|
||||
log.Println("api aliases started")
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// Wireless struct
|
||||
type Wireless struct {
|
||||
TxPower24 uint32 `json:"txpower24,omitempty"`
|
||||
Channel24 uint32 `json:"channel24,omitempty"`
|
||||
@ -11,30 +12,32 @@ type Wireless struct {
|
||||
Channel5 uint32 `json:"channel5,omitempty"`
|
||||
}
|
||||
|
||||
// WirelessStatistics struct
|
||||
type WirelessStatistics []*WirelessAirtime
|
||||
|
||||
// WirelessAirtime struct
|
||||
type WirelessAirtime struct {
|
||||
ChanUtil float32 // Channel utilization
|
||||
RxUtil float32 // Receive utilization
|
||||
TxUtil float32 // Transmit utilization
|
||||
|
||||
Active_time uint64 `json:"active"`
|
||||
Busy_time uint64 `json:"busy"`
|
||||
Rx_time uint64 `json:"rx"`
|
||||
Tx_time uint64 `json:"tx"`
|
||||
Noise uint32 `json:"noise"`
|
||||
Frequency uint32 `json:"frequency"`
|
||||
ActiveTime uint64 `json:"active"`
|
||||
BusyTime uint64 `json:"busy"`
|
||||
RxTime uint64 `json:"rx"`
|
||||
TxTime uint64 `json:"tx"`
|
||||
Noise uint32 `json:"noise"`
|
||||
Frequency uint32 `json:"frequency"`
|
||||
}
|
||||
|
||||
// FrequencyName to 11g or 11a
|
||||
func (airtime WirelessAirtime) FrequencyName() string {
|
||||
if airtime.Frequency < 5000 {
|
||||
return "11g"
|
||||
} else {
|
||||
return "11a"
|
||||
}
|
||||
return "11a"
|
||||
}
|
||||
|
||||
// Calculates the utilization values in regard to the previous values
|
||||
// SetUtilization Calculates the utilization values in regard to the previous values
|
||||
func (current WirelessStatistics) SetUtilization(previous WirelessStatistics) {
|
||||
for _, c := range current {
|
||||
for _, p := range previous {
|
||||
@ -45,21 +48,21 @@ func (current WirelessStatistics) SetUtilization(previous WirelessStatistics) {
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates the utilization values in regard to the previous values
|
||||
func (cur *WirelessAirtime) SetUtilization(prev *WirelessAirtime) {
|
||||
if cur.Active_time <= prev.Active_time {
|
||||
// SetUtilization Calculates the utilization values in regard to the previous values
|
||||
func (airtime *WirelessAirtime) SetUtilization(prev *WirelessAirtime) {
|
||||
if airtime.ActiveTime <= prev.ActiveTime {
|
||||
return
|
||||
}
|
||||
|
||||
active := float64(cur.Active_time) - float64(prev.Active_time)
|
||||
busy := float64(cur.Busy_time) - float64(prev.Busy_time)
|
||||
rx := float64(cur.Tx_time) - float64(prev.Tx_time)
|
||||
tx := float64(cur.Rx_time) - float64(prev.Rx_time)
|
||||
active := float64(airtime.ActiveTime) - float64(prev.ActiveTime)
|
||||
busy := float64(airtime.BusyTime) - float64(prev.BusyTime)
|
||||
rx := float64(airtime.TxTime) - float64(prev.TxTime)
|
||||
tx := float64(airtime.RxTime) - float64(prev.RxTime)
|
||||
|
||||
// Calculate utilizations
|
||||
if active > 0 {
|
||||
cur.ChanUtil = float32(math.Min(100, 100*(busy+rx+tx)/active))
|
||||
cur.RxUtil = float32(math.Min(100, 100*rx/active))
|
||||
cur.TxUtil = float32(math.Min(100, 100*tx/active))
|
||||
airtime.ChanUtil = float32(math.Min(100, 100*(busy+rx+tx)/active))
|
||||
airtime.RxUtil = float32(math.Min(100, 100*rx/active))
|
||||
airtime.TxUtil = float32(math.Min(100, 100*tx/active))
|
||||
}
|
||||
}
|
||||
|
@ -18,22 +18,22 @@ func TestUtilization(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
t1 := &WirelessAirtime{
|
||||
Active_time: 20,
|
||||
Busy_time: 0,
|
||||
Tx_time: 5,
|
||||
Rx_time: 0,
|
||||
ActiveTime: 20,
|
||||
BusyTime: 0,
|
||||
TxTime: 5,
|
||||
RxTime: 0,
|
||||
}
|
||||
t2 := &WirelessAirtime{
|
||||
Active_time: 120,
|
||||
Busy_time: 10,
|
||||
Tx_time: 25,
|
||||
Rx_time: 15,
|
||||
ActiveTime: 120,
|
||||
BusyTime: 10,
|
||||
TxTime: 25,
|
||||
RxTime: 15,
|
||||
}
|
||||
t3 := &WirelessAirtime{
|
||||
Active_time: 200,
|
||||
Busy_time: 40,
|
||||
Tx_time: 35,
|
||||
Rx_time: 15,
|
||||
ActiveTime: 200,
|
||||
BusyTime: 40,
|
||||
TxTime: 35,
|
||||
RxTime: 15,
|
||||
}
|
||||
|
||||
t1.SetUtilization(t2)
|
||||
@ -58,23 +58,23 @@ func TestWirelessStatistics(t *testing.T) {
|
||||
|
||||
stats := WirelessStatistics([]*WirelessAirtime{{
|
||||
Frequency: 2400,
|
||||
Active_time: 20,
|
||||
Tx_time: 10,
|
||||
ActiveTime: 20,
|
||||
TxTime: 10,
|
||||
}})
|
||||
|
||||
// Different Frequency, should not change anything
|
||||
stats.SetUtilization([]*WirelessAirtime{{
|
||||
Frequency: 5000,
|
||||
Active_time: 15,
|
||||
Tx_time: 1,
|
||||
ActiveTime: 15,
|
||||
TxTime: 1,
|
||||
}})
|
||||
assert.EqualValues(0, stats[0].ChanUtil)
|
||||
|
||||
// Same Frequency, should set the utilization
|
||||
stats.SetUtilization([]*WirelessAirtime{{
|
||||
Frequency: 2400,
|
||||
Active_time: 10,
|
||||
Tx_time: 5,
|
||||
ActiveTime: 10,
|
||||
TxTime: 5,
|
||||
}})
|
||||
assert.EqualValues(50, stats[0].ChanUtil)
|
||||
}
|
||||
|
@ -1,34 +1,41 @@
|
||||
package data
|
||||
|
||||
// Neighbours struct
|
||||
type Neighbours struct {
|
||||
Batadv map[string]BatadvNeighbours `json:"batadv"`
|
||||
LLDP map[string]LLDPNeighbours `json:"lldp"`
|
||||
//WifiNeighbours map[string]WifiNeighbours `json:"wifi"`
|
||||
NodeId string `json:"node_id"`
|
||||
NodeID string `json:"node_id"`
|
||||
}
|
||||
|
||||
// WifiLink struct
|
||||
type WifiLink struct {
|
||||
Inactive int `json:"inactive"`
|
||||
Noise int `json:"nois"`
|
||||
Signal int `json:"signal"`
|
||||
}
|
||||
|
||||
// BatmanLink struct
|
||||
type BatmanLink struct {
|
||||
Lastseen float64 `json:"lastseen"`
|
||||
Tq int `json:"tq"`
|
||||
}
|
||||
|
||||
// LLDPLink struct
|
||||
type LLDPLink struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"descr"`
|
||||
}
|
||||
|
||||
// BatadvNeighbours struct
|
||||
type BatadvNeighbours struct {
|
||||
Neighbours map[string]BatmanLink `json:"neighbours"`
|
||||
}
|
||||
|
||||
// WifiNeighbours struct
|
||||
type WifiNeighbours struct {
|
||||
Neighbours map[string]WifiLink `json:"neighbours"`
|
||||
}
|
||||
|
||||
// LLDPNeighbours struct
|
||||
type LLDPNeighbours map[string]LLDPLink
|
||||
|
@ -1,7 +1,8 @@
|
||||
package data
|
||||
|
||||
// NodeInfo struct
|
||||
type NodeInfo struct {
|
||||
NodeId string `json:"node_id"`
|
||||
NodeID string `json:"node_id"`
|
||||
Network Network `json:"network"`
|
||||
Owner *Owner `json:"-"` // Removed for privacy reasons
|
||||
System System `json:"system"`
|
||||
@ -12,6 +13,8 @@ type NodeInfo struct {
|
||||
VPN bool `json:"vpn"`
|
||||
Wireless *Wireless `json:"wireless,omitempty"`
|
||||
}
|
||||
|
||||
// BatInterface struct
|
||||
type BatInterface struct {
|
||||
Interfaces struct {
|
||||
Wireless []string `json:"wireless,omitempty"`
|
||||
@ -20,6 +23,7 @@ type BatInterface struct {
|
||||
} `json:"interfaces"`
|
||||
}
|
||||
|
||||
// Network struct
|
||||
type Network struct {
|
||||
Mac string `json:"mac"`
|
||||
Addresses []string `json:"addresses"`
|
||||
@ -27,20 +31,24 @@ type Network struct {
|
||||
MeshInterfaces []string `json:"mesh_interfaces"`
|
||||
}
|
||||
|
||||
// Owner struct
|
||||
type Owner struct {
|
||||
Contact string `json:"contact"`
|
||||
}
|
||||
|
||||
// System struct
|
||||
type System struct {
|
||||
SiteCode string `json:"site_code"`
|
||||
}
|
||||
|
||||
// Location struct
|
||||
type Location struct {
|
||||
Longtitude float64 `json:"longitude"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Altitude float64 `json:"altitude,omitempty"`
|
||||
}
|
||||
|
||||
// Software struct
|
||||
type Software struct {
|
||||
Autoupdater struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
@ -59,10 +67,11 @@ type Software struct {
|
||||
Release string `json:"release,omitempty"`
|
||||
} `json:"firmware,omitempty"`
|
||||
StatusPage struct {
|
||||
Api int `json:"api"`
|
||||
API int `json:"api"`
|
||||
} `json:"status-page,omitempty"`
|
||||
}
|
||||
|
||||
// Hardware struct
|
||||
type Hardware struct {
|
||||
Nproc int `json:"nproc"`
|
||||
Model string `json:"model"`
|
||||
|
@ -1,5 +1,6 @@
|
||||
package data
|
||||
|
||||
// ResponseData struct
|
||||
type ResponseData struct {
|
||||
Neighbours *Neighbours `json:"neighbours"`
|
||||
NodeInfo *NodeInfo `json:"nodeinfo"`
|
||||
|
@ -5,8 +5,9 @@ package data
|
||||
They always return float.
|
||||
*/
|
||||
|
||||
//Statistics struct
|
||||
type Statistics struct {
|
||||
NodeId string `json:"node_id"`
|
||||
NodeID string `json:"node_id"`
|
||||
Clients Clients `json:"clients"`
|
||||
RootFsUsage float64 `json:"rootfs_usage,omitempty"`
|
||||
LoadAverage float64 `json:"loadavg,omitempty"`
|
||||
@ -30,25 +31,30 @@ type Statistics struct {
|
||||
Wireless WirelessStatistics `json:"wireless,omitempty"`
|
||||
}
|
||||
|
||||
// MeshVPNPeerLink struct
|
||||
type MeshVPNPeerLink struct {
|
||||
Established float64 `json:"established"`
|
||||
}
|
||||
|
||||
// MeshVPNPeerGroup struct
|
||||
type MeshVPNPeerGroup struct {
|
||||
Peers map[string]*MeshVPNPeerLink `json:"peers"`
|
||||
Groups map[string]*MeshVPNPeerGroup `json:"groups"`
|
||||
}
|
||||
|
||||
// MeshVPN struct
|
||||
type MeshVPN struct {
|
||||
Groups map[string]*MeshVPNPeerGroup `json:"groups,omitempty"`
|
||||
}
|
||||
|
||||
// Traffic struct
|
||||
type Traffic struct {
|
||||
Bytes float64 `json:"bytes,omitempty"`
|
||||
Packets float64 `json:"packets,omitempty"`
|
||||
Dropped float64 `json:"dropped,omitempty"`
|
||||
}
|
||||
|
||||
// Clients struct
|
||||
type Clients struct {
|
||||
Wifi uint32 `json:"wifi"`
|
||||
Wifi24 uint32 `json:"wifi24"`
|
||||
@ -56,6 +62,7 @@ type Clients struct {
|
||||
Total uint32 `json:"total"`
|
||||
}
|
||||
|
||||
// Memory struct
|
||||
type Memory struct {
|
||||
Cached uint32 `json:"cached"`
|
||||
Total uint32 `json:"total"`
|
||||
@ -63,6 +70,7 @@ type Memory struct {
|
||||
Free uint32 `json:"free"`
|
||||
}
|
||||
|
||||
// SwitchPort struct
|
||||
type SwitchPort struct {
|
||||
Speed uint32 `json:"speed"`
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ func TestStatistics(t *testing.T) {
|
||||
obj := &Statistics{}
|
||||
testfile("statistics.json", obj)
|
||||
|
||||
assert.Equal("f81a67a601ea", obj.NodeId)
|
||||
assert.Equal("f81a67a601ea", obj.NodeID)
|
||||
assert.Equal("52:54:00:a9:f7:6e", obj.Gateway)
|
||||
assert.Equal(float64(57861871176), obj.Traffic.Rx.Bytes)
|
||||
assert.Equal(uint32(35), obj.Clients.Total)
|
||||
|
@ -85,11 +85,12 @@ func (db *DB) AddCounterMap(name string, m models.CounterMap) {
|
||||
}
|
||||
|
||||
// Add data for a single node
|
||||
func (db *DB) Add(nodeId string, node *models.Node) {
|
||||
func (db *DB) Add(nodeID string, node *models.Node) {
|
||||
tags, fields := node.ToInflux()
|
||||
db.AddPoint(MeasurementNode, tags, fields, time.Now())
|
||||
}
|
||||
|
||||
// Close all connection and clean up
|
||||
func (db *DB) Close() {
|
||||
close(db.quit)
|
||||
close(db.points)
|
||||
|
@ -5,21 +5,26 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// TimeFormat of JSONTime
|
||||
const TimeFormat = "2006-01-02T15:04:05-0700"
|
||||
|
||||
//Time struct of JSONTime
|
||||
type Time struct {
|
||||
time time.Time
|
||||
}
|
||||
|
||||
// Now current Time
|
||||
func Now() Time {
|
||||
return Time{time.Now()}
|
||||
}
|
||||
|
||||
//MarshalJSON to bytearray
|
||||
func (t Time) MarshalJSON() ([]byte, error) {
|
||||
stamp := `"` + t.time.Format(TimeFormat) + `"`
|
||||
return []byte(stamp), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON from bytearray
|
||||
func (t *Time) UnmarshalJSON(data []byte) (err error) {
|
||||
if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
|
||||
return errors.New("invalid jsontime")
|
||||
@ -29,24 +34,33 @@ func (t *Time) UnmarshalJSON(data []byte) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetTime normal
|
||||
func (t Time) GetTime() time.Time {
|
||||
return t.time
|
||||
}
|
||||
|
||||
// Unix of this time
|
||||
func (t Time) Unix() int64 {
|
||||
return t.time.Unix()
|
||||
}
|
||||
|
||||
// IsZero is time zero?
|
||||
func (t Time) IsZero() bool {
|
||||
return t.time.IsZero()
|
||||
}
|
||||
|
||||
// Add given Duration to this time
|
||||
func (t Time) Add(d time.Duration) Time {
|
||||
return Time{time: t.time.Add(d)}
|
||||
}
|
||||
|
||||
// After is this time after the given?
|
||||
func (t Time) After(u Time) bool {
|
||||
return t.time.After(u.GetTime())
|
||||
}
|
||||
|
||||
// Before is this time before the given?
|
||||
func (t Time) Before(u Time) bool {
|
||||
return t.time.Before(u.GetTime())
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ type Node struct {
|
||||
Neighbours *data.Neighbours `json:"-"`
|
||||
}
|
||||
|
||||
// Flags status of node set by collector for the meshviewer
|
||||
type Flags struct {
|
||||
Online bool `json:"online"`
|
||||
Gateway bool `json:"gateway"`
|
||||
@ -36,8 +37,9 @@ type NodesV2 struct {
|
||||
List []*Node `json:"nodes"` // the current nodemap, as array
|
||||
}
|
||||
|
||||
// Statistics a meshviewer spezifisch struct, diffrent from respondd
|
||||
type Statistics struct {
|
||||
NodeId string `json:"node_id"`
|
||||
NodeID string `json:"node_id"`
|
||||
Clients uint32 `json:"clients"`
|
||||
RootFsUsage float64 `json:"rootfs_usage,omitempty"`
|
||||
LoadAverage float64 `json:"loadavg,omitempty"`
|
||||
@ -59,18 +61,19 @@ type Statistics struct {
|
||||
} `json:"traffic,omitempty"`
|
||||
}
|
||||
|
||||
// NewStatistics transform respond Statistics to meshviewer Statistics
|
||||
func NewStatistics(stats *data.Statistics) *Statistics {
|
||||
total := stats.Clients.Total
|
||||
if total == 0 {
|
||||
total = stats.Clients.Wifi24 + stats.Clients.Wifi5
|
||||
}
|
||||
/* The Meshviewer could not handle absolute memory output
|
||||
* calc the used memory as a float witch 100% equal 1.0
|
||||
*/
|
||||
* calc the used memory as a float witch 100% equal 1.0
|
||||
*/
|
||||
memoryUsage := (float64(stats.Memory.Total) - float64(stats.Memory.Free)) / float64(stats.Memory.Total)
|
||||
|
||||
return &Statistics{
|
||||
NodeId: stats.NodeId,
|
||||
NodeID: stats.NodeID,
|
||||
Gateway: stats.Gateway,
|
||||
RootFsUsage: stats.RootFsUsage,
|
||||
LoadAverage: stats.LoadAverage,
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/FreifunkBremen/respond-collector/data"
|
||||
)
|
||||
|
||||
// Alias a change request for other nodes
|
||||
type Alias struct {
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
Location *data.Location `json:"location,omitempty"`
|
||||
@ -17,14 +18,14 @@ type Alias struct {
|
||||
Owner string `json:"owner,omitempty"`
|
||||
}
|
||||
|
||||
// Nodes struct: cache DB of Node's structs
|
||||
// Aliases struct: cache DB of Node's structs
|
||||
type Aliases struct {
|
||||
List map[string]*Alias `json:"nodes"` // the current nodemap, indexed by node ID
|
||||
config *Config
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewNodes create Nodes structs
|
||||
// NewAliases create Nodes structs
|
||||
func NewAliases(config *Config) *Aliases {
|
||||
aliases := &Aliases{
|
||||
List: make(map[string]*Alias),
|
||||
@ -39,6 +40,7 @@ func NewAliases(config *Config) *Aliases {
|
||||
return aliases
|
||||
}
|
||||
|
||||
// Update a alias in aliases cache
|
||||
func (e *Aliases) Update(nodeID string, newalias *Alias) {
|
||||
e.Lock()
|
||||
e.List[nodeID] = newalias
|
||||
@ -51,7 +53,7 @@ func (e *Aliases) load() {
|
||||
log.Println("loading", path)
|
||||
|
||||
if data, err := ioutil.ReadFile(path); err == nil {
|
||||
if err := json.Unmarshal(data, e); err == nil {
|
||||
if err = json.Unmarshal(data, e); err == nil {
|
||||
log.Println("loaded", len(e.List), "aliases")
|
||||
} else {
|
||||
log.Println("failed to unmarshal nodes:", err)
|
||||
|
@ -1,11 +1,14 @@
|
||||
package models
|
||||
|
||||
// Ansible struct
|
||||
type Ansible struct {
|
||||
Nodes []string `json:"nodes"`
|
||||
Meta struct {
|
||||
HostVars map[string]*AnsibleHostVars `json:"hostvars,omitempty"`
|
||||
} `json:"_meta"`
|
||||
}
|
||||
|
||||
// AnsibleHostVars new values for a node
|
||||
type AnsibleHostVars struct {
|
||||
Address string `json:"ansible_ssh_host"`
|
||||
Hostname string `json:"node_name,omitempty"`
|
||||
@ -18,6 +21,7 @@ type AnsibleHostVars struct {
|
||||
GeoLongitude float64 `json:"geo_longitude,omitempty"`
|
||||
}
|
||||
|
||||
// GenerateAnsible but nodes and aliases together to a ansible change output
|
||||
func GenerateAnsible(nodes *Nodes, aliases map[string]*Alias) *Ansible {
|
||||
ansible := &Ansible{Nodes: make([]string, 0)}
|
||||
ansible.Meta.HostVars = make(map[string]*AnsibleHostVars)
|
||||
|
@ -19,7 +19,7 @@ type Config struct {
|
||||
Port string `yaml:"port"`
|
||||
Address string `yaml:"address"`
|
||||
Webroot string `yaml:"webroot"`
|
||||
Api struct {
|
||||
API struct {
|
||||
Passphrase string `yaml:"passphrase"`
|
||||
NewNodes bool `yaml:"newnodes"`
|
||||
Aliases bool `yaml:"aliases"`
|
||||
@ -47,7 +47,7 @@ type Config struct {
|
||||
}
|
||||
}
|
||||
|
||||
// reads a config models by path to a yml file
|
||||
// ReadConfigFile reads a config model from path of a yml file
|
||||
func ReadConfigFile(path string) *Config {
|
||||
config := &Config{}
|
||||
file, _ := ioutil.ReadFile(path)
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Graph a struct for all links between the nodes
|
||||
type Graph struct {
|
||||
Version int `json:"version"`
|
||||
Batadv struct {
|
||||
@ -15,10 +16,13 @@ type Graph struct {
|
||||
} `json:"batadv"`
|
||||
}
|
||||
|
||||
// GraphNode small struct of a node for the graph struct
|
||||
type GraphNode struct {
|
||||
ID string `json:"id"`
|
||||
NodeID string `json:"node_id"`
|
||||
}
|
||||
|
||||
// GraphLink a struct for the link between two nodes
|
||||
type GraphLink struct {
|
||||
Source int `json:"source"`
|
||||
Target int `json:"target"`
|
||||
@ -27,14 +31,16 @@ type GraphLink struct {
|
||||
Bidirect bool `json:"bidirect"`
|
||||
}
|
||||
|
||||
type GraphBuilder struct {
|
||||
// GraphBuilder a temporaty struct during fill the graph from the node neighbours
|
||||
type graphBuilder struct {
|
||||
macToID map[string]string // mapping from MAC address to node id
|
||||
links map[string]*GraphLink // mapping from $idA-$idB to existing link
|
||||
vpn map[string]interface{} // IDs/addresses of VPN servers
|
||||
}
|
||||
|
||||
// BuildGraph transform from nodes (Neighbours) to Graph
|
||||
func (nodes *Nodes) BuildGraph() *Graph {
|
||||
builder := &GraphBuilder{
|
||||
builder := &graphBuilder{
|
||||
macToID: make(map[string]string),
|
||||
links: make(map[string]*GraphLink),
|
||||
vpn: make(map[string]interface{}),
|
||||
@ -44,11 +50,11 @@ func (nodes *Nodes) BuildGraph() *Graph {
|
||||
|
||||
graph := &Graph{Version: 1}
|
||||
graph.Batadv.Directed = false
|
||||
graph.Batadv.Nodes, graph.Batadv.Links = builder.Extract()
|
||||
graph.Batadv.Nodes, graph.Batadv.Links = builder.extract()
|
||||
return graph
|
||||
}
|
||||
|
||||
func (builder *GraphBuilder) readNodes(nodes map[string]*Node) {
|
||||
func (builder *graphBuilder) readNodes(nodes map[string]*Node) {
|
||||
// Fill mac->id map
|
||||
for sourceID, node := range nodes {
|
||||
if nodeinfo := node.Nodeinfo; nodeinfo != nil {
|
||||
@ -70,7 +76,7 @@ func (builder *GraphBuilder) readNodes(nodes map[string]*Node) {
|
||||
|
||||
// Iterate over local MAC addresses from LLDP
|
||||
if neighbours := node.Neighbours; neighbours != nil {
|
||||
for sourceAddr, _ := range neighbours.LLDP {
|
||||
for sourceAddr := range neighbours.LLDP {
|
||||
builder.macToID[sourceAddr] = sourceID
|
||||
}
|
||||
}
|
||||
@ -90,7 +96,7 @@ func (builder *GraphBuilder) readNodes(nodes map[string]*Node) {
|
||||
}
|
||||
// LLDP
|
||||
for _, neighbours := range neighbours.LLDP {
|
||||
for targetAddress, _ := range neighbours {
|
||||
for targetAddress := range neighbours {
|
||||
if targetID, found := builder.macToID[targetAddress]; found {
|
||||
builder.addLink(targetID, sourceID, 255)
|
||||
}
|
||||
@ -101,7 +107,7 @@ func (builder *GraphBuilder) readNodes(nodes map[string]*Node) {
|
||||
}
|
||||
}
|
||||
|
||||
func (builder *GraphBuilder) Extract() ([]*GraphNode, []*GraphLink) {
|
||||
func (builder *graphBuilder) extract() ([]*GraphNode, []*GraphLink) {
|
||||
links := make([]*GraphLink, len(builder.links))
|
||||
nodes := make([]*GraphNode, len(builder.macToID))
|
||||
idToIndex := make(map[string]int)
|
||||
@ -131,7 +137,7 @@ func (builder *GraphBuilder) Extract() ([]*GraphNode, []*GraphLink) {
|
||||
return nodes, links
|
||||
}
|
||||
|
||||
func (builder *GraphBuilder) isVPN(ids ...string) bool {
|
||||
func (builder *graphBuilder) isVPN(ids ...string) bool {
|
||||
for _, id := range ids {
|
||||
if _, found := builder.vpn[id]; found {
|
||||
return true
|
||||
@ -140,7 +146,7 @@ func (builder *GraphBuilder) isVPN(ids ...string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (builder *GraphBuilder) addLink(targetID string, sourceID string, linkTq int) {
|
||||
func (builder *graphBuilder) addLink(targetID string, sourceID string, linkTq int) {
|
||||
// Sort IDs to generate the key
|
||||
var key string
|
||||
if strings.Compare(sourceID, targetID) > 0 {
|
||||
|
@ -22,7 +22,7 @@ type Node struct {
|
||||
func (node *Node) ToInflux() (tags imodels.Tags, fields imodels.Fields) {
|
||||
stats := node.Statistics
|
||||
|
||||
tags.SetString("nodeid", stats.NodeId)
|
||||
tags.SetString("nodeid", stats.NodeID)
|
||||
|
||||
fields = map[string]interface{}{
|
||||
"load": stats.LoadAverage,
|
||||
|
@ -31,14 +31,15 @@ func NewNodes(config *Config) *Nodes {
|
||||
if config.Nodes.NodesDynamicPath != "" {
|
||||
nodes.load()
|
||||
}
|
||||
/**
|
||||
* Version '-1' because the nodes.json would not be defined,
|
||||
* it would be change with the change of the respondd application on gluon
|
||||
*/
|
||||
/**
|
||||
* Version '-1' because the nodes.json would not be defined,
|
||||
* it would be change with the change of the respondd application on gluon
|
||||
*/
|
||||
nodes.Version = -1
|
||||
return nodes
|
||||
}
|
||||
|
||||
//Start all services to manage Nodes
|
||||
func (nodes *Nodes) Start() {
|
||||
go nodes.worker()
|
||||
}
|
||||
@ -180,7 +181,7 @@ func (nodes *Nodes) load() {
|
||||
path := nodes.config.Nodes.NodesDynamicPath
|
||||
|
||||
if f, err := os.Open(path); err == nil { // transform data to legacy meshviewer
|
||||
if err := json.NewDecoder(f).Decode(nodes); err == nil {
|
||||
if err = json.NewDecoder(f).Decode(nodes); err == nil {
|
||||
log.Println("loaded", len(nodes.List), "nodes")
|
||||
} else {
|
||||
log.Println("failed to unmarshal nodes:", err)
|
||||
|
@ -79,7 +79,7 @@ func TestToInflux(t *testing.T) {
|
||||
|
||||
node := Node{
|
||||
Statistics: &data.Statistics{
|
||||
NodeId: "foobar",
|
||||
NodeID: "foobar",
|
||||
LoadAverage: 0.5,
|
||||
},
|
||||
Nodeinfo: &data.NodeInfo{
|
||||
|
@ -1,7 +1,9 @@
|
||||
package models
|
||||
|
||||
// CounterMap to manage multiple values
|
||||
type CounterMap map[string]uint32
|
||||
|
||||
// GlobalStats struct
|
||||
type GlobalStats struct {
|
||||
Clients uint32
|
||||
ClientsWifi uint32
|
||||
@ -14,7 +16,7 @@ type GlobalStats struct {
|
||||
Models CounterMap
|
||||
}
|
||||
|
||||
// Returns global statistics for InfluxDB
|
||||
//NewGlobalStats returns global statistics for InfluxDB
|
||||
func NewGlobalStats(nodes *Nodes) (result *GlobalStats) {
|
||||
result = &GlobalStats{
|
||||
Firmwares: make(CounterMap),
|
||||
@ -24,7 +26,7 @@ func NewGlobalStats(nodes *Nodes) (result *GlobalStats) {
|
||||
nodes.Lock()
|
||||
for _, node := range nodes.List {
|
||||
if node.Flags.Online {
|
||||
result.Nodes += 1
|
||||
result.Nodes++
|
||||
if stats := node.Statistics; stats != nil {
|
||||
result.Clients += stats.Clients.Total
|
||||
result.ClientsWifi24 += stats.Clients.Wifi24
|
||||
@ -32,7 +34,7 @@ func NewGlobalStats(nodes *Nodes) (result *GlobalStats) {
|
||||
result.ClientsWifi += stats.Clients.Wifi
|
||||
}
|
||||
if node.Flags.Gateway {
|
||||
result.Gateways += 1
|
||||
result.Gateways++
|
||||
}
|
||||
if info := node.Nodeinfo; info != nil {
|
||||
result.Models.Increment(info.Hardware.Model)
|
||||
@ -53,7 +55,7 @@ func (m CounterMap) Increment(key string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns fields for InfluxDB
|
||||
// Fields returns fields for InfluxDB
|
||||
func (stats *GlobalStats) Fields() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"nodes": stats.Nodes,
|
||||
|
@ -28,7 +28,7 @@ type Collector struct {
|
||||
stop chan interface{}
|
||||
}
|
||||
|
||||
// Creates a Collector struct
|
||||
// NewCollector creates a Collector struct
|
||||
func NewCollector(db *database.DB, nodes *models.Nodes, iface string) *Collector {
|
||||
// Parse address
|
||||
addr, err := net.ResolveUDPAddr("udp", "[::]:0")
|
||||
@ -140,24 +140,24 @@ func (res *Response) parse() (*data.ResponseData, error) {
|
||||
|
||||
func (coll *Collector) saveResponse(addr net.UDPAddr, res *data.ResponseData) {
|
||||
// Search for NodeID
|
||||
var nodeId string
|
||||
var nodeID string
|
||||
if val := res.NodeInfo; val != nil {
|
||||
nodeId = val.NodeId
|
||||
nodeID = val.NodeID
|
||||
} else if val := res.Neighbours; val != nil {
|
||||
nodeId = val.NodeId
|
||||
nodeID = val.NodeID
|
||||
} else if val := res.Statistics; val != nil {
|
||||
nodeId = val.NodeId
|
||||
nodeID = val.NodeID
|
||||
}
|
||||
|
||||
// Updates nodes if NodeID found
|
||||
if len(nodeId) != 12 {
|
||||
log.Printf("invalid NodeID '%s' from %s", nodeId, addr.String())
|
||||
if len(nodeID) != 12 {
|
||||
log.Printf("invalid NodeID '%s' from %s", nodeID, addr.String())
|
||||
return
|
||||
}
|
||||
node := coll.nodes.Update(nodeId, res)
|
||||
node := coll.nodes.Update(nodeID, res)
|
||||
|
||||
if coll.db != nil && node.Statistics != nil {
|
||||
coll.db.Add(nodeId, node)
|
||||
coll.db.Add(nodeID, node)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,5 +23,5 @@ func TestParse(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
assert.NotNil(data)
|
||||
|
||||
assert.Equal("f81a67a5e9c1", data.NodeInfo.NodeId)
|
||||
assert.Equal("f81a67a5e9c1", data.NodeInfo.NodeID)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user