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