Merge pull request #14 from FreifunkBremen/measurements
Add measurements for models and firmwares
This commit is contained in:
commit
da9d2cf8c6
|
@ -14,6 +14,8 @@ import (
|
||||||
const (
|
const (
|
||||||
MeasurementNode = "node" // Measurement for per-node statistics
|
MeasurementNode = "node" // Measurement for per-node statistics
|
||||||
MeasurementGlobal = "global" // Measurement for summarized global statistics
|
MeasurementGlobal = "global" // Measurement for summarized global statistics
|
||||||
|
MeasurementFirmware = "firmware" // Measurement for firmware statistics
|
||||||
|
MeasurementModel = "model" // Measurement for model statistics
|
||||||
batchMaxSize = 500
|
batchMaxSize = 500
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,6 +67,23 @@ func (db *DB) AddPoint(name string, tags imodels.Tags, fields imodels.Fields, ti
|
||||||
db.points <- point
|
db.points <- point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Saves the values of a CounterMap in the database.
|
||||||
|
// The key are used as 'value' tag.
|
||||||
|
// The value is used as 'counter' field.
|
||||||
|
func (db *DB) AddCounterMap(name string, m models.CounterMap) {
|
||||||
|
now := time.Now()
|
||||||
|
for key, count := range m {
|
||||||
|
db.AddPoint(
|
||||||
|
name,
|
||||||
|
imodels.Tags{
|
||||||
|
imodels.Tag{Key: []byte("value"), Value: []byte(key)},
|
||||||
|
},
|
||||||
|
imodels.Fields{"count": count},
|
||||||
|
now,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add data for a single node
|
// Add data for a single node
|
||||||
func (db *DB) Add(nodeId string, node *models.Node) {
|
func (db *DB) Add(nodeId string, node *models.Node) {
|
||||||
tags, fields := node.ToInflux()
|
tags, fields := node.ToInflux()
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/FreifunkBremen/respond-collector/data"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestToInflux(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
node := Node{
|
|
||||||
Statistics: &data.Statistics{
|
|
||||||
NodeId: "foobar",
|
|
||||||
LoadAverage: 0.5,
|
|
||||||
},
|
|
||||||
Nodeinfo: &data.NodeInfo{
|
|
||||||
Owner: &data.Owner{
|
|
||||||
Contact: "nobody",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Neighbours: &data.Neighbours{},
|
|
||||||
}
|
|
||||||
|
|
||||||
tags, fields := node.ToInflux()
|
|
||||||
|
|
||||||
assert.Equal("foobar", tags.GetString("nodeid"))
|
|
||||||
assert.Equal("nobody", tags.GetString("owner"))
|
|
||||||
assert.Equal(0.5, fields["load"])
|
|
||||||
}
|
|
|
@ -21,14 +21,6 @@ type Nodes struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type GlobalStats struct {
|
|
||||||
Nodes uint32
|
|
||||||
Clients uint32
|
|
||||||
ClientsWifi uint32
|
|
||||||
ClientsWifi24 uint32
|
|
||||||
ClientsWifi5 uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNodes create Nodes structs
|
// NewNodes create Nodes structs
|
||||||
func NewNodes(config *Config) *Nodes {
|
func NewNodes(config *Config) *Nodes {
|
||||||
nodes := &Nodes{
|
nodes := &Nodes{
|
||||||
|
@ -207,36 +199,6 @@ func (nodes *Nodes) save() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns global statistics for InfluxDB
|
|
||||||
func (nodes *Nodes) GlobalStats() (result *GlobalStats) {
|
|
||||||
result = &GlobalStats{}
|
|
||||||
nodes.Lock()
|
|
||||||
for _, node := range nodes.List {
|
|
||||||
if node.Flags.Online {
|
|
||||||
result.Nodes += 1
|
|
||||||
if stats := node.Statistics; stats != nil {
|
|
||||||
result.Clients += stats.Clients.Total
|
|
||||||
result.ClientsWifi24 += stats.Clients.Wifi24
|
|
||||||
result.ClientsWifi5 += stats.Clients.Wifi5
|
|
||||||
result.ClientsWifi += stats.Clients.Wifi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nodes.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns fields for InfluxDB
|
|
||||||
func (stats *GlobalStats) Fields() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"nodes": stats.Nodes,
|
|
||||||
"clients.total": stats.Clients,
|
|
||||||
"clients.wifi": stats.ClientsWifi,
|
|
||||||
"clients.wifi24": stats.ClientsWifi24,
|
|
||||||
"clients.wifi5": stats.ClientsWifi5,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshals the input and writes it into the given file
|
// Marshals the input and writes it into the given file
|
||||||
func save(input interface{}, outputFile string) {
|
func save(input interface{}, outputFile string) {
|
||||||
tmpFile := outputFile + ".tmp"
|
tmpFile := outputFile + ".tmp"
|
||||||
|
|
|
@ -74,28 +74,25 @@ func TestUpdateNodes(t *testing.T) {
|
||||||
assert.Equal(1, len(nodes.List))
|
assert.Equal(1, len(nodes.List))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGlobalStats(t *testing.T) {
|
func TestToInflux(t *testing.T) {
|
||||||
stats := createTestNodes().GlobalStats()
|
|
||||||
|
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
assert.EqualValues(uint32(1), stats.Nodes)
|
|
||||||
assert.EqualValues(uint32(23), stats.Clients)
|
node := Node{
|
||||||
|
Statistics: &data.Statistics{
|
||||||
|
NodeId: "foobar",
|
||||||
|
LoadAverage: 0.5,
|
||||||
|
},
|
||||||
|
Nodeinfo: &data.NodeInfo{
|
||||||
|
Owner: &data.Owner{
|
||||||
|
Contact: "nobody",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Neighbours: &data.Neighbours{},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodesMini(t *testing.T) {
|
tags, fields := node.ToInflux()
|
||||||
mini := createTestNodes().GetNodesMini()
|
|
||||||
|
|
||||||
assert := assert.New(t)
|
assert.Equal("foobar", tags.GetString("nodeid"))
|
||||||
assert.Equal(1, len(mini.List))
|
assert.Equal("nobody", tags.GetString("owner"))
|
||||||
}
|
assert.Equal(0.5, fields["load"])
|
||||||
|
|
||||||
func createTestNodes() *Nodes {
|
|
||||||
nodes := NewNodes(&Config{})
|
|
||||||
|
|
||||||
res := &data.ResponseData{
|
|
||||||
Statistics: &data.Statistics{},
|
|
||||||
}
|
|
||||||
res.Statistics.Clients.Total = 23
|
|
||||||
nodes.Update("abcdef012345", res)
|
|
||||||
return nodes
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type CounterMap map[string]uint32
|
||||||
|
|
||||||
|
type GlobalStats struct {
|
||||||
|
Clients uint32
|
||||||
|
ClientsWifi uint32
|
||||||
|
ClientsWifi24 uint32
|
||||||
|
ClientsWifi5 uint32
|
||||||
|
Gateways uint32
|
||||||
|
Nodes uint32
|
||||||
|
|
||||||
|
Firmwares CounterMap
|
||||||
|
Models CounterMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns global statistics for InfluxDB
|
||||||
|
func NewGlobalStats(nodes *Nodes) (result *GlobalStats) {
|
||||||
|
result = &GlobalStats{
|
||||||
|
Firmwares: make(CounterMap),
|
||||||
|
Models: make(CounterMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.Lock()
|
||||||
|
for _, node := range nodes.List {
|
||||||
|
if node.Flags.Online {
|
||||||
|
result.Nodes += 1
|
||||||
|
if stats := node.Statistics; stats != nil {
|
||||||
|
result.Clients += stats.Clients.Total
|
||||||
|
result.ClientsWifi24 += stats.Clients.Wifi24
|
||||||
|
result.ClientsWifi5 += stats.Clients.Wifi5
|
||||||
|
result.ClientsWifi += stats.Clients.Wifi
|
||||||
|
}
|
||||||
|
if node.Flags.Gateway {
|
||||||
|
result.Gateways += 1
|
||||||
|
}
|
||||||
|
if info := node.Nodeinfo; info != nil {
|
||||||
|
result.Models.Increment(info.Hardware.Model)
|
||||||
|
result.Firmwares.Increment(info.Software.Firmware.Release)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodes.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment counter in the map by one
|
||||||
|
// if the value is not empty
|
||||||
|
func (m CounterMap) Increment(key string) {
|
||||||
|
if key != "" {
|
||||||
|
val := m[key]
|
||||||
|
m[key] = val + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns fields for InfluxDB
|
||||||
|
func (stats *GlobalStats) Fields() map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"nodes": stats.Nodes,
|
||||||
|
"gateways": stats.Gateways,
|
||||||
|
"clients.total": stats.Clients,
|
||||||
|
"clients.wifi": stats.ClientsWifi,
|
||||||
|
"clients.wifi24": stats.ClientsWifi24,
|
||||||
|
"clients.wifi5": stats.ClientsWifi5,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/FreifunkBremen/respond-collector/data"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGlobalStats(t *testing.T) {
|
||||||
|
stats := NewGlobalStats(createTestNodes())
|
||||||
|
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.EqualValues(1, stats.Gateways)
|
||||||
|
assert.EqualValues(3, stats.Nodes)
|
||||||
|
assert.EqualValues(25, stats.Clients)
|
||||||
|
|
||||||
|
// check models
|
||||||
|
assert.EqualValues(2, len(stats.Models))
|
||||||
|
assert.EqualValues(2, stats.Models["TP-Link 841"])
|
||||||
|
assert.EqualValues(1, stats.Models["Xeon Multi-Core"])
|
||||||
|
|
||||||
|
// check firmwares
|
||||||
|
assert.EqualValues(1, len(stats.Firmwares))
|
||||||
|
assert.EqualValues(1, stats.Firmwares["2016.1.6+entenhausen1"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodesMini(t *testing.T) {
|
||||||
|
mini := createTestNodes().GetNodesMini()
|
||||||
|
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.Equal(2, len(mini.List))
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestNodes() *Nodes {
|
||||||
|
nodes := NewNodes(&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
|
||||||
|
}
|
|
@ -56,6 +56,10 @@ func NewCollector(db *database.DB, nodes *models.Nodes, interval time.Duration,
|
||||||
go collector.receiver()
|
go collector.receiver()
|
||||||
go collector.parser()
|
go collector.parser()
|
||||||
|
|
||||||
|
if collector.db != nil {
|
||||||
|
go collector.globalStatsWorker()
|
||||||
|
}
|
||||||
|
|
||||||
// Run senders
|
// Run senders
|
||||||
go func() {
|
go func() {
|
||||||
collector.sendOnce() // immediately
|
collector.sendOnce() // immediately
|
||||||
|
@ -97,11 +101,6 @@ func (coll *Collector) sender() {
|
||||||
case <-coll.stop:
|
case <-coll.stop:
|
||||||
return
|
return
|
||||||
case <-coll.ticker.C:
|
case <-coll.ticker.C:
|
||||||
// save global statistics
|
|
||||||
if coll.db != nil {
|
|
||||||
coll.db.AddPoint(database.MeasurementGlobal, nil, coll.nodes.GlobalStats().Fields(), time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
// send the multicast packet to request per-node statistics
|
// send the multicast packet to request per-node statistics
|
||||||
coll.sendOnce()
|
coll.sendOnce()
|
||||||
}
|
}
|
||||||
|
@ -172,3 +171,24 @@ func (coll *Collector) receiver() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (coll *Collector) globalStatsWorker() {
|
||||||
|
ticker := time.NewTicker(time.Minute)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-coll.stop:
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
coll.saveGlobalStats()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// saves global statistics
|
||||||
|
func (coll *Collector) saveGlobalStats() {
|
||||||
|
stats := models.NewGlobalStats(coll.nodes)
|
||||||
|
|
||||||
|
coll.db.AddPoint(database.MeasurementGlobal, nil, stats.Fields(), time.Now())
|
||||||
|
coll.db.AddCounterMap(database.MeasurementFirmware, stats.Firmwares)
|
||||||
|
coll.db.AddCounterMap(database.MeasurementModel, stats.Models)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue