Reboot check – Perl Script

Used to perform post reboot checks. Must be ran once before reboot and once after reboot. Look at all _REPORT files to get a status after the reboot. Only new event IDs will be shown in the _REPORT eventlog files.
The /before and /after must be ran on the same day or the /l switch must be used; otherwise /after will fail.
Usage: RebootCheck /b[efore] /a[fter] /f[ilename] <filename> /s[erver] <hostname> /c[ancel] /reboot /shutdown /l[ogdir] <directory>

Varialbles

/before     Creates pre-reboot reference files
/after      Compares reference files with post-reboot status and generates report
/filename   Name of files with list of servers to check
/server     Name of target server (useless if /f is also used)
/reboot     Reboots server(s) in 5 minutes, then loops on /after, until servers are up again and fully checked. Script will timeout after 15 minutes if a server does not respond.
/shutdown   Shutdowns server(s) in 5 minutes
/cancel     Cancels reboot immediately
/logdir     Log files directory (must already exist). This parameter is optional. If not specified, log files will be stored in a directory named after the current date

use Win32;
use Win32::Lanman;
use Getopt::Long;
use Win32::Service;
use Win32::PerfLib;
use Win32::EventLog;

##################
# main procedure #
##################

# parse the command line for arguments
p_parsecmdline(%config,@ARGV);

# check the combination of arguments is valid
p_checkargs();

# this hash will store the service information for the remote server so that it may be re-used outside
# the p_retservices procedure
my %ServiceList;

# generate a log directory name based on today's date if none have been specified
unless ($logdir) {
$logdir = p_createlogpathdir();
}

# open the _REPORT.error.log filename; if it already exists open it in append mode
if (-f "$logdir\_REPORT.error.log") {
open (ERRORLOG,">>$logdir\_REPORT.error.log")
or die "ERROR: cannot open _REPORT.error.log in append mode!n";
} else {
open (ERRORLOG,">$logdir\_REPORT.error.log")
or die "ERROR: cannot open _REPORT.error.log!n";
}

# if a list of servers has been specified, read the file
if ($filename) {
p_GetServerList();
}

# if /after switch has been set, open the _REPORT files
if (($after == 1) or ($rebootaction eq "reboot")) {
open (SERVREPORT,">$logdir\_REPORT.services.log")
or die "ERROR: cannot open _REPORT.services.log in append mode!n";
open (LOGREPORT,">$logdir\_REPORT.eventlog.log")
or die "ERROR: cannot open _REPORT.eventlog.log!n";
}

# if /reboot switch has been used, open the _REPORT.reboot log file
if ($reboot == 1) {
open (REBOOTLOG,">$logdir\_REPORT.reboot.error.log")
or die "ERROR: cannot open _REPORT.reboot.error.log!n";
}

# process each server
for ($i=0;$serverlist[$i];++$i) {
# reboot or abort the reboot
if ($reboot) {
p_reboot($serverlist[$i],$rebootaction);
next;
}
if ($i == 0) {
if ($before == 1) {
print ERRORLOG "n------ Before ------n";
} elsif ($after == 1) {
print ERRORLOG "n------ After ------n";
}
}
# see how long the server has been up for
unless ($uptime = p_GetSysUptime($serverlist[$i])) {
print "ERROR: Cannot determine system uptime for $serverlist[$i]n";
next;
}
# see what time it is on the server
unless (Win32::Lanman::NetRemoteTOD("\\$serverlist[$i]", %info)) {
print "ERROR: $serverlist[$i]: cannot query time: ".Win32::Lanman::GetLastError()."n";
print "n";
next;
}
# determine what the time offset for reading the logs should be
$time = $info{elapsedt} - $uptime;
if ($before == 1) {
p_retEVTerrors("before",$serverlist[$i],$time,"System");
p_retEVTerrors("before",$serverlist[$i],$time,"Application");
p_retservices("before",$serverlist[$i]);
} elsif ($after == 1) {
p_retEVTerrors("after",$serverlist[$i],$time,"System");
p_retEVTerrors("after",$serverlist[$i],$time,"Application");
p_retservices("after",$serverlist[$i]);
p_compareservices($serverlist[$i]);
p_comparelog($serverlist[$i],"System");
p_comparelog($serverlist[$i],"Application");
#p_checktivoli($serverlist[$i]);
}
print "n";
}

