9 require 'SNMP_Session.pm';
14 my $password = 'removed';
17 my $dbh = nms::db_connect();
18 $dbh->{AutoCommit} = 0;
20 # normal mode: fetch switches from the database
21 # instant mode: poll the switches specified on the command line
22 my $instant = (defined($ARGV[0]));
25 $qualification = "sysname=?";
27 $qualification = <<"EOF";
28 (last_updated IS NULL OR now() - last_updated > poll_frequency)
29 AND (locked='f' OR now() - last_updated > '15 minutes'::interval)
33 my $qswitch = $dbh->prepare(<<"EOF")
36 DATE_TRUNC('second', now() - last_updated - poll_frequency) AS overdue
39 NATURAL LEFT JOIN switchtypes
45 FOR UPDATE OF switches
47 or die "Couldn't prepare qswitch";
48 my $qlock = $dbh->prepare("UPDATE switches SET locked='t', last_updated=now() WHERE switch=?")
49 or die "Couldn't prepare qlock";
50 my $qunlock = $dbh->prepare("UPDATE switches SET locked='f', last_updated=now() WHERE switch=?")
51 or die "Couldn't prepare qunlock";
52 my $qpoll = $dbh->prepare("INSERT INTO polls (time, switch, port, bytes_in, bytes_out, errors_in, errors_out) VALUES (timeofday()::timestamp,?,?,?,?,?,?)")
53 or die "Couldn't prepare qpoll";
54 my $qtemppoll = $dbh->prepare("INSERT INTO temppoll (time, switch, temp) VALUES (timeofday()::timestamp,?::text::int,?::text::float)")
55 or die "Couldn't prepare qtemppoll";
56 my $qcpupoll = $dbh->prepare("INSERT INTO cpuloadpoll (time, switch, entity, value) VALUES (timeofday()::timestamp,?::text::int,?,?)")
57 or die "Couldn't prepare qtemppoll";
62 $sysname = shift @ARGV;
63 exit if (!defined($sysname));
64 $qswitch->execute($sysname)
65 or die "Couldn't get switch";
67 # Find a switch to grab
69 or die "Couldn't get switch";
71 my $switch = $qswitch->fetchrow_hashref();
73 if (!defined($switch)) {
77 mylog("No such switch $sysname available, quitting.");
80 mylog("No available switches in pool, sleeping.");
86 $qlock->execute($switch->{'switch'})
87 or die "Couldn't lock switch";
90 if ($switch->{'locked'}) {
91 mylog("WARNING: Lock timed out on $switch->{'ip'}, breaking lock");
95 if (defined($switch->{'overdue'})) {
96 $msg = sprintf "Polling ports %s on %s (%s), %s overdue.",
97 $switch->{'ports'}, $switch->{'ip'}, $switch->{'sysname'}, $switch->{'overdue'};
99 $msg = sprintf "Polling ports %s on %s (%s), never polled before.",
100 $switch->{'ports'}, $switch->{'ip'}, $switch->{'sysname'};
104 my $ip = $switch->{'ip'};
106 if ($ip eq '127.0.0.1') {
107 mylog("Polling disabled for this switch, skipping.");
108 $qunlock->execute($switch->{'switch'})
109 or die "Couldn't unlock switch";
114 my $community = $switch->{'community'};
115 my $start = [Time::HiRes::gettimeofday];
118 if ($switch->{'wide_counters'}) {
119 $session = SNMPv2c_Session->open($ip, $community, 161)
120 or die "Couldn't talk to switch";
122 $session = SNMP_Session->open($ip, $community, 161)
123 or die "Couldn't talk to switch";
125 my @ports = expand_ports($switch->{'ports'});
127 for my $port (@ports) {
128 my $in = fetch_data($session, $port, 0, $switch->{'wide_counters'});
129 my $out = fetch_data($session, $port, 1, $switch->{'wide_counters'});
130 my $ine = fetch_errors($session, $port, 0);
131 my $oute = fetch_errors($session, $port, 1);
133 $qpoll->execute($switch->{'switch'}, $port, $in, $out, $ine, $oute);
135 my $conn = switch_connect($ip);
136 if (!defined($conn)) {
137 print "Could not connect to switch ".$switch->{'switch'}."\n";
138 } elsif ($switch->{'switchtype'} eq 'es3024') {
139 my @data = switch_exec('sys monitor status', $conn);
140 my @fields = split(/\s+/, $data[2]);
141 # The temp fields are 6, 7, 8
142 print "$fields[7] + $fields[8] + $fields[9]\n";
143 my $avgtemp = ($fields[7] + $fields[8] + $fields[9]) / 3;
144 print $avgtemp." avgtemp\n";
145 $qtemppoll->execute($switch->{'switch'},
146 $avgtemp) or die "Could not exec qtemppoll";
147 } elsif ($switch->{'switchtype'} eq 'cisco6509') {
148 # fetch load data for the entities
151 my $oid = BER::encode_oid(1, 3, 6, 1, 4, 1, 9, 9, 109, 1, 1, 1, 1, 2, $i);
152 my $entity = fetch_snmp($session, $oid);
154 next if (!defined($entity));
156 # last-minute load for the given entity
157 $oid = BER::encode_oid(1, 3, 6, 1, 4, 1, 9, 9, 109, 1, 1, 1, 1, 4, $i);
158 my $value = fetch_snmp($session, $oid);
160 $qcpupoll->execute($switch->{'switch'}, $entity, $value)
161 or die "Could not exec qcpupoll";
164 # fetch load data for the matrix
165 for my $i ([1,0], [1,1], [2,0], [2,1], [4,0], [5,0], [6,0]) {
167 my $oid = BER::encode_oid(1, 3, 6, 1, 4, 1, 9, 9, 217, 1, 3, 1, 1, 6, $i->[0], $i->[1]);
168 my $in_util = fetch_snmp($session, $oid);
170 $qcpupoll->execute($switch->{'switch'}, 100000 + $i->[0] * 10 + $i->[1], $in_util)
171 or die "Could not exec qcpupoll";
174 $oid = BER::encode_oid(1, 3, 6, 1, 4, 1, 9, 9, 217, 1, 3, 1, 1, 7, $i->[0], $i->[1]);
175 my $out_util = fetch_snmp($session, $oid);
177 $qcpupoll->execute($switch->{'switch'}, 200000 + $i->[0] * 10 + $i->[1], $out_util)
178 or die "Could not exec qcpupoll";
184 mylog("ERROR: $@ (during poll of $ip)");
188 my $elapsed = Time::HiRes::tv_interval($start);
189 $msg = sprintf "Polled $switch->{'ip'} in %5.3f seconds.", $elapsed;
192 $qunlock->execute($switch->{'switch'})
193 or die "Couldn't unlock switch";
198 my ($session, $port, $out, $wide_counters) = @_;
201 if ($wide_counters) {
203 $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 10, $port); # interfaces.ifTable.ifEntry.ifHCOutOctets
205 $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 6, $port); # interfaces.ifTable.ifEntry.ifHCInOctets
209 $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 2, 2, 1, 16, $port); # interfaces.ifTable.ifEntry.ifOutOctets
211 $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 2, 2, 1, 10, $port); # interfaces.ifTable.ifEntry.ifInOctets
215 return fetch_snmp($session, $oid);
218 my ($session, $port, $out) = @_;
222 $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 2, 2, 1, 20, $port); # interfaces.ifTable.ifEntry.ifOutErrors
224 $oid = BER::encode_oid(1, 3, 6, 1, 2, 1, 2, 2, 1, 14, $port); # interfaces.ifTable.ifEntry.ifInErrors
227 return fetch_snmp($session, $oid);
231 my ($session, $oid) = @_;
233 if ($session->get_request_response($oid)) {
234 my ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
236 while ($bindings ne '') {
237 ($binding,$bindings) = &decode_sequence ($bindings);
238 my ($oid,$value) = &decode_by_template ($binding, "%O%@");
239 return BER::pretty_print($value);
242 die "Couldn't get info from switch";
247 my @ranges = split /,/, $in;
250 for my $range (@ranges) {
251 if ($range =~ /^\d+$/) {
253 } elsif ($range =~ /^(\d+)-(\d+)$/) {
258 die "Couldn't understand '$range' in ports";
262 return (sort { $a <=> $b } @ret);
267 my $time = POSIX::ctime(time);
269 printf STDERR "[%s] %s\n", $time, $msg;
273 my ($cmd, $conn) = @_;
275 # Send the command and get data from switch
276 # $conn->dump_log(*STDOUT);
277 my @data = $conn->cmd($cmd);
279 foreach my $line (@data) {
280 # Remove escape-7 sequence
281 $line =~ s/\x1b\x37//g;
291 my $conn = new Net::Telnet( Timeout => $timeout,
292 Dump_Log => '/tmp/dumplog-tempfetch',
294 Prompt => '/es-3024|e(\-)?\d+\-\dsw>/i');
295 my $ret = $conn->open( Host => $ip);
296 if (!$ret || $ret != 1) {
299 # XXX: Just send the password as text, I did not figure out how to
300 # handle authentication with only password through $conn->login().
301 #$conn->login( Prompt => '/password[: ]*$/i',
303 # Password => $password);
304 my @data = $conn->cmd($password);