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" "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 // http://blog.3960.org/post/7309573249/genauigkeit-bei-geo-koordinaten
const GEOROUND = 0.0000001 const GEOROUND = 0.0000001
func geoEqual(a, b float64) bool { func geoEqual(a, b float64) bool {
@ -20,29 +19,33 @@ func geoEqual(a, b float64) bool {
return false return false
} }
type ApiAliases struct { // AliasesAPI struct for API
type AliasesAPI struct {
aliases *models.Aliases aliases *models.Aliases
config *models.Config config *models.Config
nodes *models.Nodes nodes *models.Nodes
} }
// NewAliases Bind to API
func NewAliases(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) { func NewAliases(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) {
api := &ApiAliases{ api := &AliasesAPI{
aliases: models.NewAliases(config), aliases: models.NewAliases(config),
nodes: nodes, nodes: nodes,
config: config, config: config,
} }
router.GET(prefix, api.GetAll) 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.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) 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 { if alias := api.aliases.List[ps.ByName("nodeid")]; alias != nil {
jsonOutput(w, r, alias) jsonOutput(w, r, alias)
return 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") 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 var alias models.Alias
err := json.NewDecoder(r.Body).Decode(&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") fmt.Print("[api] node updated '", ps.ByName("nodeid"), "'\n")
jsonOutput(w, r, alias) 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") fmt.Print("[api] ansible\n")
jsonOutput(w, r, models.GenerateAnsible(api.nodes, api.aliases.List)) jsonOutput(w, r, models.GenerateAnsible(api.nodes, api.aliases.List))
} }

View File

@ -2,15 +2,15 @@ package api
import ( import (
"bytes" "bytes"
"strings"
"net/http"
"encoding/json"
"encoding/base64" "encoding/base64"
"encoding/json"
"net/http"
"strings"
"github.com/julienschmidt/httprouter" "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) js, err := json.Marshal(data)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) 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-Origin", origin)
} }
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") 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.Header().Set("Access-Control-Allow-Credentials", "true")
w.Write(js) w.Write(js)
} }
// BasicAuth for API request
func BasicAuth(h httprouter.Handle, pass []byte) httprouter.Handle { func BasicAuth(h httprouter.Handle, pass []byte) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if origin := r.Header.Get("Origin"); origin != "" { if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-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)
} }
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" "net/http"
) )
type ApiNodes struct { // NodesAPI struct for API
type NodesAPI struct {
config *models.Config config *models.Config
nodes *models.Nodes nodes *models.Nodes
} }
// NewNodes Bind to API
func NewNodes(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) { func NewNodes(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) {
api := &ApiNodes{ api := &NodesAPI{
nodes: nodes, nodes: nodes,
config: config, config: config,
} }
router.GET(prefix, api.GetAll) 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) jsonOutput(w, r, api.nodes.List)
} }

View File

