2009/10/29

Perl script to monitor ports services monitor.pl

CURRENT CONFIGURATION: Windows 2003 Server x64, Perl

OBJECTIVE: Monitor ports and services inside data centre,  send notification by email, keep statistics in log files

SOLUTION: I wrote monitor.pl scripts to check are services, network and Internet connections alive. The Perl script based on Jonathan Eisenzopf moniker.pl script. Configuration file is in csv format.

Monitor.pl script:
#!/usr/bin/perl
#
# Changed by Vadims Zenins http://vadimszenins.blogspot.com
# Date 28/03/2007 18:02
# Version 1.00
#
# This script based on idea
# moniker.pl - monitors named server ports
# by Jonathan Eisenzopf. v0.2 20000107
#
# Description: Script to monitor ports (services), send notification by email,
# keep statistics in log files
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#

# INCLUDES
use strict;
use Net::Telnet;
use Getopt::Std;
use Text::CSV_PP;
use Mail::Sender;
use Sys::HostIP;
use Sys::Hostname;
use Cwd;
use Archive::Zip;
use File::Find;
use File::Basename;
require "ctime.pl";

# CONSTANTS
my $timeout = 4;
my $emailsender = 'monitor@mycompany.com';
my $report_email = 'sysadmin@mycompany.com';
# Write Yours report folder name
my $report_folder = "reports";
# Write Your Reports type
my $report_file = "ServicesFaultReport";
# Write Your Reports file extension
my $report_file_ext = "csv";
# Write Your log name
my $log_folder = "logs";
my $log_temp = '_temp.txt';
# Write SMTP server addresse
my $serversmtp = 'smtp.mycompany.com';
# Write email subjects
my $email_alarm_subj = "Services Monitor Alarm";
my $email_warning_subj = "Services Monitor Warning";
my $email_report_subj = "Services Monitor Report";
# Write fault quantity for warning email
my $email_warning_quantity = 1;
# Write fault quantity for alert email (for data centre)
my $email_alarm_quantity = 3;


#******************** END of config ********************
my ($host, $service, $_ERR, $sock, $send_to_warning, $send_to_alarm);
my ($quantity_current, $conf_alarm_quantity, $conf_warning_quantity);
my ($listfile, $extensions, @files);
my ($tmp, $temp, @tmp);
my ($Date, $report_archive);
#my $report = ();


my $services = {
    ftp    => { port => 21,   print => "", waitfor => '/220/' },
    ssh    => { port => 22,   print => "", waitfor => '/SSH/' },
    telnet => { port => 23,   print => "", waitfor => '/(login|username)/' },
    domain => { port => 42,   print => "", waitfor => '' },
    http   => { port => 80,   print => "HEAD / HTTP/1.0\n\n", waitfor => '/200/' },
    https  => { port => 443,  print => "HEAD / HTTP/1.0\n\n", waitfor => '/200/' },
    pop    => { port => 110,  print => "", waitfor => '/\+OK/' },
    nntp   => { port => 119,  print => "", waitfor => '/200/' },
    imap   => { port => 143,  print => "", waitfor => '/OK/' },
    irc    => { port => 6667, print => "", waitfor => '' },
    smtp   => { port => 25,   print => "", waitfor => '/SMTP/' }
};

# MAIN
# get the command line arguments
my %opts = ();
getopt('hsef', \%opts);

##$service  = $opts{s};
##$host     = $opts{h};
##$send_to_warning    = $opts{e} if exists $opts{e};
##$listfile = $opts{f} if exists $opts{f};
##print "Host: $host; Service: $service; Email: $send_to_warning\n";

&get_time();

my $myipaddress = hostip; # get (text) dotted-decimal ip
my $myhostname = hostname; # get hostname
# change email subjects
$email_alarm_subj = "Services Monitor Alarm from $myhostname";
$email_warning_subj = "Services Monitor Warning from $myhostname";

