diff --git a/ffmap/stattools.py b/ffmap/stattools.py
index 997cb5a..41083ce 100644
--- a/ffmap/stattools.py
+++ b/ffmap/stattools.py
@@ -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,)
diff --git a/ffmap/web/application.py b/ffmap/web/application.py
index 1316370..0897923 100755
--- a/ffmap/web/application.py
+++ b/ffmap/web/application.py
@@ -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':
diff --git a/ffmap/web/filters.py b/ffmap/web/filters.py
index 6701aff..8d721af 100644
--- a/ffmap/web/filters.py
+++ b/ffmap/web/filters.py
@@ -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())
diff --git a/ffmap/web/templates/bootstrap.html b/ffmap/web/templates/bootstrap.html
index 80fdf9d..37bfcee 100644
--- a/ffmap/web/templates/bootstrap.html
+++ b/ffmap/web/templates/bootstrap.html
@@ -39,6 +39,7 @@
(["router_list", "router_info"], "Routers"),
(["user_list", "user_info"], "Users"),
(["global_statistics"], "Statistics"),
+ (["gateways"], "GWs"),
(["apidoc"], "API"),
] %}
{{ text }}
diff --git a/ffmap/web/templates/gws.html b/ffmap/web/templates/gws.html
new file mode 100644
index 0000000..d637ef1
--- /dev/null
+++ b/ffmap/web/templates/gws.html
@@ -0,0 +1,226 @@
+{% extends "bootstrap.html" %}
+{% block title %}{{super()}} :: Gateways{% endblock %}
+{% block head %}{{super()}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block content %}
+
+
+
+
Gateways (selected / others)
+
+
+
+
+ Gateway |
+ On |
+ Off |
+ Unk. |
+ Sum |
+
+
+
+ {%- for gw, value in gws.items() %}
+
+ {{ value["name"] }} |
+ {{ value["selected"]["online"] or 0 }} / {{ value["others"]["online"] or 0 }} |
+ {{ value["selected"]["offline"] or 0 }} / {{ value["others"]["offline"] or 0 }} |
+ {{ value["selected"]["unknown"] or 0 }} / {{ value["others"]["unknown"] or 0 }} |
+ {{ value["selected"]|sumdict if value["selected"] else 0 }} / {{ value["others"]|sumdict if value["others"] else 0 }} |
+
+ {%- endfor %}
+
+
+
+
+
+
DHCP ranges
+
+
+
+
+ Gateway |
+ VPN |
+ batX |
+ Range |
+
+
+
+ {%- for ip in dhcp %}
+
+ {{ ip["name"] }} |
+ {{ ip["vpnif"] }} |
+ {{ ip["batif"] }} |
+ {{ ip["dhcpstart"] }} - {{ ip["dhcpend"] }} |
+
+ {%- endfor %}
+
+
+
+
+
+
+
+
IPv4 List
+
+
+
+
+ Gateway |
+ VPN |
+ batX |
+ IPv4 |
+ Stat |
+
+
+
+ {%- for ip in ipv4 %}
+
+ {{ ip["name"] }} |
+ {{ ip["vpnif"] }} |
+ {{ ip["batif"] }} |
+ {{ ip["ipv4"] }} |
+ {%- if ip["mac"] %}
+ Stats |
+ {%- else %}
+ |
+ {%- endif %}
+
+ {%- endfor %}
+
+
+
+
+
+
IPv6 List
+
+
+
+
+ Gateway |
+ VPN |
+ batX |
+ IPv6 |
+ Stat |
+
+
+
+ {%- for ip in ipv6 %}
+
+ {{ ip["name"] }} |
+ {{ ip["vpnif"] }} |
+ {{ ip["batif"] }} |
+ {{ ip["ipv6"]|longip }} |
+ {%- if ip["mac"] %}
+ Stats |
+ {%- else %}
+ |
+ {%- endif %}
+
+ {%- endfor %}
+
+
+
+
+
+
+
+{% endblock %}