File Coverage

blib/lib/Net/Peep/Client.pm
Criterion Covered Total %
statement 148 273 54.2
branch 35 110 31.8
condition 7 42 16.6
subroutine 27 36 75.0
pod 0 21 0.0
total 217 482 45.0


line stmt bran cond sub pod time code
1             package Net::Peep::Client;
2              
3             require 5.00503;
4 3     3   18 use strict;
  3         8  
  3         123  
5             # use warnings; # commented out for 5.005 compatibility
6 3     3   15 use Carp;
  3         6  
  3         344  
7 3     3   1078 use Data::Dumper;
  3         7440  
  3         211  
8 3     3   19 use Socket;
  3         7  
  3         2210  
9 3     3   1301 use Getopt::Long;
  3         12127  
  3         22  
10 3     3   2317 use File::Tail;
  3         54625  
  3         135  
11 3     3   3800 use Pod::Text;
  3         200394  
  3         265  
12 3     3   2648 use Net::Peep::BC;
  3         11  
  3         163  
13 3     3   17 use Net::Peep::Log;
  3         6  
  3         84  
14 3     3   16 use Net::Peep::Parser;
  3         6  
  3         85  
15 3     3   19 use Net::Peep::Notifier;
  3         7  
  3         121  
16              
17             require Exporter;
18              
19 3     3   15 use vars qw{ @ISA %EXPORT_TAGS @EXPORT_OK @EXPORT $VERSION };
  3         7  
  3         465  
