improve complete code with comments and co (thanks linter)

This commit is contained in:
Martin Geno 2017-01-20 22:27:44 +01:00
parent 798db6a063
commit d855248f6a
24 changed files with 204 additions and 132 deletions

View File

@ -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))
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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")
}

View File

@ -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))
}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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"`

View File

@ -1,5 +1,6 @@
package data
// ResponseData struct
type ResponseData struct {
Neighbours *Neighbours `json:"neighbours"`
NodeInfo *NodeInfo `json:"nodeinfo"`

View File

@ -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"`
}

View File

@ -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)

View File

@ -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)

View File

@ -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())
}

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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,

View File

@ -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)

View File

@ -79,7 +79,7 @@ func TestToInflux(t *testing.T) {
node := Node{
Statistics: &data.Statistics{
NodeId: "foobar",
NodeID: "foobar",
LoadAverage: 0.5,
},
Nodeinfo: &data.NodeInfo{

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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)
}