# --- Set final log file name ---
#--- replace / OR : with - in the string ---
($temp = $Date) =~ s/[\/:]/-/g;
#--- delete first space and all after one in the string ---
$temp =~ s/[" "].*//g;
#--- replace " "  with , in the string ---
#$temp =~ s/[ ]/,/g;
my $finallog_name = "log-$myhostname-$temp.log";
print "finallog name: $finallog_name\n";

# --- Set report file name ---
#--- replace first / to - in the string ---
($temp = $Date) =~ s/[\/:]/-/o;
#--- delete first / and all after one in the string ---
$temp =~ s/\/.*//g;
my $log_archive = "log-$temp.zip";
$report_archive = "$report_file-$myhostname-$temp.zip";
my $report_file = "$report_file-$myhostname-$temp.$report_file_ext";

print "report_file name: $report_file\n";

# get working directory
my $current_dir = dirname($opts{f}) if (exists $opts{f})   ;
# change '\' to '/' (avoids trouble in substitution on Win2k)
$current_dir =~ s|\\|/|g;
#my $current_dir = getcwd; # get pathname of current working directory
print "Working dir: $current_dir\n";

$report_folder = "$current_dir/$report_folder";
$log_folder = "$current_dir/$log_folder";

# --- Check Log folder ---
&folder_check_create($log_folder);
print "log name: $log_folder/$log_temp\n";

# --- Delete old temporary log file ---
&dirfile_delete ($log_folder, $log_temp);

# --- Check report folder ---
&folder_check_create($report_folder);

# Check if Final log file exists, archive old if does not
if (-e "$log_folder/$finallog_name") {
    print "Final log name: $log_folder/$finallog_name\n";;
} else {
    &archive_reports($log_folder,"log",$log_archive,'move');
#    my $finallog_exist = 0;
}

# Check if Report file exists, archive old if does not
if (-e "$report_folder/$report_file") {
#    my $finallog_exist = 1;
    print "File $report_folder/$report_file exists\n";
} else {
    &archive_reports($report_folder,$report_file_ext,$report_archive,'move');
    &send_mail ($report_folder, $report_archive, $serversmtp, $emailsender,
        $report_email, '', "$email_report_subj $report_archive", ' ');
    $tmp = "Number of consecutive failed requests,Date,Host,Service,Error,Monitor ip address,Monitor hostname\n";
    &file_open_add_close($report_folder,$report_file,$tmp);
    #    my $reportfile_exist = 0;
}

# --- Start temporary log file ---
$tmp = "--- Start of programm on $myhostname ---\n";
&PrintAndWriteToLog($log_folder,$log_temp,$tmp);

# --- Check for file operators -f ---
$listfile = $opts{f} if exists $opts{f};
#$tmp = "Config filename (-f): $listfile\n";
#&PrintAndWriteToLog($log_folder,$log_temp,$tmp);