20              
21             @ISA = qw(Exporter);
22             %EXPORT_TAGS = ( 'all' => [ qw( INTERVAL MAX_INTERVAL ADJUST_AFTER ) ] );
23             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
24             @EXPORT = qw( );
25             $VERSION = do { my @r = (q$Revision: 1.12 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
26              
27             # These are in seconds and are the parameters for File::Tail
28              
29             # File Tail uses the idea of intervals and predictions to try to keep
30             # blocking time at a maximum. These three parameters are the ones that
31             # people will want to tune for performance vs. load. The smaller the
32             # interval, the higher the load but faster events are picked up.
33              
34             # The interval that File::Tail waits before checking the log
35 3     3   16 use constant INTERVAL => 0.1;
  3         6  
  3         153  
36             # The maximum interval that File::Tail will wait before checking the
37             # log
38 3     3   14 use constant MAX_INTERVAL => 1;
  3         5  
  3         108  
39             # The time after which File::Tail adjusts its predictions
40 3     3   13 use constant ADJUST_AFTER => 2;
  3         4  
  3         9823  
41              
42             sub new {
43              
44 4     4 0 12 my $self = shift;
45 4   33     40 my $class = ref($self) || $self;
46 4         12 my $this = {};
47 4         26 bless $this, $class;
48              
49             } # end sub new
50              
51             sub name {
52              
53 118     118 0 242 my $self = shift;
54 118 100       267 if (@_) { $self->{'NAME'} = shift; }
  4         39  
55 118         470 return $self->{'NAME'};
56              
57             } # end sub name
58              
59             sub callback {
60              
61 1     1 0 25 my $self = shift;
62 1         1 my $callback = shift;
63 1 50       180 confess "Cannot register callback: Expecting a code reference. (Got [$callback].)"
64             unless ref($callback) eq 'CODE';
65 1         4 $self->{"__CALLBACK"} = $callback;
66 1         3 return 1;
67              
68             } # end sub callback
69              
70             sub getCallback {
71              
72 0     0 0 0 my $self = shift;
73 0 0       0 confess "Cannot get callback: A callback has not been set yet."
74             unless exists $self->{"__CALLBACK"};
75 0         0 return $self->{"__CALLBACK"};
76              
77             } # end sub getCallback
78              
79             sub parser {
80              
81 4     4 0 29 my $self = shift;
82 4         10 my $parser = shift;
83 4 50       21 confess "Cannot register parser: Expecting a code reference. (Got [$parser].)"
84             unless ref($parser) eq 'CODE';
85 4         11 $self->{"__PARSER"} = $parser;
86 4         13 return 1;
87              
88             } # end sub parser
89              
90             sub getParser {
91              
92 1     1 0 2 my $self = shift;
93 1 50       6 confess "Cannot get parser: A parser has not been set yet."
94             unless exists $self->{"__PARSER"};
95 1         3 return $self->{"__PARSER"};
96              
97             } # end sub getParser
98              
99             sub runParser {
100              
101 1     1 0 3 my $self = shift;
102 1         7 my @text = @_;
103              
104 1         9 my $parser = $self->getParser();
105 1         6 &$parser(@text);
106              
107             } # end sub runParser
108              
109             sub loop {
110              
111 0     0 0 0 my $self = shift;
112 0 0       0 if (@_) { $self->{"__LOOP"} = shift; }
  0         0  
113 0         0 return $self->{"__LOOP"};
114              
115             } # end sub loop
116              
117             sub peck {
118              
119 0     0 0 0 my $self = shift;
120 0         0 my $conf = $self->conf();
121              
122 0 0       0 unless (exists $self->{"__PEEP"}) {
123 0 0       0 if ($conf->getOptions()) {
124 0         0 $self->{"__PEEP"} = Net::Peep::BC->new( $self->name(), $conf );
125             } else {
126 0         0 confess "Error: Expecting options to have been parsed by now.";
127             }
128             }
129 0         0 return $self->{"__PEEP"};
130              
131             } # end sub peck
132              
133             sub initialize {
134              
135 4     4 0 18 my $self = shift;
136 4         22 my %options = @_;
137              
138 4         26 my $conf = $self->conf();
139              
140 4         29 $conf->client($self);
141              
142             my (
143 4         22 $config,
144             $logfile,
145             $debug,
146             $daemon,
147             $output,
148             $pidfile,
149             $autodiscovery,
150             $server,
151             $port,
152             $silent,
153             $help) = ('/etc/peep.conf','',0,1,'','',1,'','',0,0);
154              
155 4         60 my %standardOptions = (
156             'config=s' => \$config,
157             'logfile=s' => \$logfile,
158             'debug=s' => \$debug,
159             'daemon!' => \$daemon,
160             'output=s' => \$output,
161             'pidfile=s' => \$pidfile,
162             'autodiscovery!' => \$autodiscovery,
163             'server=s' => \$server,
164             'port=s' => \$port,
165             'silent' => \$silent,
166             'help' => \$help
167             );
168              
169 4         24 for my $option (keys %standardOptions) {
170 44 100       108 if (exists $options{$option}) {
171 3         11 delete $standardOptions{$option};
172             }
173             }
174              
175 4         45 my %allOptions = (%options,%standardOptions);
176              
177 4         42 GetOptions(%allOptions);
178              
179             # set the debug level first so we can start printing debugging
180             # messages
181 4 50       6737 $Net::Peep::Log::logfile = $output if $output;
182 4 50       16 $Net::Peep::Log::debug = $debug if $debug;
183              
184 4         10 my $found;
185              
186 4 100       8 if (-f ${$allOptions{'config=s'}}) {
  4         113  
187 3         7 $found = ${$allOptions{'config=s'}};
  3         8  
188             } else {
189 1         4 for my $dir ('.','/usr/local/etc','/usr','/usr/local','/opt') {
190 1 50       19 if (-f "$dir/peep.conf") {
191 1         3 $found = "$dir/peep.conf";
192 1         2 last;
193             }
194             }
195             }
196              
197 4 50       23 if ($found) {
198 4         36 $self->logger()->debug(1,"The Peep configuration file has been identified as [$found]");
199             } else {
200 0         0 $self->logger()->log("No peep configuration file could be found. Exiting gracefully ....");
201 0         0 exit 2;
202             }
203              
204 4         31 $conf->setOption('config',$found);
205 4 50       7 $conf->setOption('logfile',${$allOptions{'logfile=s'}}) if ${$allOptions{'logfile=s'}} ne '';
  0         0  
  4         19  
206 4         10 $conf->setOption('debug',${$allOptions{'debug=s'}});
  4         18  
207 4         6 $conf->setOption('daemon',${$allOptions{'daemon!'}});
  4         19  
208 4         10 $conf->setOption('output',${$allOptions{'output=s'}});
  4         18  
209 4 50       7 $conf->setOption('pidfile',${$allOptions{'pidfile=s'}}) if ${$allOptions{'pidfile=s'}} ne '';
  0         0  
  4         34  
210 4         8 $conf->setOption('autodiscovery',${$allOptions{'autodiscovery!'}});
  4         24  
211 4 100       7 $conf->setOption('server',${$allOptions{'server=s'}}) if ${$allOptions{'server=s'}} ne '';
  3         25  
  4         36  
212 4 100       9 $conf->setOption('port',${$allOptions{'port=s'}}) if ${$allOptions{'port=s'}} ne '';
  3         13  
  4         22  
213 4         9 $conf->setOption('silent',${$allOptions{'silent'}});
  4         19  
214 4         9 $conf->setOption('help',${$allOptions{'help'}});
  4         17  
215              
216 4 50       41 return $help ? 0 : 1;
217              
218             } # end sub initialize
219              
220             sub MainLoop {
221              
222 0     0 0 0 my $self = shift;
223 0         0 my $sleep = shift;
224              
225 0   0     0 my $client = $self->name() || confess "Cannot begin main loop: Client name not specified.";
226 0   0     0 my $conf = $self->conf() || confess "Cannot begin main loop: Configuration object not found.";
227              
228 0         0 my $fork = $conf->getOption('daemon');
229              
230 0 0       0 if ($fork) {
231              
232 0         0 $self->logger()->debug(7,"Running in daemon mode. Forking ...");
233              
234 0 0       0 if (fork()) {
235             # If we're here, then we're the parent
236 0         0 close (STDIN);
237 0         0 close (STDOUT);
238 0         0 close (STDERR);
239 0         0 exit(0);
240             }
241              
242 0         0 $self->logger()->debug(7,"\tForked.");
243              
244             # Else we're the child. Let's write out our pid
245 0         0 my $pid = 0;
246 0 0       0 if ($conf->optionExists('pidfile')) {
247 0   0     0 my $pidfile = $conf->getOption('pidfile') || confess "Cannot fork: Pidfile not found.";
248 0 0       0 if (open PIDFILE, ">$pidfile") {
249 0         0 select (PIDFILE); $| = 1; # autoflush
  0         0  
250 0         0 select (STDERR);
251 0         0 print PIDFILE "$$\n";
252 0         0 close PIDFILE;
253 0         0 $pid = 1;
254             } else {
255 0         0 $self->logger()->log("Warning: Couldn't open pid file: No pidfile option.");
256 0         0 $self->logger()->log("\tContinuing anyway ...");
257             }
258             } else {
259 0         0 $self->logger()->log("Warning: Couldn't open pid file: Pidfile option not specified.");
260 0         0 $self->logger()->log("\tContinuing anyway ...");
261             }
262              
263             }
264              
265 0         0 my $sub = $self->getCallback();
266 0 0       0 if ($sleep) {
267 0         0 while (1) {
268 0         0 $self->logger()->debug(9,"Executing callback from within infinite loop ...");
269 0         0 &$sub();
270 0         0 $self->logger()->debug(9,"\tSleeping [$sleep] seconds ...");
271 0         0 sleep($sleep);
272             }
273             } else {
274 0         0 $self->logger()->debug(9,"Executing callback ...");
275 0         0 &$sub();
276             }
277              
278             } # end sub MainLoop
279              
280             sub configure {
281              
282 4     4 0 13 my $self = shift;
283 4   33     16 my $conf = $self->conf()
284             || confess "Cannot parse configuration object: Configuration object not found";
285 4         34 Net::Peep::Parser->new()->load($conf);
286              
287 4 50       80 my @version = $self->conf()->versionExists()
288             ? split /\./, $self->conf()->getVersion()
289             : ();
290              
291 4         24 my $config = $conf->getOption('config');
292 4 50 33     77 unless (@version && $version[0] >= 0 && $version[1] >= 4 && $version[2] > 3) {
      33        
      33        
293              
294 0         0 print STDERR <<"eop";
295              
296             [$0] Warning: The configuration file
297              
298             $config
299              
300             appears to use an old configuration file syntax. You may want to
301             update your configuration to be consistent with the 0.4.4 release.
302             The older syntax may not be supported as of the 0.5.0 release.
303              
304             eop
305              
306             ;
307              
308             }
309              
310 4         20 return $conf;
311              
312             } # end sub configure
313              
314             sub conf {
315              
316 20     20 0 38 my $self = shift;
317 20 100       90 $self->{"__CONF"} = Net::Peep::Conf->new() unless exists $self->{"__CONF"};
318 20         97 return $self->{"__CONF"};
319              
320             } # end sub conf
321              
322             sub pods {
323              
324 0     0 0 0 my $self = shift;
325 0         0 my $message = shift;
326              
327 0 0       0 print "\n$message\n\n" if $message;
328              
329 0 0       0 open(POD,$0) || die "Cannot print help text for $0: $!";
330 0         0 Pod::Text->new()->parse_from_filehandle(\*POD);
331 0         0 close POD;
332              
333 0         0 exit 0;
334              
335             } # end sub pods
336              
337             # returns a logging object
338             sub logger {
339              
340 15     15 0 27 my $self = shift;
341 15 100       51 if ( ! exists $self->{'__LOGGER'} ) { $self->{'__LOGGER'} = new Net::Peep::Log }
  4         34  
342 15         90 return $self->{'__LOGGER'};
343              
344             } # end sub logger
345              
346             # Remove our pidfile with garbage collection (if it exists) The client
347             # needs to call this function explicitly upon receipt of a signal with
348             # the appropriate reference.
349             sub shutdown {
350 0     0 0 0 my $self = shift;
351 0         0 print STDERR "Shutting down ...\n";
352 0         0 my $notifier = new Net::Peep::Notifier;
353 0         0 print STDERR "\tFlushing notification buffers ...\n";
354 0         0 my $n = $notifier->force();
355 0 0       0 my $string = $n ?
356             "\t$n notifications were flushed from the buffers.\n" :
357             "\tNo notifications were flushed from the buffers: The buffers were empty.\n";
358 0         0 print STDERR $string;
359 0         0 my $conf = $self->conf();
360 0 0 0     0 my $pidfile = defined $conf && $conf->optionExists('pidfile') && -f $conf->getOption('pidfile')
361             ? $conf->getOption('pidfile') : '';
362 0 0       0 if ($pidfile) {
363 0         0 print STDERR "\tUnlinking pidfile ", $pidfile, " ...\n";
364 0         0 unlink $pidfile;
365             }
366 0         0 print STDERR "Done.\n";
367             }
368              
369             sub getConfigSection {
370              
371 2     2 0 2 my $self = shift;
372 2         5 my $section = shift;
373 2         11 my @text = @_;
374              
375 2         2 my @return;
376 2         4 my $read = 0;
377 2         11 for my $line (@text) {
378 64 100       321 if ($line =~ /^\s*$section/) {
    100          
    100          
379 1         3 $read = 1;
380             } elsif ($line =~ /^\s*end events/) {
381 2         7 $read = 0;
382             } elsif ($read) {
383 12 100 66     68 next if $line =~ /^\s*#/ || $line =~ /^\s*$/;
384 10         21 push @return, $line;
385             } else {
386             # do nothing
387             }
388             }
389 2 50       21 return wantarray ? @return : \@return;
390              
391             } # end sub getConfigSection
392              
393             sub tempParseDefaults {
394              
395 1     1 0 3 my $self = shift;
396 1         7 my @text = @_;
397              
398 1         4 my $conf = $self->conf();
399              
400 1         12 for my $line ($self->getConfigSection('default',@text)) {
401              
402 0 0         if ($line =~ /^\s*([\w\-]+)\s+(\S+)\s*$/) {
403 0           my ($option,$value) = ($1,$2);
404 0 0         if ($conf->optionExists($option)) {
405 0           $self->logger()->debug(6,"\t\tNot setting option [$option]: It has already been set (possibly from the command-line).");
406             } else {
407 0           $self->logger()->debug(6,"\t\tSetting option [$option] to value [$value].");
408 0 0         $conf->setOption($option,$value) unless $conf->optionExists($option);
409             }
410             }
411              
412             }
413              
414             } # sub tempParseDefaults
415              
416             sub getGroups {
417              
418 0     0 0   my $self = shift;
419 0           my $conf = $self->conf();
420 0 0         unless (exists $self->{'GROUPS'}) {
421 0 0         my $groups = $conf->optionExists('groups') ? $conf->getOption('groups') : '';
422 0           my @groups = split /,/, $groups;
423 0           $self->{'GROUPS'} = \@groups;
424             }
425 0 0         return wantarray ? @{$self->{'GROUPS'}} : $self->{'GROUPS'};
  0            
426            
427             } # end sub getGroups
428              
429             sub getExcluded {
430              
431 0     0 0   my $self = shift;
432 0           my $conf = $self->conf();
433 0 0         unless (exists $self->{'EXCLUDED'}) {
434 0 0         my $excluded = $conf->optionExists('excluded') ? $conf->getOption('excluded') : '';
435 0           my @excluded = split /,/, $excluded;
436 0           $self->{'EXCLUDED'} = \@excluded;
437             }
438 0 0         return wantarray ? @{$self->{'EXCLUDED'}} : $self->{'EXCLUDED'};
  0            
439            
440             } # end sub getExcluded
441              
442             sub filter {
443              
444 0     0 0   my $self = shift;
445 0   0       my $object = shift || confess "Object not found";
446 0           my $nogrp = shift;
447              
448 0           my $conf = $self->conf();
449            
450 0           my $return = 0;
451            
452 0           my $name = $object->name();
453              
454 0 0         unless ($nogrp) {
455              
456 0           $self->logger()->debug(9,"Checking group [$name] against group and excluded group lists ...");
457              
458 0           my $group = $object->group();
459              
460 0           my @groups = ();
461 0           my @exclude = ();
462              
463 0           @groups = $self->getGroups('groups');
464 0           @exclude = $self->getExcluded('exclude');
465            
466 0 0         if (grep /^all$/, @groups) {
467 0           $return = 1;
468             } else {
469 0           for my $group_option (@groups) {
470 0 0         $return = 1 if $group eq $group_option;
471             }
472             }
473            
474 0           for my $exclude_option (@exclude) {
475 0 0         $return = 0 if $group eq $exclude_option;
476             }
477            
478 0 0         $self->logger()->debug(8,"[$name] will be ignored: The group [$group] is either not in the group ".
479             "list [@groups] or it is in the excluded list [@exclude].") if $return == 0;
480             }
481              
482 0           my $hosts = $object->hosts();
483 0 0         my @version = $conf->versionExists() ? split /\./, $conf->getVersion() : ();
484            
485 0 0 0       if (@version && $version[0] >= 0 && $version[1] >= 4 && $version[2] > 3) {
      0        
      0        
486 0 0         if ($object->pool()->isInHostPool($Net::Peep::Notifier::HOSTNAME)) {
487 0           $return = 1;
488             } else {
489 0           $return = 0;
490 0           $self->logger()->debug(8,"[$name] will be ignored: Host [$Net::Peep::Notifier::HOSTNAME] ".
491             "is not in the host pool [$hosts].");
492             }
493             }
494            
495 0           return $return;
496            
497             } # end sub filter
498              
499             1;
500              
501             __END__