File Coverage

lib/Test/MonitorSites.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Test::MonitorSites;
2              
3 6     6   1435223 use warnings;
  6         17  
  6         980  
4 6     6   222 use strict;
  6         14  
  6         182  
5 6     6   35 use Carp;
  6         23  
  6         475  
6 6     6   33 use Cwd;
  6         13  
  6         548  
7 6     6   6796 use Config::Simple;
  6         104986  
  6         92  
8 6     6   3238 use WWW::Mechanize;
  6         275460  
  6         166  
9 6     6   9499 use Test::WWW::Mechanize;
  0            
  0            
10             use Test::HTML::Tidy;
11             use HTTP::Request::Common;
12             use Test::More;
13             use Data::Dumper;
14             use Test::Builder;
15             use Mail::Mailer;
16             # use Mail::Mailer qw(mail);
17              
18             use vars qw($VERSION);
19             $VERSION = '0.15';
20              
21             1; # Magic true value required at end of module
22              
23             sub new {
24             my $class = shift;
25             my $args = shift;
26             my $self = {};
27             my ($cfg,%sites,@sites,$site);
28              
29             if (defined($args->{'config_file'})){
30             my $config_file = $args->{'config_file'};
31             if(-s $config_file){
32             $cfg = new Config::Simple($config_file);
33             if(!defined($cfg->{'_DATA'})){
34             $self->{'config_file'} = undef;
35             $self->{'error'} .= 'No configuration data is available.';
36             } else {
37             foreach my $key (keys %{$cfg->{'_DATA'}}){
38             if($key =~ m/^site_/){
39             $site = $key;
40             $site =~ s/^site_//;
41             push @sites, $site;
42             }
43             }
44             # print STDERR @sites, "\n";
45             $self->{'sites'} = \@sites;
46             foreach my $s (@sites){
47             # if($s =~ m/not ok/) { print "Found Error; \$s is $s\n"; exit; }
48             if(!defined($cfg->{'_DATA'}{"site_$s"}{'ip'})){
49             $cfg->{'_DATA'}{"site_$s"}{'ip'} = [ '0.0.0.0' ];
50             }
51             }
52             my $cwd = getcwd();
53             # {
54             # no strict 'refs';
55             # $cwd = `pwd`;
56             # }
57             # print STDERR "The current working directory is: $cwd.\n";
58             if(defined($cfg->param('global.result_log'))){
59             if($cfg->param('global.result_log') !~ m/^\//){
60             $self->{'result_log'} = $cwd . '/' . $cfg->param('global.result_log');
61             } else {
62             $self->{'result_log'} = $cfg->param('global.result_log');
63             }
64             } else {
65             $self->{'result_log'} = "$cwd/Test_MonitorSites_result.log";
66             }
67             if(!defined($cfg->param('global.MonitorSites_email'))){
68             $self->{'error'} .= "Configuration fails to define global.MonitorSites_email for From: line.\n";
69             $cfg->param('global.MonitorSites_email','MonitorSites@example.net');
70             } else {
71             1;
72             }
73             foreach my $i ('ips', 'critical_errors', 'servers_with_failures', 'tests', 'sites') {
74             $self->{'result'}->{$i} = 0;
75             }
76             if(!defined($cfg->param('global.report_success'))
77             || $cfg->param('global.report_success') != 1){
78             $cfg->param('global.report_success',0);
79             }
80             if(!defined($cfg->param('global.MonitorSites_subject'))) {
81             $cfg->param('global.MonitorSites_subject','Critical Failures');
82             }
83             if(!defined($cfg->param('global.MonitorSites_all_ok_subject'))) {
84             $cfg->param('global.MonitorSites_all_ok_subject','Servers All OK');
85             }
86             }
87             } else {
88             $self->{'config_file'} = undef;
89             $self->{'error'} .= 'The config_file was not found, or was empty.';
90             }
91             } else {
92             $self->{'config_file'} = undef;
93             $self->{'error'} .= 'The config_file was not set in the constructor.';
94             }
95             $self->{'config'} = $cfg;
96             my $agent = WWW::Mechanize->new();
97             my $mech = Test::WWW::Mechanize->new();
98             $self->{'agent'} = $agent;
99             $self->{'mech'} = $mech;
100              
101             bless $self, $class;
102             return $self;
103             }
104              
105             sub test_sites {
106             my $self = shift;
107             my $sites = shift;
108             my(%sites);
109             if(defined($sites)){
110             %sites = %{$sites};
111             } elsif(defined($self->{'config'}->{'_DATA'})) {
112             %sites = %{$self->{'config'}->{'_DATA'}};
113             foreach my $key (keys %sites){
114             if($key !~ m/^site_/){
115             delete $sites{$key};
116             }
117             }
118             } else {
119             $self->{'error'} .= 'No sites have been identified for testing. Please add sites to: ' . $self->{'config_file'};
120             }
121              
122             my ($key, $url, $expected_content,$expected,$ip);
123             my(@url,@expected,@sites,@test_links,@test_valid_html);
124             my $agent = $self->{'agent'};
125             my $mech = $self->{'mech'};
126             # print STDERR Dumper(\%sites);
127             my $log_file = $self->{'result_log'};
128             my $log_file_ok = $log_file . '_ok';
129             my $log_file_diag = $log_file . '_diag';
130             my $log_file_todo = $log_file . '_todo';
131              
132             my $Test = Test::Builder->new;
133             my @handle_names = qw/ output failure_output todo_output /;
134             my %old;
135             $old{$_} = $Test->$_ for @handle_names;
136             $Test->$_(\*STDOUT) for @handle_names;
137              
138             {
139             $Test->output($log_file_ok);
140             $Test->failure_output($log_file_diag);
141             $Test->todo_output($log_file_todo);
142              
143             # print STDERR Dumper(\%sites);
144             foreach my $site (keys %sites){
145             if($site !~ m/^site_/){ next; }
146             # diag("The site is $site");
147             $site =~ s/^site_//;
148             # if($site =~ m/not ok/) { print "Found Error; \$site is $site\n"; exit; }
149             # diag("The site is $site");
150             push @sites, $site;
151             $self->{'result'}->{'sites'}++;
152             $ip = @{$self->{'config'}->{'_DATA'}->{"site_$site"}->{'ip'}}[0];
153             if (defined($ip) && !defined($self->{'result'}->{'ips_tested'}->{$ip})){
154             $self->{'result'}->{'ips_tested'}->{$ip} = 1;
155             $self->{'result'}->{'ips'}++;
156             }
157             # diag("The site is $site");
158             # diag("The hash key is site_$site");
159             $url = $self->{'config'}->{'_DATA'}->{"site_$site"}->{'url'};
160             $expected = $self->{'config'}->{'_DATA'}->{"site_$site"}->{'expected_content'};
161             # diag("The url is $url.");
162             @url = @{$url};
163             @expected = @{$expected};
164             # $self->_test_tests();
165             $self->_test_site($agent,$url[0],$expected[0]);
166             $self->{'result'}->{'tests'} = $self->{'result'}->{'tests'} + 2;
167             if(defined($sites{"site_$site"}{'test_links'})){
168             @test_links = @{$sites{"site_$site"}{'test_links'}};
169             if ($test_links[0] == 1) {
170             $self->_test_links($mech,$url[0]);
171             $self->{'result'}->{'tests'} = $self->{'result'}->{'tests'} + 1;
172             } else {
173             diag("Skipping tests of links at: $site.");
174             }
175             }
176             if(defined($sites{"site_$site"}{'test_valid_html'})){
177             @test_valid_html = @{$sites{"site_$site"}{'test_valid_html'}};
178             if ($test_valid_html[0] == 1) {
179             $self->_test_valid_html($mech,$url[0]);
180             $self->{'result'}->{'tests'} = $self->{'result'}->{'tests'} + 1;
181             } else {
182             diag("Skipping tests of html validity at: $site.");
183             }
184             }
185            
186             }
187             }
188             $Test->todo_output(*STDOUT);
189             $Test->failure_output(*STDERR);
190             $Test->output(*STDOUT);
191              
192             my $critical_failures = $self->_analyze_test_logs();
193             # print STDERR "Count of critical failures: $critical_failures->{'count'}\n";
194             # print "global.report_success is " . $self->{'config'}->param('global.report_success');
195             if($critical_failures->{'count'} != 0){
196             # print STDERR "Next we send an sms message.\n";
197             $self->sms($critical_failures);
198             } elsif ($self->{'config'}->param('global.report_success') == 1) {
199             $self->sms($critical_failures);
200             } else {
201             $self->{'error'} .= "We won't send an sms, there were no critical_failures and global.report_success is not set true.\n";
202             }
203              
204             if(defined($self->{'config'}->param('global.results_recipients'))){
205             if($self->{'config'}->param('global.send_summary')
206             || $self->{'config'}->param('global.send_diagnostics')){
207             # print STDERR "Next we send some email.\n";
208             $self->email($critical_failures);
209             } else {
210             $self->{'error'} .= "We won't send an email, neither send_summary nor send_diagnostics were set to true in the configuration file.\n";
211             }
212             } else {
213             $self->{'error'} .= "We won't send an email, there was no results_recipient defined in the configuration file.\n";
214             }
215              
216             my %result = (
217             'ips' => $self->{'result'}->{'ips'},
218             'sites' => $self->{'sites'},
219             'planned' => '',
220             'run' => $self->{'result'}->{'tests'},
221             'passed' => '',
222             'failed' => $critical_failures->{'count'},
223             'critical_failures' => $critical_failures,
224             );
225              
226             # print Dumper($critical_failures);
227             return \%result;
228             }
229              
230             sub _analyze_test_logs {
231             my $self = shift;
232             my $critical_failures = 0;
233             my %critical_failures;
234             $critical_failures{'count'} = 0;
235             foreach my $test ('linked_to','expected_content','all_links','valid'){
236             # print STDERR "This \$test is $test.\n";
237             if($self->{'config'}->param("critical_failure.$test") == 1){
238             $critical_failures{"$test"} = 1;
239             }
240             }
241             my ($url,$test,$test_string,$ip,$param_name,@ip);
242             open('SUMMARY','<',$self->{'config'}->param('global.result_log') . '_ok');
243             while(){
244             if(m/^not ok/){
245             $url = $_;
246             chomp($url);
247             $url =~ s/^.*https?:\/\///;
248             $url =~ s/\/.*$//;
249             $url =~ s/\.$//;
250             if($url =~ m/not ok/) { print "Found parsing error; \$url is $url\n"; exit; }
251             $param_name = 'site_' . $url;
252             if(defined(@{$self->{'config'}->{'_DATA'}->{"$param_name"}->{'ip'}}[0])){
253             @ip = @{$self->{'config'}->{'_DATA'}->{"$param_name"}->{'ip'}};
254             # @ip = @{$ip} if(ref($ip));
255             } else {
256             $self->{'error'} .= "IP address not defined for $url.\n";
257             print 'ip array is: ' . Dumper(\$self->{'config'}->{'_DATA'}->{"$param_name"}->{'ip'});
258             print 'ip: ' . @{$self->{'config'}->{'_DATA'}->{"$param_name"}->{'ip'}}[0] . "\n";
259             # print Dumper(\$self->{'config'}->{'_DATA'});
260             # print Dumper(\$self->{'config'}->{'_DATA'}->{"$param_name"});
261             # print Dumper(\$self->{'error'});
262             print "Site key is: $param_name.\n";
263             print "Now exiting.\n";
264             exit;
265             }
266             if(!defined($critical_failures{'failed_tests'}{'ip'}{"$ip[0]"}{'count'})){
267             $critical_failures{'failed_tests'}{'ip'}{"$ip[0]"}{'count'} = 0;
268             }
269             foreach my $test (keys %critical_failures){
270             if($test eq 'failed_tests'){ next; }
271             $test_string = $test;
272             $test_string =~ s/_/ /g;
273             if($_ =~ m/$test_string/){
274             $critical_failures++;
275             $critical_failures{'failed_tests'}{'ip'}{"$ip[0]"}{'count'}++ if(defined($ip[0]));
276             $critical_failures{'failed_tests'}{'ip'}{"$ip[0]"}{"$url"} = $_ if(defined($ip[0]));
277             $critical_failures{'failed_tests'}{'url'}{"$url"}{"$test"} = $_;
278             $critical_failures{'failed_tests'}{'test'}{"$test"}{"$url"} = $_;
279             }
280             }
281             }
282             }
283             close('SUMMARY');
284             $critical_failures{'count'} = $critical_failures;
285             if($critical_failures{'count'} == 0){
286             # print STDERR "The count of critical failures is 0.\n";
287             }
288              
289             return \%critical_failures;
290             }
291              
292             sub _return_result_log {
293             my $self = shift;
294             return $self->{'result_log'};
295             }
296              
297             sub email {
298             my $self = shift;
299             my $critical_failures = shift;
300             my ($type,@args,$body);
301              
302             my %headers = (
303             'To' => $self->{'config'}->param('global.results_recipients'),
304             'From' => $self->{'config'}->param('global.MonitorSites_email'),
305             'Subject' => 'MonitorSites log',
306             );
307              
308             $body = "
309             This summary brought to you by
310             Test::MonitorSites version $VERSION
311             ===================================\n";
312              
313             $body .= ' Tests: ' . $self->{'result'}->{'tests'};
314             $body .= ', IPs: ' . $self->{'result'}->{'ips'};
315             $body .= ', Sites: ' . $self->{'result'}->{'sites'};
316             $body .= ', CFs: ' . $critical_failures->{'count'};
317             # $body .= ', CFs: ' . $self->{'result'}->{'critical_failures'}->{'count'};
318             $body .= "\n ===================================\n\n";
319            
320             # $self->{'result'}->{'message'} = $body;
321             # print Dumper(\$self->{'result'});
322              
323             my $file = $self->{'config'}->param('global.result_log');
324             if($self->{'config'}->param('global.send_summary') == 1){
325             open('RESULT','<',$file . '_ok');
326             while(){
327             $body .= $_;
328             }
329             close('RESULT');
330             } else {
331             $self->{'error'} .= "Configuration file disabled email dispatch of results log.\n";
332             }
333              
334             if(defined($self->{'config'}->param('global.send_diagnostics'))
335             && $self->{'config'}->param('global.send_diagnostics') == 1){
336             $body .= <<'End_of_Separator';
337              
338             ==============================================
339             End of Summary, Beginning of Diagnostics
340             ==============================================
341              
342             End_of_Separator
343              
344             open('RESULT','<',$file . '_diag');
345             while(){
346             $body .= $_;
347             }
348             close('RESULT');
349             } else {
350             $self->{'error'} .= "Configuration file disabled email dispatch of diagnostic log.\n";
351             }
352              
353             # is(1,1,'About to send email now.');
354             $type = 'sendmail';
355             my $mailer = new Mail::Mailer $type, @args;
356             $mailer->open(\%headers);
357             print $mailer $body;
358             $mailer->close;
359             return 1;
360             }
361              
362             sub sms {
363             my $self = shift;
364             my $critical_failures = shift;
365             my %critical_failures = %{$critical_failures};
366             my %headers = (
367             'To' => $self->{'config'}->param('global.sms_recipients'),
368             'From' => $self->{'config'}->param('global.MonitorSites_email'),
369             'Subject' => $self->{'config'}->param('global.MonitorSites_subject'),
370             );
371              
372             ### diag('report_by_ip is: ' . $self->{'config'}->param("global.report_by_ip"));
373             my ($mailer,$type,@args,$body,$test,$url,$ip);
374             my($failing_domains,$failing_domains_at_ip);
375             if($critical_failures->{'count'} == 0){
376             $headers{'Subject'} = $self->{'config'}->param('global.MonitorSites_all_ok_subject');
377              
378             $body = '';
379             $body .= 'Tests: ' . $self->{'result'}->{'tests'};
380             $body .= ', IPs: ' . $self->{'result'}->{'ips'};
381             $body .= ', Sites: ' . $self->{'result'}->{'sites'};
382             $body .= ', CFs: ' . $self->{'result'}->{'critical_errors'};
383             $body .= '; No critical errors found.';
384             $self->{'result'}->{'message'} = $body;
385              
386             $mailer = new Mail::Mailer $type, @args;
387             $mailer->open(\%headers);
388             print $mailer $body;
389             $mailer->close;
390             $self->_log_sms($body);
391              
392             } elsif(defined($self->{'config'}->param("global.report_by_ip"))
393             && $self->{'config'}->param("global.report_by_ip") == 1) {
394             foreach my $ip (keys %{$critical_failures{'failed_tests'}{'ip'}}){
395             if($critical_failures{'failed_tests'}{'ip'}{$ip}{'count'} != 0
396             || $self->{'config'}->param('global.report_success') == 1) {
397             $failing_domains = 0;
398             $failing_domains_at_ip = "";
399             foreach my $url (keys %{$critical_failures{'failed_tests'}{'ip'}{"$ip"}}){
400             if($url eq 'count'){ next; }
401             $failing_domains++;
402             $failing_domains_at_ip .= "NOK: $url, ";
403             }
404             $body = "$critical_failures{'failed_tests'}{'ip'}{$ip}{'count'}";
405             $body .= " critical errors at $ip; incl $failing_domains domains: ";
406             $body .= $failing_domains_at_ip;
407             # is(1,1,'About to send sms now about $url.');
408             $mailer = new Mail::Mailer $type, @args;
409             $mailer->open(\%headers);
410             print $mailer $body;
411             $mailer->close;
412             $self->_log_sms($body);
413             }
414             }
415             } else {
416             my $i = 0;
417             foreach my $url (keys %{$critical_failures{'failed_tests'}{'url'}}){
418             $i++;
419             $body = "Failure: $i of $critical_failures{'count'}: $url: ";
420             foreach my $test (keys %{$critical_failures{'failed_tests'}{'url'}{"$url"}}){
421             $body .= "Not OK: $test, ";
422             }
423             # is(1,1,'About to send sms now about $url.');
424             $mailer = new Mail::Mailer $type, @args;
425             $mailer->open(\%headers);
426             print $mailer $body;
427             $mailer->close;
428             $self->_log_sms($body);
429             }
430             }
431              
432             return 1;
433             }
434              
435             sub _log_sms {
436             my $self = shift;
437             my $body = shift;
438              
439             my $header = "To: " . $self->{'config'}->param('global.sms_recipients') . "\n";
440             $header .= "From: " . $self->{'config'}->param('global.MonitorSites_email') . "\n";
441             $header .= "Subject:Critical Failures\n";
442             my $log_msg = $header . $body;
443              
444             my @sms_log;
445             if(defined($self->{'sms_log'})){
446             @sms_log = @{$self->{'sms_log'}};
447             } else {
448             @sms_log = ();
449             }
450              
451             push @sms_log, $log_msg;
452             $self->{'sms_log'} = \@sms_log;
453             my $count = @sms_log;
454              
455             return $count;
456             }
457              
458             sub _test_tests {
459             is(12,12,'Twelve is twelve.');
460             is(12,13,'Twelve is thirteen.');
461             diag("Diagnostic output from subroutine called while redirecting output.");
462             return;
463             }
464              
465             sub _test_links {
466             my ($self,$mech,$url) = @_;
467             $mech->get_ok($url, " . . . linked to $url");
468             $mech->page_links_ok( " . . . successfully checked all links for $url" );
469             return;
470             }
471              
472             sub _test_valid_html {
473             my ($self,$mech,$url) = @_;
474             $mech->get_ok($url, " . . . linked to $url");
475             html_tidy_ok( $mech->content(), " . . . html content is valid for $url" );
476             return;
477             }
478              
479             sub _test_site {
480             my($self,$agent,$url,$expected_content) = @_;
481             $agent->get("$url");
482             is ($agent->success,1,"Successfully linked to $url.");
483             like($agent->content,qr/$expected_content/," . . . and found expected content at $url");
484             return $agent->success();
485             }
486              
487             __END__