#!/usr/bin/perl
# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html
#(C)IBM Corp
#
###################################################################
#
# Description:
#     The xcatsnap command gathers configuration, log and trace information
#     about the xCAT components that are installed. This command only collects
#     the data on the local node on which this command is run.
#
#     This command is typically executed when a problem is encountered with
#     any of these components in order to provide service information to the
#     IBM Support Center.
#
#     This command should only be executed at the instruction of the IBM
#     Support Center.
# Syntax:
#       perl xcatsnap [-h] [-v] [-B] [-d output_directory]
#
# Flags:
#       -h displays the usage of this command to standard output.
#       -v displays the version of this tool.
#       -B Bypass mode.
#       -d is used to specify the output directory,
#          otherwise the default directory is /tmp/xcatsnap
#
# Ouput:
#     tar file : xcatsnap.host_name.nnnnnnnn.tar.gz
#     log file : xcatsnap.host_name.nnnnnnnn.log
#     (where host_name is the base name of the host name of the
#     node on which the command is running; nnnnnnnn is a timestamp).
#
# Exit Status:
#     0 - Command ran successfully
#     1 - error occured
#
# Security:
#     This command can only be run by a root user.
#
####################################################################

use File::Spec;
use Getopt::Long;
use strict;
my $OSname;
my @Commands_array;
my $Command_name;
my $Command;
my $currentDirectory;
my $logDirectory="/tmp/xcatsnap";
my $output_dir;
my @files_array;
my @file_list;
my $TarFile;
my $LogFile;
my $INSTALLDIR;
my $circular = 0;

##############################FUNCTIONS##################################

################### usage:Display usage message ##########################

sub usage {
    print "Usage: xcatsnap [-h][-v][-B][-d output_directory]";
    print "\n       -h Display this usage statement.";
    print "\n       -v Display the version.";
    print "\n       -B Run in bypass mode. Use if xcatd hung";
    print "\n       -d Output directory (default:/tmp/xcatsnap)\n";
}

######### valid dir: Converts relative path to an absolute path #############

sub valid_dir {
    $logDirectory = File::Spec->rel2abs($logDirectory);
    my $Dir_last_char = substr $logDirectory, -1, 1;
    if ( $Dir_last_char eq "/" ) {
        chop($logDirectory);

    }
}

######################### run_cmd: Runs Commands ###############################

sub run_cmd {
    my @output;
    $Command_name = $Command;#Constructing the output file name from the command
    $Command_name =~ s/ /_/g;
    $Command_name =~ s/-//g;
    $Command_name =~ s/tabdump_//g;
    $Command_name =~ s/\//_/g;
    
    print "$Command -> $Command_name.out";
    print "\n\tExecuting: $Command \n";
    eval {
        local $SIG{ALRM} = sub { die "Timeout\n" };
        alarm 60;
        @output = `$Command`;
        alarm 0;
    };
    if ($@) {
        print "\t$Command  timed out.\n";
        @output = "$Command timed out.\n";
    }
    
        print "\tExecution Complete :$Command.\n";    #Writing command output into a file
        my $outfile = $output_dir . $Command_name . ".out";
        open( MYFILE, ">", $outfile );
        print MYFILE @output;
        close(MYFILE);
    
}

######################## Tar it: Tars files and folders #############################

sub Tar_it {
    my $file= shift;
    print "\n Processing $file ..\n";
    my $last = substr $file, -1, 1;
    if ( $last eq "*" ) {
        @file_list = `ls $file 2>/dev/null`;
        foreach my $i (@file_list) {
            print "\tProcessing $i";
        }
    }
    if ( -l $file ) {

        check_symbolic_link($file);    # Checking Symbolic links
    }
    if ( $circular != 1 ) {
        if ( -e $TarFile ) {
            `cd /; tar -uf $TarFile .$file 2>/dev/null`;
        }
        else {
            `cd /; tar -cf $TarFile .$file 2>/dev/null`;
        }
    }
    $circular = 0;
    print "\n$file processed...\n";
}

################ check_symbolic_link: Checks symoblic links #####################

sub check_symbolic_link {

    my $file= shift;
    my $max_link_count = 32;
    my $i              = 0;
    while ( defined( my $link = readlink $file ) && $i <= $max_link_count ) {
        $file = $link;
        $i++;
    }
    if ( $i >= $max_link_count ) {
        $circular = 1;
        print
"Either the link is circular or the symbolic link count exceeds max_link_count";
    }
}

############## make_output_dir: Creates output directory #######################

sub make_output_dir {
    if ( -d $output_dir ) {
        `rm -rf $output_dir`;
    }
    `mkdir  $output_dir`;
}

##################### snap_it:Does the main job ###########################

