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