yanic/output/meshviewer/graph.go

192 lines
4.7 KiB
Go
Raw Normal View History

package meshviewer
2016-03-07 02:29:31 +01:00
import (
"fmt"
"strings"
"github.com/FreifunkBremen/yanic/runtime"
2016-03-07 02:29:31 +01:00
)
// Graph a struct for all links between the nodes
2016-03-07 02:29:31 +01:00
type Graph struct {
Version int `json:"version"`
Batadv struct {
2016-06-16 18:03:45 +02:00
Directed bool `json:"directed"`
Graph []string `json:"graph"`
Nodes []*GraphNode `json:"nodes"`
Links []*GraphLink `json:"links"`
2016-03-07 02:29:31 +01:00
} `json:"batadv"`
}
// GraphNode small struct of a node for the graph struct
2016-05-16 11:41:45 +02:00
type GraphNode struct {
2016-06-16 18:03:45 +02:00
ID string `json:"id"`
NodeID string `json:"node_id"`
2016-05-16 11:41:45 +02:00
}
// GraphLink a struct for the link between two nodes
2016-03-07 02:29:31 +01:00
type GraphLink struct {
2016-06-16 21:17:06 +02:00
Source int `json:"source"`
Target int `json:"target"`
VPN bool `json:"vpn"`
TQ float32 `json:"tq"`
Bidirect bool `json:"bidirect"`
2016-03-07 02:29:31 +01:00
}
// 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
idToMac map[string]string // mapping from node id to one MAC address
links map[string]*GraphLink // mapping from $idA-$idB to existing link
2016-03-07 02:29:31 +01:00
}
// BuildGraph transform from nodes (Neighbours) to Graph
func BuildGraph(nodes *runtime.Nodes) *Graph {
builder := &graphBuilder{
2016-03-07 02:29:31 +01:00
macToID: make(map[string]string),
idToMac: make(map[string]string),
2016-03-07 02:29:31 +01:00
links: make(map[string]*GraphLink),
2016-03-07 12:05:53 +01:00
}
2016-03-07 02:29:31 +01:00
builder.readNodes(nodes.List)
2016-05-16 11:41:45 +02:00
graph := &Graph{Version: 1}
graph.Batadv.Directed = false
graph.Batadv.Nodes, graph.Batadv.Links = builder.extract()
2016-03-07 02:29:31 +01:00
return graph
}
func (builder *graphBuilder) readNodes(nodes map[string]*runtime.Node) {
2016-03-07 02:29:31 +01:00
// Fill mac->id map
2016-06-16 18:03:45 +02:00
for sourceID, node := range nodes {
if nodeinfo := node.Nodeinfo; nodeinfo != nil {
2016-07-13 17:36:01 +02:00
if nodeinfo.Network.Mac != "" {
2017-03-07 11:47:41 +01:00
builder.idToMac[sourceID] = nodeinfo.Network.Mac
}
2016-07-13 17:36:01 +02:00
// Batman neighbours
2016-06-16 18:03:45 +02:00
for _, batinterface := range nodeinfo.Network.Mesh {
addresses := batinterface.Addresses()
2016-04-29 08:25:45 +02:00
for _, sourceAddress := range addresses {
2016-06-16 18:03:45 +02:00
builder.macToID[sourceAddress] = sourceID
2016-03-07 12:05:53 +01:00
}
2016-03-07 02:29:31 +01:00
}
}
2016-07-13 17:36:01 +02:00
// Iterate over local MAC addresses from LLDP
if neighbours := node.Neighbours; neighbours != nil {
for sourceAddr := range neighbours.LLDP {
2016-07-13 17:36:01 +02:00
builder.macToID[sourceAddr] = sourceID
}
}
2016-03-07 02:29:31 +01:00
}
// Add links
2016-06-16 18:03:45 +02:00
for sourceID, node := range nodes {
if node.Online {
vpnInterface := make(map[string]interface{})
if nodeinfo := node.Nodeinfo; nodeinfo != nil {
for _, batinterface := range nodeinfo.Network.Mesh {
for _, vpn := range batinterface.Interfaces.Tunnel {
vpnInterface[vpn] = nil
}
}
}
if neighbours := node.Neighbours; neighbours != nil {
2016-07-13 17:36:01 +02:00
// Batman neighbours
for sourceMAC, batadvNeighbours := range neighbours.Batadv {
for targetAddress, link := range batadvNeighbours.Neighbours {
if targetID, found := builder.macToID[targetAddress]; found {
_, vpn := vpnInterface[sourceMAC]
builder.addLink(targetID, sourceID, link.Tq, vpn)
}
2016-03-07 02:29:31 +01:00
}
}
2016-07-13 17:36:01 +02:00
// LLDP
for _, neighbours := range neighbours.LLDP {
for targetAddress := range neighbours {
2016-07-13 17:36:01 +02:00
if targetID, found := builder.macToID[targetAddress]; found {
builder.addLink(targetID, sourceID, 255, false)
2016-07-13 17:36:01 +02:00
}
}
}
2016-03-07 02:29:31 +01:00
}
}
}
}
type graphNodeCache struct {
idToMac map[string]string
idToIndex map[string]int
count int
Nodes []*GraphNode
}
2016-03-07 02:29:31 +01:00
func newGraphNodeCache(idToMac map[string]string) *graphNodeCache {
return &graphNodeCache{
idToMac: idToMac,
idToIndex: make(map[string]int),
}
}
func (gn *graphNodeCache) getIndex(nodeID string) int {
index, ok := gn.idToIndex[nodeID]
if !ok {
node := &GraphNode{
ID: gn.idToMac[nodeID],
2016-05-16 11:41:45 +02:00
NodeID: nodeID,
}
gn.Nodes = append(gn.Nodes, node)
gn.idToIndex[nodeID] = gn.count
index = gn.count
gn.count++
2016-05-16 11:41:45 +02:00
}
return index
}
func (builder *graphBuilder) extract() ([]*GraphNode, []*GraphLink) {
links := make([]*GraphLink, len(builder.links))
cache := newGraphNodeCache(builder.idToMac)
2016-06-16 21:17:06 +02:00
// collect links
i := 0
2016-05-16 11:41:45 +02:00
for key, link := range builder.links {
2016-06-16 21:17:06 +02:00
pos := strings.IndexByte(key, '-')
link.Source = cache.getIndex(key[:pos])
link.Target = cache.getIndex(key[pos+1:])
2016-06-16 21:17:06 +02:00
links[i] = link
i++
2016-03-07 02:29:31 +01:00
}
return cache.Nodes, links
2016-03-07 02:29:31 +01:00
}
func (builder *graphBuilder) addLink(targetID string, sourceID string, linkTq int, vpn bool) {
2016-03-20 12:09:32 +01:00
// Sort IDs to generate the key
2016-03-07 02:29:31 +01:00
var key string
2016-06-16 18:03:45 +02:00
if strings.Compare(sourceID, targetID) > 0 {
key = fmt.Sprintf("%s-%s", sourceID, targetID)
2016-03-07 02:29:31 +01:00
} else {
2016-06-16 18:03:45 +02:00
key = fmt.Sprintf("%s-%s", targetID, sourceID)
2016-03-07 02:29:31 +01:00
}
var tq float32
if linkTq > 0 {
tq = float32(1.0 / (float32(linkTq) / 255.0))
}
if link, ok := builder.links[key]; !ok {
builder.links[key] = &GraphLink{
VPN: vpn,
2016-06-16 18:03:45 +02:00
TQ: tq,
2016-03-07 02:29:31 +01:00
}
} else {
// Use lowest of both link qualities
if tq < link.TQ {
link.TQ = tq
}
link.Bidirect = true
}
}