sub snap_it {
    print "Collecting files ...\n";
    chop( $INSTALLDIR =
          `tabdump site | grep installdir | cut -f2 -d ,` );
    $INSTALLDIR =~ s/"//g;
    # make a list of all files in /tftpboot
    # need to limit what we get due to size
    `ls -lR /tftpboot > /tftpboot/tftpboot.list`;
    if ( $OSname eq "AIX" ) {
        
        @files_array = (
            "/etc/xcat/*","$::ROOTHOME/.xcat/*", "$INSTALLDIR/autoinst/*",
            "$INSTALLDIR/postscripts/*", "$INSTALLDIR/prescripts/*",
            "/tftpboot/*",               "/var/log/consoles/*",
            "/tmp/spot.out.*",           "/var/lib/dhcpd/dhcpd.leases",
            "/etc/hosts",                "/etc/conserver.cf",
            "/var/log/conserver",        "/etc/db_file.cr",
            "/etc/dhcpsd.cnf",           "/var/adm/ras/nimlog",
            "/etc/resolv.conf", "/etc/named.conf",  "/var/log/messages");
    }

    elsif ( $OSname eq "Linux" ) {
        
        @files_array = (
            "/etc/xcat/*","$::ROOTHOME/.xcat/*", "$INSTALLDIR/autoinst/*",
            "$INSTALLDIR/postscripts/*",   "$INSTALLDIR/prescripts/*",
            "/tftpboot/*",                 "/var/log/consoles/*",
            "/etc/*-release",              "/etc/dhcpd.conf",
            "/var/lib/dhcpd/dhcpd.leases", "/etc/hosts", "/etc/resolv.conf",
            "/etc/named.conf", "/etc/conserver.cf",           "/var/log/conserver",
            "/etc/nsswitch.conf",          "/var/log/messages");
        print("@files_array \n");
    }
            foreach my $item (@files_array) {
            my $file = $item;
            Tar_it($file);
        }
    print "Done collecting files ...\n\n";
    print "Gathering system configuration...\n\n";
    $output_dir = "$logDirectory/commands_output/";
    my $xcatroot=$ENV{'XCATROOT'};
    my $installdir;
    chop( $installdir =
          `tabdump site | grep installdir | cut -f2 -d ,` );
    make_output_dir();
    if ( $OSname eq "AIX" ) {
        @Commands_array = (
            "uname -a","ifconfig -a","netstat -in","netstat -rn","env",
            "reventlog -a","lsmod","/sbin/lspci","lssrc -a","rpm -qa",
            "ls $installdir",
            "find /tftpboot -size -32k","ls -lR $xcatroot",
            "arp -a","ps -edlf","ps -aux","ulimit -a","df -k","oslevel",
            "netstat -A","errpt -a","/usr/sbin/instfix -i",
            "/usr/sbin/lsnim -l","lssrc -l -s dhcpsd","lslpp -hac","lsxcatd -a");
    }
    elsif ( $OSname eq "Linux" ) {
        @Commands_array = (
            "uname -a","ifconfig -a","netstat -in","netstat -rn","env",
            "reventlog -a","lsmod","/sbin/lspci","lssrc -a","rpm -qa",
            "ls $installdir",
            "find /tftpboot -size -32k","ls -lR $xcatroot",
            "arp -a","ps -edlf","ps -aux","ulimit -a","df -k",
            "cat /etc/issue","lsxcatd -a");
    }
    foreach my $item (@Commands_array) {
        $Command = $item;
        run_cmd;
    }
    print "Done gathering system configuration...\n\n";

    if ( -d "/opt/xcat/" ) {
        print "Capturing xCAT specific information...\n\n";
        print "Gathering management node configurations...\n";
        @Commands_array = (
            "lsdef -t site -l","lsdef -t group -l","lsdef -t osimage -l",
            "nodels","lsdef -t node -l","rpower all stat","nodestat all",
            "nodels all groups","monls -a","lsvm all","rinv all all",
            "rvitals all all");
        foreach my $item (@Commands_array) {
            $Command = $item;
            run_cmd;
        }
        print "Done gathering managment node configuration...\n\n";
        print "Retrieving xCAT database...\n";
        $output_dir = "$logDirectory/xcat-database/";
        make_output_dir();
        # do not snap the ISNM tables, too big
        my $cmd;
        $cmd="XCAT_SKIPTABLES=isnm_perf,isnm_perf_dlink,isnm_perf_dlink_sum,isnm_perf_hfi,isnm_perf_hfi_sum,isnm_perf_isr,isnm_perf_isr_sum,isnm_perf_lllink,isnm_perf_lllink_sum,isnm_perf_lrlink,isnm_perf_lrlink_sum,isnm_perf_sum";
         $cmd .= " dumpxCATdb -p $output_dir";
        `$cmd`;
        # now get auditlog and eventlog, last two days
         # get number of seconds in the day count
         my $numberdays=2;
         my $numbersecs=($numberdays * 86400);
        # get time now
        my $timenow=time;
        my $secsdaysago=$timenow - $numbersecs;
         # Format like the database table timestamp record
        my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
              localtime($secsdaysago);
         my $daysago = sprintf("%04d-%02d-%02d %02d:%02d:%02d",
                                        $year + 1900, $mon + 1, $mday,
                                        $hour, $min, $sec);
        # now tabdump days gt 2 days ago
        $cmd = "tabdump -w \"audittime>$daysago\" auditlog > $output_dir/auditlog.csv";

        `$cmd`;
        $cmd = "tabdump -w \"eventtime>$daysago\" eventlog > $output_dir/eventlog.csv";

        `$cmd`;
 
        print "xCAT database retrieved.\n";
    }
    `rm /tftpboot/tftpboot.list`;  # remove temp list
}    
##################### getHomeDirectory ###########################
#  input userid output homedir
#####################################################################
sub getHomeDir
{
  my @user;
  my $homedir;
  @user = getpwuid($>);
  my $username=$user[0];

  if ($user[7]) { #  if homedir
      $homedir= $user[7];
  } else { # no home
      $homedir=`su - $username -c  pwd`;
      chop $homedir;
  }
  return $homedir;

}
############################# Main Section ####################################

