nodes split to meshviewernodes

This commit is contained in:
Martin Geno 2016-05-29 21:41:58 +02:00
parent bc525f2636
commit dc47ab8719
6 changed files with 178 additions and 113 deletions

View File

@ -1,80 +1,101 @@
package api
import (
"fmt"
"net/http"
"encoding/json"
"github.com/julienschmidt/httprouter"
"fmt"
"github.com/FreifunkBremen/respond-collector/models"
"github.com/julienschmidt/httprouter"
"net/http"
)
type ApiAliases struct {
aliases *models.Aliases
config *models.Config
nodes *models.Nodes
config *models.Config
nodes *models.Nodes
}
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{
aliases: models.NewAliases(config),
nodes: nodes,
config: config,
nodes: nodes,
config: config,
}
router.GET(prefix, api.GetAll)
router.GET(prefix+"/ansible", api.AnsibleDiff)
router.GET(prefix+"/cleanup", api.Cleanup)
router.GET(prefix+"/auth", BasicAuth(api.Cleanup, []byte(config.Webserver.Api.Passphrase)))
router.GET(prefix+"/alias/:nodeid", api.GetOne)
router.POST(prefix+"/alias/:nodeid", BasicAuth(api.SaveOne,[]byte(config.Webserver.Api.Passphrase)))
router.GET(prefix+"/auth", BasicAuth(api.Cleanup, []byte(config.Webserver.Api.Passphrase)))
router.GET(prefix+"/alias/:nodeid", api.GetOne)
router.POST(prefix+"/alias/:nodeid", BasicAuth(api.SaveOne, []byte(config.Webserver.Api.Passphrase)))
}
// clean up the aliases by correct values in nodes
func (api *ApiAliases) cleaner(){
for key,alias := range api.aliases.List {
if node := api.nodes.List[key]; node !=nil {
if nodeinfo := node.Nodeinfo; nodeinfo !=nil {
func (api *ApiAliases) cleaner() {
for key, alias := range api.aliases.List {
if node := api.nodes.List[key]; node != nil {
if nodeinfo := node.Nodeinfo; nodeinfo != nil {
//counter for the diffrent attribute
count := 1
count := 3
if alias.Hostname == nodeinfo.Hostname {
count -= 1
}
if alias.Location.Latitude == nodeinfo.Location.Latitude {
count -= 1
}
if alias.Location.Longtitude == nodeinfo.Location.Longtitude {
count -= 1
}
/*
if alias.Freq24.TxPower == nodeinfo.Freq24.TxPower {
count -= 1
}
if alias.Freq24.Channel == nodeinfo.Freq24.Channel {
count -= 1
}
if alias.Freq5.TxPower == nodeinfo.Freq5.TxPower {
count -= 1
}
if alias.Freq5.Channel == nodeinfo.Freq5.Channel {
count -= 1
}
*/
//delete element
if count <= 0 {
delete(api.aliases.List,key)
delete(api.aliases.List, key)
}
}
}
}
}
func (api *ApiAliases) 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) {
if alias := api.aliases.List[ps.ByName("nodeid")]; alias !=nil{
jsonOutput(w,r,alias)
if alias := api.aliases.List[ps.ByName("nodeid")]; alias != nil {
jsonOutput(w, r, alias)
return
}
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) {
var alias models.Alias
err := json.NewDecoder(r.Body).Decode(&alias)
if err != nil{
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
fmt.Fprint(w, "Decode: ", ps.ByName("nodeid"),"\n")
fmt.Fprint(w, "Decode: ", ps.ByName("nodeid"), "\n")
return
}
api.aliases.Update(ps.ByName("nodeid"),&alias)
jsonOutput(w,r,alias)
api.aliases.Update(ps.ByName("nodeid"), &alias)
jsonOutput(w, r, alias)
}
func (api *ApiAliases) Cleanup(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
api.cleaner()
jsonOutput(w,r,api.aliases.List)
jsonOutput(w, r, api.aliases.List)
}
func (api *ApiAliases) AnsibleDiff(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
api.cleaner()
jsonOutput(w,r,models.GenerateAnsible(api.nodes,api.aliases.List))
jsonOutput(w, r, models.GenerateAnsible(api.nodes, api.aliases.List))
}

View File

@ -1,24 +1,24 @@
package api
import (
"net/http"
"github.com/julienschmidt/httprouter"
"github.com/FreifunkBremen/respond-collector/models"
"github.com/julienschmidt/httprouter"
"net/http"
)
type ApiNodes struct {
config *models.Config
nodes *models.Nodes
config *models.Config
nodes *models.Nodes
}
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{
nodes: nodes,
nodes: nodes,
config: config,
}
router.GET(prefix, api.GetAll)
router.GET(prefix, api.GetAll)
}
func (api *ApiNodes) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
jsonOutput(w,r,api.nodes.List)
jsonOutput(w, r, api.nodes.List)
}

View File

@ -3,20 +3,31 @@ package models
import (
"encoding/json"
"io/ioutil"
"sync"
"log"
"sync"
"time"
"github.com/FreifunkBremen/respond-collector/data"
)
type Alias struct {
Hostname string `json:"hostname"`
Hostname string `json:"hostname"`
Location *data.Location `json:"location"`
Freq24 *Frequence `json:"freq24"`
Freq5 *Frequence `json:"freq5"`
}
type Frequence struct {
TxPower int `json:"txpower"`
Channel int `json:"channel"`
}
// Nodes struct: cache DB of Node's structs
type Aliases struct {
List map[string]*Alias `json:"nodes"` // the current nodemap, indexed by node ID
config *Config
List map[string]*Alias `json:"nodes"` // the current nodemap, indexed by node ID
config *Config
sync.Mutex
}
// NewNodes create Nodes structs
func NewAliases(config *Config) *Aliases {
aliases := &Aliases{

View File

@ -2,27 +2,39 @@ package models
type Ansible struct {
Nodes []string `json:"nodes"`
Meta struct {
Meta struct {
HostVars []*AnsibleHostVars `json:"hostvars"`
} `json:"_meta"`
}
type AnsibleHostVars struct {
Address string `json:"ansible_ssh_host"`
Hostname string `json:"node_name"`
Address string `json:"ansible_ssh_host"`
Hostname string `json:"node_name"`
Channel24 int `json:"radio24_channel"`
TxPower24 int `json:"radio24_txpower"`
Channel5 int `json:"radio5_channel"`
TxPower5 int `json:"radio5_txpower"`
GeoLatitude float64 `json:"geo_latitude"`
GeoLongitude float64 `json:"geo_longitude"`
}
func GenerateAnsible(nodes *Nodes,aliases map[string]*Alias) *Ansible{
ansible := &Ansible{Nodes:make([]string,0)}
for nodeid,alias := range aliases{
func GenerateAnsible(nodes *Nodes, aliases map[string]*Alias) *Ansible {
ansible := &Ansible{Nodes: make([]string, 0)}
for nodeid, alias := range aliases {
if node := nodes.List[nodeid]; node != nil {
ansible.Nodes = append(ansible.Nodes,nodeid)
ansible.Nodes = append(ansible.Nodes, nodeid)
vars := &AnsibleHostVars{
Address: node.Nodeinfo.Network.Addresses[0],
Hostname: alias.Hostname,
Address: node.Nodeinfo.Network.Addresses[0],
Hostname: alias.Hostname,
Channel24: alias.Freq24.Channel,
Channel5: alias.Freq5.Channel,
TxPower24: alias.Freq24.TxPower,
TxPower5: alias.Freq5.TxPower,
GeoLatitude: alias.Location.Latitude,
GeoLongitude: alias.Location.Longtitude,
}
ansible.Meta.HostVars = append(ansible.Meta.HostVars,vars)
ansible.Meta.HostVars = append(ansible.Meta.HostVars, vars)
}
}

View File

@ -1,26 +0,0 @@
package models
import "github.com/FreifunkBremen/respond-collector/data"
type MeshviewerStatistics struct {
NodeId string `json:"node_id"`
Clients uint32 `json:"clients"`
RootFsUsage float64 `json:"rootfs_usage,omitempty""`
LoadAverage float64 `json:"loadavg,omitempty""`
Memory data.Memory `json:"memory,omitempty""`
Uptime float64 `json:"uptime,omitempty""`
Idletime float64 `json:"idletime,omitempty""`
Gateway string `json:"gateway,omitempty"`
Processes struct {
Total uint32 `json:"total"`
Running uint32 `json:"running"`
} `json:"processes,omitempty""`
MeshVpn *data.MeshVPN `json:"mesh_vpn,omitempty"`
Traffic struct {
Tx *data.Traffic `json:"tx"`
Rx *data.Traffic `json:"rx"`
Forward *data.Traffic `json:"forward"`
MgmtTx *data.Traffic `json:"mgmt_tx"`
MgmtRx *data.Traffic `json:"mgmt_rx"`
} `json:"traffic,omitempty""`
}

View File

@ -10,21 +10,17 @@ import (
"github.com/FreifunkBremen/respond-collector/data"
"github.com/FreifunkBremen/respond-collector/jsontime"
"github.com/FreifunkBremen/respond-collector/meshviewer"
)
// Node struct
type Node struct {
Firstseen jsontime.Time `json:"firstseen"`
Lastseen jsontime.Time `json:"lastseen"`
Flags *Flags `json:"flags,omitempty"`
Statistics *MeshviewerStatistics `json:"statistics"`
Nodeinfo *data.NodeInfo `json:"nodeinfo"`
Neighbours *data.Neighbours `json:"-"`
}
type Flags struct {
Online bool `json:"online"`
Gateway bool `json:"gateway"`
Firstseen jsontime.Time `json:"firstseen"`
Lastseen jsontime.Time `json:"lastseen"`
Flags *meshviewer.Flags `json:"flags,omitempty"`
Statistics *data.Statistics `json:"statistics"`
Nodeinfo *data.NodeInfo `json:"nodeinfo"`
Neighbours *data.Neighbours `json:"-"`
}
// Nodes struct: cache DB of Node's structs
@ -62,8 +58,8 @@ func (nodes *Nodes) Update(nodeID string, res *data.ResponseData) {
if node == nil {
node = &Node{
Firstseen: now,
Flags: &Flags{
Online: true,
Flags: &meshviewer.Flags{
Online: true,
Gateway: false,
},
}
@ -73,7 +69,7 @@ func (nodes *Nodes) Update(nodeID string, res *data.ResponseData) {
node.Lastseen = now
if node.Flags !=nil {
if node.Flags != nil {
node.Flags.Online = true
}
@ -90,22 +86,45 @@ func (nodes *Nodes) Update(nodeID string, res *data.ResponseData) {
// Update statistics
if val := res.Statistics; val != nil {
node.Statistics = &MeshviewerStatistics{
NodeId: val.NodeId,
Clients: 0,
Gateway: val.Gateway,
RootFsUsage: val.RootFsUsage,
LoadAverage: val.LoadAverage,
Memory: val.Memory,
Uptime: val.Uptime,
Idletime: val.Idletime,
Processes: val.Processes,
MeshVpn: val.MeshVpn,
Traffic: val.Traffic,
}
node.Statistics.Clients = val.Clients.Total
node.Statistics = val
}
}
func (nodes *Nodes) GetMeshviewer() *meshviewer.Nodes {
meshviewerNodes := &meshviewer.Nodes{
Version: nodes.Version,
List: make(map[string]*meshviewer.Node),
Timestamp: nodes.Timestamp,
}
for nodeID, _ := range nodes.List {
meshviewerNodes.Lock()
node, _ := meshviewerNodes.List[nodeID]
if node == nil {
node = &meshviewer.Node{
Firstseen: nodes.List[nodeID].Firstseen,
Lastseen: nodes.List[nodeID].Lastseen,
Flags: nodes.List[nodeID].Flags,
Nodeinfo: nodes.List[nodeID].Nodeinfo,
}
meshviewerNodes.List[nodeID] = node
}
meshviewerNodes.Unlock()
node.Statistics = &meshviewer.Statistics{
NodeId: nodes.List[nodeID].Statistics.NodeId,
Clients: nodes.List[nodeID].Statistics.Clients.Total,
Gateway: nodes.List[nodeID].Statistics.Gateway,
RootFsUsage: nodes.List[nodeID].Statistics.RootFsUsage,
LoadAverage: nodes.List[nodeID].Statistics.LoadAverage,
Memory: nodes.List[nodeID].Statistics.Memory,
Uptime: nodes.List[nodeID].Statistics.Uptime,
Idletime: nodes.List[nodeID].Statistics.Idletime,
Processes: nodes.List[nodeID].Statistics.Processes,
MeshVpn: nodes.List[nodeID].Statistics.MeshVpn,
Traffic: nodes.List[nodeID].Statistics.Traffic,
}
}
return meshviewerNodes
}
// Periodically saves the cached DB to json file
func (nodes *Nodes) worker() {
@ -115,21 +134,17 @@ func (nodes *Nodes) worker() {
log.Println("saving", len(nodes.List), "nodes")
nodes.Timestamp = jsontime.Now()
nodes.Lock()
//
//
// set node as offline (without statistics)
for _,node := range nodes.List {
if node.Statistics != nil && node.Lastseen.Unix()+int64(1000*nodes.config.Respondd.CollectInterval) < nodes.Timestamp.Unix() {
node.Statistics = &MeshviewerStatistics{
NodeId: node.Statistics.NodeId,
Clients: 0,
}
for _, node := range nodes.List {
if node.Statistics != nil && node.Lastseen.Unix()+int64(5*nodes.config.Respondd.CollectInterval) < nodes.Timestamp.Unix() {
if node.Flags != nil {
node.Flags.Online = false
}
}
}
// serialize nodes
save(nodes, nodes.config.Nodes.NodesPath)
save(nodes.GetMeshviewer(), nodes.config.Nodes.NodesPath)
if path := nodes.config.Nodes.GraphsPath; path != "" {
save(nodes.BuildGraph(), path)
@ -143,8 +158,40 @@ func (nodes *Nodes) load() {
path := nodes.config.Nodes.NodesPath
log.Println("loading", path)
if data, err := ioutil.ReadFile(path); err == nil {
if err := json.Unmarshal(data, nodes); err == nil {
if filedata, err := ioutil.ReadFile(path); err == nil {
meshviewerNodes := &meshviewer.Nodes{}
if err := json.Unmarshal(filedata, meshviewerNodes); err == nil {
nodes.Version = meshviewerNodes.Version
nodes.Timestamp = meshviewerNodes.Timestamp
nodes.List = make(map[string]*Node)
for nodeID, _ := range meshviewerNodes.List {
nodes.Lock()
node, _ := nodes.List[nodeID]
if node == nil {
node = &Node{
Firstseen: meshviewerNodes.List[nodeID].Firstseen,
Lastseen: meshviewerNodes.List[nodeID].Lastseen,
Flags: meshviewerNodes.List[nodeID].Flags,
Nodeinfo: meshviewerNodes.List[nodeID].Nodeinfo,
}
nodes.List[nodeID] = node
}
nodes.Unlock()
node.Statistics = &data.Statistics{
NodeId: meshviewerNodes.List[nodeID].Statistics.NodeId,
Clients: data.Clients{Total: meshviewerNodes.List[nodeID].Statistics.Clients},
Gateway: meshviewerNodes.List[nodeID].Statistics.Gateway,
RootFsUsage: meshviewerNodes.List[nodeID].Statistics.RootFsUsage,
LoadAverage: meshviewerNodes.List[nodeID].Statistics.LoadAverage,
Memory: meshviewerNodes.List[nodeID].Statistics.Memory,
Uptime: meshviewerNodes.List[nodeID].Statistics.Uptime,
Idletime: meshviewerNodes.List[nodeID].Statistics.Idletime,
Processes: meshviewerNodes.List[nodeID].Statistics.Processes,
MeshVpn: meshviewerNodes.List[nodeID].Statistics.MeshVpn,
Traffic: meshviewerNodes.List[nodeID].Statistics.Traffic,
}
}
log.Println("loaded", len(nodes.List), "nodes")
} else {
log.Println("failed to unmarshal nodes:", err)