pvl.hosts-graph: keep track of partial links, update reverse bridge links, implement directional/bidirectional manually configured links
authorTero Marttila <terom@paivola.fi>
Mon, 31 Mar 2014 16:36:45 +0300
changeset 410 afe9cc8032ec
parent 409 b2b1bc488195
child 411 9ac5dae42adb
pvl.hosts-graph: keep track of partial links, update reverse bridge links, implement directional/bidirectional manually configured links
bin/pvl.hosts-graph
--- a/bin/pvl.hosts-graph	Mon Mar 31 15:31:22 2014 +0300
+++ b/bin/pvl.hosts-graph	Mon Mar 31 16:36:45 2014 +0300
@@ -176,10 +176,14 @@
     """
 
     nodes = { } # host: label
-    links = { }
+    links = { } # (local, local_port, remote_port, remote_host): (local_untag, tagged, remote_untag)
 
     hosts_by_lldp = { } # chassis: host
     hosts_by_ethernet = { } # ethernet: host
+    hosts_by_location = { } # (domain, location): host
+
+    links_out = { } # (local, remote): local_port
+    links_in = { } # (remote, local): remote_port
     
     # first scan: lldp hosts
     for host, host_attrs in snmp.iteritems() :
@@ -195,6 +199,9 @@
         for ethernet in host.ethernet.itervalues() :
             hosts_by_ethernet[ethernet] = host
 
+        if host.location :
+            hosts_by_location[(host.domain, host.location)] = host
+
     # first graph: lldp remotes
     for host, host_attrs in snmp.iteritems() :
         local_node = host
@@ -236,7 +243,7 @@
                     
                     # ensure remote node
                     if remote_node not in nodes :
-                        log.info("%s:%s: lazy-add remote %s (%s)", host, port, remote_node, remote_label)
+                        log.debug("%s:%s: lazy-add remote %s (%s)", host, port, remote_node, remote_label)
 
                         nodes[remote_node] = remote_label
                     
@@ -248,10 +255,15 @@
 
                     if port_vlans :
                         local_untag, local_tagged = port_vlans
+
+                    # directional mapping
+                    links_out[(local_node, remote_node)] = local_port
                     
                     # bidirectional mappings
                     remote_port = remote_attrs['port']
 
+                    links_in[(remote_node, local_node)] = remote_port
+
                     forward = (local_node, local_port, remote_port, remote_node)
                     reverse = (remote_node, remote_port, local_port, local_node)
 
@@ -285,6 +297,7 @@
 
         for port, ethernets in bridge.iteritems() :
             local_node = host
+            local_port = port
 
             for ethernet in ethernets :
                 if ethernet in hosts_by_ethernet :
@@ -292,21 +305,125 @@
 
                     remote_label = remote_host.location or str(remote_host)
 
-                    log.info("%s:%s: bridge ethernet %s -> %s (%s)", host, port, ethernet, remote_host, remote_label)
+                    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.info("%s:%s: lazy-add remote %s (%s)", host, port, remote_node, remote_label)
+                    log.debug("%s:%s: lazy-add remote %s (%s)", host, port, remote_node, remote_label)
 
                     nodes[remote_node] = remote_label
                 
-                link = (host, port, None, remote_node)
-                
                 # unknown vlans
                 link_vlans = None
+
+                # directional link
+                links_out[(local_node, remote_node)] = local_port
                 
-                links[link] = link_vlans
+                # 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
+        host_links = host.extensions.get('link')
+
+        if host_links :
+            if local_node not in nodes :
+                # XXX: copypasta
+                nodes[local_node] = host.location or str(host)
+
+            for port, remote in host_links.iteritems() :
+                # map remote -> remote_host
+                if '@' in remote :
+                    remote_location, remote_domain = remote.split('@', 1)
+                else :
+                    remote_location = remote
+                    remote_domain = host.domain
+
+                remote_node = remote_host = hosts_by_location.get((remote_domain, remote_location))
+
+                if not remote_host :
+                    log.warning("%s:%s: unknown remote location: %s@%s", host, port, remote_location, remote_domain)
+                    continue
+
+                remote_label = remote_host.location or str(remote_host)
+                
+                log.info("%s:%s: link -> %s@%s (%s)", host, port, remote_host, remote_host.domain, remote_label)
+
+                if remote_node not in nodes :
+                    # XXX: copypasta
+                    nodes[remote_node] = remote_label
+            
+                # directional links
+                local_port = links_out.get((local_node, remote_node))
+                
+                if not local_port :
+                    log.info("%s:%s: unconfirmed -> %s", host, port, remote_host)
+
+                elif local_port != port :
+                    log.warn("%s:%s: port mismatch %s -> %s", host, port, local_port, remote_host)
+
+                else :
+                    log.debug("%s:%s: confirm -> %s", host, port, remote_host)
+                
+                links_out[(local_node, remote_node)] = port
+
+                # 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 :
+                    log.warn("%s:%s: reverse port mismatch %s <- %s", host, port, reverse_port, remote_node)
+
+
+                if local_port and remote_port :
+                    log.debug("%s:%s link <-> %s:%s", local_node, local_port, remote_port, remote_node)
+
+                elif local_port :
+                    # we have the forward mapping already, so this doesn't add any new info
+                    log.debug("%s:%s link -> %s:%s", local_node, local_port, remote_port, remote_node)
+
+                elif remote_port and reverse_port :
+                    # we have the bidirectional mapping already, so this doesn't add any new info
+                    log.debug("%s:%s link <-> %s:%s", local_node, local_port, remote_port, remote_node)
+
+                elif remote_port :
+                    # we had the reverse mapping already, make it bidirectional
+                    local_port = port
+                    log.info("%s:%s link <- %s:%s", local_node, local_port, remote_port, remote_node)
+
+                    links[(remote_node, remote_port, local_port, local_node)] = links.pop((remote_node, remote_port, None, local_node))
+                
+                else :
+                    local_port = port
+
+                    # mapping was completely missing
+                    log.warn("%s:%s link -> %s", local_node, local_port, remote_node)
+
+                    links[(local_node, local_port, None, remote_node)] = None
+
 
     return nodes, links