# Copyright 2008-2013 Cumulus Systems Incorporated.
# All Rights Reserved.

# This file contains the code to verify the connection to an Server .
#
# @param : IP Address - FusionIO Server  IP Address
#
# @return : In case of Success, returns 0. In case of Failure, returns 1.
#
# Sample command: perl fioServerVerifier.pl <IP Address>

#!/usr/bin/perl -w

# It is used for strict compilation.
use strict;

# It is used to get base file name from the complete file name.
use File::Basename;

# These are used to get the current date and time.
use Time::HiRes qw(gettimeofday);
use POSIX "strftime";

# This is used to create folder recursively.
use File::Path qw(mkpath);

# It is used to idicate the path where common module is placed.
use lib "/usr/local/megha/lib/fio";

# It is required for using the function of common module.
use commonModule;

# ----------------------------------------------------------------------------------------------------------------------------------------------------
# Error codes corresponding to fioGetServerData.sh. Always keep it in sync with error codes in fioGetServerData.sh.
# ----------------------------------------------------------------------------------------------------------------------------------------------------
my $ERROR_SUCCESS                        = 0;
my $ERROR_ANY_OTHER_ERROR                = 1;
my $ERROR_SSH_INVALID_CRED               = 3;
my $ERROR_SSH_CONNECTION_FAILED          = 4;

# ----------------------------------------------------------------------------------------------------------------------------------------------------
# GLOBAL Variables
# ----------------------------------------------------------------------------------------------------------------------------------------------------

# This variable is used to store the complete name of file where data from Server  through SSH method will be dumped.
my $sshDumpFile = "";

# This variable is used to store the complete name of file where error output from Server  will be dumped.
my $errorDumpFile = "";

# This variable is used to store the complete name of file which contain the output of this script.
my $verificationOutputFile = "";

# This variable is used to store the Server  User-Id which is used to get the  Configuration data.
my $serverUserId = "";

# This variable is used to store the Server  Password corresponding to $serverConfUserId.
my $serverPassword = "";

# This variable is used to store the Server  Password corresponding to $serverPerfUserId.
my $serverPerfPassword = "";

# This variable is used to store the FusionIO Server  IP Address.
my $fioIpAddress = "";

# This variable is used to store the host name
my $hostName = "";

# This variable is used to store the return status of function calls.
my $status = 0;

# This variable is used to store the MARS Site name.
my $marsSiteName = "";

# This variable is used to store the location of fio runtime files.
my $fioRunTimeLocation = "";

# This variable is used to store the complete path of MARS app.properties file.
my $appPropertiesFilePath = "";

# This variable is used to store error message which will be displayed in UI.
my $errorMsg = "";

# This variable is used to store the complete name of log file.
my $logFile = "";

# This variable is used to store the file handle of log file.
my $LOG_HANDLE;

# This variable is used to store the status of credential verification.
my $credentialVerified = 0;

# This variable is used to store the base folder path on the MARS probe.
my $baseFolder = "";

# ----------------------------------------------------------------------------------------------------------------------------------------------------
# Main function
# ----------------------------------------------------------------------------------------------------------------------------------------------------

# Initialize the environment.
$status = init();
if (0 != $status)
{
    logMsg("Error: Call to init() failed.");
    goto EXIT;
}

# Verify Server credentials.
$status = verifyCredential();
if (0 != $status)
{
    logMsg("Error: Call to verifyCredential() failed.");
    goto EXIT;
}

EXIT:

# If verification is successful then dump the  model in a temporary file so that caller of this script can read the information and update
# Probe specific properties file with the model information. If verification fail then dump the respective error message in the temporary file.
$status = dumpVerificationInfo();
if (0 != $status)
{
    logMsg("Error: Call to dumpVerificationInfo() failed.");
}

# De-init the environment.
deInit();

exit $status;

# ----------------------------------------------------------------------------------------------------------------------------------------------------
# Sub-routines
# ----------------------------------------------------------------------------------------------------------------------------------------------------

# This function is used to verify Server credentials.
#
# @affected global variables :
#   $credentialVerified
#
# @return :
#   0 if Success
#   1 if Error
sub verifyCredential
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    # Connect to the Server and fetch some data.
    $retVal = getServerData();
    #if (0 != $retVal)
    #{
    #logMsg("Error: Call to getServerData() failed.");
    #return 1;
    #}

    # Verify the Server configuration data.
    $retVal = verifyServerData();
    if (0 != $retVal)
    {
        logMsg("Error: Call to verifyServerData() failed.");
        return 1;
    }

    $credentialVerified = 1;

    return 0;
}

