diff --git a/api/aliases.go b/api/aliases.go index 013b4ee..abd861b 100644 --- a/api/aliases.go +++ b/api/aliases.go @@ -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)) } diff --git a/api/nodes.go b/api/nodes.go index a59ec01..bc02a5d 100644 --- a/api/nodes.go +++ b/api/nodes.go @@ -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) } diff --git a/models/aliases.go b/models/aliases.go index bd842ee..92f3c62 100644 --- a/models/aliases.go +++ b/models/aliases.go @@ -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{ diff --git a/models/ansible.go b/models/ansible.go index ecc62a3..fef859d 100644 --- a/models/ansible.go +++ b/models/ansible.go @@ -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) } } diff --git a/models/meshviewerstatistics.go b/models/meshviewerstatistics.go deleted file mode 100644 index 7572590..0000000 --- a/models/meshviewerstatistics.go +++ /dev/null @@ -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""` -} diff --git a/models/nodes.go b/models/nodes.go index f8c1bad..6887fc4 100644 --- a/models/nodes.go +++ b/models/nodes.go @@ -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)