File Coverage

blib/lib/Net/SNMP/Util.pm
Criterion Covered Total %
statement 316 366 86.3
branch 115 170 67.6
condition 21 29 72.4
subroutine 25 32 78.1
pod 10 10 100.0
total 487 607 80.2


line stmt bran cond sub pod time code
1             # =============================================================================
2             package Net::SNMP::Util;
3             # -----------------------------------------------------------------------------
4             $Net::SNMP::Util::VERSION = '1.04';
5             # -----------------------------------------------------------------------------
6 6     6   656091 use strict;
  6         17  
  6         280  
7 6     6   39 use warnings;
  6         12  
  6         242  
8              
9 6     6   36 use constant DEBUG => 0;
  6         16  
  6         1697  
10             do { require Data::Dumper; import Data::Dumper; } if DEBUG;
11              
12              
13             =head1 NAME
14              
15             Net::SNMP::Util - Utility functions for Net::SNMP
16              
17             =head1 SYNOPSIS
18              
19             @hosts = qw( host1 host2 host3 );
20             %oids = (
21             'ifType' => '1.3.6.1.2.1.2.2.1.3',
22             'ifXData => [ '1.3.6.1.2.1.31.1.1.1.1', # ifName
23             '1.3.6.1.2.1.31.1.1.1.15' ], # ifHighSpeed
24             'someMib' => '1.3.6.1.4.1.99999.12.3'
25             );
26             %snmpparams = (
27             -version => 2,
28             -community => "comname"
29             );
30              
31             # Blocking Function
32             use Net::SNMP::Util;
33              
34             ($result,$error) = snmpawlk(
35             hosts => \@hosts,
36             oids => \%oids,
37             snmp => \%snmpparams
38             );
39             die "[ERROR] $error\n" unless defined $result;
40              
41             # Non-blocking One
42             use Net::SNMP::Util qw(:para);
43              
44             ($result,$error) = snmpparawalk(
45             hosts => \@hosts,
46             oids => \%oids,
47             snmp => \%snmpparams
48             );
49             die "[ERROR] $error\n" unless defined $result;
50              
51             # output result sample
52             foreach $host ( @hosts ){
53             foreach $index ( sort keys %{$result->{$host}{ifType}} ){
54             printf "$host - $index - type:%d - %s (%d kbps)\n",
55             $result->{$host}{ifType}{$index},
56             $result->{$host}{ifXData}[0]{$index}, # ifName
57             $result->{$host}{ifXData}[1]{$index}; # ifHighSpeed
58             }
59             }
60              
61              
62             =head1 DESCRIPTION
63              
64             This module, C, gives you functions of SNMP getting operation
65             interfaces using L communicating with B and B.
66              
67              
68             =head1 OVERVIEW
69              
70             Functions of C are grouped by type whether using B
71             or B.
72              
73              
74             =head2 Blocking Functions
75              
76             Blocking functions, C, C and C, are exported
77             by defalut. These functions use C blocking object and exchange SNMP
78             messages serially.
79              
80              
81             =head2 Non-blocking Functions
82              
83             Using tag C<":para"> or C<":parallel">, Non-Blocking functions which use
84             C B are exported. These functions can exchange
85             SNMP messages to multiple hosts and treat response MIB values in order of
86             message receiving while the loop. These functions will apparently behave in
87             parallel, so they have "para" in its own names.
88              
89              
90             =head2 Arguments
91              
92             The way of passing arguments is unified whether function is Non-blocking or
93             Blocking.
94             Basically pass arguments with name and following value like hash pair below;
95              
96             $r = snmpwalk( hosts => $hostsval,
97             oids => $oidval,
98             snmp => $snmpval );
99              
100             Mostly original C functions' arguments are able to be passed.
101              
102             $r = snmpparabulk(
103             hosts => $hostsval, oids => $oidval, snmp => $snmpval
104             -maxrepetitions => 20,
105             -delay => 2,
106             );
107              
108             But some original parameter, C<-callback>, C<-nonrepeaters> and C<-varbindlist>
109             are not supported by reason of a algorithm.
110              
111              
112             =head3 Argument "hosts"
113              
114             By argument C<"hosts">, specify hosts to communicate. This takes a hash or
115             array reference or hostname.
116              
117             When only hash reference using, it is possible to use prepared C
118             object like below;
119              
120             # Using hash reference with prepared Net::SNMP object
121             $session1 = Net::SNMP->session( -hostname=>"www.freshes.org", ... );
122             $session2 = Net::SNMP->session( -hostname=>"192.168.10.8", ... );
123             $r = snmpwalk( hosts => {
124             "peach" => $session1,
125             "berry" => $session2
126             }, ...
127             );
128              
129             In this way, keys of hash are not specifying target hosts but just used to
130             classfy result.
131              
132             Except such way of using prepered object like above, a temporary C
133             session object will be made, used and deleted internally and automaticaly. See
134             the way below, this example will make temporary session with hash parameters of
135             Csession()>;
136              
137             # Using hash reference with parameters
138             $r = snmpwalk( hosts => {
139             "pine" => {
140             -hostname => "192.168.10.9",
141             },
142             "passion" => {
143             -hostname => "exchanger.local",
144             -port => 10161,
145             }
146             }, ...
147             );
148              
149             More hash argument C<"snmp"> are given, it will be used as common parameters
150             for each temporary session making. This argument C<"snmp"> hash is not only
151             for hash but also for specifying by array rererence or hostname string.
152              
153             # hash "snmp" using examples
154             $r = snmpwalk( hosts => {
155             "peach" => { -hostname => "www.freshes.org" },
156             "berry" => { -hostname => "192.168.10.8" },
157             "pine" => { -hostname => "192.168.20.8", },
158             "passion" => { -hostname => "exchanger.local",
159             -port => 10161, },
160             },
161             snmp => { -community => "4leaf-clover",
162             -timeout => 10,
163             -retries => 2,
164             }, ...
165             );
166              
167             # Using array reference or string
168             $r5 = snmpwalk( hosts => [ "dream","rouge","lemonade","mint","aqua" ],
169             snmp => { -version => 1,
170             -community => "yes5",
171             }, ...
172             );
173             $r6 = snmpwalk( hosts => "milkyrose",
174             snmp => { -version => 2,
175             -community => "yes5gogo",
176             }, ...
177             );
178              
179             Note that values of arguments C<"host"> in array reference case or hostname
180             string are used as values of C<-hostname> parameters for C, and
181             at the same, time used as classfying key of result.
182              
183              
184             =head3 Arguments "oids"
185              
186             Specify OIDs to investigate by hash reference argument named C<"oids">. Keys
187             of this hash will be used as just classfying of result. Values must be an
188             array reference listing OIDs, or singular OID string. And this hash allows
189             that these two types are mixed into it.
190              
191             $r = snmpwalk( hosts => \@hosts,
192             oids => {
193             "system" => "1.3.6.1.2.1.1",
194             "ifInfo" => [ "1.3.6.1.2.1.2.2.1.3", # ifType
195             "1.3.6.1.2.1.31.1.1.1.1", ] # ifName
196             }, ...
197             );
198              
199             Each value of this C<"oids"> hash will make one B. So singular
200             OID value makes Var Bindings contains one OID, and multiple OID specified by
201             array reference makes one contains several OIDs.
202              
203             It is allowed to specify arguments C<"oids"> as array reference. In this case,
204             result content will not be classfied by keys of OID name but keys of
205             suboids. See section of "Return Values" below.
206              
207              
208             =head3 Argument "snmp"
209              
210             If argument C<"hosts"> is specified, hash argument C<"snmp"> will mean common
211             parameters to Csession()> mentioned above.
212              
213             Well, it is possible to omit parameter C<"host">. In this case, value of
214             C<"snmp"> will be used to specify the target. Same as argument "hosts",
215             giving prepared C session object is allowed.
216              
217             # Prepared session
218             $session = Net::SNMP->session( -hostname => "blossom", ... );
219             $r = snmpwalk( snmp => $session,
220             oids => \%oids,
221             ...
222             );
223             # Temporary session
224             $r = snmpwalk( snmp => { -hostname => "marine",
225             -community => "heartcatchers",
226             },
227             oids => \%oids,
228             ...
229             );
230              
231              
232             =head3 Forbiddings
233              
234             These case below causes an error;
235              
236             =over
237              
238             =item *
239              
240             Argument C<"snmp"> with prepared C object and C<"hosts"> are
241             specified at the same time.
242             Chomp C<"hosts"> or let parameter C<"snmp"> a hash reference.
243              
244             # NG
245             $session = Net::SNMP->session( ... );
246             $r = snmpwalk( hosts => \%something,
247             snmp => $session,
248             );
249              
250             =item *
251              
252             Non-blocking prepared C object are given as C<"hosts"> or C<"snmp">
253             value to Blocking functions.
254              
255             =item *
256              
257             Blocking prepared C object are given as C<"hosts"> or C<"snmp">
258             value to Non-blocking functions.
259              
260             =back
261              
262              
263             =head2 Return Values
264              
265             =head3 Errors
266              
267             In list context, a hash reference result value and errors string will be
268             returned. In scalar, only result value will be returned. In both case, critical
269             errors will make result value B and make errors string.
270              
271             If several hosts checking and some errors occured while communicating, each
272             error messages will be chained to errors string. For checking errors by host
273             individually or in scalar context, use functions C. This function
274             will return a hash reference which contains error messages for each hosts.
275              
276             =head3 Gained MIB Values
277              
278             In success, gained MIB value will be packed into a hash and its reference will
279             be returned.
280              
281             For example, case of C and C operations;
282              
283             snmpget( oids => { sysContact => "1.3.6.1.2.1.1.4.0",
284             sysInfo => [ "1.3.6.1.2.1.1.5.0", # sysName
285             "1.3.6.1.2.1.1.6.0" ], # sysLocation
286             }, ...
287             );
288              
289             yeilds;
290              
291             {
292             sysContact => "Cure Flower ",
293             sysInfo => [ "palace", "some place, some world" ],
294             }
295              
296             Other functions, value will be a more hash which contains pairs of key as
297             sub OID and its values.
298             For example;
299              
300             snmpwalk( oids => { "system" => "1.3.6.1.2.1.1",
301             "ifInfo" => [ "1.3.6.1.2.1.2.2.1.3", # ifType
302             "1.3.6.1.2.1.31.1.1.1.1", ] # ifName
303             }, ...
304             );
305              
306             yeilds;
307              
308             {
309             "system" => {
310             "1.0" => "Testing system the fighters are strong enough",
311             "2.0" => "1.3.6.1.4.1.99999.1",
312             ... ,
313             },
314             "ifInfo" => [
315             {
316             "1" => 62, # 1.3.6.1.2.1.2.2.1.3.1
317             "10101" => 62, # 1.3.6.1.2.1.2.2.1.3.10101
318             ...
319             },
320             {
321             "1" => "mgmt", # 1.3.6.1.2.1.31.1.1.1.1.1
322             "10101" => "1/1", # 1.3.6.1.2.1.31.1.1.1.1.10101
323             ...
324             }
325             ]
326             }
327              
328             As stated above, when OIDs are specified in an array, values also will be
329             contained in an array.
330              
331             If parameter C<"snmp"> decides target host without C<"hosts">, result data
332             will be the same as above examples yields. If not so, parameter C<"hosts">
333             is specified, result data of each host will be contained to parentally
334             hash which key will be identified by hostname.
335             For example;
336              
337             $r1 = snmpget(
338             hosts => [ "bloom", "eaglet" ],
339             oids => {
340             system => [ "1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.1.3.0" ],
341             }, ...
342             );
343              
344             $r2 = snmpwalk(
345             hosts => {
346             "kaoru" => { -hostname => '192.168.11.10', ... },
347             "michiru" => { -hostname => '192.168.12.10', ... },
348             },
349             oids => { "system" => "1.3.6.1.2.1.1",
350             "ifInfo" => [ "1.3.6.1.2.1.2.2.1.3", # ifType
351             "1.3.6.1.2.1.31.1.1.1.1", ] # ifName
352             }, ...
353             );
354              
355             returns hashref;
356              
357             # $r1
358             {
359             "bloom" => { # hostname
360             "system" => [ ...VALUES... ]
361             },
362             "eaglet" => { # hostname
363             "system" => [ ...VALUES... ]
364             }
365             }
366              
367             # $r2
368             {
369             "system" => {
370             "1.0" => "...", "2.0" => "...", ...
371             },
372             "ifInfo" => [
373             {
374             "1" => 62, # 1.3.6.1.2.1.2.2.1.3.1
375             "10101" => 62, # 1.3.6.1.2.1.2.2.1.3.10101
376             ...
377             },
378             {
379             "1" => "mgmt", # 1.3.6.1.2.1.31.1.1.1.1.1
380             "10101" => "1/1", # 1.3.6.1.2.1.31.1.1.1.1.10101
381             ...
382             }
383             ]
384             }
385              
386             If OIDs specifying by C<"oids"> are not a hash but an array reference, values
387             of gained data will be not hash but array.
388             For example,
389              
390             snmpget( oids => [ "1.3.6.1.2.1.1.5.0", # sysName
391             "1.3.6.1.2.1.1.6.0" ], # sysLocation
392             }, ...
393             );
394              
395             yeilds;
396              
397             [ "takocafe", # string of sysName
398             "Wakabadai-park, Tokyo" ], # string of sysLocation
399              
400              
401             =head2 Callback function
402              
403             Apart from original C<-callback> option of functions of C,
404             functions of C provides another callback logic, by specifying
405             common option, C<-mycallback>. This option is possible to be used whether
406             Non-blocking or Blocking.
407              
408             This callback function will be called when each MIB value recieving with
409             passing arguments; session object, host name, key name and reference to array
410             of values.
411              
412             For example, C and C operations, array contains
413             values which order is same as a member of parameter C<"oids"> specifys.
414              
415             snmpget(
416             hosts => \%hosts,
417             oids => { someMIB1 => $oid1,
418             someMIB2 => [ $oid2, $oid3, $oid4 ]
419             },
420             -mycallback => sub {
421             ($session, $host, $key, $valref) = @_;
422             # $valref will be;
423             # [ $val1 ] when $key is "someMIB1"
424             # or
425             # [ $val2, $val3, $val4 ] when $key is "someMIB2"
426             }
427             );
428              
429             Other functions, passing array reference will contain more array references
430             which will have two value, sub OID and value. Values ordering rule is, same
431             as above, a member of parameter C<"oids"> specifys.
432              
433             snmpwalk(
434             hosts => \%hosts,
435             oids => { someMIB1 => $oid1,
436             someMIB2 => [ $oid2, $oid3, $oid4 ]
437             },
438             -mycallback => sub {
439             ($session, $host, $key, $valref) = @_;
440             # $valref will be;
441             # [ [ $suboid1, $val1 ] ] when $key is "someMIB1"
442             # or
443             # [ [ $suboid2,$val2 ], [ $suboid3,$val3 ], [ $suboid4,$val4 ] ]
444             # when $key is "someMIB2"
445             }
446             );
447              
448              
449             =cut
450              
451             # =============================================================================
452              
453 6     6   43 use Carp qw();
  6         13  
  6         120  
