File Coverage

blib/lib/Net/Peep/Notifier.pm
Criterion Covered Total %
statement 21 176 11.9
branch 0 72 0.0
condition 0 72 0.0
subroutine 7 23 30.4
pod 0 5 0.0
total 28 348 8.0


line stmt bran cond sub pod time code
1             package Net::Peep::Notifier;
2              
3             require 5.005;
4 3     3   17 use strict;
  3         6  
  3         104  
5 3     3   15 use Carp;
  3         6  
  3         180  
6 3     3   16 use Sys::Hostname;
  3         5  
  3         130  
7 3     3   15 use Data::Dumper;
  3         5  
  3         115  
8 3     3   16 use Net::Peep::Log;
  3         5  
  3         91  
9 3     3   1486 use Net::Peep::Mail;
  3         7  
  3         179  
10              
11             require Exporter;
12              
13 3         7675 use vars qw{ @ISA %EXPORT_TAGS @EXPORT_OK @EXPORT $VERSION $LOGGER
14             $NOTIFICATIONS $NOTIFICATION_INTERVAL
15             %NOTIFICATION_LEVEL %NOTIFICATION_RECIPIENTS
16             %NOTIFICATION_HOSTS
17 3     3   15 @SMTP_RELAYS $HOSTNAME $USER $FROM };
  3         6  
