# HG changeset patch # User Tero Marttila # Date 1396273005 -10800 # Node ID afe9cc8032ec18fe43b49a4b9a2db6f1580e8cb6 # Parent b2b1bc48819577ca619a44ebe88433f0040a8b3d pvl.hosts-graph: keep track of partial links, update reverse bridge links, implement directional/bidirectional manually configured links diff -r b2b1bc488195 -r afe9cc8032ec 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