diff options
author | Steinar H. Gunderson <sgunderson@bigfoot.com> | 2014-04-06 14:24:55 +0200 |
---|---|---|
committer | Steinar H. Gunderson <sgunderson@bigfoot.com> | 2014-04-06 14:24:55 +0200 |
commit | d2ccb04d77e266fa690831d8734389ae24807155 (patch) | |
tree | 5cefb2305c945f85844ccd37fe45afc8c93d810a | |
parent | 72f79dcd924420e9f3237c263c22f97ef36fba06 (diff) |
Switch from Net::SNMP to SNMP, since it has much better table support and seems to be faster.
-rwxr-xr-x | clients/accesspoints.pl | 2 | ||||
-rwxr-xr-x | clients/snmpfetch.pl | 48 | ||||
-rw-r--r-- | include/FixedSNMP.pm | 125 | ||||
-rw-r--r-- | include/nms.pm | 47 |
4 files changed, 172 insertions, 50 deletions
diff --git a/clients/accesspoints.pl b/clients/accesspoints.pl index 3e62996..85a32c2 100755 --- a/clients/accesspoints.pl +++ b/clients/accesspoints.pl @@ -35,7 +35,7 @@ sub poll_loop { my $blade = $aps->{'blade'}; my $port = $aps->{'port'}; my $oid = "1.3.6.1.2.1.105.1.1.1.9.$blade.$port"; # POWER-ETHERNET-MIB...pethPsePortType - my $mode = $session->get_request(-varbindlist=>[$oid])->{$oid}; + my $mode = $session->get($oid); $qpoll->execute($mode, $aps->{'switch'}); printf "%s (%s:%s/%s): %s\n", $sysname, $core, $blade, $port, $mode; } diff --git a/clients/snmpfetch.pl b/clients/snmpfetch.pl index e5218af..9e089dc 100755 --- a/clients/snmpfetch.pl +++ b/clients/snmpfetch.pl @@ -5,17 +5,11 @@ use Time::HiRes; use Net::Telnet; use strict; use warnings; -use Net::SNMP; use lib '../include'; use nms; use threads; -my $in_octets_oid = "1.3.6.1.2.1.31.1.1.1.6"; # interfaces.ifTable.ifEntry.ifHCInOctets -my $out_octets_oid = "1.3.6.1.2.1.31.1.1.1.10"; # interfaces.ifTable.ifEntry.ifHCOutOctets -my $in_errors_oid = "1.3.6.1.2.1.2.2.1.14"; # interfaces.ifTable.ifEntry.ifInErrors -my $out_errors_oid = "1.3.6.1.2.1.2.2.1.20"; # interfaces.ifTable.ifEntry.ifOutErrors - # normal mode: fetch switches from the database # instant mode: poll the switches specified on the command line if (defined($ARGV[0])) { @@ -35,6 +29,7 @@ sub poll_loop { my $dbh = nms::db_connect(); $dbh->{AutoCommit} = 0; + $dbh->{RaiseError} = 1; my $qualification; if ($instant) { @@ -138,34 +133,25 @@ EOF $ports{$port} = 1; } - my $in_result = $session->get_table( - -maxrepetitions => 200, - -baseoid => $in_octets_oid, - ); - my $out_result = $session->get_table( - -maxrepetitions => 200, - -baseoid => $out_octets_oid, - ); - my $ine_result = $session->get_table( - -maxrepetitions => 200, - -baseoid => $in_errors_oid, - ); - my $oute_result = $session->get_table( - -maxrepetitions => 200, - -baseoid => $out_errors_oid, + # ifHCInOctets / ifHCOutOctets are strictly speaking part of ifXTable + # and not ifTable, but it seems to work fine nevertheless, + # as long as we explicitly ask for them. + my $result = $session->gettable('ifTable', + noindexes => 1, + repeat => 200, + columns => [ 'ifHCInOctets', 'ifHCOutOctets', 'ifInErrors', 'ifOutErrors' ], ); - die "SNMP fetch failed: " . $session->error() if (!defined($in_result)); - - for my $key (keys %$in_result) { - (my $port = $key) =~ s/^\Q$in_octets_oid\E\.//; - my $in = $in_result->{$in_octets_oid . '.' . $port}; - my $out = $out_result->{$out_octets_oid . '.' . $port}; - my $ine = $ine_result->{$in_errors_oid . '.' . $port}; - my $oute = $oute_result->{$out_errors_oid . '.' . $port}; + die "SNMP fetch failed: " . $session->{'ErrorStr'} if (!defined($result)); + + while (my ($key, $value) = each %$result) { + my $port = $key; + my $in = $value->{'ifHCInOctets'} // -1; # Does not exist for some weird stack ports. + my $out = $value->{'ifHCOutOctets'} // -1; + my $ine = $value->{'ifInErrors'}; + my $oute = $value->{'ifOutErrors'}; my $official_port = exists($ports{$port}) ? 1 : 0; - $qpoll->execute($switch->{'switch'}, $port, $in, $out, $ine, $oute, $official_port) || die "%s:%s: %s\n", $switch->{'switch'}, $port, $in; + $qpoll->execute($switch->{'switch'}, $port, $in, $out, $ine, $oute, $official_port); } - $session->close; }; if ($@) { mylog("ERROR: $@ (during poll of $ip)"); diff --git a/include/FixedSNMP.pm b/include/FixedSNMP.pm new file mode 100644 index 0000000..1ea3089 --- /dev/null +++ b/include/FixedSNMP.pm @@ -0,0 +1,125 @@ +# A bugfix to the gettable functions of SNMP.pm, that deals properly +# with bulk responses being overridden. Original copyright: +# +# Copyright (c) 1995-2006 G. S. Marzot. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +# To use, just "use FixedSNMP;" and then use SNMP::Session as usual. + +use strict; +use warnings; +use SNMP; + +package FixedSNMP::Session; + +sub _gettable_do_it() { + my ($this, $vbl, $parse_indexes, $textnode, $state) = @_; + + my ($res); + + $vbl = $_[$#_] if ($state->{'options'}{'callback'}); + + my $num_vbls = scalar @$vbl; + my $num_stopconds = scalar @{$state->{'stopconds'}}; + + while ($num_vbls > 0 && !$this->{ErrorNum}) { + my @found_eof = (0) x $num_stopconds; + + for (my $i = 0; $i <= $#$vbl; $i++) { + my $row_oid = SNMP::translateObj($vbl->[$i][0]); + my $row_text = $vbl->[$i][0]; + my $row_index = $vbl->[$i][1]; + my $row_value = $vbl->[$i][2]; + my $row_type = $vbl->[$i][3]; + + my $stopcond_num = $i % $num_stopconds; + my $stopcond = $state->{'stopconds'}[$stopcond_num]; + if ($row_oid !~ /^\Q$stopcond\E/ || $row_value eq 'ENDOFMIBVIEW') { + $found_eof[$stopcond_num] = 1; + } else { + + if ($row_type eq "OBJECTID") { + + # If the value returned is an OID, translate this + # back in to a textual OID + + $row_value = SNMP::translateObj($row_value); + + } + + # continue past this next time + + $state->{'varbinds'}[$stopcond_num] = [ $row_text, $row_index ]; + + # Place the results in a hash + + $state->{'result_hash'}{$row_index}{$row_text} = $row_value; + } + } + + my @newstopconds = (); + my @newvarbinds = (); + for (my $i = 0; $i < $num_stopconds; ++$i) { + unless ($found_eof[$i]) { + push @newstopconds, $state->{'stopconds'}[$i]; + push @newvarbinds, $state->{'varbinds'}[$i]; + } + } + if ($#newstopconds == -1) { + last; + } + $state->{'varbinds'} = \@newvarbinds; + $state->{'stopconds'} = \@newstopconds; + $vbl = $state->{'varbinds'}; + $num_vbls = scalar @newvarbinds; + $num_stopconds = scalar @newstopconds; + + # + # if we've been configured with a callback, then call the + # sub-functions with a callback to our own "next" processing + # function (_gettable_do_it). or else call the blocking method and + # call the next processing function ourself. + # + if ($state->{'options'}{'callback'}) { + if ($this->{Version} ne '1' && !$state->{'options'}{'nogetbulk'}) { + $res = $this->getbulk(0, $state->{'repeatcount'}, $vbl, + [\&_gettable_do_it, $this, $vbl, + $parse_indexes, $textnode, $state]); + } else { + $res = $this->getnext($vbl, + [\&_gettable_do_it, $this, $vbl, + $parse_indexes, $textnode, $state]); + } + return; + } else { + if ($this->{Version} ne '1' && !$state->{'options'}{'nogetbulk'}) { + $res = $this->getbulk(0, $state->{'repeatcount'}, $vbl); + } else { + $res = $this->getnext($vbl); + } + } + } + + # finish up + _gettable_end_routine($state, $parse_indexes, $textnode); + + # return the hash if no callback was specified + if (!$state->{'options'}{'callback'}) { + return($state->{'result_hash'}); + } + + # + # if they provided a callback, call it + # (if an array pass the args as well) + # + if (ref($state->{'options'}{'callback'}) eq 'ARRAY') { + my $code = shift @{$state->{'options'}{'callback'}}; + $code->(@{$state->{'options'}{'callback'}}, $state->{'result_hash'}); + } else { + $state->{'options'}{'callback'}->($state->{'result_hash'}); + } +} + +*FixedSNMP::Session::_gettable_end_routine = *SNMP::Session::_gettable_end_routine; +*SNMP::Session::_gettable_do_it = *FixedSNMP::Session::_gettable_do_it; diff --git a/include/nms.pm b/include/nms.pm index 49b3cad..0bf557e 100644 --- a/include/nms.pm +++ b/include/nms.pm @@ -4,7 +4,7 @@ use warnings; use DBI; use Net::Telnet; use Data::Dumper; -use Net::SNMP; +use FixedSNMP; use FileHandle; package nms; @@ -16,6 +16,13 @@ BEGIN { eval { require "config.local.pm"; }; + + # $SNMP::debugging = 1; + SNMP::initMib(); + SNMP::loadModules('SNMPv2-MIB'); + SNMP::loadModules('ENTITY-MIB'); + SNMP::loadModules('IF-MIB'); + #SNMP::loadModules('LLDP-MIB'); } sub db_connect { @@ -105,34 +112,38 @@ sub switch_disconnect { sub snmp_open_session { my ($ip, $community) = @_; - my $domain = ($ip =~ /:/) ? 'udp6' : 'udp4'; - my $version; - my %options = ( - -hostname => $ip, - -domain => $domain, - ); + my %options = (UseEnums => 1); + if ($ip =~ /:/) { + $options{'DestHost'} = "udp6:$ip"; + } else { + $options{'DestHost'} = "udp:$ip"; + } if ($community =~ /^snmpv3:(.*)$/) { my ($username, $authprotocol, $authpassword, $privprotocol, $privpassword) = split /\//, $1; - $options{'-username'} = $username; - $options{'-authprotocol'} = $authprotocol; - $options{'-authpassword'} = $authpassword; + $options{'SecName'} = $username; + $options{'SecLevel'} = 'authNoPriv'; + $options{'AuthProto'} = $authprotocol; + $options{'AuthPass'} = $authpassword; if (defined($privprotocol) && defined($privpassword)) { - $options{'-privprotocol'} = $privprotocol; - $options{'-privpassword'} = $privpassword; + $options{'SecLevel'} = 'authPriv'; + $options{'PrivProto'} = $privprotocol; + $options{'PrivPass'} = $privpassword; } - $options{'-version'} = 3; + $options{'Version'} = 3; } else { - $options{'-version'} = 2; + $options{'Version'} = 2; } - my ($session, $error) = Net::SNMP->session(%options); - die "SNMP session failed: " . $error if (!defined($session)); - - return $session; + my $session = SNMP::Session->new(%options); + if (defined($session) && defined($session->getnext('sysDescr'))) { + return $session; + } else { + die 'Could not open SNMP session'; + } } # Not currently in use; kept around for reference. |