' + escape(kwargs.get("label", get_as_name(_as))).replace("\r", " ") + " |
" + graph.create_dot() + "") return Response(graph.create_png(), mimetype='image/png') def build_as_tree_from_raw_bird_ouput(host, proto, text): """Extract the as path from the raw bird "show route all" command""" path = None paths = [] net_dest = None re_via = re.compile(r'(.*)via\s+([0-9a-fA-F:\.]+)\s+on.*\[(\w+)\s+') re_unreachable = re.compile(r'(.*)unreachable\s+\[(\w+)\s+') for line in text: line = line.strip() expr = re_via.search(line) if expr: if path: path.append(net_dest) paths.append(path) path = None if expr.group(1).strip(): net_dest = expr.group(1).strip() peer_ip = expr.group(2).strip() peer_protocol_name = expr.group(3).strip() # Check if via line is a internal route for rt_host, rt_ips in app.config["ROUTER_IP"].iteritems(): # Special case for internal routing if peer_ip in rt_ips: path = [rt_host] break else: # ugly hack for good printing path = [peer_protocol_name] # path = ["%s\r%s" % (peer_protocol_name, get_as_name(get_as_number_from_protocol_name(host, proto, peer_protocol_name)))] expr2 = re_unreachable.search(line) if expr2: if path: path.append(net_dest) paths.append(path) path = None if expr2.group(1).strip(): net_dest = expr2.group(1).strip() if line.startswith("BGP.as_path:"): path.extend(line.replace("BGP.as_path:", "").strip().split(" ")) if path: path.append(net_dest) paths.append(path) return paths def build_as_tree_from_full_view(host, proto, res): # FIXME: debug # {u'noc.chi1.us': [[u'chi1', u'31133', u'6697', u'193.232.248.0/22']], # u'noc.dal1.us': [[u'dal1', u'31133', u'6697', u'193.232.248.0/22']], # u'noc.sng2.sg': [[u'sng2', u'31133', u'6697', u'193.232.248.0/22']], # u'noc.war1.pl': [[u'war1', u'31133', u'6697', u'193.232.248.0/22']], # u'rc1.ash1.us': [[u'ash1', u'31133', u'6697', u'193.232.248.0/22']], # u'rc1.atl1.us': [[u'atl1', u'31133', u'6697', u'193.232.248.0/22']], # u'rc1.fra6.de': [[u'fra6', u'31133', u'6697', u'193.232.248.0/22']], # u'rc1.lax1.us': [[u'lax1', u'31133', u'6697', u'193.232.248.0/22']], # u'rc1.lon1.uk': [[u'lon1', u'31133', u'6697', u'193.232.248.0/22']], # u'rc1.nyc1.us': [[u'nyc1', u'31133', u'6697', u'193.232.248.0/22']], # u'rc1.par2.fr': [[u'par2', u'31133', u'6697', u'193.232.248.0/22']], # u'rc1.sjo1.us': [[u'sjo1', u'31133', u'6697', u'193.232.248.0/22']]} # {'ginwyntcore2': [['ginwyntcore2', '9002', '6697', '193.232.248.0/22']]} re_chunk_start = re.compile(r'(.*)unreachable\s+\[(.*)\s+.*\s+from\s+(.*)\].*\(.*\)\s\[.*\]') result = dict() dest_subnet = None for line in res: line = line.strip() expr = re_chunk_start.search(line) if expr: # Beginning of the BGP reply chunk if not dest_subnet: dest_subnet = expr.group(1).strip() router_tag = expr.group(2).strip() router_ip = expr.group(3).strip() result[router_ip] = list() elif line.startswith('BGP.as_path:'): # BGP AS path line = line.replace('BGP.as_path:', '') line = line.strip() path = [router_tag, ] for as_num in line.split(' '): if as_num: path.append(as_num) path.append(dest_subnet) result[router_ip].append(path) elif line.startswith('BGP.community:'): # BGP community line = line.replace('BGP.community:', '') line = line.strip() elif line.startswith('BGP.cluster_list:'): # BGP cluster size line = line.replace('BGP.cluster_list:', '') line = line.strip() # Sort and filter out result # 0. Breakdown by possible BGP pathes # 1. Sort by BGP path length, shortest first # 2. Sort by cluster size, smallest first # 3. Sort by community (?) return result def show_route(request_type, hosts, proto): expression = get_query() if not expression: abort(400) set_session(request_type, hosts, proto, expression) bgpmap = request_type.endswith("bgpmap") all = (request_type.endswith("detail") and " all" or "") if bgpmap: all = " all" if request_type.startswith("adv"): command = "show route " + expression.strip() if bgpmap and not command.endswith("all"): command = command + " all" elif request_type.startswith("where"): command = "show route where net ~ [ " + expression + " ]" + all else: mask = "" if len(expression.split("/")) == 2: expression, mask = (expression.split("/")) if not mask and proto == "ipv4": mask = "32" if not mask and proto == "ipv6": mask = "128" if not mask_is_valid(mask): return error_page("mask %s is invalid" % mask) if proto == "ipv6" and not ipv6_is_valid(expression): try: expression = resolve(expression, "AAAA") except: return error_page("%s is unresolvable or invalid for %s" % (expression, proto)) if proto == "ipv4" and not ipv4_is_valid(expression): try: expression = resolve(expression, "A") except: return error_page("%s is unresolvable or invalid for %s" % (expression, proto)) if mask: expression += "/" + mask command = "show route for " + expression + all detail = {} errors = [] for host in hosts.split("+"): ret, res = bird_command(host, proto, command) res = res.split("\n") if ret is False: errors.append("%s" % res) continue if len(res) <= 1: errors.append("%s: bird command failed with error, %s" % (host, "\n".join(res))) continue if bgpmap: if app.config['BIRD_HAS_FULL_VIEW']: detail = build_as_tree_from_full_view(host, proto, res) else: detail[host] = build_as_tree_from_raw_bird_ouput(host, proto, res) else: detail[host] = add_links(res) if bgpmap: detail = json.dumps(detail) return render_template((bgpmap and 'bgpmap.html' or 'route.html'), detail=detail, command=command, expression=expression, errors=errors) if __name__ == "__main__": app.run(app.config.get("BIND_IP", "0.0.0.0"), app.config.get("BIND_PORT", 5000))