I'm quite new at Perl and have written a script that polls network equipment for sysUptime data. It first queries the database for possible hosts, returns the information stored in the db as an array, then proceeds to poll the host for uptime by Net::SNMP
for each array element, based on its IP address. The script looks like this (with relevant portions):
use Net::SNMP;
# Loop through the DB array
while( my @row = $sth->fetchrow_array() ) {
# Dereference into columns
my ( $hostname, $ip, $vendor, $model, $status, $queue ) = @row;
# Pad and print
printf( "%-24s%-18s%-16s%-20s%-16s%-14s", $hostname, $ip, $vendor, $model, $status, $queue );
# Open the SNMP session
my ($session, $error) = Net::SNMP->session( -hostname => $ip,
-community => 'communityname',
-version => 'v2c',
-timeout => 5,
-retries => 0 );
# Report error and close it the session doesn't open
if( !defined $session ) { printf "[SESSION ERROR] %s\n", $error; $session->close(); }
# Get the host uptime.
# *** This is the line where Perl fails with the error message. ***
my $snmp_response = $session->get_request( -varbindlist => [ ".1.3.6.1.2.1.1.3.0" ] )->{".1.3.6.1.2.1.1.3.0"};
# If we don't get a response as the var isn't getting defined, report and close
if( !defined $snmp_response ) {
printf "[RESPONSE ERROR]: %s\n", $error;
$session->close();
# Otherwise print the query result and then close
} else {
print $snmp_response;
$session->close();
}
}
The script works every time for hosts that are reachable (and returns their correct sysUptime response). A typical valid @row
would look like this:
router10.10.10.1CiscoISR4221ProdQ1
The script dies at the first unreachable instance with "Can't use an undefined value as a HASH reference at script.pl"
and I can't figure out why this is happening. I have used "-debug => DEBUG_ALL"
in the session, and can see that the host is timing out, and rather than returning the response timeout error, it just dies.
The following output is from Net::SNMP->session
output when the -debug => DEBUG_ALL
flag is used.
error: [349] Net::SNMP::Dispatcher::_transport_timeout(): No response from remote host "10.10.10.10"
error: [2363] Net::SNMP::__ANON__(): No response from remote host "10.10.10.10"
debug: [517] Net::SNMP::Dispatcher::_event_delete(): deleted [ARRAY(0x2b8bae8)], list is now empty
Can't use an undefined value as a HASH reference at script.pl line 103.
It also doesn't change the situation whether I increase the timeout or retry values. I can't get my head around to understand why it works for the working hosts but fails before grasping the definition error.
If I use Data::Dumper
, it still doesn't output anything for the failed host. For a working host, the Dumper output is as follows:
$VAR1 = bless( {
'_transport_argv' => [
'-hostname',
'10.10.10.1',
'-timeout',
5,
'-retries',
0
],
'_nonblocking' => 0,
'_discovery_queue' => [],
'_error' => undef,
'_context_name' => undef,
'_version' => 1,
'_delay' => 0,
'_translate' => 255,
'_callback' => undef,
'_transport' => bless( {
'_dest_name' => '��a�(',
'_socket' => bless( \*Symbol::GEN16, 'IO::Socket' ),
'_sock_name' => '',
'_timeout' => 5,
'_dest_hostname' => '10.10.10.1',
'_max_msg_size' => 1472,
'_sock_hostname' => '',
'_error' => undef,
'_retries' => 0
}, 'Net::SNMP::Transport::IPv4::UDP' ),
'_hostname' => '10.10.10.1',
'_security' => bless( {
'_error' => undef,
'_community' => 'communityname',
'_version' => 1
}, 'Net::SNMP::Security::Community' ),
'_context_engine_id' => undef,
'_pdu' => bless( {
'_security' => $VAR1->{'_security'},
'_request_id' => 2009673841,
'_index' => 48,
'_leading_dot' => 1,
'_buffer' => '0.communityname�!w�0q0+C�S�f',
'_timeout_id' => [],
'_callback' => undef,
'_transport' => $VAR1->{'_transport'},
'_length' => 48,
'_error_index' => 0,
'_var_bind_types' => {
'.1.3.6.1.2.1.1.3.0' => 67
},
'_version' => 1,
'_security_name' => 'community',
'_var_bind_list' => {
'.1.3.6.1.2.1.1.3.0' => '255 days, 00:18:57.66'
},
'_translate' => 255,
'_error_status' => 0,
'_scoped' => 0,
'_var_bind_names' => [
'.1.3.6.1.2.1.1.3.0'
],
'_error' => undef,
'_pdu_type' => 162
}, 'Net::SNMP::PDU' )
}, 'Net::SNMP' );
Now.. the culprit with the failing host is that the SNMP community for it is different than for the other hosts -- however, even then, shouldn't the script already return the session error as it is failing to open the session to the host in question?
Let's check the line where it is failing (shown with some added new lines for better reading):
my $snmp_response =
$session->get_request( -varbindlist => [ ".1.3.6.1.2.1.1.3.0" ] )
->{".1.3.6.1.2.1.1.3.0"};
This is basically the same as
my $result = $session->get_request( -varbindlist => [ ".1.3.6.1.2.1.1.3.0" ] );
my $snmp_response = $result->{".1.3.6.1.2.1.1.3.0"};
The problem is when the first line with get_request
fails - because this will return undef. Then it will try to treat an undefined value as a hash reference - which is exactly the error you got.
The fix is not to ignore that get_request
might fail. But check for it before using it as a hash.