# This function is used to initialize the environment to run the Perl script correctly.
#
# @affected global variables :
#   $fioIpAddress
#   $appPropertiesFilePath
#   $verificationOutputFile
#
# @return :
#   0 if Success
#   1 if Error
sub init
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    # This variable is used to store the complete path of probe specific folder in db/probe folder.
    my $probeFolder = "";

    # Set MARS base folder.
    $baseFolder = "/usr/local/megha";
    #$baseFolder = "/cygdrive/c/Users/Ajit/work/megha_70/megha/server/deploy";

    # If number of command line arguments are not correct then quit.
    if (4 != ($#ARGV + 1))
    {
        logMsg("Error: Number of arguments to serverVerifier.pl are not correct. Input arguments = ".($#ARGV + 1));
        $retVal = 1;

        goto EXIT;
    }

    # Get the FusionIO Server  IP Addrss from command line argument.
    $fioIpAddress = $ARGV[0];
    logMsg("Info: Input IP Address = [$fioIpAddress]");

    # Get the MARS Site name from command line argument.
    $marsSiteName = $ARGV[1];
    logMsg("Info: Input Site name = [$marsSiteName]");

    # Get the Server User name from command line argument.
    $serverUserId = $ARGV[2];
    logMsg("Info: Input User = [$serverUserId]");

    # Get the Server password from command line argument.
    $serverPassword = $ARGV[3];

    # Set MARS probe folder path.
    $probeFolder = $baseFolder."/db/probe/".$marsSiteName."/Server_".$fioIpAddress;
    logMsg("Info: Creating folder: [$probeFolder].");

    # Create the Server Probe folder in the db/probe folder.
    unless (defined eval {mkpath($probeFolder)})
    {
        logMsg("Error: Not able to create folder: [$probeFolder].");
        $retVal = 1;

        goto EXIT;
    }

    # Set the file name where the Server  data through SSH method will be dumped.
    $sshDumpFile = $probeFolder."/serverVerificationSsh.dump";
    logMsg("Info: SSH Verification dump file: [$sshDumpFile].");

    # Set the file name where the Server error message will be dumped.
    $errorDumpFile = $probeFolder."/serverErrorVerification.dump";
    logMsg("Info: Server Error Verification dump file: [$errorDumpFile].");

    # Set the file name where output of this script will be stored.
    $verificationOutputFile = $probeFolder."/serverVerificationOutput.txt";
    logMsg("Info: Verification output file: [$verificationOutputFile].");

    # Set the complete path of MARS app.properties file.
    $appPropertiesFilePath = $baseFolder."/conf/app.properties";
    logMsg("Info: app.properties file path: [$appPropertiesFilePath].");

    # Read the content of app.properties file.
    $retVal = readAppPropertyFile($appPropertiesFilePath, $fioIpAddress, $fioRunTimeLocation);
    if (0 != $retVal)
    {
        logMsg("Error: Call to readAppPropertyFile() failed.");
        $retVal = 1;

        goto EXIT;
    }

EXIT:

    return $retVal;
}

# This function is used to run a shell script which connects to the Server  and dump some configuration data in a file.
#
# @affected global variables :
#   None
#
# @return :
#   0 if Success
#   1 if Error
sub getServerData
{
    # This variable is used to store the exit status of "system" command.
    my $exitCode = 0;

    # This variable is used to store the exit status of the function.
    my $exitStatus = 0;

    # This variable is used to store the command to get the Server conf data.
    my $cmd = "";

    # Construct the command to get the Server Conf data. First check whether the SSH is enabled on .
    $cmd = $fioRunTimeLocation."/fioGetServerData.sh $fioIpAddress $serverUserId $serverPassword $fioRunTimeLocation/fioVerify.sh $fioRunTimeLocation 0 > $sshDumpFile 2>&1";
	logMsg("Info: Trying to get Configuration information using SSH method. Running command: [$fioRunTimeLocation/fioGetServerData.sh $fioIpAddress $serverUserId <password> $fioRunTimeLocation/fioVerify.sh $fioRunTimeLocation 0 > $sshDumpFile 2>&1");

    $exitCode = executeSystemCmd($cmd);
    logMsg("Exit code is [$exitCode]");
    if ($ERROR_SUCCESS == $exitCode)
    {
        # If SSH is enabled on  then return with Success.
        logMsg("Info: Succeded in getting ServerConfData through SSH.");
    }
    # If SSH connection to  failed with "Invalid credential" error then exit with error.
    elsif ($ERROR_SSH_INVALID_CRED == $exitCode)
    {
        logMsg("Error: SSH connection to server failed. Error: Invalid credentials.");
        $errorMsg = "Error: SSH connection to server failed due to invalid credentials.";
        $exitStatus = $exitCode;

        goto EXIT;
    }
    elsif ($ERROR_SSH_CONNECTION_FAILED == $exitCode)
    {
        logMsg("Error: SSH connection to server failed. Error: Connection time out.");
        $errorMsg = "Error: SSH connection to server failed due to connection time out.";
        $exitStatus = $exitCode;

        goto EXIT;
    }
    elsif ($ERROR_ANY_OTHER_ERROR == $exitCode)
    {
        logMsg("Error: SSH connection to server failed. Error: Unknown.");
        $exitStatus = $exitCode;
        goto EXIT;
    }
EXIT:

    return $exitStatus;
}

# This function is used to verify the data retrieved from the Server .
#
# @affected global variables :
#   None
#
# @return :
#   0 if  connection is successful.
#   1 if  connection failed.
sub verifyServerData
{
    # This variable is used to store the return value of the function.
    my $retVal = 0;

    # It is used to check whether we found correct information in the Verification dump file.
    my $found = 0;
	
	# It is used to create error message string for global errorMsg variable.
    my $errorMessage = "";

    logMsg("Info: Opening server verification dump file. File = [$sshDumpFile].");

	open(SERVER_DUMP, $sshDumpFile) or die logMsg("Error: Not able to open the file: [$sshDumpFile].");

	if ($errorMsg eq '') {
		$errorMessage = "Error:";
	}

    # Loop through the content of Verification dump file.
    while (<SERVER_DUMP>)
    {
		# Remove the new line character.
        $_ =~ s/\r\n//g;
        $_ =~ s/\n//g;
		
		if ($errorMsg eq '') {
			$errorMessage .= $_.' ';
		}
			
        logMsg("Info: Server  connection data = [$_].");
        
        # If the data collection is through ssh then look for the following pattern. 
        if (/host=([\w\.]+)/)
        {
            $hostName = "Host:".$1;

            logMsg("Info: Host Name = [$hostName].");
            $found = 1;

            last;
        }
    }

    if (0 == $found)
    {
        $retVal = 1;
		
		if ($errorMsg eq '') {
			$errorMsg = $errorMessage;
		}
    }

    close(SERVER_DUMP);

    return $retVal;
}

# This function is used to create a file at: db/probe/Server-<IP>/serverVerificationInfo.txt
# It puts Server  model name in this file. Caller of this script is responsible for deleting this file.
#
# @affected global variables :
#   None
#
# @return :
#   0 if Success
#   1 if Error
sub dumpVerificationInfo
{
    # This variable is used to store the return value of the function.
    my $retVal = 0;

    # Use the open() function to create the file.
    unless(open FILE, '>'.$verificationOutputFile)
    {
        # Die with error message if we can't open it.
        logMsg("Error: Unable to create: [$verificationOutputFile].");

        $retVal = 1;
        goto EXIT;
    }

    # If there is something in hostName variable, it means that we connected successfully
    if("" ne $hostName)
    {
        # Write  host name in this file.
        print FILE $hostName; 
    }
    else
    {
        # Write error message in this file.
        print FILE $errorMsg;
    }

    # Close the file.
    close(FILE);

EXIT:

    return $retVal;
}

# This function is used to run a command through system() call. system() call somehow returns error code differently by shifting the exit code to 8
# bits left side. So this function also shifted back the exit code to 8 bits right side and return it.
#
# @affected global variables :
#   None
#
# @return :
#   Exit code shifted to 8 bits right.
sub executeSystemCmd
{
    my $tempExitCode = system($_[0]);

    my $exitCode = $tempExitCode >> 8;

    return $exitCode;
}

# This function is used to log a message in the log file. It logs the message in the following format:
# [YYYY-MM-DD HH:MM:ss,SSS]  [Server-<IP Address>] [Verifier] <Log message>
#
# @param :
#   $_[0] - Log message.
#
# @affected global variables :
#   None
#
# @return :
#   Nothing
sub logMsg
{
    # This variable is used to store the complete path of probe specific folder in db/probe folder.
    my $serverLogFolder = "";

    # Set Server log folder path.
    $serverLogFolder = $baseFolder."/logs/fioProbe";

    # If log folder does not exist then create it.
    if (!(-e $serverLogFolder))
    {
        # TODO: Check the return value of mkpath.
        mkpath($serverLogFolder);
    }

    # Get the current date and time.
    my ($seconds, $microseconds) = gettimeofday;
    my $currentTime = strftime("%Y-%m-%d %H:%M:%S", localtime($seconds));

    # Get the date from the given date and time .
    my($date, $time) = split(' ', $currentTime, 2);

    # Set the name of log file.
    $logFile = $serverLogFolder."/fioProbe.".$date.".log";

    # Open the log file.
    open($LOG_HANDLE, ">>", $logFile) or die "Not able to open the file: [$logFile].";

    # Read input parameters.
    my $logMsg = $_[0];

    # For getting time in a given format.
    $currentTime = "[".$currentTime.",".$microseconds."]";

    # Construct the final log message.
    my $finalMsg = $currentTime." [Server-".$fioIpAddress."] [Verifier] ".$logMsg."\n";

    # Dump the final log message in log file.
    print $LOG_HANDLE $finalMsg;
}

# This function is used to de-initialize the environment.
#
# @affected global variables :
#   $CONF_HANDLE
#
# @return :
#   None
sub deInit
{
    close($LOG_HANDLE);
}
# ----------------------------------------------------------------------------------------------------------------------------------------------------
