Merge pull request #1 from TimStallard/tds

better bgpmap internal route handling, other minor changes
This commit is contained in:
Simon Marsh 2019-01-30 18:41:38 +00:00 committed by GitHub
commit fca8545fcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 91 additions and 35 deletions

103
lg.py
View File

@ -123,7 +123,7 @@ def whois_command(query):
def bird_command(host, proto, query): def bird_command(host, proto, query):
"""Alias to bird_proxy for bird service""" """Alias to bird_proxy for bird service"""
if app.config.get("UNIFIED_DAEMON", False): if app.config.get("UNIFIED_DAEMON", False):
return bird_proxy(host, "ipv4", "bird", query) return bird_proxy(host, app.config.get("PROTO_DEFAULT", "ipv4"), "bird", query)
else: else:
return bird_proxy(host, proto, "bird", query) return bird_proxy(host, proto, "bird", query)
@ -145,6 +145,8 @@ def bird_proxy(host, proto, service, query):
path = service path = service
proxyHost = app.config["PROXY"].get(host, "") proxyHost = app.config["PROXY"].get(host, "")
if isinstance(proxyHost, int):
proxyHost = "%s:%s" % (host, proxyHost)
if not proxyHost: if not proxyHost:
return False, 'Host "%s" invalid' % host return False, 'Host "%s" invalid' % host
@ -185,14 +187,12 @@ def inject_commands():
return dict(commands=commands, commands_dict=commands_dict) return dict(commands=commands, commands_dict=commands_dict)
@app.context_processor
def inject_all_host():
return dict(all_hosts="+".join(app.config["PROXY"].keys()))
@app.route("/") @app.route("/")
def hello(): def hello():
return redirect("/summary/%s/ipv4" % "+".join(app.config["PROXY"].keys())) if app.config.get("UNIFIED_DAEMON", False):
return redirect("/summary/all")
else:
return redirect("/summary/all/%s" % app.config.get("PROTO_DEFAULT", "ipv4"))
def error_page(text): def error_page(text):
@ -230,18 +230,20 @@ def whois():
return jsonify(output=output, title=query) return jsonify(output=output, title=query)
SUMMARY_UNWANTED_PROTOS = ["Kernel", "Static", "Device"] SUMMARY_UNWANTED_PROTOS = ["Kernel", "Static", "Device", "BFD", "Direct"]
@app.route("/summary/<hosts>") @app.route("/summary/<hosts>")
@app.route("/summary/<hosts>/<proto>") @app.route("/summary/<hosts>/<proto>")
def summary(hosts, proto="ipv4"): def summary(hosts, proto="ipv4"):
set_session("summary", hosts, proto, "") set_session("summary", hosts, proto, "")
command = "show protocols" command = "show protocols"
summary = {} summary = {}
errors = [] errors = []
for host in hosts.split("+"): hosts = hosts.split("+")
if hosts == ["all"]:
hosts = app.config["PROXY"].keys()
for host in hosts:
ret, res = bird_command(host, proto, command) ret, res = bird_command(host, proto, command)
res = res.split("\n") res = res.split("\n")
@ -284,8 +286,9 @@ def summary(hosts, proto="ipv4"):
return render_template('summary.html', summary=summary, command=command, errors=errors) return render_template('summary.html', summary=summary, command=command, errors=errors)
@app.route("/detail/<hosts>")
@app.route("/detail/<hosts>/<proto>") @app.route("/detail/<hosts>/<proto>")
def detail(hosts, proto): def detail(hosts, proto="ipv4"):
name = get_query() name = get_query()
if not name: if not name:
@ -296,7 +299,10 @@ def detail(hosts, proto):
detail = {} detail = {}
errors = [] errors = []
for host in hosts.split("+"): hosts = hosts.split("+")
if hosts == ["all"]:
hosts = app.config["PROXY"].keys()
for host in hosts:
ret, res = bird_command(host, proto, command) ret, res = bird_command(host, proto, command)
res = res.split("\n") res = res.split("\n")
@ -308,13 +314,14 @@ def detail(hosts, proto):
errors.append("%s: bird command failed with error, %s" % (host, "\n".join(res))) errors.append("%s: bird command failed with error, %s" % (host, "\n".join(res)))
continue continue
detail[host] = {"status": res[1], "description": add_links(res[2:])} detail[host] = {"status": res[1], "description": add_links(res[2:]).decode("utf-8")}
return render_template('detail.html', detail=detail, command=command, errors=errors) return render_template('detail.html', detail=detail, command=command, errors=errors)
@app.route("/traceroute/<hosts>")
@app.route("/traceroute/<hosts>/<proto>") @app.route("/traceroute/<hosts>/<proto>")
def traceroute(hosts, proto): def traceroute(hosts, proto="ipv4"):
q = get_query() q = get_query()
if not q: if not q:
@ -325,9 +332,16 @@ def traceroute(hosts, proto):
if app.config.get("UNIFIED_DAEMON", False): if app.config.get("UNIFIED_DAEMON", False):
if not ip_is_valid(q): if not ip_is_valid(q):
try: try:
q = resolve_any(q) if app.config.get("UNIFIED_TRACEROUTE_IPV6", True):
q = resolve_any(q)
else:
q = resolve(q, "A")
except: except:
return error_page("%s is unresolvable" % q) return error_page("%s is unresolvable" % q)
if ipv6_is_valid(q):
proto = "ipv6"
else:
proto = "ipv4"
else: else:
if proto == "ipv6" and not ipv6_is_valid(q): if proto == "ipv6" and not ipv6_is_valid(q):
try: try:
@ -342,7 +356,10 @@ def traceroute(hosts, proto):
errors = [] errors = []
infos = {} infos = {}
for host in hosts.split("+"): hosts = hosts.split("+")
if hosts == ["all"]:
hosts = app.config["PROXY"].keys()
for host in hosts:
status, resultat = bird_proxy(host, proto, "traceroute", q) status, resultat = bird_proxy(host, proto, "traceroute", q)
if status is False: if status is False:
errors.append("%s" % resultat) errors.append("%s" % resultat)
@ -353,43 +370,51 @@ def traceroute(hosts, proto):
return render_template('traceroute.html', infos=infos, errors=errors) return render_template('traceroute.html', infos=infos, errors=errors)
@app.route("/adv/<hosts>")
@app.route("/adv/<hosts>/<proto>") @app.route("/adv/<hosts>/<proto>")
def show_route_filter(hosts, proto): def show_route_filter(hosts, proto="ipv4"):
return show_route("adv", hosts, proto) return show_route("adv", hosts, proto)
@app.route("/adv_bgpmap/<hosts>")
@app.route("/adv_bgpmap/<hosts>/<proto>") @app.route("/adv_bgpmap/<hosts>/<proto>")
def show_route_filter_bgpmap(hosts, proto): def show_route_filter_bgpmap(hosts, proto="ipv4"):
return show_route("adv_bgpmap", hosts, proto) return show_route("adv_bgpmap", hosts, proto)
@app.route("/where/<hosts>")
@app.route("/where/<hosts>/<proto>") @app.route("/where/<hosts>/<proto>")
def show_route_where(hosts, proto): def show_route_where(hosts, proto="ipv4"):
return show_route("where", hosts, proto) return show_route("where", hosts, proto)
@app.route("/where_detail/<hosts>")
@app.route("/where_detail/<hosts>/<proto>") @app.route("/where_detail/<hosts>/<proto>")
def show_route_where_detail(hosts, proto): def show_route_where_detail(hosts, proto="ipv4"):
return show_route("where_detail", hosts, proto) return show_route("where_detail", hosts, proto)
@app.route("/where_bgpmap/<hosts>")
@app.route("/where_bgpmap/<hosts>/<proto>") @app.route("/where_bgpmap/<hosts>/<proto>")
def show_route_where_bgpmap(hosts, proto): def show_route_where_bgpmap(hosts, proto="ipv4"):
return show_route("where_bgpmap", hosts, proto) return show_route("where_bgpmap", hosts, proto)
@app.route("/prefix/<hosts>")
@app.route("/prefix/<hosts>/<proto>") @app.route("/prefix/<hosts>/<proto>")
def show_route_for(hosts, proto): def show_route_for(hosts, proto="ipv4"):
return show_route("prefix", hosts, proto) return show_route("prefix", hosts, proto)
@app.route("/prefix_detail/<hosts>")
@app.route("/prefix_detail/<hosts>/<proto>") @app.route("/prefix_detail/<hosts>/<proto>")
def show_route_for_detail(hosts, proto): def show_route_for_detail(hosts, proto="ipv4"):
return show_route("prefix_detail", hosts, proto) return show_route("prefix_detail", hosts, proto)
@app.route("/prefix_bgpmap/<hosts>")
@app.route("/prefix_bgpmap/<hosts>/<proto>") @app.route("/prefix_bgpmap/<hosts>/<proto>")
def show_route_for_bgpmap(hosts, proto): def show_route_for_bgpmap(hosts, proto="ipv4"):
return show_route("prefix_bgpmap", hosts, proto) return show_route("prefix_bgpmap", hosts, proto)
@ -519,7 +544,9 @@ def show_bgpmap():
hop_label = "" hop_label = ""
add_node(_as, fillcolor=(first and "#F5A9A9" or "white")) add_node(_as, fillcolor=("white"))
if first:
nodes[_as].set_fillcolor("#F5A9A9")
if hop_label: if hop_label:
edge = add_edge(nodes[previous_as], nodes[_as], label=hop_label, fontsize="7") edge = add_edge(nodes[previous_as], nodes[_as], label=hop_label, fontsize="7")
else: else:
@ -587,7 +614,8 @@ def build_as_tree_from_raw_bird_ouput(host, proto, text):
for rt_host, rt_ips in app.config["ROUTER_IP"].iteritems(): for rt_host, rt_ips in app.config["ROUTER_IP"].iteritems():
# Special case for internal routing # Special case for internal routing
if peer_ip in rt_ips: if peer_ip in rt_ips:
path = [rt_host] paths.append([peer_protocol_name, rt_host])
path = None
break break
else: else:
# ugly hack for good printing # ugly hack for good printing
@ -604,7 +632,13 @@ def build_as_tree_from_raw_bird_ouput(host, proto, text):
if expr3.group(1).strip(): if expr3.group(1).strip():
net_dest = expr3.group(1).strip() net_dest = expr3.group(1).strip()
if line.startswith("BGP.as_path:"): expr4 = re.search(r'^dev', line)
#handle on-link routes
if expr4:
paths.append([peer_protocol_name, net_dest])
path = None
if line.startswith("BGP.as_path:") and path:
path.extend(line.replace("BGP.as_path:", "").strip().split(" ")) path.extend(line.replace("BGP.as_path:", "").strip().split(" "))
if path: if path:
@ -677,7 +711,11 @@ def show_route(request_type, hosts, proto):
detail = {} detail = {}
errors = [] errors = []
for host in hosts.split("+"): hosts = hosts.split("+")
if hosts == ["all"]:
hosts = app.config["PROXY"].keys()
allhosts = hosts[:]
for host in allhosts:
ret, res = bird_command(host, proto, command) ret, res = bird_command(host, proto, command)
res = res.split("\n") res = res.split("\n")
@ -691,6 +729,15 @@ def show_route(request_type, hosts, proto):
if bgpmap: if bgpmap:
detail[host] = build_as_tree_from_raw_bird_ouput(host, proto, res) detail[host] = build_as_tree_from_raw_bird_ouput(host, proto, res)
#for internal routes via hosts not selected
#add them to the list, but only show preferred route
if host not in hosts:
detail[host] = detail[host][:1]
for path in detail[host]:
if len(path) == 2:
if (path[1] not in allhosts) and (path[1] in app.config["PROXY"]):
allhosts.append(path[1])
else: else:
detail[host] = add_links(res) detail[host] = add_links(res)

