yanic/models/graph.go

156 lines
3.5 KiB
Go
Raw Normal View History

2016-03-07 02:29:31 +01:00
package models
import (
"fmt"
"strings"
)
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"`
}
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
}
2016-03-07 02:29:31 +01:00
type GraphLink struct {
2016-06-16 18:03:45 +02:00
Source interface{} `json:"source"`
Target interface{} `json:"target"`
VPN bool `json:"vpn"`
TQ float32 `json:"tq"`
Bidirect bool `json:"bidirect"`
2016-03-07 02:29:31 +01:00
}
type GraphBuilder struct {
2016-03-07 12:05:53 +01:00
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
2016-03-07 02:29:31 +01:00
}
2016-03-20 18:30:44 +01:00
func (nodes *Nodes) BuildGraph() *Graph {
2016-03-07 02:29:31 +01:00
builder := &GraphBuilder{
macToID: make(map[string]string),
links: make(map[string]*GraphLink),
2016-03-07 12:05:53 +01:00
vpn: make(map[string]interface{}),
}
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]*Node) {
// Fill mac->id map
2016-06-16 18:03:45 +02:00
for sourceID, node := range nodes {
if nodeinfo := node.Nodeinfo; nodeinfo != nil {
// is VPN address?
if nodeinfo.VPN {
2016-06-16 18:03:45 +02:00
builder.vpn[sourceID] = nil
}
2016-06-16 18:03:45 +02:00
for _, batinterface := range nodeinfo.Network.Mesh {
2016-04-29 08:25:45 +02:00
interfaces := batinterface.Interfaces
addresses := append(append(interfaces.Other, interfaces.Tunnel...), interfaces.Wireless...)
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
}
}
}
// Add links
2016-06-16 18:03:45 +02:00
for sourceID, node := range nodes {
2016-03-07 02:29:31 +01:00
if neighbours := node.Neighbours; neighbours != nil {
for _, batadvNeighbours := range neighbours.Batadv {
for targetAddress, link := range batadvNeighbours.Neighbours {
2016-06-16 18:03:45 +02:00
if targetID, found := builder.macToID[targetAddress]; found {
builder.addLink(targetID, sourceID, link.Tq)
2016-03-07 02:29:31 +01:00
}
}
}
}
}
}
2016-06-16 18:03:45 +02:00
func (builder *GraphBuilder) Extract() ([]*GraphNode, []*GraphLink) {
2016-05-16 11:41:45 +02:00
iNodes := 0
iLinks := 0
2016-03-07 02:29:31 +01:00
links := make([]*GraphLink, len(builder.links))
2016-05-16 11:41:45 +02:00
nodes := make([]*GraphNode, len(builder.macToID))
2016-03-07 02:29:31 +01:00
2016-05-16 11:41:45 +02:00
for mac, nodeID := range builder.macToID {
nodes[iNodes] = &GraphNode{
2016-06-16 18:03:45 +02:00
ID: mac,
2016-05-16 11:41:45 +02:00
NodeID: nodeID,
}
2016-06-16 18:03:45 +02:00
iNodes++
2016-05-16 11:41:45 +02:00
}
for key, link := range builder.links {
2016-06-16 18:03:45 +02:00
linkPart := strings.Split(key, "-")
2016-05-16 11:41:45 +02:00
both := 0
2016-06-16 18:03:45 +02:00
for i, node := range nodes {
if linkPart[0] == node.NodeID {
2016-05-16 11:41:45 +02:00
link.Source = i
2016-06-16 18:03:45 +02:00
both++
2016-05-16 11:41:45 +02:00
continue
}
2016-06-16 18:03:45 +02:00
if linkPart[1] == node.NodeID {
2016-05-16 11:41:45 +02:00
link.Target = i
2016-06-16 18:03:45 +02:00
both++
2016-05-16 11:41:45 +02:00
break
}
}
if both == 2 {
links[iLinks] = link
2016-06-16 18:03:45 +02:00
iLinks++
2016-05-16 11:41:45 +02:00
}
2016-03-07 02:29:31 +01:00
}
2016-06-16 18:03:45 +02:00
return nodes, links[:iLinks]
2016-03-07 02:29:31 +01:00
}
2016-03-07 12:05:53 +01:00
func (builder *GraphBuilder) isVPN(ids ...string) bool {
for _, id := range ids {
if _, found := builder.vpn[id]; found {
return true
}
}
return false
}
2016-06-16 18:03:45 +02:00
func (builder *GraphBuilder) addLink(targetID string, sourceID string, linkTq int) {
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{
2016-06-16 18:03:45 +02:00
VPN: builder.isVPN(sourceID, targetID),
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
}
}