6 from collections import defaultdict
8 help ='''## readable_flows.py:
10 This script pretty prints ovs flows created by ovsdb. Features include:
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
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
24 if len(sys.argv) > 1 and sys.argv[1] in ['-h', '-?', '--h', '--help']:
32 20: 'GATEWAY_RESOLVER',\
34 10: 'SFC_CLASSIFIER',\
47 PORT_LIST_CMD = 'neutron port-list -c mac_address -c fixed_ips'.split(' ')
49 LINE_PATTERN = '.*(?P<table>table=\d*), (?P<counters>n_packets=\d*, n_bytes=\d*), (?P<prio>priority=\d*)?,?(?P<rule>.*)'
52 def print_rules(table, rules_by_prio):
55 print table.upper() + ' (' + TABLE_NAME[int(table[cut+1:])] + ')'
57 prios = rules_by_prio.keys()
61 print ' priority=%i' % prio,
62 if DEFAULT_PRIO == prio: print '(DEFAULT_PRIO)'
64 for rule in rules_by_prio[prio]:
65 print_flow(' ', re.sub('actions=', 'ACTIONS=', rule))
67 def tun_match_to_decimal(m):
69 return 'tun_id=0x%s(%i)' % (s, int(s, 16))
70 def tun_set_to_decimal(m):
72 return '0x%s(%i)->tun_id' % (s, int(s, 16))
74 def print_flow(indent, f):
78 # if len(flow) <= MAX_LINE:
82 # cut = flow.find('ACTIONS')
83 # match = flow[0:cut - 1]
84 # action = indent + indent + flow[cut:]
87 # if len(action) <= MAX_LINE:
90 # cut = action.rfind(',', 0, MAX_LINE)
94 # print action[0:cut + 1]
95 # action = indent + indent + (' ' * len('ACTIONS=')) + action[cut +1:]
97 port_list_out = subprocess.check_output(PORT_LIST_CMD)
100 for line in port_list_out.split('\n')[2:]:
101 if re.match('^$', line): continue
102 if '----' in line: continue
103 line = re.sub('^\| ', '', line)
104 line = re.sub(' *\|$', '', line)
105 (mac, fixed_ip) = line.split(' | ')
106 ip = eval(fixed_ip)['ip_address']
107 addresses.append((mac, ip))
110 rules_by_prio = defaultdict(list)
111 for line in sys.stdin:
112 for (mac, ip) in addresses:
113 line = re.sub(mac, '%s(%s)' % (mac, ip), line)
114 line = re.sub('=%s(\D)' % ip, '=%s(%s)\\1' % (ip, mac), line)
115 line = re.sub('tun_id=0x(?P<num>[0-9a-fA-F]*)', tun_match_to_decimal, line)
116 line = re.sub('0x(?P<num>[0-9a-fA-F]*)->tun_id', tun_set_to_decimal, line)
117 match = re.match(LINE_PATTERN, line)
119 print '[Not a flow line?]: ' + line,
122 if match.group('table') != table:
124 print_rules(table, rules_by_prio)
125 rules_by_prio = defaultdict(list)
126 table = match.group('table')
129 prio_str = match.group('prio')
130 if None != prio_str: prio = int(prio_str[9:])
131 rules_by_prio[prio].append(match.group('rule'))
133 print_rules(table, rules_by_prio)