# --- if they passed us a filename ---
if (exists $opts{f} && -e $opts{f}) {
    # --- create new Text::CSV object ---
  my $csv = new Text::CSV_PP;
  # --- open the file they specified ---
  open(SERVERSFILE, "< $listfile")
      or die "&PrintAndWriteToLog($log_folder,$log_temp,Cannot open file $listfile: $!\n)";
  while () {
        #--- skip the line if it's empty ---
        next unless /\S+/;
        #--- get the columns for the line ---
        $csv->parse($_);
            ($host,$service,$send_to_warning,$conf_warning_quantity,
            $send_to_alarm,$conf_alarm_quantity) = $csv->fields;
#        $tmp = "Host: $host; Service: $service;
#            send_to_warning: $send_to_warning; conf_warning_quantity: $conf_warning_quantity;
#            send_to_alarm: $send_to_alarm; conf_alert_quantity: $conf_alarm_quantity\n";
#        &PrintAndWriteToLog($log_folder,$log_temp,$tmp);       
       
        # overwrite email_*_quantity from config file
        if ($email_warning_quantity != $conf_warning_quantity && 1 <= $conf_warning_quantity) {
            $email_warning_quantity = $conf_warning_quantity };
        if ($email_alarm_quantity != $conf_alarm_quantity && 1 <= $conf_alarm_quantity) {
            $email_alarm_quantity = $conf_alarm_quantity };
        # Check $email_warning_quantity > $email_alarm_quantity, bu should be <=
        $email_warning_quantity = $email_alarm_quantity if $email_warning_quantity > $email_alarm_quantity;
#        $tmp = "Host: $host; Service: $service;
#            send_to_warning: $send_to_warning; warning_quantity: $email_warning_quantity;
#            send_to_alarm: $send_to_alarm; alert_quantity: $email_alarm_quantity\n";
#        &PrintAndWriteToLog($log_folder,$log_temp,$tmp);
       
        next unless ($host =~ /\S+/ && $service =~ /\S+/);

        #--- check the service and alert ---
        &check_service_and_alarm($host,$service,$send_to_warning,$email_warning_quantity,$send_to_alarm,$email_alarm_quantity);
  }
    close(SERVERSFILE);
}
#else {
#    &usage unless exists($opts{h}) && exists($opts{s});
#
#    $service = $opts{s};
#    $host    = $opts{h};
#    $send_to   = $opts{e} if exists $opts{e};
#    print "Host: $host; Service: $service; Email: $send_to\n";
#
#    # check the service
#    if (&check_service($host,$service)) {
#    print "$host:$service is operational.\n";
#    } else {
##    &alert($send_to,$host,$service,$_ERR);
#    print "$host:$service is NOT operational.\nError: $_ERR.\n";
#    }
#}

$tmp = "--- End of programm. ---\n\n";
&PrintAndWriteToLog($log_folder,$log_temp,$tmp);
addfile1tofile2("$log_folder/$log_temp","$log_folder/$finallog_name");

### SUBROUTINES

#**** Archive reports *****
sub archive_reports {
my ($dir,$report_file_ext,$archive_name,$arcaction) = @_;
@files = ();
cwd $dir;
$extensions = $report_file_ext;
find(\&files_list_short, $dir);
$tmp = scalar @files;
$tmp = "Found $tmp $extensions file(s) for archiving\n";
&PrintAndWriteToLog($log_folder,$log_temp,$tmp);
if (@files <= 0) {
    return 2;
    } else {
my $zip = Archive::Zip->new();

my $zippath = "$dir/$archive_name";
# read-in the existing zip file if any
if (defined $zippath && -f $zippath) {
    my $status = $zip->read($zippath);
    warn "Read $zippath failed\n" if $status != "AZ_OK";
}
# add files
cwd $dir;
foreach my $memberName (@files) {
    if (-d $memberName ){
        warn "Can't add tree $memberName\n"
#        if $zip->addFile( $memberName ) != "AZ_OK";
        if $zip->addTree( $memberName, $memberName ) != "AZ_OK";
    } else {
#        print "dir,file: $dir, $memberName\n";
            $zip->addFile( $memberName )
            or &PrintAndWriteToLog($log_folder,$log_temp,"Can't add file $memberName\n");
    }
}
# prepare the new zip path
my $newzipfile = genfilename();
my $newzippath = "$dir/$newzipfile";
# write the new zip file
my $status = $zip->writeToFileNamed($newzippath);
if ($status == "AZ_OK") {
    # rename (and overwrite the old zip file if any)?
    if (defined $zippath) {
        my $res = rename $newzippath, $zippath;
        if ($res) {
            &PrintAndWriteToLog($log_folder,$log_temp,"Updated file $zippath\n");
        } else {
            &PrintAndWriteToLog($log_folder,$log_temp,"Created file $newzippath, failed to rename to $zippath\n");
        }
        } else {
        &PrintAndWriteToLog($log_folder,$log_temp,"Created file $newzippath\n");
        }
        } else {
        &PrintAndWriteToLog($log_folder,$log_temp,"Failed to create file $newzippath\n");
        }
        if ($arcaction eq 'move') {
#            print " Moving to archive\n";
            if  (-e $zippath) {
                foreach my $memberName (@files) {
                    &file_delete($memberName);
                }
            }
        }
    }
}
#**** list all files in folder *****
sub files_list_short {
#my ($dir, $extensions) = @_;
#    print "directory: $File::Find::dir\nExtentions: $extensions\n";
my $filename = ();
    if (/\.($extensions)$/) {
        cwd $File::Find::dir;
        return if -d $File::Find::name; # skip directories
#        my $fileagedays = fileAgeDays($_);
#        if ($fileagedays < $maxFileAgeDays) {
#            printf STDERR "$File::Find::name    (%.3g)\n"; #, $fileagedays;
            (my $filename = $File::Find::name) =~ s/^[a-zA-Z]://;  # remove the leading drive letter:
        push @files, $filename;
        print "$extensions file: $filename\n";
#        push @files, "./$_";
#        print "$extensions file: $_\n";
#        }
return @files;
    }
}
#**** Generate file name *****
sub genfilename {
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
    sprintf "%04d%02d%02d-%02d%02d%02d.zip", $year+1900, $mon+1, $mday, $hour, $min, $sec;
}
#**** Check folder *****
sub folder_check_create {
my ($temp) = @_;
    if (-e $temp) {
        print "Folder $temp already exists\n";
        } else {
            mkdir ($temp);
            print "Folder $temp was created\n";
    }
}
#**** get time *****
sub get_time() {
$Date = &ctime(time);
return $Date;
}

