diff --git a/config_example.toml b/config_example.toml index e20ee2e..6ea5eb9 100644 --- a/config_example.toml +++ b/config_example.toml @@ -67,6 +67,10 @@ enable = false template_path = "/var/lib/collector/html-template.tmp" output_path = "/var/www/html/index.html" +[[nodes.output.nodelist]] +enable = true +path = "/var/www/html/meshviewer/data/nodelist.json" + [database] # cleaning data of measurement node, diff --git a/contrib/example-template.tmpl b/contrib/example-template.tmpl index 3df930a..969da37 100644 --- a/contrib/example-template.tmpl +++ b/contrib/example-template.tmpl @@ -2,7 +2,7 @@ function ffhbCurrentStats(data) { $("#freifunk").html("

bremen.freifunk.net

- Nutzer: 0
+ Nutzer: 0
(auf 0 Geräte verteilt)

diff --git a/output/meshviewer/nodes_v1.go b/output/meshviewer/nodes_v1.go index 2203275..1352b41 100644 --- a/output/meshviewer/nodes_v1.go +++ b/output/meshviewer/nodes_v1.go @@ -21,8 +21,7 @@ func BuildNodesV1(toFilter filter, nodes *runtime.Nodes) interface{} { Timestamp: jsontime.Now(), } - for nodeID := range nodes.List { - nodeOrigin := nodes.List[nodeID] + for nodeID, nodeOrigin := range nodes.List { nodeFiltere := toFilter(nodeOrigin) if nodeOrigin.Statistics == nil || nodeFiltere == nil { continue diff --git a/output/meshviewer/nodes_v2.go b/output/meshviewer/nodes_v2.go index 9eabd37..290a97b 100644 --- a/output/meshviewer/nodes_v2.go +++ b/output/meshviewer/nodes_v2.go @@ -20,8 +20,7 @@ func BuildNodesV2(toFilter filter, nodes *runtime.Nodes) interface{} { Timestamp: jsontime.Now(), } - for nodeID := range nodes.List { - nodeOrigin := nodes.List[nodeID] + for _, nodeOrigin := range nodes.List { nodeFiltere := toFilter(nodeOrigin) if nodeOrigin.Statistics == nil || nodeFiltere == nil { continue diff --git a/output/nodelist/nodelist.go b/output/nodelist/nodelist.go new file mode 100644 index 0000000..4bb54f3 --- /dev/null +++ b/output/nodelist/nodelist.go @@ -0,0 +1,64 @@ +package nodelist + +import ( + "github.com/FreifunkBremen/yanic/jsontime" + "github.com/FreifunkBremen/yanic/runtime" +) + +// NodeList rewritten after: https://github.com/ffnord/ffmap-backend/blob/c33ebf62f013e18bf71b5a38bd058847340db6b7/lib/nodelist.py +type NodeList struct { + Version string `json:"version"` + Timestamp jsontime.Time `json:"updated_at"` // Timestamp of the generation + List []*Node `json:"nodes"` +} + +type Node struct { + ID string `json:"id"` + Name string `json:"name"` + Position *Position `json:"position,omitempty"` + Status struct { + Online bool `json:"online"` + LastContact jsontime.Time `json:"lastcontact"` + Clients uint32 `json:"clients"` + } `json:"status"` +} + +type Position struct { + Lat float64 `json:"lat"` + Long float64 `json:"long"` +} + +func NewNode(n *runtime.Node) *Node { + if nodeinfo := n.Nodeinfo; nodeinfo != nil { + node := &Node{ + ID: nodeinfo.NodeID, + Name: nodeinfo.Hostname, + } + if location := nodeinfo.Location; location != nil { + node.Position = &Position{Lat: location.Latitude, Long: location.Longtitude} + } + + node.Status.Online = n.Online + node.Status.LastContact = n.Lastseen + if statistics := n.Statistics; statistics != nil { + node.Status.Clients = statistics.Clients.Total + } + return node + } + return nil +} + +func transform(nodes *runtime.Nodes) *NodeList { + nodelist := &NodeList{ + Version: "1.0.1", + Timestamp: jsontime.Now(), + } + + for _, nodeOrigin := range nodes.List { + node := NewNode(nodeOrigin) + if node != nil { + nodelist.List = append(nodelist.List, node) + } + } + return nodelist +} diff --git a/output/nodelist/nodelist_test.go b/output/nodelist/nodelist_test.go new file mode 100644 index 0000000..f9fcd8d --- /dev/null +++ b/output/nodelist/nodelist_test.go @@ -0,0 +1,60 @@ +package nodelist + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/FreifunkBremen/yanic/data" + "github.com/FreifunkBremen/yanic/runtime" +) + +func TestTransform(t *testing.T) { + nodes := transform(createTestNodes()) + + assert := assert.New(t) + assert.Len(nodes.List, 3) +} + +func createTestNodes() *runtime.Nodes { + nodes := runtime.NewNodes(&runtime.Config{}) + + nodeData := &data.ResponseData{ + Statistics: &data.Statistics{ + Clients: data.Clients{ + Total: 23, + }, + }, + NodeInfo: &data.NodeInfo{ + Hardware: data.Hardware{ + Model: "TP-Link 841", + }, + }, + } + nodeData.NodeInfo.Software.Firmware.Release = "2016.1.6+entenhausen1" + nodes.Update("abcdef012345", nodeData) + + nodes.Update("112233445566", &data.ResponseData{ + Statistics: &data.Statistics{ + Clients: data.Clients{ + Total: 2, + }, + }, + NodeInfo: &data.NodeInfo{ + Hardware: data.Hardware{ + Model: "TP-Link 841", + }, + }, + }) + + nodes.Update("0xdeadbeef0x", &data.ResponseData{ + NodeInfo: &data.NodeInfo{ + VPN: true, + Hardware: data.Hardware{ + Model: "Xeon Multi-Core", + }, + }, + }) + + return nodes +} diff --git a/output/nodelist/output.go b/output/nodelist/output.go new file mode 100644 index 0000000..760c6b4 --- /dev/null +++ b/output/nodelist/output.go @@ -0,0 +1,51 @@ +package nodelist + +import ( + goTemplate "text/template" + + "github.com/FreifunkBremen/yanic/output" + "github.com/FreifunkBremen/yanic/runtime" +) + +type Output struct { + output.Output + config Config + nodes *runtime.Nodes + template *goTemplate.Template +} + +type Config map[string]interface{} + +func (c Config) Enable() bool { + return c["enable"].(bool) +} + +func (c Config) Path() string { + return c["path"].(string) +} + +func init() { + output.RegisterAdapter("nodelist", Register) +} + +func Register(nodes *runtime.Nodes, configuration interface{}) (output.Output, error) { + var config Config + config = configuration.(map[string]interface{}) + if !config.Enable() { + return nil, nil + } + + return &Output{ + config: config, + nodes: nodes, + }, nil +} + +func (o *Output) Save() { + o.nodes.RLock() + defer o.nodes.RUnlock() + + if path := o.config.Path(); path != "" { + runtime.SaveJSON(transform(o.nodes), path) + } +}