1 # Copyright (C) 2002-2011 Stanislav Sinyagin
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
17 # Stanislav Sinyagin <ssinyagin@yahoo.com>
19 # this policy complains about accessing private methods in Net::SNMP
20 ## no critic (Variables::ProtectPrivateVars)
22 # this policy complains about regexp result untested, although it is tested
23 ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
25 package Torrus
::Collector
::SNMP
;
29 use Torrus
::Collector
::SNMP_Params
;
30 use Torrus
::ConfigTree
;
32 use Torrus
::SNMP_Failures
;
36 use Net
::SNMP
qw(:snmp);
41 # Register the collector type
42 $Torrus::Collector
::collectorTypes
{'snmp'} = 1;
45 # List of needed parameters and default values
47 $Torrus::Collector
::params
{'snmp'} = {
48 'snmp-ipversion' => undef,
49 'snmp-transport' => undef,
50 'snmp-version' => undef,
52 'snmp-community' => undef,
53 'snmp-username' => undef,
54 'snmp-authkey' => undef,
55 'snmp-authpassword' => undef,
56 'snmp-authprotocol' => 'md5',
57 'snmp-privkey' => undef,
58 'snmp-privpassword' => undef,
59 'snmp-privprotocol' => 'des',
60 'snmp-timeout' => undef,
61 'snmp-retries' => undef,
62 'domain-name' => undef,
64 'snmp-localaddr' => undef,
65 'snmp-localport' => undef,
66 'snmp-object' => undef,
67 'snmp-oids-per-pdu' => undef,
68 'snmp-object-type' => 'OTHER',
69 'snmp-check-sysuptime' => 'yes',
70 'snmp-max-msg-size' => undef,
71 'snmp-maxrepetitions' => 10,
72 'snmp-ignore-mib-errors' => undef,
75 my $sysUpTime = '1.3.6.1.2.1.1.3.0';
77 # Hosts that are running SNMPv1. We do not reresh maps on them, as
81 # SNMP tables lookup maps
84 # Old lookup maps, used temporarily during refresh cycle
87 # How frequent we refresh the SNMP mapping
88 our $mapsRefreshPeriod;
90 # Random factor in refresh period
91 our $mapsRefreshRandom;
93 # Time period after configuration re-compile when we refresh existing mappings
94 our $mapsUpdateInterval;
96 # how often we check for expired maps
97 our $mapsExpireCheckPeriod;
99 # expiration time for each map
102 # Lookups scheduled for execution
103 my %mapLookupScheduled;
105 # SNMP session objects for map lookups
109 # Timestamps of hosts last found unreachable
110 my %hostUnreachableSeen;
112 # Last time we tried to reach an unreachable host
113 my %hostUnreachableRetry;
115 # Hosts that were deleted because of unreachability for too long
116 my %unreachableHostDeleted;
121 # Flush stats after a restart or recompile
122 $Torrus::Collector
::initCollectorGlobals
{'snmp'} =
123 \
&Torrus
::Collector
::SNMP
::initCollectorGlobals
;
125 sub initCollectorGlobals
128 my $instance = shift;
130 if( not defined( $db_failures ) )
133 Torrus
::SNMP_Failures
->new( -Tree
=> $tree,
134 -Instance
=> $instance,
138 if( defined( $db_failures ) )
140 $db_failures->init();
143 # re-init counters and collect garbage
145 %hostUnreachableSeen = ();
146 %hostUnreachableRetry = ();
147 %unreachableHostDeleted = ();
149 # Configuration re-compile was probably caused by new object instances
150 # appearing on the monitored devices. Here we force the maps to refresh
151 # soon enough in order to catch up with the changes
154 for my $maphash ( keys %mapsExpire )
156 $mapsExpire{$maphash} = int( $now + rand( $mapsUpdateInterval ) );
162 # This is first executed per target
164 $Torrus::Collector
::initTarget
{'snmp'} = \
&Torrus
::Collector
::SNMP
::initTarget
;
170 my $collector = shift;
173 my $tref = $collector->tokenData( $token );
175 $collector->registerDeleteCallback
176 ( $token, \
&Torrus
::Collector
::SNMP
::deleteTarget
);
178 my $hosthash = getHostHash
( $collector, $token );
179 if( not defined( $hosthash ) )
184 $tref->{'hosthash'} = $hosthash;
186 return Torrus
::Collector
::SNMP
::initTargetAttributes
( $collector, $token );
190 sub initTargetAttributes
192 my $collector = shift;
195 &Torrus
::DB
::checkInterrupted
();
197 my $tref = $collector->tokenData( $token );
198 my $cref = $collector->collectorData( 'snmp' );
200 my $hosthash = $tref->{'hosthash'};
202 my $version = $collector->param($token, 'snmp-version');
203 if( $version eq '1' )
205 $snmpV1Hosts{$hosthash} = 1;
208 # If the object is defined as a map, retrieve the whole map
211 if( isHostDead
( $collector, $hosthash ) )
216 if( not checkUnreachableRetry
( $collector, $hosthash ) )
218 $cref->{'needsRemapping'}{$token} = 1;
222 my $oid = $collector->param($token, 'snmp-object');
223 $oid = expandOidMappings
( $collector, $token, $hosthash, $oid );
227 if( $unreachableHostDeleted{$hosthash} )
229 # we tried our best, but the target is dead
234 # we return OK status, to let the storage initiate
235 $cref->{'needsRemapping'}{$token} = 1;
239 elsif( $oid eq 'notfound' )
244 # Collector should be able to find the target
245 # by host, port, community, and oid.
246 # There can be several targets with the same host|port|community+oid set.
248 $cref->{'targets'}{$hosthash}{$oid}{$token} = 1;
249 $cref->{'activehosts'}{$hosthash} = 1;
251 $tref->{'oid'} = $oid;
253 $cref->{'oids_per_pdu'}{$hosthash} =
254 $collector->param($token, 'snmp-oids-per-pdu');
256 if( $collector->paramString($token, 'snmp-object-type') eq 'COUNTER64' )
258 $cref->{'64bit_oid'}{$oid} = 1;
261 if( $collector->paramDisabled($token, 'snmp-check-sysuptime') )
263 $cref->{'nosysuptime'}{$hosthash} = 1;
266 if( $collector->paramEnabled($token, 'snmp-ignore-mib-errors') )
268 $cref->{'ignoremiberrors'}{$hosthash}{$oid} = 1;
277 my $collector = shift;
280 my $hostname = $collector->param($token, 'snmp-host');
281 my $domain = $collector->paramString($token, 'domain-name');
283 if( $domain ne '' and
284 index($hostname, '.') < 0 and
285 index($hostname, ':') < 0 )
287 $hostname .= '.' . $domain;
290 my $port = $collector->param($token, 'snmp-port');
291 my $version = $collector->param($token, 'snmp-version');
294 if( $version eq '1' or $version eq '2c' )
296 $community = $collector->param($token, 'snmp-community');
300 # We use community string to identify the agent.
301 # For SNMPv3, it's the user name
302 $community = $collector->param($token, 'snmp-username');
305 return join('|', $hostname, $port, $community);
311 my $collector = shift;
313 my $hosthash = shift;
315 my $cref = $collector->collectorData( 'snmp' );
316 if( defined( $cref->{'snmpargs'}{$hosthash} ) )
318 return $cref->{'snmpargs'}{$hosthash};
321 my $transport = $collector->param($token, 'snmp-transport') . '/ipv' .
322 $collector->param($token, 'snmp-ipversion');
324 my ($hostname, $port, $community) = split(/\|/o, $hosthash);
326 my $version = $collector->param($token, 'snmp-version');
327 my $ret = [ -domain
=> $transport,
328 -hostname
=> $hostname,
330 -timeout
=> $collector->param($token, 'snmp-timeout'),
331 -retries
=> $collector->param($token, 'snmp-retries'),
332 -version
=> $version ];
334 for my $arg ( qw(-localaddr -localport) )
336 my $val = $collector->param($token, 'snmp' . $arg);
337 if( defined( $val ) )
339 push( @
{$ret}, $arg, $val );
343 if( $version eq '1' or $version eq '2c' )
345 push( @
{$ret}, '-community', $community );
349 push( @
{$ret}, -username
, $community);
351 for my $arg ( qw(-authkey -authpassword -authprotocol
352 -privkey -privpassword -privprotocol) )
354 my $val = $collector->param($token, 'snmp' . $arg);
355 if( defined( $val ) )
357 push( @
{$ret}, $arg, $val );
362 $cref->{'snmpargs'}{$hosthash} = $ret;
368 sub openBlockingSession
370 my $collector = shift;
372 my $hosthash = shift;
374 my $args = snmpSessionArgs
( $collector, $token, $hosthash );
375 my ($session, $error) =
376 Net
::SNMP
->session( @
{$args},
378 -translate
=> ['-all', 0, '-octetstring', 1] );
379 if( not defined($session) )
381 Error
('Cannot create SNMP session for ' . $hosthash . ': ' . $error);
385 my $maxmsgsize = $collector->param($token, 'snmp-max-msg-size');
386 if( defined( $maxmsgsize ) and $maxmsgsize > 0 )
388 $session->max_msg_size( $maxmsgsize );
395 sub openNonblockingSession
397 my $collector = shift;
399 my $hosthash = shift;
401 my $args = snmpSessionArgs
( $collector, $token, $hosthash );
403 my ($session, $error) =
404 Net
::SNMP
->session( @
{$args},
406 -translate
=> ['-timeticks' => 0] );
407 if( not defined($session) )
409 Error
('Cannot create SNMP session for ' . $hosthash . ': ' . $error);
413 if( $collector->param($token, 'snmp-transport') eq 'udp' )
415 # We set SO_RCVBUF only once, because Net::SNMP shares
416 # one UDP socket for all sessions.
418 my $sock_name = $session->transport()->sock_name();
419 my $refcount = $Net::SNMP
::Transport
::SOCKETS
->{
420 $sock_name}->[&Net
::SNMP
::Transport
::_SHARED_REFC
()];
422 if( defined($refcount) and $refcount == 1 )
424 my $buflen = int($Torrus::Collector
::SNMP
::RxBuffer
);
425 my $socket = $session->transport()->socket();
426 my $ok = $socket->sockopt( SO_RCVBUF
, $buflen );
429 Error
('Could not set SO_RCVBUF to ' .
430 $buflen . ': ' . $!);
434 Debug
('Set SO_RCVBUF to ' . $buflen);
439 my $maxmsgsize = $collector->param($token, 'snmp-max-msg-size');
440 if( defined( $maxmsgsize ) and $maxmsgsize > 0 )
442 $session->max_msg_size( $maxmsgsize );
450 sub expandOidMappings
452 my $collector = shift;
454 my $hosthash = shift;
457 my $cref = $collector->collectorData( 'snmp' );
461 # Process Map statements
463 while( index( $oid, 'M(' ) >= 0 )
465 if( not $oid =~ m/^(.*)M\(\s*([0-9\.]+)\s*,\s*([^\)]+)\)(.*)$/o )
467 Error
("Error in OID mapping syntax: $oid");
476 # Remove trailing space from key
480 lookupMap
( $collector, $token, $hosthash, $map, $key );
482 if( defined( $value ) )
484 if( $value eq 'notfound' )
490 $oid = $head . $value . $tail;
499 # process value lookups
501 while( index( $oid, 'V(' ) >= 0 )
503 if( not $oid =~ /^(.*)V\(\s*([0-9\.]+)\s*\)(.*)$/o )
505 Error
("Error in OID value lookup syntax: $oid");
515 if( not defined( $cref->{'value-lookups'}
516 {$hosthash}{$key} ) )
518 # Retrieve the OID value from host
520 my $session = openBlockingSession
( $collector, $token, $hosthash );
521 if( not defined($session) )
526 my $result = $session->get_request( -varbindlist
=> [$key] );
528 if( defined $result and defined($result->{$key}) )
530 $value = $result->{$key};
531 $cref->{'value-lookups'}{$hosthash}{$key} = $value;
535 Error
("Error retrieving $key from $hosthash: " .
537 probablyDead
( $collector, $hosthash );
544 $cref->{'value-lookups'}{$hosthash}{$key};
546 if( defined( $value ) )
548 $oid = $head . $value . $tail;
556 # Debug('OID expanded: ' . $oid_in . ' -> ' . $oid');
560 # Look up table index in a map by value
564 my $collector = shift;
566 my $hosthash = shift;
570 my $cref = $collector->collectorData( 'snmp' );
571 my $maphash = join('#', $hosthash, $map);
573 if( not defined( $maps{$hosthash}{$map} ) )
577 if( defined( $oldMaps{$hosthash}{$map} ) and
580 $ret = $oldMaps{$hosthash}{$map}{$key};
583 if( $mapLookupScheduled{$maphash} )
588 if( not $collector->didNotRun()
590 (scalar(@mappingSessions) >=
591 $Torrus::Collector
::SNMP
::maxSessionsPerDispatcher
) )
594 @mappingSessions = ();
595 %mapLookupScheduled = ();
598 # Retrieve map from host
599 Debug
('Retrieving map ' . $map . ' from ' . $hosthash);
601 my $session = openNonblockingSession
( $collector, $token, $hosthash );
602 if( not defined($session) )
608 push( @mappingSessions, $session );
611 # Retrieve the map table
613 my $maxrepetitions = $collector->param($token, 'snmp-maxrepetitions');
614 $session->get_table( -baseoid
=> $map,
615 -maxrepetitions
=> $maxrepetitions,
616 -callback
=> [\
&mapLookupCallback
,
617 $collector, $hosthash, $map] );
619 $mapLookupScheduled{$maphash} = 1;
621 if( not $snmpV1Hosts{$hosthash} )
623 $mapsExpire{$maphash} =
624 int( time() + $mapsRefreshPeriod +
625 rand( $mapsRefreshPeriod * $mapsRefreshRandom ) );
631 if( defined( $key ) )
633 my $value = $maps{$hosthash}{$map}{$key};
634 if( not defined $value )
636 Error
("Cannot find value $key in map $map for $hosthash in ".
637 $collector->path($token));
638 if( defined ( $maps{$hosthash}{$map} ) )
640 Error
('Current map follows');
641 while( my($mkey, $mval) = each
642 %{$maps{$hosthash}{$map}} )
644 Error
("'$mkey' => '$mval'");
651 if( not $snmpV1Hosts{$hosthash} )
653 $cref->{'mapsDependentTokens'}{$maphash}{$token} = 1;
654 $cref->{'mapsRelatedMaps'}{$token}{$maphash} = 1;
667 sub mapLookupCallback
670 my $collector = shift;
671 my $hosthash = shift;
674 &Torrus
::DB
::checkInterrupted
();
676 Debug
('Received mapping PDU from ' . $hosthash);
678 my $result = $session->var_bind_list();
679 if( defined $result )
681 my $preflen = length($map) + 1;
683 while( my( $oid, $key ) = each %{$result} )
685 my $val = substr($oid, $preflen);
686 $maps{$hosthash}{$map}{$key} = $val;
687 # Debug("Map $map discovered: '$key' -> '$val'");
692 Error
("Error retrieving table $map from $hosthash: " .
695 probablyDead
( $collector, $hosthash );
702 sub activeMappingSessions
704 return scalar( @mappingSessions );
707 # The target host is unreachable. We try to reach it few more times and
708 # give it the final diagnose.
712 my $collector = shift;
713 my $hosthash = shift;
715 my $cref = $collector->collectorData( 'snmp' );
717 # Stop all collection for this host, until next initTargetAttributes
719 delete $cref->{'activehosts'}{$hosthash};
721 my $probablyAlive = 1;
723 if( defined( $hostUnreachableSeen{$hosthash} ) )
725 if( $Torrus::Collector
::SNMP
::unreachableTimeout
> 0 and
727 $hostUnreachableSeen{$hosthash} >
728 $Torrus::Collector
::SNMP
::unreachableTimeout
)
735 $hostUnreachableSeen{$hosthash} = time();
737 if( defined( $db_failures ) )
739 $db_failures->host_failure('unreachable', $hosthash);
740 $db_failures->set_counter('unreachable',
741 scalar( keys %hostUnreachableSeen));
747 Info
('Target host is unreachable. Will try again later: ' . $hosthash);
751 # It is dead indeed. Delete all tokens associated with this host
752 Info
('Target host is unreachable during last ' .
753 $Torrus::Collector
::SNMP
::unreachableTimeout
.
754 ' seconds. Giving it up: ' . $hosthash);
755 my @deleteTargets = ();
756 while( my ($oid, $ref1) =
757 each %{$cref->{'targets'}{$hosthash}} )
759 while( my ($token, $dummy) = each %{$ref1} )
761 push( @deleteTargets, $token );
765 Debug
('Deleting ' . scalar( @deleteTargets ) . ' tokens');
766 for my $token ( @deleteTargets )
768 $collector->deleteTarget($token);
771 delete $hostUnreachableSeen{$hosthash};
772 delete $hostUnreachableRetry{$hosthash};
773 $unreachableHostDeleted{$hosthash} = 1;
775 if( defined( $db_failures ) )
777 $db_failures->host_failure('deleted', $hosthash);
778 $db_failures->set_counter('unreachable',
779 scalar( keys %hostUnreachableSeen));
780 $db_failures->set_counter('deleted',
781 scalar( keys %unreachableHostDeleted));
785 return $probablyAlive;
788 # Return false if the try is too early
790 sub checkUnreachableRetry
792 my $collector = shift;
793 my $hosthash = shift;
796 if( $hostUnreachableSeen{$hosthash} )
798 my $lastRetry = $hostUnreachableRetry{$hosthash};
800 if( not defined( $lastRetry ) )
802 $lastRetry = $hostUnreachableSeen{$hosthash};
805 if( time() < $lastRetry +
806 $Torrus::Collector
::SNMP
::unreachableRetryDelay
)
812 $hostUnreachableRetry{$hosthash} = time();
822 my $collector = shift;
823 my $hosthash = shift;
825 return $unreachableHostDeleted{$hosthash};
829 sub hostReachableAgain
831 my $collector = shift;
832 my $hosthash = shift;
834 if( exists( $hostUnreachableSeen{$hosthash} ) )
836 delete $hostUnreachableSeen{$hosthash};
837 if( defined( $db_failures ) )
839 $db_failures->remove_host($hosthash);
840 $db_failures->set_counter('unreachable',
841 scalar( keys %hostUnreachableSeen));
848 # Callback executed by Collector
852 my $collector = shift;
855 my $tref = $collector->tokenData( $token );
856 my $cref = $collector->collectorData( 'snmp' );
858 my $hosthash = $tref->{'hosthash'};
859 my $oid = $tref->{'oid'};
863 delete $cref->{'targets'}{$hosthash}{$oid}{$token};
864 if( scalar(keys %{$cref->{'targets'}{$hosthash}{$oid}}) == 0 )
866 delete $cref->{'targets'}{$hosthash}{$oid};
868 if( scalar(keys %{$cref->{'targets'}{$hosthash}}) == 0 )
870 delete $cref->{'targets'}{$hosthash};
875 delete $cref->{'needsRemapping'}{$token};
877 for my $maphash ( keys %{$cref->{'mapsRelatedMaps'}{$token}} )
879 delete $cref->{'mapsDependentTokens'}{$maphash}{$token};
881 delete $cref->{'mapsRelatedMaps'}{$token};
885 # Main collector cycle
887 $Torrus::Collector
::runCollector
{'snmp'} =
888 \
&Torrus
::Collector
::SNMP
::runCollector
;
892 my $collector = shift;
895 # Info(sprintf('runCollector() Offset: %d, active hosts: %d, maps: %d',
896 # $collector->offset(),
897 # scalar( keys %{$cref->{'activehosts'}} ),
898 # scalar(keys %maps)));
900 # Create one SNMP session per host address.
901 # We assume that version, timeout and retries are the same
904 # We limit the number of sessions per snmp_dispatcher run
905 # because of some strange bugs: with more than 400 sessions per
906 # dispatcher, some requests are not sent out
908 my @hosts = keys %{$cref->{'activehosts'}};
910 while( scalar(@mappingSessions) + scalar(@hosts) > 0 )
913 while( ( scalar(@mappingSessions) + scalar(@batch) <
914 $Torrus::Collector
::SNMP
::maxSessionsPerDispatcher
)
918 push( @batch, pop( @hosts ) );
921 &Torrus
::DB
::checkInterrupted
();
925 for my $hosthash ( @batch )
927 my @oids = sort keys %{$cref->{'targets'}{$hosthash}};
929 # Info(sprintf('Host %s: %d OIDs',
933 # Find one representative token for the host
935 if( scalar( @oids ) == 0 )
940 my @reptokens = keys %{$cref->{'targets'}{$hosthash}{$oids[0]}};
941 if( scalar( @reptokens ) == 0 )
945 my $reptoken = $reptokens[0];
948 openNonblockingSession
( $collector, $reptoken, $hosthash );
950 &Torrus
::DB
::checkInterrupted
();
952 if( not defined($session) )
958 Debug
('Created SNMP session for ' . $hosthash);
959 push( @sessions, $session );
962 my $oids_per_pdu = $cref->{'oids_per_pdu'}{$hosthash};
967 while( scalar( @oids ) > 0 )
969 my $oid = shift @oids;
970 push( @pdu_oids, $oid );
972 if( scalar( @oids ) == 0 or
973 ( scalar( @pdu_oids ) >= $oids_per_pdu ) )
975 if( not $cref->{'nosysuptime'}{$hosthash} )
977 # We insert sysUpTime into every PDU, because
978 # we need it in further processing
979 push( @pdu_oids, $sysUpTime );
982 if( Torrus
::Log
::isDebug
() )
984 Debug
('Sending SNMP PDU to ' . $hosthash . ':');
985 for my $oid ( @pdu_oids )
991 # Generate the list of tokens that form this PDU
993 for my $oid ( @pdu_oids )
995 if( defined( $cref->{'targets'}{$hosthash}{$oid} ) )
998 ( keys %{$cref->{'targets'}{$hosthash}{$oid}} )
1000 $pdu_tokens->{$oid}{$token} = 1;
1006 get_request
( -delay
=> $delay,
1008 [ \
&Torrus
::Collector
::SNMP
::callback
,
1009 $collector, $pdu_tokens, $hosthash ],
1010 -varbindlist
=> \
@pdu_oids );
1011 if( not defined $result )
1013 Error
('Cannot create SNMP request: ' .
1022 &Torrus
::DB
::checkInterrupted
();
1026 # Check if there were pending map lookup sessions
1028 if( scalar( @mappingSessions ) > 0 )
1030 @mappingSessions = ();
1031 %mapLookupScheduled = ();
1040 my $session = shift;
1041 my $collector = shift;
1042 my $pdu_tokens = shift;
1043 my $hosthash = shift;
1045 &Torrus
::DB
::checkInterrupted
();
1047 my $cref = $collector->collectorData( 'snmp' );
1049 Debug
('SNMP Callback executed for ' . $hosthash);
1051 if( not defined( $session->var_bind_list() ) )
1053 Error
('SNMP Error for ' . $hosthash . ': ' . $session->error() .
1054 ' when retrieving ' . join(' ', sort keys %{$pdu_tokens}));
1056 probablyDead
( $collector, $hosthash );
1059 delete $maps{$hosthash};
1060 for my $oid ( keys %{$pdu_tokens} )
1062 for my $token ( keys %{$pdu_tokens->{$oid}} )
1064 $cref->{'needsRemapping'}{$token} = 1;
1071 hostReachableAgain
( $collector, $hosthash );
1074 my $timestamp = time();
1076 my $checkUptime = not $cref->{'nosysuptime'}{$hosthash};
1083 my $uptimeTicks = $session->var_bind_list()->{$sysUpTime};
1084 if( defined $uptimeTicks )
1086 $uptime = $uptimeTicks / 100;
1087 Debug
('Uptime: ' . $uptime);
1091 Error
('Did not receive sysUpTime for ' . $hosthash);
1094 if( $uptime < $collector->period() or
1095 ( defined($cref->{'knownUptime'}{$hosthash})
1097 $uptime + $collector->period() <
1098 $cref->{'knownUptime'}{$hosthash} ) )
1100 # The agent has reloaded. Clean all maps and push UNDEF
1101 # values to the storage
1103 Info
('Agent rebooted: ' . $hosthash);
1104 delete $maps{$hosthash};
1106 $timestamp -= $uptime;
1107 for my $oid ( keys %{$pdu_tokens} )
1109 for my $token ( keys %{$pdu_tokens->{$oid}} )
1111 $collector->setValue( $token, 'U', $timestamp, $uptime );
1112 $cref->{'needsRemapping'}{$token} = 1;
1118 $cref->{'knownUptime'}{$hosthash} = $uptime;
1123 while( my ($oid, $value) = each %{ $session->var_bind_list() } )
1125 # Debug("OID=$oid, VAL=$value");
1126 if( $value eq 'noSuchObject' or
1127 $value eq 'noSuchInstance' or
1128 $value eq 'endOfMibView' )
1130 if( not $cref->{'ignoremiberrors'}{$hosthash}{$oid} )
1132 Error
("Error retrieving $oid from $hosthash: $value");
1134 for my $token ( keys %{$pdu_tokens->{$oid}} )
1136 if( defined( $db_failures ) )
1138 $db_failures->mib_error
1139 ($hosthash, $collector->path($token));
1142 $collector->deleteTarget($token);
1148 if( $cref->{'64bit_oid'}{$oid} )
1150 $value = Math
::BigInt
->new($value);
1153 for my $token ( keys %{$pdu_tokens->{$oid}} )
1155 $collector->setValue( $token, $value,
1156 $timestamp, $uptime );
1165 # Execute this after the collector has finished
1167 $Torrus::Collector
::postProcess
{'snmp'} =
1168 \
&Torrus
::Collector
::SNMP
::postProcess
;
1172 my $collector = shift;
1175 # It could happen that postProcess is called for a collector which
1176 # has no targets, and therefore it's the only place where we can
1177 # initialize these variables
1179 if( not defined( $cref->{'mapsLastExpireChecked'} ) )
1181 $cref->{'mapsLastExpireChecked'} = 0;
1184 if( not defined( $cref->{'mapsRefreshed'} ) )
1186 $cref->{'mapsRefreshed'} = [];
1189 # look if some maps are ready after last expiration check
1190 if( scalar( @
{$cref->{'mapsRefreshed'}} ) > 0 )
1192 for my $maphash ( @
{$cref->{'mapsRefreshed'}} )
1195 ( keys %{$cref->{'mapsDependentTokens'}{$maphash}} )
1197 $cref->{'needsRemapping'}{$token} = 1;
1200 $cref->{'mapsRefreshed'} = [];
1205 if( $cref->{'mapsLastExpireChecked'} + $mapsExpireCheckPeriod <= $now )
1207 $cref->{'mapsLastExpireChecked'} = $now;
1209 # Check the maps expiration and arrange lookup for expired
1211 while( my ( $maphash, $expire ) = each %mapsExpire )
1213 if( $expire <= $now and not $mapLookupScheduled{$maphash} )
1215 &Torrus
::DB
::checkInterrupted
();
1217 my ( $hosthash, $map ) = split( /\#/o, $maphash );
1219 if( $unreachableHostDeleted{$hosthash} )
1221 # This host is no longer polled. Remove the leftovers
1223 delete $mapsExpire{$maphash};
1224 delete $maps{$hosthash};
1228 # Find one representative token for the map
1230 keys %{$cref->{'mapsDependentTokens'}{$maphash}};
1231 if( scalar( @tokens ) == 0 )
1235 my $reptoken = $tokens[0];
1237 # save the map for the time of refresh
1238 $oldMaps{$hosthash}{$map} = $maps{$hosthash}{$map};
1239 delete $maps{$hosthash}{$map};
1241 # this will schedule the map retrieval for the next
1243 Debug
('Refreshing map: ' . $maphash);
1245 lookupMap
( $collector, $reptoken,
1246 $hosthash, $map, undef );
1248 # After the next collector period, the maps will be
1249 # ready and tokens may be updated without losing the data
1250 push( @
{$cref->{'mapsRefreshed'}}, $maphash );
1256 for my $token ( keys %{$cref->{'needsRemapping'}} )
1258 &Torrus
::DB
::checkInterrupted
();
1260 delete $cref->{'needsRemapping'}{$token};
1261 if( not Torrus
::Collector
::SNMP
::initTargetAttributes
1262 ( $collector, $token ) )
1264 $collector->deleteTarget($token);
1271 #### ==== SNMP Reachability Collector ====
1273 # Register the collector type (it should always run after snmp collector)
1274 $Torrus::Collector
::collectorTypes
{'snmp-reachable'} = 2;
1277 # List of needed parameters and default values
1279 $Torrus::Collector
::params
{'snmp-reachable'} = {
1280 'snmp-version' => undef,
1281 'snmp-port' => undef,
1282 'snmp-community' => undef,
1283 'snmp-username' => undef,
1284 'domain-name' => undef,
1285 'snmp-host' => undef,
1289 # This is first executed per target
1291 $Torrus::Collector
::initTarget
{'snmp-reachable'} = \
&reachable_initTarget
;
1294 sub reachable_initTarget
1296 my $collector = shift;
1299 my $cref = $collector->collectorData('snmp-reachable');
1300 my $tref = $collector->tokenData( $token );
1301 my $hosthash = getHostHash
( $collector, $token );
1302 if( not defined( $hosthash ) )
1307 $tref->{'hosthash'} = $hosthash;
1308 $cref->{'targets'}{$hosthash}{$token} = 1;
1314 # Main collector cycle
1316 $Torrus::Collector
::runCollector
{'snmp-reachable'} = \
&reachable_runCollector
;
1318 sub reachable_runCollector
1320 my $collector = shift;
1323 if( not defined( $db_failures ) )
1325 Error
('$db_failures is not initialized. ' .
1326 'snmp-reachable collector cannot continue');
1330 my $timestamp = time();
1332 while(my ($hosthash, $r) = each %{$cref->{'targets'}} )
1334 for my $token (keys %{$r})
1336 my $val = $db_failures->is_host_available($hosthash) ?
100:0;
1337 $collector->setValue( $token, $val, $timestamp, 0 );
1349 # indent-tabs-mode: nil
1350 # perl-indent-level: 4