#!/usr/bin/env python ''' # Parsing lvrouted trees from various nodes to detect inconsistencies exploring # routing paths and stabilities. # # Rick van der Zwet # ''' import subprocess import sys import yaml import hashlib import logging import argparse from collections import defaultdict logging.basicConfig(level=logging.INFO, stream=sys.stderr) logger = logging.getLogger() class MakeLabel(): def __init__(self): self.nid = 0 self.i2n = {} self.h2n = {} def make_label(self,ip): # Single entries if not self.i2n.has_key(ip): node = 'n%i' % self.nid self.i2n[ip] = node self.nid += 1 def __getitem__(self,key): return self.i2n[key] def __str__(self): output = '' for ip,node in self.i2n.iteritems(): output += ' %s [label="%s"];\n' % (node, ip) return output class MakeHostLabel(): def __init__(self,ip2host, host2pos): self.nid = 0 self.ifindex = 0 self.h2n = {} self.h2used = defaultdict(dict) self.ip2host = ip2host self.host2pos = host2pos self.host2pos['unknown'] = {'x' : 0, 'y' : 0 } def make_label(self,ip): try: host = self.ip2host[ip] except KeyError: logger.warning("ignoring '%s' as not found in ip2host mapping", ip) return # Combined entries if not self.h2n.has_key(host): node = '%i' % self.nid self.h2n[host] = node self.nid += 1 if not ip in self.h2used[host]: self.h2used[host][ip] = self.ifindex self.ifindex += 1 def __getitem__(self, key, at_block=False): if at_block: return self.h2n[self.ip2host[key]] + ":r" + str(self.h2used[self.ip2host[key]][key]) else: return self.h2n[self.ip2host[key]] def is_same_host(self, k1, k2): try: return self.ip2host[k1] == self.ip2host[k2] except KeyError: logger.warning("ignoring '%s' -> '%s' as not found in ip2host mapping", k1, k2) return True def __str__(self): output = '' for host,ips in self.h2used.iteritems(): # Detailed overview shape #output += ' %s [label="%s|%s", shape="record", pos="%s,%s"];\n' % (self.h2n[host], host, '|'.join([" %s" % (x[1], x[0]) for x in ips.iteritems()]), self.host2pos[host]['x'], self.host2pos[host]['y']) # Single item #output += ' %s [label="%s", pos="%s,%s!", shape="rectangle"];\n' % (self.h2n[host], host, self.host2pos[host]['x'], self.host2pos[host]['y']) output += '''\ ''' % (self.h2n[host], host, self.host2pos[host]['x'], self.host2pos[host]['y']) return output def make_result(args, links, ip2host, host2pos): output = '''\ ''' # output = '''\ #graph G { # size = "10,10!"; # ratio = expand; # rankdir = LR; # overlap = prism; # node [fontsize="8", fontname="Ubuntu Mono", shape="ellipse", width=0, height=0]; # edge [fontsize="8", fontname="Ubuntu Mono", penwidth=1]; #''' # We cannot name our nodes using IP labels, syntax not supported ml = MakeHostLabel(ip2host, host2pos) for (parent, child) in links: ml.make_label(parent) ml.make_label(child) # Print labels output += ' \n' output += str(ml) + '\n' output += ' \n' # Print links output += ' \n' edge_id = 0 for (parent,child),count in links.iteritems(): # Do not bother printing intern targets if ml.is_same_host(parent, child): continue #output += ' %s -> %s [label="%s"];\n' % (ml[parent], ml[child], count) #output += ' %s -- %s;\n' % (ml[parent], ml[child]) output += ' \n' % (edge_id, ml[parent], ml[child]) edge_id += 1 output += ' \n' # All done output += '''\ ''' return output if __name__ == '__main__': parser = argparse.ArgumentParser(description=__doc__,formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('hostnames', type=str, nargs='+', metavar='host') parser.add_argument('-g','--debug', default=False, action='store_true') args = parser.parse_args() if args.debug: logger.setLevel(logging.DEBUG) ip2host = {} with open('host-ips.txt','r') as f: for line in f: items = line.strip().split() host = items.pop(0) for ip in items: ip2host[ip] = host host2pos = {} with open('host-pos.txt','r') as f: for line in f: items = line.strip().split() host, x, y = items for ip in items: host2pos[host] = { 'x' : x, 'y' : y} links = defaultdict(int) for fname in args.hostnames: path = [] with open(fname,'r') as f: for line in f: items = line.rstrip().split('\t') depth = len(filter(lambda x: x == '',items)) network = items[-1] logger.debug("%s: %s" % (depth, '->'.join(path))) if depth == 0: path = [network] elif depth == len(path): links[(path[-1], network)] += 1 path.append(network) elif depth < len(path): path = path[0:depth] links[(path[-1], network)] += 1 path.append(network) else: assert False, "Invalid depth %s (%s)" % (depth, network) print make_result(args, links, ip2host, host2pos)