18              
19             @ISA = qw(Exporter);
20             %EXPORT_TAGS = ( );
21             @EXPORT_OK = ( );
22             @EXPORT = qw( );
23             $VERSION = do { my @r = (q$Revision: 1.1 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
24              
25             $LOGGER = Net::Peep::Log->new();
26              
27             $NOTIFICATIONS = {}; # used for caching failsafe information
28             $NOTIFICATION_INTERVAL = 1800; # seconds
29             %NOTIFICATION_LEVEL = ( ); # see below for more information
30             %NOTIFICATION_RECIPIENTS = ( );
31             %NOTIFICATION_HOSTS = ( );
32             @SMTP_RELAYS = ( 'localhost' );
33              
34             $HOSTNAME = hostname() ? hostname() : 'localhost';
35             $USER = $ENV{'USER'} ? $ENV{'USER'} : 'peep';
36              
37             $FROM = "Peep Notification <$USER\@$HOSTNAME>";
38              
39             sub new {
40              
41 0     0 0   my $self = shift;
42 0   0       my $class = ref($self) || $self;
43 0           my $this = {};
44 0           bless $this, $class;
45              
46             } # end sub new
47              
48             sub _timeToSend {
49              
50             # answers the question: based on the time that the last
51             # notification was sent, is it time to send another notification
52              
53 0     0     my $self = shift;
54 0           my $client = shift;
55              
56 0           my $last_send_time;
57 0 0         if ($self->_sendTimeExists($client)) {
58 0           $last_send_time = $self->_getSendTime($client);
59             } else {
60 0           return 1; # if there is no send time, it's time!
61             }
62              
63 0           my $time = time();
64              
65 0 0         if ($time - $last_send_time > $NOTIFICATION_INTERVAL) {
66 0           return 1;
67             } else {
68 0           return 0;
69             }
70              
71             } # end sub _timeToSend
72              
73             sub _notificationExists {
74              
75             # returns 1 if a notification has been sent for a client of the
76             # type stored in the generator object, 0 otherwise
77              
78 0     0     my $self = shift;
79 0   0       my $client = shift || confess "client not found";
80              
81 0 0         if (exists $NOTIFICATIONS->{$client}) {
82 0           return 1;
83             } else {
84 0           return 0;
85             }
86              
87             } # end sub _notificationExists
88              
89             sub _getNotificationRecipients {
90              
91 0     0     my $self = shift;
92 0   0       my $client = shift || confess "client not found";
93              
94 0 0         unless (exists $NOTIFICATION_RECIPIENTS{$client}) {
95 0 0         return wantarray ? () : [];
96             }
97              
98 0 0         return wantarray ? @{$NOTIFICATION_RECIPIENTS{$client}} : $NOTIFICATION_RECIPIENTS{$client};
  0            
99              
100             } # end sub _getNotificationRecipients
101              
102             sub _getNotificationHosts {
103              
104 0     0     my $self = shift;
105 0   0       my $client = shift || confess "client not found";
106              
107 0 0         unless (exists $NOTIFICATION_HOSTS{$client}) {
108 0 0         return wantarray ? () : [];
109             }
110              
111 0 0         return wantarray ? @{$NOTIFICATION_HOSTS{$client}} : $NOTIFICATION_HOSTS{$client};
  0            
112              
113             } # end sub _getNotificationHosts
114              
115             sub notify {
116              
117 0     0 0   my $self = shift;
118 0   0       my $notification = shift || confess "notification not found";
119 0           my $force = shift;
120              
121 0   0       my $client = $notification->client() || confess "client not found";
122              
123 0 0 0       if (exists $NOTIFICATION_HOSTS{$client} &&
      0        
124             exists $NOTIFICATION_RECIPIENTS{$client} &&
125             exists $NOTIFICATION_LEVEL{$client}) {
126 0 0         unless (scalar($self->_getNotificationRecipients($client))) {
127 0           $LOGGER->debug(1,"Cannot notify recipients for client [$client]: ".
128             "No recipients were specified.");
129 0           return 0;
130             }
131              
132 0           my @hosts = $self->_getNotificationHosts($client);
133              
134 0 0         unless (scalar(@hosts)) {
135 0           $LOGGER->debug(1,"Cannot notify recipients for client [$client]: ".
136             "No acceptable notification hosts [@hosts] were specified.");
137 0           return 0;
138             }
139              
140 0           my $ok = 0;
141            
142 0           for my $host (@hosts) {
143 0           $LOGGER->debug(8,"Checking host [$host] against hostname [$HOSTNAME] ...\n");
144 0 0         $ok++ if $host eq $HOSTNAME;
145 0 0         $ok++ if $host =~ /^(all|localhost|127\.0\.0\.1)$/;
146 0 0         $ok = 0, last if $host eq 'none';
147             }
148            
149 0           my $status = $notification->status();
150 0 0 0       if ($NOTIFICATION_LEVEL{$client} eq 'crit' && $status ne 'crit') {
    0 0        
151 0           $LOGGER->debug(1,"Notification ignored: Notification level is [$status]. ".
152             "Notifications are only sent at [crit].");
153 0           return 1;
154             } elsif ($NOTIFICATION_LEVEL{$client} eq 'warn' && $status !~ /^(warn|crit)$/) {
155 0           $LOGGER->debug(1,"Notification ignored: Notification level is [$status]. ".
156             "Notifications are only sent at [crit] or [warn].");
157 0           return 1;
158             } else {
159             # do nothing
160             }
161            
162 0           my $return;
163            
164 0 0         if ($ok) {
165            
166 0           eval {
167            
168 0 0         confess "Cannot notify recipients: No client was specified."
169             unless $client;
170 0 0         confess "Cannot notify recipients: No mail relays were specified."
171             unless scalar(@SMTP_RELAYS);
172            
173 0           $self->store($notification);
174            
175 0 0         if ($self->_notificationExists($client)) {
176 0 0 0       if ($self->_timeToSend($client) || $force) {
177 0           $return = $self->_notify($client);
178             } else {
179 0           $return = 1;
180             }
181             } else {
182 0           $return = $self->_notify($client);
183             }
184            
185             };
186              
187 0 0 0       if ($@ || ! $return) {
188 0           $LOGGER->log(ref($self),": Error generating notification: $@");
189             }
190            
191 0           return $return;
192            
193             } else {
194            
195 0           $LOGGER->debug(1,"Notification ignored: [$HOSTNAME] was not among the list of acceptable notification hosts.");
196 0           return 1;
197            
198             }
199              
200             } else {
201              
202 0           $LOGGER->debug(1,"Notification ignored: No notification information is available for client [$client].\n".
203             "\t(Possibly because no notification block was defined for the client in the Peep \n".
204             "\tconfiguration file.)");
205 0           return 1;
206              
207             }
208            
209             } # end sub notify
210              
211             sub _notify {
212              
213             # notify the recipients of the notification
214 0     0     my $self = shift;
215 0   0       my $client = shift || confess "client not found";
216            
217 0 0         if (scalar($self->_getNotifications($client))) {
218              
219 0           my $mailer = Net::Peep::Mail->new();
220            
221 0           $mailer->smtp_server(@SMTP_RELAYS);
222 0           $mailer->from("Peep Notification <$USER\@$HOSTNAME>");
223 0           $mailer->to($self->_getNotificationRecipients($client));
224 0           $mailer->subject("[Peep Notification] $client on $HOSTNAME");
225 0           $mailer->body($self->_getBody($client));
226 0 0         if ($mailer->send()) {
227 0           $self->_setSendTime($client);
228 0           $self->_garbageCollect($client);
229 0           return 1;
230             } else {
231 0           return 0;
232             }
233              
234             }
235              
236             } # end sub _notify
237              
238             sub _getNotifications {
239              
240             # Return only those notifications exceeding the notification
241             # threshhold defined in the Peep configuration file for the
242             # specified client
243              
244 0     0     my $self = shift;
245 0   0       my $client = shift || confess "client not found";
246              
247 0 0         return () unless $self->_notificationExists($client);
248              
249 0   0       my $level = $NOTIFICATION_LEVEL{$client}
250             || confess "Notification level for client [$client] not found.";
251              
252 0 0         if ($level eq 'crit') {
    0          
253 0           return grep $_->status() eq 'crit', @{$NOTIFICATIONS->{$client}->{'NOTIFICATIONS'}};
  0            
254             } elsif ($level eq 'warn') {
255 0           return grep $_->status() =~ /warn|crit/, @{$NOTIFICATIONS->{$client}->{'NOTIFICATIONS'}};
  0            
256             } else {
257 0           return @{$NOTIFICATIONS->{$client}->{'NOTIFICATIONS'}};
  0            
258             }
259              
260             } # end sub _getNotifications
261              
262             sub _getBody {
263              
264 0     0     my $self = shift;
265 0           my $client = shift;
266              
267 0 0         return undef unless $self->_notificationExists($client);
268              
269 0           my @notifications = $self->_getNotifications($client);
270              
271 0           my $n_notifications = scalar(@notifications);
272              
273 0           my $time = scalar(localtime(time()));
274              
275 0           my $body = <<"eop";
276             User: $USER
277             Host: $HOSTNAME
278             Client: $client
279             Time: $time
280             Notifications: $n_notifications
281              
282             eop
283              
284             ;
285              
286 0           my $i = 1;
287              
288             # reverse the order of notifications to get the most recent first
289 0           for my $notification (reverse @notifications) {
290              
291 0           my $status = ucfirst($notification->status());
292 0           my $message = $notification->message();
293 0           my $time = scalar(localtime($notification->datetime()));
294              
295 0           $body .= <<"eop";
296             \[Notification $i: $status at $time\]
297             $message
298              
299             eop
300              
301             ;
302              
303 0           $i++;
304              
305             }
306              
307 0           return $body;
308              
309             } # end sub _getBody
310              
311             sub store {
312              
313             # store failsafe information for future reference
314 0     0 0   my $self = shift;
315 0           my $notification = shift; # a Net::Peep::Notification object
316              
317 0   0       my $client = $notification->client() || confess "client not found";
318 0           push @{$NOTIFICATIONS->{$client}->{'NOTIFICATIONS'}}, $notification;
  0            
319              
320 0           return 1;
321              
322             } # end sub store
323              
324             sub _sendTimeExists {
325              
326 0     0     my $self = shift;
327 0   0       my $client = shift || confess "client not found";
328            
329 0 0 0       if (exists $NOTIFICATIONS->{$client} &&
330             exists $NOTIFICATIONS->{$client}->{'SEND_TIME'}) {
331 0           return 1;
332             } else {
333 0           return 0;
334             }
335              
336             } # end sub _sendTimeExists
337              
338             sub _setSendTime {
339              
340 0     0     my $self = shift;
341 0   0       my $client = shift || confess "client not found";
342 0           $NOTIFICATIONS->{$client}->{'SEND_TIME'} = time();
343              
344             } # end sub _setSendTime
345              
346             sub _getSendTime {
347              
348 0     0     my $self = shift;
349 0   0       my $client = shift || confess "client not found";
350              
351 0 0         if (exists $NOTIFICATIONS->{$client}) {
352 0 0         if (exists $NOTIFICATIONS->{$client}->{'SEND_TIME'}) {
353 0           return $NOTIFICATIONS->{$client}->{'SEND_TIME'};
354             } else {
355 0           confess "Cannot find SEND_TIME key for client [$client]";
356             }
357             } else {
358 0           return undef;
359             }
360              
361             } # end sub _getSendTime
362              
363             sub _garbageCollect {
364              
365             # clear out old notification information
366              
367 0     0     my $self = shift;
368              
369 0   0       my $client = shift || confess "client not found";
370 0 0 0       delete $NOTIFICATIONS->{$client}->{NOTIFICATIONS}
371             if exists $NOTIFICATIONS->{$client} &&
372             exists $NOTIFICATIONS->{$client}->{NOTIFICATIONS};
373 0           $NOTIFICATIONS->{$client}->{NOTIFICATIONS} = [];
374              
375 0           return 1;
376              
377             } # end sub _garbageCollect
378              
379             sub force {
380              
381             # clear out any unsent notifications whether or not they're ready
382             # to be sent
383 0     0 0   my $self = shift;
384              
385 0           my $force = 1;
386              
387 0           my $n;
388              
389 0           for my $client (keys %$NOTIFICATIONS) {
390 0 0 0       if (exists $NOTIFICATIONS->{$client}->{'NOTIFICATIONS'} &&
391             exists $NOTIFICATIONS->{$client}->{'SEND_TIME'}) {
392 0           my @notifications = @{$NOTIFICATIONS->{$client}->{'NOTIFICATIONS'}};
  0            
393 0           $n = @notifications;
394 0 0         $self->notify($notifications[0],$force) if @notifications;
395             }
396             }
397              
398 0           return $n;
399              
400             } # end sub force
401              
402             sub flush {
403              
404             # clear out any unsent notifications that are ready to be sent
405 0     0 0   my $notifier = Net::Peep::Notifier->new();
406              
407 0           for my $client (keys %$NOTIFICATIONS) {
408 0 0 0       if (exists $NOTIFICATIONS->{$client}->{'NOTIFICATIONS'} &&
409             exists $NOTIFICATIONS->{$client}->{'SEND_TIME'}) {
410 0           my @notifications = @{$NOTIFICATIONS->{$client}->{'NOTIFICATIONS'}};
  0            
411 0           $notifier->notify($client);
412             }
413             }
414              
415             } # end sub flush
416              
417             1;
418              
419             __END__