From d0415e4dafdf9a52837a41eea258af5fb4742d1a Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 15 Mar 2016 09:22:43 +0200 Subject: [PATCH] Scripts to pretty print ovs flows and odl logs Change-Id: If002fef89ed257057b9832227203c2d9e6dd3655 Signed-off-by: Josh --- resources/commons/README | 4 + resources/commons/pplog.py | 69 ++++++++++++++ resources/commons/readable_flows.py | 134 ++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100755 resources/commons/pplog.py create mode 100755 resources/commons/readable_flows.py diff --git a/resources/commons/README b/resources/commons/README index a6f6920bb..6aac3d3ce 100644 --- a/resources/commons/README +++ b/resources/commons/README @@ -34,3 +34,7 @@ Contents - Qos-and-Queue-Collection-Environment-Variables.postman_environment : Postman environment file that defines variables used by the Qos-and-Queue-Collection.json.postman_collection - Qos-and-Queue-Collection.json.postman_collection - Collection of Postman Restconf commands for doing CRUD operations on Qos and Queue entries. + +- readable_flows.py : script for formatting ovs-ofctl dump-flows output into a more readable format. Run w/ -h for help + +- pplogs.py : script for reformatting serialized nested objects in OpenDaylight log lines. Run w/ -h for help diff --git a/resources/commons/pplog.py b/resources/commons/pplog.py new file mode 100755 index 000000000..8cc4e3d26 --- /dev/null +++ b/resources/commons/pplog.py @@ -0,0 +1,69 @@ +#!/usr/bin/python + +import string +from sys import stdout,stdin,argv,exit + +if len(argv) > 1 and argv[1] in ['-h', '-?', '--h', '--help']: + print 'pplog.py pretty prints Open Daylight log lines, useful for lines containing large nested objects' + print 'Usage: Simply pipe the lines you are interested through this script' + exit(0) + +line_num = 0 +def nl(): + global line_num + line_num += 1 + stdout.write('\n' + i) + +for l in stdin: + if '|' not in l: continue + + (t, level, component, cat, art, msg) = string.split(l, '|', maxsplit=5) + + I = ' ' + opener = '[{<' + closer = ']}>' + i = '' + + in_ws = 1 + is_empty_list = 0 + last_char = '' + title = '' + title_stack = [] + + for c in msg: + if in_ws and c in ' \t': continue + in_ws = 0 + + if c in closer: + i = i[:-2] + if not is_empty_list and last_char not in closer: nl() + in_ws = 1 + is_empty_list = 0 + title = '' + elif is_empty_list: + is_empty_list = 0 + nl() + + if last_char in closer and c != ',': nl() + + stdout.write(c) + if not c in opener: title += c + last_char = c + + if c in closer: + if len(title_stack): + (t,ln) = title_stack.pop() + if (line_num - ln) > 5: stdout.write(' /* ' + t.strip() + ' */') + + if c in opener: + i += I + in_ws = 1 + is_empty_list = 1 + if title: + title_stack.append((title, line_num)) + title = '' + + if c == ',': + nl() + in_ws = 1 + title = '' diff --git a/resources/commons/readable_flows.py b/resources/commons/readable_flows.py new file mode 100755 index 000000000..43901844a --- /dev/null +++ b/resources/commons/readable_flows.py @@ -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 = '.*(?Ptable=\d*), (?Pn_packets=\d*, n_bytes=\d*), (?Ppriority=\d*)?,?(?P.*)' +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[0-9a-fA-F]*)', tun_match_to_decimal, line) + line = re.sub('0x(?P[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) + -- 2.36.6