| NAVIGATE: Home · Resume · Photography · Brew Blog · About Us · Contact Us | |
| SiteChecker Script (Perl Sample) |
|
When you administer Windows servers, you get used to the fact that you're going to have server crashes. I was getting a bit tired of walking into work in the mornings to be greeted by "Server down messages" from our morning server crew. Since we don't have 24 hour monitoring, our servers can be down for an hour or more by the time someone finds it. No one seemed too eager to do anything about that, so I whipped up this script so that we at least could be alerted earlier and send someone in to reboot. The one limitation is that it doesn't check SSL connections. I'm still looking for an SSL Perl module that will work with ActiveState's Perl (we run a Windows shop, which is *really* frustrating when you care about things like flexibility and uptime). The code checks the return code from the servers and then makes sure it starts and ends with tags. It runs from my home machine once every five minutes and shoots an email to my cellphone pager if something goes wrong. Emails in the code have been changed to protect the innocent. |
#!e:/perl/bin/perl.exe -w
use Net::SMTP;
require LWP::UserAgent;
###########################################################
# SiteCheck.pl
# By Rob Zazueta
#
# Based on an idea posed in an article found at O'Reilly Net.
# (http://www.onlamp.com/pub/a/onlamp/2003/04/03/linuxhacks.html)
# Grabs a web page from a server and checks to make sure it
# has both an opening and closing tag. If not, it assumes
# the server has gone down (but it checks a couple of times before
# raising any alerts).
#
# Best used in a cron or Windows Scheduler, run at regular intervals.
###########################################################
###########################################################
# Configuration Settings
###########################################################
$tolerance = 3; # Set this to the number of failures req'd before raising an alert.
$tests = 5; # Set to the number of times to try the sites
$urlListFile = 'c:\perl\code\sitecheck.conf'; # Location of the file containing the list of URLS to check.
$logFile = 'c:\perl\code\sitecheck.log'; # Location of the log file.
$debug = 0; #Set to 1 for debug mode.
###########################################################
# Variable Declaration
###########################################################
my %checkUrls; #Where the urls loaded from the conf file go.
#The HTTP return codes that we consider OK.
my %acceptedCodes = ( 200 => 1 );
# raiseAlert( $url, $code )
#
# Gets called when the site being monitored doesn't respond accordingly.
sub raiseAlert {
#TODO: Implement this or you'll get no warning when things go awry
my $url = $_[0];
#my $code = $_[1];
#print "\nERROR! $url isn't working!";
$errMesg = "Received an error while testing $url.";
logError($errMesg);
$smtp = Net::SMTP->new('mail.robzazueta.com', Hello=>'mail.robzazueta.com');
if(! defined($smtp)) {
logError("Unable to connect to SMTP server.");
} else {
$smtp->mail('server@server.com');
$smtp->to('alert@alert.com');
$smtp->data();
$smtp->datasend('alert@alert.com'."\n");
$smtp->datasend("Subject: $url appears broken.\n\n");
$smtp->datasend($errMesg);
$smtp->dataend();
$smtp->quit();
$smtp = '';
}
}
# debug( $message )
#
# Easy little debug function. Set the Debug variable to 1 to activate it.
sub debug {
my $mesg = $_[0];
if($debug == 1) {
print ("DEBUG: ".$mesg."\n");
}
}
# logError ($message)
#
# Simple logging mechanism.
sub logError {
my $mesg = $_[0];
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year = $year + 1900; #Perl's lame y2k fix.
my $timestamp = $mon.'/'.$mday.'/'.$year.' '.$hour.':'.$min.':'.$sec;
open (LOGFILE, ">>$logFile") || die "Unable to open logfile $logFile";
print(LOGFILE $timestamp.' - '.$mesg."\n");
close(LOGFILE);
}
# loadUrls()
#
# Loads the URLs from the urlsListFile into an associative array
sub loadUrls {
debug("Opening $urlListFile...");
open(URLFILE, "<$urlListFile") || die "Could not open $urlListFile: $!\n";
while ($line = ) {
#Hashes represent comments in the url file.
debug("Line Before: ".$line);
chomp($line);
$line =~ s/[#].*//g;
debug("Line after: ".$line );
if( $line ) {
debug("Adding $line to list.");
$checkUrls{$line} = 0;
}
}
close(URLFILE);
if( scalar(keys(%checkUrls)) < 1) {
die "No URLs found in the config file $urlListFile";
}
}
# monitor()
#
# Goes through the monitoring loop.
sub monitor {
for($n=0; $n < $tests; $n++) {
foreach $item (keys(%checkUrls)) {
if(!checkSite($item)) {
$checkUrls{$item}++;
}
}
sleep(1);
}
}
# checkSite( $url )
#
# Checks the URL to see if it's working as expected.
#
# Since this is geared toward websites, it first checks to see if the site is even accessible.
# Next, it grabs the page located at the site it's checking. If the page returns a valid
# HTML file (starts and ends with tags) then it returns true.
sub checkSite {
$url = $_[0];
debug("Checking $url...");
my $ua = LWP::UserAgent->new( agent => 'RobZs-SiteChecker/1.0',
timeout => 30,
);
$request = HTTP::Request->new('GET', $url);
$response = $ua->send_request($request);
if (! exists($acceptedCodes{$response->code()})) {
debug ("Site appears broken. Error ".$response->code());
#reset the variables
$request = '';
$response = '';
return 0;
}
debug("Returned OK on $url");
$page = $response->content();
#Reset the variables
$request = '';
$response = '';
if (($page =~ //) && ($page =~ /<\/html>/)) {
debug("HTML OK on $url");
return 1;
}
debug("Bad HTML on $url");
return 0;
}
# checkStatus()
#
# Goes through the list of URLs and checks whether there are errors
sub checkStatus {
foreach $item (keys(%checkUrls)) {
if ($checkUrls{$item} >= $tolerance) {
raiseAlert($item);
}
}
}
#Main Block
{
loadUrls();
monitor();
checkStatus();
}
| Home · Photography · Brew Blog · Resume · Contact me |
![]() |
![]() Unless otherwise noted, all content on this site is Copyright © 2004 by Rob Zazueta and licensed under a Creative Commons License. |