View File

@ -5,7 +5,10 @@ function change_url(loc){
} }
function reload(){ function reload(){
loc = "/" + request_type + "/" + hosts + "/" + proto; loc = "/" + request_type + "/" + hosts;
if(proto){
loc += "/" + proto;
}
if (request_type != "summary" ){ if (request_type != "summary" ){
if( request_args != undefined && request_args != ""){ if( request_args != undefined && request_args != ""){
loc = loc + "?q=" + encodeURIComponent(request_args); loc = loc + "?q=" + encodeURIComponent(request_args);
@ -23,7 +26,9 @@ function update_view(){
$(".navbar li").removeClass('active'); $(".navbar li").removeClass('active');
$("a#"+proto+".proto").addClass('active'); if(proto){
$("a#"+proto+".proto").addClass('active');
}
$(".hosts a[id='"+hosts+"']").addClass('active'); $(".hosts a[id='"+hosts+"']").addClass('active');
$(".request_type a#"+request_type).parent().addClass('active'); $(".request_type a#"+request_type).parent().addClass('active');

View File

@ -18,7 +18,7 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<a class="navbar-brand" href="/">{{config.DOMAIN|capitalize}}</a> <a class="navbar-brand" href="/">{{config.DOMAIN|capitalize}}</a>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
<li class="nav-item hosts mr-1"><a class="nav-link" id="{{all_hosts}}" href="#">all</a></li> <li class="nav-item hosts mr-1"><a class="nav-link" id="all" href="#">all</a></li>
{% for host in config.PROXY %} {% for host in config.PROXY %}
<li class="nav-item hosts mr-1"><a class="nav-link" id="{{host}}" href="#">{{host}}</a></li> <li class="nav-item hosts mr-1"><a class="nav-link" id="{{host}}" href="#">{{host}}</a></li>
@ -76,7 +76,7 @@
<div class="list-group history"> <div class="list-group history">
{% for hosts, proto, request_type, request_args in session.history %} {% for hosts, proto, request_type, request_args in session.history %}
<a class="list-group-item list-group-item-action {% if loop.first %}active{% endif %}" <a class="list-group-item list-group-item-action {% if loop.first %}active{% endif %}"
href="/{{ [request_type, hosts, proto]|join("/") }}{% if request_args %}?q={{request_args}}{% endif %}"> href="/{{ [request_type, hosts]|join("/") }}{% if not config.UNIFIED_DAEMON %}/{{proto}}{% endif %}{% if request_args %}?q={{request_args}}{% endif %}">
{{hosts}}{% if not config.UNIFIED_DAEMON %}/{{proto}}{% endif %}: {{ commands_dict[request_type]|replace("...", request_args) }} {{hosts}}{% if not config.UNIFIED_DAEMON %}/{{proto}}{% endif %}: {{ commands_dict[request_type]|replace("...", request_args) }}
</a> </a>
@ -119,7 +119,11 @@
request_type = "{{session.request_type}}"; request_type = "{{session.request_type}}";
request_args = "{{session.request_args}}"; request_args = "{{session.request_args}}";
hosts = "{{session.hosts}}"; hosts = "{{session.hosts}}";
{% if config.UNIFIED_DAEMON %}
proto = ""
{% else %}
proto = "{{session.proto}}"; proto = "{{session.proto}}";
{% endif %}
history_query = {{session.history|tojson|safe}}; history_query = {{session.history|tojson|safe}};
</script> </script>
<script type="text/javascript" src="{{url_for('static', filename='js/lg.js') }}"></script> <script type="text/javascript" src="{{url_for('static', filename='js/lg.js') }}"></script>

View File

@ -3,7 +3,7 @@
{% for host in detail %} {% for host in detail %}
<h3> <h3>
{{host}}: {{command}} {{host}}: {{command}}
<small><a class="pull-right" href="/{{session.request_type|replace("_detail","")}}_bgpmap/{{session.hosts}}/{{session.proto}}?q={{session.request_args}}">View the BGP map</a></small> <small><a class="pull-right" href="/{{session.request_type|replace("_detail","")}}_bgpmap/{{session.hosts}}{% if not config.UNIFIED_DAEMON %}/{{session.proto}}{% endif %}?q={{session.request_args}}">View the BGP map</a></small>
</h3> </h3>
{% if session.request_args != expression|replace("/32","")|replace("/128","") %} {% if session.request_args != expression|replace("/32","")|replace("/128","") %}
<i>DNS: <a href="/whois/{{session.request_args}}" class="whois">{{session.request_args}}</a> => <a href="/whois/{{ expression|replace("/32","")|replace("/128","") }}" class="whois">{{expression|replace("/32","")|replace("/128","")}}</a></i><br /> <i>DNS: <a href="/whois/{{session.request_args}}" class="whois">{{session.request_args}}</a> => <a href="/whois/{{ expression|replace("/32","")|replace("/128","") }}" class="whois">{{expression|replace("/32","")|replace("/128","")}}</a></i><br />

View File

@ -9,7 +9,7 @@
<tbody> <tbody>
{% for row in summary[host] %} {% for row in summary[host] %}
<tr class="{{ loop.cycle('odd', 'even') }}"> <tr class="{{ loop.cycle('odd', 'even') }}">
<td><a href="/detail/{{host}}/{{session.proto}}?q={{row.name}}">{{row.name}}</a></td> <td><a href="/detail/{{host}}{% if not config.UNIFIED_DAEMON %}/{{session.proto}}{% endif %}?q={{row.name}}">{{row.name}}</a></td>
<td>{{row.proto}}</td> <td>{{row.proto}}</td>
<td><span class="badge badge-{% if row.state == "up" %}success{% elif row.state == "down" %}default{% elif row.state == "start" and row.info == "Passive" %}info{% else %}danger{% endif %}">{{row.state}}</span></td> <td><span class="badge badge-{% if row.state == "up" %}success{% elif row.state == "down" %}default{% elif row.state == "start" and row.info == "Passive" %}info{% else %}danger{% endif %}">{{row.state}}</span></td>
<td>{{row.since}}</td> <td>{{row.since}}</td>

View File

@ -1,7 +1,7 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
{% for host in infos %} {% for host in infos %}
<h3 id="traceroute_cmd_{{host}}">{{host}}/{{session.proto}}: traceroute {{session.request_args}}</h3><br /> <h3 id="traceroute_cmd_{{host}}">{{host}}{% if not config.UNIFIED_DAEMON %}/{{session.proto}}{% endif %}: traceroute {{session.request_args}}</h3><br />
{% if infos[host]|trim %} {% if infos[host]|trim %}
<pre>{{infos[host]|trim|safe}}</pre> <pre>{{infos[host]|trim|safe}}</pre>
{% endif %} {% endif %}