if ($rebootaction eq "reboot") {
print "nWaiting 6 minutes for server(s) to reboot...nn";
sleep 360;
for ($i=0;$serverlist[$i];++$i) {
my $Counter = 0;
while ((! Win32::Lanman::NetRemoteTOD("\\$serverlist[$i]", %info)) and ($Counter < 15)) {
print "INFO: $serverlist[$i] is not responding, trying again in 1 minute...n";
sleep 60;
++$Counter;
}
unless ($Counter <15) {
print ERRORLOG "ERROR: $serverlist[$i] timed out while trying to do post-reboot checks. Please check this server manually.n";
}
print "n";
if ($uptime = p_GetSysUptime($serverlist[$i])) {
$time = $info{elapsedt} - $uptime;
p_retEVTerrors("after",$serverlist[$i],$time,"System");
p_retEVTerrors("after",$serverlist[$i],$time,"Application");
p_retservices("after",$serverlist[$i]);
p_compareservices($serverlist[$i]);
p_comparelog($serverlist[$i],"System");
p_comparelog($serverlist[$i],"Application");
#p_checktivoli($serverlist[$i]);
}
print "n";
}
}

close (ERRORLOG)
or print "ERROR: cannot close _REPORT.error.log!n";

if (($after == 1) or ($rebootaction eq "reboot")) {
close (SERVREPORT)
or print "ERROR: cannot close _REPORT.services.log!n";
close (LOGREPORT)
or print "ERROR: cannot close _REPORT.eventlog.log!n";
}

if ($reboot == 1) {
close (REBOOTLOG)
or print "ERROR: cannot close _REPORT.reboot.error.log!n";
}

##################
# sub-procedures #
##################

