From f12a3f5a3e68dcab025b143317d14355a884e693 Mon Sep 17 00:00:00 2001 From: Adrian Schmutzler Date: Tue, 9 Jan 2018 13:35:47 +0100 Subject: [PATCH] api/alfred and router.html: Collect and show gateway information This requires changes to the MySQL database! Signed-off-by: Adrian Schmutzler --- ffmap/db/routers.py | 32 +++++++++++++++ ffmap/routertools.py | 69 +++++++++++++++++++++++++++++++++ ffmap/web/application.py | 14 +++++++ ffmap/web/static/js/graph.js | 43 ++++++++++++++++++++ ffmap/web/templates/router.html | 45 +++++++++++++++++++++ 5 files changed, 203 insertions(+) diff --git a/ffmap/db/routers.py b/ffmap/db/routers.py index 132fed7..ad0d508 100755 --- a/ffmap/db/routers.py +++ b/ffmap/db/routers.py @@ -109,6 +109,23 @@ mysql.execute(""" ADD PRIMARY KEY (`router`,`time`,`type`) """) +mysql.execute(""" + CREATE TABLE router_gw ( + `router` mediumint(8) UNSIGNED NOT NULL, + `mac` char(17) COLLATE utf8_unicode_ci NOT NULL, + `quality` smallint(6) NOT NULL, + `nexthop` char(17) COLLATE utf8_unicode_ci DEFAULT NULL, + `netif` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL, + `gw_class` varchar(25) COLLATE utf8_unicode_ci DEFAULT NULL, + `selected` tinyint(1) NOT NULL DEFAULT '0' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci +""") + +mysql.execute(""" + ALTER TABLE router_gw + ADD PRIMARY KEY (`router`,`mac`) +""") + mysql.execute(""" CREATE TABLE router_ipv6 ( `router` mediumint(8) UNSIGNED NOT NULL, @@ -183,6 +200,21 @@ mysql.execute(""" ADD KEY `router` (`router`) """) +mysql.execute(""" + CREATE TABLE router_stats_gw ( + `time` int(11) NOT NULL, + `router` mediumint(8) UNSIGNED NOT NULL, + `mac` char(17) COLLATE utf8_unicode_ci NOT NULL, + `quality` smallint(6) NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci +""") + +mysql.execute(""" + ALTER TABLE router_stats_gw + ADD PRIMARY KEY (`time`,`router`,`mac`), + ADD KEY `router` (`router`) +""") + mysql.execute(""" CREATE TABLE router_stats_neighbor ( `time` int(11) NOT NULL, diff --git a/ffmap/routertools.py b/ffmap/routertools.py index b6adf72..dee9cb8 100644 --- a/ffmap/routertools.py +++ b/ffmap/routertools.py @@ -164,6 +164,14 @@ def import_nodewatcher_xml(mysql, mac, xml, banned, netifdict): else: mysql.execute("DELETE FROM router_neighbor WHERE router = %s",(router_id,)) + gwkeys = [] + for g in router_update["gws"]: + gwkeys.append(g["mac"]) + if gwkeys: + mysql.execute("DELETE FROM router_gw WHERE router = %s AND mac NOT IN ({})".format(','.join(['%s'] * len(gwkeys))),tuple([router_id] + gwkeys)) + else: + mysql.execute("DELETE FROM router_gw WHERE router = %s",(router_id,)) + else: # insert new router created = mysql.utcnow() @@ -232,6 +240,21 @@ def import_nodewatcher_xml(mysql, mac, xml, banned, netifdict): type=VALUES(type) """,nbdata) + gwdata = [] + for g in router_update["gws"]: + gwdata.append((router_id,g["mac"],g["quality"],g["nexthop"],g["netif"],g["gw_class"],g["selected"],)) + + mysql.executemany(""" + INSERT INTO router_gw (router, mac, quality, nexthop, netif, gw_class, selected) + VALUES (%s, %s, %s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE + quality=VALUES(quality), + nexthop=VALUES(nexthop), + netif=VALUES(netif), + gw_class=VALUES(gw_class), + selected=VALUES(selected) + """,gwdata) + if router_id: new_router_stats(mysql, router_id, uptime, router_update, netifdict) @@ -354,6 +377,17 @@ def delete_old_stats(mysql): writelog(CONFIG["debug_dir"] + "/deletetime.txt", "Delete stats: %.3f seconds" % (time.time() - start_time)) print("--- Delete stats: %.3f seconds ---" % (time.time() - start_time)) + time.sleep(10) + start_time = time.time() + mysql.execute(""" + DELETE s FROM router_stats_gw AS s + LEFT JOIN router AS r ON s.router = r.id + WHERE s.time < %s AND (r.status = 'online' OR r.status IS NULL) + """,(threshold,)) + mysql.commit() + writelog(CONFIG["debug_dir"] + "/deletetime.txt", "Delete gw-stats: %.3f seconds" % (time.time() - start_time)) + print("--- Delete gw-stats: %.3f seconds ---" % (time.time() - start_time)) + time.sleep(10) start_time = time.time() mysql.execute(""" @@ -494,6 +528,15 @@ def new_router_stats(mysql, router_id, uptime, router_update, netifdict): INSERT INTO router_stats_neighbor (time, router, mac, quality) VALUES (%s, %s, %s, %s) """,nbdata) + + gwdata = [] + for gw in router_update["gws"]: + with suppress(KeyError): + gwdata.append((time,router_id,gw["mac"],gw["quality"],)) + mysql.executemany(""" + INSERT INTO router_stats_gw (time, router, mac, quality) + VALUES (%s, %s, %s, %s) + """,gwdata) def calculate_network_io(mysql, router_id, uptime, router_update): """ @@ -538,6 +581,14 @@ def evalxpathint(tree,p,default=0): tmp = int(tree.xpath(p)[0]) return tmp +def evalxpathbool(tree,p): + tmp = False + with suppress(IndexError): + tmp = tree.xpath(p)[0] + if tmp: + return (tmp.lower()=="true" or tmp=="1") + return False + def parse_nodewatcher_xml(xml): try: assert xml != "" @@ -547,6 +598,7 @@ def parse_nodewatcher_xml(xml): "status": evalxpath(tree,"/data/system_data/status/text()"), "hostname": evalxpath(tree,"/data/system_data/hostname/text()"), "last_contact": utcnow().strftime('%Y-%m-%d %H:%M:%S'), + "gws": [], "neighbours": [], "netifs": [], # hardware @@ -680,6 +732,23 @@ def parse_nodewatcher_xml(xml): visible_neighbours += len(l3_neighbours) router_update["visible_neighbours"] = visible_neighbours router_update["neighbours"] += l3_neighbours + + for gw in tree.xpath("/data/batman_adv_gateway_list/*"): + gw_mac = evalxpath(gw,"gateway/text()") + if (gw_mac and len(gw_mac)>12): # Throw away headline + gw = { + "mac": gw_mac.lower(), + "quality": evalxpathint(gw,"link_quality/text()"), + "nexthop": evalxpath(gw,"nexthop/text()",None), + "netif": evalxpath(gw,"outgoing_interface/text()",None), + "gw_class": evalxpath(gw,"gw_class/text()",None), + "selected": evalxpathbool(gw,"selected/text()") + } + if gw["netif"]=="false": + tmp = gw["gw_class"].split(None,1) + gw["netif"] = tmp[0] + gw["gw_class"] = tmp[1] + router_update["gws"].append(gw) return router_update except (AssertionError, lxml.etree.XMLSyntaxError, IndexError) as e: diff --git a/ffmap/web/application.py b/ffmap/web/application.py index 11814bc..de47e7f 100755 --- a/ffmap/web/application.py +++ b/ffmap/web/application.py @@ -118,6 +118,12 @@ def router_info(dbid): """,(dbid,)) # FIX SQL: only one from router_netif + router["gws"] = mysql.fetchall(""" + SELECT mac, quality, netif, gw_class, selected + FROM router_gw + WHERE router = %s + """,(dbid,)) + router["events"] = mysql.fetchall("""SELECT * FROM router_events WHERE router = %s""",(dbid,)) router["events"] = mysql.utcawaretuple(router["events"],"time") @@ -142,6 +148,13 @@ def router_info(dbid): for ns in neighfetch: ns["time"] = mysql.utcawareint(ns["time"]) + gwfetch = mysql.fetchall(""" + SELECT quality, mac, time FROM router_stats_gw WHERE router = %s + """,(dbid,)) + + for ns in gwfetch: + ns["time"] = mysql.utcawareint(ns["time"]) + if request.method == 'POST': if request.form.get("act") == "delete": # a router may not have a owner, but admin users still can delete it @@ -193,6 +206,7 @@ def router_info(dbid): tileurls = tileurls, netifstats = netiffetch, neighstats = neighfetch, + gwstats = gwfetch, authuser = is_authorized(router["user"], session), authadmin = session.get('admin') ) diff --git a/ffmap/web/static/js/graph.js b/ffmap/web/static/js/graph.js index b6fda93..35e33c5 100644 --- a/ffmap/web/static/js/graph.js +++ b/ffmap/web/static/js/graph.js @@ -142,6 +142,49 @@ function neighbour_graph(neighbours) { setup_plot_zoom(plot, pdata, len); } +function gw_graph(gws) { + var gwstat = $("#gwstat"); + var pdata = []; + for (j=0; j + {%- if router.gws|length > 0 %} +
+
Gateways
+
+
+ + + + + + + + {%- for gw in router.gws %} + {%- if gw.selected %} + + {%- else %} + + {%- endif %} + + + + + + {%- endfor %} +
MAC (fastd/l2tp)QualNetifClass
{{ gw.mac }}{{ gw.quality }}{{ gw.netif }}{{ gw.gw_class }}
+
+ {# hack for graph vertical align #} + {%- if router.gws|length < 3 %} + {%- for n in range(3- router.gws|length) %} +
+ {%- endfor %} + {%- endif %} +
+
+
+ {%- endif %}
@@ -347,15 +383,24 @@ var router_stats = {{ router.stats|statbson2json|safe }}; var netif_stats = {{ netifstats|statbson2json|safe }}; var neigh_stats = {{ neighstats|statbson2json|safe }}; + var gw_stats = {{ gwstats|statbson2json|safe }}; var neighbours = [ {%- for neighbour in router.neighbours %} {"name": "{{ neighbour.hostname or neighbour.mac }}", "mac": "{{ neighbour.mac }}", "netif": "{{ neighbour.netif }}"}, {%- endfor %} ]; + var gws = [ + {%- for gw in router.gws %} + {"name": "{{ gw.mac }}", "mac": "{{ gw.mac }}", "netif": "{{ gw.netif }}"}, + {%- endfor %} + ]; $(document).ready(function() { {%- if router.neighbours|length > 0 %} neighbour_graph(neighbours); {%- endif %} + {%- if router.gws|length > 0 %} + gw_graph(gws); + {%- endif %} memory_graph(); process_graph(); client_graph();