Decoupling SFC from NetVirt
[netvirt.git] / resources / commons / readable_flows.py
1 #!/usr/bin/python
2
3 import subprocess
4 import sys
5 import re
6 from collections import defaultdict
7
8 help ='''## readable_flows.py:
9
10 This script pretty prints ovs flows created by ovsdb. Features include:
11
12 1. Where possible, MACs are followed by their corresponding IP (in parenthases) and vice-versa
13 2. Tunnel ids are followed by the decimal representation used by Open Daylight
14 3. Counters and stats are removed so that meaningful diffs may be generated
15 4. Table numbers are given together with descriptive names
16 5. Flows are grouped together by priority in decending order
17
18 ### Usage:
19 This script must be run on the OpenStack controller since it uses the
20 neutron command to map MACs and IPs.
21 > sudo ovs-ofctl -OOpenFlow13 dump-flows br-int | python readable_flows.py
22 '''
23
24 if len(sys.argv) > 1 and sys.argv[1] in ['-h', '-?', '--h', '--help']:
25     print help
26     sys.exit(0)
27
28 DEFAULT_PRIO=32768
29
30 TABLE_NAME = { \
31 0: 'CLASSIFIER',\
32 20: 'GATEWAY_RESOLVER',\
33 10: 'DIRECTOR',\
34 20: 'ARP_RESPONDER',\
35 30: 'INBOUND_NAT',\
36 40: 'EGRESS_ACL',\
37 50: 'LOAD_BALANCER',\
38 60: 'ROUTING',\
39 70: 'ICMP_ECHO',\
40 70: 'L3_FORWARDING',\
41 80: 'L2_REWRITE',\
42 90: 'INGRESS_ACL',\
43 100: 'OUTBOUND_NAT',\
44 110: 'L2_FORWARDING'}
45
46 PORT_LIST_CMD = 'neutron port-list -c mac_address -c fixed_ips'.split(' ')
47
48 LINE_PATTERN = '.*(?P<table>table=\d*), (?P<counters>n_packets=\d*, n_bytes=\d*), (?P<prio>priority=\d*)?,?(?P<rule>.*)'
49 MAX_LINE = 30
50
51 def print_rules(table, rules_by_prio):
52     print ''
53     cut = table.find('=')
54     print table.upper() + ' (' + TABLE_NAME[int(table[cut+1:])] + ')'
55
56     prios = rules_by_prio.keys()
57     prios.sort()
58     prios.reverse()
59     for prio in prios:
60         print '    priority=%i' % prio,
61         if DEFAULT_PRIO == prio: print '(DEFAULT_PRIO)'
62         else: print ''
63         for rule in rules_by_prio[prio]:
64             print_flow('        ', re.sub('actions=', 'ACTIONS=', rule))
65
66 def tun_match_to_decimal(m):
67     s = m.group('num')
68     return 'tun_id=0x%s(%i)' % (s, int(s, 16))
69 def tun_set_to_decimal(m):
70     s = m.group('num')
71     return '0x%s(%i)->tun_id' % (s, int(s, 16))
72
73 def print_flow(indent, f):
74     print indent + f
75 # WIP
76 #    flow = indent + f
77 #    if len(flow) <= MAX_LINE:
78 #        print flow
79 #        return
80 #
81 #    cut = flow.find('ACTIONS')
82 #    match = flow[0:cut - 1]
83 #    action = indent + indent + flow[cut:]
84 #    print match
85 #    while action:
86 #        if len(action) <= MAX_LINE:
87 #            print action
88 #            break
89 #        cut = action.rfind(',', 0, MAX_LINE)
90 #        if cut < 2:
91 #            print action
92 #            break
93 #        print action[0:cut + 1]
94 #        action = indent + indent + (' ' * len('ACTIONS=')) + action[cut +1:]
95
96 port_list_out = subprocess.check_output(PORT_LIST_CMD)
97
98 addresses = []
99 for line in port_list_out.split('\n')[2:]:
100     if re.match('^$', line): continue
101     if '----' in line: continue
102     line = re.sub('^\| ', '', line)
103     line = re.sub(' *\|$', '', line)
104     (mac, fixed_ip) = line.split(' | ')
105     ip = eval(fixed_ip)['ip_address']
106     addresses.append((mac, ip))
107
108 table = ''
109 rules_by_prio = defaultdict(list)
110 for line in sys.stdin:
111     for (mac, ip) in addresses:
112         line = re.sub(mac, '%s(%s)' % (mac, ip), line)
113         line = re.sub('=%s(\D)' % ip, '=%s(%s)\\1' % (ip, mac), line)
114     line = re.sub('tun_id=0x(?P<num>[0-9a-fA-F]*)', tun_match_to_decimal, line)
115     line = re.sub('0x(?P<num>[0-9a-fA-F]*)->tun_id', tun_set_to_decimal, line)
116     match = re.match(LINE_PATTERN, line)
117     if not match:
118         print '[Not a flow line?]:  ' + line,
119         continue
120
121     if  match.group('table') != table:
122         if table:
123             print_rules(table, rules_by_prio)
124             rules_by_prio = defaultdict(list)
125         table = match.group('table')
126
127     prio = DEFAULT_PRIO
128     prio_str = match.group('prio')
129     if None != prio_str: prio = int(prio_str[9:])
130     rules_by_prio[prio].append(match.group('rule'))
131
132 print_rules(table, rules_by_prio)
133