my $userid = `id -ru`;    #Checking if the user is root
if ( $userid != 0 ) {
    print "You must be root to run the xcatsnap tool";
    exit 1;
}

`export PATH=/opt/xcat/bin:/opt/xcat/sbin:/bin:/sbin:/usr/bin:/usr/sbin:`;

#Checking the program arguments

if (
    !GetOptions(
                'd|dir=s'        => \$::DIRECTORY,
                'B|bypass'     => \$::BYPASS,
                'h|help'       => \$::HELP,
                'v|version'    => \$::VERSION,
    )
  )
{
    &usage;
    exit(1);
}
if ($::HELP ) {
    usage();
    exit 0;
}
if ($::VERSION) {
    print " xcatsnap tool version 1.0\n";
    exit 0;

}
if ($::BYPASS)
{
    $ENV{XCATBYPASS} = "yes";  # bypass xcatd
}


if (!($::DIRECTORY)) {
    print " Log Directory will be /tmp/xcatsnap/\n";
}
else  {
    $logDirectory = $::DIRECTORY ;
    valid_dir();
}
unless ( -d $logDirectory ) {   #Create the output directory if it doesn't exist
    `mkdir -p $logDirectory`;
     if ($?!=0) {  
       print " Could not create $logDirectory\n";
       exit 1;
     }  
  
    valid_dir();
}
my $hostname;
chop( $OSname   = `uname` );
chop( $hostname = `hostname -s` );
my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
  localtime(time);
$mon = $mon + 1;
my @date_array = ( $mon, $mday, $hour, $min );
foreach my $item (@date_array) {
    $item =
      sprintf( "%2d", $item );    #Formatting the date for dispaly in file name
    $item =~ tr/ /0/;
}
my $logdate = $date_array[0] . $date_array[1] . $date_array[2] . $date_array[3];
$LogFile = $logDirectory . "/xcatsnap." . $hostname . "." . $logdate . ".log";
$TarFile = $logDirectory . "/xcatsnap." . $hostname . "." . $logdate . ".tar";

open( STDOUT, "| tee  $LogFile" );
print "Time Stamp:" . `date`;
print "Log Directory: $logDirectory \n";
print "Preparation Complete...\n";
$::ROOTHOME = &getHomeDir();
snap_it(); # Calling the main function that gathers files,command output and MN database
print "Compiling Information...\n";
`cd $logDirectory;tar -uf $TarFile "./commands_output" 2>/dev/null`;
`cd $logDirectory;tar -uf $TarFile "./xcat-database" 2>/dev/null`;
`rm -rf $logDirectory/commands_output/`;
`rm -rf $logDirectory/xcat-database/`;

print "Information compiled...\n";
`chmod 400 $LogFile`;    # Processing the log file
print "Send $LogFile to IBM Support.\n";
my $donotdelete=0;
if ( `which gunzip` == 0 ) {    # Compressing the tar file
    `gzip -f $TarFile`;
}
elsif ( `which compress` == 0 ) {
    `compress -f $TarFile`;
}
else {
    print
"gzip and compress are not available. The tar file $TarFile will not be compressed";
$donotdelete=1;
}

if (-e $TarFile  && $donotdelete == 0){   # Don't remove if only file to send 
    `rm $TarFile`;
}

if ( -e $TarFile . ".gz" ) {
    `chmod 400 $TarFile".gz"`;
    print "Send $TarFile.gz to IBM Support.\n";
}
elsif ( -e $TarFile . ".z" ) {
    `chmod 400 $TarFile".z"`;
    print "Send $TarFile.z to IBM Support.\n";
}
elsif ( -e $TarFile ) {
    `chmod 400 $TarFile`;
    print "Send $TarFile to IBM Support.\n";
}
close(STDOUT);
exit 0;