# procedure p_help
# displays a help message
sub p_help {
my ($script)=($0=~/([^\/]*?)$/);
my ($header)=$script." v1.1 - Author: suparatul@gmail.com - http://www.tipsandtutorials.net";
my ($line)="-" x length($header);
print <

$header
$line
Used to perform post reboot checks. Must be ran once before
reboot and once after reboot. Look at all _REPORT files to
get a status after the reboot. Only new event IDs will be
shown in the _REPORT eventlog files.
The /before and /after must be ran on the same day or the /l switch
must be used; otherwise /after will fail.

Usage: RebootCheck /b[efore] /a[fter] /f[ilename] /s[erver]
/c[ancel] /reboot /shutdown /l[ogdir]

/before Creates pre-reboot reference files
/after Compares reference files with post-reboot
status and generates report
/filename Name of files with list of servers to check
/server Name of target server (useless if /f is also
used)
/reboot Reboots server(s) in 5 minutes, then loops on
/after, until servers are up again and fully checked.
Script will timeout after 15 minutes if a server does not
respond.
/shutdown Shutdowns server(s) in 5 minutes
/cancel Cancels reboot immediately
/logdir Log files directory (must already exist). This
parameter is optional. If not specified, log
files will be stored in a directory named
after the current date
EOT

exit 1;
}
# procedure p_parsecmdline
# parses the command line and retrieves arguments values
sub p_parsecmdline {
my ($config) = @_;
my $result = 0;
Getopt::Long::Configure("prefix_pattern=(-|/)");
$result = GetOptions($config, qw(
before|b
after|a
filename|f=s
server|s=s
reboot
shutdown
logdir|l=s
cancel|c
help|?|h));
}
# procedure p_checkargs
# checks arguments are valid
sub p_checkargs {
if ($config{help} eq 1) {
p_help();
}
if ($config{before} eq 1) {
$before = 1;
} elsif ($config{after} eq 1) {
$after = 1;
}
if ($config{filename}) {
$filename = $config{filename};
} elsif ($config{server}) {
@serverlist = $config{server};
}
if ($config{reboot} eq 1) {
$reboot = 1;
$rebootaction = "reboot";
} elsif ($config{cancel} eq 1) {
$reboot = 1;
$rebootaction = "abort";
} elsif ($config{shutdown} eq 1) {
$reboot = 1;
$rebootaction = "shutdown";
}
if ($config{logdir}) {
$logdir = $config{logdir};
}

unless ($reboot) {
unless ($before or $after) {
p_help();
}
if ($before and $after) {
p_help();
}
}
unless (($filename) or (@serverlist)) {
p_help();
}
}
# procedure p_GetServerList
# creates @serverlist
sub p_GetServerList {
# Process file to retrieve the list of server names to work with
open (SERVERLIST, $filename) || die "ERROR: can't open $filename";
my $i = 0;
while (defined ($servername = )) {
chomp($servername);
#Store each server name in the array @serverlist
$serverlist[$i] = $servername;
$i++;
}
close (SERVERLIST) || die "ERROR: can't close $filename";
}
# procedure p_GetSysUptime
# retrieves a machine's uptime
sub p_GetSysUptime {
my $machine = $_[0];
my $perf;
my $Numerator;
my $Denominator;
my $TimeBase;
my $counter;
my $seconds;
my $hour;
my $minute;
my $days;
if ($perf = new Win32::PerfLib($machine)) {
my $objlist = {};
my $system = 2;
if($perf->GetObjectList("$system", $objlist)) {
$perf->Close();
my $Counters = $objlist->{Objects}->{$system}->{Counters};
unless ($Counters) {
print "ERROR: $machine: System counters could not be retrievedn";
return;
}
foreach $o ( keys %{$Counters}) {
$id = $Counters->{$o}->{CounterNameTitleIndex};
if($id == 674) {
$Numerator = $Counters->{$o}->{Counter};
$Denominator = $objlist->{Objects}->{$system}->{PerfTime};
$TimeBase = $objlist->{Objects}->{$system}->{PerfFreq};
$counter = int(($Denominator - $Numerator) / $TimeBase );
$seconds = $counter;
$days = int($seconds / 86400);
$seconds -= $days * 86400;
$hour = int($seconds / 3600);
$seconds -= $hour * 3600;
$minute = int($seconds / 60);
$seconds -= $minute * 60;
print "INFO: $machine: has been up for $days days $hour hours $minute minutes $seconds secondsn";
if (($after == 1) and ($counter > 1800)) {
print "WARNING: $machine: has been up for more than 30 minutesn";
print ERRORLOG "WARNING: $machine: has been up for more than 30 minutesn";
}
last;
}
}
}
} else {
print "ERROR: $machine: cannot get system uptime: $^En";
print ERRORLOG "ERROR: $machine: cannot get system uptime: $^En";
}
return ($counter);
}
# procedure p_retsyserrors
# retrieves all errors from system event log for the last 48 hours
sub p_retEVTerrors {
my $prefix = $_[0];
my $servername = $_[1];
my $timeoffset = $_[2];
my $log = $_[3];
my $message;
my $time;
my $recs;
my $base;
my $x;
my $EVTdate;
my $EVTsource;
my $EVTid;
my $EVTmessage;
my $syshashRef;
my $syserror = 0;

#$Win32::EventLog::GetMessageText = 1;

unless (open (SYSERRORS,">$logdir\$servername.$prefix.$log.log")) {
print "ERROR: $servername: cannot open $prefix.system.logn";
print ERRORLOG "ERROR: $servername: cannot open $prefix.system.logn";
}
unless ($handle=Win32::EventLog->new($log, $servername)) {
print "ERROR: $servername: cannot open $log eventlog: $^En";
print ERRORLOG "ERROR: $servername: cannot open $log eventlog: $^En";
}
unless ($handle->GetNumber($recs)) {
print "ERROR: $servername: cannot get number of eventlog records: $^En";
print ERRORLOG "ERROR: $servername: cannot get number of eventlog records: $^En";
}
unless ($handle->GetOldest($base)) {
print "ERROR: $servername: cannot get number of oldest eventlog record: $^En";
print ERRORLOG "ERROR: $servername: cannot get number of oldest eventlog record: $^En";
}

my $tempdate = localtime($timeoffset);
print "INFO: $serverlist[$i]: getting $log log errors since $tempdaten";

while ($handle->Read(EVENTLOG_BACKWARDS_READ|EVENTLOG_SEQUENTIAL_READ,0,$syshashRef) && ($syshashRef->{TimeGenerated} > $timeoffset)) {
if ($syshashRef->{EventType} eq 1) {
Win32::EventLog::GetMessageText($syshashRef);
$EVTdate = $syshashRef->{TimeGenerated};
$EVTdate = localtime($EVTdate);
$EVTsource = $syshashRef->{Source};
$EVTid = $syshashRef->{EventID} & 0xffff;
$EVTmessage = $syshashRef->{Message};
# some event messages can't be retrieved, so retrieve strings instead
unless ($EVTmessage) {
$EVTmessage = $syshashRef->{Strings};
}
$EVTmessage = join(' ',split(/n/,$EVTmessage));
$EVTmessage = join('',split(/r/,$EVTmessage));
print SYSERRORS "$EVTdate, $EVTsource, $EVTid, $EVTmessagen";
$syserror = 1;
}
}
unless (defined($syshashRef)) {
print SYSERRORS "ERROR: $servername: could not read event log. Please check server manually.n";
}
if ($syserror == 1) {
print "WARNING: $servername: there are $log log errorsn";
print ERRORLOG "WARNING: $servername: there are $log log errorsn";
}
unless (close (SYSERRORS)) {
print "ERROR: $servername: cannot close $prefix.system.logn";
print ERRORLOG "ERROR: $servername: cannot close $prefix.system.logn";
}
$handle->Close();
}
# procedure p_retservices
# retrieves running services
sub p_retservices {
my %list;
my $prefix = $_[0];
my $machine = $_[1];
unless (open (SERVICES,">$logdir\$machine.$prefix.services.log")) {
print "ERROR: $machine: cannot open $prefix.services.logn";
print ERRORLOG "ERROR: $machine: cannot open $prefix.services.logn";
}
print "INFO: $machine: getting list of running servicesn";
if (Win32::Service::GetServices($machine,%list)) {
# Save the service list information so that it may be used later outside of this procedure
%ServiceList = %list;
foreach $service (sort(keys(%list))) {
if (Win32::Service::GetStatus($machine,$list{$service},%status)) {
$status = $status{CurrentState};
if ($status == 4) {
print SERVICES "$servicen";
}
} else {
print "ERROR: $machine: cannot get service status for $service: $^En";
print ERRORLOG "ERROR: $machine: cannot get service status for $service: $^En";
}
}
} else {
print "ERROR: $machine: cannot get service list: $^En";
print ERRORLOG "ERROR: $machine: cannot get service list: $^En";
print SERVICES "ERROR: $machine: cannot get service list. Please check server manually.n";
}
unless (close (SERVICES)) {
print "ERROR: $machine: cannot close $prefix.services.logn";
print ERRORLOG "ERROR: $machine: cannot close $prefix.services.logn";
}
}
# procedure p_compareservices
# compares before and after files for list of running services
sub p_compareservices {
my $servername = $_[0];
my $entry;
my $match;
my $before_entry;
my $after_entry;
my @before;
my @after;
my $first = 0;
unless (open (BEFORE, "$logdir\$servername.before.services.log")) {
print "ERROR: $servername: cannot open before.services.logn";
print ERRORLOG "ERROR: $servername: cannot open before.services.logn";
}
unless (open (AFTER, "$logdir\$servername.after.services.log")) {
print "ERROR: $servername: cannot open after.services.logn";
print ERRORLOG "ERROR: $servername: cannot open after.services.logn";
}
my $j = 0;
while (defined ($entry = )) {
chomp($entry);
$before[$j] = $entry;
++$j;
}
$j = 0;
while (defined ($entry = )) {
chomp($entry);
$after[$j] = $entry;
++$j;
}
foreach $before_entry (@before) {
foreach $after_entry (@after) {
if ($before_entry eq $after_entry) {
$match = 1;
last;
} else {
$match = 0;
}
}
if ($match == 0) {
if ($first == 0) {
print ERRORLOG "WARNING: $servername: at least one service is not running anymore!n";
++$first;
}
# Insert code here to check service configuration and try to restart it.
my %ServiceConfig;
my $StartType = "Startup Undefined";

if (Win32::Lanman::QueryServiceConfig("\\$servername", "", "$ServiceList{$before_entry}", %ServiceConfig)) {
if ($ServiceConfig{start} == 0) {
$StartType = "Boot";
} elsif ($ServiceConfig{start} == 1) {
$StartType = "System";
} elsif ($ServiceConfig{start} == 2) {
$StartType = "Automatic";
} elsif ($ServiceConfig{start} == 3) {
$StartType = "Manual";
} elsif ($ServiceConfig{start} == 4) {
$StartType = "Disabled";
}
}
print "WARNING: $servername: $before_entry service ($StartType) is not running anymore!n";
if (Win32::Lanman::StartService($servername,"",$ServiceList{$before_entry})) {
print "INFO: $servername: Service "$before_entry" was startedn";
} else {
my $Error = Win32::Lanman::GetLastError();
print "ERROR: $servername: Could not start service '$before_entry':".Win32::FormatMessage($Error)."n";
}
print SERVREPORT "$servername,$before_entryn";
}
}
unless (close (BEFORE)) {
print "ERROR: $servername: cannot close before.services.logn";
print ERRORLOG "ERROR: $servername: cannot close before.services.logn";
}
unless (close (AFTER)) {
print "ERROR: $servername: cannot close after.services.logn";
print ERRORLOG "ERROR: $servername: cannot close after.services.logn";
}
}
# procedure p_comparelog
# compares before and after event log error files
sub p_comparelog {
my $servername = $_[0];
my $logfile = $_[1];
my $entry;
my $match;
my $before_entry;
my $after_entry;
my @before;
my @after;
my @before_split;
my @after_split;
unless (open (BEFORE, "$logdir\$servername.before.$logfile.log")) {
print "ERROR: $servername: cannot open before.$logfile.logn";
print ERRORLOG "ERROR: $servername: cannot open before.$logfile.logn";
}
unless (open (AFTER, "$logdir\$servername.after.$logfile.log")) {
print "ERROR: $servername: cannot open after.$logfile.logn";
print ERRORLOG "ERROR: $servername: cannot open after.$logfile.logn";
}
my $j = 0;
while (defined ($entry = )) {
chomp($entry);
$before[$j] = $entry;
++$j;
}
$j = 0;
while (defined ($entry = )) {
chomp($entry);
$after[$j] = $entry;
++$j;
}
foreach $after_entry (@after) {
foreach $before_entry (@before) {
@before_split = split(/,/,$before_entry);
@after_split = split(/,/,$after_entry);
if ($before_split[1] eq $after_split[1]) {
if ($before_split[2] eq $after_split[2]) {
$match = 1;
last;
}
} else {
$match = 0;
}
}
if ($match == 0) {
print "WARNING: $servername: $logfile log: $after_entryn";
print LOGREPORT "WARNING: $servername: $logfile log: $after_entryn";
}
}
unless (close (BEFORE)) {
print "ERROR: $servername: cannot close before.$logfile.logn";
print ERRORLOG "ERROR: $servername: cannot close before.$logfile.logn";
}
unless (close (AFTER)) {
print "ERROR: $servername: cannot close after.$logfile.logn";
print ERRORLOG "ERROR: $servername: cannot close after.$logfile.logn";
}
}
# procedure p_reboot
# reboots the specified server in 5 minutes or aborts reboot
sub p_reboot {
my $machine = $_[0];
my $action = $_[1];
my $count = 300;
my $force = 1;
if ($action eq "reboot") {
my $message = "This server will reboot in $count seconds";
my $restart = 1;
if (Win32::InitiateSystemShutdown($machine,$message,$count,$force,$restart)) {
print "INFO: $machine: will reboot in $count secondsn";
} else {
print "ERROR: $machine: failed to initiate shutdown: $^En";
print REBOOTLOG "ERROR: $machine: failed to initiate shutdown: $^En";
}
} elsif ($action eq "abort") {
if (Win32::AbortSystemShutdown($machine)) {
print "INFO: $machine: reboot aborted successfullyn";
} else {
print "ERROR: $machine: could not abort reboot: $^En";
print REBOOTLOG "ERROR: $machine: could not reboot: $^En";
}
} elsif ($action eq "shutdown") {
my $message = "This server will shutdown in $count seconds";
my $restart = 0;
if (Win32::InitiateSystemShutdown($machine,$message,$count,$force,$restart)) {
print "INFO: $machine: will shutdown in $count secondsn";
} else {
print "ERROR: $machine: failed to initiate shutdown: $^En";
print REBOOTLOG "ERROR: $machine: failed to initiate shutdown: $^En";
}
}
}
# procedure p_createlogpathdir
# creates a target directory for all log files based on today's date
sub p_createlogpathdir {
my $time = time();
$time = localtime($time);
my @time = split(/s+/,$time);
my $logpath = "$time[4]-$time[1]-$time[2]-$time[0]";
unless (-d $logpath) {
my $rc = `mkdir $logpath`;
unless (-d $logpath) {
die "ERROR: cannot create $logpath directory: $rcn";
}
}
return $logpath;
}
# procedure p_checktivoli
# checks dm_ep_engine.exe process is running on the remote server
#sub p_checktivoli {
# my $machine = $_[0];
# my $process_obj = 230;
# my $process_id = 784;
# my $perflib = new Win32::PerfLib($machine);
# my $proc_ref = {};
# my $keyprocess = "dm_ep_engine";
# my $match = 0;

# print "INFO: $machine: checking dm_ep_engine processn";

# $perflib->GetObjectList($process_obj, $proc_ref);
# $perflib->Close();
# my $instance_ref = $proc_ref->{Objects}->{$process_obj}->{Instances};

# foreach $p (keys %{$instance_ref}) {
# $counter_ref = $instance_ref->{$p}->{Counters};
# foreach $i (keys %{$counter_ref}) {
# next if $instance_ref->{$p}->{Name} eq "_Total";
# if($counter_ref->{$i}->{CounterNameTitleIndex} == $process_id) {
# $process{$counter_ref->{$i}->{Counter}} = $instance_ref->{$p}->{Name};
# }
# }
# }

# foreach $p (sort { $a <=> $b } keys %process) {
# if ($process{$p} =~ /dm_ep_engin/i) {
# $match = 1;
# }
# }

# unless ($match == 1) {
# print "WARNING: $machine: $keyprocess is not runningn";
# print ERRORLOG "WARNING: $machine: $keyprocess is not runningn";
# }
#}

Leave a Comment