@ -57,11 +57,11 @@ func main() {
if config.Webserver.Enable { if config.Webserver.Enable {
router := httprouter.New() router := httprouter.New()
if config.Webserver.Api.NewNodes { if config.Webserver.API.NewNodes {
api.NewNodes(config, router, "/api/nodes", nodes) api.NewNodes(config, router, "/api/nodes", nodes)
log.Println("api nodes started") log.Println("api nodes started")
} }
if config.Webserver.Api.Aliases { if config.Webserver.API.Aliases {
api.NewAliases(config, router, "/api/aliases", nodes) api.NewAliases(config, router, "/api/aliases", nodes)
log.Println("api aliases started") log.Println("api aliases started")
} }

View File

@ -4,6 +4,7 @@ import (
"math" "math"
) )
// Wireless struct
type Wireless struct { type Wireless struct {
TxPower24 uint32 `json:"txpower24,omitempty"` TxPower24 uint32 `json:"txpower24,omitempty"`
Channel24 uint32 `json:"channel24,omitempty"` Channel24 uint32 `json:"channel24,omitempty"`
@ -11,30 +12,32 @@ type Wireless struct {
Channel5 uint32 `json:"channel5,omitempty"` Channel5 uint32 `json:"channel5,omitempty"`
} }
// WirelessStatistics struct
type WirelessStatistics []*WirelessAirtime type WirelessStatistics []*WirelessAirtime
// WirelessAirtime struct
type WirelessAirtime struct { type WirelessAirtime struct {
ChanUtil float32 // Channel utilization ChanUtil float32 // Channel utilization
RxUtil float32 // Receive utilization RxUtil float32 // Receive utilization
TxUtil float32 // Transmit utilization TxUtil float32 // Transmit utilization
Active_time uint64 `json:"active"` ActiveTime uint64 `json:"active"`
Busy_time uint64 `json:"busy"` BusyTime uint64 `json:"busy"`
Rx_time uint64 `json:"rx"` RxTime uint64 `json:"rx"`
Tx_time uint64 `json:"tx"` TxTime uint64 `json:"tx"`
Noise uint32 `json:"noise"` Noise uint32 `json:"noise"`
Frequency uint32 `json:"frequency"` Frequency uint32 `json:"frequency"`
} }
// FrequencyName to 11g or 11a
func (airtime WirelessAirtime) FrequencyName() string { func (airtime WirelessAirtime) FrequencyName() string {
if airtime.Frequency < 5000 { if airtime.Frequency < 5000 {
return "11g" 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) { func (current WirelessStatistics) SetUtilization(previous WirelessStatistics) {
for _, c := range current { for _, c := range current {
for _, p := range previous { for _, p := range previous {
@ -45,21 +48,21 @@ func (current WirelessStatistics) SetUtilization(previous WirelessStatistics) {
} }
} }
// Calculates the utilization values in regard to the previous values // SetUtilization Calculates the utilization values in regard to the previous values
func (cur *WirelessAirtime) SetUtilization(prev *WirelessAirtime) { func (airtime *WirelessAirtime) SetUtilization(prev *WirelessAirtime) {
if cur.Active_time <= prev.Active_time { if airtime.ActiveTime <= prev.ActiveTime {
return return
} }
active := float64(cur.Active_time) - float64(prev.Active_time) active := float64(airtime.ActiveTime) - float64(prev.ActiveTime)
busy := float64(cur.Busy_time) - float64(prev.Busy_time) busy := float64(airtime.BusyTime) - float64(prev.BusyTime)
rx := float64(cur.Tx_time) - float64(prev.Tx_time) rx := float64(airtime.TxTime) - float64(prev.TxTime)
tx := float64(cur.Rx_time) - float64(prev.Rx_time) tx := float64(airtime.RxTime) - float64(prev.RxTime)
// Calculate utilizations // Calculate utilizations
if active > 0 { if active > 0 {
cur.ChanUtil = float32(math.Min(100, 100*(busy+rx+tx)/active)) airtime.ChanUtil = float32(math.Min(100, 100*(busy+rx+tx)/active))
cur.RxUtil = float32(math.Min(100, 100*rx/active)) airtime.RxUtil = float32(math.Min(100, 100*rx/active))
cur.TxUtil = float32(math.Min(100, 100*tx/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) assert := assert.New(t)
t1 := &WirelessAirtime{ t1 := &WirelessAirtime{
Active_time: 20, ActiveTime: 20,
Busy_time: 0, BusyTime: 0,
Tx_time: 5, TxTime: 5,
Rx_time: 0, RxTime: 0,
} }
t2 := &WirelessAirtime{ t2 := &WirelessAirtime{
Active_time: 120, ActiveTime: 120,
Busy_time: 10, BusyTime: 10,
Tx_time: 25, TxTime: 25,
Rx_time: 15, RxTime: 15,
} }
t3 := &WirelessAirtime{ t3 := &WirelessAirtime{
Active_time: 200, ActiveTime: 200,
Busy_time: 40, BusyTime: 40,
Tx_time: 35, TxTime: 35,
Rx_time: 15, RxTime: 15,
} }
t1.SetUtilization(t2) t1.SetUtilization(t2)
@ -58,23 +58,23 @@ func TestWirelessStatistics(t *testing.T) {
stats := WirelessStatistics([]*WirelessAirtime{{ stats := WirelessStatistics([]*WirelessAirtime{{
Frequency: 2400, Frequency: 2400,
Active_time: 20, ActiveTime: 20,
Tx_time: 10, TxTime: 10,
}}) }})
// Different Frequency, should not change anything // Different Frequency, should not change anything
stats.SetUtilization([]*WirelessAirtime{{ stats.SetUtilization([]*WirelessAirtime{{
Frequency: 5000, Frequency: 5000,
Active_time: 15, ActiveTime: 15,
Tx_time: 1, TxTime: 1,
}}) }})
assert.EqualValues(0, stats[0].ChanUtil) assert.EqualValues(0, stats[0].ChanUtil)
// Same Frequency, should set the utilization // Same Frequency, should set the utilization
stats.SetUtilization([]*WirelessAirtime{{ stats.SetUtilization([]*WirelessAirtime{{
Frequency: 2400, Frequency: 2400,
Active_time: 10, ActiveTime: 10,
Tx_time: 5, TxTime: 5,
}}) }})
assert.EqualValues(50, stats[0].ChanUtil) assert.EqualValues(50, stats[0].ChanUtil)
} }

View File

@ -1,34 +1,41 @@
package data package data
// Neighbours struct
type Neighbours struct { type Neighbours struct {
Batadv map[string]BatadvNeighbours `json:"batadv"` Batadv map[string]BatadvNeighbours `json:"batadv"`
LLDP map[string]LLDPNeighbours `json:"lldp"` LLDP map[string]LLDPNeighbours `json:"lldp"`
//WifiNeighbours map[string]WifiNeighbours `json:"wifi"` //WifiNeighbours map[string]WifiNeighbours `json:"wifi"`
NodeId string `json:"node_id"` NodeID string `json:"node_id"`
} }
// WifiLink struct
type WifiLink struct { type WifiLink struct {
Inactive int `json:"inactive"` Inactive int `json:"inactive"`
Noise int `json:"nois"` Noise int `json:"nois"`
Signal int `json:"signal"` Signal int `json:"signal"`
} }
// BatmanLink struct
type BatmanLink struct { type BatmanLink struct {
Lastseen float64 `json:"lastseen"` Lastseen float64 `json:"lastseen"`
Tq int `json:"tq"` Tq int `json:"tq"`
} }
// LLDPLink struct
type LLDPLink struct { type LLDPLink struct {
Name string `json:"name"` Name string `json:"name"`
Description string `json:"descr"` Description string `json:"descr"`
} }
// BatadvNeighbours struct
type BatadvNeighbours struct { type BatadvNeighbours struct {
Neighbours map[string]BatmanLink `json:"neighbours"` Neighbours map[string]BatmanLink `json:"neighbours"`
} }
// WifiNeighbours struct
type WifiNeighbours struct { type WifiNeighbours struct {
Neighbours map[string]WifiLink `json:"neighbours"` Neighbours map[string]WifiLink `json:"neighbours"`
} }
// LLDPNeighbours struct
type LLDPNeighbours map[string]LLDPLink type LLDPNeighbours map[string]LLDPLink

View File

@ -1,7 +1,8 @@
package data package data
// NodeInfo struct
type NodeInfo struct { type NodeInfo struct {
NodeId string `json:"node_id"` NodeID string `json:"node_id"`
Network Network `json:"network"` Network Network `json:"network"`
Owner *Owner `json:"-"` // Removed for privacy reasons Owner *Owner `json:"-"` // Removed for privacy reasons
System System `json:"system"` System System `json:"system"`
@ -12,6 +13,8 @@ type NodeInfo struct {
VPN bool `json:"vpn"` VPN bool `json:"vpn"`
Wireless *Wireless `json:"wireless,omitempty"` Wireless *Wireless `json:"wireless,omitempty"`
} }
// BatInterface struct
type BatInterface struct { type BatInterface struct {
Interfaces struct { Interfaces struct {
Wireless []string `json:"wireless,omitempty"` Wireless []string `json:"wireless,omitempty"`
@ -20,6 +23,7 @@ type BatInterface struct {
} `json:"interfaces"` } `json:"interfaces"`
} }
// Network struct
type Network struct { type Network struct {
Mac string `json:"mac"` Mac string `json:"mac"`
Addresses []string `json:"addresses"` Addresses []string `json:"addresses"`
@ -27,20 +31,24 @@ type Network struct {
MeshInterfaces []string `json:"mesh_interfaces"` MeshInterfaces []string `json:"mesh_interfaces"`
} }
// Owner struct
type Owner struct { type Owner struct {
Contact string `json:"contact"` Contact string `json:"contact"`
} }
// System struct
type System struct { type System struct {
SiteCode string `json:"site_code"` SiteCode string `json:"site_code"`
} }
// Location struct
type Location struct { type Location struct {
Longtitude float64 `json:"longitude"` Longtitude float64 `json:"longitude"`
Latitude float64 `json:"latitude"` Latitude float64 `json:"latitude"`
Altitude float64 `json:"altitude,omitempty"` Altitude float64 `json:"altitude,omitempty"`
} }
// Software struct
type Software struct { type Software struct {
Autoupdater struct { Autoupdater struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
@ -59,10 +67,11 @@ type Software struct {
Release string `json:"release,omitempty"` Release string `json:"release,omitempty"`
} `json:"firmware,omitempty"` } `json:"firmware,omitempty"`
StatusPage struct { StatusPage struct {
Api int `json:"api"` API int `json:"api"`
} `json:"status-page,omitempty"` } `json:"status-page,omitempty"`
} }
// Hardware struct
type Hardware struct { type Hardware struct {
Nproc int `json:"nproc"` Nproc int `json:"nproc"`
Model string `json:"model"` Model string `json:"model"`

View File

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

View File

@ -5,8 +5,9 @@ package data
They always return float. They always return float.
*/ */
//Statistics struct
type Statistics struct { type Statistics struct {
NodeId string `json:"node_id"` NodeID string `json:"node_id"`
Clients Clients `json:"clients"` Clients Clients `json:"clients"`
RootFsUsage float64 `json:"rootfs_usage,omitempty"` RootFsUsage float64 `json:"rootfs_usage,omitempty"`
LoadAverage float64 `json:"loadavg,omitempty"` LoadAverage float64 `json:"loadavg,omitempty"`
@ -30,25 +31,30 @@ type Statistics struct {
Wireless WirelessStatistics `json:"wireless,omitempty"` Wireless WirelessStatistics `json:"wireless,omitempty"`
} }
// MeshVPNPeerLink struct
type MeshVPNPeerLink struct { type MeshVPNPeerLink struct {
Established float64 `json:"established"` Established float64 `json:"established"`
} }
// MeshVPNPeerGroup struct
type MeshVPNPeerGroup struct { type MeshVPNPeerGroup struct {
Peers map[string]*MeshVPNPeerLink `json:"peers"` Peers map[string]*MeshVPNPeerLink `json:"peers"`
Groups map[string]*MeshVPNPeerGroup `json:"groups"` Groups map[string]*MeshVPNPeerGroup `json:"groups"`
} }
// MeshVPN struct
type MeshVPN struct { type MeshVPN struct {
Groups map[string]*MeshVPNPeerGroup `json:"groups,omitempty"` Groups map[string]*MeshVPNPeerGroup `json:"groups,omitempty"`
} }
// Traffic struct
type Traffic struct { type Traffic struct {
Bytes float64 `json:"bytes,omitempty"` Bytes float64 `json:"bytes,omitempty"`
Packets float64 `json:"packets,omitempty"` Packets float64 `json:"packets,omitempty"`
Dropped float64 `json:"dropped,omitempty"` Dropped float64 `json:"dropped,omitempty"`
} }
// Clients struct
type Clients struct { type Clients struct {
Wifi uint32 `json:"wifi"` Wifi uint32 `json:"wifi"`
Wifi24 uint32 `json:"wifi24"` Wifi24 uint32 `json:"wifi24"`
@ -56,6 +62,7 @@ type Clients struct {
Total uint32 `json:"total"` Total uint32 `json:"total"`
} }
// Memory struct
type Memory struct { type Memory struct {
Cached uint32 `json:"cached"` Cached uint32 `json:"cached"`
Total uint32 `json:"total"` Total uint32 `json:"total"`
@ -63,6 +70,7 @@ type Memory struct {
Free uint32 `json:"free"` Free uint32 `json:"free"`
} }
// SwitchPort struct
type SwitchPort struct { type SwitchPort struct {
Speed uint32 `json:"speed"` Speed uint32 `json:"speed"`
} }

View File

@ -13,7 +13,7 @@ func TestStatistics(t *testing.T) {
obj := &Statistics{} obj := &Statistics{}
testfile("statistics.json", obj) 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("52:54:00:a9:f7:6e", obj.Gateway)
assert.Equal(float64(57861871176), obj.Traffic.Rx.Bytes) assert.Equal(float64(57861871176), obj.Traffic.Rx.Bytes)
assert.Equal(uint32(35), obj.Clients.Total) 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 // 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() tags, fields := node.ToInflux()
db.AddPoint(MeasurementNode, tags, fields, time.Now()) db.AddPoint(MeasurementNode, tags, fields, time.Now())
} }
// Close all connection and clean up
func (db *DB) Close() { func (db *DB) Close() {
close(db.quit) close(db.quit)
close(db.points) close(db.points)

View File

@ -5,21 +5,26 @@ import (
"time" "time"
) )
// TimeFormat of JSONTime
const TimeFormat = "2006-01-02T15:04:05-0700" const TimeFormat = "2006-01-02T15:04:05-0700"
//Time struct of JSONTime
type Time struct { type Time struct {
time time.Time time time.Time
} }
// Now current Time
func Now() Time { func Now() Time {
return Time{time.Now()} return Time{time.Now()}
} }
//MarshalJSON to bytearray
func (t Time) MarshalJSON() ([]byte, error) { func (t Time) MarshalJSON() ([]byte, error) {
stamp := `"` + t.time.Format(TimeFormat) + `"` stamp := `"` + t.time.Format(TimeFormat) + `"`
return []byte(stamp), nil return []byte(stamp), nil
} }
// UnmarshalJSON from bytearray
func (t *Time) UnmarshalJSON(data []byte) (err error) { func (t *Time) UnmarshalJSON(data []byte) (err error) {
if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' { if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
return errors.New("invalid jsontime") return errors.New("invalid jsontime")
@ -29,24 +34,33 @@ func (t *Time) UnmarshalJSON(data []byte) (err error) {
} }
return return
} }
// GetTime normal
func (t Time) GetTime() time.Time { func (t Time) GetTime() time.Time {
return t.time return t.time
} }
// Unix of this time
func (t Time) Unix() int64 { func (t Time) Unix() int64 {
return t.time.Unix() return t.time.Unix()
} }
// IsZero is time zero?
func (t Time) IsZero() bool { func (t Time) IsZero() bool {
return t.time.IsZero() return t.time.IsZero()
} }
// Add given Duration to this time
func (t Time) Add(d time.Duration) Time { func (t Time) Add(d time.Duration) Time {
return Time{time: t.time.Add(d)} return Time{time: t.time.Add(d)}
} }
// After is this time after the given?
func (t Time) After(u Time) bool { func (t Time) After(u Time) bool {
return t.time.After(u.GetTime()) return t.time.After(u.GetTime())
} }
// Before is this time before the given?
func (t Time) Before(u Time) bool { func (t Time) Before(u Time) bool {
return t.time.Before(u.GetTime()) return t.time.Before(u.GetTime())
} }

View File

@ -15,6 +15,7 @@ type Node struct {
Neighbours *data.Neighbours `json:"-"` Neighbours *data.Neighbours `json:"-"`
} }
// Flags status of node set by collector for the meshviewer
type Flags struct { type Flags struct {
Online bool `json:"online"` Online bool `json:"online"`
Gateway bool `json:"gateway"` Gateway bool `json:"gateway"`
@ -36,8 +37,9 @@ type NodesV2 struct {
List []*Node `json:"nodes"` // the current nodemap, as array List []*Node `json:"nodes"` // the current nodemap, as array
} }
// Statistics a meshviewer spezifisch struct, diffrent from respondd
type Statistics struct { type Statistics struct {
NodeId string `json:"node_id"` NodeID string `json:"node_id"`
Clients uint32 `json:"clients"` Clients uint32 `json:"clients"`
RootFsUsage float64 `json:"rootfs_usage,omitempty"` RootFsUsage float64 `json:"rootfs_usage,omitempty"`
LoadAverage float64 `json:"loadavg,omitempty"` LoadAverage float64 `json:"loadavg,omitempty"`
@ -59,18 +61,19 @@ type Statistics struct {
} `json:"traffic,omitempty"` } `json:"traffic,omitempty"`
} }
// NewStatistics transform respond Statistics to meshviewer Statistics
func NewStatistics(stats *data.Statistics) *Statistics { func NewStatistics(stats *data.Statistics) *Statistics {
total := stats.Clients.Total total := stats.Clients.Total
if total == 0 { if total == 0 {
total = stats.Clients.Wifi24 + stats.Clients.Wifi5 total = stats.Clients.Wifi24 + stats.Clients.Wifi5
} }
/* The Meshviewer could not handle absolute memory output /* 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) memoryUsage := (float64(stats.Memory.Total) - float64(stats.Memory.Free)) / float64(stats.Memory.Total)
return &Statistics{ return &Statistics{
NodeId: stats.NodeId, NodeID: stats.NodeID,
Gateway: stats.Gateway, Gateway: stats.Gateway,
RootFsUsage: stats.RootFsUsage, RootFsUsage: stats.RootFsUsage,
LoadAverage: stats.LoadAverage, LoadAverage: stats.LoadAverage,

View File

@ -10,6 +10,7 @@ import (
"github.com/FreifunkBremen/respond-collector/data" "github.com/FreifunkBremen/respond-collector/data"
) )
// Alias a change request for other nodes
type Alias struct { type Alias struct {
Hostname string `json:"hostname,omitempty"` Hostname string `json:"hostname,omitempty"`
Location *data.Location `json:"location,omitempty"` Location *data.Location `json:"location,omitempty"`
@ -17,14 +18,14 @@ type Alias struct {
Owner string `json:"owner,omitempty"` Owner string `json:"owner,omitempty"`
} }
// Nodes struct: cache DB of Node's structs // Aliases struct: cache DB of Node's structs
type Aliases struct { type Aliases struct {
List map[string]*Alias `json:"nodes"` // the current nodemap, indexed by node ID List map[string]*Alias `json:"nodes"` // the current nodemap, indexed by node ID
config *Config config *Config
sync.Mutex sync.Mutex
} }
// NewNodes create Nodes structs // NewAliases create Nodes structs
func NewAliases(config *Config) *Aliases { func NewAliases(config *Config) *Aliases {
aliases := &Aliases{ aliases := &Aliases{
List: make(map[string]*Alias), List: make(map[string]*Alias),
@ -39,6 +40,7 @@ func NewAliases(config *Config) *Aliases {
return aliases return aliases
} }
// Update a alias in aliases cache
func (e *Aliases) Update(nodeID string, newalias *Alias) { func (e *Aliases) Update(nodeID string, newalias *Alias) {
e.Lock() e.Lock()
e.List[nodeID] = newalias e.List[nodeID] = newalias
@ -51,7 +53,7 @@ func (e *Aliases) load() {
log.Println("loading", path) log.Println("loading", path)
if data, err := ioutil.ReadFile(path); err == nil { 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") log.Println("loaded", len(e.List), "aliases")
} else { } else {
log.Println("failed to unmarshal nodes:", err) log.Println("failed to unmarshal nodes:", err)

View File

@ -1,11 +1,14 @@
package models package models
// Ansible struct
type Ansible struct { type Ansible struct {
Nodes []string `json:"nodes"` Nodes []string `json:"nodes"`
Meta struct { Meta struct {
HostVars map[string]*AnsibleHostVars `json:"hostvars,omitempty"` HostVars map[string]*AnsibleHostVars `json:"hostvars,omitempty"`
} `json:"_meta"` } `json:"_meta"`
} }
// AnsibleHostVars new values for a node
type AnsibleHostVars struct { type AnsibleHostVars struct {
Address string `json:"ansible_ssh_host"` Address string `json:"ansible_ssh_host"`
Hostname string `json:"node_name,omitempty"` Hostname string `json:"node_name,omitempty"`
@ -18,6 +21,7 @@ type AnsibleHostVars struct {
GeoLongitude float64 `json:"geo_longitude,omitempty"` 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 { func GenerateAnsible(nodes *Nodes, aliases map[string]*Alias) *Ansible {
ansible := &Ansible{Nodes: make([]string, 0)} ansible := &Ansible{Nodes: make([]string, 0)}
ansible.Meta.HostVars = make(map[string]*AnsibleHostVars) ansible.Meta.HostVars = make(map[string]*AnsibleHostVars)

View File

@ -19,7 +19,7 @@ type Config struct {
Port string `yaml:"port"` Port string `yaml:"port"`
Address string `yaml:"address"` Address string `yaml:"address"`
Webroot string `yaml:"webroot"` Webroot string `yaml:"webroot"`
Api struct { API struct {
Passphrase string `yaml:"passphrase"` Passphrase string `yaml:"passphrase"`
NewNodes bool `yaml:"newnodes"` NewNodes bool `yaml:"newnodes"`
Aliases bool `yaml:"aliases"` 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 { func ReadConfigFile(path string) *Config {
config := &Config{} config := &Config{}
file, _ := ioutil.ReadFile(path) file, _ := ioutil.ReadFile(path)

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
) )
// Graph a struct for all links between the nodes
type Graph struct { type Graph struct {
Version int `json:"version"` Version int `json:"version"`
Batadv struct { Batadv struct {
@ -15,10 +16,13 @@ type Graph struct {
} `json:"batadv"` } `json:"batadv"`
} }
// GraphNode small struct of a node for the graph struct
type GraphNode struct { type GraphNode struct {
ID string `json:"id"` ID string `json:"id"`
NodeID string `json:"node_id"` NodeID string `json:"node_id"`
} }
// GraphLink a struct for the link between two nodes
type GraphLink struct { type GraphLink struct {
Source int `json:"source"` Source int `json:"source"`
Target int `json:"target"` Target int `json:"target"`
@ -27,14 +31,16 @@ type GraphLink struct {
Bidirect bool `json:"bidirect"` 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 macToID map[string]string // mapping from MAC address to node id
links map[string]*GraphLink // mapping from $idA-$idB to existing link links map[string]*GraphLink // mapping from $idA-$idB to existing link
vpn map[string]interface{} // IDs/addresses of VPN servers vpn map[string]interface{} // IDs/addresses of VPN servers
} }
// BuildGraph transform from nodes (Neighbours) to Graph
func (nodes *Nodes) BuildGraph() *Graph { func (nodes *Nodes) BuildGraph() *Graph {
builder := &GraphBuilder{ builder := &graphBuilder{
macToID: make(map[string]string), macToID: make(map[string]string),
links: make(map[string]*GraphLink), links: make(map[string]*GraphLink),
vpn: make(map[string]interface{}), vpn: make(map[string]interface{}),
@ -44,11 +50,11 @@ func (nodes *Nodes) BuildGraph() *Graph {
graph := &Graph{Version: 1} graph := &Graph{Version: 1}
graph.Batadv.Directed = false graph.Batadv.Directed = false
graph.Batadv.Nodes, graph.Batadv.Links = builder.Extract() graph.Batadv.Nodes, graph.Batadv.Links = builder.extract()
return graph return graph
} }
func (builder *GraphBuilder) readNodes(nodes map[string]*Node) { func (builder *graphBuilder) readNodes(nodes map[string]*Node) {
// Fill mac->id map // Fill mac->id map
for sourceID, node := range nodes { for sourceID, node := range nodes {
if nodeinfo := node.Nodeinfo; nodeinfo != nil { 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 // Iterate over local MAC addresses from LLDP
if neighbours := node.Neighbours; neighbours != nil { if neighbours := node.Neighbours; neighbours != nil {
for sourceAddr, _ := range neighbours.LLDP { for sourceAddr := range neighbours.LLDP {
builder.macToID[sourceAddr] = sourceID builder.macToID[sourceAddr] = sourceID
} }
} }
@ -90,7 +96,7 @@ func (builder *GraphBuilder) readNodes(nodes map[string]*Node) {
} }
// LLDP // LLDP
for _, neighbours := range neighbours.LLDP { for _, neighbours := range neighbours.LLDP {
for targetAddress, _ := range neighbours { for targetAddress := range neighbours {
if targetID, found := builder.macToID[targetAddress]; found { if targetID, found := builder.macToID[targetAddress]; found {
builder.addLink(targetID, sourceID, 255) 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)) links := make([]*GraphLink, len(builder.links))
nodes := make([]*GraphNode, len(builder.macToID)) nodes := make([]*GraphNode, len(builder.macToID))
idToIndex := make(map[string]int) idToIndex := make(map[string]int)
@ -131,7 +137,7 @@ func (builder *GraphBuilder) Extract() ([]*GraphNode, []*GraphLink) {
return nodes, links return nodes, links
} }
func (builder *GraphBuilder) isVPN(ids ...string) bool { func (builder *graphBuilder) isVPN(ids ...string) bool {
for _, id := range ids { for _, id := range ids {
if _, found := builder.vpn[id]; found { if _, found := builder.vpn[id]; found {
return true return true
@ -140,7 +146,7 @@ func (builder *GraphBuilder) isVPN(ids ...string) bool {
return false 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 // Sort IDs to generate the key
var key string var key string
if strings.Compare(sourceID, targetID) > 0 { 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) { func (node *Node) ToInflux() (tags imodels.Tags, fields imodels.Fields) {
stats := node.Statistics stats := node.Statistics
tags.SetString("nodeid", stats.NodeId) tags.SetString("nodeid", stats.NodeID)
fields = map[string]interface{}{ fields = map[string]interface{}{
"load": stats.LoadAverage, "load": stats.LoadAverage,

View File

@ -31,14 +31,15 @@ func NewNodes(config *Config) *Nodes {
if config.Nodes.NodesDynamicPath != "" { if config.Nodes.NodesDynamicPath != "" {
nodes.load() nodes.load()
} }
/** /**
* Version '-1' because the nodes.json would not be defined, * Version '-1' because the nodes.json would not be defined,
* it would be change with the change of the respondd application on gluon * it would be change with the change of the respondd application on gluon
*/ */
nodes.Version = -1 nodes.Version = -1
return nodes return nodes
} }
//Start all services to manage Nodes
func (nodes *Nodes) Start() { func (nodes *Nodes) Start() {
go nodes.worker() go nodes.worker()
} }
@ -180,7 +181,7 @@ func (nodes *Nodes) load() {
path := nodes.config.Nodes.NodesDynamicPath path := nodes.config.Nodes.NodesDynamicPath
if f, err := os.Open(path); err == nil { // transform data to legacy meshviewer 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") log.Println("loaded", len(nodes.List), "nodes")
} else { } else {
log.Println("failed to unmarshal nodes:", err) log.Println("failed to unmarshal nodes:", err)

View File

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

View File

@ -1,7 +1,9 @@
package models package models
// CounterMap to manage multiple values
type CounterMap map[string]uint32 type CounterMap map[string]uint32
// GlobalStats struct
type GlobalStats struct { type GlobalStats struct {
Clients uint32 Clients uint32
ClientsWifi uint32 ClientsWifi uint32
@ -14,7 +16,7 @@ type GlobalStats struct {
Models CounterMap Models CounterMap
} }
// Returns global statistics for InfluxDB //NewGlobalStats returns global statistics for InfluxDB
func NewGlobalStats(nodes *Nodes) (result *GlobalStats) { func NewGlobalStats(nodes *Nodes) (result *GlobalStats) {
result = &GlobalStats{ result = &GlobalStats{
Firmwares: make(CounterMap), Firmwares: make(CounterMap),
@ -24,7 +26,7 @@ func NewGlobalStats(nodes *Nodes) (result *GlobalStats) {
nodes.Lock() nodes.Lock()
for _, node := range nodes.List { for _, node := range nodes.List {
if node.Flags.Online { if node.Flags.Online {
result.Nodes += 1 result.Nodes++
if stats := node.Statistics; stats != nil { if stats := node.Statistics; stats != nil {
result.Clients += stats.Clients.Total result.Clients += stats.Clients.Total
result.ClientsWifi24 += stats.Clients.Wifi24 result.ClientsWifi24 += stats.Clients.Wifi24
@ -32,7 +34,7 @@ func NewGlobalStats(nodes *Nodes) (result *GlobalStats) {
result.ClientsWifi += stats.Clients.Wifi result.ClientsWifi += stats.Clients.Wifi
} }
if node.Flags.Gateway { if node.Flags.Gateway {
result.Gateways += 1 result.Gateways++
} }
if info := node.Nodeinfo; info != nil { if info := node.Nodeinfo; info != nil {
result.Models.Increment(info.Hardware.Model) 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{} { func (stats *GlobalStats) Fields() map[string]interface{} {
return map[string]interface{}{ return map[string]interface{}{
"nodes": stats.Nodes, "nodes": stats.Nodes,

View File

@ -28,7 +28,7 @@ type Collector struct {
stop chan interface{} stop chan interface{}
} }
// Creates a Collector struct // NewCollector creates a Collector struct
func NewCollector(db *database.DB, nodes *models.Nodes, iface string) *Collector { func NewCollector(db *database.DB, nodes *models.Nodes, iface string) *Collector {
// Parse address // Parse address
addr, err := net.ResolveUDPAddr("udp", "[::]:0") 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) { func (coll *Collector) saveResponse(addr net.UDPAddr, res *data.ResponseData) {
// Search for NodeID // Search for NodeID
var nodeId string var nodeID string
if val := res.NodeInfo; val != nil { if val := res.NodeInfo; val != nil {
nodeId = val.NodeId nodeID = val.NodeID
} else if val := res.Neighbours; val != nil { } else if val := res.Neighbours; val != nil {
nodeId = val.NodeId nodeID = val.NodeID
} else if val := res.Statistics; val != nil { } else if val := res.Statistics; val != nil {
nodeId = val.NodeId nodeID = val.NodeID
} }
// Updates nodes if NodeID found // Updates nodes if NodeID found
if len(nodeId) != 12 { if len(nodeID) != 12 {
log.Printf("invalid NodeID '%s' from %s", nodeId, addr.String()) log.Printf("invalid NodeID '%s' from %s", nodeID, addr.String())
return return
} }
node := coll.nodes.Update(nodeId, res) node := coll.nodes.Update(nodeID, res)
if coll.db != nil && node.Statistics != nil { 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.NoError(err)
assert.NotNil(data) assert.NotNil(data)
assert.Equal("f81a67a5e9c1", data.NodeInfo.NodeId) assert.Equal("f81a67a5e9c1", data.NodeInfo.NodeID)
} }