#!/usr/bin/perl
#
# This script scans a SpinServer to gather information about backup failures (and successes).
# This script was plagiarized from get_support_info and modified appropriately.

use warnings;
use strict;
use Getopt::Long;
use Data::Dumper;

# Get the P4 changelist number
my $p4version = '$Change: 43777 $';

my $backup_info_file = "backup_info";
my $spinver="/sbin/spinver";
my $cat="/bin/cat";
my $ifconfig="/sbin/ifconfig";
my $ls="/bin/ls";
my $file="/usr/bin/file";
my $ps="/bin/ps";
my $df="/bin/df";
my $tar="/bin/tar";
my $rm="/bin/rm";
my $fcdRpcTest="/sbin/fcdRpcTest";
my $rpm="/bin/rpm";
my $sum="/usr/bin/sum";
my $find="/usr/bin/find";
my $lpInfo="/sbin/lpInfo";
my $spinlocate="/sbin/spinlocate.pl";
my $cp="/bin/cp";
my $compress=0;
my $messages="/var/log/messages";
our $directory;

use constant Dir => 0;
use constant File => 1;

$p4version =~ s/^\s*\$[C][h][a]n[g][e]:\s*(\d+)\s*\$\s*$/$1/;

my $majorversion = getMajorVersion();

my %options=(
	     username       => "Administrator",
	     password       => "",
	     cifsserver     => "",
	     bypass         => "",
	     compress       =>  0,
	     skipclean      =>  0,
	     showonly       =>  0,
	     messages_limit => -1,
	     messages_file  => "",
	     );
my @bypass;

Getopt::Long::GetOptions(\%options, 
			 'username=s', 
			 'password=s', 
			 'cifsserver=s', 
			 'bypass=s', 
			 'compress', 
			 'skipclean', 
			 'showonly', 
			 'messages_limit=i',
			 'messages_file=s',
			 'proc', 
			 'sums', 
			 );

@bypass = split(/,/, $options{bypass});
my $docifs= 0;
if ($options{username} && $options{password} && $options{cifsserver}){
    $docifs = 1;
}
my @date = localtime;
my $date = sprintf("%02d%02d%02d_%02d%02d%02d", $date[5]+1900, $date[4]+1, $date[3], $date[2], $date[1], $date[0]);
$directory = "/space1/backup_$date";

print "Writing backup info to the $directory directory\n";

if (-d $directory){
    `rm -rf $directory`;
    if ($?){
	die "Could not remove $directory. Error was $?. Exiting";
    }
}
mkdir($directory, 0777) || die "Could not mkdir $directory";
chdir($directory) || die "Could not chdir to $directory";

if (open(OF, ">$backup_info_file")){
    print OF "\nData generated by $0 version $p4version\n";
    close OF;
} else {
    warn "Couldn't open backup_info to write version information: $p4version";
}

if ($options{messages_file}) {
    $messages = $options{messages_file};
}

getBackupInfo();

# Usually, we want to grab all the messages files. But if the name of a messages file
# is explicitly passed in, that's an indicator that this script is being run to analyze a
# specific file, and we only need to generate an analysis file of that one messages file.
# In fact, if we're only working with one explicit file, don't even tar things up at the
# end.
if (!$options{messages_file}) {
    # grab all of the messages files, limited by the command line parameter passed
    my $message_dir = "/var/log/";
    my @files = glob($message_dir . "messages*");

    while ($options{messages_limit}-- && (my $filename = shift @files)){
	$filename =~ s/$message_dir//g;
	getInfo(File => "/dev/null", Cmd => "$cp $message_dir$filename $directory/$filename");
    }

    my $tarfile="$directory.tgz";
    `$tar cvfz $tarfile $directory > /dev/null 2>&1`;
    if ($?){
	print "Error creating tar file $tarfile";
    } else {
	print "Tar file is $tarfile\n";
	`$rm -rf $directory` unless $options{skipclean};
    }
}

#*******************************************************************************

sub getMajorVersion {
    my $retval = undef;
    if (open(IF, "$spinver |")){
	# Skip header lines
	<IF>;
	<IF>;
	my %revs;
	while (<IF>){
	    my ($module, $version, $build) = split;
	    $revs{$version}++;
	}
	close(IF);
	my $max = 0;
	foreach my $key (keys %revs){
	    if ($revs{$key} > $max){
		$max = $revs{$key};
		$retval = $key;
	    }
	}
    }
    $retval;
}

#*******************************************************************************

