########################################################################## # package SNMPTools.pm # This Perl module contains routines which function as wrappers # around the similarly named routines in SNMP.pm (the net-snmp tool kit's # Perl module) and Net-SNMP (David Towne's pure Perl toolkit) # V Who When What # --------------------------------------------------------------------------- # 1.4.1 skendric 06-16-2008 Add char_device # 1.4.0 skendric 06-09-2008 Import Netops-style snmp routines # 1.3.4 skendric 05-21-2008 Support PDUs # 1.3.3 skendric 03-12-2007 Stylistic mods # 1.3.2 skendric 02-26-2006 Skip APC devices: gross hack # 1.3.1 skendric 08-25-2005 chomp sys_descr # 1.3.0 skendric 06-12-2005 Check subroutine parameters # 1.2.9 skendric 06-04-2005 Fix bugs in snmpWalk and snmpBulkWalk # 1.2.8 skendric 02-09-2005 Convert %hostname to %nodename # 1.2.7 skendric 02-07-2005 snmp_char returns success or failure now # 1.2.6 skendric 02-06-2005 Remove redundant assign to %hostname # 1.2.5 skendric 01-11-2005 More error checking get_sysObjectID # 1.2.4 skendric 01-03-2005 Change return value of get_hostname # 1.2.3 skendric 11-22-2004 Fixed bug in snmpWalk # 1.2.2 skendric 09-11-2004 Log sys_descr and sysObjectID # 1.2.1 skendric 08-27-2004 Stylistic mods, skip -> skip_name # 1.2.0 skendric 08-05-2004 Add $trash to snmpBulkWalk # 1.1.0 skendric 07-13-2004 Make snmp_char more robust # 1.0.5 skendric 07-02-2004 Add $SNMP::dump_packet debugging # 1.0.4 skendric 06-19-2004 snmp_char fixes # 1.0.3 skendric 06-18-2004 Add support for UseSprintValue # 1.0.2 skendric 06-06-2004 snmpWalk returns entire varbind as well # as the OIDs # 1.0.1 skendric 06-06-2004 Add snmp_char, sys_descr/objectid # 1.0.0 skendric 06-04-2004 First Version # # # # Authors: Stuart Kendrick # # Source: http://www.skendric.com/device/soma # # This software is available under the GNU GENERAL PUBLIC LICENSE, see # http://www.fsf.org/licenses/gpl.html # package FHCRC::VDOPS::SNMPTools; #### Load modules #### use strict; use warnings; use Carp qw(carp cluck croak confess); use Data::Dumper; use English qw( -no_match_vars ); use Exporter; use List::MoreUtils qw(any first_index uniq); use Net::IPAddress; use Net::SNMP qw( :asn1 :debug :snmp); use Perl6::Say; use Readonly; use SNMP; use Switch; use Text::Trim; use lib '/home/soma/lib'; use FHCRC::VDOPS::HostTools; use FHCRC::VDOPS::SomaData; use FHCRC::VDOPS::Utilities; #### Set-up export stuff #### our @ISA = qw(Exporter); our @EXPORT = qw( char_device clean_sys_descr compile_mibs get_oid get_sys_descr get_sysObjectID snmp_char snmpGet snmpSet snmpWalk walk_oid ); ##### Only subroutines below here #### ######################################################################## # Sort IP addresses ######################################################################## sub by_ip { my ($a1, $a2, $a3, $a4) = split ('\.', $a); my ($b1, $b2, $b3, $b4) = split ('\.', $b); if (($a1 <=> $b1) != 0) { $a1 <=> $b1 } elsif (($a2 <=> $b2) != 0) { $a2 <=> $b2 } elsif (($a3 <=> $b3) != 0) { $a3 <=> $b3 } elsif (($a4 <=> $b4) != 0) { $a4 <=> $b4 } } ######################################################################## # Given a reference to a hash of devices keyed by IP address, # characterize the SNMP side (version, read string, sysDescr, and # sysObjectID) of each device, populating the appropriate shared # hashes ######################################################################## sub char_device { my $devices = shift; # Ref to hash of devices we will characterize my @running; # List of threads currently running my $thr; my @threads; # List of thread objects I have spawned my %threads; # List of thread objects I have spawned, # keyed by device # Debug trace trace_location('begin') if $debug; # Sanity check confess 'No parameters!' unless defined $devices; confess 'Wrong type' unless ref $devices eq 'HASH'; # If we are in thread mode, process each device in parallel if ($thrMode == 1) { # Walk through $devices, identifying read-only strings and SNMP versions for my $addr (sort by_ip keys %$devices) { say "Spawning snmp_char for $devices->{$addr}" if $debug > 1; $thr = threads->new(\&snmp_char, $addr, \@snmp_read_list, \@snmp_version_list); push @threads, $thr; $threads{$addr} = $thr; $thr->detach; sleep 1; } # Wait for threads to complete say 'Waiting for snmp_char threads to complete' if $debug > 1; sleep $processCharTimeout; wait_for_threads(\@threads, $processCharTimeout); # If any threads are still running, figure out which ones they are # and log the event @running = threads->running( @threads ); if (@running) { my ($addr, %rev_threads); %rev_threads = reverse %threads; for my $thr (@running) { $addr = $rev_threads{$thr}; # I don't understand why, but sometimes $addr isn't defined if (defined $addr) { if (defined $nodename{$addr}) { print_it("snmp_char did not complete against $nodename{$addr}\n"); } else { print_it("snmp_char did not complete against $addr\n"); } } } # End 'for my $thr (@running)' } # End 'if (@running)' } # End 'if $thMode == 1' # Otherwise, process each device serially else { for my $addr (sort by_ip keys %$devices) { my $name = $devices->{$addr}; eval { local $SIG{ALRM} = sub { print_it("snmp_char for $name did not complete\n"); die 'alarm clock restart'; }; alarm $processCharTimeout * 10; # schedule the alarm eval { print_it("Running snmp_char on $name\n") if $debug > 1; snmp_char($addr, \@snmp_read_list, \@snmp_version_list); }; alarm 0; # cancel the alarm }; alarm 0; die if ($@ and $@ !~ /alarm clock restart/); # reraise } # End 'for my $addr (sort...' loop } # End 'process each device serially' # Debug trace trace_location('end') if $debug; return 1; } ####################################################################### # Strip the incoming string of weird characters, extra spaces, line # feeds ######################################################################## sub clean_sys_descr { my $sysDescr = shift; # Debug trace trace_location('begin') if $debug > 2; # Sanity check confess 'No parameters!' unless defined $sysDescr; # Do the work $sysDescr =~ s{ "}{ }g; $sysDescr =~ s{\n}{ }g; $sysDescr =~ s{"}{}g; trim($sysDescr); # Debug trace trace_location('end') if $debug > 2; return $sysDescr; } ####################################################################### # Compile the MIB files found in the global variables @mib_dirs and # @mib_files ######################################################################## sub compile_mibs { my $mib_dirs = shift; my $mib_files = shift; # Debug trace trace_location('begin') if $debug; # If SNMP.pm is loaded, compile mibs if (exists $INC{'SNMP.pm'} and $snmp_module eq 'SNMP') { say 'Compiling MIB files' if $debug; # Initialize SNMP::initMib(); # Add mibDirs for my $mib_dir (@mib_dir) { SNMP::addMibDirs($mib_dir); } # Add mibFiles for my $mib_file (@mib_file) { SNMP::loadModules($mib_file); } } # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Given a target and an OID, get the OID and return the value ######################################################################## sub get_oid { my %arg; my $host = shift; my $oid = shift; my $ov; my $val; # Debug trace trace_location('begin') if $debug; # Sanity check confess 'Must provide a host' unless defined $host; confess 'Must provide an oid' unless defined $oid; # Translate OID $ov = translate_oid($oid); # Get OID say "Getting $ov" if $debug > 3; %arg = ( host => $host, oid => $oid ); $val = snmpGet(\%arg); # Debug trace trace_location('end') if $debug; return $val; } ####################################################################### # Find sys_descr ######################################################################## sub get_sys_descr { my %arg; # Args for snmpGet my $host = shift; my $sysDescr; # Debug trace trace_location('begin') if $debug > 2; # Sanity check confess 'Must receive host name' unless defined $host; # Build %arg %arg = ( host => $host, oid => $sysDescr_oid ); # Do the work say ' Getting sys_descr' if $debug > 3; $sysDescr = snmpGet(\%arg); $sysDescr = clean_sys_descr($sysDescr) if defined $sysDescr; # Debug trace trace_location('end') if $debug > 2; return $sysDescr; } ####################################################################### # Get sysObjectID.0 and return it ######################################################################## sub get_sysObjectID { my %arg; # Args for snmpGet my $english; my $host = shift; my $sysObjectID; # Debug trace trace_location('begin') if $debug > 2; # Sanity check confess 'Must receive a host name' unless defined $host; # Build %arg %arg = ( host => $host, oid => $sysObjectID_oid ); # Acquire sysObjectID,0 say ' Getting sysObjectID' if $debug > 3; $sysObjectID = snmpGet(\%arg); # Translate into English if (exists $INC{'SNMP.pm'}) { if (defined $sysObjectID) { if ($english = SNMP::translateObj($sysObjectID)) { if ($english eq 'enterprises.') { $sysObjectID = 'unknown'; } else { $sysObjectID = $english; } } } else { $sysObjectID = 'unknown'; } } # Debug info say " sysObjectID{$host} = $sysObjectID" if $debug > 2; # Debug trace trace_location('end') if $debug > 2; return $sysObjectID; } ######################################################################## # Given a target and a reference to an array of strings and versions, # characterize SNMP parameters of target: SNMP version, SNMP read # string, sys_descr, and sysObjectID. If an entry doesn't already exist, # discover nodename. Populate the relevant shared hashes. # Notice that this routine contains domain-specific optimizations. ######################################################################## sub snmp_char { my $flag; # '1' means we own it, '0' means we don't my $found_str; # The first $string which works my $found_ver; # The first SNMP version which works my $ip = shift; # IP address to query my $nodename; # nodename associated with IP address my $refStrings = shift; # Reference to an array of community strings my $refVersions = shift; # Reference to an array of versions my $sess; # SNMP module variable my @strings; # Local array to hold strings my $val; # Place to store sys_descr.0 my @versions; # Local array to hold versions # Debug trace trace_location('begin') if $debug > 2; # Create local copies of arrays @strings = @$refStrings; @versions = @$refVersions; # Sanity check confess 'Must receive IP address' unless defined $ip; confess "$ip not an IP address" unless validaddr($ip); confess 'strings is empty' unless @strings > 0; confess 'versions is empty' unless @versions > 0; # If this isn't one of our boxes, then we don't want to hand it our # community strings. For starters, they won't be correct. And second, # we don't want to hand our strings to anonymous boxes, in case they # are sniffing. # # Fortunately, we name our devices with a well-defined naming convention, # so we can easily make a guess as to whether or not we own it. # Assume that $ip does not belong to us $flag = 0; # Define $nodename if (defined $nodename{$ip}) { $nodename = $nodename{$ip}; } else { $nodename = get_nodename($ip); $nodename{$ip} = $nodename; } # If we found a nodename, see if it belongs to us if (defined $nodename) { # If the nodename contains one of the @suffixes, then most likely it # belongs to us $flag = 1 if any { $nodename =~ /$_/i } @suffixes; # However, some devices I don't want to touch, even if I believe it # belongs to us. If host matches one of the skip patterns, ignore $flag = 0 if any { $nodename =~ /$_/i } @skip_name; } # If $flag is set, then it belongs to us. Check to see if the # device belongs to APC (this is a gross hack ... but it allows me # to avoid filling the APC logs with authentication error attempts) if ($flag) { if ($nodename =~ /-emu|-pdu|-ups/) { @versions = @wimpy_snmp_version_list; @strings = @wimpy_snmp_read_list; } } # Otherwise, assume that this isn't one of ours, and drop back to # a simple guess at SNMP version and read string else { @strings = @snmp_read_plebian; @versions = @snmp_version_plebian; } # OK, let's try speaking SNMP to this puppy if (defined $nodename{$ip}) { say "Attempting to speak SNMP to $nodename{$ip}" if $debug > 2; } else { say "Attempting to speak SNMP to $ip" if $debug > 2; } # Iterate through the possible versions and read strings, stopping # at the first success READ_STRING: for my $version (@versions) { for my $string (@strings) { my (%arg, $sysDescr); say "Trying SNMP v$version, read string: $string" if $debug == 4; # Get sys_descr.0 %arg = ( host => $ip, oid => $sysDescr_oid, read => $string, version => $version ); $sysDescr = snmpGet(\%arg); # If we succeeded, save the result and quit looking if (defined $sysDescr) { $snmp_read{$ip} = $string; $snmp_version{$ip} = $version; $sysDescr{$ip} = clean_sys_descr($sysDescr); $sysObjectID{$ip} = get_sysObjectID($ip); if ($debug == 4) { say "Found SNMP v$version, read string: $string"; say "sys_descr = $sysDescr{$ip}"; say "sysObjectID = $sysObjectID{$ip}"; } last READ_STRING; } } # string loop } # version loop # Fill with blanks if needed $sysDescr{$ip} = $DASH unless defined $sysDescr{$ip}; $sysObjectID{$ip} = $DASH unless defined $sysObjectID{$ip}; # Debug trace trace_location('end') if $debug > 2; return 1; } ######################################################################## # Given a hash which contains an OID, return the result of a GET # %hash = ( # host => {string or ip address}, # oid => {object value or oid}, # msg_size => {integer bytes}, # read => {string}, # retries => {integer}, # port => {port}, # timeout => {seconds or milliseconds}, # translate => {0 or 1}, # version => {integer}, # ); ######################################################################## sub snmpGet { my $arg_ref = shift; # Input hash ref my $error; # Holds error string from SNMP operation my $host; # Host to query my $oid; # Variable to GET my $msg_size; # Maximum size of PDU my $port; # UDP port for SNMP queries my $read; # SNMP community string my $retries; # Number of times to retry the query my $session; # Module object my $timeout; # Number of seconds to wait before retrying my $translate; # Translate output into human-readable format my $val; # Holds result of GET my $version; # SNMP version: 1, 2, or 3 # Debug trace trace_location('begin') if $debug > 3 and $debug < 6; # Sanity check confess 'No parameters!' unless defined $arg_ref; confess 'Parameter not a hash ref' unless ref $arg_ref eq 'HASH'; confess 'Must specify host' unless defined $arg_ref->{host}; confess 'Must specify oid' unless defined $arg_ref->{oid}; # Unpack args $host = $arg_ref->{host}; $oid = $arg_ref->{oid}; $msg_size = defined $arg_ref->{msg_size} ? $arg_ref->{msg_size} : $snmp_max_msg_size ; $port = defined $arg_ref->{port} ? $arg_ref->{port} : $snmp_port ; $read = defined $arg_ref->{read} ? $arg_ref->{read} : $snmp_read{$host} ; $retries = defined $arg_ref->{retries} ? $arg_ref->{retries} : $snmp_retries ; $timeout = defined $arg_ref->{timeout} ? $arg_ref->{timeout} : $snmp_timeout ; $translate = defined $arg_ref->{translate} ? $arg_ref->{translate} : $snmp_translate ; $version = defined $arg_ref->{version} ? $arg_ref->{version} : $snmp_version{$host} ; # Use SNMP.pm (bundled with net-snmp) if ($snmp_module eq 'SNMP') { # Enable debugging $SNMP::verbose = $debug; $SNMP::dump_packet = 1 if $debug == 5; # Define SNMP session $session = new SNMP::Session ( Community => $read, DestHost => $host, RemotePort => $port, Retries => $retries, UseSprintValue => $translate, Timeout => $timeout, Version => $version ); unless (defined $session) { print_it("Cannot build SNMP object for $host"); return; } # Perform the get $val = $session->get($oid); $error = $session->{ErrorStr}; undef $error if $error eq $EMPTY_STR; # Print debugging information if ($debug > 3 and $debug < 6) { say "For $host:"; say " oid = $oid" if defined $oid; say " val = $val" if defined $val; say " ERROR: $error" if defined $error; } # Handle errors if (defined $error) { undef $val; print_it("Unable to retrieve $oid from $host using version $version and string $read: $error\n") if $debug > 3; } } # end 'use SNMP' # Use Net::SNMP (david town's pure Perl module) elsif ($snmp_module eq 'Net::SNMP') { my $bitmask; switch ($debug) { case 5 { $bitmask = DEBUG_ALL } else { $bitmask = DEBUG_NONE } } # Create new session ($session, $error) = Net::SNMP->session( -hostname => $host, -port => $port, -version => $version, -timeout => $timeout, -retries => $retries, -community => $read, -translate => $translate, -maxmsgsize => $msg_size, -debug => $bitmask ); unless (defined $session) { print_it("Cannot build Net::SNMP object for $host: $error"); return; } # Get value my $result = $session->get_request( -varbindlist => [$oid] ); $error = $session->error; # Print debugging information if ($debug > 3 and $debug < 6) { say "For $host:"; say " oid = $oid" if defined $oid; say " val = $val" if defined $val; } # Handle errors unless (defined $result) { undef $val; print_it("Unable to retrieve $oid from $host using version $version and string $read: $error\n") if $debug > 3; } # Unpack $result $val = $result->{$oid}; # Clean-up $session->close; } # end 'use Net::SNMP' # Something is broken else { confess "Cannot find an SNMP module"; } # Debug trace trace_location('end') if $debug > 3 and $debug < 6; return $val; } ######################################################################## # Given a hash which includes starting oid or object value, walk the # tree starting at that oid, returning a reference to an array of varbind # hashes which themselves contain the results # %hash = ( # host => {string or ip address}, # oid => {object value or oid}, # max_rep => {integer}, # msg_size => {integer bytes}, # read => {string}, # retries => {integer}, # non_rep => {integer}, # port => {port}, # timeout => {seconds or milliseconds}, # translate => {0 or 1}, # version => {integer} # ); ######################################################################## sub snmpWalk { my @answer; # An array of varbinds my $arg_ref = shift; # Input hash ref my $error; # Holds error string from SNMP operation my $host; # Host to query my $max_rep; # Maximum number of iterations over $starting_oid my $msg_size; # Maximum size of PDU my $non_rep; # Number of non-repetitions my $port; # UDP port for SNMP queries my $read; # SNMP community string my $result; # Holds result of walk my $retries; # Number of times to retry the query my $s; # Module object my $starting_oid; # Place from which we start to walk my $timeout; # Number of seconds to wait before retrying my $translate; # Translate output into human-readable format my $version; # SNMP version: 1, 2, or 3 # Debug trace trace_location('begin') if $debug > 2; # Sanity check confess 'No parameters!' unless defined $arg_ref; confess 'Parameter not a hash ref' unless ref $arg_ref eq 'HASH'; confess 'Must specify host' unless defined $arg_ref->{host}; confess 'Must specify oid' unless defined $arg_ref->{oid}; # Unpack args $host = $arg_ref->{host}; $starting_oid = $arg_ref->{oid}; $max_rep = defined $arg_ref->{max_rep} ? $arg_ref->{max_rep} : $snmp_max_rep ; $msg_size = defined $arg_ref->{msg_size} ? $arg_ref->{msg_size} : $snmp_max_msg_size ; $non_rep = defined $arg_ref->{non_rep} ? $arg_ref->{non_rep} : $snmp_non_rep ; $port = defined $arg_ref->{port} ? $arg_ref->{port} : $snmp_port ; $read = defined $arg_ref->{read} ? $arg_ref->{read} : $snmp_read{$host} ; $retries = defined $arg_ref->{retries} ? $arg_ref->{retries} : $snmp_retries ; $timeout = defined $arg_ref->{timeout} ? $arg_ref->{timeout} : $snmp_timeout ; $translate = defined $arg_ref->{translate} ? $arg_ref->{translate} : $snmp_translate ; $version = defined $arg_ref->{version} ? $arg_ref->{version} : $snmp_version{$host} ; # Debug info say " snmpWalk on $host for $starting_oid using $read" if ($debug > 3 and $debug < 6); # Use SNMP.pm (bundled with net-snmp) if ($snmp_module eq 'SNMP') { # Enable debugging $SNMP::verbose = $debug; $SNMP::dump_packet = 1 if $debug == 5; say ' Using SNMP.pm' if ($debug > 3 and $debug < 6); # Define SNMP session $s = SNMP::Session->new ( Community => $read, DestHost => $host, RemotePort => $port, Retries => $retries, Timeout => $timeout, UseSprintValue => $translate, Version => $version ); # Create the varbind my $vb = SNMP::Varbind->new([$starting_oid]); # Walk the tree if ($version == 1) { my $result = $s->getnext($vb); say ' Using walk' if ($debug > 3 and $debug < 6); until ($s->{ErrorNum} or not ($vb->[$SNMP::Varbind::tag_f] =~ /^$starting_oid/)) { my ($iid, $obj, $type, $val); my %varbind = ( oid => $vb->tag, iid => $vb->iid, val => $vb->val, type => $vb->type ); push @answer, \%varbind; $val = $s->getnext($vb); } } else { say ' Using bulkwalk' if ($debug > 3 and $debug < 6); $result = $s->bulkwalk($non_rep, $max_rep, $vb); for my $varbind (@{$result->[0]}) { my %varbind = ( oid => $varbind->tag, iid => $varbind->iid, val => $varbind->val, type => $varbind->type ); push @answer, \%varbind; } } $error = $s->{ErrorStr}; undef $error if $error eq $EMPTY_STR; # Debug info say "ERROR = $error" if defined $error and $debug > 1; say Dumper($result) if $debug == 6; } # end 'use SNMP' # Use Net::SNMP (david town's pure Perl module) elsif ($snmp_module eq 'Net::SNMP') { my $bitmask; switch ($debug) { case 5 { $bitmask = DEBUG_ALL } else { $bitmask = DEBUG_NONE } } say ' Using Net::SNMP' if ($debug > 3 and $debug < 6); # Create new session ($s, $error) = Net::SNMP->session( -hostname => $host, -port => $port, -version => $version, -timeout => $timeout, -retries => $retries, -community => $read, -translate => $translate, -maxmsgsize => $msg_size, -debug => $bitmask, ); unless (defined $s) { print_it("Cannot build Net::SNMP object for $host: $error"); return; } # Perform the walk using get_next_request if the agent speaks SNMP v1 if ($version == 1) { my $nextoid = $starting_oid; say ' Using walk' if ($debug > 3 and $debug < 6); WALK: while (defined $s->get_next_request( -varbindlist => [$nextoid] )) { my ($iid, $oid, $type, $val, %varbind); # Grab the result $oid = ($s->var_bind_names())[0]; # Quit if we've left the tree last WALK unless oid_base_match($starting_oid, $oid); last WALK if $s->var_bind_list()->{$oid} eq 'endOfMibView'; # Build the varbind ($iid) = ($oid) =~ /$starting_oid\.(.*)/; $val = $s->var_bind_list()->{$oid}; $type = snmp_type_ntop($s->var_bind_types()->{$oid}); %varbind = ( oid => $oid, iid => $iid, val => $val, type => $type ); push @answer, \%varbind; $nextoid = $oid; } # End WALK } # End 'version 1' # Perform the walk using get_bulk_request if the agent speaks SNMP v2c/3 else { my $nextoid = $starting_oid; say ' Using bulkwalk' if ($debug > 3 and $debug < 6); BULKWALK: while (defined $s->get_bulk_request( -varbindlist => [$nextoid], -maxrepetitions => $max_rep )) { # Walk the list of OIDs we just generated, building varbinds my @oids = oid_lex_sort(keys %{$s->var_bind_list()}); for my $oid (@oids) { my ($iid, $type, $val, %varbind); # Quit if we've left the tree last BULKWALK unless oid_base_match($starting_oid, $oid); last BULKWALK if $s->var_bind_list()->{$oid} eq 'endOfMibView'; # Build the varbind ($iid) = ($oid) =~ /$starting_oid\.(.*)/; $val = $s->var_bind_list()->{$oid}; $type = snmp_type_ntop($s->var_bind_types()->{$oid}); %varbind = ( oid => $oid, iid => $iid, val => $val, type => $type ); push @answer, \%varbind; } # End 'Walk the list of OIDs' $nextoid = pop @oids; } # End BULKWALK } # End 'use get_bulk_request' # Handle errors $error = $s->error; if (defined $error and $error ne $EMPTY_STR) { $error{$host} .= "Unable to walk $starting_oid "; if ($debug) { print_it("Unable to walk $starting_oid on $host: $error"); } } # Clean-up $s->close; } # end 'use Net::SNMP' # Something is broken else { confess 'Cannot find an SNMP module'; } # Debug trace trace_location('end') if $debug > 2; return \@answer; } ######################################################################## # Given a target and an OID, walk the OID and return a reference to a # hash of values indexed by iid. If provided a third argument, use that # as the snmp_translate value ######################################################################## sub walk_oid { my %arg; my $host = shift; my $oid = shift; my $ov; my %result; # Hash of values indexed by iid my $translate = shift; my $val; # Debug trace trace_location('begin') if $debug; # Sanity check confess 'Must provide a host' unless defined $host; confess 'Must provide an oid' unless defined $oid; if (defined $translate) { unless ($translate eq '0' or $translate eq '1') { confess 'Translate parameter must be 0 or 1'; } } else { $translate = 0; } # Translate OID $ov = translate_oid($oid); # Walk OID say " Walking $ov" if $debug > 2; %arg = ( host => $host, oid => $oid, translate => $translate ); $val = snmpWalk(\%arg); # Populate %result for my $varbind (@$val) { my $iid = $varbind->{iid}; my $val = $varbind->{val}; $result{$iid} = $val; } # Debug trace trace_location('end') if $debug; return \%result; } 1;