File Coverage

blib/lib/AnyEvent/Graphite/SNMPAgent.pm
Criterion Covered Total %
statement 18 54 33.3
branch 0 8 0.0
condition 0 8 0.0
subroutine 6 12 50.0
pod 0 3 0.0
total 24 85 28.2


line stmt bran cond sub pod time code
1             package AnyEvent::Graphite::SNMPAgent;
2              
3 1     1   12836 use warnings;
  1         2  
  1         38  
4 1     1   6 use strict;
  1         2  
  1         49  
5              
6 1     1   6 use AnyEvent::Graphite;
  1         2  
  1         20  
7 1     1   24975 use AnyEvent::SNMP;
  1         335567  
  1         101  
8 1     1   18 use AnyEvent::DNS;
  1         2  
  1         36  
9 1     1   6 use Net::SNMP;
  1         2  
  1         1020  
10              
11             sub new {
12 0     0 0   my($class, %args) = @_;
13              
14             # the rest of the args will get passed to the object
15             # so remove host and port as we'll possibly set them manually here
16 0           my $graphite = AnyEvent::Graphite->new(host => $args{host}, port => $args{port});
17 0           delete $args{host};
18 0           delete $args{port};
19            
20             # the hosts we'll be storing metrics in
21 0           my $hosts = {};
22 0   0       my $interval = delete $args{interval} || 60;
23              
24 0   0       my $timeout = delete $args{timeout} || 3;
25              
26 0   0       my $snmp_version = delete $args{snmp_version} || "1";
27              
28             # need to know about $self before defining the callback just below this
29 0           my $self = bless {
30             graphite => $graphite,
31             hosts => $hosts,
32             snmp_version => $snmp_version,
33             %args,
34             }, $class;
35              
36            
37             # start the timer running
38 0     0     $self->{timer} = AE::timer 0, $interval, sub { $self->gather_metrics; };
  0            
39              
40 0           return $self;
41             }
42              
43             sub add_snmp {
44 0     0 0   my ($self, %arg) = @_;
45             # toss the arguments into the (per-host) queue
46             # look up the host with AnyEvent::DNS so we can be async even here
47             AnyEvent::DNS::a $arg{'host'}, sub {
48 0     0     my (@addrs) = @_;
49 0           push(@{$self->{hosts}{$addrs[0]}}, \%arg);
  0            
50 0           };
51             }
52              
53             # this is called by $self->{timer} every $interval seconds
54             sub gather_metrics {
55 0     0 0   my ($self) = @_;
56 0           for my $host (keys %{$self->{hosts}}) {
  0            
57              
58             # skip any hosts that did not resolve
59 0 0         next unless $host;
60              
61             # steal a community string from the first item in the list. They should all be the same
62 0   0       my $community = $self->{hosts}{$host}[0]{community} || "public";
63 0           my $session = Net::SNMP->session(
64             -hostname => $host,
65             -community => $community,
66             -nonblocking => 1,
67             -version => $self->{snmp_version}
68             );
69              
70             # in this kind of context it's not clear what would be better to do with errors, here.
71 0 0         next unless $session;
72              
73             # if you don't set a timeout, you can fill up your queues of outstanding processes
74             # protects against 'lame' servers
75 0           $session->timeout($self->{timeout});
76              
77 0           for my $metric (@{$self->{hosts}{$host}}) {
  0            
78             $session->get_request(
79             -varbindlist => [$metric->{oid}],
80             -callback => sub {
81 0     0     my ($session) = @_;
82 0           my $result = $session->var_bind_list();
83 0           my $value = $result->{$metric->{oid}};
84 0 0         if(!defined($value)) {
85 0           warn "undefined reply from $host" . ":" . "$metric->{oid}: " . $session->error();
86             } else {
87 0 0         if($metric->{filter}) {
88 0           $value = $metric->{filter}->($value);
89             }
90 0           $self->{graphite}->send($metric->{graphite_key}, $value);
91             }
92 0           });
93             }
94             }
95              
96             }
97              
98             =head1 NAME
99              
100             AnyEvent::Graphite::SNMPAgent - An SNMP agent which does non-blocking streaming of data from an SNMP server
101              
102             =head1 SYNOPSIS
103            
104             my $agent = AnyEvent::Graphite::SNMPAgent->new(
105             host => '127.0.0.1',
106             port => '2003',
107             interval => 60,
108             timeout => 5,
109             );
110              
111             'host' and 'port' are for the graphite server
112             'interval' is how many seconds should elapse between each time we try to fire off the queries.
113             If you need multiple intervals create one AE::Graphite::SNMPAgent instance per set of metrics
114              
115              
116             $agent->add_snmp(host => $host, oid => $oid, community => $community, graphite_key => $key, filter => sub { ... });
117              
118             print "Running forever. CTRL-C to interrupt\n";
119             AnyEvent->condvar->recv;
120              
121              
122             =head1 AUTHOR
123              
124             Joshua Barratt, C<< <josh at mediatemple.net> >>
125              
126             =head1 COPYRIGHT & LICENSE
127              
128             This program is free software; you can redistribute it and/or modify it
129             under the same terms as Perl itself.
130              
131             =cut
132              
133             1; # End of AnyEvent::Graphite