pvl.hosts-graph: map bridge ethers from bridge nodes onto non-bridge leaf nodes
authorTero Marttila <terom@paivola.fi>
Mon, 31 Mar 2014 18:19:59 +0300
changeset 414 b9aa8c35741f
parent 413 cb7782e5a2fd
child 415 658bc929cf77
pvl.hosts-graph: map bridge ethers from bridge nodes onto non-bridge leaf nodes
bin/pvl.hosts-graph
--- a/bin/pvl.hosts-graph	Mon Mar 31 17:30:42 2014 +0300
+++ b/bin/pvl.hosts-graph	Mon Mar 31 18:19:59 2014 +0300
@@ -184,6 +184,7 @@
 
     nodes_port = { } # (local, int(local_port)): remote
     nodes_out = { } # local: {remote}
+    nodes_in = { } # remote: {local}
     links_out = { } # (local, remote): local_port
     links_in = { } # (remote, local): remote_port
     
@@ -268,6 +269,7 @@
                     links_out[(local_node, remote_node)] = local_port
                     nodes_port[(local_node, port)] = remote_node
                     nodes_out.setdefault(local_node, set()).add(remote_node)
+                    nodes_in.setdefault(remote_node, set()).add(local_node)
                     
                     # bidirectional mappings
                     remote_port = remote_attrs['port']
@@ -349,6 +351,7 @@
                 links_out[(local_node, remote_node)] = port
                 nodes_port[(local_node, port)] = remote_node
                 nodes_out.setdefault(local_node, set()).add(remote_node)
+                nodes_in.setdefault(remote_node, set()).add(local_node)
 
                 # update directional or missing links
                 remote_port = links_out.get((remote_node, local_node))
@@ -386,17 +389,23 @@
                     links[(local_node, local_port, None, remote_node)] = None
 
     if options.graph_bridge :
+        # scan hosts with bridges
+        bridge_hosts = set()
+
+        for host, host_attrs in snmp.iteritems() :
+            if 'bridge' in host_attrs or any('bridge' in vlan_attrs for vlan_attrs in host_attrs.get('vlan', { }).itervalues()) :
+                bridge_hosts.add(host)
+
         # third graph: bridge
         for host, host_attrs in snmp.iteritems() :
-            local_node = host
-            
-            node_out = nodes_out.get(local_node)
+            local_out = nodes_out.get(host)
 
-            if not node_out :
+            if not local_out :
                 log.warning("%s: no outgoing links, skipping bridge", host)
                 continue
-
-            bridge = { }
+            
+            # scan vlan/port bridge ethers
+            bridge = { } # (port, vlan): {ethernet}
 
             for port, ethernets in host_attrs.get('bridge', { }).iteritems() :
                 bridge[(port, None)] = ethernets
@@ -406,11 +415,31 @@
                     bridge[(port, vlan)] = ethernets
 
             for (port, vlan), ethernets in bridge.iteritems() :
+                local_node = host
                 local_port = port
                 
                 remote_node = nodes_port.get((local_node, local_port))
+
+                if remote_node :
+                    remote_in = nodes_in.get(remote_node, set())
+                else :
+                    remote_in = set()
                 
-                if remote_node :
+                remote_leaf = (remote_in == set((host, )))
+                
+                # TODO: add ether node and link if remote node also has this ether on this link
+                #       also do this if all remote_in's agree that the ether is on the remote_node
+                if not remote_node :
+                    log.debug("%s:%s: no remote node", host, port)
+                
+                elif remote_leaf and (remote_node not in bridge_hosts) and len(ethernets) > 1 :
+                    log.info("%s:%s: map onto non-bridge leaf remote %s", host, port, remote_node)
+
+                    # map links out of the assumed remote bridge
+                    local_node = remote_node
+                    local_port = None
+
+                else :
                     log.debug("%s:%s/%s bridge skip -> %s", host, port, vlan, remote_node)
                     continue
 
@@ -424,10 +453,15 @@
                         log.debug("%s:%s/%s bridge %s = %s (%s)", host, port, vlan, ethernet, remote_host, remote_label)
                     else :
                         remote_node = remote_host = None
+
+                        log.debug("%s:%s/%s bridge %s ?", host, port, vlan, ethernet)
                     
                     if not remote_host :
                         continue
 
+                    elif remote_host == host and local_node != host :
+                        log.debug("%s:%s: skip remote-mapped self", host, port)
+
                     if remote_node not in nodes :
                         log.debug("%s:%s: lazy-add remote %s (%s)", host, port, remote_node, remote_label)
 
@@ -448,7 +482,7 @@
                     if remote_port :
                         reverse = (remote_node, remote_port, None, local_node)
 
-                        log.info("%s:%s <-> %s:%s", local_node, local_port, remote_port, remote_host)
+                        log.info("%s:%s bridge <-> %s:%s", local_node, local_port, remote_port, remote_host)
 
                         # fill in remote_port for bidirectional link
                         del links[reverse]
@@ -456,7 +490,7 @@
                         links[reverse] = link_vlans
 
                     else :
-                        log.info("%s:%s -> %s", local_node, local_port, remote_host)
+                        log.info("%s:%s bridge -> %s", local_node, local_port, remote_host)
 
                         links[forward] = link_vlans