454 6     6   37 use Scalar::Util qw();
  6         14  
  6         108  
455 6     6   1466 use Net::SNMP;
  6         90342  
  6         996  
456              
457 6     6   48 use base qw( Exporter );
  6         11  
  6         36327  
458             our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);
459             @EXPORT = qw( get_errstr get_errhash snmpget snmpwalk snmpbulk snmpbulk );
460             @EXPORT_OK = ();
461             %EXPORT_TAGS = (
462             para => [ @EXPORT,
463             qw( snmpparaget snmpparawalk snmpparabulk snmpparabulkwalk ) ],
464             );
465             Exporter::export_ok_tags( qw(para) );
466             $EXPORT_TAGS{all} = [ @EXPORT, @EXPORT_OK ];
467             $EXPORT_TAGS{parallel} = $EXPORT_TAGS{para};
468              
469             my $_error;
470             my $_errhash;
471              
472              
473             # ============================================================================
474             # private object methods
475             # ============================================================================
476             sub _getmanager
477             {
478 96     96   124 my $class = shift;
479 96         188 my ($command, $session, $table, $error, $host, $key, $boids, $mycb) = @_;
480              
481             # check OIDs are array and init storing value
482 96         136 my @baseoids = ();
483 96         140 my $vbtype = ref($boids);
484 96 100       256 if ( $vbtype eq 'ARRAY' ){
485 48         159 $table->{$host}{$key} = [];
486 48         82 push @baseoids, @{$boids};
  48         132  
487             } else {
488 48         137 $table->{$host}{$key} = {};
489 48         99 push @baseoids, $boids;
490             }
491              
492 96         1366 my $self = {
493             command => $command, # get, get_next or get_bulk
494             session => $session, # Net::SNMP session object
495             table => $table, # Storing table
496             error => $error, # hashref to storage of error
497             host => $host, # hostname
498             key => $key, # given keyname by oids
499             baseoids => \@baseoids, # baseoid of requested
500             curoids => [@baseoids],# current digging OID
501             mycb => $mycb, # my callback
502             isMulOid => $vbtype # given oid is plural or not
503             };
504 96         397 Scalar::Util::weaken($self->{session}); # Avoid for circular references
505 96         401 bless $self, $class;
506             }
507              
508             # get current grabing oids to investigate
509             sub _get_oids
510             {
511 508     508   607 grep { defined $_ } @{$_[0]->{curoids}};
  764         2646  
  508         978  
512             }
513              
514             # stringify
515             sub _stringify
516             {
517 1     1   2 my $self = shift;
518 1         5 sprintf("%s::%s::%s",
519             $self->{host}, $self->{key}, join('-',$self->_get_oids())
520             );
521             }
522              
523             # memorize error message
524             sub _memo_error
525             {
526 1     1   3 my ($self, $flag) = @_;
527 1         3 my ($host, $key) = ($self->{host}, $self->{key});
528 1 50       9 $self->{error}{$host}{$key} = sprintf( '%s%s %s',
529             defined($flag)? "($flag)": '',
530             $self->_stringify(),
531             $self->{session}->error()
532             );
533             }
534              
535             #return Net::SNMP getting operation function
536             sub _exec_operation
537             {
538 352     352   427 my $self = shift;
539 352         489 my $com = $self->{command};
540              
541 352         307 printf "[DEBUG] %s) execute %s\n", $com, $self->_stringify() if DEBUG;
542              
543             return
544 352 50       1676 ( $com eq 'get' )? $self->{session}->get_request(@_):
    100          
    100          
545             ( $com eq 'get_next' )? $self->{session}->get_next_request(@_):
546             ( $com eq 'get_bulk' )? $self->{session}->get_bulk_request(@_):
547             undef;
548             }
549              
550             # kicker of varBindList treator
551             sub _treat_varbindings
552             {
553 351     351   448 my $self = shift;
554 351         590 my $com = $self->{command};
555              
556 351         331 printf "[DEBUG] %s) checking %s\n", $com, $self->_stringify() if DEBUG;
557              
558             return
559 351 50       1270 ( $com eq 'get' )? $self->_treat_get_varbindings():
    100          
    100          
560             ( $com eq 'get_next' )? $self->_treat_getnext_varbindings():
561             ( $com eq 'get_bulk' )? $self->_treat_getbulk_varbindings():
562             undef;
563             }
564              
565              
566             # treating varBindList yeilded by GetRequest
567             sub _treat_get_varbindings
568             {
569 28     28   35 my $self = shift;
570 28         42 my ($session, $host, $key) = map { $self->{$_} } qw(session host key);
  84         212  
571              
572             # get varBindList and names
573 28         93 my $vlist = $session->var_bind_list();
574 28 50       273 return undef unless defined $vlist; # error
575 28 50       27 return 0 unless %{$vlist}; # if result is empty
  28         78  
576              
577 28         81 my @ret = map { $vlist->{$_} } $session->var_bind_names();
  42         352  
578              
579             # kick my callback
580 28 50       84 if ( defined $self->{mycb} ){
581 0         0 my $r = $self->{mycb}->( $session, $host, $key, \@ret);
582 0 0       0 return 0 unless $r; # avoiding to store
583             }
584              
585             # store data
586 28 100       56 if ( $self->{isMulOid} ){
587 14         17 push @{$self->{table}{$host}{$key}}, @ret;
  14         41  
588             } else {
589 14         39 $self->{table}{$host}{$key} = $ret[0];
590             }
591 28         236 return 0;
592             }
593              
594              
595             # treating varBindList yeilded by GetNextRequest
596             sub _treat_getnext_varbindings
597             {
598 168     168   164 my $self = shift;
599 168         199 my ($session, $host, $key) = map { $self->{$_} } qw(session host key);
  504         848  
600              
601 168         179 printf "[DEBUG] %s) parsing %s\n", "get_next", $self->_stringify() if DEBUG;
602              
603             # get varBindList and names
604 168         369 my $vlist = $session->var_bind_list();
605              
606             # printf "[DEBUG] %s) vlist:%s\n", "get_next", Dumper($vlist) if DEBUG;
607              
608 168 50       1477 return undef unless defined $vlist; # error
609 168 50       159 return 0 unless %{$vlist}; # if result is empty
  168         313  
610              
611 168         374 my @names = $session->var_bind_names();
612 168         1640 my $types = $session->var_bind_types();
613              
614             # check out of the branch of each oid in varBindList
615 168         1094 my @ret = ();
616 168         161 my $num = @{$self->{baseoids}};
  168         241  
617 168         166 my $c = 0;
618 168         324 for ( my $i=0; $i<$num; $i++ )
619             {
620 252 50       480 next unless defined $self->{curoids}[$i];
621              
622 252         304 my $baseoid = $self->{baseoids}[$i];
623 252         263 my $name = shift @names;
624 252         320 my $type = $types->{$name};
625              
626 252 100 66     3524 if ( $name !~ /^\.?\Q$baseoid.\E(.+)$/ ||
627             $type == ENDOFMIBVIEW
628             ){
629             # the leaf is not-exists or out of branch
630 42         50 $ret[$i] = undef;
631 42         146 $self->{curoids}[$i] = undef;
632             }
633             else {
634             # the leaf is within the branch
635 210         1367 $ret[$i] = [ $1, $vlist->{$name} ];
636 210         374 $self->{curoids}[$i] = $name;
637 210         687 $c++;
638             }
639             }
640 168 100       467 return 0 if !$c; # all necessary oids are checked
641              
642             # kick my callback
643 140 50       252 if ( defined $self->{mycb} ){
644 0         0 my $r = $self->{mycb}->( $session, $host, $key, \@ret);
645 0 0       0 return 0 unless defined $r; # to stop operate
646 0 0       0 return 1 unless $r; # to avoid to store
647             }
648              
649             # store data
650 140 100       207 if ( $self->{isMulOid} ){
651 70         146 for ( my $i=0; $i<$num; $i++ ){
652 140 50       232 next unless defined $ret[$i];
653 140 50       101 next unless @{$ret[$i]};
  140         245  
654 140         138 my ($suboid, $val) = @{$ret[$i]};
  140         208  
655 140         499 $self->{table}{$host}{$key}->[$i]{$suboid} = $val;
656             }
657             } else {
658 70         64 my ($suboid, $val) = @{$ret[0]};
  70         109  
659 70         194 $self->{table}{$host}{$key}->{$suboid} = $val;
660             }
661              
662 140         562 return 1; # return valid number for next investigation
663             }
664              
665              
666             # treating varBindList yeilded by GetBulkRequest
667             sub _treat_getbulk_varbindings
668             {
669 155     155   204 my $self = shift;
670 155         226 my ($session, $host, $key) = map { $self->{$_} } qw(session host key);
  465         1001  
671              
672             # get varBindList and names
673 155         637 my $vlist = $session->var_bind_list();
674              
675 155 50       1668 return undef unless defined $vlist; # error
676 155 50       156 return 0 unless %{$vlist}; # if result is empty
  155         477  
677              
678 155         434 my @names = $session->var_bind_names();
679 155         2175 my $types = $session->var_bind_types();
680              
681             # check out of the branch of each oid in varBindList
682 155         1372 my @ret = ();
683 155         180 my $num0= @{$self->{baseoids}};
  155         249  
684 155         169 my $num = @{[$self->_get_oids()]};
  155         293  
685              
686 155         253 my $c;
687 155         348 while ( @names ){
688 417         1142 my @n = splice(@names,0,$num);
689 417         1206 for ( my $i=0,$c=0; $i<$num0; $i++ )
690             {
691 626 100       1533 next unless defined $self->{curoids}[$i];
692              
693 599         956 my $baseoid = $self->{baseoids}[$i];
694 599         732 my $name = shift @n;
695 599         1114 my $type = $types->{$name};
696              
697 599 100 100     10001 if ( $name !~ /^\.?\Q$baseoid.\E(.+)$/ ||
698             $type == ENDOFMIBVIEW
699             ){
700             # the leaf is not-exists or out of branch
701 88         2068 $self->{curoids}[$i] = 0;
702             }
703             else {
704             # the leaf is within the branch
705 511         2455 push @{$ret[$i]}, [ $1, $vlist->{$name} ];
  511         2435  
706 511         1342 $self->{curoids}[$i] = $name;
707 511         1829 $c++;
708             }
709             }
710 417 100       1400 last if !$c; # no more check
711             }
712 155         395 for ( my $i=0; $i<$num0; $i++ ){
713 233   100     1016 $self->{curoids}[$i] ||= undef;
714             }
715              
716             # kick my callback
717 155 50       623 if ( defined $self->{mycb} ){
718 0         0 my $r = $self->{mycb}->( $session, $host, $key, \@ret);
719 0 0       0 return 0 unless defined $r; # to stop operate
720 0 0       0 return 1 unless $r; # to avoid to store
721             }
722              
723             # store data
724 155 100       283 if ( $self->{isMulOid} ){
725 78         203 for ( my $i=0; $i<$num0; $i++ ){
726 156         160 foreach my $leaf ( @{$ret[$i]} ){
  156         378  
727 323 50       625 next unless defined $leaf;
728 323         319 my ($suboid, $val) = @{$leaf};
  323         537  
729 323         1482 $self->{table}{$host}{$key}->[$i]{$suboid} = $val;
730             }
731             }
732             } else {
733 77         86 foreach my $leaf ( @{$ret[0]} ){
  77         181  
734 188 50       355 next unless defined $leaf;
735 188         276 my ($suboid, $val) = @{$leaf};
  188         300  
736 188         667 $self->{table}{$host}{$key}->{$suboid} = $val;
737             }
738             }
739              
740 155         1234 return $c; # return valid number for next investigation
741             }
742              
743             # =============================================================================
744              
745             sub _parse_params
746             {
747 50 50   50   191 if ( @_ & 1 ){
748 0         0 Carp::carp("Odd number of arguments.");
749 0         0 return (undef, "Odd number of arguments.");
750             }
751 50         263 my %p = @_;
752 50         95 my %sessions = ();
753 50         91 my (%istmp,$oids,$mycb) = ();
754 50         73 my $arghosts = 1;
755              
756 50         60 my $nonblocking = 0;
757 50 50       145 if ( defined $p{nonblocking} ){
758 50         103 $nonblocking = delete $p{nonblocking};
759             }
760              
761             # --- checking "snmp" ---
762 50         69 my $snmphash = undef;
763 50         55 my $snmpobj = undef;
764 50         76 my %errhash = ();
765              
766 50 100       152 if ( defined $p{snmp} ){
767 29         67 my $type = ref($p{snmp});
768 29 100       84 if ( $type eq 'HASH' ){
    50          
769 25         57 $snmphash = delete $p{snmp};
770             }
771             elsif ( $type eq 'Net::SNMP' ){
772 4         38 $snmpobj = delete $p{snmp};
773             }
774             else {
775 0         0 return (undef, q(Parameter "snmp" must be a hash reference or Net::SNMP object.));
776             }
777             }
778              
779             # --- parsing "hosts" ---
780 50 100       120 if ( defined $p{hosts} )
781             {
782 37 100       84 if ( defined $snmpobj ){
783 2         11 return ( undef, q(In case specifying parameters both "hosts" and "snmp", ).
784             q("snmp" must be not Net::SNMP object but a hash reference.) );
785             }
786              
787 35         73 my $type = ref($p{hosts});
788              
789             # hosts => \%hashref;
790 35 100       127 if ( $type eq 'HASH' ){
    100          
791             # regard key as hostname and value as Net::SNMP object or parameter
792 15         25 while ( my ($host, $value) = each %{$p{hosts}} )
  50         206  
793             {
794 37         91 $type = ref($value);
795              
796             # treat value as Net::SNMP object
797 37 100       120 if ( $type eq 'Net::SNMP' ){
    100          
798 13 100 66     53 if ( $nonblocking && !$value->nonblocking() ){
799 1         12 $errhash{$host} = "About $host, blocking Net::SNMP object ".
800             "was specified to call non-blocking function.";
801 1         3 next;
802             }
803 12 100 66     101 if ( !$nonblocking && $value->nonblocking() ){
804 1         13 $errhash{$host} = "About $host, non-blocking Net::SNMP object ".
805             "was specified to call blocking function.";
806 1         3 next;
807             }
808 11         92 $sessions{$host} = $value;
809             }
810              
811             # if hashref, make temporary sessions.
812             elsif ( $type eq 'HASH' ){
813 11         61 my ($s, $e) = Net::SNMP->session(
814 11         71 %{$snmphash},
815             -nonblocking => $nonblocking,
816             -hostname => $host,
817 11         20 %{$value}
818             );
819 11 50       12591 unless ( defined($s) ){
820 0         0 $errhash{$host} = "$host, session making error: $e";
821 0         0 next;
822             }
823 11         29 $sessions{$host} = $s;
824 11         35 $istmp{$host} = 1;
825             }
826              
827             # othre cases cause error.
828             else {
829             # othre reference without string will be an error
830 13 100 66     88 if ( !$type && defined($value) ){
831 11         76 my ($s, $e) = Net::SNMP->session(
832 11         19 %{$snmphash},
833             -nonblocking => $nonblocking,
834             -hostname => $value,
835             );
836 11 50       3937 unless ( defined($s) ){
837 0         0 $errhash{$host} = "$host, session making error: $e";
838 0         0 next;
839             }
840 11         22 $sessions{$host} = $s;
841 11         35 $istmp{$host} = 1;
842             } else {
843 2         14 return (undef, qq(Value of "$host" must be a string,).
844             qq( an array reference or a hash reference));
845             }
846             }
847             }
848             }
849              
850             # hosts => \@arrayref;
851             elsif ( $type eq 'ARRAY' ){
852             # regard it as hostname list
853 9         13 foreach my $host ( @{$p{hosts}} ){
  9         28  
854 18         119 my ($s, $e) = Net::SNMP->session(
855 18         29 %{$snmphash},
856             -nonblocking => $nonblocking,
857             -hostname => $host,
858             );
859 18 50       8107 unless ( defined($s) ){
860 0         0 $errhash{$host} = "$host, session making error: $e";
861 0         0 next;
862             }
863 18         43 $sessions{$host} = $s;
864 18         54 $istmp{$host} = 1;
865             }
866              
867             } else {
868             # othre reference will be an error
869 11 100       35 if ( $type ){
870 2         10 return (undef, q(Parameter "hosts" must be a string, an array reference or a hash reference));
871             }
872              
873             # but string is ok. it will be regards as hostname.
874             else {
875 9         17 my $host = $p{hosts};
876 9         69 my ($s, $e) = Net::SNMP->session(
877 9         57 %{$snmphash},
878             -nonblocking => $nonblocking,
879             -hostname => $host,
880             );
881 9 50       4401 unless ( defined($s) ){
882 0         0 $errhash{$host} = "$host, session making error: $e";
883             } else {
884 9         26 $sessions{$host} = $s;
885 9         24 $istmp{$host} = 1;
886             }
887             }
888             }
889 31         99 delete $p{hosts};
890              
891             # Erase "snmp" parameter (hashref).
892             # this is no longer need.
893 31         52 $snmphash = undef;
894             }
895             else {
896 13         25 $arghosts = 0;
897             }
898              
899             # --- parsing "snmp" ---
900             # This parsing will be invoked when parameter "host" isn't specified.
901 44 100       131 if ( defined $snmpobj ){
902             # Net::SNMP object is given, use it as it is.
903 2 100 66     18 if ( $nonblocking && !$snmpobj->nonblocking() ){
904 1         10 return (undef, "Blocking Net::SNMP object was specified to call non-blocking function.");
905             }
906 1 50 33     7 if ( !$nonblocking && $snmpobj->nonblocking() ){
907 1         9 return (undef, "Non-Blocking Net::SNMP object was specified to call blocking function.");
908             }
909 0         0 $sessions{$snmpobj->hostname()} = $snmpobj;
910             }
911 42 100       91 if ( defined $snmphash ){
912             # Hash reference is given, use it as parameter for making temp session
913 9         64 my ($s, $e) = Net::SNMP->session(
914 9         15 %{$snmphash},
915             -nonblocking => $nonblocking
916             );
917 9 50       4678 return (undef, "Making session error; $e") unless defined $s;
918              
919 9         39 $sessions{$s->hostname()} = $s;
920 9         52 $istmp{$s->hostname()} = 1;
921             }
922              
923             # --- parsing "oids" ---
924 42 100       152 if ( exists($p{oids}) ){
925 40         94 my $type = ref($p{oids});
926 40 100       105 if ( $type eq 'HASH' ) {
927 14         26 $oids = $p{oids};
928             }
929             else {
930 26         96 $oids = {
931             '_ANONY_' => $p{oids}
932             };
933             }
934             # Check type of each oid
935 40         64 foreach my $oid ( values %{$oids} ){
  40         122  
936 54 50       124 unless ( defined $oid ){
937 0         0 return (undef, "Undefined value specified as OID");
938             }
939 54         73 $type = ref($oid);
940 54 50 66     270 if ( $type && $type ne 'ARRAY' ){
941 0         0 return (undef, "Each OID values must be an array reference or string");
942             }
943             }
944 40         242 delete $p{oids};
945             }
946 42 100       119 unless ( defined $oids ){
947 2         10 return (undef, q(Parameter "oids" is not given));
948             }
949              
950             # --- parsing "-mycallback" ---
951 40         76 foreach ( qw( mycallback -mycallback ) ){
952 80 50       219 if ( defined($p{$_}) ){
953 0         0 $mycb = delete $p{$_};
954 0 0       0 unless ( ref($mycb) eq 'CODE' ){
955 0         0 Carp::carp("Non code given as -mycallback, ignored.");
956 0         0 $mycb = undef;
957             }
958             }
959             }
960 40         80 foreach ( qw( callback -callback ) ){
961 80 50       198 if ( defined($p{$_}) ){
962 0         0 Carp::carp("option $_ is ignored.");
963 0         0 delete $p{$_};
964             }
965             }
966              
967             # --- parsing end ---
968 40         182 return (\%sessions,\%errhash,\%istmp,$oids,\%p,$mycb,$arghosts);
969              
970             }
971              
972              
973             # =============================================================================
974              
975             =head1 BLOCKING FUNCTIONS
976              
977             C exports bloking functions defalut.
978              
979             =cut
980              
981             # -----------------------------------------------------------------------------
982             sub _snmpkick
983             {
984 44     44   79 my $command = shift;
985              
986 44         121 _clear_error();
987 44         168 my ($sessions,$error,$istmp,$oids,$opts,$mycb,$arghosts) = _parse_params(
988             @_,
989             nonblocking => 0
990             );
991 44 100       119 return _retresults(undef, $error) unless defined $sessions;
992              
993 39         58 my $table = {};
994 39         129 while ( my ($host,$session) = each %{$sessions} )
  108         395  
995             {
996 69         79 foreach my $key ( keys %{$oids} ){
  69         152  
997              
998 96         158 my $oid = $oids->{$key};
999             # memo: dont use "while...(each %{$oids})" here.
1000             # because when $result is undef by error, not-resetted
1001             # iterating counter of %{$oids} will be used at next
1002             # $host's loop...
1003 96         336 my $manager = __PACKAGE__->_getmanager(
1004             $command, $session, $table, $error, $host, $key, $oid, $mycb
1005             );
1006              
1007 96         122 my $result;
1008 96         105 do {
1009 352         1190 $result = $manager->_exec_operation(
1010 352         399 %{$opts},
1011             -varbindlist => [ $manager->_get_oids() ],
1012             );
1013 352 100       308755 unless ( defined $result ){
1014 1         6 $manager->_memo_error();
1015             # if some error occuer, terminate process of
1016             # error host and delete data at Blocking Mode
1017 1         14 delete $table->{$host};
1018 1         8 last;
1019             }
1020             } while ( $manager->_treat_varbindings() );
1021             }
1022             }
1023              
1024             # closing temporary session and finishing
1025 39         61 while ( my ($host,$session) = each %{$sessions} ){
  108         353  
1026 69 100       179 if ( $istmp->{$host} ){
1027 58         208 $session->close();
1028 58         2035 undef $session;
1029             }
1030             }
1031              
1032 39         139 return _retresults($table, $error, $arghosts);
1033             }
1034              
1035              
1036             # =============================================================================
1037              
1038             =head2 snmpget()
1039              
1040             C is a Blocking function which gather MIB values with SNMP
1041             GetRequest operation via Cget_request()>.
1042              
1043             =cut
1044              
1045             # -----------------------------------------------------------------------------
1046             sub snmpget
1047             {
1048 18     18 1 157352 _snmpkick('get', @_);
1049             }
1050              
1051              
1052             # =============================================================================
1053              
1054             =head2 snmpwalk()
1055              
1056             C is a Blocking function which gather MIB values with SNMP
1057             GetNextRequest operation via Cget_next_request()>.
1058              
1059             =cut
1060              
1061             # -----------------------------------------------------------------------------
1062             sub snmpwalk
1063             {
1064 12     12 1 35931 _snmpkick('get_next', @_);
1065             }
1066              
1067              
1068             # =============================================================================
1069              
1070             =head2 snmpbulk()
1071              
1072             C is a Blocking function which gather MIB values with SNMP
1073             GetBulkRequest operation via Cget_bulk_request()>. So using
1074             this function needs that target devices are acceptable for SNMP version 2c or
1075             more.
1076              
1077             Note that C<-maxrepetitions> should be passed with some value. C
1078             will set this parameter 0 by defalut.
1079             Also note that reason of algorithm, -nonrepeaters is not supported.
1080              
1081             =head2 snmpbulkwalk()
1082              
1083             An alias of C.
1084              
1085             =cut
1086              
1087             # -----------------------------------------------------------------------------
1088             sub snmpbulk
1089             {
1090 14     14 1 42253 _snmpkick('get_bulk', @_, -nonrepeaters=>0 );
1091             }
1092              
1093 0     0 1 0 sub snmpbulkwalk { snmpbulk(@_) }
1094              
1095              
1096             # =============================================================================
1097              
1098             =head1 NON-BLOCKING FUNCTIONS
1099              
1100             C gives some Non-blocking functions. Use these Non-blocking
1101             functions, import them with ":para" tag at C pragma.
1102              
1103             =cut
1104              
1105             # -----------------------------------------------------------------------------
1106             sub _snmpparakick
1107             {
1108 6     6   12 my $command = shift;
1109              
1110 6         16 _clear_error();
1111 6         19 my ($sessions,$error,$istmp,$oids,$opts,$mycb,$arghosts) = _parse_params(
1112             @_,
1113             nonblocking => 1
1114             );
1115 6 100       26 return _retresults(undef, $error) unless defined $sessions;
1116              
1117             # define callback subroutine
1118             my $callback = sub {
1119 0     0   0 my $s = shift;
1120 0         0 my ($this_cb, $m, $opts) = @_;
1121              
1122             # treat VarBindList
1123 0         0 my $r = $m->_treat_varbindings();
1124 0 0       0 $m->_memo_error() unless defined $r; # undef means get some error
1125 0 0       0 return unless $r; # not true value terminates
1126              
1127             # request again (at get_next or get_bulk)
1128 0         0 $r = $m->_exec_operation(
1129 0         0 %{$opts},
1130             -varbindlist => [ $m->_get_oids() ],
1131             -callback => [ $this_cb, @_ ],
1132             );
1133 0 0       0 $m->_memo_error() unless defined $r;
1134 1         7 };
1135              
1136             # making first request operation
1137 1         2 my $table = {};
1138 1         2 while ( my ($host,$session) = each %{$sessions} )
  1         11  
1139             {
1140 0         0 while ( my ($key, $oid) = each %{$oids} )
  0         0  
1141             {
1142 0         0 my $manager = __PACKAGE__->_getmanager(
1143             $command, $session, $table, $error, $host, $key, $oid, $mycb
1144             );
1145 0         0 my $result = $manager->_exec_operation(
1146 0         0 %{$opts},
1147             -varbindlist => [ $manager->_get_oids() ],
1148             -callback => [ $callback, $callback, $manager, $opts ],
1149             );
1150 0 0       0 $manager->_memo_error() unless defined $result;
1151             }
1152             }
1153              
1154             # execute to communicate
1155 1         6 snmp_dispatcher();
1156              
1157             # closing temporary session and finishing
1158 1         34 while ( my ($host,$session) = each %{$sessions} ){
  1         5  
1159 0 0       0 if ( $istmp->{$host} ){
1160 0         0 $session->close();
1161 0         0 undef $session;
1162             }
1163             }
1164              
1165 1         3 return _retresults($table, $error, $arghosts);
1166             }
1167              
1168              
1169             # =============================================================================
1170              
1171             =head2 snmpparaget()
1172              
1173             C is a Non-blocking function which gather MIB values with SNMP
1174             GetRequest operation via Cget_request()>.
1175              
1176             =cut
1177              
1178             # -----------------------------------------------------------------------------
1179             sub snmpparaget
1180             {
1181 6     6 1 43900 _snmpparakick('get', @_);
1182             }
1183              
1184              
1185             # =============================================================================
1186              
1187             =head2 snmpparawalk()
1188              
1189             C is a Non-blocking function which gather MIB values with SNMP
1190             GetNextRequest operation via Cget_next_request()>.
1191              
1192             =cut
1193              
1194             # -----------------------------------------------------------------------------
1195             sub snmpparawalk
1196             {
1197 0     0 1 0 _snmpparakick('get_next', @_);
1198             }
1199              
1200              
1201             # =============================================================================
1202              
1203             =head2 snmpparabulk()
1204              
1205             C is a Non-blocking function which gather MIB values with SNMP
1206             GetBulkRequest operation via Cget_bulk_request()>. So using
1207             this function needs that target devices are acceptable for SNMP version 2c or
1208             more.
1209              
1210             Note that C<-maxrepetitions> should be passwd with some value. C
1211             will set this parameter 0 by defalut.
1212             Also note that reason of algorithm, -nonrepeaters is not supported.
1213              
1214             =head2 snmpparabulkwalk()
1215              
1216             An alias of C.
1217              
1218             =cut
1219              
1220              
1221             # -----------------------------------------------------------------------------
1222             sub snmpparabulk
1223             {
1224 0     0 1 0 _snmpparakick('get_bulk', @_, -nonrepeaters=>0 );
1225             }
1226              
1227 0     0 1 0 sub snmpparabulkwalk { snmpparabulk(@_) }
1228              
1229              
1230             # =============================================================================
1231              
1232             =head1 OTHER FUNCTIONS
1233              
1234             =head2 get_errstr()
1235              
1236             $lasterror = get_errstr();
1237              
1238             C returns last error message string that is chained all of error
1239             messages for each hosts.
1240              
1241             =head2 get_errhash()
1242              
1243             $lasterror = get_errhash();
1244              
1245             C returns hash reference which contains last error messages
1246             identified by host names.
1247              
1248             =cut
1249              
1250             # -----------------------------------------------------------------------------
1251              
1252             sub _clear_error {
1253 50     50   113 $_error =
1254             $_errhash = {};
1255             }
1256              
1257             sub get_errstr {
1258 0     0 1 0 return $_error;
1259             }
1260             sub get_errhash {
1261 0     0 1 0 return $_errhash;
1262             }
1263              
1264             sub _retresults {
1265 50     50   85 my ($table, $error, $arghosts) = @_;
1266              
1267 50 50       242 return unless defined wantarray;
1268              
1269 50         99 my %ret = ();
1270 50 100 100     143 if ( defined($table) && %{$table} )
  40         247  
1271             {
1272 38         123 while ( my ($host,$keys) = each %{$table} )
  106         349  
1273             {
1274 68         95 while ( my ($key, $mibvals) = each %{$keys} )
  122         384  
1275             {
1276 95 100       225 if ( $key eq '_ANONY_' ){
1277              
1278 41         63 $table->{$host} = $mibvals;
1279 41         115 last;
1280             }
1281             }
1282             }
1283             # No "hosts" option and specified target host by "snmp",
1284             # the result will not contain hash of hosts.
1285 38 100       100 $table = (values %{$table})[0] if !$arghosts;
  9         21  
1286             }
1287              
1288 50         87 my $message = '';
1289 50 50       203 if ( $error ){
1290 50 100       141 if ( ref($error) eq 'HASH' ){
1291 40         53 foreach my $h ( keys %{$error} ){
  40         128  
1292 3 100       15 if ( ref($error->{$h}) eq 'HASH' ){
1293 1         3 $error->{$h} = join('; ', values %{$error->{$h}});
  1         7  
1294             }
1295             }
1296 40 100       65 if ( %{$error} ){
  40         107  
1297 3         6 $message = join("; ", (values %{$error}));
  3         10  
1298             }
1299             }
1300             else {
1301 10         12 $message = $error;
1302 10         17 $error = undef;
1303             }
1304             }
1305              
1306 50         75 $_error = $message;
1307 50         84 $_errhash = $error;
1308 50 50       409 return wantarray? ($table, $message): $table;
1309             }
1310              
1311             # =============================================================================
1312              
1313             =head1 APPENDIX
1314              
1315             C has sub modules; C and
1316             C.
1317              
1318             L gives MIBname-OID converter utilities.
1319             For example, you can specify basic OIDs when call function like below;
1320              
1321             use Net::SNMP::Util::OID qw(if*); # import if* MIB name maps
1322              
1323             %oids = (
1324             sysInfo => [
1325             oid( "ifDescr", "ifType" ) # equals '1.3.6.1.2.1.2.2.1.2','1.3.6.1.2.1.2.2.1.3'
1326             ],
1327             oidm("ifName") # equals "ifName" => "1.3.6.1.2.1.31.1.1.1.1"
1328             );
1329             ($result,$error) = snmpparaawlk(
1330             hosts => \@hosts,
1331             oids => \%oids,
1332             snmp => \%snmpparams
1333             );
1334              
1335             L gives MIBEnumValue-Text convertor utilities.
1336             For example, you can convert value of ifAdminStatus, ifOperStatus and ifType
1337             like below;
1338              
1339             use Net::SNMP::Util::TC;
1340              
1341             $tc = Net::SNMP::Util::TC->new;
1342             $astat = $tc->ifAdminStatus( $value_admin_stat ); # "up", "down" or etc.
1343             $ostat = $tc->ifOperStatus( $value_oper_stat );
1344             $iftype = $tc->ifType( $value_iftype ); # "ethernet-csmacd" or etc.
1345              
1346              
1347             =head1 PRACTICAL EXAMPLES
1348              
1349             =head2 1. Check system information simply
1350              
1351             This example get some system entry MIB values from several hosts with C.
1352              
1353             #!/usr/local/bin/perl
1354             use strict;
1355             use warnings;
1356             use Getopt::Std;
1357             use Net::SNMP::Util;
1358              
1359             my %opt;
1360             getopts('hv:c:r:t:', \%opt);
1361              
1362             sub HELP_MESSAGE {
1363             print "Usage: $0 [-v VERSION] [-c COMMUNITY_NAME] ".
1364             "[-r RETRIES] [-t TIMEOUT] HOST [,HOST2 ...]\n";
1365             exit 1;
1366             }
1367             HELP_MESSAGE() if ( !@ARGV || $opt{h} );
1368              
1369             (my $version = ($opt{v}||2)) =~ tr/1-3//cd; # now "2c" is ok
1370             my ($ret, $err) = snmpget(
1371             hosts => \@ARGV,
1372             snmp => { -version => $version,
1373             -timeout => $opt{t} || 5,
1374             -retries => $opt{r} || 1,
1375             -community => $opt{c} || "public" },
1376             oids => { descr => '1.3.6.1.2.1.1.1.0',
1377             uptime => '1.3.6.1.2.1.1.3.0',
1378             name => '1.3.6.1.2.1.1.5.0',
1379             location => '1.3.6.1.2.1.1.6.0',
1380             }
1381             );
1382             die "[ERROR] $err\n" unless defined $ret;
1383              
1384             foreach my $h ( @ARGV ){
1385             if ( $ret->{$h} ){
1386             printf "%s @%s (up %s) - %s\n",
1387             map { $ret->{$h}{$_} or 'N/A' } qw(name location uptime descr);
1388             } else {
1389             printf "%s [ERROR]%s\n", $h, $err->{$h};
1390             }
1391             }
1392              
1393             __END__
1394              
1395              
1396             =head2 2. Realtime monitor of host interfaces (SNMPv2c)
1397              
1398             This program shows realtime traffic throughput of interfaces of a host on your
1399             console with using C and callbacking.
1400              
1401             Notice: This program is for devices which can deal SNMP version 2c.
1402              
1403             #!/usr/local/bin/perl
1404              
1405             use strict;
1406             use warnings;
1407             use Getopt::Std;
1408             use Term::ANSIScreen qw/:color :screen :constants/;
1409             use Net::SNMP::Util;
1410              
1411             my %opt;
1412             getopts('hv:c:w:x:', \%opt);
1413             my $host = shift @ARGV;
1414              
1415             sub HELP_MESSAGE {
1416             print "Usage: $0 [-c COMMUNITY_NAME] [-w WAIT] [-x REGEXP] HOST\n";
1417             exit 1;
1418             }
1419             HELP_MESSAGE() if ( !$host || $opt{h} );
1420              
1421             my ($wait,$regexp) = ($opt{w}||5, $opt{x}? qr/$opt{x}/: '');
1422             my $console = Term::ANSIScreen->new();
1423             local $| = 1;
1424              
1425             # make session
1426             my ($ses, $err) = Net::SNMP->session(
1427             -hostname => $host,
1428             -version => "2",
1429             -community => ($opt{c} || "public")
1430             );
1431             die "[ERROR] $err\n" unless defined $ses;
1432              
1433             # main loop
1434             my (%pdata, %cdata); # flag, previous and current octets data
1435             my $first = 1;
1436             while ( 1 ){
1437             %cdata = ();
1438             (my $ret, $err) = snmpwalk(
1439             snmp => $ses,
1440             oids => {
1441             sysUpTime => '1.3.6.1.2.1.1.3',
1442             ifTable => [
1443             '1.3.6.1.2.1.31.1.1.1.1', # [0] ifName
1444             '1.3.6.1.2.1.2.2.1.7', # [1] ifAdminStatus
1445             '1.3.6.1.2.1.2.2.1.8', # [2] ifOperStatus
1446             '1.3.6.1.2.1.31.1.1.1.6', # [3] ifHCInOctets
1447             '1.3.6.1.2.1.31.1.1.1.10', # [4] ifHCOutOctets
1448             '1.3.6.1.2.1.31.1.1.1.15', # [5] ifHighSpeed
1449             ] },
1450             -mycallback => sub {
1451             my ($s, $host, $key, $val) = @_;
1452             return 1 if $key ne 'ifTable';
1453             my $name = $val->[0][1];
1454             return 0 if ( $regexp && $name !~ /$regexp/ );
1455             # storing current octets data
1456             $cdata{$name}{t} = time;
1457             $cdata{$name}{i} = $val->[3][1];
1458             $cdata{$name}{o} = $val->[4][1];
1459             return 1;
1460             }
1461             );
1462             die "[ERROR] $err\n" unless $ret;
1463              
1464             # header
1465             $console->Cls();
1466             $console->Cursor(0, 0);
1467              
1468             printf "%s, up %s - %s\n\n",
1469             BOLD.$host.CLEAR, $ret->{sysUpTime}{0}, scalar(localtime(time));
1470              
1471             # matrix
1472             printf "%s%-30s (%-10s) %2s %2s %10s %10s %10s%s\n",
1473             UNDERSCORE, qw/ifName ifIndex Ad Op BW(Mbps) InBps(M) OutBps(M)/, CLEAR;
1474              
1475             my $iftable = $ret->{ifTable};
1476             foreach my $i ( sort { $a <=> $b } keys %{$iftable->[1]} )
1477             {
1478             my ($name, $astat, $ostat, $bw)
1479             = map { $iftable->[$_]{$i} } qw( 0 1 2 5 );
1480             if ( $first ){
1481             printf "%-30s (%-10d) %2d %2d %10.1f %10s %10s\n",
1482             $name, $i, $astat, $ostat, $bw/1000, '-', '-';
1483             next; # skip first
1484             }
1485              
1486             # calculate (k)bps
1487             my $td = $cdata{$name}{t} - $pdata{$name}{t};
1488             my ($inbps, $outbps) = map {
1489             my $delta = $cdata{$name}{$_} - $pdata{$name}{$_};
1490             $delta<0? 0: $delta / $td / 1000; # Kbps
1491             } qw( i o );
1492              
1493             printf "%-30s (%-10d) %2d %2d %10.1f %10.1f %10.1f\n",
1494             $name, $i, $astat, $ostat, map { $_/1000 } ($bw, $inbps, $outbps);
1495             }
1496              
1497             %pdata = %cdata;
1498             $first = 0;
1499             sleep $wait;
1500             }
1501              
1502             __END__
1503              
1504              
1505             =head2 3. Tiny MRTG with RRDTool (SNMPv2c)
1506              
1507             With installing Tobias Oetiker's RRDTool and RRD::Simple, this sample will do
1508             like MRTG. (It is better to execute this by cron.)
1509              
1510             If Environmental variables, PATH2DATADIR and URL2HTMLDIR, are defined, files will
1511             be stored under PATH2DATADIR and URL pathes will include URL2HTMLDIR in html.
1512             Or Modify $datadir and $htmldir to decide these path and URL where browser can
1513             access through your http service.
1514              
1515             Notice: This program is for devices which can deal SNMP version 2c.
1516              
1517             #!/usr/local/bin/perl
1518             use strict;
1519             use warnings;
1520             use Getopt::Std;
1521             use CGI qw(:html);
1522             use RRD::Simple; # install the "RRDTool" and RRD::Simple
1523             use Net::SNMP::Util qw(:para);
1524              
1525             my %opt;
1526             getopts('hc:x:', \%opt);
1527             my @hosts = @ARGV;
1528              
1529             sub HELP_MESSAGE {
1530             print "Usage: $0 [-c COMMUNITY_NAME] [-x REGEXP] HOST [HOST [...]]\n";
1531             exit 1;
1532             }
1533             HELP_MESSAGE() if ( !@hosts || $opt{h} );
1534              
1535             my $datadir = $ENV{PATH2DATADIR} || "/path/to/datadir"; # !!! Modify !!!
1536             my $htmldir = $ENV{URL2HTMLDIR} || "/path/to/htmldir"; # !!! Modify !!!
1537             my $regexp = $opt{x}? qr/$opt{x}/: '';
1538             my %sesopts = ( -version => 2, -community=> ($opt{c} || 'public') );
1539              
1540             sub escname {
1541             my $n = shift;
1542             $n =~ tr/\\\/\*\?\|"<>:,;%/_/;
1543             return $n;
1544             }
1545              
1546             # gather traffic data and store to RRD
1547             my ($result, $error) = snmpparawalk(
1548             hosts => \@hosts,
1549             snmp => \%sesopts,
1550             oids => {
1551             ifData => [ '1.3.6.1.2.1.31.1.1.1.1', # ifName
1552             '1.3.6.1.2.1.31.1.1.1.6', # ifHCInOctets
1553             '1.3.6.1.2.1.31.1.1.1.10' ] # ifHCOutOctets
1554             },
1555              
1556             # this callback will work everything of necessary
1557             -mycallback => sub {
1558             my ($s, $host, $key, $val) = @_;
1559             # val=[[index,name], [index,inOcts], [index,outOcts]]
1560             my ($index, $name) = @{$val->[0]};
1561              
1562             # check necessarity by ifName
1563             return 0 if ( $regexp && $name !~ /$regexp/ );
1564              
1565             my $basename = "$host.".escname($name);
1566             my $rrdfile = "$datadir/$basename.rrd";
1567              
1568             # treat RRD
1569             my $rrd = RRD::Simple->new( file => $rrdfile );
1570              
1571             #eval { # wanna catch an error, uncomment here.
1572              
1573             $rrd->create($rrdfile, 'mrtg',
1574             'in' => 'COUNTER', 'out' => 'COUNTER'
1575             ) unless -e $rrdfile;
1576              
1577             $rrd->update( $rrdfile, time,
1578             'in' => $val->[1][1], 'out' => $val->[2][1]
1579             );
1580              
1581             $rrd->graph( $rrdfile,
1582             destination => $datadir,
1583             basename => $basename,
1584             title => "$host :: $name",
1585             sources => [ qw( in out ) ],
1586             source_labels => [ qw( incoming outgoing ) ],
1587             source_colors => [ qw( 00cc00 0000ff ) ],
1588             source_drawtypes => [ qw( AREA LINE1 ) ]
1589             );
1590              
1591             #}; warn "[EVAL ERROR] $@" if $@;
1592              
1593             return 1;
1594             }
1595             );
1596             die "[ERROR] $error\n" unless $result;
1597              
1598             # make html
1599             sub mkimgtag {
1600             my ($host, $name, $type) = @_;
1601             my $basename = escname($name);
1602             img({ -src => "$htmldir/$host.$basename-$type.png",
1603             -alt => "$host $name $type",
1604             -title => "$type graph of $host $name",
1605             -border=> 0 });
1606             }
1607              
1608             open(HTML,"> $datadir/index.html") or die "$!";
1609             print HTML start_html(
1610             -title=> 'Traffic Monitor',
1611             -head => meta({ -http_equiv => 'refresh',
1612             -content => 300 })
1613             ), h1('Traffic Monitor');
1614              
1615             foreach my $host ( sort @hosts ){
1616             print HTML h2($host);
1617             foreach my $i ( sort keys %{$result->{$host}{ifData}[0]} ){
1618             my $name = $result->{$host}{ifData}[0]{$i};
1619             my $subhtml = "$host.".escname($name).".html";
1620              
1621             printf HTML a( {-href=>"$htmldir/$subhtml"},
1622             mkimgtag($host, $name, 'daily')
1623             );
1624              
1625             if ( open(HTML2,"> $datadir/$subhtml") ){
1626             print HTML2 start_html(
1627             -title=> 'Traffic Monitor',
1628             -head => meta({ -http_equiv => 'refresh',
1629             -content => 300 }) ),
1630             h1("$host $name"),
1631             (map { h2($_).p(mkimgtag($host, $name, $_)) }
1632             qw(daily weekly monthly annual)),
1633             end_html();
1634             close(HTML2);
1635             } else {
1636             warn "$!";
1637             }
1638             }
1639             }
1640              
1641             print HTML end_html();
1642             close(HTML);
1643              
1644             __END__
1645              
1646              
1647             =head1 REQUIREMENTS
1648              
1649             See C.
1650              
1651             =head1 AUTHOR
1652              
1653             t.onodera, C<< >>
1654              
1655             =head1 TO DO
1656              
1657             - Implementation of simple trapping functions
1658              
1659             =head1 SEE ALSO
1660              
1661             =over
1662              
1663             =item *
1664              
1665             L - Core module of C which brings us good SNMP
1666             implementations.
1667              
1668             =item *
1669              
1670             L - Sub module of C which provides
1671             easy and simple functions to treat OID.
1672              
1673             =item *
1674              
1675             L - Sub module of C which provides
1676             easy and simple functions to treat textual conversion.
1677              
1678             =back
1679              
1680             =head1 LICENSE AND COPYRIGHT
1681              
1682             Copyright(C) 2011- Takahiro Ondoera.
1683              
1684             This program is free software; you may redistribute it and/or modify it under
1685             the same terms as the Perl 5 programming language system itself.
1686              
1687             =cut
1688              
1689             1; # End of Net::SNMP::Util