bin/pvl.hosts-lldp
changeset 387 75158fd28784
parent 386 9e1abcf47d27
child 388 5a034dbd641c
equal deleted inserted replaced
386:9e1abcf47d27 387:75158fd28784
     1 #!/usr/bin/env python
     1 #!/usr/bin/env python
     2 
     2 
     3 """
     3 """
     4     Requirements:
     4     Requirements:
     5         pysnmp
     5         pydot
     6         pysnmp-mibs
       
     7         memoized-property
       
     8 
       
     9     Setup:
       
    10         ./opt/bin/build-pysnmp-mib -o usr/mibs/LLDP-MIB.py etc/mibs/LLDP-MIB
       
    11 
       
    12     Run:
       
    13         PYSNMP_MIB_DIRS=usr/mibs/ ./opt/bin/python ...
       
    14 """
     6 """
    15 
     7 
    16 import pvl.args
     8 import pvl.args
    17 import pvl.hosts
     9 import pvl.hosts
    18 from pvl.invoke import merge
    10 from pvl.invoke import merge
    83             
    75             
    84             log.info("%s: %s: %s (%s)", host, port, remote, remote_host)
    76             log.info("%s: %s: %s (%s)", host, port, remote, remote_host)
    85 
    77 
    86             yield host, merge(agent.local, port), remote, remote_host
    78             yield host, merge(agent.local, port), remote, remote_host
    87 
    79 
       
    80 def apply_graph (options, items) :
       
    81     import pydot
       
    82 
       
    83     dot = pydot.Dot(graph_name='lldp_hosts', graph_type='digraph',
       
    84             splines     = 'true',
       
    85 
       
    86             sep             = '+25,25',
       
    87             overlap         = 'scalexy',
       
    88 
       
    89             # only applies to loops
       
    90             nodesep     = 0.5,
       
    91     )
       
    92     dot.set_edge_defaults(
       
    93             labeldistance   = 3.0,
       
    94     )
       
    95 
       
    96     nodes = { }
       
    97     edges = { }
       
    98 
       
    99     for host, local, remote, remote_host in items :
       
   100         # src
       
   101         src_name = str(host)
       
   102         src_label = '"{host.location}"'.format(host=host)
       
   103 
       
   104         if src_name in nodes :
       
   105             src = nodes[src_name]
       
   106         else :
       
   107             src = nodes[src_name] = pydot.Node(src_name, label=src_label)
       
   108             dot.add_node(src)
       
   109         
       
   110         # dst
       
   111         if remote_host :
       
   112             dst_name = str(remote_host)
       
   113             dst_label = '"{host.location}"'.format(host=remote_host)
       
   114         else :
       
   115             dst_name = remote['chassis'].replace(':', '-')
       
   116 
       
   117             # XXX: pydot is not smart enough to quote this
       
   118             if remote['sys_name'] :
       
   119                 dst_label = '"{remote[chassis]} ({remote[sys_name]})"'.format(remote=remote)
       
   120             else :
       
   121                 dst_label = '"{remote[chassis]}"'.format(remote=remote)
       
   122 
       
   123         
       
   124         if dst_name in nodes :
       
   125             dst = nodes[dst_name]
       
   126         else :
       
   127             dst = nodes[dst_name] = pydot.Node(dst_name, label=dst_label)
       
   128             dot.add_node(dst)
       
   129 
       
   130         # edge
       
   131         headlabel = '"{remote[port]}"'.format(remote=remote)
       
   132         taillabel = '"{local[port]}"'.format(local=local)
       
   133 
       
   134         if (src_name, dst_name) in edges :
       
   135             log.warning("%s <- %s: duplicate", src_name, dst_name)
       
   136         
       
   137         elif (dst_name, src_name) in edges :
       
   138             log.info("%s <-> %s", src_name, dst_name)
       
   139             edge = edges[(dst_name, src_name)]
       
   140 
       
   141             if edge.get('headlabel') != taillabel :
       
   142                 log.warn("%s -> %s: local port mismatch: %s vs %s", src_name, dst_name, local['port'], edge.get('headlabel'))
       
   143             
       
   144             if edge.get('taillabel') != headlabel :
       
   145                 log.warn("%s -> %s: remote port mismatch: %s vs %s", src_name, dst_name, remote['port'], edge.get('taillabel'))
       
   146 
       
   147             # mark as bidirectional
       
   148             edges[(src_name, dst_name)] = edge
       
   149             
       
   150             edge.set('dir', 'both')
       
   151 
       
   152         else :
       
   153             edge = edges[(src_name, dst_name)] = pydot.Edge(src, dst,
       
   154                     dir         = 'back',
       
   155                     headlabel   = headlabel,
       
   156                     taillabel   = taillabel,
       
   157             )
       
   158 
       
   159             dot.add_edge(edge)
       
   160 
       
   161     if options.graph_dot :
       
   162         dot.write(options.graph_dot)
       
   163 
    88 def main (argv) :
   164 def main (argv) :
    89     """
   165     """
    90         SNMP polling.
   166         SNMP polling.
    91     """
   167     """
    92 
   168 
    93     parser = optparse.OptionParser(main.__doc__)
   169     parser = optparse.OptionParser(main.__doc__)
    94     parser.add_option_group(pvl.args.parser(parser))
   170     parser.add_option_group(pvl.args.parser(parser))
    95     parser.add_option_group(pvl.hosts.optparser(parser))
   171     parser.add_option_group(pvl.hosts.optparser(parser))
    96     parser.add_option_group(pvl.snmp.snmp.options(parser))
   172     parser.add_option_group(pvl.snmp.snmp.options(parser))
    97 
   173 
       
   174     parser.add_option('--graph-dot',
       
   175             help="Output .dot graph data")
    98 
   176 
    99     options, args = parser.parse_args(argv[1:])
   177     options, args = parser.parse_args(argv[1:])
   100     pvl.args.apply(options)
   178     pvl.args.apply(options)
   101 
   179 
   102     # input
   180     # input
   103     hosts = pvl.hosts.apply(options, args)
   181     hosts = pvl.hosts.apply(options, args)
   104     
   182     
   105     # apply
   183     # apply
   106     for host, local, remote, remote_host in apply_hosts_lldp(options, hosts) :
   184     items = apply_hosts_lldp(options, hosts)
   107         if remote_host :
   185 
   108             print "{host:30} {host.location:>30} {local[port]:>25} <-> {remote[port]:<25} {remote_host.location:>30} # {remote[chassis]} ({remote_host})".format(host=host, local=local, remote=remote, remote_host=remote_host)
   186     # print
   109         else :
   187     if options.graph_dot :
   110             print "{host:30} {host.location:>30} {local[port]:>25} <-- {remote[port]:<25} {empty:30} # {remote[chassis]} ({remote[sys_name]})".format(host=host, local=local, remote=remote, empty='')
   188         apply_graph(options, items)
       
   189     else :
       
   190         for host, local, remote, remote_host in items :
       
   191             if remote_host :
       
   192                 print "{host:30} {host.location:>30} {local[port]:>25} <-> {remote[port]:<25} {remote_host.location:>30} # {remote[chassis]} ({remote_host})".format(host=host, local=local, remote=remote, remote_host=remote_host)
       
   193             else :
       
   194                 print "{host:30} {host.location:>30} {local[port]:>25} <-- {remote[port]:<25} {empty:30} # {remote[chassis]} ({remote[sys_name]})".format(host=host, local=local, remote=remote, empty='')
   111 
   195 
   112 if __name__ == '__main__':
   196 if __name__ == '__main__':
   113     pvl.args.main(main)
   197     pvl.args.main(main)