Add Trunks and duplicate flow analysis 55/70155/4
authorVishal Thapar <vishal.thapar@ericsson.com>
Wed, 28 Mar 2018 06:52:52 +0000 (12:22 +0530)
committerSam Hague <shague@redhat.com>
Thu, 12 Apr 2018 13:22:28 +0000 (13:22 +0000)
Change-Id: Ieb3cadec7608e58153e0840f4abfa466564c4782
Signed-off-by: Vishal Thapar <vishal.thapar@ericsson.com>
resources/tools/odl/netvirt/constants.py
resources/tools/odl/netvirt/ds_analyze.py
resources/tools/odl/netvirt/ds_get_data.py
resources/tools/odl/netvirt/flow_parser.py
resources/tools/odl/netvirt/netvirt_utils.py

index b4286bce424d3571ddf0001a90bebc2c67a0f65c..691e475110e735117341c20cb965927a77e4ce80 100644 (file)
@@ -38,6 +38,8 @@ DSMAP = {
                       'id-pools', 'id-pool'],
     'ifconfig': ['iface-config.log', 'config', 'ietf-interfaces:interfaces',
                  'interfaces', 'interface'],
+    'itmconfig': ['itm-config.log', 'config' 'itm-state:dpn-teps-state',
+                  'dpn-teps-state', 'dpns-teps'],
     'ifindexes': ['ifindexes.log', 'operational',
                   'odl-interface-meta:if-indexes-interface-map',
                   'if-indexes-interface-map', 'if-index-interface'],
@@ -52,11 +54,13 @@ DSMAP = {
                           'neutronvpn:neutron-vpn-portip-port-data',
                           'neutron-vpn-portip-port-data',
                           'vpn-portip-to-port'],
-    'tunconfig': ['tunnel-config.log', 'config', 'itm-state:tunnel-list',
-                  'tunnel-list', 'internal-tunnel'],
+    'neutrontrunks': ['neutron-trunks.log', 'config', 'neutron:neutron/trunks',
+                     'trunks', 'trunk'],
     'tunconfig-external': ['tunnel-config-external.log', 'config',
                            'itm-state:external-tunnel-list',
                            'external-tunnel-list', 'external-tunnel'],
+    'tunconfig': ['tunnel-config.log', 'config', 'itm-state:tunnel-list',
+                  'tunnel-list', 'internal-tunnel'],
     'tunstate': ['tunnel-state.log', 'operational', 'itm-state:tunnels_state',
                  'tunnels_state', 'state-tunnel-list'],
     'vpninstance-to-vpnid': ['vpninstance-to-vpnid.log', 'config',
index b182ef3d286f0f64531f0a1b765361e9cdafeb96..b0f211d8b839384b1b3e26b941bf02906d1a9f12 100644 (file)
@@ -1,10 +1,9 @@
-import collections
 import constants as const
 import ds_get_data as dsg
 import flow_parser as fp
 import json
 import netvirt_utils as utils
-import ovs_get_data as og
+from collections import defaultdict
 
 
 # Required
@@ -24,10 +23,10 @@ def by_ifname(ifname):
     port = None
     tunnel = None
     tunState = None
-    if iface.get('type') == const.IFTYPE_VLAN:
+    if iface and iface.get('type') == const.IFTYPE_VLAN:
         ports = dsg.get_neutron_ports()
         port = ports.get(ifname)
-    elif iface.get('type') == const.IFTYPE_TUNNEL:
+    elif iface and iface.get('type') == const.IFTYPE_TUNNEL:
         tunnels = dsg.get_config_tunnels()
         tunnel = tunnels.get(ifname)
         tunStates = dsg.get_tunnel_states()
@@ -44,13 +43,13 @@ def print_keys():
 
 
 def analyze_interface(ifname=None):
-    global ifaces,ifstates
-    ifaces = dsg.get_config_interfaces()
-    ifstates = dsg.get_interface_states()
+    global ifaces, ifstates
     if not ifname:
         print_keys()
         exit(1)
     ifname = ifname[0]
+    ifaces = dsg.get_config_interfaces()
+    ifstates = dsg.get_interface_states()
     iface,ifstate,port,tunnel,tunState = by_ifname(ifname)
     print "InterfaceConfig: "
     utils.pretty_print(iface)
@@ -124,7 +123,7 @@ def get_dpn_host_mapping(oper_nodes=None):
 def get_groups(ofnodes=None):
     of_nodes = ofnodes or dsg.get_inventory_config()
     key ='group-id'
-    group_dict = collections.defaultdict(dict)
+    group_dict = defaultdict(dict)
     for node in of_nodes.itervalues():
         dpnid = utils.get_dpn_from_ofnodeid(node['id'])
         for group in node[const.NODE_GROUP]:
@@ -245,7 +244,7 @@ def show_stale_flows(sort_by='table'):
         #print 'Flow:', json.dumps(parse_flow(flow['flow']))
 
 
-def get_all_flows(modules=['ifm']):
+def get_all_flows(modules=['ifm'], filter=[]):
     if not modules:
         return 'No modules specified'
     ifaces = {}
@@ -306,15 +305,16 @@ def get_all_flows(modules=['ifm']):
                                         ifaces, ifstates, ifindexes,
                                         fibentries, vpnids, vpninterfaces,
                                         einsts, eifaces)
