182 hosts_by_ethernet = { } # ethernet: host |
182 hosts_by_ethernet = { } # ethernet: host |
183 hosts_by_location = { } # (domain, location): host |
183 hosts_by_location = { } # (domain, location): host |
184 |
184 |
185 nodes_port = { } # (local, int(local_port)): remote |
185 nodes_port = { } # (local, int(local_port)): remote |
186 nodes_out = { } # local: {remote} |
186 nodes_out = { } # local: {remote} |
|
187 nodes_in = { } # remote: {local} |
187 links_out = { } # (local, remote): local_port |
188 links_out = { } # (local, remote): local_port |
188 links_in = { } # (remote, local): remote_port |
189 links_in = { } # (remote, local): remote_port |
189 |
190 |
190 # first scan: lldp hosts |
191 # first scan: lldp hosts |
191 for host, host_attrs in snmp.iteritems() : |
192 for host, host_attrs in snmp.iteritems() : |
266 |
267 |
267 # directional mapping |
268 # directional mapping |
268 links_out[(local_node, remote_node)] = local_port |
269 links_out[(local_node, remote_node)] = local_port |
269 nodes_port[(local_node, port)] = remote_node |
270 nodes_port[(local_node, port)] = remote_node |
270 nodes_out.setdefault(local_node, set()).add(remote_node) |
271 nodes_out.setdefault(local_node, set()).add(remote_node) |
|
272 nodes_in.setdefault(remote_node, set()).add(local_node) |
271 |
273 |
272 # bidirectional mappings |
274 # bidirectional mappings |
273 remote_port = remote_attrs['port'] |
275 remote_port = remote_attrs['port'] |
274 |
276 |
275 links_in[(remote_node, local_node)] = remote_port |
277 links_in[(remote_node, local_node)] = remote_port |
347 log.debug("%s:%s: confirm -> %s", host, port, remote_host) |
349 log.debug("%s:%s: confirm -> %s", host, port, remote_host) |
348 |
350 |
349 links_out[(local_node, remote_node)] = port |
351 links_out[(local_node, remote_node)] = port |
350 nodes_port[(local_node, port)] = remote_node |
352 nodes_port[(local_node, port)] = remote_node |
351 nodes_out.setdefault(local_node, set()).add(remote_node) |
353 nodes_out.setdefault(local_node, set()).add(remote_node) |
|
354 nodes_in.setdefault(remote_node, set()).add(local_node) |
352 |
355 |
353 # update directional or missing links |
356 # update directional or missing links |
354 remote_port = links_out.get((remote_node, local_node)) |
357 remote_port = links_out.get((remote_node, local_node)) |
355 reverse_port = links_in.get((local_node, remote_node)) |
358 reverse_port = links_in.get((local_node, remote_node)) |
356 |
359 |
384 log.info("%s:%s link -> %s", local_node, local_port, remote_node) |
387 log.info("%s:%s link -> %s", local_node, local_port, remote_node) |
385 |
388 |
386 links[(local_node, local_port, None, remote_node)] = None |
389 links[(local_node, local_port, None, remote_node)] = None |
387 |
390 |
388 if options.graph_bridge : |
391 if options.graph_bridge : |
|
392 # scan hosts with bridges |
|
393 bridge_hosts = set() |
|
394 |
|
395 for host, host_attrs in snmp.iteritems() : |
|
396 if 'bridge' in host_attrs or any('bridge' in vlan_attrs for vlan_attrs in host_attrs.get('vlan', { }).itervalues()) : |
|
397 bridge_hosts.add(host) |
|
398 |
389 # third graph: bridge |
399 # third graph: bridge |
390 for host, host_attrs in snmp.iteritems() : |
400 for host, host_attrs in snmp.iteritems() : |
391 local_node = host |
401 local_out = nodes_out.get(host) |
392 |
402 |
393 node_out = nodes_out.get(local_node) |
403 if not local_out : |
394 |
|
395 if not node_out : |
|
396 log.warning("%s: no outgoing links, skipping bridge", host) |
404 log.warning("%s: no outgoing links, skipping bridge", host) |
397 continue |
405 continue |
398 |
406 |
399 bridge = { } |
407 # scan vlan/port bridge ethers |
|
408 bridge = { } # (port, vlan): {ethernet} |
400 |
409 |
401 for port, ethernets in host_attrs.get('bridge', { }).iteritems() : |
410 for port, ethernets in host_attrs.get('bridge', { }).iteritems() : |
402 bridge[(port, None)] = ethernets |
411 bridge[(port, None)] = ethernets |
403 |
412 |
404 for vlan, vlan_attrs in host_attrs.get('vlan', { }).iteritems() : |
413 for vlan, vlan_attrs in host_attrs.get('vlan', { }).iteritems() : |
405 for port, ethernets in vlan_attrs.get('bridge', { }).iteritems() : |
414 for port, ethernets in vlan_attrs.get('bridge', { }).iteritems() : |
406 bridge[(port, vlan)] = ethernets |
415 bridge[(port, vlan)] = ethernets |
407 |
416 |
408 for (port, vlan), ethernets in bridge.iteritems() : |
417 for (port, vlan), ethernets in bridge.iteritems() : |
|
418 local_node = host |
409 local_port = port |
419 local_port = port |
410 |
420 |
411 remote_node = nodes_port.get((local_node, local_port)) |
421 remote_node = nodes_port.get((local_node, local_port)) |
412 |
422 |
413 if remote_node : |
423 if remote_node : |
|
424 remote_in = nodes_in.get(remote_node, set()) |
|
425 else : |
|
426 remote_in = set() |
|
427 |
|
428 remote_leaf = (remote_in == set((host, ))) |
|
429 |
|
430 # TODO: add ether node and link if remote node also has this ether on this link |
|
431 # also do this if all remote_in's agree that the ether is on the remote_node |
|
432 if not remote_node : |
|
433 log.debug("%s:%s: no remote node", host, port) |
|
434 |
|
435 elif remote_leaf and (remote_node not in bridge_hosts) and len(ethernets) > 1 : |
|
436 log.info("%s:%s: map onto non-bridge leaf remote %s", host, port, remote_node) |
|
437 |
|
438 # map links out of the assumed remote bridge |
|
439 local_node = remote_node |
|
440 local_port = None |
|
441 |
|
442 else : |
414 log.debug("%s:%s/%s bridge skip -> %s", host, port, vlan, remote_node) |
443 log.debug("%s:%s/%s bridge skip -> %s", host, port, vlan, remote_node) |
415 continue |
444 continue |
416 |
445 |
417 for ethernet in ethernets : |
446 for ethernet in ethernets : |
418 # remote host |
447 # remote host |
422 remote_label = remote_host.location or str(remote_host) |
451 remote_label = remote_host.location or str(remote_host) |
423 |
452 |
424 log.debug("%s:%s/%s bridge %s = %s (%s)", host, port, vlan, ethernet, remote_host, remote_label) |
453 log.debug("%s:%s/%s bridge %s = %s (%s)", host, port, vlan, ethernet, remote_host, remote_label) |
425 else : |
454 else : |
426 remote_node = remote_host = None |
455 remote_node = remote_host = None |
|
456 |
|
457 log.debug("%s:%s/%s bridge %s ?", host, port, vlan, ethernet) |
427 |
458 |
428 if not remote_host : |
459 if not remote_host : |
429 continue |
460 continue |
|
461 |
|
462 elif remote_host == host and local_node != host : |
|
463 log.debug("%s:%s: skip remote-mapped self", host, port) |
430 |
464 |
431 if remote_node not in nodes : |
465 if remote_node not in nodes : |
432 log.debug("%s:%s: lazy-add remote %s (%s)", host, port, remote_node, remote_label) |
466 log.debug("%s:%s: lazy-add remote %s (%s)", host, port, remote_node, remote_label) |
433 |
467 |
434 nodes[remote_node] = remote_label |
468 nodes[remote_node] = remote_label |
446 remote_port = links_out.get((remote_node, local_node)) |
480 remote_port = links_out.get((remote_node, local_node)) |
447 |
481 |
448 if remote_port : |
482 if remote_port : |
449 reverse = (remote_node, remote_port, None, local_node) |
483 reverse = (remote_node, remote_port, None, local_node) |
450 |
484 |
451 log.info("%s:%s <-> %s:%s", local_node, local_port, remote_port, remote_host) |
485 log.info("%s:%s bridge <-> %s:%s", local_node, local_port, remote_port, remote_host) |
452 |
486 |
453 # fill in remote_port for bidirectional link |
487 # fill in remote_port for bidirectional link |
454 del links[reverse] |
488 del links[reverse] |
455 reverse = local_node, local_port, remote_port, remote_node |
489 reverse = local_node, local_port, remote_port, remote_node |
456 links[reverse] = link_vlans |
490 links[reverse] = link_vlans |
457 |
491 |
458 else : |
492 else : |
459 log.info("%s:%s -> %s", local_node, local_port, remote_host) |
493 log.info("%s:%s bridge -> %s", local_node, local_port, remote_host) |
460 |
494 |
461 links[forward] = link_vlans |
495 links[forward] = link_vlans |
462 |
496 |
463 |
497 |
464 |
498 |