filter/search router_list
This commit is contained in:
parent
5b8609102a
commit
b5b4ff60fe
|
@ -6,6 +6,7 @@ client = MongoClient()
|
|||
db = client.freifunk
|
||||
|
||||
# create db indexes
|
||||
db.routers.create_index("hostname")
|
||||
db.routers.create_index("status")
|
||||
db.routers.create_index("last_contact")
|
||||
db.routers.create_index("netifs.mac")
|
||||
|
|
|
@ -8,6 +8,7 @@ from ffmap.web.api import api
|
|||
from ffmap.web.filters import filters
|
||||
from ffmap.dbtools import FreifunkDB
|
||||
from ffmap import stattools
|
||||
from ffmap.web.helpers import *
|
||||
|
||||
from flask import Flask, render_template, request, Response
|
||||
import bson
|
||||
|
@ -36,15 +37,8 @@ def router_map():
|
|||
|
||||
@app.route('/routers')
|
||||
def router_list():
|
||||
query = {}
|
||||
for allowed_filter in ('hostname', 'status', 'hood', 'user.nickname', 'hardware.name', 'netifs.mac'):
|
||||
if allowed_filter in request.args:
|
||||
query[allowed_filter] = request.args[allowed_filter]
|
||||
if allowed_filter == 'netifs.mac':
|
||||
query[allowed_filter] = query[allowed_filter].lower()
|
||||
if query[allowed_filter] == "EXISTS_NOT":
|
||||
query[allowed_filter] = {"$exists": False}
|
||||
return render_template("router_list.html", routers=db.routers.find(query, {
|
||||
query, query_str = parse_router_list_search_query(request.args)
|
||||
return render_template("router_list.html", query_str=query_str, routers=db.routers.find(query, {
|
||||
"hostname": 1,
|
||||
"status": 1,
|
||||
"hood": 1,
|
||||
|
@ -53,7 +47,7 @@ def router_list():
|
|||
"created": 1,
|
||||
"system.uptime": 1,
|
||||
"system.clients": 1,
|
||||
}))
|
||||
}).sort("hostname", pymongo.ASCENDING))
|
||||
|
||||
@app.route('/routers/<dbid>')
|
||||
def router_info(dbid):
|
||||
|
|
|
@ -35,6 +35,10 @@ def utc2local(dt):
|
|||
def format_dt(dt):
|
||||
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
@filters.app_template_filter('format_dt_date')
|
||||
def format_dt_date(dt):
|
||||
return dt.strftime("%Y-%m-%d")
|
||||
|
||||
@filters.app_template_filter('dt2jstimestamp')
|
||||
def dt2jstimestamp(dt):
|
||||
return int(dt.timestamp())*1000
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import bson
|
||||
|
||||
def format_query(query_usr):
|
||||
query_list = []
|
||||
for key, value in query_usr.items():
|
||||
if key == "hostname":
|
||||
qtag = ""
|
||||
else:
|
||||
qtag = "%s:" % key
|
||||
query_list.append("%s%s" % (qtag, value))
|
||||
return " ".join(query_list)
|
||||
|
||||
allowed_filters = ('status', 'hood', 'user.nickname', 'hardware.name', 'software.firmware', 'netifs.mac', 'hostname')
|
||||
def parse_router_list_search_query(args):
|
||||
query_usr = bson.SON()
|
||||
if "q" in args:
|
||||
for word in args["q"].strip().split(" "):
|
||||
if not ':' in word:
|
||||
key = "hostname"
|
||||
value = word
|
||||
else:
|
||||
key, value = word.split(':')
|
||||
if key in allowed_filters:
|
||||
query_usr[key] = query_usr.get(key, "") + value
|
||||
query = {}
|
||||
for key, value in query_usr.items():
|
||||
if value == "EXISTS":
|
||||
query[key] = {"$exists": True}
|
||||
elif value == "EXISTS_NOT":
|
||||
query[key] = {"$exists": False}
|
||||
elif key == 'netifs.mac':
|
||||
query[key] = value.lower()
|
||||
elif key == 'hostname':
|
||||
query[key] = {"$regex": value, "$options": 'i'}
|
||||
else:
|
||||
query[key] = value
|
||||
return (query, format_query(query_usr))
|
|
@ -308,10 +308,15 @@ function global_router_firmwares_graph() {
|
|||
}
|
||||
var plot = $.plot(placeholder, pdata, {
|
||||
legend: {noColumns: 1, show: true, "labelFormatter": legendFormatter},
|
||||
grid: {hoverable: true, clickable: false},
|
||||
grid: {hoverable: true, clickable: true},
|
||||
tooltip: {show: true, content: "<b>%s</b>: %p.0%", shifts: {x: 15, y: 5}, defaultTheme: true},
|
||||
series: {pie: {show: true, radius: 99/100, label: {show: true, formatter: labelFormatter, radius: 0.5, threshold: 0.10}}}
|
||||
});
|
||||
placeholder.bind("plotclick", function(event, pos, obj) {
|
||||
if (obj) {
|
||||
window.location.href = routers_page_url + "?q=software.firmware:" + obj.series.label;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function global_router_models_graph() {
|
||||
|
@ -324,9 +329,14 @@ function global_router_models_graph() {
|
|||
});
|
||||
}
|
||||
var plot = $.plot(placeholder, pdata, {
|
||||
legend: {noColumns: 2, show: true, "labelFormatter": legendFormatter},
|
||||
grid: {hoverable: true, clickable: false},
|
||||
legend: {noColumns: 1, show: true, "labelFormatter": legendFormatter},
|
||||
grid: {hoverable: true, clickable: true},
|
||||
tooltip: {show: true, content: "<b>%s</b>: %p.0%", shifts: {x: 15, y: 5}, defaultTheme: true},
|
||||
series: {pie: {show: true, radius: 99/100, label: {show: true, formatter: labelFormatter, radius: 0.5, threshold: 0.2}}}
|
||||
});
|
||||
placeholder.bind("plotclick", function(event, pos, obj) {
|
||||
if (obj) {
|
||||
window.location.href = routers_page_url + "?q=hardware.name:" + obj.series.label;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
{% block head %}
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="{% block viewport %}width=device-width, initial-scale=1{% endblock %}">
|
||||
<meta name="viewport" content="{% block viewport %}width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no{% endblock %}">
|
||||
|
||||
<title>{% block title %}FF-Map{% endblock %}</title>
|
||||
|
||||
|
@ -40,6 +40,8 @@
|
|||
<li class="{{ "active" if request.endpoint in fkt }}"><a href="{{ url_for(fkt[0]) }}">{{ text }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{%- block search %}
|
||||
{%- endblock %}
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{% extends "bootstrap.html" %}
|
||||
{% block viewport %}{{super()}}, maximum-scale=1.0, user-scalable=no{% endblock %}
|
||||
{% block title %}{{super()}} Map{% endblock %}
|
||||
{% block head %}{{super()}}
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.css" />
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{% extends "bootstrap.html" %}
|
||||
{% block viewport %}{{super()}}, maximum-scale=1.0, user-scalable=no{% endblock %}
|
||||
{% block title %}{{super()}} :: {{ router.hostname }}{% endblock %}
|
||||
{% block head %}{{super()}}
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.css" />
|
||||
|
@ -98,7 +97,7 @@
|
|||
<tr><th>Hood</th><td>{{ router.hood }}</td></tr>
|
||||
{%- endif %}
|
||||
{%- if router.user %}
|
||||
<tr><th>User</th><td><a href="{{ url_for('router_list') }}?user.nickname={{ router.user.nickname }}">{{ router.user.nickname }}</a></td></tr>
|
||||
<tr><th>User</th><td><a href="{{ url_for('router_list') }}?q=user.nickname:{{ router.user.nickname }}">{{ router.user.nickname }}</a></td></tr>
|
||||
{%- endif %}
|
||||
<tr><th>Hardware</th><td>{{ router.hardware.name }} ({{ router.hardware.chipset }})</td></tr>
|
||||
<tr><th>WAN Uplink</th><td><span class="{{ "glyphicon glyphicon-ok" if router.system.has_wan_uplink else "glyphicon glyphicon-remove" }}"></span></td></tr>
|
||||
|
@ -271,7 +270,7 @@
|
|||
</div>
|
||||
<script type="text/javascript">
|
||||
var router_stats = {{ router.stats|statbson2json|safe }};
|
||||
$(function() {
|
||||
$(document).ready(function() {
|
||||
memory_graph();
|
||||
process_graph();
|
||||
client_graph();
|
||||
|
|
|
@ -1,31 +1,79 @@
|
|||
{% extends "bootstrap.html" %}
|
||||
{% block title %}{{super()}} Routers{% endblock %}
|
||||
{% block head %}{{super()}}
|
||||
<script src="https://cdn.datatables.net/1.10.10/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.10.10/js/dataTables.bootstrap.min.js"></script>
|
||||
<!--<script src="https://cdn.datatables.net/responsive/2.0.0/js/dataTables.responsive.min.js"></script>-->
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.10/css/dataTables.bootstrap.min.css">
|
||||
<!--<link rel="stylesheet" href="https://cdn.datatables.net/responsive/2.0.0/css/responsive.dataTables.min.css">-->
|
||||
<style type="text/css">
|
||||
@media(min-width:991px) {
|
||||
.text-nowrap-responsive {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{%- block search %}
|
||||
<form class="navbar-form navbar-right" role="search">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="Search routers" name="q" value="{{ query_str }}">
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-search"></span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
{%- endblock %}
|
||||
{% block content %}
|
||||
<table class="table table-condensed table-striped table-bordered" style="margin-bottom: 8px;">
|
||||
<tr>
|
||||
<th style="width: 1%;">Hostname</th>
|
||||
<th style="width: 45px;">Status</th>
|
||||
<th>Hood</th>
|
||||
<th>User</th>
|
||||
<th>Hardware</th>
|
||||
<th>Created</th>
|
||||
<th>Uptime</th>
|
||||
<th>Clients</th>
|
||||
</tr>
|
||||
{%- for router in routers %}
|
||||
<tr>
|
||||
<td class="text-nowrap"><a href="{{ url_for("router_info", dbid=router._id) }}">{{ router.hostname }}</a></td>
|
||||
<td class="text-center"><span class="{{ router.status|status2css }}">{{ router.status }}</span></td>
|
||||
<td>{{ router.hood }}</td>
|
||||
<td>{{ router.user.nickname if router.user }}</td>
|
||||
<td>{{ router.get("hardware", {}).get("name", "") }}</td>
|
||||
<td>{{ router.created|utc2local|format_dt }}</td>
|
||||
<td>{{ router.system.uptime|format_ts_diff }}</td>
|
||||
<td>{{ router.system.clients }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
<div class="table-responsive">
|
||||
<table id="routerlist" class="table table-condensed table-striped table-bordered" style="margin-bottom: 8px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 1%; padding-right: 5px; min-width: 90px;">Hostname</th>
|
||||
<th style="width: 45px; padding-right: 5px;">Status</th>
|
||||
<th style="padding-right: 5px;">Hood</th>
|
||||
<th style="padding-right: 5px;">User</th>
|
||||
<th style="padding-right: 5px;">Hardware</th>
|
||||
<th style="padding-right: 5px;">Created</th>
|
||||
<th style="padding-right: 5px;">Uptime</th>
|
||||
<th>Clients</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- for router in routers %}
|
||||
<tr>
|
||||
<td class="text-nowrap-responsive"><a href="{{ url_for("router_info", dbid=router._id) }}">{{ router.hostname }}</a></td>
|
||||
<td class="text-center"><span class="{{ router.status|status2css }}">{{ router.status }}</span></td>
|
||||
<td>{{ router.hood }}</td>
|
||||
<td>{{ router.user.nickname if router.user }}</td>
|
||||
<td class="text-nowrap">{{ router.get("hardware", {}).get("name", "") }}</td>
|
||||
<td class="text-nowrap">{{ router.created|utc2local|format_dt_date }}</td>
|
||||
<td class="text-nowrap">{{ router.system.uptime|format_ts_diff }}</td>
|
||||
<td>{{ router.system.clients }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="margin-bottom: 20px;">
|
||||
{{ routers.count() }} Router{{ "s" if (routers.count() > 1) else "" }} found.
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#routerlist").DataTable({
|
||||
"paging": false,
|
||||
"info": false,
|
||||
"searching": false,
|
||||
/*"responsive": {
|
||||
"details": false
|
||||
},*/
|
||||
"columnDefs": [
|
||||
{"orderable": false, "targets": 1},
|
||||
{"orderable": false, "targets": -2},
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{% extends "bootstrap.html" %}
|
||||
{% block viewport %}{{super()}}, maximum-scale=1.0, user-scalable=no{% endblock %}
|
||||
{% block title %}{{super()}} Statistics{% endblock %}
|
||||
{% block head %}{{super()}}
|
||||
<script src="{{ url_for('static', filename='js/graph/date.js') }}"></script>
|
||||
|
@ -52,13 +51,13 @@
|
|||
<th class="active" title="Total Routers">Total</th>
|
||||
<th class="info">Clients</th>
|
||||
</tr>
|
||||
{%- for hood in hoods %}
|
||||
{%- for hood, value in hoods|dictsort %}
|
||||
<tr>
|
||||
<td>{{ hood }}</td>
|
||||
<td class="success">{{ hoods[hood]["online"] or 0 }}</td>
|
||||
<td class="danger">{{ hoods[hood]["offline"] or 0 }}</td>
|
||||
<td class="warning">{{ hoods[hood]["unknown"] or 0 }}</td>
|
||||
<td class="active">{{ hoods_sum[hood]["routers"] or 0 }}</td>
|
||||
<td class="success">{{ value["online"] or 0 }}</td>
|
||||
<td class="danger">{{ value["offline"] or 0 }}</td>
|
||||
<td class="warning">{{ value["unknown"] or 0 }}</td>
|
||||
<td class="active">{{ value["routers"] or 0 }}</td>
|
||||
<td class="info">{{ hoods_sum[hood]["clients"] or 0 }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
|
@ -134,9 +133,10 @@
|
|||
</div>
|
||||
<script type="text/javascript">
|
||||
var global_stats = {{ stats|statbson2json|safe }};
|
||||
var router_firmwares = {{ router_firmwares|tojson }}
|
||||
var router_models = {{ router_models|tojson }}
|
||||
$(function() {
|
||||
var router_firmwares = {{ router_firmwares|tojson }};
|
||||
var router_models = {{ router_models|tojson }};
|
||||
var routers_page_url = "{{ url_for('router_list') }}";
|
||||
$(document).ready(function() {
|
||||
global_client_graph();
|
||||
global_router_graph();
|
||||
global_router_firmwares_graph();
|
||||
|
|
Loading…
Reference in New Issue