-                if flow_dict is not None:
+                if (flow_dict is not None and
+                        utils.filter_flow(flow_dict, filter)):
                     flows.append(flow_dict)
     return flows
 
 
-def show_all_flows():
+def show_flows(modules=['ifm'], sort_by='table', filter_by=[]):
     compute_map = get_dpn_host_mapping()
     nports = dsg.get_neutron_ports()
-    for flow in utils.sort(get_all_flows(['all']), 'table'):
+    for flow in utils.sort(get_all_flows(modules, filter_by), sort_by):
         host = compute_map.get(flow.get('dpnid'), flow.get('dpnid'))
         ip_list = get_ips_for_iface(nports, flow.get('ifname'))
         if ip_list:
@@ -323,13 +323,77 @@ def show_all_flows():
             flow['table'], host, flow['id'],
             utils.show_optionals(flow))
         print result
-        #print 'Flow:', json.dumps(parse_flow(flow['flow']))
+        print 'Flow:', json.dumps(parse_flow(flow['flow']))
+
+
+def show_all_flows():
+    show_flows(modules=['all'])
 
 
 def show_elan_flows():
-    for flow in utils.sort(get_stale_flows(['elan']), 'table'):
-        print 'Table:', flow['table'], 'FlowId:', flow['id'], utils.show_optionals(flow)
-        print 'Flow:', json.dumps(parse_flow(flow['flow']))
+    compute_map = get_dpn_host_mapping()
+    for flow in utils.sort(get_all_flows(['elan']), 'id'):
+        host = compute_map.get(flow.get('dpnid'), flow.get('dpnid'))
+        result = 'MacHost:{}{},Table:{},FlowId:{},{},Flow:{}'.format(flow['id'][-17:],host,flow['table'],flow['id'],utils.show_optionals(flow),json.dumps(parse_flow(flow['flow'])))
+        print result
+        #print 'Flow:', json.dumps(parse_flow(flow['flow']))
+
+
+def get_matchstr(flow):
+    if flow and flow.get('flow') and flow.get('flow').get('match'):
+        return json.dumps(flow.get('flow').get('match', None))
+
+
+def get_key_for_dup_detect(flow):
+    result = '{}:{}:{}'.format(flow.get('dpnid'), flow.get('table'), get_matchstr(flow))
+    return result
+
+
+def show_dup_flows():
+    mmac = dsg.get_mip_mac()
+    einsts = dsg.get_elan_instances()
+    flows = utils.sort(get_all_flows(['elan']), 'table')
+    matches = defaultdict(list)
+    compute_map = get_dpn_host_mapping()
+    for flow in flows:
+        dup_key = get_key_for_dup_detect(flow)
+        if dup_key:
+            if matches and matches.get(dup_key):
+                matches[dup_key].append(flow)
+            else:
+                matches[dup_key].append(flow)
+    for k, v in matches.iteritems():
+        if len(v) > 1:
+            dpnid = k.split(':')[0]
+            host = compute_map.get(dpnid, dpnid)
+            result = 'Host:{},FlowCount:{},MatchKey:{},ElanTag:{}'.format(host, len(v), k,v[0].get('elan-tag'))
+            print result
+            for idx, flow in enumerate(v):
+                result = "Duplicate"
+                mac_addr = flow.get('dst-mac')
+                if mac_addr and mmac.get(mac_addr):
+                    result = fp.is_correct_elan_flow(flow, mmac.get(mac_addr), einsts, host)
+                print '    {}Flow-{}:{}'.format(result, idx, json.dumps(parse_flow(flow.get('flow'))))
+
+
+def show_learned_mac_flows():
+    nports = dsg.get_neutron_ports(key_field='mac-address')
+    flows = utils.sort(get_all_flows(['elan']), 'table')
+    compute_map = get_dpn_host_mapping()
+    for flow_info in flows:
+        flow = flow_info.get('flow')
+        dpnid = flow_info.get('dpnid')
+        host = compute_map.get(dpnid, dpnid)
+        if ((flow_info.get('table') == 50 and
+                flow.get('idle-timeout') == 300 and not
+                nports.get(flow_info.get('src-mac'))) or
+                (flow_info.get('table') == 51 and
+                 not nports.get(flow_info.get('dst-mac')))):
+            result = 'Table:{},Host:{},FlowId:{}{}'.format(
+                flow_info.get('table'), host, flow.get('id'),
+                utils.show_optionals(flow_info))
+            print result
+            print 'Flow:{}'.format(json.dumps(parse_flow(flow)))
 
 
 def show_elan_instances():
