File Coverage

blib/lib/Mojo/SNMP/Dispatcher.pm
Criterion Covered Total %
statement 21 114 18.4
branch 1 38 2.6
condition 0 9 0.0
subroutine 7 20 35.0
pod 8 8 100.0
total 37 189 19.5


line stmt bran cond sub pod time code
1             package Mojo::SNMP::Dispatcher;
2              
3             =head1 NAME
4              
5             Mojo::SNMP::Dispatcher - Instead of Net::SNMP::Dispatcher
6              
7             =head1 DESCRIPTION
8              
9             This module works better with L since it register the
10             L sockets in with the mojo reactor.
11              
12             =cut
13              
14 11     11   57 use Errno;
  11         19  
  11         486  
15 11     11   61 use Mojo::Base -base;
  11         24  
  11         75  
16 11     11   1529 use Mojo::IOLoop::Stream;
  11         20  
  11         88  
17 11     11   9054 use Net::SNMP::MessageProcessing ();
  11         582868  
  11         324  
18 11     11   115 use Net::SNMP::Message qw( TRUE FALSE );
  11         25  
  11         654  
19 11     11   61 use Scalar::Util ();
  11         21  
  11         345  
20 11 50   11   59 use constant DEBUG => $ENV{MOJO_SNMP_DEBUG} ? 1 : 0;
  11         15  
  11         19174  
21              
22             =head1 ATTRIBUTES
23              
24             =head2 ioloop
25              
26             Holds a L object. Same as L.
27              
28             =head2 message_processing
29              
30             Holds an instance of L.
31              
32             =head2 debug
33              
34             Does nothing. Use C instead to get debug information.
35              
36             =head2 error
37              
38             Holds the last error.
39              
40             =cut
41              
42             has ioloop => sub { Mojo::IOLoop->singleton };
43             has message_processing => sub { Net::SNMP::MessageProcessing->instance };
44             has debug => 0; # Use MOJO_SNMP_DEBUG=1 instead
45              
46             =head2 connections
47              
48             Holds the number of active sockets.
49              
50             =cut
51              
52 0     0 1   sub connections { int values %{$_[0]->{descriptors}} }
  0            