# ***** Send Mail *****
sub send_mail {
my ($dir,$file_name,$smtp,$emailsender,$send_to,$send_cc,$email_subj,@message) = @_;
# print "dir,file,to = @_  \n";
chdir("$dir");
open my $DEBUG, ">> $log_folder/$log_temp"
    or die "Can't open the debug file: $!\n";
ref (my $sender = new Mail::Sender({from => $emailsender,
smtp => $smtp})) or die &PrintAndWriteToLog($log_folder,$log_temp,"$Mail::Sender::Error\n");
if ($send_to ne 0){
    if ($file_name eq 'not_file')
    {
        ref ($sender->MailMsg(
        {to => $send_to,
        cc => $send_cc,
        subject => $email_subj,
#        debug => $DEBUG'
        msg => "@message"
        }));
        $tmp = "Mail sent TO: $send_to CC: $send_cc without attachment\n";
        &PrintAndWriteToLog($log_folder,$log_temp,$tmp);
    } else {   
    ref ($sender->MailFile(
    {to => $send_to,
    cc => $send_cc,
    subject => $email_subj,
    msg => "@message",
    file => $file_name    }));
    $tmp = "Mail sent TO: $send_to CC: $send_cc with attachment $file_name\n";
    &PrintAndWriteToLog($log_folder,$log_temp,$tmp);
    }
}
else {
    $tmp = "! Dont sent to $send_to $Mail::Sender::Error\n";
    &PrintAndWriteToLog($log_folder,$log_temp,$tmp);}
}

#***** Message print & write to log *****
sub PrintAndWriteToLog
{
my ($dir,$filename,$temp) = @_;
&get_time();
$temp = "$Date $temp";
print "$temp";
&file_open_add_close($dir,$filename,$temp);
}

# ***** Delete file if specified directory and file name *****
sub dirfile_delete {
my ($rdir, $filename) = @_;
$temp = "$rdir/$filename";
if (-e $temp) {
    unlink($temp) ;
    &PrintAndWriteToLog($log_folder,$log_temp,"Deleted File: $temp\n");
    } else {
        &PrintAndWriteToLog($log_folder,$log_temp,"can not delete file: $temp, because file does not exist\n");
    }
}

# ***** Delete file if specified full file name *****
sub file_delete {
my ($temp) = @_;
if (-e $temp) {
    unlink($temp) ;
    &PrintAndWriteToLog($log_folder,$log_temp,"Deleted File: $temp\n");
    } else {
        &PrintAndWriteToLog($log_folder,$log_temp,"can not delete file: $temp, because file does not exist\n");
    }
}

