add statistics

This commit is contained in:
Dominik Heidler 2015-11-03 23:55:33 +01:00
parent 88904b184d
commit f495283208
3 changed files with 369 additions and 47 deletions

View File

@ -109,14 +109,19 @@ def detect_offline_routers():
def new_router_stats(router, router_update):
if router["system"]["uptime"] < router_update["system"]["uptime"]:
netifs = {}
neighbours = {}
for netif in router_update["netifs"]:
# sanitize name
name = netif["name"].replace(".", "").replace("$", "")
with suppress(KeyError):
netifs[name] = {"rx": netif["traffic"]["rx"], "tx": netif["traffic"]["tx"]}
for neighbour in router_update["neighbours"]:
with suppress(KeyError):
neighbours[neighbour["mac"]] = neighbour["quality"]
return [{
"time": datetime.datetime.utcnow(),
"netifs": netifs,
"neighbours": neighbours,
"memory": router_update["system"]["memory"],
"processes": router_update["system"]["processes"],
"clients": router_update["system"]["clients"],

View File

@ -1,5 +1,6 @@
var points_per_px = 0.3;
function network_graph(netif) {
var points_per_px = 0.3;
var netstat = $("#netstat");
var tx = [], rx = [];
var len, i;
@ -40,7 +41,7 @@ function network_graph(netif) {
plot.clearSelection();
netstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("right", (plot.getPlotOffset().right+42) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "block");
});
var netstat_controls = $("<div style='right:60px;top:13px;position:absolute;display:none;' id='controls'></div>").appendTo(netstat);
@ -59,10 +60,283 @@ function network_graph(netif) {
plot.draw();
netstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("right", (plot.getPlotOffset().right+42) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "none");
});
netstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("right", (plot.getPlotOffset().right+42) + "px");
.css("left", (plot.getPlotOffset().left+5) + "px");
}
function neighbour_graph(neighbours) {
var meshstat = $("#meshstat");
var pdata = [];
for (j=0; j<neighbours.length; j++) {
var label = neighbours[j].name;
var mac = neighbours[j].mac;
var data = [];
var len, i;
for (len=router_stats.length, i=0; i<len; i++) {
try {
var quality = router_stats[i].neighbours[mac];
var date_value = router_stats[i].time.$date;
if(quality != null) {
data.push([date_value, quality]);
}
}
catch(TypeError) {
// pass
}
}
pdata.push({"label": label, "data": data});
}
var plot = $.plot(meshstat, pdata, {
xaxis: {mode: "time", timezone: "browser"},
selection: {mode: "x"},
yaxis: {min: 0, max: 400},
series: {downsample: {threshold: Math.floor(meshstat.width() * points_per_px)}}
});
meshstat.bind("plotselected", function (event, ranges) {
$.each(plot.getXAxes(), function(_, axis) {
var zoom_correction_factor = (1000*300*len)/(ranges.xaxis.to - ranges.xaxis.from);
plot.getOptions().series.downsample.threshold = Math.floor(meshstat.width() * points_per_px * zoom_correction_factor);
axis.options.min = ranges.xaxis.from;
axis.options.max = ranges.xaxis.to;
});
plot.setData(pdata);
plot.setupGrid();
plot.draw();
plot.clearSelection();
meshstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "block");
});
var meshstat_controls = $("<div style='right:60px;top:13px;position:absolute;display:none;' id='controls'></div>").appendTo(meshstat);
$("<div class='btn btn-default btn-xs'>Reset</div>")
.appendTo(meshstat_controls)
.click(function (event) {
event.preventDefault();
console.log("button");
$.each(plot.getXAxes(), function(_, axis) {
axis.options.min = null;
axis.options.max = null;
});
plot.getOptions().series.downsample.threshold = Math.floor(meshstat.width() * points_per_px);
plot.setData(pdata);
plot.setupGrid();
plot.draw();
meshstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "none");
});
meshstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px");
}
function memory_graph() {
var memstat = $("#memstat");
var free = [], caching = [], buffering = [];
var len, i;
for (len=router_stats.length, i=0; i<len; i++) {
try {
var free_value = router_stats[i].memory.free*1024;
var caching_value = router_stats[i].memory.caching*1024;
var buffering_value = router_stats[i].memory.buffering*1024;
var date_value = router_stats[i].time.$date;
if(free_value != null && caching_value != null && buffering_value != null) {
free.push([date_value, free_value]);
caching.push([date_value, caching_value]);
buffering.push([date_value, buffering_value]);
}
}
catch(TypeError) {
// pass
}
}
var pdata = [
{"label": "free", "data": free, "color": "#4DA74A"},
{"label": "caching", "data": caching, "color": "#EDC240"},
{"label": "buffering", "data": buffering, "color": "#8CACC6"}
];
var plot = $.plot(memstat, pdata, {
xaxis: {mode: "time", timezone: "browser"},
selection: {mode: "x"},
yaxis: {min: 0, mode: "byte"},
series: {downsample: {threshold: Math.floor(memstat.width() * points_per_px)}}
});
memstat.bind("plotselected", function (event, ranges) {
$.each(plot.getXAxes(), function(_, axis) {
var zoom_correction_factor = (1000*300*len)/(ranges.xaxis.to - ranges.xaxis.from);
plot.getOptions().series.downsample.threshold = Math.floor(memstat.width() * points_per_px * zoom_correction_factor);
axis.options.min = ranges.xaxis.from;
axis.options.max = ranges.xaxis.to;
});
plot.setData(pdata);
plot.setupGrid();
plot.draw();
plot.clearSelection();
memstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "block");
});
var memstat_controls = $("<div style='right:60px;top:13px;position:absolute;display:none;' id='controls'></div>").appendTo(memstat);
$("<div class='btn btn-default btn-xs'>Reset</div>")
.appendTo(memstat_controls)
.click(function (event) {
event.preventDefault();
console.log("button");
$.each(plot.getXAxes(), function(_, axis) {
axis.options.min = null;
axis.options.max = null;
});
plot.getOptions().series.downsample.threshold = Math.floor(memstat.width() * points_per_px);
plot.setData(pdata);
plot.setupGrid();
plot.draw();
memstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "none");
});
memstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px");
}
function process_graph() {
var procstat = $("#procstat");
var runnable = [], total = [];
var len, i;
for (len=router_stats.length, i=0; i<len; i++) {
try {
var runnable_value = router_stats[i].processes.runnable;
var total_value = router_stats[i].processes.total;
var date_value = router_stats[i].time.$date;
if(runnable_value != null && total_value != null) {
runnable.push([date_value, runnable_value]);
total.push([date_value, total_value]);
}
}
catch(TypeError) {
// pass
}
}
var pdata = [
{"label": "runnable", "data": runnable, "color": "#CB4B4B"},
{"label": "total", "data": total, "color": "#EDC240"},
];
var plot = $.plot(procstat, pdata, {
xaxis: {mode: "time", timezone: "browser"},
selection: {mode: "x"},
yaxis: {min: 0, max: 50},
series: {downsample: {threshold: Math.floor(procstat.width() * points_per_px)}}
});
procstat.bind("plotselected", function (event, ranges) {
$.each(plot.getXAxes(), function(_, axis) {
var zoom_correction_factor = (1000*300*len)/(ranges.xaxis.to - ranges.xaxis.from);
plot.getOptions().series.downsample.threshold = Math.floor(procstat.width() * points_per_px * zoom_correction_factor);
axis.options.min = ranges.xaxis.from;
axis.options.max = ranges.xaxis.to;
});
plot.setData(pdata);
plot.setupGrid();
plot.draw();
plot.clearSelection();
procstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "block");
});
var procstat_controls = $("<div style='right:60px;top:13px;position:absolute;display:none;' id='controls'></div>").appendTo(procstat);
$("<div class='btn btn-default btn-xs'>Reset</div>")
.appendTo(procstat_controls)
.click(function (event) {
event.preventDefault();
console.log("button");
$.each(plot.getXAxes(), function(_, axis) {
axis.options.min = null;
axis.options.max = null;
});
plot.getOptions().series.downsample.threshold = Math.floor(procstat.width() * points_per_px);
plot.setData(pdata);
plot.setupGrid();
plot.draw();
procstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "none");
});
procstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px");
}
function client_graph() {
var clientstat = $("#clientstat");
var clients = [];
var len, i;
for (len=router_stats.length, i=0; i<len; i++) {
try {
var client_value = router_stats[i].clients;
var date_value = router_stats[i].time.$date;
if(client_value != null) {
clients.push([date_value, client_value]);
}
}
catch(TypeError) {
// pass
}
}
var pdata = [
{"label": "clients", "data": clients, "color": "#8CACC6", lines: {fill: true}}
];
console.log(pdata);
var plot = $.plot(clientstat, pdata, {
xaxis: {mode: "time", timezone: "browser"},
selection: {mode: "x"},
yaxis: {min: 0},
series: {downsample: {threshold: Math.floor(clientstat.width() * points_per_px)}}
});
clientstat.bind("plotselected", function (event, ranges) {
$.each(plot.getXAxes(), function(_, axis) {
var zoom_correction_factor = (1000*300*len)/(ranges.xaxis.to - ranges.xaxis.from);
plot.getOptions().series.downsample.threshold = Math.floor(clientstat.width() * points_per_px * zoom_correction_factor);
axis.options.min = ranges.xaxis.from;
axis.options.max = ranges.xaxis.to;
});
plot.setData(pdata);
plot.setupGrid();
plot.draw();
plot.clearSelection();
clientstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "block");
});
var clientstat_controls = $("<div style='right:60px;top:13px;position:absolute;display:none;' id='controls'></div>").appendTo(clientstat);
$("<div class='btn btn-default btn-xs'>Reset</div>")
.appendTo(clientstat_controls)
.click(function (event) {
event.preventDefault();
console.log("button");
$.each(plot.getXAxes(), function(_, axis) {
axis.options.min = null;
axis.options.max = null;
});
plot.getOptions().series.downsample.threshold = Math.floor(clientstat.width() * points_per_px);
plot.setData(pdata);
plot.setupGrid();
plot.draw();
clientstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px")
.css("display", "none");
});
clientstat.children("#controls")
.css("top", (plot.getPlotOffset().top+5) + "px")
.css("left", (plot.getPlotOffset().left+5) + "px");
}

