Scripts to pretty print ovs flows and odl logs 27/36227/2
authorJosh <jhershbe@redhat.com>
Tue, 15 Mar 2016 07:22:43 +0000 (09:22 +0200)
committerJosh <jhershbe@redhat.com>
Tue, 15 Mar 2016 07:26:55 +0000 (09:26 +0200)
Change-Id: If002fef89ed257057b9832227203c2d9e6dd3655
Signed-off-by: Josh <jhershbe@redhat.com>
resources/commons/README
resources/commons/pplog.py [new file with mode: 0755]
resources/commons/readable_flows.py [new file with mode: 0755]

index a6f6920bb62e6e30abe07ab2eea2ffae704a3e73..6aac3d3ce30da14ba33dfeadb8ef18eca80f9777 100644 (file)
@@ -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 (executable)
index 0000000..8cc4e3d
--- /dev/null
@@ -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 (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)
+