53              
54             sub error {
55 0     0 1   my ($self, $format, @args) = @_;
56              
57 0 0         return $self->{error} if @_ == 1;
58 0 0         $self->{error} = defined $format ? sprintf $format, @args : undef;
59 0           warn "[Mojo::SNMP::Dispatcher] error: $self->{error}\n" if DEBUG and defined $format;
60 0           return $self;
61             }
62              
63             =head1 METHODS
64              
65             =head2 send_pdu
66              
67             This method will send a PDU to the SNMP server.
68              
69             =cut
70              
71             sub send_pdu {
72 0     0 1   my ($self, $pdu, $delay) = @_;
73              
74 0 0         unless (ref $pdu) {
75 0           $self->error('The required PDU object is missing or invalid');
76 0           return FALSE;
77             }
78              
79 0           $self->error(undef);
80 0           $self->schedule($delay, [_send_pdu => $pdu, $pdu->retries]);
81              
82 0           return TRUE;
83             }
84              
85             =head2 return_response_pdu
86              
87             No idea what this does (?)
88              
89             =cut
90              
91             sub return_response_pdu {
92 0     0 1   $_[0]->send_pdu($_[1], -1);
93             }
94              
95             =head2 msg_handle_alloc
96              
97             No idea what this does (?)
98              
99             =cut
100              
101             sub msg_handle_alloc {
102 0     0 1   $_[0]->message_processing->msg_handle_alloc;
103             }
104              
105             =head2 schedule
106              
107             Used to schedule events at a given time. Use L to
108             do the heavy lifting.
109              
110             =cut
111              
112             sub schedule {
113 0     0 1   my ($self, $time, $callback) = @_;
114 0           my $code = shift @$callback;
115              
116 0           warn "[Mojo::SNMP::Dispatcher] Schedule $time $code(@$callback)\n" if DEBUG;
117              
118 0           Scalar::Util::weaken($self);
119 0     0     $self->ioloop->timer($time => sub { $self->$code(@$callback) });
  0            
120             }
121              
122             =head2 register
123              
124             Register a new transport object with L.
125              
126             =cut
127              
128             sub register {
129 0     0 1   my ($self, $transport) = @_;
130 0           my $reactor = $self->ioloop->reactor;
131 0           my $fileno;
132              
133 0 0 0       unless (defined $transport and defined($fileno = $transport->fileno)) {
134 0           $self->error('The Transport Domain object is invalid');
135 0           return FALSE;
136             }
137              
138 0 0         if ($self->{descriptors}{$fileno}++) {
139 0           return $transport;
140             }
141              
142 0           Scalar::Util::weaken($self);
143             $reactor->io(
144             $transport->socket,
145             sub {
146 0     0     $self->_transport_response_received($transport);
147             }
148 0           );
149              
150 0           $reactor->watch($transport->socket, 1, 0);
151 0           warn "[Mojo::SNMP::Dispatcher] Add handler for descriptor $fileno\n" if DEBUG;
152 0           return $transport;
153             }
154              
155             =head2 deregister
156              
157             The opposite of L.
158              
159             =cut
160              
161             sub deregister {
162 0     0 1   my ($self, $transport) = @_;
163 0           my $fileno = $transport->fileno;
164 0 0         return if --$self->{descriptors}{$fileno} > 0;
165 0           delete $self->{descriptors}{$fileno};
166 0           warn "[Mojo::SNMP::Dispatcher] Remove handler for descriptor $fileno\n" if DEBUG;
167 0           $self->ioloop->reactor->remove($transport->socket);
168             }
169              
170             sub _send_pdu {
171 0     0     my ($self, $pdu, $retries) = @_;
172 0           my $mp = $self->message_processing;
173 0           my $msg = $mp->prepare_outgoing_msg($pdu);
174              
175 0 0         unless (defined $msg) {
176 0           warn "[Mojo::SNMP::Dispatcher] prepare_outgoing_msg: @{[$mp->error]}\n" if DEBUG;
177 0           $pdu->status_information($mp->error);
178 0           return;
179             }
180 0 0         unless (defined $msg->send) {
181 0 0         if ($pdu->expect_response) {
182 0           $mp->msg_handle_delete($msg->msg_id);
183             }
184 0 0 0       if ($retries-- > 0 and $!{EAGAIN} or $!{EWOULDBLOCK}) {
      0        
185 0           warn "[Mojo::SNMP::Dispatcher] Attempt to recover from temporary failure: $!\n" if DEBUG;
186 0           $self->schedule($pdu->timeout, [_send_pdu => $pdu, $retries]);
187 0           return FALSE;
188             }
189              
190 0           $pdu->status_information($msg->error);
191 0           return;
192             }
193              
194 0 0         if ($pdu->expect_response) {
195 0           $self->register($msg->transport);
196 0           $msg->timeout_id($self->schedule($pdu->timeout, ['_transport_timeout', $pdu, $retries, $msg->msg_id,]));
197             }
198              
199 0           return TRUE;
200             }
201              
202             sub _transport_timeout {
203 0     0     my ($self, $pdu, $retries, $handle) = @_;
204              
205 0           $self->deregister($pdu->transport);
206 0           $self->message_processing->msg_handle_delete($handle);
207              
208 0 0         if ($retries-- > 0) {
209 0           warn "[Mojo::SNMP::Dispatcher] Retries left: $retries\n" if DEBUG;
210 0           return $self->_send_pdu($pdu, $retries);
211             }
212             else {
213 0           warn "[Mojo::SNMP::Dispatcher] No response from remote host @{[ $pdu->hostname ]}\n" if DEBUG;
214 0           $pdu->status_information(q{No response from remote host "%s"}, $pdu->hostname);
215 0           return;
216             }
217             }
218              
219             sub _transport_response_received {
220 0     0     my ($self, $transport) = @_;
221 0           my $mp = $self->message_processing;
222 0           my ($msg, $error) = Net::SNMP::Message->new(-transport => $transport);
223              
224 0           $self->error(undef);
225              
226 0 0         if (not defined $msg) {
227 0           die sprintf 'Failed to create Message object: %s', $error;
228             }
229 0 0         if (not defined $msg->recv) {
230 0           $self->error($msg->error);
231 0 0         $self->deregister($transport) unless $transport->connectionless;
232 0           return;
233             }
234 0 0         if (not $msg->length) {
235 0           warn "[Mojo::SNMP::Dispatcher] Ignoring zero length message\n" if DEBUG;
236 0           return;
237             }
238 0 0         if (not defined $mp->prepare_data_elements($msg)) {
239 0           $self->error($mp->error);
240 0           return;
241             }
242 0 0         if ($mp->error) {
243 0           $msg->error($mp->error);
244             }
245              
246 0           warn "[Mojo::SNMP::Dispatcher] Processing pdu\n" if DEBUG;
247 0           $self->ioloop->remove($msg->timeout_id);
248 0           $self->deregister($transport);
249 0           $msg->process_response_pdu;
250             }
251              
252             =head1 COPYRIGHT & LICENSE
253              
254             This library is free software. You can redistribute it and/or modify
255             it under the same terms as Perl itself.
256              
257             =head1 AUTHOR
258              
259             Jan Henning Thorsen - C
260              
261             =cut
262              
263             1;