ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-SNMP/SNMP.pm
Revision: 1.2
Committed: Fri Apr 10 06:50:16 2009 UTC (15 years, 1 month ago) by root
Branch: MAIN
CVS Tags: rel-0_11
Changes since 1.1: +1 -1 lines
Log Message:
0.11

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     AnyEvent::SNMP - adaptor to integrate Net::SNMP into Anyevent.
4    
5     =head1 SYNOPSIS
6    
7     use AnyEvent::SNMP;
8     use Net::SNMP;
9    
10     # just use Net::SNMP and AnyEvent as you like:
11    
12     # use a condvar to transfer results, this is
13     # just an example, you can use a naked callback as well.
14     my $cv = AnyEvent->condvar;
15    
16     # ... start non-blocking snmp request(s)...
17     Net::SNMP->session (-hostname => "127.0.0.1",
18     -community => "public",
19     -nonblocking => 1)
20     ->get_request (-callback => sub { $cv->send (@_) });
21    
22     # ... do something else until the result is required
23     my @result = $cv->wait;
24    
25     =head1 DESCRIPTION
26    
27     This module implements an alternative "event dispatcher" for Net::SNMP,
28     using AnyEvent as a backend.
29    
30     This integrates Net::SNMP into AnyEvent: You can make non-blocking
31     Net::SNMP calls and as long as other parts of your program also use
32     AnyEvent (or some event loop supported by AnyEvent), they will run in
33     parallel.
34    
35     Also, the Net::SNMP scheduler is very inefficient with respect to both CPU
36     and memory usage. Most AnyEvent backends (including the pure-perl backend)
37     fare much better than the Net::SNMP dispatcher.
38    
39     A potential disadvantage is that replacing the dispatcher is not at all
40     a documented thing to do, so future changes in Net::SNP might break this
41     module (or the many similar ones).
42    
43     This module does not export anything and does not require you to do
44     anything special apart from loading it I<before doing any non-blocking
45     requests with Net::SNMP>. It is recommended but not required to load this
46     module before C<Net::SNMP>.
47    
48     =cut
49    
50     package AnyEvent::SNMP;
51    
52     no warnings;
53     use strict qw(subs vars);
54    
55     # it is possible to do this without loading
56     # Net::SNMP::Dispatcher, but much more awkward.
57     use Net::SNMP::Dispatcher;
58    
59     sub Net::SNMP::Dispatcher::instance {
60     AnyEvent::SNMP::
61     }
62    
63     use Net::SNMP ();
64     use AnyEvent ();
65    
66 root 1.2 our $VERSION = '0.11';
67 root 1.1
68     $Net::SNMP::DISPATCHER = instance Net::SNMP::Dispatcher;
69    
70     our $MESSAGE_PROCESSING = $Net::SNMP::Dispatcher::MESSAGE_PROCESSING;
71    
72     # avoid the method call
73     my $timer = sub { shift->timer (@_) };
74     AnyEvent::post_detect { $timer = AnyEvent->can ("timer") };
75    
76     our $BUSY;
77     our %TRANSPORT; # address => [count, watcher]
78    
79     sub _send_pdu {
80     my ($pdu, $retries) = @_;
81    
82     # mostly copied from Net::SNMP::Dispatch
83    
84     # Pass the PDU to Message Processing so that it can
85     # create the new outgoing message.
86     my $msg = $MESSAGE_PROCESSING->prepare_outgoing_msg ($pdu);
87    
88     if (!defined $msg) {
89     --$BUSY;
90     # Inform the command generator about the Message Processing error.
91     $pdu->status_information ($MESSAGE_PROCESSING->error);
92     return;
93     }
94    
95     # Actually send the message.
96     if (!defined $msg->send) {
97     $MESSAGE_PROCESSING->msg_handle_delete ($pdu->msg_id)
98     if $pdu->expect_response;
99    
100     # A crude attempt to recover from temporary failures.
101     if ($retries-- > 0 && ($!{EAGAIN} || $!{EWOULDBLOCK} || $!{ENOSPC})) {
102     my $retry_w; $retry_w = AnyEvent->$timer (after => $pdu->timeout, cb => sub {
103     undef $retry_w;
104     _send_pdu ($pdu, $retries);
105     });
106     } else {
107     --$BUSY;
108     }
109    
110     # Inform the command generator about the send() error.
111     $pdu->status_information ($msg->error);
112     return;
113     }
114    
115     # Schedule the timeout handler if the message expects a response.
116     if ($pdu->expect_response) {
117     my $transport = $msg->transport;
118    
119     # register the transport
120     unless ($TRANSPORT{$transport+0}[0]++) {
121     $TRANSPORT{$transport+0}[1] = AnyEvent->io (fh => $transport->socket, poll => 'r', cb => sub {
122     # Create a new Message object to receive the response
123     my ($msg, $error) = Net::SNMP::Message->new (-transport => $transport);
124    
125     if (!defined $msg) {
126     die sprintf 'Failed to create Message object [%s]', $error;
127     }
128    
129     # Read the message from the Transport Layer
130     if (!defined $msg->recv) {
131     # for some reason, connected-oriented transports seem to need this
132     unless ($transport->connectionless) {
133     delete $TRANSPORT{$transport+0}
134     unless --$TRANSPORT{$transport+0}[0];
135     }
136    
137     $msg->error;
138     return;
139     }
140    
141     # For connection-oriented Transport Domains, it is possible to
142     # "recv" an empty buffer if reassembly is required.
143     if (!$msg->length) {
144     return;
145     }
146    
147     # Hand the message over to Message Processing.
148     if (!defined $MESSAGE_PROCESSING->prepare_data_elements ($msg)) {
149     $MESSAGE_PROCESSING->error;
150     return;
151     }
152    
153     # Set the error if applicable.
154     $msg->error ($MESSAGE_PROCESSING->error) if $MESSAGE_PROCESSING->error;
155    
156     # Cancel the timeout.
157     my $rtimeout_w = $msg->timeout_id;
158     if ($$rtimeout_w) {
159     undef $$rtimeout_w;
160     delete $TRANSPORT{$transport+0}
161     unless --$TRANSPORT{$transport+0}[0];
162    
163     --$BUSY;
164     }
165    
166     # Notify the command generator to process the response.
167     $msg->process_response_pdu;
168     });
169     }
170    
171     #####d# timeout_id, wtf?
172     $msg->timeout_id (\(my $rtimeout_w =
173     AnyEvent->$timer (after => $pdu->timeout, cb => sub {
174     my $rtimeout_w = $msg->timeout_id;
175     if ($$rtimeout_w) {
176     undef $$rtimeout_w;
177     delete $TRANSPORT{$transport+0}
178     unless --$TRANSPORT{$transport+0}[0];
179     }
180    
181     if ($retries--) {
182     _send_pdu ($pdu, $retries);
183     } else {
184     --$BUSY;
185     $MESSAGE_PROCESSING->msg_handle_delete ($pdu->msg_id);
186     $pdu->status_information ("No response from remote host '%s'", $pdu->hostname);
187     }
188     })
189     ));
190     } else {
191     --$BUSY;
192     }
193     }
194    
195     sub send_pdu($$$) {
196     my (undef, $pdu, $delay) = @_;
197    
198     ++$BUSY;
199    
200     if ($delay > 0) {
201     my $delay_w; $delay_w = AnyEvent->$timer (after => $delay, cb => sub {
202     undef $delay_w;
203     _send_pdu ($pdu, $pdu->retries);
204     });
205     return 1;
206     }
207    
208     _send_pdu $pdu, $pdu->retries;
209     1
210     }
211    
212     sub activate($) {
213     AnyEvent->one_event while $BUSY;
214     }
215    
216     sub one_event($) {
217     die;
218     }
219    
220     =head1 SEE ALSO
221    
222     L<AnyEvent>, L<Net::SNMP>, L<Net::SNMP::EV>.
223    
224     =head1 AUTHOR
225    
226     Marc Lehmann <schmorp@schmorp.de>
227     http://home.schmorp.de/
228    
229     =cut
230    
231     1
232