View File

@ -64,47 +64,6 @@
</div>
</div>
</div>
<div class="col-xs-12 col-md-6" style="height: 422px; display: flex; flex-flow: column;">
{%- if router.neighbours|length > 0 %}
<div class="panel panel-default" style="flex: 0 1 auto;">
<div class="panel-heading">Neighbours</div>
<div class="panel-body">
<table class="neighbours" style="width: 100%;">
<tr>
<th>Hostname</th>
<th>MAC Address</th>
<th>Quality</th>
<th>Interface</th>
</tr>
{%- for neighbour in router.neighbours %}
<tr style="background-color: {{ neighbour.quality|neighbour_color }};">
<td><a href="{{ url_for('router_info', dbid=neighbour._id) }}">{{ neighbour.hostname }}</a></td>
<td>{{ neighbour.mac }}</td>
<td>{{ neighbour.quality }}</td>
<td>{{ neighbour.net_if }}</td>
</tr>
{%- endfor %}
</table>
</div>
</div>
{%- endif %}
<div class="panel panel-default" style="flex: 1 1 auto;">
<div class="panel-heading">Events</div>
<div class="panel-body">
<table class="table table-condensed">
{%- for event in router.events[-5:] %}
<tr>
<td style="width: 11em;">{{ event.time|utc2local|format_dt|nbsp|safe }}</td>
<td style="width: 1em;"><span class="{{ event.type|status2css }}">{{ event.type }}</span></td>
<td>{{ event.comment }}</td>
</tr>
{%- endfor %}
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-6">
<div class="panel panel-default">
<div class="panel-heading">System</div>
@ -129,12 +88,15 @@
<tr><th>User</th><td>{{ router.user.nickname }}</td></tr>
{%- endif %}
<tr><th>Hardware</th><td>{{ router.hardware.name }} ({{ router.hardware.chipset }})</td></tr>
<tr><th>Clients</th><td>{{ router.system.clients }}</td></tr>
</table>
</div>
</div>
</div>
</div>
<div class="row" style="display: flex;">
{%- if router.software is defined %}
<div class="col-xs-12 col-md-6">
<div class="col-xs-12 col-md-6" style="display: flex; flex-flow: column;">
<div class="panel panel-default">
<div class="panel-heading">Software</div>
<div class="panel-body">
@ -147,6 +109,68 @@
</table>
</div>
</div>
{%- if not router.neighbours|length > 0 %}
</div>
<div class="col-xs-12 col-md-6">
{%- endif %}
{%- else %}
<div class="col-xs-12 col-md-6">
{%- endif %}
<div class="panel panel-default" style="flex: 1 1 auto;">
<div class="panel-heading">Events</div>
<div class="panel-body">
<table class="table table-condensed">
{%- for event in router.events[-5:] %}
<tr>
<td style="width: 11em;">{{ event.time|utc2local|format_dt|nbsp|safe }}</td>
<td style="width: 1em;"><span class="{{ event.type|status2css }}">{{ event.type }}</span></td>
<td>{{ event.comment }}</td>
</tr>
{%- endfor %}
</table>
</div>
</div>
</div>
{%- if router.neighbours|length > 0 %}
<div class="col-xs-12 col-md-6" style="display: flex; flex-flow: column;">
<div class="panel panel-default" style="flex: 1 1 auto;">
<div class="panel-heading">Neighbours</div>
<div class="panel-body" style="height: 100%;">
<table class="neighbours" style="width: 100%; margin-bottom: 6px;">
<tr>
<th>Hostname</th>
<th>MAC Address</th>
<th>Quality</th>
<th>Interface</th>
</tr>
{%- for neighbour in router.neighbours %}
<tr style="background-color: {{ neighbour.quality|neighbour_color }};">
<td><a href="{{ url_for('router_info', dbid=neighbour._id) }}">{{ neighbour.hostname }}</a></td>
<td>{{ neighbour.mac }}</td>
<td>{{ neighbour.quality }}</td>
<td>{{ neighbour.net_if }}</td>
</tr>
{%- endfor %}
</table>
{# hack for graph vertical align #}
{%- if router.neighbours|length < 3 %}
{%- for n in range(3- router.neighbours|length) %}
<br />
{%- endfor %}
{%- endif %}
<div id="meshstat" class="graph" style="height: 300px;"></div>
<script type="text/javascript">
var neighbours = [
{%- for neighbour in router.neighbours %}
{"name": "{{ neighbour.hostname or neighbour.mac }}", "mac": "{{ neighbour.mac }}"},
{%- endfor %}
];
$(function() {
neighbour_graph(neighbours);
});
</script>
</div>
</div>
</div>
{%- endif %}
</div>
@ -208,7 +232,6 @@
</ul>
</div>
<script type="text/javascript">
var router_stats = {{ router.stats|bson2json|safe }};
$(function() {
$("#netif-list li").on("click", function() {
$("#netif-list li").removeClass("active");
@ -220,5 +243,25 @@
});
</script>
</div>
<div class="col-xs-12 col-md-6">
<div class="panel panel-default">
<div class="panel-heading">Statistics</div>
<div class="panel-body">
<div id="memstat" class="graph"></div>
<div id="procstat" class="graph"></div>
<div id="clientstat" class="graph"></div>
</div>
</div>
<script type="text/javascript">
$(function() {
memory_graph();
process_graph();
client_graph();
});
</script>
</div>
</div>
<script type="text/javascript">
var router_stats = {{ router.stats|bson2json|safe }};
</script>
{% endblock %}