--- 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