Scripts to pretty print ovs flows and odl logs
[netvirt.git] / resources / commons / readable_flows.py
diff --git a/resources/commons/readable_flows.py b/resources/commons/readable_flows.py
new file mode 100755 (executable)
index 0000000..4390184
--- /dev/null
@@ -0,0 +1,134 @@
+#!/usr/bin/python
+
+import subprocess
+import sys
+import re
+from collections import defaultdict
+
+help ='''## readable_flows.py:
+
+This script pretty prints ovs flows created by ovsdb. Features include:
+
+1. Where possible, MACs are followed by their corresponding IP (in parenthases) and vice-versa
+2. Tunnel ids are followed by the decimal representation used by Open Daylight
+3. Counters and stats are removed so that meaningful diffs may be generated
+4. Table numbers are given together with descriptive names
+5. Flows are grouped together by priority in decending order
+
+### Usage:
+This script must be run on the OpenStack controller since it uses the
+neutron command to map MACs and IPs.
+> sudo ovs-ofctl -OOpenFlow13 dump-flows br-int | python readable_flows.py
+'''
+
+if len(sys.argv) > 1 and sys.argv[1] in ['-h', '-?', '--h', '--help']:
+    print help
+    sys.exit(0)
+
+DEFAULT_PRIO=32768
+
+TABLE_NAME = { \
+0: 'CLASSIFIER',\
+20: 'GATEWAY_RESOLVER',\
+10: 'DIRECTOR',\
+10: 'SFC_CLASSIFIER',\
+20: 'ARP_RESPONDER',\
+30: 'INBOUND_NAT',\
+40: 'EGRESS_ACL',\
+50: 'LOAD_BALANCER',\
+60: 'ROUTING',\
+70: 'ICMP_ECHO',\
+70: 'L3_FORWARDING',\
+80: 'L2_REWRITE',\
+90: 'INGRESS_ACL',\
+100: 'OUTBOUND_NAT',\
+110: 'L2_FORWARDING'}
+
+PORT_LIST_CMD = 'neutron port-list -c mac_address -c fixed_ips'.split(' ')
+
+LINE_PATTERN = '.*(?P<table>table=\d*), (?P<counters>n_packets=\d*, n_bytes=\d*), (?P<prio>priority=\d*)?,?(?P<rule>.*)'
+MAX_LINE = 30
+
+def print_rules(table, rules_by_prio):
+    print ''
+    cut = table.find('=')
+    print table.upper() + ' (' + TABLE_NAME[int(table[cut+1:])] + ')'
+
+    prios = rules_by_prio.keys()
+    prios.sort()
+    prios.reverse()
+    for prio in prios:
+        print '    priority=%i' % prio,
+        if DEFAULT_PRIO == prio: print '(DEFAULT_PRIO)'
+        else: print ''
+        for rule in rules_by_prio[prio]:
+            print_flow('        ', re.sub('actions=', 'ACTIONS=', rule))
+
+def tun_match_to_decimal(m):
+    s = m.group('num')
+    return 'tun_id=0x%s(%i)' % (s, int(s, 16))
+def tun_set_to_decimal(m):
+    s = m.group('num')
+    return '0x%s(%i)->tun_id' % (s, int(s, 16))
+
+def print_flow(indent, f):
+    print indent + f
+# WIP
+#    flow = indent + f
+#    if len(flow) <= MAX_LINE: 
+#        print flow
+#        return
+#    
+#    cut = flow.find('ACTIONS')
+#    match = flow[0:cut - 1]
+#    action = indent + indent + flow[cut:]
+#    print match
+#    while action:
+#        if len(action) <= MAX_LINE:
+#            print action
+#            break
+#        cut = action.rfind(',', 0, MAX_LINE)
+#        if cut < 2: 
+#            print action
+#            break
+#        print action[0:cut + 1]
+#        action = indent + indent + (' ' * len('ACTIONS=')) + action[cut +1:]
+
+port_list_out = subprocess.check_output(PORT_LIST_CMD)
+
+addresses = []
+for line in port_list_out.split('\n')[2:]:
+    if re.match('^$', line): continue
+    if '----' in line: continue
+    line = re.sub('^\| ', '', line)
+    line = re.sub(' *\|$', '', line)
+    (mac, fixed_ip) = line.split(' | ')
+    ip = eval(fixed_ip)['ip_address']
+    addresses.append((mac, ip))
+
+table = ''
+rules_by_prio = defaultdict(list)
+for line in sys.stdin:
+    for (mac, ip) in addresses: 
+        line = re.sub(mac, '%s(%s)' % (mac, ip), line)
+        line = re.sub('=%s(\D)' % ip, '=%s(%s)\\1' % (ip, mac), line)
+    line = re.sub('tun_id=0x(?P<num>[0-9a-fA-F]*)', tun_match_to_decimal, line)
+    line = re.sub('0x(?P<num>[0-9a-fA-F]*)->tun_id', tun_set_to_decimal, line)
+    match = re.match(LINE_PATTERN, line)
+    if not match: 
+        print '[Not a flow line?]:  ' + line,
+        continue
+
+    if  match.group('table') != table:
+        if table:
+            print_rules(table, rules_by_prio)
+            rules_by_prio = defaultdict(list)
+        table = match.group('table')
+
+    prio = DEFAULT_PRIO
+    prio_str = match.group('prio')
+    if None != prio_str: prio = int(prio_str[9:])
+    rules_by_prio[prio].append(match.group('rule'))
+
+print_rules(table, rules_by_prio)
+