@@ -360,13 +424,22 @@ def get_duplicate_ids():
 
 
 def show_idpools():
+    ports = dsg.get_neutron_ports()
+    iface_ids = []
     for k,v in get_duplicate_ids().iteritems():
         result = "Id:{},Keys:{}".format(k, json.dumps(v.get('id-keys')))
         if v.get('pool-name'):
             result = "{},Pool:{}".format(result, v.get('pool-name'))
+            if v.get('pool-name') == 'interfaces':
+                iface_ids.extend(v.get('id-keys'))
         if v.get('parent-pool-name'):
             result = "{},ParentPool:{}".format(result, v.get('parent-pool-name'))
         print result
+    print "\nNeutron Ports"
+    print "============="
+    for id in iface_ids:
+        port = ports.get(id, {})
+        print "Iface={}, NeutronPort={}".format(id, json.dumps(port))
 
 
 def parse_flow(flow):
@@ -422,6 +495,66 @@ def show_all_groups():
             print 'Dpn:', dpn, 'ID:', group_key, 'Group:', json.dumps(groups[dpn][group_key])
 
 
+def analyze_trunks():
+    nports = dsg.get_neutron_ports()
+    ntrunks = dsg.get_neutron_trunks()
+    vpninterfaces = dsg.get_vpninterfaces()
+    ifaces = dsg.get_config_interfaces()
+    ifstates = dsg.get_interface_states()
+    subport_dict = {}
+    for v in ntrunks.itervalues():
+        nport = nports.get(v.get('port-id'))
+        s_subports = []
+        for subport in v.get('sub-ports'):
+            sport_id = subport.get('port-id')
+            snport = nports.get(sport_id)
+            svpniface = vpninterfaces.get(sport_id)
+            siface = ifaces.get(sport_id)
+            sifstate = ifstates.get(sport_id)
+            subport['SubNeutronPort'] = 'Correct' if snport else 'Wrong'
+            subport['SubVpnInterface'] = 'Correct' if svpniface else 'Wrong'
+            subport['ofport'] = utils.get_ofport_from_ncid(sifstate.get('lower-layer-if')[0]) 
+            if siface:
+                vlan_mode = siface.get('odl-interface:l2vlan-mode')
+                parent_iface_id = siface.get('odl-interface:parent-interface')
+                if vlan_mode !='trunk-member':
+                    subport['SubIface'] = 'WrongMode'
+                elif parent_iface_id !=v.get('port-id'):
+                    subport['SubIface'] = 'WrongParent'
+                elif siface.get('odl-interface:vlan-id') !=subport.get('segmentation-id'):
+                    subport['SubIface'] = 'WrongVlanId'
+                else:
+                    subport['SubIface'] = 'Correct'
+            else:
+                subport['SubIface'] = 'Wrong'
+            s_subport = 'SegId:{},PortId:{},SubNeutronPort:{},SubIface:{},SubVpnIface:{}'.format(
+                    subport.get('segmentation-id'),subport.get('port-id'),
+                    subport.get('SubNeutronPort'),
+                    subport.get('SubIface'),
+                    subport.get('SubVpnInterface'))
+            s_subports.append(subport)
+            subport_dict[subport['port-id']] = subport
+        s_trunk = 'TrunkName:{},TrunkId:{},PortId:{},NeutronPort:{},SubPorts:{}'.format(
+                v.get('name'), v.get('uuid'), v.get('port-id'),
+                'Correct' if nport else 'Wrong', json.dumps(s_subports))
+        print s_trunk
+    print '\n------------------------------------'
+    print   'Analyzing Flow status for SubPorts'
+    print   '------------------------------------'
+    for flow in utils.sort(get_all_flows(['ifm'], ['vlanid']), 'ifname'):
+        subport = subport_dict.get(flow.get('ifname')) or None
+        vlanid = subport.get('segmentation-id') if subport else None
+        ofport = subport.get('ofport') if subport else None
+        flow_status = 'Okay'
+        if flow.get('ofport') and flow.get('ofport') != ofport:
+            flow_status = 'OfPort mismatch for SubPort:{} and Flow:{}'.format(subport, flow.get('flow'))
+        if flow.get('vlanid') and flow.get('vlanid') != vlanid:
+            flow_status = 'VlanId mismatch for SubPort:{} and Flow:{}'.format(subport, flow.get('flow'))
+        if subport:
+            print 'SubPort:{},Table:{},FlowStatus:{}'.format(
+                    subport.get('port-id'), flow.get('table'), flow_status)
+
+
 def main(args=None):
     options, args = utils.parse_args()
     if options.callMethod:
@@ -436,7 +569,7 @@ def main(args=None):
     #analyze_inventory('openflow:165862438671169',ifName='tunf94333cc491')
     #show_stale_flows()
     #show_stale_bindings()
-    analyze_interface(args)
+    analyze_interface([args[1]])
     #og.print_flow_dict(og.get_ofctl_flows())
 
 
index c929901c0d651cd96ce7d5748ebd2d742657bf9c..cba305e1829af9ba1cb2d91284d04f4c05b07043 100644 (file)
@@ -32,14 +32,31 @@ def get_config_interfaces(file_name=None):
     return if_dict
 
 
-def get_neutron_ports(file_name=None):
+def get_itm_config_interfaces(file_name=None):
+    tun_dict = {}
+    tunifaces = get_ds_data('itmconfig',file_name)
+    for sourcedpn in tunifaces:
+        for remotedpn in sourcedpn['remote-dpns']:
+            tun_dict[remotedpn['tunnel-name']] = remotedpn
+    return tun_dict
+
+
+def get_neutron_ports(file_name=None, key_field='uuid'):
     port_dict = {}
     ports = get_ds_data('neutronports',file_name)
     for port in ports:
-        port_dict[port['uuid']] = port
+        port_dict[port[key_field]] = port
     return port_dict
 
 
+def get_neutron_trunks(file_name=None):
+    trunk_dict = {}
+    trunks = get_ds_data('neutrontrunks',file_name)
+    for trunk in trunks:
+        trunk_dict[trunk['uuid']] = trunk
+    return trunk_dict
+
+
 def get_interface_states(file_name=None):
     ifs_dict = {}
     ifstates = get_ds_data('ifstate',file_name)
@@ -104,6 +121,7 @@ def get_inventory_nodes(file_name, dsType = 'config'):
     return nodes_dict
 
 
+
 def get_inventory_config(file_name=None):
     return get_inventory_nodes(file_name)
 
@@ -182,3 +200,16 @@ def get_idpools(filename=None):
     for idpool in idpools:
         idpools_dict[idpool['pool-name']] = idpool
     return idpools_dict
+
+
+def get_mip_mac(filename='mip-mac.log'):
+    mmac_dict = collections.defaultdict(dict)
+    try:
+        with open(filename) as data_file:
+            data = json.load(data_file)
+    except IOError:
+        data = []
+    for entry in data:
+        entry['mac'] = entry['mac'].lower()
+        mmac_dict[entry.get('mac')][entry.get('network-id')] = entry
+    return mmac_dict
index 8fc095f69943c5b49bb4c8d45c8cc418e6b66507..bb632d3130c26b7b6b4710f6de6ab2ebdffc5f4b 100644 (file)
@@ -2,7 +2,8 @@ import netvirt_utils as utils
 import constants as const
 
 
-OPTIONALS = ['ifname', 'lport', 'elan-tag', 'mpls', 'vpnid', 'reason']
+OPTIONALS = ['ifname', 'lport', 'elan-tag', 'mpls', 'vpnid', 'reason',
+             'dst-mac', 'src-mac', 'ofport', 'vlanid']
 MAC_LEN = 17
 
 # Flow table constants
@@ -114,7 +115,8 @@ def get_any_flow(flow, flow_info, groups, ifaces, ifstates, ifindexes,
 def stale_ifm_flow(flow, flow_info, ifaces, ifstates):
     get_flow_info_from_ifm_table(flow_info, flow)
     flow_ifname = flow_info['ifname']
-    if flow_ifname is not None and not ifaces.get(flow_ifname):
+    iface = ifaces.get(flow_ifname)
+    if flow_ifname is not None and not iface:
         flow_info['reason'] = 'Interface doesnt exist'
         return create_flow_dict(flow_info, flow)
     elif flow_ifname and ifstates.get(flow_ifname):
@@ -129,6 +131,13 @@ def stale_ifm_flow(flow, flow_info, ifaces, ifstates):
                 and flow_info['lport'] != ifstate['if-index']):
             flow_info['reason'] = 'Lport and IfIndex mismatch'
             return create_flow_dict(flow_info, flow)
+        if (flow_info.get('ofport') and ifstate.get('lower-layer-if')
+                and flow_info['ofport'] != utils.get_ofport_from_ncid(
+                    ifstate.get('lower-layer-if')[0])):
+            flow_info['reason'] = 'OfPort mismatch'
+        if (flow_info.get('vlanid') and iface.get('odl-interface:vlan-id')
+                and flow_info['vlanid'] != iface.get('odl-interface:vlan-id')):
+            flow_info['reason'] = 'VlanId mismatch'
     return None
     # return create_flow_dict(flow_info, flow)
 
@@ -196,14 +205,14 @@ def stale_acl_flow(flow, flow_info, ifaces, ifindexes, einsts, eifaces):
     iface = get_iface_for_lport(ifaces, ifindexes, lport)
     if lport and not iface:
             flow_info['reason'] = 'Interface for lport not found'
-            #return create_flow_dict(flow_info, flow)
+            return create_flow_dict(flow_info, flow)
     if iface:
         flow_info['ifname'] = iface['name']
     if not is_elantag_valid(eltag, eifaces, einsts, iface):
         flow_info['reason'] = 'Lport Elantag mismatch'
-        #return create_flow_dict(flow_info, flow)
-    return create_flow_dict(flow_info, flow)
-    #return None
+        return create_flow_dict(flow_info, flow)
+    #return create_flow_dict(flow_info, flow)
+    return None
 
 
 def is_elantag_valid(eltag, eifaces, einsts, iface):
@@ -213,6 +222,27 @@ def is_elantag_valid(eltag, eifaces, einsts, iface):
     return True
 
 
+def is_correct_elan_flow(flow_info, mmac, einsts, flow_host):
+    flow = flow_info.get('flow')
+    flow_etag = flow_info.get('elan-tag')
+    for k, v in mmac.iteritems():
+        mac_host = v.get('compute')
+        if einsts.get(k):
+            einst_tag = einsts.get(k).get('elan-tag')
+            #print einst_tag, flow_etag, mac_host
+            if flow_etag and einst_tag and flow_etag == einst_tag:
+                if mac_host.startswith(flow_host):
+                    act_resubmit = get_act_resubmit(flow)
+                    if (act_resubmit and act_resubmit.get('table') == 220):
+                        return 'Correct'
+                else:
+                    act_tunnel = get_act_set_tunnel(flow)
+                    if act_tunnel:
+                        return 'Correct'
+                return 'Wrong'
+    return 'Wrong'
+
+
 def get_iface_for_lport(ifaces, ifindexes, lport):
     if lport:
         if ifindexes.get(lport):
@@ -267,6 +297,40 @@ def get_act_group(flow):
                     return action['group-action']
 
 
+def get_act_set_tunnel(flow):
+    for instruction in flow['instructions'].get('instruction', []):
+        if 'apply-actions' in instruction:
+            for action in instruction['apply-actions'].get('action', []):
+                if 'set-field' in action and 'tunnel' in action.get('set-field'):
+                    return action.get('set-field').get('tunnel')
+
+
+def get_act_resubmit(flow):
+    for instruction in flow['instructions'].get('instruction', []):
+        if 'apply-actions' in instruction:
+            for action in instruction['apply-actions'].get('action', []):
+                if ('openflowplugin-extension-nicira-action:nx-resubmit'
+                        in action):
+                    return action[
+                        'openflowplugin-extension-nicira-action:nx-resubmit']
+
+
+def get_act_set_vlanid(flow):
+    for instruction in flow['instructions'].get('instruction', []):
+        if 'apply-actions' in instruction:
+            for action in instruction['apply-actions'].get('action', []):
+                if 'set-field' in action and 'vlan-match' in action.get('set-field'):
+                    return action.get('set-field').get('vlan-match').get('vlan-id')
+
+
+def get_act_output(flow):
+    for instruction in flow['instructions'].get('instruction', []):
+        if 'apply-actions' in instruction:
+            for action in instruction['apply-actions'].get('action', []):
+                if 'output-action' in action and 'output-node-connector' in action.get('output-action'):
+                    return action.get('output-action')
+
+
 def get_match_metadata(flow):
     return flow['match'].get('metadata')
 
@@ -295,6 +359,30 @@ def get_match_tunnelid(flow):
     return None
 
 
+def get_match_ether_dest(flow):
+    if flow.get('match').get('ethernet-match') and flow['match'].get('ethernet-match').get('ethernet-destination'):
+        return flow['match'].get('ethernet-match').get('ethernet-destination')
+    return None
+
+
+def get_match_ether_src(flow):
+    if flow.get('match').get('ethernet-match') and flow['match'].get('ethernet-match').get('ethernet-source'):
+        return flow['match'].get('ethernet-match').get('ethernet-source')
+    return None
+
+
+def get_match_vlanid(flow):
+    if flow.get('match').get('vlan-match') and flow['match'].get('vlan-match').get('vlan-id'):
+        return flow['match'].get('vlan-match').get('vlan-id')
+    return None
+
+
+def get_match_inport(flow):
+    if flow.get('match').get('in-port'):
+        return flow['match'].get('in-port')
+    return None
+
+
 def get_flow_info_from_any(flow_info, flow):
     w_mdata = get_instruction_writemeta(flow)
     if w_mdata:
@@ -316,6 +404,12 @@ def get_flow_info_from_any(flow_info, flow):
             lport = ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
             if lport:
                 flow_info['lport'] = int(lport, 16)
+    m_ether_dest = get_match_ether_dest(flow)
+    if m_ether_dest and m_ether_dest.get('address'):
+        flow_info['dst-mac'] = m_ether_dest.get('address').lower()
+    m_ether_src = get_match_ether_src(flow)
+    if m_ether_src and m_ether_src.get('address'):
+        flow_info['src-mac'] = m_ether_src.get('address').lower()
     return flow_info
 
 # Table specific parsing
@@ -343,12 +437,25 @@ def get_flow_info_from_ifm_table(flow_info, flow):
         if (mask & LPORT_MASK):
             lport = ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
             flow_info['lport'] = int(lport, 16)
-            return flow_info
     m_reg6 = get_match_reg6(flow)
-    if m_reg6 and m_reg6.get('value'):
+    if not flow.get('lport') and m_reg6 and m_reg6.get('value'):
         lport = (('%x' % (m_reg6.get('value') & LPORT_REG6_MASK))
                  [:-LPORT_REG6_MASK_ZLEN])
         flow_info['lport'] = int(lport, 16)
+    if flow['table_id'] == 0:
+        m_inport = get_match_inport(flow)
+        if m_inport:
+            flow_info['ofport'] = utils.get_ofport_from_ncid(m_inport)
+        m_vlan = get_match_vlanid(flow)
+        if m_vlan and m_vlan.get('vlan-id'):
+            flow_info['vlanid'] = m_vlan.get('vlan-id')
+    elif flow['table_id'] == 220:
+        a_output = get_act_output(flow)
+        a_vlan = get_act_set_vlanid(flow)
+        if a_output and a_output.get('output-node-connector'):
+            flow_info['ofport'] = a_output.get('output-node-connector')
+        if a_vlan and a_vlan.get('vlan-id'):
+            flow_info['vlanid'] = a_vlan.get('vlan-id')
     return flow_info
 
 
@@ -391,7 +498,12 @@ def get_flow_info_from_elan_table(flow_info, flow):
         if (mask & LPORT_MASK):
             lport = ('%x' % (metadata & LPORT_MASK))[:-LPORT_MASK_ZLEN]
             flow_info['lport'] = int(lport, 16)
-
+    m_ether_dest = get_match_ether_dest(flow)
+    if m_ether_dest and m_ether_dest.get('address'):
+        flow_info['dst-mac'] = m_ether_dest.get('address').lower()
+    m_ether_src = get_match_ether_src(flow)
+    if m_ether_src and m_ether_src.get('address'):
+        flow_info['src-mac'] = m_ether_src.get('address').lower()
     if not flow_info.get('lport'):
         reg6_load = get_act_reg6load(flow)
         if reg6_load and reg6_load.get('value'):
index 9c6b92f25690b690237888f82dc2a494c27ad699..41ef19456dba3dc51d73c945a81fed679a06d306 100644 (file)
@@ -81,7 +81,11 @@ def get_port_name(port):
 
 
 def get_dpn_from_ofnodeid(node_id):
-    return node_id.split(':')[1]
+    return node_id.split(':')[1] if node_id else 'none'
+
+
+def get_ofport_from_ncid(ncid):
+    return ncid.split(':')[2] if ncid and ncid.startswith('openflow') else 0
 
 
 def to_hex(data, ele=None):
@@ -99,6 +103,15 @@ def sort(data, field):
     return sorted(data, key=lambda x: x[field])
 
 
+def filter_flow(flow_dict, filter_list):
+    if not filter_list:
+        return True
+    for flow_filter in filter_list:
+        if flow_dict.get(flow_filter):
+            return True
+    return False
+
+
 def show_optionals(flow):
     result = ''
     lport = flow.get('lport')
@@ -106,14 +119,26 @@ def show_optionals(flow):
     label = flow.get('mpls')
     vpnid = flow.get('vpnid')
     ip = flow.get('iface-ips')
+    smac = flow.get('src-mac')
+    dmac = flow.get('dst-mac')
+    vlanid = flow.get('vlanid')
+    ofport = flow.get('ofport')
     if lport:
         result = '{},LportTag:{}/{}'.format(result, lport, to_hex(lport))
+    if ofport:
+        result = '{},OfPort:{}'.format(result, ofport)
+    if vlanid:
+        result = '{},VlanId:{}'.format(result, vlanid)
     if vpnid:
         result = '{},VpnId:{}/{}'.format(result, vpnid, to_hex(vpnid*2))
     if label:
         result = '{},MplsLabel:{}'.format(result, label)
     if elantag:
         result = '{},ElanTag:{}/{}'.format(result, elantag, to_hex(elantag))
+    if smac:
+        result = '{},SrcMac:{}'.format(result, smac)
+    if dmac:
+        result = '{},DstMac:{}'.format(result, dmac)
     if ip:
         result = '{},LportIp:{}'.format(result, json.dumps(ip))
     result = '{},Reason:{}'.format(result, flow.get('reason'))