#! /usr/bin/perl -w

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

###############################################################################################
# What the program does?
#
# Given a list of hosts (names or IP addresses), this scripts runs on a central host and installs
# our linux probe software on specified target nodes with the help of install.pl script present
# on specified target node.
#
# Following are the command line options:
#
# -k <file>                  : SSH Key file for password less login.
# -c <Host IP>               : Probe appliance IP.
# -n                         : Add SSH Options.
# -l <User name>             : User name of target machine.
# -p <Password>              : Password of target machine.
# -d <Destination Directory> : Destination install directory.
# -i                         : Installation mode.
# -u                         : Un-installation mode.
# -r                         : Check prerequisites mode.
# -a                         : Start data collection mode.
# -b                         : Stop data collection mode.
# -s                         : Clean target machine's' installed directory mode.
# -j <sample interval>       : Sample interval.
# -y <transfer interval>     : Transfer Interval.
# -g <site name>             : Site name.
# -q <probe id>              : Probe ID.
################################################################################################

# Switch on warnings. It is used so that we can switch-off warning in Getopts() call.
$^W = 1;

# Flush output after every write. Unbuffered output is usually desired.
$| = 1;

# It is used for strict compilation.
use strict;

# Used for running commands on target machine.
use Net::OpenSSH;

# 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);

use IO::File;

use POSIX qw(tmpnam);

# Used to read script arguments.
use Getopt::Std;

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

# It is required for logging information.
use commonLogModule;

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

# This variable stores the probe type.
my $probeType = "Linux";

# This variable stores commonLogModule instance.
my $logObj = "";

# List of remote hosts where we need to install the software.
my @clients = undef;

# SSH object for remote communication.
my $ssh = undef;

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

# Installation directory.
my $installDir = undef;

# Temporary directory during installation.
my $installStaging = undef;

# Install script name on remote host.
my $ISCRIPT = undef;

# MARS Linux TAR file name.
my $TAR_NAME = "probe.tar";

# Host IP from where probes are deployed.
$::opt_c = undef;

# Add SSH Options.
$::opt_n = undef;

# User name.
$::opt_l = undef;

# Password.
$::opt_p = undef;

# Destination install directory.
$::opt_d = undef;

# Install mode.
$::opt_i = undef;

# Un-install mode.
$::opt_u = undef;

# Check prerequisites mode.
$::opt_r = undef;

# Start data collection mode.
$::opt_a = undef;

# Stop data collection mode.
$::opt_b = undef;

# Clean target machine's' installed directory mode.
$::opt_s = undef;

# Sample interval.
$::opt_j = undef;

# Transfer interval.
$::opt_y = undef;

# Probe ID.
$::opt_q = undef;

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

# Initialize the environment.
$status = init();
if (0 != $status)
{
    $logObj->error("Call to init() failed.");
    goto EXIT;
}

# Configure all target machines.
$status = configureClient();
if (0 != $status)
{
    $logObj->error("Call to configureClient() failed.");
    goto EXIT;
}

# Call main functionality.
$status = callTarget();
if (0 != $status)
{
    $logObj->error("Call to callTarget() failed.");
    goto EXIT;
}

EXIT:

exit $status;

# This function is used to initialize the environment to run the Perl script correctly.
#
# @affected global variables :
#    $::opt_l
#    $installDir
#    $installStaging
#    $ISCRIPT
#    @clients
#
# @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 base folder path on the MARS probe.
    my $baseFolder = "/usr/local/megha";
    
    # This variable stores Log folder path.
    my $logFolder = $baseFolder."/logs";

    # If log folder does not exist then create it.
    if (! (-e $logFolder))
    {
        mkpath($logFolder);
    }

    # Get commonLogModule instance.
    $logObj = commonLogModule->getInstance($logFolder, $probeType, "");
    
    # If first argument is not an option then it is an error.
    if (@ARGV && $ARGV[0] !~ "^-.+")
    {
        $logObj->error("First argument is not an option.");
        $retVal = 1;

        goto EXIT;
    }
    else
    {
        # Suppress annoying undef warnings.
        local($^W) = 0;

        # Get options.
        &getopts("nk:c:sl:iurabp:j:y:g:q:d:");
    }

    if (!defined($::opt_l) || !defined($::opt_p) || !defined($::opt_d) ||  !defined($::opt_c) ||
       !(defined($::opt_i) || defined($::opt_u) || defined($::opt_r) || defined($::opt_a) || defined($::opt_b) || defined($::opt_s)))
    {
        print("Usage: perl installOnRemoteHost.pl <nk:vtc:f:m:sl:iurabp:j:y:g:q:d:>");

        print("-k <file>                  : SSH Key file for password less login.\n");
        print("-c <Host IP>               : Probe appliance IP.\n");
        print("-n                         : Add SSH Options.\n");
        print("-l <User name>             : User name of target machine.\n");
        print("-p <Password>              : Password of target machine.\n");
        print("-d <Destination Directory> : Destination install directory.\n");
        print("-i                         : Installation mode.\n");
        print("-u                         : Un-installation mode.\n");
        print("-r                         : Check prerequisites mode.\n");
        print("-a                         : Start data collection mode.\n");
        print("-b                         : Stop data collection mode.\n");
        print("-s                         : Clean target machine's' installed directory mode.\n");
        print("-j                         : Sample interval.\n");
        print("-y                         : Transfer Interval.\n");
        print("-g                         : Site name.\n");
        print("-q                         : Probe ID.\n");

        die $logObj->error("Missing command line options.");
    }

    # If central-host is 'eth0', get our own eth0 IP address.
    if ($::opt_c eq 'eth0')
    {
        $::opt_c = &getIp('eth0');
        $::opt_c =~ s/"//g;
        $::opt_c =~ s/'//g;
    }
    
    if (defined($::opt_q))
    {
        # Update the final log message information with populated Linux probe id.
        $logObj->setProbeId($::opt_q);
    }
    
    # Remove " or ' from user name.
    $::opt_l  =~ s/"//g;
    $::opt_l  =~ s/'//g;

    $installDir = $::opt_d;

    $installStaging = $::opt_d."/probe/installer";

    $ISCRIPT = "install.pl";

    # Set list of remote hosts.
    @clients = @ARGV;

EXIT:

    return $retVal;
}

# This function does configuration of target machine like copying key on target machine.
#
# @affected global variables:
#   None
#
# @param:
#   None
#
# @return:
#   0 if Success
#   1 if Error
sub configureClient
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    # Do operation for every host.
    for my $host (@clients)
    {
        # Get an SSH object to do operation on target machine.
        $ssh = Net::OpenSSH->new($host, (user => $::opt_l, passwd => $::opt_p, master_opts => [-o => "StrictHostKeyChecking=no"]));
        if ($ssh->error)
        {
            # If error contains "bad password" that means password or user name is invalid.
            if (index($ssh->error, "bad password") != -1) 
            {
                print STDERR "Unable to connect.\n";
            } 
            
            die $logObj->error("Error connecting to [$host], error: [".$ssh->error."].");
        }

        # If -k was specified then copy public key on target machine.
        # TODO: Currently there is no facility to get key from User.
        if (! $::opt_k)
        {
            $logObj->error("Not setting SSH keys on target machine as -k option not provided.");
            goto EXIT;
        }

        if (! -r $::opt_k)
        {
            die $logObj->error("Can't read ssh public key file [".$::opt_k."], error: [$!].");
        }

        $logObj->info("Getting user home directory for target [$host].");

        # Get current home directory.
        my $homeDir = $ssh->capture({}, 'echo $HOME');

        if ($ssh->error)
        {
            die $logObj->error("Failed connect to [$host], error: [".$ssh->error."].");
        }

        chomp($homeDir);

        # Set home directory if not already set.
        if (!defined $homeDir || $homeDir eq "")
        {
            if ($::opt_l eq "root")
            {
                $logObj->info("Setting home directory as /root because no home directory specified.");
                $homeDir = "/root";
            }
            else
            {
                $logObj->info("Setting home directory to: [/home/".$::opt_l."].");
                $homeDir = "/home/".$::opt_l;
            }
        }

        # Create SSH folder on target machine.
        $logObj->info("Creating SSH folder on target: [$host] at location: [$homeDir].");
        my @cmd = ("mkdir -p $homeDir/.ssh", "chmod og-w $homeDir/.ssh");

        $logObj->info("Command to execute for target [$host] - [@cmd].");
        if (! defined($ssh->system({}, @cmd)))
        {
            die $logObj->error("Failed to create .ssh folder on target machine [$host], error: [".$ssh->error."]");
        }

        # Create key file and get the pipe handle.
        my ($rin, $pid) = $ssh->pipe_in("cat >>$homeDir/.ssh/authorized_keys") or $logObj->error("pipe_in method failed for target [$host], error: [".$ssh->error."]");

        # Access local key file content by running cat command.
        open(FH, "cat $::opt_k |");

        # Copy data from local file to remote file.
        pipe(FH, $rin);

        close($rin);
        close(FH);
    }

EXIT:

    return $retVal;
}

# This function call core functionality depending upon command line options.
#
# @param:
#     None
#
# @return :
#     0 if Success
#     1 if Error
sub callTarget
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    # Install operation.
    if ($::opt_i)
    {
        $retVal = install();
        if (0 != $status)
        {
            $logObj->error("Call to install() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }
    # Un-install operation.
    elsif ($::opt_u)
    {
        $retVal = uninstall();
        if (0 != $status)
        {
            $logObj->error("Call to uninstall() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }
    # Check pre-requisites.
    elsif ($::opt_r)
    {
        $retVal = checkPrerequisites();
        if (0 != $status)
        {
            $logObj->error("Call to checkPrerequisites() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }
    # Start data collection.
    elsif ($::opt_a)
    {
        $retVal = start();
        if (0 != $status)
        {
            $logObj->error("Call to start() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }
    # Stop data collection.
    elsif ($::opt_b)
    {
        $retVal = stop();
        if (0 != $status)
        {
            $logObj->error("Call to stop() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }
    # Clean installation directory on target machine.
    elsif ($::opt_s)
    {
        $retVal = cleanUpTarget();
        if (0 != $status)
        {
            $logObj->error("Call to cleanUpTarget() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }

EXIT:

    return $retVal;
}

# This function calls installer scripts method to start data collection on target machine.
#
# @param:
#    None
#
# @return:
#    0 if Success
#    1 if Error
sub start
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    my $cmd = "$installStaging/$ISCRIPT -a -d $installDir";

    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute [$cmd], error: [$err].");
        $retVal = 1;
    }

    return $retVal;
}

# This function calls installer scripts method to stop data collection on target machine.
#
# @param:
#    None.
#
# @return:
#    0 if Success
#    1 if Error
sub stop
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    my $cmd = "$installStaging/$ISCRIPT -b -d $installDir";

    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute [$cmd], error: [$err].");
        $retVal = 1;
    }

    return $retVal;
}

# This function calls installer scripts method to clean installation on target machine after deletion of probe.
#
# @param:
#    None.
#
# @return :
#    0 if Success
#    1 if Error
sub cleanUpTarget
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    my $cmd = "$installStaging/$ISCRIPT -u -d $installDir";

    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute [$cmd], error: [$err].");
        $retVal = 1;
    }

    return $retVal;
}

# This function copy installation script on target machine.
#
# @affected global variables:
#    None
#
# @param:
#    $_[0]: [IN} SSH object.
#
# @return:
#   0 if Success
#   1 if Error
#
sub install
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    #my $sourceHome = "/usr/local/megha/lib/linux";
    my $sourceHome = "/usr/local/megha/lib/linux";

    my $sourceDir = $sourceHome."/probe";

    # Modify runtime.properties first.
    my $configFile = $sourceDir."/conf/runtime.properties";

    # Edit configuration file to contain name of central host. Default interval is one seconds.
    my $interval = 1;

    # If collection interval is 1 second then number of samples will be 60.
    my $samples = 60;

    # Default cron execution frequency.
    my $mainCronFreq = 15;

    # Used to decide whether we got correct information or not.
    my $valsFetched = 0;

    my $iterations = undef;

    # Use input interval.
    if (defined($::opt_j))
    {
        $interval = $::opt_j;

        # If collection interval is 1 minute then number of samples will be 15.
        if ($interval == 60)
        {
            $samples = 15;
        }
    }

    # If Probe id is not provided then error.
    if (! defined($::opt_q))
    {
        die $logObj->error("Probe Id not provided.");
    }

    # If transfer interval is not provided then error.
    if (! defined($::opt_y))
    {
        die $logObj->error("Transfer interval not provided.");
    }

    # Open existing config file and a new config file.
    open(CF, '<', $configFile) || die $logObj->error("Can not read [$configFile], error: [$!].");
    open(CFNEW, '>', "$configFile.new") || die $logObj->error("Can not write to [$configFile.new], error: [$!].");

    $logObj->info("Updating runtime.properties.new with user provided values.");

    while (<CF>)
    {
        # Get value from current line (MAINCRONFREQ=15) and update in new file.
        if (/^\s*MAINCRONFREQ\s*=\s*(\d+)/)
        {
            $mainCronFreq = $1;
            $valsFetched++;
        }
        # Get value from current line (SAMPLES=60) and update in new file.
        elsif (/^\s*SAMPLES\s*=\s*(\S+)/)
        {
            s/%SAMPLES%/$samples/g;
        }
        # If line is INTERVAL=%INTERVAL% then replace %INTERVAL% with $interval.
        elsif (/^\s*INTERVAL\s*=\s*(\S+)/)
        {
            s/%INTERVAL%/$interval/g;
        }
        # If line is XFER=%XFER% then replace %XFER% with $::opt_y (transfer interval).
        elsif (/^\s*XFER\s*=\s*(\S+)/)
        {
            s/%XFER%/$::opt_y/g;
        }
        # If line is DESTHOST="%DESTHOST%" then replace %DESTHOST% with $::opt_c (probe appliance).
        elsif (/^\s*DESTHOST\s*=\s*(\S+)/)
        {
            s/%DESTHOST%/$::opt_c/g;
        }
        # If line is PROBEID=%PROBEID% then replace %PROBEID% with $::opt_q (probe id).
        elsif (/^\s*PROBEID\s*=\s*(\S+)/)
        {
            s/%PROBEID%/$::opt_q/g;
        }
        # If line is ITERATIONS=%ITERATIONS% then replace %ITERATIONS% with $iterations if defined.
        elsif (/^\s*ITERATIONS\s*=\s*(\S+)/)
        {
            if (defined($iterations))
            {
                s/%ITERATIONS%/$iterations/g;
            }
            else
            {
                die $logObj->error("Number of iterations not defined.");
            }
        }
        else
        {
            # Skip the line.
        }

        # Calculate iterations and update it in runtime.properties.new.
        if ($valsFetched == 1)
        {
            $iterations = ($mainCronFreq * 60) / ($interval * $samples);

            $logObj->info("Iterations: [$iterations], CRON Freq: [$mainCronFreq], Interval: [$interval], Samples: [$samples].");
            $valsFetched = 0;
        }

        # Printing modified line in new runtime.
        print CFNEW $_;
    }

    if (! close(CFNEW))
    {
        die $logObj->error("Failed to close [$configFile.new], error: [$!].");
    }

    $logObj->info("File [runtime.properties.new] updated successfully.");

    # Create TAR file name for local and remote machine.

    my $cmd = "";

    my $tarLocal = $sourceHome."/".$TAR_NAME;
    my $tarRemote = $installDir."/".$TAR_NAME;

    # Delete existing tar file on local machine.
    if (-e $tarLocal)
    {
        unlink($tarLocal);
    }

    chdir($sourceHome);

    # Create installation tar.
    $cmd = "tar cf $tarLocal probe/";

    $logObj->info("Executing command [$cmd].");

    my $exitCode = system($cmd);
    if (0 != $exitCode)
    {
        $logObj->error("Can not create installation tar.");
        $retVal = 1;

        goto EXIT;
    }

    # Cross check whether we are able to see local tar file.
    if (! -e $tarLocal)
    {
        $logObj->error("Can not find ['$tarLocal'], error: [$!].");
        $retVal = 1;

        goto EXIT;
    }

    # Copy tar file on remote machine.
    # NOTE: scp may prompt user for password or key.
    if (! $ssh->scp_put({}, $tarLocal, $installDir))
    {
        die $logObj->error("Failed while uploading software on remote host, error: [".$ssh->error."].");
    }

    # Un-tar tar file on remote machine.
    $cmd = "tar xf $tarRemote -C $installDir";

    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Can not extract [$tarRemote] on remote machine, error: [$err], STDOUT: [$out].");
        $retVal = 1;

        goto EXIT;
    }

    # Update permission of installation script.
    $cmd = "chmod a+x $installStaging/$ISCRIPT";

    $logObj->info("Executing command [$cmd].");

    ($out,$err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Can not change permission of install script on remote machine, error: [$err]. STDOUT: [$out].");
        $retVal = 1;

        goto EXIT;
    }

    # Delete tar file from remote machine.
    $cmd = "rm -f $tarRemote";

    $logObj->info("Executing command [$cmd].");

    ($out,$err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Can not remove [$tarRemote], error: [$err], STDOUT [$out].");

        $retVal = 1;

        goto EXIT;
    }

    # Command to install software on remote machine.
    $cmd = "$installStaging/$ISCRIPT -i -d $installDir";

    $logObj->info("Executing command [$cmd].");

    # Install software on remote machine.
    ($out, $err) = $ssh->capture2({}, "$cmd");

    # If there is an error while uploading software then uninstall.
    if ($err)
    {
        $logObj->error("Uninstalling setup as found error installing linux probe on remote machine, error: [$err], STDOUT: [$out].");

        # Command to un-install software on remote machine.
        $cmd = "$installStaging/$ISCRIPT -u -d $installDir";

        $logObj->info("Executing command [$cmd].");

        ($out, $err) = $ssh->system({}, "$cmd") or warn $logObj->error("Can not revert back changes after unsuccessfull install, error: [$err], STDOUT: [$out].");
        $retVal = 1;

        goto EXIT;
    }

EXIT:

    return $retVal;
}

# This function un-install linux probe software from remote machine.
#
# @param:
#    $_[0]: [IN] SSH object.
#
# @return :
#    0 if Success
#    1 if Error
sub uninstall
{
    # Store return value of this function.
    my $retVal = 0;

    my $cmd = "$installStaging/$ISCRIPT -u -d $installDir";

    $logObj->info("Executing command [$cmd].");

    # Un-install software on remote machine.
    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute [$cmd], error: [".$ssh->error."]");
        $retVal = 1;
    }

    return $retVal;
}

# This function check following on target machine.
# 1). Is installDir is present or not.
# 2). Is installation directory is empty or not. If installDir is not empty then it will quit.
# 3). Is there sufficient permission for that directory available or not.
#
# @param:
#    None.
#
# @return:
#    0 if Success
#    1 if Error
sub checkPrerequisites
{
    # Store return value of this function.
    my $retVal = 0;

    # Check whether install directory is exist or not.
    my $cmd = "ls $installDir".' | wc -l';
    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->capture2({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute command [$cmd]. Error: [$err].");
        print STDERR "Listing failed.\n";

        $retVal = 1;
        goto EXIT;
    }

    $out = trim($out);

    # If install directory is not empty then return.
    if ($out ne "0")
    {
        $logObj->error("Installation directory [$installDir] is not empty.");
        print STDERR "Directory is not empty.\n";

        $retVal = 1;
        goto EXIT;
    }

    # Check installation directory permission.
    $cmd = "ls -ld $installDir".' | awk \'{print $1}\'';
    $logObj->info("Executing command [$cmd].");

    ($out, $err) = $ssh->capture2({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute command [$cmd], error: [$err].");
        print STDERR "$err\n";

        $retVal = 1;
        goto EXIT;
    }

    $out = trim($out);

    # If folder permission doesn't start with drwx then in-sufficient permission.
    if ($out !~ m!^drwx!)
    {
        $logObj->error("Installation directory [$installDir] does not have sufficient permissions.");
        print STDERR "Permissions issue.\n";

        $retVal = 1;
        goto EXIT;
    }

    # Check whether Perl module exist or not.
    $cmd = "perl -v" ;
    $logObj->info("Executing command [$cmd].");

    ($out, $err) = $ssh->capture2({}, $cmd);

    if ($err)
    {
        $logObj->error("perl not found.");
        print STDERR "perl not found.\n";

        $retVal = 1;
        goto EXIT;
    }

    # Check sysstat.
    foreach ("iostat", "mpstat", "vmstat")
    {
        ($out, $err) = $ssh->capture2({}, "$_");
        if ($err)
        {
            $logObj->error("Module [$_] not found.");
            print STDERR "Sysstat not found.\n";

            $retVal = 1;
            goto EXIT;
        }
    }

    # Check network info.
    $cmd = 'cat /proc/net/dev';
    $logObj->info("Executing command [$cmd].");

    ($out, $err) = $ssh->capture2({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute command [$cmd].");
        print STDERR "/proc/net/dev not found.\n";

        $retVal = 1;
        goto EXIT;
    }

EXIT:

    return $retVal;
}

# Remove white space character from beginning and end of a string.
#
# @param:
#    $_[0]: [IN] Input string.
#
# @return:
#    String without white space character at beginning and end.
sub trim
{
    my $str = shift;

    $str =~ s!^\s+!!g;
    $str =~ s!\s+$!!g;

    return $str;
}

# Get IP address of specified interface. If not found then return any IP address which is not loopback IP.
#
# @param:
#    $_[0]: [IN] Interface name.
#
# @return:
#    IP address in success case. "no-ip-found-for-eth0" string in failure case.
sub getIp
{
    my $interfaceName = @_;

    # Try to get IP address associated with a network interface.
    my $ip = `/sbin/ifconfig $interfaceName | sed 1d`;

    # If we get IP address for specified interface then return.
    if ($ip =~ /addr:(\d+\.\d+\.\d+\.\d+)/)
    {
        return $1;
    }
    # If we are not able to find IP address for given interface then find any IP address which is not loopback IP.
    else
    {
        $ip = getNonLoopBackIp();
        chomp($ip);

        return $ip;
    }

    return 'no-ip-found-for-eth0';
}

# This function is used to get the Loopback IP address
#
# @param:
#     None
#
# @return:
#     Loopback IP address list.
sub getLoopBackIps
{
    # Get ifconfig output.
    my $ifconfig = `/sbin/ifconfig -a`;

    # Used to decide the line which contain IP address.
    my $readNextLine = 0;

    # This list is used to store complete line having IP address.
    my @outArray = ();

    # Output list which contain Loopback IP address.
    my @outIpList = ();

    # Split output for each line.
    for (split(/\n/, $ifconfig))
    {
        # If current line is empty then go to next line.
        if (/^$/)
        {
            next;
        }

        # If current line doesn't have "Loopback" string or exact "inet" string (no "inet6") then go to next line.
        if ($_ !~ /Loopback|\binet\b/)
        {
            next;
        }

        # This line has IP address.
        if ($readNextLine == 1)
        {
            push(@outArray, $_);
        }

        # If current line has "Loopback" string then means that next line will have IP address.
        if (~/Loopback$/)
        {
            $readNextLine = 1;
        }
        # If current line has no "Loopback" string then means that we already found IP address.
        else
        {
            $readNextLine = 0;
        }
    }

    # Use only those lines where "inet" is at the start of line.
    my @ipArray = grep {/^\s*inet/} @outArray;

    foreach (@ipArray)
    {
        # Find IP address and push in output list.
        if (m/addr:(.*)\s+M/)
        {
            push(@outIpList, $1);
        }
    }

    return @outIpList;
}

# This function is used to return an IP address which is not a loopback IP.
#
# @param
#    None
#
# @return
#    IP address which is not loopback IP.
sub getNonLoopBackIp
{
    # Get IP address list as a single string.
    my $ifconf = `/sbin/ifconfig | sed -n '/.*inet .*dr:/{;s/.*dr://;s/ .*//;p;}'`;

    # Get IP addresses in a list.
    my @ips = split(/\n/, $ifconf);

    # Get the list of loopback IPs.
    my @loopBackIps = getLoopBackIps();

    # Loop for all IPs.
    for (my $i = 0; $i <= $#ips; $i++)
    {
        # Loop for all loopback IPs.
        foreach my $loopBackaddr (@loopBackIps)
        {
            # If we found any IP which is not a loopback IP then return that IP.
            if ($ips[$i] ne $loopBackaddr)
            {
                return $ips[$i];
            }
        }
    }

    return undef;
}