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:
Adrian Schmutzler 2018-08-25 19:29:50 +02:00
parent 4d638c3744
commit 4c2b4f1628
5 changed files with 341 additions and 2 deletions

View File

@ -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,)

View File

@ -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':

View File

@ -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())

View File

@ -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>

View File

@ -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">&nbsp;</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">&nbsp;</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 %}