#!/usr/bin/perl

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

# This file contains the code to configure raidcom. Allocate instance number and port number, do entry of vertual command device and
# finally collect a set of data from raidcom and validate user credentials
#
# @param : 	IP address - SVP IP address
#			User name - SVP user name
#			Password - SVP Password 
#
# @return : In case of Success, returns 0. In case of Failure, returns 1.
#
# Sample command: perl configureRaidcom.pl <IP Address> <User name> <Password>

# It is used for strict compilation.
use strict;

# It helps in debugging. 	
use warnings;

# It is use to copy files
use File::Copy;

# It is use to get current date and time.
use Time::HiRes qw(gettimeofday);
use POSIX "strftime";
use Time::Local;

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

# This is used to delete folder recursively.
use File::Path 'rmtree';

# Set Mars as baseFolder.
my $baseFolder = '/usr/local/megha';

# This variable hold path of svp folder
my $svpFolder = "$baseFolder/lib/svp";

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

# This variable hold path of raidcomError file
my $errorFile ="$svpFolder/raidcomErrorfile.txt";

# This variable hold path of insSerialnum File
my $insSerialFile = "$svpFolder/insSerialNum.txt";

# If number of command line arguments are not correct then quit.
if (4 > ($#ARGV + 1))
{
    logMsg("Error:Number of arguments to configureRaidcom.pl are not correct. Input arguments = ".($#ARGV + 1));
	
	if ( open(Error_FILE, ">", $errorFile) )
	{
		print Error_FILE "Error :Number of arguments to configureRaidcom.pl are not correct. Input arguments ".($#ARGV + 1).".";
		close(Error_FILE);
	}
		
    exit 1;
}

# Get SVP IP  Address from command line argument.
my $svpIP = $ARGV[0];

# Get SVP user name from command line argument.
my $svpUserId = $ARGV[1];

# Get SVP password from command line argument.
my $svpPassword = $ARGV[2]; 

# Get SVP password from command line argument.
my $storageType = $ARGV[3];

# serial number 
my $serialNumber = "";

my $status = 0;

my  $tempFile = "$baseFolder/temp/tempRcomOutput.txt";
# this variable hold 1 if storage type USVP
my $isUspv = 0;
if ($storageType eq "USPV")
{
	$isUspv = 1;
}
#check if storage type is USPV, if uspv use In-band raidcom method of data collection 

if ($isUspv)
{
	$serialNumber = $ARGV[4];
	
	#check for command device.
	$status = checkCommandDevice();
	if (0 != $status)
	{
		logMsg("Error: Call to checkCommandDevice() failed.");
		if ( open(Error_FILE, ">", $errorFile) )
		{
			print Error_FILE "Error:PADUR_COMMAND_DEVICE_OF_PROVIDED_SERIAL_NUMBER_[_%S_]_IS_NOT_PRESENT=$serialNumber";
			close(Error_FILE);
		}
		goto EXITSTATUS;
	}
	
}
logMsg("Info: SVP IP = $svpIP , username =$svpUserId.");

# Take backup of /etc/services file
my $servicefile = '/etc/services';
my $tempServiceFile = "$baseFolder/temp/temp_services.txt";


# This variable stores instance number.
my $insNum ="";

# This variable stores port number.
my $portNum;

$status = setInstanceNumber();
if (0 != $status)
{
    logMsg("Error: Call to setInstanceNumber() failed.");
    goto EXITSTATUS;
}

# Create horcm_instanceNum.conf file under /etc/

if ( !open(HORCM_FILE, ">" ,"/etc/horcm$insNum.conf") )
{
	logMsg("Error:Not able to create [ /etc/horcm$insNum.conf ] file .");
	
	if ( open(Error_FILE, ">", $errorFile) )
	{
		print Error_FILE "Error:PADUR_NOT_ABLE_TO_OPEN_FILE_[_%S_]_FILE=/etc/horcm$insNum.conf";
		close(Error_FILE);
	}
	
	$status = 1;
	goto EXITSTATUS;
}

print HORCM_FILE "HORCM_MON\n";
print HORCM_FILE "#ip_address   service   poll(10ms)   timeout(10ms)\n";
print HORCM_FILE "127.0.0.1   horcm$insNum   1000    3000\n";
print HORCM_FILE "\n";
print HORCM_FILE "HORCM_CMD\n";
print HORCM_FILE "#dev_name\n";
if ($isUspv)
{
	print HORCM_FILE "\\\\.\\CMD-$serialNumber:/dev/sd\n";
}
else
{
	print HORCM_FILE "\\\\.\\IPCMD-$svpIP-31001\n";
}
close(HORCM_FILE);

# check for IP address

# If given ip is incorrect than horcmstart.sh call failed.
my $ret =system("horcmstart.sh $insNum > $tempFile 2>&1");
if($ret)
{
	
	logMsg("Error:Given IP [ $svpIP ] incorrect.");

	# Dumping error to log File
	if ( open(ERROR_MSG_HANDLER, "<", $tempFile ) )
	{
		my @errorMsgArray = <ERROR_MSG_HANDLER>;
        close(ERROR_MSG_HANDLER);
		logMsg("Error:@errorMsgArray");
    }
	else{
		logMsg("Error:Not able to open error file [$tempFile].");
	}

	if ( open(ERROR_FILE, ">", $errorFile ) )
	{
		if (!$isUspv)
		{
			print ERROR_FILE "Error:PADUR_INCORRECT_IP";
		}
		else
		{
			print ERROR_FILE "Error:Unable to start Raidcom Instance";
		}
		close(ERROR_FILE);
	}
	cleanup();
	$status = 1;
	goto EXITSTATUS;
}

logMsg("Info: IP validation pass.");

# Set envornment varible
$ENV{'HORCMINST'} = $insNum;

# Check credential if storage type VSP/HUS-VM
if (!$isUspv)
{

	# Check for valid user name and password
	logMsg("Info: Checking for valid User.");

	my $retres = system("raidcom -login $svpUserId $svpPassword > $tempFile 2>&1");
	if ($retres)
	{
		logMsg("Error:Authentication failed with User [ $svpUserId ].");
		# Dumping error to log File
		if (open(ERROR_MSG_HANDLER, "<", $tempFile ))
		{
			my @errorMsgArray = <ERROR_MSG_HANDLER>;
			close(ERROR_MSG_HANDLER);
			logMsg("Error:@errorMsgArray");
		}
		else{
			logMsg("Error:Not able to open error file [$tempFile].");
		}

		if (open(ERROR_FILE, ">", $errorFile ))
		{
			print ERROR_FILE "Error:PADUR_USER_AUTHENTICATION_FAILED";
			close(ERROR_FILE);
		}
	
		cleanup();
		$status = 1;
		goto EXITSTATUS;
	}
}

#collect a set of data from raidcom command
logMsg("Info: Collecting a set of data from RAIDCOM.");
my $retResource =system("raidcom get port > $tempFile 2>&1");
if($retResource)
{
		
	logMsg("Error:Failed in Collecting a set of data from raidcom.");
	if ( open(ERROR_MSG_HANDLER, "<", $tempFile ) )
        {
                my @errorMsgArray = <ERROR_MSG_HANDLER>;
                close(ERROR_MSG_HANDLER);
                logMsg("Error:@errorMsgArray");
        }
        else{
                logMsg("Error:Not able to open error file [$tempFile].");
        }

	if ( open(ERROR_FILE, ">", $errorFile ) )
	{
		print ERROR_FILE "Error:PADUR_FAILED_WHILE_COLLECTING_A_SET_OF_DATA_FROM_RAIDCOM";
		close(ERROR_FILE);
	}
		$status = 1;
		cleanup();
       goto EXIT; 
}

# Query raidqry for serial number and Storage micro version.
logMsg("Info: Query raidqry for serial number and Storage micro version.");
my $retQry =system("raidqry -l > $tempFile 2>&1");
if($retQry)
{	
	logMsg("Error:Failed from raidqry.");

	 if ( open(ERROR_MSG_HANDLER, "<", $tempFile ) )
        {
                my @errorMsgArray = <ERROR_MSG_HANDLER>;
                close(ERROR_MSG_HANDLER);
                logMsg("Error:@errorMsgArray");
        }
        else{
                logMsg("Error:Not able to open error file [$tempFile].");
        }
	if ( open(ERROR_FILE, ">", $errorFile ) )
	{	
		print ERROR_FILE "Error:PADUR_FAILED_WHILE_QUERY_[_RAIDQRY_-L_]_TO_RAIDCOM";
		close(ERROR_FILE);
	}
	$status = 1;
	cleanup();
    goto EXIT; 
}

# Parse temp file for serial number and micro version of storage.
my @storageInfo = ();
@storageInfo = ( @storageInfo , "InstanceNum:$insNum");
if( open(SERIAL_FILE, "<", $tempFile) )
{
	my $checkMicroVer;
	while( <SERIAL_FILE>)
	{
		# parse for line eg:  1  ---     localhost         01-29-03/06    0    53103  70-05-14/00     22528
		#from above line serialno: 53103, microversion: 70-05-14/00 .
		if( $_ =~ m/^(\s*)(\d+)(\s+)(\S*)(\s+)(\S*)(\s*)(\S+)(\s+)(\S*)(\s+)(\d+)(\s+)(\S*)/ )
		{
			@storageInfo = (@storageInfo, "SerialNumber:$12");
			my $microversion = $14;
			$microversion =~s/\//-/g;
			@storageInfo = (@storageInfo, "MicroVersion:$microversion");
			$checkMicroVer = $microversion;
			goto EXITWHILE;
		}
	}
EXITWHILE:
	close ($tempFile);
	
	#check for USPV storage
	if($isUspv)
	{
		$checkMicroVer =~s/-//g;
		if ((60000000 <= $checkMicroVer) && (70000000 > $checkMicroVer))
		{
			logMsg("Info:Correct storage type [ $storageType ] selected ");
		}
		else{
			logMsg("Error:Incorrect storage type [ $storageType ] selected ");
			if ( open(ERROR_FILE, ">", $errorFile ) )
			{
				print ERROR_FILE "Error:PADUR_THIS_IS_A_HUS-VM_STORAGE,_YOU_HAVE_SELECTED_VSP";	
				close(ERROR_FILE);
			}
			$status =1;
			cleanup();
			goto EXIT;
		}
	}
	
}
else{
	logMsg("Error:Not able to open [ $tempFile ] file ");
	$status =1;
	if ( open(ERROR_FILE, ">", $errorFile ) )
	{
		print ERROR_FILE "Error:PADUR_NOT_ABLE_TO_OPEN_FILE_[_%S_]_FILE=$tempFile";	
		close(ERROR_FILE);
	}
	cleanup();
	goto EXIT;
}

# Do entry of instance number , serial number and micro version of storage in insSerialFile file ( This file will read by java )
if (open(STORAGE_FILE, ">" , $insSerialFile))
{
	foreach my $val (@storageInfo)
	{
		print STORAGE_FILE "$val\n";
	}
	close(STORAGE_FILE);
}
else{
	logMsg("Error:Not able to open [ $insSerialFile ] file ");
	$status =1;
	if ( open(ERROR_FILE, ">", $errorFile ) )
	{
		print ERROR_FILE "Error:PADUR_NOT_ABLE_TO_OPEN_FILE_[_%S_]_FILE=$insSerialFile";	
		close(ERROR_FILE);
	}
	cleanup();
}

EXIT:
if (!$isUspv)
{
	my $sysresval = system("raidcom -logout");

	if($sysresval)
	{
		logMsg("Error:Failed to Logout from raidcom.");
	}
}
# delete  tempFile
if( -e $tempFile)
{
	unlink $tempFile or logMsg("Error:Not able to delete [ $tempFile ] file.");
}

EXITSTATUS:
exit $status;

# This function is used to clean entry of instance number in /etc/services and horcm file.
#
# @param :
# 	Nothing
#
# @return :
#   Nothing

sub cleanup
{
	# This variable use to store path of raidcom log file.
	my $raidcomLog = "/HORCM/log$insNum";
	
	my $sysreturn = system("horcmshutdown.sh $insNum");
	if($sysreturn)
	{
		logMsg("Error: Failed in shuting down instance [$insNum]");
		
		# kill that process
		my $processID = "";
		$processID = `pidof horcmd_0$insNum`;
		my $syscmd = system("kill -9 $processID");
		if($syscmd){
			logMsg("Error: Not able to kill process [horcmd_0$insNum]");
		}
	}
	# delete .conf file and remove entry of instance number
	if( -e "/etc/horcm$insNum.conf")
	{
		logMsg("Info: Deleting horcm file [horcm$insNum.conf] .");
		#delete .conf file
		unlink "/etc/horcm$insNum.conf" or logMsg("Error:Not able to delete [ /etc/horcm$insNum.conf ] file.");
	}
	
	# delete  tempFile
	 if( -e $tempFile)
        {
        	 unlink $tempFile or logMsg("Error:Not able to delete [ $tempFile ] file.");
        }

	# take backup of /etc/service file
	if ( !copy( $servicefile, $tempServiceFile ) )
	{
		if ( open(ERROR_FILE, ">", $errorFile ) )
		{
			print ERROR_FILE "Error:PADUR_NOT_ABLE_TO_TAKE_BACKUP_OF_[_%S_]_FILE=$servicefile";	
			close(ERROR_FILE);
		}
		logMsg("Error:Error in cleanup sub rutine : Not able to take backup of [ $servicefile ] to [ $tempServiceFile ] file.");
		exit 1;
	}

	if ( ! open(TEMP_SERV_FILE, "<", $tempServiceFile) )
	{
		if ( open(ERROR_FILE, ">", $errorFile ) )
		{
			print ERROR_FILE "Error:PADUR_NOT_ABLE_TO_TAKE_BACKUP_OF_[_%S_]_FILE=$servicefile";	
			close(ERROR_FILE);
		}
		logMsg("Error:Error in cleanup sub rutine : Not able to take backup of [ $servicefile ] file.");
		exit 1;
	}
	
	my @serviceFileArray = <TEMP_SERV_FILE>;
	close (TEMP_SERV_FILE);
	
	#remove entry of instance number and port number
	if ( open(SERV_FILE, ">", $tempServiceFile) )#|| die "error in opening  $tempServiceFile file";
	{	foreach my $line (@serviceFileArray)
		{
					
			print SERV_FILE $line unless ( $line =~ m/^horcm$insNum/ );
		}
		close(SERV_FILE);
	}
	
	# Rename tempServiceFile to seviceFile.
	rename($tempServiceFile, $servicefile);
	
	#delete raidcom log file
	if( -e $raidcomLog)
	{
		logMsg("Info: Deleting log file $raidcomLog");
		rmtree($raidcomLog);
	}
	
}

# This function is used to set raidcom insatnce number and port number under /etc/services.
#
# @param :
# insNum Instance Number
# portNum Port Number
#
# @return :
#   0 if Success
#   1 if Error
sub setInstanceNumber
{
	# This variable is used to store the return value of function calls.
    my $retVal = 0;
	
	# This array stores List of instance number allocated.
	my @insList = ();

	# This array stores List of port number allocated.
	my @portList = ();
	
	# This variable stores Last allocated instance number.
	my $lastInsNum;

	# This variable stores Last allocated port number.
	my $lastPortNum;

	# This variable stores size of array. 
	my $size;

	if (! copy( $servicefile, $tempServiceFile ) )
	{
		if ( open(ERROR_FILE, ">", $errorFile ) )
		{
			print ERROR_FILE "Error:PADUR_NOT_ABLE_TO_TAKE_BACKUP_OF_[_%S_]_FILE=$servicefile";	
			close(ERROR_FILE);
		}
		logMsg("Error:Not able to take backup of [ $servicefile ] to [ $tempServiceFile ] file.");
		$retVal = 1;
		goto EXIT;
	}

	if ( open(SER_FILE, "<", $tempServiceFile) )
	{	
		while (<SER_FILE>)
		{
			# Remove the new line character.
			chomp;
			if ($_ =~ /^horcm(\d+)(\s+)(\d+)(\S*)/)
			{
				@insList = (@insList, $1);
				@portList = (@portList, $3);
			}    	
		}
		close (SER_FILE);
	}
	else{
	
		if ( open(ERROR_FILE, ">", $errorFile ) )
		{
			print ERROR_FILE "Error:PADUR_NOT_ABLE_TO_OPEN_FILE_[_%S_]_FILE=$tempServiceFile";	
			close(ERROR_FILE);
		}
	
	}


	# change owner of file here.


	$size = @insList;
	#check if no instance yet created.
	if($size == 0)
	{
		# start instance no. from 700 and port no. from 7000
		$insNum = 700;
		$portNum =7000;
	}
	else{
		# sort array and find Last instance and port number
		@insList = sort { $a <=> $b } @insList;
		@portList = sort { $a <=> $b } @portList;
		print "sort instance number @insList\n";
		print "sort port number @portList\n";
	
		$lastInsNum = $insList[$#insList];
		$lastPortNum = $portList[ $#portList];
	
		logMsg("Info: Last int and port number are $lastInsNum $lastPortNum");
	
		$insNum = $lastInsNum + 1;
		$portNum = $lastPortNum + 1;
	}

	logMsg("Info: new instance numeber is $insNum and port num is $portNum");

	# Open tempServiceFile in appened mode

	if ( open(SER_FILE, ">>", $tempServiceFile) )
	{
		print SER_FILE "horcm$insNum  $portNum/udp\n";

		close(SER_FILE);
	}

	else{
		logMsg("Error:Not able to open [ $tempServiceFile ] file for appending instance number and port number.");
	
		if ( open(Error_FILE, ">", $errorFile) )
		{
			print Error_FILE "Error:PADUR_NOT_ABLE_TO_OPEN_FILE_[_%S_]_FILE=$tempServiceFile";
			close(Error_FILE);
		}
		$retVal = 1;
		goto EXIT;
	}

	# Rename tempServiceFile to seviceFile.
	if ( !rename($tempServiceFile, $servicefile) )
	{
		logMsg("Error:Not able to Rename [ $tempServiceFile ] to [$servicefile] file .");
	
		if ( open(Error_FILE, ">", $errorFile) )
		{	#TODO: Add key for this
			print Error_FILE "Error:Not able to Rename [ $tempServiceFile ] to [$servicefile] file .";
			close(Error_FILE);
		}
	
		$retVal = 1;
		goto EXIT;
	}
EXIT:
	return $retVal;
	
}

# This function is used to check existence of command device, this sub routine calls volinfo exe.
#
# @param :
# serialNumber
#
# @return :
#   0 if Success
#   1 if Error

sub checkCommandDevice
{
	# This variable is used to store the return value of function calls.
    my $retVal = 0;
	my $tempSerialno = "";
	#$serialNumber
	# call volinfo for 
	my $retvol = 0;
	$retvol =system("$svpFolder/list_devices > $tempFile");
	if($retvol)
	{
		logMsg("Error:Error in executing list_devices");
		$retVal =1;
		goto EXIT;
	}
	#parse $tempFile to see if command device of given serial number is present.
	
	if(open (CommandHandler, "<", $tempFile))
	{
		my @deviceList = <CommandHandler>;
		close(CommandHandler);
		
		unlink $tempFile or logMsg("Error:Not able to delete [ $tempFile ] file.");
		
		#check if command device of given serial number present or not 
		my $count = grep ( /(.+)HITACHI(.+)CM(.+)Serial Number:(\s*)$serialNumber/, @deviceList);
		
        if($count)
		{
			$retVal = 0;
			logMsg("Info: Serial number [$serialNumber] matched ,command device is present" );			
		}
		else
		{
			$retVal =1;
			logMsg("Error: Command Device of given serial number [$serialNumber] is not present.");
		}
	}
	else
	{
		logMsg("Error:Not able to open [ $tempFile ] file.");
		$retVal =1;
		goto EXIT;
	}
	
EXIT:
	return $retVal;
}	

# 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]  [SVP-<IP Address>] [Perf_Collection] <Log message>
#
# @param :
# $_[0] - Log message.
#
# @return :
#   Nothing
sub logMsg
{
    # This variable is used to store the complete path of probe specific folder in db/probe folder.
    my $svpLogFolder = "";

    # Set Eternus log folder path.
    $svpLogFolder = $baseFolder."/logs/SVPProbe";

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

    # 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 = $svpLogFolder."/SVPProbe.".$date.".log";

    # Open the log file.
   if ( open(LOG_HANDLE, ">>", $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." [SVP-".$svpIP."] [configureRaidcom] ".$logMsg."\n";

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

		close (LOG_HANDLE);
	}
}


