gateways: Add new page with gateway data (IPs, DHCP ranges)
Shows all interfaces without checking vpnif. Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de>
This commit is contained in:
parent
4d638c3744
commit
4c2b4f1628
|
@ -248,7 +248,82 @@ def hoods_gws(mysql):
|
|||
result[rs["hood"]] = rs["count"]
|
||||
return result
|
||||
|
||||
def gws(mysql,selecthood=None):
|
||||
def gateways(mysql):
|
||||
macs = mysql.fetchall("""
|
||||
SELECT router_gw.mac, gw.name, gw.id AS gw, gw_netif.netif
|
||||
FROM router
|
||||
INNER JOIN router_gw ON router.id = router_gw.router
|
||||
LEFT JOIN (gw_netif INNER JOIN gw ON gw_netif.gw = gw.id)
|
||||
ON router_gw.mac = gw_netif.mac
|
||||
WHERE router.status <> 'orphaned' AND NOT ISNULL(gw.name)
|
||||
GROUP BY router_gw.mac
|
||||
ORDER BY gw.name ASC, gw_netif.netif ASC, router_gw.mac ASC
|
||||
""")
|
||||
selected = mysql.fetchall("""
|
||||
SELECT gw_netif.gw, router.status, COUNT(router_gw.router) AS count
|
||||
FROM router
|
||||
INNER JOIN router_gw ON router.id = router_gw.router
|
||||
INNER JOIN gw_netif ON gw_netif.mac = router_gw.mac
|
||||
WHERE router_gw.selected = TRUE AND router.status <> 'orphaned'
|
||||
GROUP BY gw_netif.gw, router.status
|
||||
""")
|
||||
others = mysql.fetchall("""
|
||||
SELECT gw_netif.gw, router.status, COUNT(router_gw.router) AS count
|
||||
FROM router
|
||||
INNER JOIN router_gw ON router.id = router_gw.router
|
||||
INNER JOIN gw_netif ON gw_netif.mac = router_gw.mac
|
||||
WHERE router_gw.selected = FALSE AND router.status <> 'orphaned'
|
||||
GROUP BY gw_netif.gw, router.status
|
||||
""")
|
||||
|
||||
result = OrderedDict()
|
||||
for m in macs:
|
||||
if not m["gw"] in result:
|
||||
result[m["gw"]] = {"name":m["name"],"macs":[],"selected":{},"others":{}}
|
||||
result[m["gw"]]["macs"].append(m["mac"])
|
||||
for rs in selected:
|
||||
result[rs["gw"]]["selected"][rs["status"]] = rs["count"]
|
||||
for rs in others:
|
||||
result[rs["gw"]]["others"][rs["status"]] = rs["count"]
|
||||
return result
|
||||
|
||||
def gws_ipv4(mysql):
|
||||
data = mysql.fetchall("""
|
||||
SELECT name, n1.ipv4, n1.netif AS batif, n2.netif AS vpnif, n2.mac FROM gw
|
||||
INNER JOIN gw_netif AS n1 ON gw.id = n1.gw
|
||||
LEFT JOIN gw_netif AS n2 ON n2.mac = n1.vpnmac AND n1.gw = n2.gw
|
||||
WHERE n1.ipv4 IS NOT NULL
|
||||
GROUP BY name, n1.ipv4, n1.netif, n2.netif, n2.mac
|
||||
ORDER BY n1.ipv4
|
||||
""")
|
||||
|
||||
return data
|
||||
|
||||
def gws_ipv6(mysql):
|
||||
data = mysql.fetchall("""
|
||||
SELECT name, n1.ipv6, n1.netif AS batif, n2.netif AS vpnif, n2.mac FROM gw
|
||||
INNER JOIN gw_netif AS n1 ON gw.id = n1.gw
|
||||
LEFT JOIN gw_netif AS n2 ON n2.mac = n1.vpnmac AND n1.gw = n2.gw
|
||||
WHERE n1.ipv6 IS NOT NULL
|
||||
GROUP BY name, n1.ipv6, n1.netif, n2.netif, n2.mac
|
||||
ORDER BY n1.ipv6
|
||||
""")
|
||||
|
||||
return data
|
||||
|
||||
def gws_dhcp(mysql):
|
||||
data = mysql.fetchall("""
|
||||
SELECT name, n1.dhcpstart, n1.dhcpend, n1.netif AS batif, n2.netif AS vpnif, n2.mac FROM gw
|
||||
INNER JOIN gw_netif AS n1 ON gw.id = n1.gw
|
||||
LEFT JOIN gw_netif AS n2 ON n2.mac = n1.vpnmac AND n1.gw = n2.gw
|
||||
WHERE n1.dhcpstart IS NOT NULL
|
||||
GROUP BY name, n1.dhcpstart, n1.dhcpend, n1.netif, n2.netif, n2.mac
|
||||
ORDER BY n1.dhcpstart
|
||||
""")
|
||||
|
||||
return data
|
||||
|
||||
def gws_ifs(mysql,selecthood=None):
|
||||
if selecthood:
|
||||
where = " AND hood=%s"
|
||||
tup = (selecthood,)
|
||||
|
|
|
@ -501,7 +501,7 @@ def global_gwstatistics(selectgw):
|
|||
def helper_statistics(mysql,stats,selecthood,selectgw):
|
||||
try:
|
||||
hoods = stattools.hoods(mysql,selectgw)
|
||||
gws = stattools.gws(mysql,selecthood)
|
||||
gws = stattools.gws_ifs(mysql,selecthood)
|
||||
|
||||
if selecthood:
|
||||
selecthoodname = mysql.findone("SELECT name FROM hoods WHERE id = %s",(selecthood,),'name')
|
||||
|
@ -588,6 +588,27 @@ def helper_statistics(mysql,stats,selecthood,selectgw):
|
|||
import traceback
|
||||
writefulllog("Warning: Failed to display stats page: %s\n__%s" % (e, traceback.format_exc().replace("\n", "\n__")))
|
||||
|
||||
@app.route('/gateways')
|
||||
def gateways():
|
||||
try:
|
||||
mysql = FreifunkMySQL()
|
||||
gws = stattools.gateways(mysql)
|
||||
ipv4 = stattools.gws_ipv4(mysql)
|
||||
ipv6 = stattools.gws_ipv6(mysql)
|
||||
dhcp = stattools.gws_dhcp(mysql)
|
||||
mysql.close()
|
||||
|
||||
return render_template("gws.html",
|
||||
gws = gws,
|
||||
ipv4 = ipv4,
|
||||
ipv6 = ipv6,
|
||||
dhcp = dhcp
|
||||
)
|
||||
except Exception as e:
|
||||
writelog(CONFIG["debug_dir"] + "/fail_gateways.txt", str(e))
|
||||
import traceback
|
||||
writefulllog("Warning: Failed to display gateways page: %s\n__%s" % (e, traceback.format_exc().replace("\n", "\n__")))
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
|
|
|
@ -10,6 +10,7 @@ import datetime
|
|||
import re
|
||||
import hashlib
|
||||
from ffmap.misc import int2mac, int2shortmac, inttoipv4, bintoipv6
|
||||
from ipaddress import ip_address
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + '/' + '../..'))
|
||||
from ffmap.misc import *
|
||||
|
@ -20,6 +21,13 @@ filters = Blueprint("filters", __name__)
|
|||
def sumdict(d):
|
||||
return sum(d.values())
|
||||
|
||||
@filters.app_template_filter('longip')
|
||||
def longip(d):
|
||||
if len(d) > 32:
|
||||
return d.replace('::','::... ...::')
|
||||
else:
|
||||
return d
|
||||
|
||||
@filters.app_template_filter('int2mac')
|
||||
def int2macfilter(d):
|
||||
return int2mac(d)
|
||||
|
@ -36,6 +44,14 @@ def int2ipv4filter(d):
|
|||
def bin2ipv6filter(d):
|
||||
return bintoipv6(d)
|
||||
|
||||
@filters.app_template_filter('ip2int')
|
||||
def ip2intfilter(d):
|
||||
return int(ip_address(d))
|
||||
|
||||
@filters.app_template_filter('ipnet2int')
|
||||
def ipnet2intfilter(d):
|
||||
return int(ip_address(d.split("/")[0]))
|
||||
|
||||
@filters.app_template_filter('utc2local')
|
||||
def utc2local(dt):
|
||||
return dt.astimezone(tz.tzlocal())
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
(["router_list", "router_info"], "Routers"),
|
||||
(["user_list", "user_info"], "Users"),
|
||||
(["global_statistics"], "Statistics"),
|
||||
(["gateways"], "GWs"),
|
||||
(["apidoc"], "API"),
|
||||
] %}
|
||||
<li class="{{ "active" if request.endpoint in fkt }}"><a href="{{ url_for(fkt[0]) }}">{{ text }}</a></li>
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
{% extends "bootstrap.html" %}
|
||||
{% block title %}{{super()}} :: Gateways{% endblock %}
|
||||
{% block head %}{{super()}}
|
||||
<script src="{{ url_for('static', filename='js/graph/date.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph/jquery.flot.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph/jquery.flot.time.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph/jquery.flot.byte.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph/jquery.flot.selection.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph/jquery.flot.downsample.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph/jquery.flot.resize.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph/jquery.flot.hiddengraphs.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph/jquery.flot.pie.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph/jquery.flot.tooltip.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/graph.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/datatables/jquery.dataTables.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/datatables/dataTables.bootstrap.min.js') }}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/datatables/dataTables.bootstrap.min.css') }}">
|
||||
<style type="text/css">
|
||||
.table-condensed {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.table-condensed tr:last-child td, th {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
@media(max-width:500px) {
|
||||
th {
|
||||
padding-left: 2px !important;
|
||||
padding-right: 2px !important;
|
||||
}
|
||||
td {
|
||||
padding-left: 2px !important;
|
||||
padding-right: 2px !important;
|
||||
}
|
||||
.panel-body {
|
||||
padding-left: 3px !important;
|
||||
padding-right: 3px !important;
|
||||
}
|
||||
}
|
||||
.table-hoods th {
|
||||
text-align: center;
|
||||
}
|
||||
.table-hoods td {
|
||||
text-align: center;
|
||||
}
|
||||
.table-hoods .firstrow {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Gateways (selected / others)</div>
|
||||
<div class="panel-body">
|
||||
<table id="gwlist" class="table table-condensed table-hoods">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="firstrow">Gateway</th>
|
||||
<th class="success" title="Online Routers">On</th>
|
||||
<th class="danger" title="Offline Routers">Off</th>
|
||||
<th class="warning" title="Unknown Routers">Unk.</th>
|
||||
<th class="active" title="Total Routers">Sum</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- for gw, value in gws.items() %}
|
||||
<tr>
|
||||
<td class="firstrow"><p style="margin:0">{{ value["name"] }}</p></td>
|
||||
<td class="success" data-order="{{ (value["selected"]["online"] or 0) + (value["others"]["online"] or 0) }}"><span style="font-weight:bold">{{ value["selected"]["online"] or 0 }}</span> / {{ value["others"]["online"] or 0 }}</td>
|
||||
<td class="danger" data-order="{{ (value["selected"]["offline"] or 0) + (value["others"]["offline"] or 0) }}"><span style="font-weight:bold">{{ value["selected"]["offline"] or 0 }}</span> / {{ value["others"]["offline"] or 0 }}</td>
|
||||
<td class="warning" data-order="{{ (value["selected"]["unknown"] or 0) + (value["others"]["unknown"] or 0) }}"><span style="font-weight:bold">{{ value["selected"]["unknown"] or 0 }}</span> / {{ value["others"]["unknown"] or 0 }}</td>
|
||||
<td class="active" data-order="{{ (value["selected"]|sumdict if value["selected"] else 0) + (value["others"]|sumdict if value["others"] else 0) }}"><span style="font-weight:bold">{{ value["selected"]|sumdict if value["selected"] else 0 }}</span> / {{ value["others"]|sumdict if value["others"] else 0 }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">DHCP ranges</div>
|
||||
<div class="panel-body">
|
||||
<table id="dhcplist" class="table table-condensed table-hoods">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="firstrow">Gateway</th>
|
||||
<th class="warning" title="Interface1">VPN</th>
|
||||
<th title="Interface2">batX</th>
|
||||
<th class="success" title="IPv4">Range</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- for ip in dhcp %}
|
||||
<tr>
|
||||
<td class="firstrow">{{ ip["name"] }}</td>
|
||||
<td class="warning" data-order="{{ ip["name"] }}_{{ ip["vpnif"] }}">{{ ip["vpnif"] }}</td>
|
||||
<td data-order="{{ ip["name"] }}_{{ ip["batif"] }}">{{ ip["batif"] }}</td>
|
||||
<td class="success" data-order="{{ ip["dhcpstart"]|ip2int }}">{{ ip["dhcpstart"] }} - {{ ip["dhcpend"] }}</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">IPv4 List</div>
|
||||
<div class="panel-body">
|
||||
<table id="ipv4list" class="table table-condensed table-hoods">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="firstrow">Gateway</th>
|
||||
<th class="warning" title="Interface1">VPN</th>
|
||||
<th title="Interface2">batX</th>
|
||||
<th class="success" title="IPv4">IPv4</th>
|
||||
<th class="stats">Stat</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- for ip in ipv4 %}
|
||||
<tr>
|
||||
<td class="firstrow">{{ ip["name"] }}</td>
|
||||
<td class="warning" data-order="{{ ip["name"] }}_{{ ip["vpnif"] }}">{{ ip["vpnif"] }}</td>
|
||||
<td data-order="{{ ip["name"] }}_{{ ip["batif"] }}">{{ ip["batif"] }}</td>
|
||||
<td class="success" data-order="{{ ip["ipv4"]|ipnet2int }}">{{ ip["ipv4"] }}</td>
|
||||
{%- if ip["mac"] %}
|
||||
<td class="stats"><a href="{{ url_for('global_gwstatistics', selectgw='%s' % ip["mac"]|int2shortmac) }}">Stats</a></td>
|
||||
{%- else %}
|
||||
<td class="stats"> </td>
|
||||
{%- endif %}
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">IPv6 List</div>
|
||||
<div class="panel-body">
|
||||
<table id="ipv6list" class="table table-condensed table-hoods">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="firstrow">Gateway</th>
|
||||
<th class="warning" title="Interface1">VPN</th>
|
||||
<th title="Interface2">batX</th>
|
||||
<th class="success" title="IPv4">IPv6</th>
|
||||
<th class="stats">Stat</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{%- for ip in ipv6 %}
|
||||
<tr>
|
||||
<td class="firstrow">{{ ip["name"] }}</td>
|
||||
<td class="warning" data-order="{{ ip["name"] }}_{{ ip["vpnif"] }}">{{ ip["vpnif"] }}</td>
|
||||
<td data-order="{{ ip["name"] }}_{{ ip["batif"] }}">{{ ip["batif"] }}</td>
|
||||
<td class="success" data-order="{{ ip["ipv6"]|ipnet2int }}">{{ ip["ipv6"]|longip }}</td>
|
||||
{%- if ip["mac"] %}
|
||||
<td class="stats"><a href="{{ url_for('global_gwstatistics', selectgw='%s' % ip["mac"]|int2shortmac) }}">Stats</a></td>
|
||||
{%- else %}
|
||||
<td class="stats"> </td>
|
||||
{%- endif %}
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#gwlist").DataTable({
|
||||
"order": [],
|
||||
"paging": false,
|
||||
"info": false,
|
||||
"searching": false
|
||||
/*"responsive": {
|
||||
"details": false
|
||||
},*/
|
||||
});
|
||||
|
||||
$("#ipv4list").DataTable({
|
||||
"order": [[3,'asc']],
|
||||
"paging": false,
|
||||
"info": false,
|
||||
"searching": false,
|
||||
/*"responsive": {
|
||||
"details": false
|
||||
},*/
|
||||
"columnDefs": [
|
||||
{"orderable": false, "targets": 0},
|
||||
{"orderable": false, "targets": -1}
|
||||
]
|
||||
});
|
||||
|
||||
$("#ipv6list").DataTable({
|
||||
"order": [[3,'asc']],
|
||||
"paging": false,
|
||||
"info": false,
|
||||
"searching": false,
|
||||
/*"responsive": {
|
||||
"details": false
|
||||
},*/
|
||||
"columnDefs": [
|
||||
{"orderable": false, "targets": 0},
|
||||
{"orderable": false, "targets": -1}
|
||||
]
|
||||
});
|
||||
|
||||
$("#dhcplist").DataTable({
|
||||
"order": [[3,'asc']],
|
||||
"paging": false,
|
||||
"info": false,
|
||||
"searching": false,
|
||||
/*"responsive": {
|
||||
"details": false
|
||||
},*/
|
||||
"columnDefs": [
|
||||
{"orderable": false, "targets": 0}
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue