pvl.hosts-graph: --graph-bridge: trivial elimination of linked ports
authorTero Marttila <terom@paivola.fi>
Mon, 31 Mar 2014 17:14:51 +0300
changeset 411 9ac5dae42adb
parent 410 afe9cc8032ec
child 412 b557b472ffa1
pvl.hosts-graph: --graph-bridge: trivial elimination of linked ports
bin/pvl.hosts-graph
--- a/bin/pvl.hosts-graph	Mon Mar 31 16:36:45 2014 +0300
+++ b/bin/pvl.hosts-graph	Mon Mar 31 17:14:51 2014 +0300
@@ -182,6 +182,8 @@
     hosts_by_ethernet = { } # ethernet: host
     hosts_by_location = { } # (domain, location): host
 
+    nodes_port = { } # (local, int(local_port)): remote
+    nodes_out = { } # local: {remote}
     links_out = { } # (local, remote): local_port
     links_in = { } # (remote, local): remote_port
     
@@ -258,6 +260,8 @@
 
                     # directional mapping
                     links_out[(local_node, remote_node)] = local_port
+                    nodes_port[(local_node, port)] = remote_node
+                    nodes_out.setdefault(local_node, set()).add(remote_node)
                     
                     # bidirectional mappings
                     remote_port = remote_attrs['port']
@@ -287,63 +291,6 @@
 
                         links[reverse] = (remote_untag, remote_tagged, local_untag)
 
-        # second graph: bridge
-        if not options.graph_bridge :
-            bridge = { }
-        elif 'bridge' in host_attrs :
-            bridge = host_attrs['bridge']
-        else :
-            bridge = { }
-
-        for port, ethernets in bridge.iteritems() :
-            local_node = host
-            local_port = port
-
-            for ethernet in ethernets :
-                if ethernet in hosts_by_ethernet :
-                    remote_node = remote_host = hosts_by_ethernet[ethernet]
-
-                    remote_label = remote_host.location or str(remote_host)
-
-                    log.info("%s:%s bridge %s = %s (%s)", host, port, ethernet, remote_host, remote_label)
-                else :
-                    remote_node = remote_host = None
-                
-                if not remote_host :
-                    continue
-
-                if remote_node not in nodes :
-                    log.debug("%s:%s: lazy-add remote %s (%s)", host, port, remote_node, remote_label)
-
-                    nodes[remote_node] = remote_label
-                
-                # unknown vlans
-                link_vlans = None
-
-                # directional link
-                links_out[(local_node, remote_node)] = local_port
-                
-                # bidirectional link
-                forward = (local_node, local_port, None, remote_node)
-
-                # scan for reverse
-                remote_port = links_out.get((remote_node, local_node))
-
-                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)
-
-                    # fill in remote_port for bidirectional link
-                    del links[reverse]
-                    reverse = local_node, local_port, remote_port, remote_node
-                    links[reverse] = link_vlans
-
-                else :
-                    log.info("%s:%s -> %s", local_node, local_port, remote_host)
-
-                    links[forward] = link_vlans
-
     # second graph: manual ports
     for host in hosts :
         local_node = host
@@ -354,7 +301,12 @@
                 # XXX: copypasta
                 nodes[local_node] = host.location or str(host)
 
-            for port, remote in host_links.iteritems() :
+            for link_port, remote in host_links.iteritems() :
+                if link_port.isdigit() :
+                    port = int(link_port)
+                else :
+                    port = str(link_port)
+
                 # map remote -> remote_host
                 if '@' in remote :
                     remote_location, remote_domain = remote.split('@', 1)
@@ -389,12 +341,15 @@
                     log.debug("%s:%s: confirm -> %s", host, port, remote_host)
                 
                 links_out[(local_node, remote_node)] = port
+                nodes_port[(local_node, port)] = remote_node
+                nodes_out.setdefault(local_node, set()).add(remote_node)
 
                 # update directional or missing links
                 remote_port = links_out.get((remote_node, local_node))
                 reverse_port = links_in.get((local_node, remote_node))
 
                 if reverse_port and reverse_port != port :
+                    # XXX: this can be caused by str vs int >_>
                     log.warn("%s:%s: reverse port mismatch %s <- %s", host, port, reverse_port, remote_node)
 
 
@@ -424,6 +379,82 @@
 
                     links[(local_node, local_port, None, remote_node)] = None
 
+    if options.graph_bridge :
+        # third graph: bridge
+        for host, host_attrs in snmp.iteritems() :
+            local_node = host
+            
+            node_out = nodes_out.get(local_node)
+
+            if not node_out :
+                log.warning("%s: no outgoing links, skipping bridge", host)
+                continue
+
+            bridge = { }
+
+            for port, ethernets in host_attrs.get('bridge', { }).iteritems() :
+                bridge[(port, None)] = ethernets
+
+            for vlan, vlan_attrs in host_attrs.get('vlan', { }).iteritems() :
+                for port, ethernets in vlan_attrs.get('bridge', { }).iteritems()  :
+                    bridge[(port, vlan)] = ethernets
+
+            for (port, vlan), ethernets in bridge.iteritems() :
+                local_port = port
+                
+                remote_node = nodes_port.get((local_node, local_port))
+                
+                if remote_node :
+                    log.debug("%s:%s/%s bridge skip -> %s", host, port, vlan, remote_node)
+                    continue
+
+                for ethernet in ethernets :
+                    # remote host
+                    if ethernet in hosts_by_ethernet :
+                        remote_node = remote_host = hosts_by_ethernet[ethernet]
+
+                        remote_label = remote_host.location or str(remote_host)
+
+                        log.debug("%s:%s/%s bridge %s = %s (%s)", host, port, vlan, ethernet, remote_host, remote_label)
+                    else :
+                        remote_node = remote_host = None
+                    
+                    if not remote_host :
+                        continue
+
+                    if remote_node not in nodes :
+                        log.debug("%s:%s: lazy-add remote %s (%s)", host, port, remote_node, remote_label)
+
+                        nodes[remote_node] = remote_label
+                    
+                    # unknown vlans
+                    link_vlans = None
+
+                    # directional link
+                    links_out[(local_node, remote_node)] = local_port
+                    
+                    # bidirectional link
+                    forward = (local_node, local_port, None, remote_node)
+
+                    # scan for reverse
+                    remote_port = links_out.get((remote_node, local_node))
+
+                    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)
+
+                        # fill in remote_port for bidirectional link
+                        del links[reverse]
+                        reverse = local_node, local_port, remote_port, remote_node
+                        links[reverse] = link_vlans
+
+                    else :
+                        log.info("%s:%s -> %s", local_node, local_port, remote_host)
+
+                        links[forward] = link_vlans
+
+
 
     return nodes, links