#***** Check the Service & Alert ******
sub check_service_and_alarm {
    ($host,$service,$send_to_warning,$email_warning_quantity,
    $send_to_alarm,$email_alarm_quantity) = @_;
   
        if (&check_service($host,$service)) {
          $tmp = "$host:$service is operational.\n";
          &PrintAndWriteToLog($log_folder,$log_temp,$tmp);
          &dirfile_delete ($log_folder,"$host.$service") if (-e "$log_folder/$host.$service");
          return 1;
        } else {
        &Check_repeating_of_error($log_folder,$host,$service,$email_warning_quantity,$email_alarm_quantity);
        $_ERR = "$quantity_current,$_ERR";
#        print "quantity_current:$quantity_current\n";
        &PrintAndWriteToLog($log_folder,$log_temp,"quantity_current:$quantity_current\n");
            &file_open_add_close($report_folder,$report_file,"$_ERR\n");
        $tmp = "! $host:$service is NOT operational.
        ! Error:\n  $_ERR
        Test number is $quantity_current from $email_warning_quantity/$email_alarm_quantity\n";
        &PrintAndWriteToLog($log_folder,$log_temp,$tmp);

my @message_warning = <<"END_MESSAGE";
This is a Warning email from $myhostname IP: $myipaddress

$host:$service is NOT operationalafter $quantity_current number(s) of consecutive failed requests.
Error is:
$_ERR

Regards,
Systems Administrator
END_MESSAGE

my @message_alarm = <<"END_MESSAGE";
! This is a ALARM message from $myhostname IP: $myipaddress

$host:$service is NOT operational after $quantity_current numbers of consecutive failed requests.
Error is:
$_ERR

Regards,
Systems Administrator
END_MESSAGE

        if ($quantity_current >= $email_warning_quantity && $quantity_current < $email_alarm_quantity) {
            &send_mail ($report_folder, 'not_file', $serversmtp, $emailsender,
            $send_to_warning, '', $email_warning_subj, @message_warning);
        } else {
#        if ($quantity_current >= $email_alarm_quantity) {
            &send_mail ($report_folder, 'not_file', $serversmtp, $emailsender,
            $send_to_alarm, $send_to_warning, $email_alarm_subj, @message_alarm);
        }

#            &send_mail ($report_folder,'not_file',$serversmtp,$emailsender,$send_to_warning,$email_warning_subj,$tmp);
        return 0;
        }
}   
#***** Check repeating of error ******
sub Check_repeating_of_error {
my ($dir,$file_name,$file_extension,$email_warning_quantity,$email_alarm_quantity) = @_;
if (-e ("$dir/$file_name.$file_extension")) {
#    $temp = "File $dir/$file_name.$file_extension exists\n";
#    &PrintAndWriteToLog($log_folder,$log_temp,"$temp");
    &fault_file_replace("$dir/$file_name.$file_extension");
    return $quantity_current;   
    } else {
#        $temp = "File $dir/$file_name.$file_extension does not exist\n";
#        &PrintAndWriteToLog($log_folder,$log_temp,"$temp\n");
        &file_open_add_close($log_folder,"$host.$service",pack("A","1"));
        $quantity_current = 1;
        return $quantity_current;
    }
}
#***** replase fault File *****
sub fault_file_replace {
my ($dir,$fault_file,$fault_file_ext) = @_;
open(FAULT_FILE, "< $dir/$fault_file.$fault_file_ext")
    or die PrintAndWriteToLog("Couldn't open file $dir/$fault_file.$fault_file_ext: $!\n");
#sysopen(FAULT_FILE, "$dir/$fault_file.$fault_file_ext", "O_WRONLY|O_TRUNC")
#    or die PrintAndWriteToLog("Couldn't open file: $!\n");
@tmp = ;
close FAULT_FILE;
@tmp[0] =~ s/^\D//g;
$quantity_current = unpack("A",@tmp[0]);
#&PrintAndWriteToLog($log_folder,$log_temp,"quantity_old:$quantity_current\n");
$quantity_current = $quantity_current + 1;
#&PrintAndWriteToLog($log_folder,$log_temp,"quantity_current:$quantity_current\n");
open(FAULT_FILE, "> $dir/$fault_file.$fault_file_ext")
    or die PrintAndWriteToLog("Couldn't open file $dir/$fault_file.$fault_file_ext: $!\n");
print FAULT_FILE pack("A","$quantity_current");
close FAULT_FILE;
return $quantity_current;
}
#***** Open or create, Add and Close Report File *****
sub file_open_add_close {
my ($report_folder,$report_file,@report) = @_;
#--- open report File ---
open(REPORT_FILE,">> $report_folder/$report_file");
#--- Add to Report File ---
print REPORT_FILE @report;
#--- Close Report File ---
close REPORT_FILE;
}
#***** Check Service ******
sub check_service {
    ($host,$service) = @_;

    #--- make sure user specified a valid service ---
    die &PrintAndWriteToLog($log_folder,$log_temp,"! Service $service does not compute.\n")
        unless defined $services->{$service};
       
    my $port    = $services->{$service}->{port};
    my $sock    = new Net::Telnet (Telnetmode => 0);
    my $waitfor = $services->{$service}->{waitfor};
   
    #--- Create a connection to the remote host ---
    eval {
    $sock->open(Host    => $host,
        Port    => $port,
        Timeout => $timeout
        );
    };

    #--- Catch any errors ---
    if ($@) {
        &get_time();
        $_ERR = "$Date,$host,$service,Can not establish connection,$myipaddress,$myhostname";
        return 0;
    }
   
    # send data to remote host
    $sock->print($services->{$service}->{print}) if $services->{$service}->{print};

    # wait for regex match
    if (!($sock->waitfor($waitfor))) {
        $_ERR = "$Date,$host,$service,Timed out waiting for $waitfor,$myipaddress,$myhostname";
        return 0;
    }
   
    # things went well
    return 1;
}

