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

# This file provide common functions for CLI based probes.

# Name of the Package.
package commonPerlUtil;

# It is used for strict compilation.
use strict;

# It helps in debugging.
use warnings;

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

# This module provide functions that returns newly created HTTP::Request objects.
use HTTP::Request::Common qw(POST);

# It is used to dispatch web requests.
use LWP::UserAgent;

# This is required for logging information.
use commonLogModule;

# This is used for fetching value from property file.
use commonPropFileModule;

# This stores an instance of commonPerlUtil class.
my $commPerlUtil = undef;

# --------------------------------------------------------------------------------------------------------------------------------------------------
# Sub-routines
# --------------------------------------------------------------------------------------------------------------------------------------------------
# Constructor of class commonPerlUtil class.
#
# @param :
#   $_[0] - [In] Class name.
#   $_[1] - [In] Base folder for logging.
#   $_[2] - [In] Probe Type like NetApp.
#   $_[3] - [In] Probe Id "192.168.20.100".
#   $_[4] - [In] Instance of commonLogModule.
#
# @return :
#   Instance of commonPerlUtil class.
sub getInstance {
    # This variable stores package name.
    my $class = $_[0];

    # This variable stores base folder in which we generate logs.
    my $baseFolder = $_[1];

    # This variable stores probe type.
    my $probeType = $_[2];

    # This variable stores probe id.
    my $probeId = $_[3];

    # Get commonLogModule instance.
    my $logObject = $_[4];

    # Create class instance.
    $commPerlUtil = bless {
        probeType => $probeType,
        probeId => $probeId,
        logObj => $logObject,
        baseFolder => $baseFolder,
    }, shift unless $commPerlUtil;

    return $commPerlUtil;
}

# This function is used to get the plain data corresponding to the data stored in Probe instance properties file.
#
# @param :
#   $_[0] - [In] Object Reference.
#   $_[1] - [In] Data.
#   $_[2] - [Out] Plain Data.
#
# @return :
#   0 if Success
#   1 if Error
sub getDataValue {
    # This variable is used to store the return code for this function.
    my $retVal = 0;

    # This variable is used to store the URL which is used to get the plain data.
    my $probeURL = "";

    # This variable stores the web server mode.
    my $serverMode = "https";

    # This variable stores probe type.
    my $probeType = lc($_[0]->{probeType});

    # This variable stores probe id.
    my $probeId = $_[0]->{probeId};

    # This variable stores the data stored in probe instance property file.
    my $data = $_[1];

    # This is used in creating the URL to get plain data.
    my $appId = "_".$probeType."perlmdl!23_".$_[0]->{probeType};

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

    # Get commonPropFileModule object.
    my $commPropFileObj = commonPropFileModule->getInstance($_[0]->{baseFolder}, $_[0]->{probeType}, $_[0]->{probeId}, $_[0]->{logObj});

    $retVal = $commPropFileObj->getAppProperty("SERVER.WEB.PROTOCOL", $serverMode);
    if (0 != $retVal) {
        # If we are not able to get web protocol then continue with using "https" protocol.
        $_[0]->{logObj}->warn("Call to getAppProperty() failed for property [SERVER.WEB.PROTOCOL].");

        $serverMode = "https";
    }

    # Create appropriate URL as per Web Server Mode.
    if ("https" eq $serverMode) {
        $probeURL = $serverMode."://localhost:8443/appUtilServlet.do?";
    } elsif ("http" eq $serverMode) {
        $probeURL = $serverMode."://localhost:8080/appUtilServlet.do?";
    } else {
        $_[0]->{logObj}->error("In-appropriate Server Web Protocol found. Value: [$serverMode].");

        $retVal = 1;
        goto EXIT;
    }

    # Send an HTTP POST request for getting plain data value.
    my $request = POST($probeURL ,[a => 'getPlainData', appId =>$currentTime.$appId, data=>$data]);
    my $ua = LWP::UserAgent->new(
       ssl_opts => { verify_hostname => 0 },
    );

    # The response will either have a plain data value or some error message.
    my $response = $ua->request($request);

    if ($response->is_success) {
        # Get the response in form of an XML.
        my $content = $response->decoded_content;

        if ($content =~ /^<Response(.+)><Message><!\[CDATA\[(\s*)(.+)\]:(.+)<\/u><\/p>\]\]><\/Message><\/Response>$/) {
            # If there is some error then it will be displayed in "Message" tag after ":".

            my $message = $4;
            $_[0]->{logObj}->error("Failed to get plain data. Msg: [$message].");

            $retVal = 1;
            goto EXIT;
        } elsif ($content =~ /^<Response(.+)>(\w*)<\/Response>$/) {
            # The plain data will be displayed in "Response" tag.

            $_[2] = $2;

            goto EXIT;
        }
    } else {
        $_[0]->{logObj}->error("Error occured while processing the web request: [".$response->status_line."].");
        $retVal = 1;
    }

EXIT:

    return $retVal;
}

# This function is used to run a command through system() call. After running the command, it gets actual child process exit status and return it.
#
# @param :
#   $_[0] - [In] Object Reference.
#   $_[1] - [In] Command to execute.
#
# @affected global variables :
#   None
#
# @return :
#   Exit code shifted to 8 bits right.
sub executeSystemCmd {
    my $tempExitCode = system($_[1]);

    my $exitCode = WEXITSTATUS($tempExitCode);

    return $exitCode;
}

# This function is used to run a command through system() call and dump the output in given file. After running the command, it gets actual child
# process exit status and return it.
#
# @param :
#   $_[0] - [In] Object Reference.
#   $_[1] - [In] Command to execute.
#   $_[2] - [In] File open mode. It could be either ">>" or ">".
#   $_[3] - [In] Output file name where output of the command will be dumped.
#
# @affected global variables :
#   None
#
# @return :
#   Exit code shifted to 8 bits right.
sub executeSystemCmdEx {
    # This variable stores the complete command.
    my $command = "";

    # This variable stores mode in which the command output needs to be dumped.
    my $mode = $_[2];

    # If invalid mode provide then use append mode.
    if (! ((">>" eq $mode) || (">" eq $mode))) {
        $mode = ">>";
    }

    $command = $_[1]." ".$mode." ".$_[3];

    my $tempExitCode = system($command);

    my $exitCode = WEXITSTATUS($tempExitCode);

    return $exitCode;
}

# The file must return true ("1") as the last statement to indicate successful execution of any initialization code. So it's customary to end such a
# file with 1 unless we are sure that we will return true otherwise. But it's better just to put the 1;, in case we add more statements.
1;