aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteinar H. Gunderson <sgunderson@bigfoot.com>2014-04-06 14:24:55 +0200
committerSteinar H. Gunderson <sgunderson@bigfoot.com>2014-04-06 14:24:55 +0200
commitd2ccb04d77e266fa690831d8734389ae24807155 (patch)
tree5cefb2305c945f85844ccd37fe45afc8c93d810a
parent72f79dcd924420e9f3237c263c22f97ef36fba06 (diff)
Switch from Net::SNMP to SNMP, since it has much better table support and seems to be faster.
-rwxr-xr-xclients/accesspoints.pl2
-rwxr-xr-xclients/snmpfetch.pl48
-rw-r--r--include/FixedSNMP.pm125
-rw-r--r--include/nms.pm47
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.