sub makeParentDir {
    my ($fullpath) = @_;

    my @dirs = split(/\//, $fullpath);
    if ($fullpath =~ /^\//){
	shift @dirs;
    }
    pop @dirs;
    my $currpath = "";
    foreach my $dir (@dirs){
	$currpath .= "/$dir";
#	print "Would mkdir $currpath\n";
	if (!-d $currpath){
	    mkdir ($currpath, 0777) || warn "Could not make directory $currpath";
	}
    }
}

#*******************************************************************************

sub getBackupInfo {
    my $retval = undef;
    
    open(BACKUP_INFO, ">>$backup_info_file");

    print BACKUP_INFO "\n\nSpinFS version:\n\n";
    system "spinver >> $backup_info_file";

    print BACKUP_INFO "\n\nRPM info:\n\n";
    system "$rpm -qa | egrep spin >> $backup_info_file";

    print BACKUP_INFO "\n\nFibre Channel devices:\n\n";
    system "$fcdRpcTest localhost devlist >> $backup_info_file";

    print BACKUP_INFO "\n\nLIP resets:\n\n";
    system "grep 'LIP Reset' $messages >> $backup_info_file";

    print BACKUP_INFO "\n\nReboots of this system:\n\n";
    system "grep 'spinsyslogd' $messages >> $backup_info_file";

    print BACKUP_INFO "\n\nLP info:\n\n";
    system "$lpInfo -s >> $backup_info_file";

    print BACKUP_INFO "\n\nndmpd process failures (grep for \"ndmpd.*exit\"):\n\n";
    system "grep 'ndmpd.*exit' $messages >> $backup_info_file";

    print BACKUP_INFO "\n\nD-Blade backup timeouts (grep for \"Error on Sending\"):\n\n";
    system "grep 'Error on Sending' $messages >> $backup_info_file";

#    print BACKUP_INFO "\n\nNDMP failures (grep for \"DATA operation failed\"):\n\n";
#    system "grep 'DATA operation failed' $messages >> $backup_info_file";

#    print BACKUP_INFO "\n\nNDMP failures (grep for \"backup failed\" or \"internal error\"):\n\n";
#    system "grep -h -e 'backup failed' \\| -e 'internal error' $messages >> $backup_info_file";

#    print BACKUP_INFO "\n\nNDMP retries (grep for \"rejected with code\"):\n\n";
#    system "grep 'rejected with code' $messages >> $backup_info_file";

    print BACKUP_INFO "\n\nBackup summary (grep for a number of things):\n\n";
    system "grep -h " . 
	"-e 'start backup' \\| " . 
	"-e 'backup failed' \\| " .
	"-e 'rejected with code' \\| " .
	"-e 'internal error' $messages >> $backup_info_file";

    close(BACKUP_INFO);
    return $retval;
} # end getBackupInfo

#*******************************************************************************

sub getInfo {
    my %params;
    while (@_){
	my $key = ucfirst lc shift;
	my $value = shift;
	if ($key =~ /^Cmd$/){
	    push(@{$params{Cmd}}, $value);
	} else {
	    $params{$key} = $value;
	}
    }

    if (exists($params{Minrev}) && (CmpRev($majorversion, $params{Minrev}) < 0)){
	print "Not gathering information for $params{File}.\n  Version required $params{Minrev}.  Running version $majorversion\n";
	return;
    }
    
    if (exists($params{Maxrev}) && (CmpRev($majorversion, $params{Maxrev}) > 0 )){
	print "Not gathering information for $params{File}.\n  Running version $majorversion higher than Max version $params{Maxrev}\n";
	return;
    }
    
    if (grep(/^$params{File}$/, @bypass)){
	print "Check for $params{File} bypassed\n";
	return;
    }
    if ($params{File} =~ /\//){
	makeParentDir("$directory/$params{File}");
    }
    if ($params{File} ne "/dev/null"){
	$params{File} = "$directory/$params{File}";
	while (-e $params{File}){
	    if (!($params{File} =~ /\.\d+$/)){
		$params{File} .= ".1";
	    } else {
		$params{File}++;
	    }
	}
    }
    if (!open(LOG_FILE, ">$params{File}")){
	warn "Could not execute commands (" . join("#", @{$params{Cmd}}) . "). Error opening $params{File}";
    }
    foreach my $cmd (@{$params{Cmd}}){
	print "====> Executing: $cmd -> ";
	if (!$options{showonly}){
	    print LOG_FILE `$cmd 2>&1`;
	    if ($?){
		print "Error $?\n";
	    } else {
		print "OK\n";
	    }
	} else {
	    print "No exec\n";
	}
    }
    close(LOG_FILE) || die "Could not close LOG_FILE: $!";
}

