add statistics
This commit is contained in:
parent
88904b184d
commit
f495283208
|
@ -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"],
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
Loading…
Reference in New Issue