parent
b5a694a7a4
commit
f4650213b8
|
@ -43,6 +43,8 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodes = runtime.NewNodes(config)
|
||||||
|
|
||||||
connections, err = all.Connect(config.Database.Connection)
|
connections, err = all.Connect(config.Database.Connection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -55,7 +57,6 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes = runtime.NewNodes(config)
|
|
||||||
nodes.Start()
|
nodes.Start()
|
||||||
meshviewer.Start(config, nodes)
|
meshviewer.Start(config, nodes)
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,11 @@ type BatInterface struct {
|
||||||
} `json:"interfaces"`
|
} `json:"interfaces"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Addresses returns a flat list of all MAC addresses
|
||||||
|
func (iface *BatInterface) Addresses() []string {
|
||||||
|
return append(append(iface.Interfaces.Other, iface.Interfaces.Tunnel...), iface.Interfaces.Wireless...)
|
||||||
|
}
|
||||||
|
|
||||||
// Network struct
|
// Network struct
|
||||||
type Network struct {
|
type Network struct {
|
||||||
Mac string `json:"mac"`
|
Mac string `json:"mac"`
|
||||||
|
|
|
@ -37,6 +37,12 @@ func (conn *Connection) InsertNode(node *runtime.Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *Connection) InsertLink(link *runtime.Link, time time.Time) {
|
||||||
|
for _, item := range conn.list {
|
||||||
|
item.InsertLink(link, time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (conn *Connection) InsertGlobals(stats *runtime.GlobalStats, time time.Time) {
|
func (conn *Connection) InsertGlobals(stats *runtime.GlobalStats, time time.Time) {
|
||||||
for _, item := range conn.list {
|
for _, item := range conn.list {
|
||||||
item.InsertGlobals(stats, time)
|
item.InsertGlobals(stats, time)
|
||||||
|
|
|
@ -11,8 +11,11 @@ type Connection interface {
|
||||||
// InsertNode stores statistics per node
|
// InsertNode stores statistics per node
|
||||||
InsertNode(node *runtime.Node)
|
InsertNode(node *runtime.Node)
|
||||||
|
|
||||||
|
// InsertLink stores statistics per link
|
||||||
|
InsertLink(*runtime.Link, time.Time)
|
||||||
|
|
||||||
// InsertGlobals stores global statistics
|
// InsertGlobals stores global statistics
|
||||||
InsertGlobals(stats *runtime.GlobalStats, time time.Time)
|
InsertGlobals(*runtime.GlobalStats, time.Time)
|
||||||
|
|
||||||
// PruneNodes prunes historical per-node data
|
// PruneNodes prunes historical per-node data
|
||||||
PruneNodes(deleteAfter time.Duration)
|
PruneNodes(deleteAfter time.Duration)
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package graphite
|
package graphite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/FreifunkBremen/yanic/database"
|
|
||||||
"github.com/fgrosse/graphigo"
|
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/FreifunkBremen/yanic/database"
|
||||||
|
"github.com/fgrosse/graphigo"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package graphite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/FreifunkBremen/yanic/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InsertLink stores per link statistics
|
||||||
|
func (c *Connection) InsertLink(link *runtime.Link, time time.Time) {
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
MeasurementLink = "link" // Measurement for per-link statistics
|
||||||
MeasurementNode = "node" // Measurement for per-node statistics
|
MeasurementNode = "node" // Measurement for per-node statistics
|
||||||
MeasurementGlobal = "global" // Measurement for summarized global statistics
|
MeasurementGlobal = "global" // Measurement for summarized global statistics
|
||||||
CounterMeasurementFirmware = "firmware" // Measurement for firmware statistics
|
CounterMeasurementFirmware = "firmware" // Measurement for firmware statistics
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package influxdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/FreifunkBremen/yanic/runtime"
|
||||||
|
models "github.com/influxdata/influxdb/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InsertLink adds a link data point
|
||||||
|
func (conn *Connection) InsertLink(link *runtime.Link, t time.Time) {
|
||||||
|
tags := models.Tags{}
|
||||||
|
tags.SetString("source.id", link.SourceID)
|
||||||
|
tags.SetString("source.mac", link.SourceMAC)
|
||||||
|
tags.SetString("target.id", link.TargetID)
|
||||||
|
tags.SetString("target.mac", link.TargetMAC)
|
||||||
|
|
||||||
|
conn.addPoint(MeasurementLink, tags, models.Fields{"tq": float32(link.TQ) / 2.55}, t)
|
||||||
|
}
|
|
@ -11,24 +11,28 @@ import (
|
||||||
"github.com/FreifunkBremen/yanic/runtime"
|
"github.com/FreifunkBremen/yanic/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InsertNode implementation of database
|
// PruneNodes prunes historical per-node data
|
||||||
func (conn *Connection) InsertNode(node *runtime.Node) {
|
|
||||||
tags, fields := buildNodeStats(node)
|
|
||||||
conn.addPoint(MeasurementNode, tags, fields, time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (conn *Connection) PruneNodes(deleteAfter time.Duration) {
|
func (conn *Connection) PruneNodes(deleteAfter time.Duration) {
|
||||||
query := fmt.Sprintf("delete from %s where time < now() - %ds", MeasurementNode, deleteAfter/time.Second)
|
for _, measurement := range []string{MeasurementNode, MeasurementLink} {
|
||||||
|
query := fmt.Sprintf("delete from %s where time < now() - %ds", measurement, deleteAfter/time.Second)
|
||||||
conn.client.Query(client.NewQuery(query, conn.config.Database(), "m"))
|
conn.client.Query(client.NewQuery(query, conn.config.Database(), "m"))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns tags and fields for InfluxDB
|
// InsertNode stores statistics and neighbours in the database
|
||||||
func buildNodeStats(node *runtime.Node) (tags models.Tags, fields models.Fields) {
|
func (conn *Connection) InsertNode(node *runtime.Node) {
|
||||||
stats := node.Statistics
|
stats := node.Statistics
|
||||||
|
time := node.Lastseen.GetTime()
|
||||||
|
|
||||||
|
if stats == nil || stats.NodeID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := models.Tags{}
|
||||||
tags.SetString("nodeid", stats.NodeID)
|
tags.SetString("nodeid", stats.NodeID)
|
||||||
|
|
||||||
fields = map[string]interface{}{
|
fields := models.Fields{
|
||||||
"load": stats.LoadAverage,
|
"load": stats.LoadAverage,
|
||||||
"time.up": int64(stats.Uptime),
|
"time.up": int64(stats.Uptime),
|
||||||
"time.idle": int64(stats.Idletime),
|
"time.idle": int64(stats.Idletime),
|
||||||
|
@ -123,5 +127,7 @@ func buildNodeStats(node *runtime.Node) (tags models.Tags, fields models.Fields)
|
||||||
tags.SetString("frequency"+suffix, strconv.Itoa(int(airtime.Frequency)))
|
tags.SetString("frequency"+suffix, strconv.Itoa(int(airtime.Frequency)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn.addPoint(MeasurementNode, tags, fields, time)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package influxdb
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/influxdata/influxdb/client/v2"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/FreifunkBremen/yanic/data"
|
"github.com/FreifunkBremen/yanic/data"
|
||||||
|
@ -14,7 +15,7 @@ func TestToInflux(t *testing.T) {
|
||||||
|
|
||||||
node := &runtime.Node{
|
node := &runtime.Node{
|
||||||
Statistics: &data.Statistics{
|
Statistics: &data.Statistics{
|
||||||
NodeID: "foobar",
|
NodeID: "deadbeef",
|
||||||
LoadAverage: 0.5,
|
LoadAverage: 0.5,
|
||||||
Wireless: data.WirelessStatistics{
|
Wireless: data.WirelessStatistics{
|
||||||
&data.WirelessAirtime{Frequency: 5500},
|
&data.WirelessAirtime{Frequency: 5500},
|
||||||
|
@ -46,6 +47,7 @@ func TestToInflux(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Nodeinfo: &data.NodeInfo{
|
Nodeinfo: &data.NodeInfo{
|
||||||
|
NodeID: "deadbeef",
|
||||||
Owner: &data.Owner{
|
Owner: &data.Owner{
|
||||||
Contact: "nobody",
|
Contact: "nobody",
|
||||||
},
|
},
|
||||||
|
@ -53,12 +55,18 @@ func TestToInflux(t *testing.T) {
|
||||||
TxPower24: 3,
|
TxPower24: 3,
|
||||||
Channel24: 4,
|
Channel24: 4,
|
||||||
},
|
},
|
||||||
|
Network: data.Network{
|
||||||
|
Mac: "DEADMAC",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Neighbours: &data.Neighbours{
|
Neighbours: &data.Neighbours{
|
||||||
|
NodeID: "deadbeef",
|
||||||
Batadv: map[string]data.BatadvNeighbours{
|
Batadv: map[string]data.BatadvNeighbours{
|
||||||
"a-interface": data.BatadvNeighbours{
|
"a-interface": data.BatadvNeighbours{
|
||||||
Neighbours: map[string]data.BatmanLink{
|
Neighbours: map[string]data.BatmanLink{
|
||||||
"b-neigbourinterface": data.BatmanLink{},
|
"BAFF1E5": data.BatmanLink{
|
||||||
|
Tq: 204,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -66,23 +74,96 @@ func TestToInflux(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tags, fields := buildNodeStats(node)
|
neigbour := &runtime.Node{
|
||||||
|
Nodeinfo: &data.NodeInfo{
|
||||||
|
NodeID: "foobar",
|
||||||
|
Network: data.Network{
|
||||||
|
Mac: "BAFF1E5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Statistics: &data.Statistics{},
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal("foobar", tags.GetString("nodeid"))
|
points := testPoints(node, neigbour)
|
||||||
assert.Equal("nobody", tags.GetString("owner"))
|
var fields map[string]interface{}
|
||||||
assert.Equal(0.5, fields["load"])
|
var tags map[string]string
|
||||||
assert.Equal(0, fields["neighbours.lldp"])
|
|
||||||
assert.Equal(1, fields["neighbours.batadv"])
|
|
||||||
assert.Equal(1, fields["neighbours.vpn"])
|
|
||||||
assert.Equal(1, fields["neighbours.total"])
|
|
||||||
|
|
||||||
assert.Equal(uint32(3), fields["wireless.txpower24"])
|
assert.Len(points, 2)
|
||||||
assert.Equal(uint32(5500), fields["airtime11a.frequency"])
|
|
||||||
assert.Equal("", tags.GetString("frequency5500"))
|
|
||||||
|
|
||||||
assert.Equal(int64(1213), fields["traffic.rx.bytes"])
|
// first point contains the neighbour
|
||||||
assert.Equal(float64(1321), fields["traffic.tx.dropped"])
|
sPoint := points[0]
|
||||||
assert.Equal(int64(1322), fields["traffic.forward.bytes"])
|
tags = sPoint.Tags()
|
||||||
assert.Equal(int64(2331), fields["traffic.mgmt_rx.bytes"])
|
fields, _ = sPoint.Fields()
|
||||||
assert.Equal(float64(2327), fields["traffic.mgmt_tx.packets"])
|
|
||||||
|
assert.EqualValues("deadbeef", tags["nodeid"])
|
||||||
|
assert.EqualValues("nobody", tags["owner"])
|
||||||
|
assert.EqualValues(0.5, fields["load"])
|
||||||
|
assert.EqualValues(0, fields["neighbours.lldp"])
|
||||||
|
assert.EqualValues(1, fields["neighbours.batadv"])
|
||||||
|
assert.EqualValues(1, fields["neighbours.vpn"])
|
||||||
|
assert.EqualValues(1, fields["neighbours.total"])
|
||||||
|
|
||||||
|
assert.EqualValues(uint32(3), fields["wireless.txpower24"])
|
||||||
|
assert.EqualValues(uint32(5500), fields["airtime11a.frequency"])
|
||||||
|
assert.EqualValues("", tags["frequency5500"])
|
||||||
|
|
||||||
|
assert.EqualValues(int64(1213), fields["traffic.rx.bytes"])
|
||||||
|
assert.EqualValues(float64(1321), fields["traffic.tx.dropped"])
|
||||||
|
assert.EqualValues(int64(1322), fields["traffic.forward.bytes"])
|
||||||
|
assert.EqualValues(int64(2331), fields["traffic.mgmt_rx.bytes"])
|
||||||
|
assert.EqualValues(float64(2327), fields["traffic.mgmt_tx.packets"])
|
||||||
|
|
||||||
|
// second point contains the neighbour
|
||||||
|
nPoint := points[1]
|
||||||
|
tags = nPoint.Tags()
|
||||||
|
fields, _ = nPoint.Fields()
|
||||||
|
assert.EqualValues("link", nPoint.Name())
|
||||||
|
assert.EqualValues(map[string]string{
|
||||||
|
"source.id": "deadbeef",
|
||||||
|
"source.mac": "a-interface",
|
||||||
|
"target.id": "foobar",
|
||||||
|
"target.mac": "BAFF1E5",
|
||||||
|
}, tags)
|
||||||
|
assert.EqualValues(80, fields["tq"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes data and returns the InfluxDB points
|
||||||
|
func testPoints(nodes ...*runtime.Node) (points []*client.Point) {
|
||||||
|
// Create dummy client
|
||||||
|
influxClient, err := client.NewHTTPClient(client.HTTPConfig{Addr: "http://127.0.0.1"})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodesList := runtime.NewNodes(&runtime.Config{})
|
||||||
|
|
||||||
|
// Create dummy connection
|
||||||
|
conn := &Connection{
|
||||||
|
points: make(chan *client.Point),
|
||||||
|
client: influxClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
nodesList.Update(node.Nodeinfo.NodeID, &data.ResponseData{NodeInfo: node.Nodeinfo})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process data
|
||||||
|
go func() {
|
||||||
|
for _, node := range nodes {
|
||||||
|
conn.InsertNode(node)
|
||||||
|
if node.Neighbours != nil {
|
||||||
|
for _, link := range nodesList.NodeLinks(node) {
|
||||||
|
conn.InsertLink(&link, node.Lastseen.GetTime())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Read points
|
||||||
|
for point := range conn.points {
|
||||||
|
points = append(points, point)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,10 @@ func (conn *Connection) InsertNode(node *runtime.Node) {
|
||||||
conn.log("InsertNode: [", node.Statistics.NodeID, "] clients: ", node.Statistics.Clients.Total)
|
conn.log("InsertNode: [", node.Statistics.NodeID, "] clients: ", node.Statistics.Clients.Total)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *Connection) InsertLink(link *runtime.Link, time time.Time) {
|
||||||
|
conn.log("InsertLink: ", link)
|
||||||
|
}
|
||||||
|
|
||||||
func (conn *Connection) InsertGlobals(stats *runtime.GlobalStats, time time.Time) {
|
func (conn *Connection) InsertGlobals(stats *runtime.GlobalStats, time time.Time) {
|
||||||
conn.log("InsertGlobals: [", time.String(), "] nodes: ", stats.Nodes, ", clients: ", stats.Clients, " models: ", len(stats.Models))
|
conn.log("InsertGlobals: [", time.String(), "] nodes: ", stats.Nodes, ", clients: ", stats.Clients, " models: ", len(stats.Models))
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,8 +73,7 @@ func (builder *graphBuilder) readNodes(nodes map[string]*runtime.Node) {
|
||||||
|
|
||||||
// Batman neighbours
|
// Batman neighbours
|
||||||
for _, batinterface := range nodeinfo.Network.Mesh {
|
for _, batinterface := range nodeinfo.Network.Mesh {
|
||||||
interfaces := batinterface.Interfaces
|
addresses := batinterface.Addresses()
|
||||||
addresses := append(append(interfaces.Other, interfaces.Tunnel...), interfaces.Wireless...)
|
|
||||||
|
|
||||||
for _, sourceAddress := range addresses {
|
for _, sourceAddress := range addresses {
|
||||||
builder.macToID[sourceAddress] = sourceID
|
builder.macToID[sourceAddress] = sourceID
|
||||||
|
|
|
@ -33,9 +33,7 @@ func TestGenerateGraph(t *testing.T) {
|
||||||
|
|
||||||
func testGetNodesByFile(files ...string) *runtime.Nodes {
|
func testGetNodesByFile(files ...string) *runtime.Nodes {
|
||||||
|
|
||||||
nodes := &runtime.Nodes{
|
nodes := runtime.NewNodes(&runtime.Config{})
|
||||||
List: make(map[string]*runtime.Node),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
node := testGetNodeByFile(file)
|
node := testGetNodeByFile(file)
|
||||||
|
|
|
@ -204,14 +204,31 @@ func (coll *Collector) saveResponse(addr net.UDPAddr, res *data.ResponseData) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set fields to nil if nodeID is inconsistent
|
||||||
|
if res.Statistics != nil && res.Statistics.NodeID != nodeID {
|
||||||
|
res.Statistics = nil
|
||||||
|
}
|
||||||
|
if res.Neighbours != nil && res.Neighbours.NodeID != nodeID {
|
||||||
|
res.Neighbours = nil
|
||||||
|
}
|
||||||
|
if res.NodeInfo != nil && res.NodeInfo.NodeID != nodeID {
|
||||||
|
res.NodeInfo = nil
|
||||||
|
}
|
||||||
|
|
||||||
// Process the data and update IP address
|
// Process the data and update IP address
|
||||||
node := coll.nodes.Update(nodeID, res)
|
node := coll.nodes.Update(nodeID, res)
|
||||||
node.Address = addr.IP
|
node.Address = addr.IP
|
||||||
|
|
||||||
// Store statistics in database
|
// Store statistics in database
|
||||||
if coll.db != nil && node.Statistics != nil {
|
if db := coll.db; db != nil {
|
||||||
node.Statistics.NodeID = nodeID
|
db.InsertNode(node)
|
||||||
coll.db.InsertNode(node)
|
|
||||||
|
// Store link data
|
||||||
|
if neighbours := node.Neighbours; neighbours != nil {
|
||||||
|
for _, link := range coll.nodes.NodeLinks(node) {
|
||||||
|
db.InsertLink(&link, node.Lastseen.GetTime())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,15 @@ type Node struct {
|
||||||
Neighbours *data.Neighbours `json:"-"`
|
Neighbours *data.Neighbours `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Link represents a link between two nodes
|
||||||
|
type Link struct {
|
||||||
|
SourceID string
|
||||||
|
SourceMAC string
|
||||||
|
TargetID string
|
||||||
|
TargetMAC string
|
||||||
|
TQ int
|
||||||
|
}
|
||||||
|
|
||||||
// IsGateway returns whether the node is a gateway
|
// IsGateway returns whether the node is a gateway
|
||||||
func (node *Node) IsGateway() bool {
|
func (node *Node) IsGateway() bool {
|
||||||
if info := node.Nodeinfo; info != nil {
|
if info := node.Nodeinfo; info != nil {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
// Nodes struct: cache DB of Node's structs
|
// Nodes struct: cache DB of Node's structs
|
||||||
type Nodes struct {
|
type Nodes struct {
|
||||||
List map[string]*Node `json:"nodes"` // the current nodemap, indexed by node ID
|
List map[string]*Node `json:"nodes"` // the current nodemap, indexed by node ID
|
||||||
|
ifaceToNodeID map[string]string // mapping from MAC address to NodeID
|
||||||
config *Config
|
config *Config
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
@ -22,6 +23,7 @@ type Nodes struct {
|
||||||
func NewNodes(config *Config) *Nodes {
|
func NewNodes(config *Config) *Nodes {
|
||||||
nodes := &Nodes{
|
nodes := &Nodes{
|
||||||
List: make(map[string]*Node),
|
List: make(map[string]*Node),
|
||||||
|
ifaceToNodeID: make(map[string]string),
|
||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,28 +54,23 @@ func (nodes *Nodes) Update(nodeID string, res *data.ResponseData) *Node {
|
||||||
}
|
}
|
||||||
nodes.Unlock()
|
nodes.Unlock()
|
||||||
|
|
||||||
|
// Update wireless statistics
|
||||||
|
if statistics := res.Statistics; statistics != nil {
|
||||||
|
// Update channel utilization if previous statistics are present
|
||||||
|
if node.Statistics != nil && node.Statistics.Wireless != nil && statistics.Wireless != nil {
|
||||||
|
statistics.Wireless.SetUtilization(node.Statistics.Wireless)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update fields
|
||||||
node.Lastseen = now
|
node.Lastseen = now
|
||||||
node.Online = true
|
node.Online = true
|
||||||
|
node.Neighbours = res.Neighbours
|
||||||
|
node.Nodeinfo = res.NodeInfo
|
||||||
|
node.Statistics = res.Statistics
|
||||||
|
|
||||||
// Update neighbours
|
if node.Nodeinfo != nil {
|
||||||
if val := res.Neighbours; val != nil {
|
nodes.readIfaces(node.Nodeinfo)
|
||||||
node.Neighbours = val
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update nodeinfo
|
|
||||||
if val := res.NodeInfo; val != nil {
|
|
||||||
node.Nodeinfo = val
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update statistics
|
|
||||||
if val := res.Statistics; val != nil {
|
|
||||||
|
|
||||||
// Update channel utilization if previous statistics are present
|
|
||||||
if node.Statistics != nil && node.Statistics.Wireless != nil && val.Wireless != nil {
|
|
||||||
val.Wireless.SetUtilization(node.Statistics.Wireless)
|
|
||||||
}
|
|
||||||
|
|
||||||
node.Statistics = val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
@ -93,6 +90,33 @@ func (nodes *Nodes) Select(f func(*Node) bool) []*Node {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NodeLinks returns a list of links to known neighbours
|
||||||
|
func (nodes *Nodes) NodeLinks(node *Node) (result []Link) {
|
||||||
|
// Store link data
|
||||||
|
neighbours := node.Neighbours
|
||||||
|
if neighbours == nil || neighbours.NodeID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.RLock()
|
||||||
|
defer nodes.RUnlock()
|
||||||
|
|
||||||
|
for sourceMAC, batadv := range neighbours.Batadv {
|
||||||
|
for neighbourMAC, link := range batadv.Neighbours {
|
||||||
|
if neighbourID := nodes.ifaceToNodeID[neighbourMAC]; neighbourID != "" {
|
||||||
|
result = append(result, Link{
|
||||||
|
SourceID: neighbours.NodeID,
|
||||||
|
SourceMAC: sourceMAC,
|
||||||
|
TargetID: neighbourID,
|
||||||
|
TargetMAC: neighbourMAC,
|
||||||
|
TQ: link.Tq,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Periodically saves the cached DB to json file
|
// Periodically saves the cached DB to json file
|
||||||
func (nodes *Nodes) worker() {
|
func (nodes *Nodes) worker() {
|
||||||
c := time.Tick(nodes.config.Nodes.SaveInterval.Duration)
|
c := time.Tick(nodes.config.Nodes.SaveInterval.Duration)
|
||||||
|
@ -132,12 +156,47 @@ func (nodes *Nodes) expire() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adds the nodes interface addresses to the internal map
|
||||||
|
func (nodes *Nodes) readIfaces(nodeinfo *data.NodeInfo) {
|
||||||
|
nodeID := nodeinfo.NodeID
|
||||||
|
network := nodeinfo.Network
|
||||||
|
|
||||||
|
if nodeID == "" {
|
||||||
|
log.Println("nodeID missing in nodeinfo")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodes.Lock()
|
||||||
|
defer nodes.Unlock()
|
||||||
|
|
||||||
|
addresses := []string{network.Mac}
|
||||||
|
|
||||||
|
for _, batinterface := range network.Mesh {
|
||||||
|
addresses = append(addresses, batinterface.Addresses()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mac := range addresses {
|
||||||
|
if oldNodeID, _ := nodes.ifaceToNodeID[mac]; oldNodeID != nodeID {
|
||||||
|
if oldNodeID != "" {
|
||||||
|
log.Printf("override nodeID from %s to %s on MAC address %s", oldNodeID, nodeID, mac)
|
||||||
|
}
|
||||||
|
nodes.ifaceToNodeID[mac] = nodeID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (nodes *Nodes) load() {
|
func (nodes *Nodes) load() {
|
||||||
path := nodes.config.Nodes.StatePath
|
path := nodes.config.Nodes.StatePath
|
||||||
|
|
||||||
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")
|
||||||
|
|
||||||
|
for _, node := range nodes.List {
|
||||||
|
if node.Nodeinfo != nil {
|
||||||
|
nodes.readIfaces(node.Nodeinfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.Println("failed to unmarshal nodes:", err)
|
log.Println("failed to unmarshal nodes:", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ func TestExpire(t *testing.T) {
|
||||||
nodes := &Nodes{
|
nodes := &Nodes{
|
||||||
config: config,
|
config: config,
|
||||||
List: make(map[string]*Node),
|
List: make(map[string]*Node),
|
||||||
|
ifaceToNodeID: make(map[string]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes.Update("expire", &data.ResponseData{}) // should expire
|
nodes.Update("expire", &data.ResponseData{}) // should expire
|
||||||
|
@ -63,7 +64,10 @@ func TestLoadAndSave(t *testing.T) {
|
||||||
|
|
||||||
func TestUpdateNodes(t *testing.T) {
|
func TestUpdateNodes(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
nodes := &Nodes{List: make(map[string]*Node)}
|
nodes := &Nodes{
|
||||||
|
List: make(map[string]*Node),
|
||||||
|
ifaceToNodeID: make(map[string]string),
|
||||||
|
}
|
||||||
assert.Len(nodes.List, 0)
|
assert.Len(nodes.List, 0)
|
||||||
|
|
||||||
res := &data.ResponseData{
|
res := &data.ResponseData{
|
||||||
|
|
Loading…
Reference in New Issue