#*** Add File1 to File2 ****
sub addfile1tofile2 {
my ($file1, $file2) = @_;
open(IN,$file1) || die "cannot open $file1 for reading: $!";
open(OUT,">>$file2") || die "cannot create $file2: $!";
while () {     #read line from $file1 to $_
    print OUT $_; # write line $_ to file $file2
    }
close(IN) || die "can't close $file1: $!";
close (OUT) || die "can't close $file2: $!";
}

sub usage {
    die <
Usage: monitor -h host -s service [-e email]
       monitor -f
DIE
}

1;
__END__


Configuration file:
#hostname/IP,service,send_to_warning,conf_warning_quantity,send_to_alarm,conf_alarm_quantity
myserver.mydomain.com,http,"sysadmin@mycompany.com",1,"notifications@mycompany.com",3
myserver.mydomain.com,smtp,"sysadmin@mycompany.com",1,"notifications@mycompany.com",4
12.12.12.12,ssh,"sysadmin@mycompany.com",1,"notifications@mycompany.com",5
www.mydomain.com,http,"sysadmin@mycompany.com",1,"notifications@mycompany.com",5
www.eircom.ie,http,"sysadmin@mycompany.com",1,"notifications@mycompany.com",3
home.btireland.ie,http,"sysadmin@mycompany.com",1,"notifications@mycompany.com",3
www.magnet.ie,http,"sysadmin@mycompany.com",1,"notifications@mycompany.com",3
11.11.11.11,http,"sysadmin@mycompany.com",1,"notifications@mycompany.com",3
ftp.mydomain.com,ftp,"sysadmin@mycompany.com",1,"notifications@mycompany.com",5


DOWNLOAD: monitor.zip
Download md5: 2f982b123f93926aeca5f521bacf7185

Комментариев нет: