#!/usr/bin/perl -w
###################################################################
# (C) Copyright 2012-2015 Hewlett-Packard Development Company, L.P.
# @(#) Serviceguard cluster node setup command. 
# @(#) Product Name    : HP Serviceguard 
# @(#) Product Version : A.12.10.00 
# @(#) Patch Name      : 
#
# *** Note: This file MUST NOT be edited. *****
#
# Any changes made to it will be overwritten when you upgrade to the
# next release of HP Serviceguard.
#
# Changing this file may lead to unrecoverable
# cluster failure.
#
###################################################################

use strict;
use warnings;
use Getopt::Long qw(GetOptions HelpMessage :config no_ignore_case);
use Pod::Usage;
#------------------------------------------------------------------------------
#
# Serviceguard cluster node setup command.
#
# Description :
# This script eases the process of setting up the servers participating
# in the cluster so that user can proceed with configuring Serviceguard.
# This script is responsible for checking the availability of ports used
# by Serviceguard, starting of the xinetd service, updating of specific files
# and the setting up of the firewall.
# This script requires ssh to be setup such that the super user on every server 
# participating in the cluster can communicate with each other without password
# authentication.
# This script needs to be executed only once on any one of the nodes 
# participating in the cluster.
# cmeasyinstall should be executed on all nodes before this script is executed. 
# 
#------------------------------------------------------------------------------
#
# Steps for ssh setup:
# 1: ssh-keygen -t rsa  (press enter for all questions)
# 2: scp /root/.ssh/id_rsa.pub root@<othernode>:/root/.ssh/authorized_keys2
# 3: ssh root@<othernode> 'cat /root/.ssh/authorized_keys2 >> /root/.ssh/
#    authorized_keys'
#    Repeat Steps 2-3 for all hosts that need to communicate with each other 
#    without password.
# 4: ssh-agent $BASH
# 5: ssh-add
#------------------------------------------------------------------------------

#Globals

=head1 NAME

cmpreparecl

=cut

# arguments
# -n node_name [-u non_root_user]...[-q quorum_server]...[-h/-help]
# example :
# cmpreparecl -n node1 -n node2
# cmpreparecl -n node1 -n node2 -q quorum_server -u user1

=head1 SYNOPSIS

cmpreparecl
S<-n node_name...>
S<[-u non_root_user...]>
S<[-q quorum_server [qs_ip]]>
S<[-p]>
S<[-h/-help]>

=cut

=head1 DESCRIPTION

Easy configuartion/setup of nodes participating in a cluster.

=head1 OPTIONS

=item -n node_name

Name of the node on which configuration needs to be done.

=item -q quorum

Name of the quorum server that will be used while creating cluster.

=item -u non_root_user

Non root user who has access to cluster

=item -p 

Check if the pre-requisites needed to run the script are satisfied.

=item help

Displays help

=cut

#Open log file 

open(LOG_FILE, ">>/tmp/cmpreparecl.log") or
die "Cannot open the log file for updating status of the script.\n";

#Globals

my %ipHostPair;    # holds the hostname - IP address pairs
my %NstatHash;     # holds port-service pair data from netstat o/p
my %SGPortHash = (     # holds all documented port-service pair data
    # These are the documented ports/port-type/service details
    "8/icmp"    =>   "icmp",
    "113/tcp"   =>  "auth",
    "113/udp"   =>  "auth",
    "161/udp"   =>  "snmp",
    "162/udp"   =>  "snmptrap",
    "1238/tcp"  =>  "hacl-qs",
    "5300/tcp"  =>  "hacl-hb",
    "5300/udp"  =>  "hacl-hb",
    "5302/tcp"  =>  "hacl-cfg",
    "5302/udp"  =>  "hacl-cfg",
    "5304/tcp"  =>  "hacl-local",
    "5304/udp"  =>  "hacl-local",
    "5315/udp"  =>  "hacl-poll",
    "5988/tcp"  =>  "wbem-http",
    "5989/tcp"  =>  "wbem-https",
    # The following ports are required for connecting to SMH for SGMgr
    "2381/tcp"  =>  "compaq-https",
    "2381/udp"  =>  "compaq-https",
    "2301/tcp"  =>  "cpq-wbem",
    "2301/udp"  =>  "cpq-wbem"
);

my $DistroName, my $DistroVers; # DistroName = RHEL|SLES, DistroVers = version
my $SGVersion;           # Serviceguard Version
my $EXIT = 1;            # Exit code to abort execution on occurence of error.
my $ERROR = 2;           # Error code to notify on occurence of error.
my $WARNING = 3;         # Warning code to notify on occurence of warning.
my $SKIPPED = 1;
my $PASS = 0;
my $Stage = 0;           # keeps track of the completed part of the script.
my $prereqCount = 0;        # Keeps track of the pre-requisite checks done.
my $no_of_errors = 0;           # Keeps track of the number of errors
my $no_of_warnings = 0;         # Keeps track of the number of warnings
my $IpRule = "";         # Will be used to generate firewall rules.
my $LogPathMsg = "\n\tThe logfile is located at /tmp/cmpreparecl.log\n";
$LogPathMsg .= "\tPlease note that latest logs are appended to the log file.\n";
my $fileNotFound = "ERROR: Could not read the file";
my $arg_errors = 0;
my $ScriptName;
my %fullnameofhost; 
my %fullnameofquorum;    
my $hostcount = 0;
my @hostList;
my @nonroot;
my $remoteFile = "/tmp/cmpreparecl_remote.txt";
my $logFile = "/tmp/cmpreparecl.log";
my @quorum;       
my $is_quorum = 0;
my %qsHostPair;
my $errorFile = "/tmp/cmpreparecl_err";
my $num_elements = 9;
my @error_summary = ($SKIPPED) x ($num_elements);
if (-e $remoteFile) {
    $error_summary[$num_elements - 1] = $PASS;
}
my $num_prereq = 4;
my @error_prereq = ($SKIPPED) x ($num_prereq);
my @error_text;
my @prereq_text;
my %all_errors;
my %all_skipps;
my %node_skip;
my $dash = "-" x 70;
my $found = 0;
my $timeStamp = localtime time;
my $check_pre_req = 0;
my $update_host = 0;
my @output_summary = ();    # To store the summary of the command output
# Get the short host name
my $shortHostName = `hostname`; 
my @output = split('\.', $shortHostName);
$shortHostName = $output[0];
chomp($shortHostName);

my $ssh_cmd = "/usr/bin/ssh -q -o NumberOfPasswordPrompts=0";
my $SGSBIN;
my $Sgconf;
my $auth_type;
my $srv; 
my $quorum_installed = 0;
my $errorFlag = 0;
my $ssh_log_file = "/tmp/cmsshsetup.log";
my $firewall_disabled = 0;
#-------------------------------------------------------------------------------
# Populating @error_text with the operation cmpreparecl does in order
my $text = "1:  Check ports availability for Serviceguard and";
$text .= " Serviceguard Manager\n";
push(@error_text, $text);
push(@error_text, "2:  Start xinetd service.\n");
push(@error_text, "3:  Update Serviceguard manpage path.\n");
push(@error_text, "4:  Update name resolution in /etc/hosts file.\n");
push(@error_text, "5:  Validate host entry in /etc/nsswitch.conf file.\n");
push(@error_text, "6:  Update  hosttags in /etc/lvm/lvm.conf file.\n");
push(@error_text, "7:  Generate firewall rules for Serviceguard communications.\n");
push(@error_text, "8:  Setup firewall - Adding the generated firewall rules to \n\t".
                  "the existing firewall for Serviceguard to communicate (if enabled).\n");
push(@error_text, "9: Create cmclnodelist.\n");
#--------------------------------------------------------------------------------

GetOptions(

    'n=s' => sub { non_dash_arg(@_, \@hostList) },
    'u=s' => sub { non_dash_arg(@_, \@nonroot) },
    'q=s{,2}' => sub { non_dash_arg(@_, \@quorum)},
    'p'   => sub { non_dash_arg(@_, \$check_pre_req)},
    'H'   => sub { non_dash_arg(@_, \$update_host)},
    "h|help|?" => sub { pod2usage(-verbose => 1) } 

) ||  pod2usage();

#This will not allow more than one value with "-n/-u" options and only
#for "-q" option it will allow to pass upto 2 values.
if (@ARGV) {
    pod2usage("ERROR: Extra text on command line: \"" . join(" ", @ARGV) . "\"");
}

if ($arg_errors) {
    exit 1;
}

if ($hostcount == 0) {
    pod2usage("Atleast one hostname is required to run the script\n");
    $found = 1;
}

# look for number of IP address for quorum server as a maximum of two
# IP addresses are supported for the quorum server.
# "-q" option can be supplied only one times.
if (scalar(@quorum) > 2) {
    pod2usage("ERROR: Option -q should not be supplied more than once.\n");
}

if (!-e $remoteFile) {
    #Initializing node_skip value with the host list
    %node_skip = map { $_ => 1 } @hostList;
    &parameter_checks();
}

#-------------------------------------------------------------------------------
#Populating the pre-requisite check summary text
++$prereqCount;
push(@prereq_text, $prereqCount++.":  Pinging cluster node(s) to check the reachability");
if (@quorum) {
    push(@prereq_text, $prereqCount++.":  Pinging quorum server to check the reachability");
    $num_prereq++;
    push(@error_prereq, $SKIPPED);
}
my $num_nodes = scalar(@hostList);
if ($num_nodes > 1) {
    push(@prereq_text, $prereqCount++.":  Checking for password less ssh setup between nodes");
    $num_prereq++;
    push(@error_prereq, $SKIPPED);
}
push(@prereq_text, $prereqCount++.":  Checking if Serviceguard is installed");
push(@prereq_text, $prereqCount++.":  Checking if node is not already a part of any cluster");
push(@prereq_text, $prereqCount++.":  Checking if all nodes have the same ".
                                      "distribution of operating system");
$prereqCount = 0;
#-------------------------------------------------------------------------------
#Get the script name and the script path if not passed as arguments.
if($0 =~ m(([\w\d_\/\.]*)(\/)([a-zA-Z0-9-_\.]+))) {
    $ScriptName = $3;
}

# Get the distro
if (&getDistroInfo()) {
    # modify closeLogAndExit to not print Summary if -p is passed.
    &closeLogAndExit();
}
if ($DistroName eq "RHEL") {
    $SGSBIN = "/usr/local/cmcluster/bin";
    $Sgconf = "/usr/local/cmcluster/conf";
    $auth_type = "auth";
    $srv = "xinetd";
} else {
    $SGSBIN = "/opt/cmcluster/bin";
    $Sgconf = "/opt/cmcluster/conf";
    $auth_type = "";
    $srv = "identd";
    %SGPortHash = ("113/tcp", "ident");
    $error_text[1] = "2:  Start identd service\n";
}

# If update host option set, call updateHostsFile
if ($update_host) {
    # Update the IPs and FQDN
    $is_quorum = 0;
    &getIps(\@hostList, $is_quorum);
    # Update the /etc/hosts file
    &updateHostsFile;

    foreach my $node (@hostList) {
    next if ($node eq $shortHostName);
        my $cmcp_cmd = "$SGSBIN/cmcp /etc/hosts $node:/etc/hosts";
        (my $ret, my $op) = runandCaptureCommand($cmcp_cmd);
        if ($ret eq 0 ) {
            &printLog("Updated /etc/hosts file is copied successfully to $node node \n");
        } else {
            &printLog("Copying of /etc/hosts to $node is failed \n");
        }
    }
    exit 0;
}

# ssh setup
if (! -e $remoteFile) {  
    &printLog($dash."\n");
    &printLog("Starting ssh setup between nodes\n");
    &printLog($dash."\n");
    &printLog("If prompted, enter the password and/or add fingerprint to do passwordless ssh setup\n");
    my $nodes = join(' -n ', @hostList);
    my $cmd = "$SGSBIN/cmsshsetup -n $nodes -l $ssh_log_file > $errorFile 2>&1";
    (my $ret) = runandCaptureCommand($cmd);
    my @op = `cat $errorFile`;
    my $op = join("\t", @op);
    my $ssh_error = 0;
    if ($ret eq 0) {
        if ($op =~ /Failure/ || $op =~ /Error/) {
            $ssh_error = 1;
        } else {
            $nodes = join(', ', @hostList);
            &printLog("ssh setup between the nodes\n\t$nodes\n\tis completed successfully\n");
        }
    } else {
        $ssh_error = 1;
    }
    if ($ssh_error) {
        &printLog("The following error occured while running cmsshsetup\n");
        &printLog($op."\n");
        &printLog("For more details, Look into the log file at $ssh_log_file\n");
        &printLog($dash."\n");
        &cleanupAuthKeys();
        exit 1;
    }
    &printLog("For more details on ssh setup, Please refer to log file at $ssh_log_file\n");
    
}

# Check the pre-requisites
my $errorText = "ERROR: Pre requisites required to run the script ".
                "are not satisfied.\n\tCheck the file $logFile to determine ".
                "the problem, fix it and\n\tre-run the script.\n";
my $pre_req_error = 0;	

my $RetVal = 0;

# Pre-requisite check needs to be done in two cases :
# 1) From the local node where the script is invoked
# 2) When the local node invokes a pre-erquisite check on the remote node.
my $is_prereq_gbl = 0;
if ((-e $remoteFile && $check_pre_req) || (!-e $remoteFile)) {
    if (my $ret = pre_req_check()) {
        if ($ret eq 2) {
            &cleanupAuthKeys();
            exit 1;
        } 
        $pre_req_error++;
    }
    my $summary = join('#',@error_prereq);
    if (-e $remoteFile) {
       print $summary; 
    } else {
        $is_prereq_gbl = 1;
        &consolidateErrors($summary, $shortHostName, $is_prereq_gbl);
    }
}

if (!-e $remoteFile) {
    foreach my $node (@hostList) {
        next if ($node eq $shortHostName);
        # We call the remote node script with -p option. Pre-requisites
        # are checked even if the user has not passed -p.
        $check_pre_req++;
        if (($RetVal = &remoteExecutions($ipHostPair{$node},
                       $node)) != 0) {
            $pre_req_error++;
        }
        $check_pre_req--;
    }
    $is_prereq_gbl = 1;
    &generateSummary($is_prereq_gbl);
    # Exit from the script if -p option is passed or if
    # there are any errors.
    if ($pre_req_error) { 
        # If error occurs in pre-requisites, print the summary and exit.
        # Summary is printed only when -p is not used.
        &printOutputSummary();
        &printLog("$errorText\n");
        &cleanupAuthKeys();
        exit 1;
    } elsif($check_pre_req && !$pre_req_error) {
        cleanup_all_nodes();
        &printOutputSummary();
        &cleanupAuthKeys();
        exit 0;
    }
    %node_skip = map { $_ => 1 } @hostList;
    %all_errors = ();
    %all_skipps = ();
} else {
    &cleanupAuthKeys();
    if ($pre_req_error) {
        exit 1;
    } elsif($check_pre_req) {
        exit 0;
    }
}

# Begin with the configuration now.

&printLog("$dash\n");
&printLog("Configuration on $shortHostName [ $timeStamp ]\n");
&printLog("$dash\n");
$is_quorum = 0;
&getIps(\@hostList, $is_quorum);
if (@quorum) {
    $is_quorum = 1;
    &getIps(\@quorum, $is_quorum);
}
my $CurrentHost = $fullnameofhost{$shortHostName};
my $cmd = "rpm -qa | grep 'qs-' 2> $errorFile";
(my $ret) = runandCaptureCommand($cmd);
if ($ret eq 0) {
    $quorum_installed = 1;
}

# SCRIPT-MAIN

# Call the main function
&main();

&cleanup(); 

close(LOG_FILE);

# SUBROUTINES
#------------------------------------------------------------------------------
#
# Subroutine    : createRemoteFile()
# Calls         : runandCaptureCommand()
# Called by     : pre_req_check()
# Globals       : shortHostName, hostList
# Input Params  : -
# Return Value  : -  
#
# This function touches a file $remoteFile on the remote node.
# This is to distinguish between the operations that need to be performed
# on the local node and the remote node.
#------------------------------------------------------------------------------

sub createRemoteFile {

    foreach my $node (@hostList) {
        if ($node ne $shortHostName) {
            # The local node creates a temporary file on the other remote nodes
            # passed as input to identify the remote node during the script
            # execution.
            my $cmd = "$ssh_cmd "."root\@$ipHostPair{$node} ".
            "'/bin/touch  $remoteFile'  2> $errorFile";
            (my $ret, my $op) = runandCaptureCommand($cmd);
            if ($ret ne 0) {
                &printLog("ERROR: Unable access node $node. Make sure ".
                "that ssh is setup\n\tproperly between all the nodes".
                " on which cmpreparecl is being run.\n", $EXIT);
            }
        }
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : runandCaptureCommand()
# Calls         : open system call
# Called by     : pre_req_check()
# Globals       : -
# Input Params  : The command to be executed
# Return Value  : returns the exitcode and output of command execution.
#
# This function opens the ommand in a pipe and returns the output and 
# exit code of the command to the calling function.
#
#------------------------------------------------------------------------------

sub runandCaptureCommand {

    my ($myCmd) = @_;
    my @lines = ();
    my @output = ();

    open(CMD, "$myCmd |") or return (1 , @output);
    while (<CMD>) {
        push(@output, $_);
        last if eof(CMD);
    }
    close(CMD);
    my $exitcode = ($? >> 8);
    return($exitcode, @output);
}
#------------------------------------------------------------------------------
#
# Subroutine    : parameter_checks()
# Calls         : verify_duplicate_entries(),closeLogAndExit()
# Called by     : pre_req_check()
# Globals       : hostList, shortHostName, quorum, @nonroot.
# Input Params  : -
# Return Value  : returns the exitcode and output of command execution.
#
# This function checks the following : 
# 1. If the hostname and the quorum server nams are passed without domain name.
# 2. If a valid non-root user is passed.
# 3. if the script is run from one of the nodes passed as input to the script.
# 4. Verify the duplicate entries for host and quorum server.
#
#------------------------------------------------------------------------------

sub parameter_checks {

    foreach (@hostList) {
        if ($_ =~ /\d+\.\d+/) {
            &printLog("ERROR: Provide the name of the node as input to the script.\n", $ERROR);
            $found = 1;
        } elsif($_ =~ /\w+\.\w+/) {
            &printLog("ERROR: Provide the name of the node without domain name.\n", $ERROR);
            $found = 1;
        }
        elsif ($_ eq $shortHostName) {
            $found = 2;
        }
    }
    foreach (@quorum) {
        if ($_ =~ /\./) {
            &printLog("ERROR: Enter the quorum server name without the domain name.\n", $ERROR);
            $found = 1;
        }
    }

    foreach(@nonroot) {
        `id $_ > /dev/null 2>&1`;
        if ($? ne 0) {
            &printLog("ERROR: Invalid NON_ROOT_USER $_.\n",
                      $ERROR);
            $found = 1;
        }	
    }
    if ($found eq 1) {
        closeLogAndExit();
    } elsif($found ne 2) {
        printLog("ERROR: The script must be run from one of the nodes passed".
                 " as input to the script.\n", $EXIT);
    }
    
    my $dup_user = verify_duplicate_entries(\@nonroot);
    my $dup_host = verify_duplicate_entries(\@hostList);
    my $dup_quorum = verify_duplicate_entries(\@quorum);

    if ($dup_user || $dup_host || $dup_quorum) {
        &closeLogAndExit();
    }

}
#------------------------------------------------------------------------------
#
# Subroutine    : check_distro_on_all_nodes()
# Calls         : runandCaptureCommand()
# Called by     : pre_req_check()
# Globals       : hostList, shortHostName, DistroName.
# Input Params  : -
# Return Value  : returns 1 if distro of the input nodes dont match.
#                 0 if there is a match. 
#
# This function checks for the file /etc/<distro>-release or /etc/issue on the
# remote node and compares the distro with the local node. This process is
# repeated for all the nodes passed as input. This is called only from 
# local node and not from remote node since the distro needs to be verified
# only once 
#
#------------------------------------------------------------------------------

sub check_distro_on_all_nodes {
	
    my $cmd , my $ret = 0;
    my $op = "", my $fail = 0;
    my $DistroVerRemote = "";

    foreach my $node (@hostList) {
        if ($node ne $shortHostName) {
            if ($DistroName eq "RHEL") {
                $cmd = "$ssh_cmd root\@$ipHostPair{$node} ".
                "'cat /etc/redhat-release 2>$errorFile'";
                ($ret, $op) = runandCaptureCommand($cmd);
            } else {
                $cmd = "$ssh_cmd root\@$ipHostPair{$node} ".
                "'cat /etc/SuSE-release 2>errorFile'";
                ($ret, $op) = runandCaptureCommand($cmd);
                if ($ret ne 0) {
                    $cmd = "$ssh_cmd root\@$ipHostPair{$node} ".
                    "'cat /etc/os-release | grep PRETTY_NAME 2>errorFile'";
                    ($ret, $op) = runandCaptureCommand($cmd);
                }
            }
            if ($ret ne 0) {
                &printLog("FAILED\n", $ERROR);
                &printLog("ERROR: Unable to get the distro information of $node.\n", $ERROR);
                $fail = 1;
                next;
            }
            if ($op =~  m/(\d+)/) {
                $DistroVerRemote = $1;
                $fail = 0;
            }
            if ($DistroVers ne $DistroVerRemote) {
                &printLog("FAILED\n", $ERROR);
                &printLog("ERROR: Distro of nodes $shortHostName and $node does not match.\n", $ERROR);
                $fail = 1;
            }
        }
    }
    if ($fail) {
        return 1;
    } else {
        return 0;
    }
}
#------------------------------------------------------------------------------
#
# Subroutine    : check_ssh_on_all_nodes()
# Calls         : runandCaptureCommand(), printLog().
# Called by     : pre_req_check()
# Globals       : shortHostName
# Input Params  : -
# Return Value  : returns 1 if ssh is not setup between current node and one or
#                 more of other nodes.
#                 0 if ssh is setup properly between the current node and other 
#                 nodes. 
#
# This function checks if ssh is setup between the current node and the remote
# node by executing ssh without password prompts and date command. This function
# is called by all the nodes passed as input to the script.
#
#------------------------------------------------------------------------------

sub check_ssh_on_all_nodes {

    my $fail = 0;
    my $cmd, my $ret = 0;
    foreach my $node (@hostList) {
        if($node ne $shortHostName) {
            $cmd = "$ssh_cmd root\@$ipHostPair{$node} 'date'";
            ($ret) = runandCaptureCommand($cmd);
            if ($ret eq 255) {
                &printLog("  ERROR: ssh is not setup between $shortHostName and $node.\n", $ERROR);
                $fail = 1;
            }
        }
    }
    if ($fail) {
        return 1;
    } else {
        return 0;
    }
}
#TO-DO

#------------------------------------------------------------------------------
# Subroutine    : checkBinaryOnAllNodes
# Calls         : runandCaptureCommand
# Called by     : pre_req_check()
# Returns       : 1 if the executable file 'cmpreparecl' is not present
#                 0 otherwise
# This fucntion checks for the presence of the cmpreparecl binary in 
# $SGSBIN/cmpreparecl
# 
#------------------------------------------------------------------------------   
sub checkBinaryOnAllNodes {
   my $cmd , my $ret , my $op = "";  
   my $fail = 0;
    foreach my $node (@hostList) {
        if($node ne $shortHostName) {
            $cmd = "$ssh_cmd root\@$ipHostPair{$node} 'if [ -x $SGSBIN/cmpreparecl ];".
                   "then echo 0; else echo 1; fi'";
            ($ret, $op) = runandCaptureCommand($cmd);
            if ($op == 1) {
                $fail++;
                if ($fail eq 1) {
                    &printLog("FAILED\n", $ERROR);
                }
                printLog("ERROR: The binary $SGSBIN/cmpreparecl is not present on node $node\n", $ERROR);
            }
        }
    }
    if ($fail ) {
        return 1;   
    } else {
        return 0;
    } 
}
#---------------------------------------------------------------------------------
#
# Subroutine    : pre_req_check()
# Calls         : parameter_checks(), getIps(), createRemoteFile(), getDistroInfo(),
#                 check_distro_on_all_nodes(), runandCaptureCommand(), 
#                 check_ssh_on_all_nodes(),printLog()
#
# Called by     : script()
# Globals       : hostList, remoteFile
# Input Params  : -
# Return Value  : returns 1 if all the pre-requisites are met
#                 0 otherwise
#
# This function does the following checks :
# 1. Basic parameter checks as described in the function parameter_checks().
# 2. Checks if the nodes are pingable
# 3. Checks if ssh is setup between current node and the other nodes.
# 4. Checks if all nodes have the same distro.
# 5. Checks if Serviceguard is installed.
# 
#---------------------------------------------------------------------------------

sub pre_req_check {

    my $fail = 0, my $is_exit = 0;
    my $op = "", my $cmd, my $ret = 0;
    my $length , my $spacetext = "";
    &printLog("$dash\n");
    &printLog("Checking for pre-requisites on node $shortHostName,\n");
    &printLog("$dash\n");

    # Check if the nodes are pingable from the node on which the script is executed.
    chomp($prereq_text[$prereqCount]);
    $spacetext = " " x (80 - (length($prereq_text[$prereqCount]) + 4));
    &printLog($prereq_text[$prereqCount].$spacetext);
    $error_prereq[$prereqCount] = $ERROR;
    $is_quorum = 0;
    if (!&getIps(\@hostList, $is_quorum)) {
        $error_prereq[$prereqCount] = $PASS;
        &printLog("OK\n");
    } else {
        $fail = 1;
    }
    if (@quorum) {
        $is_quorum = 1;
        ++$prereqCount;
        chomp($prereq_text[$prereqCount]);
        $spacetext = " " x (80 - (length($prereq_text[$prereqCount]) + 4));
        &printLog($prereq_text[$prereqCount].$spacetext);
        $error_prereq[$prereqCount] = $ERROR;
        if (!&getIps(\@quorum, $is_quorum)) {
            $error_prereq[$prereqCount] = $PASS;
            &printLog("OK\n"); 
        } else {
            $fail = 1;
        }
    }
    
    # Check if ssh is set up between the nodes
    if (!$fail) {
        if ($num_nodes > 1) {
            ++$prereqCount;
            chomp($prereq_text[$prereqCount]);
            $spacetext = " " x (80 - (length($prereq_text[$prereqCount]) + 4));
            &printLog($prereq_text[$prereqCount].$spacetext);
            $error_prereq[$prereqCount] = $ERROR;
            if (&check_ssh_on_all_nodes()) {
                &printLog("FAILED\n", $ERROR);
                $fail = 1;
            } else { 
                $error_prereq[$prereqCount] = $PASS;
                &printLog("OK\n");
            }
        }
    }
    
    # If there are issues with ssh and ping then we do not proceed with the check.
    # These are mandatory to perform other checks.
    if ($fail) {
        $pre_req_error++;
        &printLog("ERROR: Pre requisites required to run the script are not satisfied.\n", $EXIT);
    }

    if (!-e $remoteFile) {
        &createRemoteFile();
    }

    # Check for existence of Serviceguard.
    ++$prereqCount;
    chomp($prereq_text[$prereqCount]);
    $spacetext = " " x (80 - (length($prereq_text[$prereqCount]) + 4));
    &printLog($prereq_text[$prereqCount].$spacetext);
    $error_prereq[$prereqCount] = $ERROR;
    $op = `$SGSBIN/cmversion 2> $errorFile`;
    if ($? eq 0 ) {
        $op =~ /^[A-Z].(\d+.\d+)/;
        $SGVersion = $1;
        if ($SGVersion < 11.20) {
             &printLog("FAILED\n", $ERROR);
             &printLog("ERROR: Serviceguard version installed in the system is not supported by this command.\n", $ERROR);
             $fail = 1;
        } else {
            # Check if cmpreparecl is present on all the nodes from the local node
            if (checkBinaryOnAllNodes()) {
                $fail = 1;
                $is_exit = 2;
            } else {
                $error_prereq[$prereqCount] = $PASS;
                &printLog("OK\n");
            }
        }
    } else {
        &printLog("FAILED\n", $ERROR);
        $fail = 1;
    }
    # Check if the node is not already a part of the cluster
    ++$prereqCount;
    chomp($prereq_text[$prereqCount]);
    $spacetext = " " x (80 - (length($prereq_text[$prereqCount]) + 4));
    &printLog($prereq_text[$prereqCount].$spacetext);
    $error_prereq[$prereqCount] = $ERROR;
    if (-s "$Sgconf/cmclconfig") {
        &printLog("FAILED\n", $ERROR);
        $fail = 1;
    } else {
        $error_prereq[$prereqCount] = $PASS;
        &printLog("OK\n");
    } 
    if (!-e $remoteFile) {
        # Check if all nodes have the same distro.
        # This check can be done from one node which will be node on
        # which the script is executed.
        ++$prereqCount;
        chomp($prereq_text[$prereqCount]);
        $spacetext = " " x (80 - (length($prereq_text[$prereqCount]) + 4));
        &printLog($prereq_text[$prereqCount].$spacetext);
        $error_prereq[$prereqCount] = $ERROR;
        if (&check_distro_on_all_nodes()) {
            $fail = 1;
        } else { 
            &printLog("OK\n");
            $error_prereq[$prereqCount] = $PASS;
        }
    }
    if ($is_exit) {
        return $is_exit;
    }
    if (!$fail) {
        return 0;
    } else {
        &printLog("ERROR: One or more of the pre-requisite conditions not met\n");
        return 1;
    }
        
}
    
#------------------------------------------------------------------------------
#
# Subroutine    : non_dash_arg()
# Calls         : printLog()
# Called by     : 
# Globals       : hostcount, arg_errors
# Input Params  : -
# Return Value  : -  
#
# This function populated the inputs passed to the script into appropriate
# variables.
#
#------------------------------------------------------------------------------

sub non_dash_arg {

    my ($name, $val, $target) = @_;
    if ($val =~ /^-/) {
        &printLog("ERROR: Option $name requires an argument.\n");
        $arg_errors++;
    } else {
        if (ref($target) eq "ARRAY") {
            push @{$target}, $val;
            if ($name eq "n") {
                $hostcount++;
            }

        } elsif (ref($target) eq "SCALAR") {
            # a scalar target generally indicates an option that should not be
            # used more than once, so check for that.
            if (${$target}) {
                &printLog("ERROR: Option $name should not be supplied more than once.\n");
                pod2usage();
                $arg_errors++;
            }
            ${$target} = $val;
        }
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : cleanupAuthKeys()
# Calls         : cmsshsetup command
# Called by     :
# Globals       : hostList
# Input Params  : hostList
# Return Value  : -.
#
# This function clears the auth keys from authorize file on all nodes.
#
#------------------------------------------------------------------------------

sub cleanupAuthKeys {

    if (! -e $remoteFile) {
        &printLog($dash."\n");
        &printLog("Deletion of ssh keys between nodes\n");
        &printLog($dash."\n");
        &printLog("If prompted, enter the password and/or add fingerprint ".
                   "to cleanup the keys of ssh setup\n");
        my $nodes = join(' -n ', @hostList);
        my $cmd = "$SGSBIN/cmsshsetup -n $nodes --delete -l $ssh_log_file > $errorFile 2>&1";
        (my $ret) = runandCaptureCommand($cmd);
        if ($ret eq 0) {
            &printLog("Auth keys are successfully deleted from auth file\n");
        } else {
            &printLog("WARNING: Auth keys deletion failed, password less ssh setup is active!\n");
            &printLog("Please remove the keys manually from the auth file.\n");
        }
        &printLog("For more details on ssh setup, Please refer to log file at $ssh_log_file\n");
    } 
}

#------------------------------------------------------------------------------
#
# Subroutine    : cleanup()
# Calls         : printLog
# Called by     : 
# Globals       : remoteFile, errorFile
# Input Params  : -
# Return Value  : -  
#
# The function cleans up the temporary error file if exists and the remote file
# if exists that is created to keep track of the remote operations.
#
#------------------------------------------------------------------------------

sub cleanup {

    my $cmd = "", my $ret = 0;
    if( -e $errorFile) {
        `rm -f $errorFile`;
    }
    if (-e $remoteFile) {
        `rm -f $remoteFile`;
        if ($? ne 0) {
            &printLog("ERROR: Unable to remove temporary file $remoteFile".
            "\n\tPlease remove the file $remoteFile manually.", $ERROR);
        }
    }
}   
#------------------------------------------------------------------------------
#
# Subroutine    : cleanup_all_nodes()
# Calls         : runandCaptureCommand()
# Called by     : script when it exits after pre-requisite checks.
# Globals       : remoteFile, errorFile
# Input Params  : -
# Return Value  : -  
#
# The function cleans up the temporary error file and the remote file
# on all the nodes in which they exist.
#
#------------------------------------------------------------------------------

sub cleanup_all_nodes {

    my $cmd, my $ret;
    foreach my $node (@hostList) {
        if ($node ne $shortHostName) {
            if (-e $errorFile) {
                $cmd = "$ssh_cmd root\@$ipHostPair{$node}".
                       " 'rm -f $errorFile'";
                ($ret) = runandCaptureCommand($cmd);
            }
            if (-e $remoteFile) {
                $cmd = "$ssh_cmd root\@$ipHostPair{$node}".
                       " 'rm -f $remoteFile'";
                ($ret) = runandCaptureCommand($cmd);
            }
        } else {
            if (-e $errorFile) {
                `rm -f $errorFile`;
            }
            if (-e $remoteFile) {
                `rm -f $remoteFile`;
            }
        }
    }
}
#------------------------------------------------------------------------------
#
# Subroutine    : getIps()
# Calls         : ping command, closeLogAndExit()
# Called by     : pre_req_check , script.
# Globals       : %ipHostPair, %qsHostPair, %fullnameofhost, %fullnameofquorum
# Input Params  : -
# Return Value  : -  
#
# This function pings all the hosts provided as input to the script and
# finds the ip address and fully qualified domain name of each of the hosts.
# ipv6 only support is not taken care of. This will be done once we have the 
# setup. This function is called as part of pre_requisite check on local node.
#
#------------------------------------------------------------------------------

sub getIps {

    my $list = shift;
    my $flag = shift;
    my $errorflag = 0;
    my $fullname = "";
    my $op, my $cmd;
    my $output = "", my $ret = 0;
    my @output;

    foreach my $node(@$list) {
        $cmd = "ping $node -c 1 2>&1";
        ($ret,@output) = runandCaptureCommand($cmd);
        my $output = join(' ', @output);
        if ($ret eq 0) {      
            if ($output =~ m/(.+)0 received, 100% packet loss(.+)/) {
                $errorflag = 1;
            } else {
                $output =~ /(\d+\.\d+\.\d+\.\d+)/;
                $op = $1;
            }
            $output =~ m/PING (.*) \(/;
            $fullname = $1;
        } else {
            if ($output =~ m/(.+)0 received, \d+% packet loss(.+)/) {
                &printLog("FAILED\n", $ERROR);
                &printLog("ERROR: Host $node is not reachable.\n");
                $errorflag = 1;
            } else {
                $cmd = "ping6 $node -c 1 2>&1";
                ($ret,$output) = runandCaptureCommand($cmd);
                if ($ret eq 0 ) {
                    if ($DistroName !~ /SLES/i) {
                        $errorflag = 1;
                        &printLog("FAILED\n", $ERROR);
                        &printLog("ERROR: Serviceguard does not support ipv6only mode for $DistroName\n");
                    } else {
                        $output =~ m/PING (.*)(\()(.*)(\))/;
                        $fullname = $3;
                        # In ipv6 mode, ping6 output does not give the ip address
                        # without -n option. Hence ping6 is run twice, once to get
                        # the full name of the host and another for the ip.
                        $cmd = "ping6 -n $node -c 1 2>&1";
                        if ($ret eq 0 ) {
                           $output =~ /PING (.*)(\()(.*)(\))/;
                           $op = $3;
                        }
                    }
                } else {           
                    $errorflag = 1;
                    &printLog("FAILED\n", $ERROR);
                    &printLog("ERROR: Unable to find ip address of $node.\n");
                    &printLog("Configure DNS or /etc/hosts to resolve the node name\n");
                }
            }
        } 
        if (!$errorflag) {
            if ($fullname ne "") {
                if ($flag) {
                    $fullnameofquorum{$node} = $fullname;
                    $qsHostPair{$node} = $op;
                } else {
                    $fullnameofhost{$node} = $fullname;
                    $ipHostPair{$node} = $op;
                }
            }
            else {
                $errorflag = 1;
            } 
        }       
    }
    if (!$errorflag) {
        return 0;
    } else {
        return 1;
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : verify_duplicate_entries()
# Calls         : -
# Called by     : script
# Globals       : -
# Input Params  : The list for which duplicate entries need to be checked.
# Return Value  : 0 in success and 1 on failure.
#
# This function pings all the hosts provided as input to the script and
# finds the ip address and fully qualified domain name of each of the hosts.
#
#------------------------------------------------------------------------------

sub verify_duplicate_entries {

    my $aref = shift;
    my %param_hash;
    my @list = ();
    my $msgtext = "", my $size;

    foreach(@$aref) {
        if (defined $param_hash{$_}) {
            push(@list, $_);
        } else {
            $param_hash{$_} = "";
        }
    }
    $size = scalar @list;
    if ($size > 1) {
        $msgtext = "parameters " .join(',' , @list)." have";
    } elsif ($size == 1) {
        $msgtext = "parameter " .join(',' , @list)." has";
    }
    if($msgtext ne "") {
        &printLog("ERROR : The $msgtext been passed more than once.\n", $ERROR);
        return 1;
    }
    return 0;
}

#------------------------------------------------------------------------------
#
# Subroutine    : closeLogAndExit()
# Calls         : printLog()
# Called by     : script
# Globals       : -
# Input Params  : -
# Return Value  : - 
#
# This function closes the log file, and exits. If the function is called by a 
# local node, then the summary is printed out before exiting. 
#----------------------------------------------------------------------------l

sub closeLogAndExit {

    if (!-e $remoteFile && !$check_pre_req) {
        &generateSummary(0);
        &printOutputSummary();
    }
    &printLog("FAILURE: The system configuration failed with $no_of_errors".
            " error(s) and $no_of_warnings warning(s).\n\n");
    print "$LogPathMsg";
    &printLog("$dash\n");
    cleanup();
    &cleanupAuthKeys();
    close(LOG_FILE);
    exit 1;
}

sub searchAndDelete {
    my $searchEntry = shift;
    my $fname = shift;
    $cmd = "perl -ni -e 'print unless/$searchEntry/' $fname 2>$errorFile";
    runandCaptureCommand($cmd);
}

sub executePerlCommands {
    my $cmd = shift;
    runandCaptureCommand($cmd);
    my $op = `cat $errorFile`;
    if ($op ne "") {
        return 1;
    } 
    return 0;
}

#------------------------------------------------------------------------------
#
# Subroutine    : consolidateErrors()
# Calls         : 
# Called by     : main()
# Globals       : %node_skip
# Input Params  : variable containing the return values of various operations 
#                  separated by '#'.
# Return Value  : -  
#
# This function parses through the summary text passed after the execution 
# of the configuration steps. It classifies each node based on whether a 
# particular operation on the node was successful/failure/skipped.
# The values for each of these is :
# success : 0
# Failure : 2 (indicated by $ERROR)
# Skipped : 1 (indicated by $SKIP)
#
# The summary to be passed to this function is a twelve digit variable
# separated by '#'. Each digit indicates whethere a particular operation has
# one of the status mentioned above. The order of these digits is the same
# as the operations executed.
#
#------------------------------------------------------------------------------

sub consolidateErrors {

    my $summary = shift;
    my $node = shift;
    my $is_prereq = shift;
    my @summary_array = split('#',$summary);
    my $count = 0, my $hash_size = 0, my $num;
    if ($is_prereq) { #consolidate the pre-requisites summary
        $num = $num_prereq;
    } else {
        $num = $num_elements;
    }
    foreach(@summary_array) {
        if ($_ eq $ERROR) {
            if ($count eq ($num - 1)) {
                if ($node eq $shortHostName || $found ne 2) {
                    $all_errors{$count} = $node;
                }
            } else {
                push @{$all_errors{$count}},$node;
            }
        } 
        elsif ($_ == $SKIPPED) {
            if ($count == ($num - 1)) {
                if ($node eq $shortHostName || $found ne 2) {
                    $all_skipps{$count} = $node;
                }
            } else {
                push @{$all_skipps{$count}}, $node;
            }
        }
        ++$count
    }
    if (exists($node_skip{$node})) {
        delete($node_skip{$node});
    }
    
}
sub printOutputSummary {
    foreach(@output_summary) {
        printLog($_);
    }
}

#----------------------------------------------------------------------------
# Subroutine    : formatNodeOutput()
#
# This function is used to format the output displayed by aligning the node
# names in the following way : two nodes in the first row, three nodes in 
# the consecutive rows for as many nodes that are present. 
#---------------------------------------------------------------------------- 

sub formatNodeOutput {
    my $nodelist = shift;
    my @nodelist = split(',', $nodelist);
    foreach(@nodelist) {
        $_ =~ s/^\s*//;
    }
    my @nodelist_sort = sort(@nodelist);
    $nodelist = join(', ' ,splice (@nodelist_sort, 0, 2));
    while (scalar(@nodelist_sort) > 0) {
        $nodelist  .= "\n"." " x 80;
        $nodelist .= join(', ' ,splice (@nodelist_sort, 0, 3));
    }
    return $nodelist;
}

#------------------------------------------------------------------------------
#
# Subroutine    : generateSummary()
# Calls         : consolidateErrors(), printLog(), formatNodeOutput()
# Called by     : main()
# Globals       : %node_skip, %all_errors, %all_skipps
# Input Params  : -
# Return Value  : -  
#
# The function first checks for nodes which errored out in the initial stages
# and have not consolidated the summary. It calls to consolidate the summary 
# with skipped values for operation on those nodes.
#
# It then loops through the error and skip hashes which contain the names
# of the nodes on which the operations have failed and skipped. It prints out 
# the various steps that are executed during cmpreparecl and the details of 
# on which node it was successful/failure/skipped.
#
# For eg : $all_errors{$count} contains all the nodes on which the operation 
# $count failed. Similarly $all_skipps{$count} contains all the nodes on which
# the operation $count was skipped. All the nodes that are not present in these 
# two hashes will be considered as nodes on which the operation was successful.
#
#------------------------------------------------------------------------------

sub generateSummary {

    my $is_prereq = shift;
    my $successMsg = "Successful on node(s): ", my $successMsgText = "";
    my $failureMsg = "Warning or error occurred on node(s): ", my $errorMsgText = "";
    my $skipMsg = "Skipped on node(s): ", my $skipMsgText = "";
    my $count = 0, my $key, my $msgtext;
    my $location = 80, my $var_space = 0;
    my $size = scalar(keys %node_skip), my $num, my @text;
    $msgtext .= "$dash\n";
    push(@output_summary, $msgtext);
    if ($is_prereq) {
        push(@output_summary, "Consolidated summary of the pre-requisite check done\n");
        $num = $num_prereq;
        @text = @prereq_text;
    } else {
        push(@output_summary, "Consolidated summary of the configuration done on all the nodes.\n");
        $num = $num_elements;
        @text = @error_text;
    }
     
    push(@output_summary, $dash);
    if ($size) {
        foreach $key (keys %node_skip) {
            my @error_summary = ($SKIPPED) x ($num);
            if ($key !~ /$shortHostName/) {
                # if $found is 2 then we know that the script is executed from 
                # one of the nodes passed as input and only then will the cmclnodelist
                # on remote node get copied from local node. Otherwise the script 
                # exits with error.
                if ($found eq 2) {
                   $error_summary[$num - 1] = $PASS;
                }
            } 
            my $summary = join('#',@error_summary);
            &consolidateErrors($summary, $key, $is_prereq );
        }
    }
    foreach(@text) {
        my $space;
        my $spacetext;    
        my $nodes, my $val, my $i;
        chomp ($text[$count]);
        my @tmparray = split("\n\t", $text[$count]);
        my $text = pop(@tmparray);
        if ($text =~ /^\d+:/) {
            $var_space = 4; 
        } else {
            $var_space = 8;
        }
        my $length = length($text); 
        %node_skip = map { $_ => 1 } @hostList;
        $skipMsgText = "";
        $errorMsgText = "";
        $successMsgText = "";
        # The last operation in the sequence is cerating cmclnodelist which is
        # done only on the local node and copied onto all the other nodes. Hence
        # the error value of the local node will be applicable to all the other
        # nodes.
        if ($count != ($num - 1)) {
            if (defined $all_errors{$count}) {
                $nodes = join(',', @{$all_errors{$count}});
                $nodes = formatNodeOutput($nodes);
                $errorMsgText = $failureMsg." ".$nodes;
                $errorFlag = 1;
                foreach $i (0 .. $#{$all_errors{$count}}) {
                    if (defined $node_skip{$all_errors{$count}[$i]}) {
                        delete ($node_skip{$all_errors{$count}[$i]});
                    }            
                }
            } 
            if (defined $all_skipps{$count}) {
                $nodes = join(',', @{$all_skipps{$count}});
                $nodes = formatNodeOutput($nodes);
                if ($nodes ne "") {
                    $skipMsgText = $skipMsg." ".$nodes;
                        foreach $i (0 .. $#{$all_skipps{$count}}) {
                            if (defined $node_skip{$all_skipps{$count}[$i]}) {
                                delete ($node_skip{$all_skipps{$count}[$i]});                        
                            }            
                        }
                    $errorFlag = 1;
                }
                
            }         
            $nodes = "";
            foreach $key(keys %node_skip) {
                if ($nodes eq "") {
                    $nodes = $key; 
                } else {
                    $nodes .= ",$key";
                }
            }
            if ($nodes ne "") {
                $nodes = formatNodeOutput($nodes);
                $successMsgText = $successMsg." ".$nodes;
            }
        } else {
            my %node_hash_temp;
            my @node_tmp;
            # Since the hostlist is used here, need to eliminate duplicate
            # entries. This is useful when printing skipped message when 
            # the script exits due to duplicate entries passed.
            
            grep { !$node_hash_temp{$_}++ } @hostList;
            foreach my $key(keys %node_hash_temp) {
                push(@node_tmp, $key);
            }
            $nodes = join(',', @node_tmp);
            $nodes = formatNodeOutput($nodes);
            if (defined $all_errors{$count}) {
                $errorMsgText = $failureMsg." ".$nodes;
            } elsif (defined $all_skipps{$count}) {
                $skipMsgText = $skipMsg." ".$nodes;
            } else {
                $successMsgText = $successMsg." ".$nodes;
            }                
        }
        # The digit 4 is to take care of the tab spaces that printLog introduces.
        # The first message be it success/failure/skipped will be printed in the
        # location calculated below
        $space = 80 - ($length + $var_space);
        $spacetext = " " x $space;
        $msgtext = $text[$count].$spacetext.":";
        
        if ($successMsgText ne "") {
            $spacetext = " " x $location;
            $msgtext .= $successMsgText."\n".$spacetext;
        }
        if ($errorMsgText ne "") {
            $spacetext = " " x $location;
            $msgtext .= $errorMsgText."\n".$spacetext;
        }
        if ($skipMsgText ne "") {
            $msgtext .= $skipMsgText."\n";
        }
        ++$count;
        push(@output_summary, $msgtext);    
        
    }
    push(@output_summary, "\n");
} 
     
#------------------------------------------------------------------------------
#
# Subroutine    : main()
# Calls         : validateSuperUser(), validateHostName(), getDistroInfo(),
#                 callConfigFunctions(), createCmclnodelist(),
#                 sgFirewallRules(), sgFirewallSetup(), printLog()
# Called by     : main part of the script
# Globals       : $DistroName, $DistroVers, %fullnameofhost, $remoteFile, 
#                 $CurrentHost, $Sgconf, %ipHostPair, $LogPathMsg
# Input Params  : none
# Return Value  : 0 if successful; 1 if not.
#
# This subroutine calls all the above listed functions to configure the system
#
#------------------------------------------------------------------------------

sub main {

    my $RetVal = 0;
    my $summary;
    my $timeStamp = localtime time;
    my $is_prereq = 0;
    my $LastNote = "";

    # Validate that the script is being run by root
    &validateSuperUser();
    if (($DistroName eq "SLES") || ($DistroName eq "RHEL")) {
        if (!-e $remoteFile) {

            # Call all the configuration functions 
            &callConfigFunctions;

            # Create the cmclnodelist file in the $SGCONF directory
            &createCmclnodelistFile;

            # Configuration on the local node is complete. Now, create the
            # summary page.
            $summary = join('#',@error_summary);
            &consolidateErrors($summary, $shortHostName, $is_prereq);

            # Print success message in Log.
            if (($no_of_errors == 0) && ($no_of_warnings == 0)) {
                &printLog("SUCCESS : The system configuration completed".
                          " successfully.\n\n");
            } elsif (($no_of_errors == 0) && ($no_of_warnings != 0)) {
                &printLog("SUCCESS : The system configuration completed".
                          " with $no_of_warnings warning(s).\n\n");
            }else {
                &printLog("FAILURE: The system configuration failed with ".
                          "$no_of_errors error(s) and $no_of_warnings warning(s).\n\n");
            }


            # All the setup on current node is complete. We start 
            # configuring the other nodes now.
            # For IPv4 Addresses
            foreach my $node (sort keys %fullnameofhost) {
                next if ($fullnameofhost{$node} eq $CurrentHost);
                &printLog("WARNING: Setup on $node node was not ".
                    "completed satisfactorily. Please check Log file at: ".
                    "$logFile in the remote node $node to".
                    " determine the problem and re-run the script.\n", $WARNING)
                if (($RetVal = &remoteExecutions($ipHostPair{$node},
                                            $node)) != 0);
            }
            &generateSummary(0);
            &printOutputSummary();
            print "$LogPathMsg";
            &cleanupAuthKeys();

            # Last Message to user, to be printed only on success
            if ($errorFlag == 0) { 
                $LastNote = "Thank you for choosing Hewlett-Packard as ".
                               "your high availability\n\tclustering solution ".
                               "provider. Please visit www.hp.com/go/sglx\n\tfor more ".
                               "product information and to keep informed about".
                               " what's new!\n\n";
                $timeStamp = localtime time;
                $LastNote .= "\tcmpreparecl execution completed successfully [ $timeStamp ]\n";

            } else {
                $LastNote = "Some error(s) occured during the execution of the script.\n".
                            "\tLook into the log file, fix the problem and re-run the command.\n"; 
            }
            &printLog("$LastNote\n");
            &printLog("$dash\n");
       
        } else {

            # call the required functions
            &callConfigFunctions;

            # All the setup on node is complete. 
            if(($no_of_errors == 0) && ($no_of_warnings == 0)) {
                &printLog("SUCCESS: The system configuration on the remote".
                          " node $CurrentHost completed successfully.\n\n");
            } elsif (($no_of_errors == 0) && ($no_of_warnings != 0)) {
                &printLog("SUCCESS : The system configuration on the remote "
                          ."node $CurrentHost completed ".
                          "with $no_of_warnings warning(s).\n\n");
            } else {
                &printLog("FAILURE: The system configuration on the remote".
                          " node $CurrentHost failed with $no_of_errors error(s) and".
                          " $no_of_warnings warning(s).\n\n");
            }
            $summary = join('#',@error_summary);
            print $summary;
        }

    } else {
        printLog("ERROR: $DistroName $DistroVers is not supported by".
                 " Serviceguard.\n", $ERROR);
        cleanup();
        return 1;
    }
    cleanup();
}

#------------------------------------------------------------------------------
#
# Subroutine    : writeToFile()
# Calls         : open command 
# Called by     :  
# Globals       :
# Input Params  : File name data reference
# Return Value  : -.
#
# This function writes the array of data to the file. 
# Data and file name are inputs.
#
#------------------------------------------------------------------------------
sub writeToFile {

    my $fname = shift;
    my $data = shift;
    if (open (FILE_HANDLE, "$fname")) { 
        if (ref($data) eq "ARRAY") {
            if (@$data) {
                print FILE_HANDLE @$data;
            }
        } else {
            if ($$data ne "") {
                print FILE_HANDLE $$data;
            }
        }
        close FILE_HANDLE;
        return 0;
    } else {
        &printLog("ERROR: Unable to open file $fname to write data.\n", $ERROR);
        return 1;
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : createBackup()
# Calls         : cp command 
# Called by     :
# Globals       :
# Input Params  : Name of the file to be backed up. 
# Return Value  : - 
#
# This function makes a backup of the file passed to <file_name>.original. 
#-----------------------------------------------------------------------------
sub createBackup {

    my $fname = shift;
    my $original = $fname.".original";
    my $cmd;
    if (-e $fname) {
        if(!(-e $original)) {
            $cmd = "cp $fname $original 2>$errorFile";
            (my $ret) = runandCaptureCommand($cmd);
            if($ret != 0) {
                my $err = `cat $errorFile`;
                &printLog("WARNING: $err");
                &printLog("NOTE: Could not create a backup of $fname.\n",$WARNING);
            } else {
                &printLog("NOTE: Backed up $fname to $original.\n");
            }
        } else {
            &printLog("NOTE: File $fname is already backed up \n\tin the name $original.".
                      "\n\tHence no new backup taken.\n");
        }
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : remoteExecutions()
# Calls         : ssh/scp commands
# Called by     : main
# Globals       : 
# Input Params  : IP address of other host
# Return Value  : 0 if successful; 1 if not. 
#
# This subroutine does all the remote executions on the other servers in the 
# cluster for the node passed as input
# 
#------------------------------------------------------------------------------

sub remoteExecutions{

    my ($nodeIP, $node) = @_;
    my $op, my $cmd, my $ret;
    my $is_prereq = 1;

    # Execute the script on the other nodes. The distro info is passed
    # on to the other node as arguments so that the script on the other
    # nodes can proceed.
    my $option = "-n ".join(' -n ', @hostList);
    if (@nonroot) {
        $option .= " -u ".join(' -u ', @nonroot);
    }
    if (@quorum) {
        $option .= " -q ".join(' -q ', @quorum);
    }
    if ($check_pre_req) {
        $option .= " -p";
    }

    $cmd = "$ssh_cmd root\@$nodeIP '$SGSBIN/$ScriptName $option'";
    ($ret, my @output) = runandCaptureCommand($cmd);

    if ($check_pre_req) {
        $is_prereq = 1;
    }
    my $summary = pop(@output);
    &consolidateErrors($summary, $node, $is_prereq);

    print @output;
    print LOG_FILE @output;
    if ($ret != 0) {
        if ($check_pre_req) {
            return 1;
        }
    }
    return 0;
}

#------------------------------------------------------------------------------
#
# Subroutine    : callConfigFunctions()
# Calls         : checkPortsAvailability(), 
#                 startService(), setManPathVariable(),
#                 setManPathVariable(), confirmSgPersistCommand(),
#                 updateHostsFile(), printLog(), enableVGActivationProtection()
#                 nsswitchConfSettings() 
# Called by     : main()
# Globals       : @DistroInfo
# Input Params  : none
# Return Value  : none
#
# This subroutine calls all the above listed functions to configure the system
#
#------------------------------------------------------------------------------

sub callConfigFunctions {

    # Check and confirm whether the ports required by SG are not reserved 
    # by other programs. 
    &checkPortsAvailability;
    
    # Check and start the authd service. Exit if service couldn't be started
    &startService($srv, $auth_type);

    # Set MANPATH entry in the /etc/manpath.config file
    &setManPathVariable;

    # Update the /etc/hosts file
    &updateHostsFile;

    # Check /etc/nsswitch.conf for correct hosts entry
    &nsswitchConfSettings;

    # Enable Volume Group Activation Protection
    &enableVGActivationProtection;
  
    # Generate firewall rules
    &sgFirewallRules;

    # Startup the Firewall service
    &sgFirewallSetup;

}

#------------------------------------------------------------------------------
#
# Subroutine    : getDistroInfo()
# Calls         : none
# Called by     : main()
# Globals       : $DistroName, $DistroVers
# Input Param   : none
# Return Value  : none
#
# This subroutine extracts the distro name and version information and
# populates the global variables $DistroName and $DistroVers with this data.
#
#------------------------------------------------------------------------------
sub getDistroInfo {
    my $releaseFileContent;
    my @file_out;
    my @distro_name;
    my @split_distro_name;    
    my @split_again = ();
    my @ver_name_out;
    if (-f "/etc/redhat-release") {
        $releaseFileContent = `cat /etc/redhat-release`;
        $DistroName = "RHEL";
        if ($releaseFileContent =~ m/(\d+)/) {
            $DistroVers = $1;
            return 0;
        }
    } elsif (-f "/etc/SuSE-release") {
        $releaseFileContent = `cat "/etc/SuSE-release"`;
        $DistroName = "SLES";
        if ($releaseFileContent =~ m/(\d+)/) {
            $DistroVers = $1;
            return 0;
        }
    } elsif (-f "/etc/os-release") {
        @file_out = `cat "/etc/os-release"`;
        @distro_name = grep(/^ID/, @file_out);
        @split_distro_name = split(/=/,$distro_name[0]);
        chomp($split_distro_name[1]);
        $split_distro_name[1] =~ s/"//g;

        if ($split_distro_name[1] eq "sles") {
            $DistroName = "SLES";
        } elsif ($split_distro_name[1] eq "rhel") {
            $DistroName = "RHEL";
        }

        @ver_name_out = grep(/^PRETTY_NAME/, @file_out);
        chomp($ver_name_out[0]);
        $ver_name_out[0] =~  /(\d+\.?\d+)/ ;
        $DistroVers = "$1";
        @split_again = split(/\./, $DistroVers);;
        $DistroVers = $split_again[0];
        return 0;
    } else {
        &printLog("ERROR: Supported distros are Red Hat Enterprise Linux Server and SUSE Enterprise Linux Server.\n", $ERROR);
        return 1;
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : startService()
# Calls         : none
# Called by     : callConfigFunctions
# Globals       : none
# Input Param   : service name (xinetd or identd)
# Return Value  : none 
#
# Confirm that xinetd, authd is installed. Confirm that xinetd
# can run at all run-levels. Setup xinetd to run at boot time. Start
# the xinetd service
#
#------------------------------------------------------------------------------

sub startService {

    my $service = shift;
    my $auth_type = shift; 
    my $rpmName = "", my $cmd, my $ret = 0;
    my $op, my $error_val = 0;
    my $newos = 0;
    ++$Stage;
    $error_summary[$Stage] = $ERROR;
    &printLog($error_text[$Stage]);
    if (-f "/usr/bin/systemctl") {
        $newos = 1;
    }
    # Get the rpm name associated with the service
    if ($auth_type ne "") {
        if ($auth_type eq "auth") {
            $rpmName = "authd";
        }

        # Confirm that the service rpm is installed
        $cmd = "rpm -qa | grep $rpmName";
        ($ret) = runandCaptureCommand($cmd);
        # Check the return value of the above command for failure 
        if ( $ret != 0) {
            &printLog("ERROR: $auth_type is not installed. Please " .
            "install $rpmName rpm from distro CD.\n", $ERROR);
            return;
        }
        if ($newos) {
            $cmd = "systemctl enable $service";
        } else {
            $cmd = "/sbin/chkconfig $auth_type on";
        }
        ($ret) = runandCaptureCommand($cmd);

    }
    # Setting the parent service to run at boot.
    if (! $newos) {
        if ($service =~ /xinetd/) {
            $cmd = "/sbin/chkconfig --level 35 $service on 2>$errorFile";
        } elsif ($service =~ /identd/) {
            $cmd = "/sbin/chkconfig --level 35 $service on 2>$errorFile";
        }
        ($ret) = runandCaptureCommand($cmd);
        # Check the return value of the above command for failure 
        if ( $ret != 0) {
            my $err = `cat $errorFile`;
            &printLog("ERROR: $err\n", $ERROR);
            $error_val = 1
        }
        &printLog("NOTE: /sbin/chkconfig --level 35 $service on\n");
    } else {
        &printLog("NOTE: systemctl enable $service\n");
    }
    
    # Start the service
    if ($newos) {
        $cmd = "systemctl restart $service";
    } else {
        $cmd = "service $service restart";
    }
    ($ret) = runandCaptureCommand($cmd);

    # Confirm that the service started
    $cmd = "ps -ef | grep -i '$service' | grep -v grep";
    ($ret , $op) = runandCaptureCommand($cmd);
    # Parse output to confirm that xinetd is running
    if (($ret ==0) && ($op =~ m/(.+)$service(.*)/ )) {
        &printLog("Successfully started $service service.\n");
    } else {
        # report failure.
        &printLog("ERROR: The $service Service failed to start. Please " .
        "check this and re-run this script.\n", $ERROR);
        $error_val = 1;
    }
    if (!$error_val) {
        $error_summary[$Stage] = $PASS;
    }
}
#------------------------------------------------------------------------------
#
# Subroutine    : removeUnwantedIP()
# Calls         : none
# Called by     : updateHostsFile
# Globals       : none
# Input Param   : array containing ip output for the node 
# Return Value  : array containing ip address 
#
# This subroutine returns the set of ip adresses to be populated in /etc/hosts
# It eliminates the virbr ip address. 
#
#------------------------------------------------------------------------------


sub removeUnwantedIP {

    my $type = shift;
    my @input = @_;
    my @output = (), my $ipv4 = 1;
    my $temp;
    foreach(@input) {
        if ($_) {
            next if ($_ =~ /virbr/);
            if ($type eq $ipv4) {
                #$_ =~ m/inet \K[\d.]+/;
                $_ =~ /(\d+\.\d+\.\d+\.\d+)/;
                my $temp = $1;
                if ($_ !~ /127.0.0.1/) {
                    push(@output, $temp);
                }
            } else {
                if ($_ =~ /inet6\s+(\S+)\s+scope (global|site)/ ) {
                    $_ =~ /inet6 (.*)\/(.*)/;
                    push(@output, $1);

                }
            }
        }
    }
    return @output;
}
#------------------------------------------------------------------------------
#
# Subroutine    : expand_ipv6()
# Calls         : none
# Called by     : ip_address_sort
# Globals       : none
# Input Param   : IPv6 address
# Return Value  : Expanded IPv6 address
#
# This subroutine returns the expanded ipv6 adresses for comparision purpose.
#
#------------------------------------------------------------------------------

sub
expand_ipv6 {
    my $arg = shift;
    if ($arg !~ /:/) {
        return $arg;
    }
    my ($front, $back) = split(/::/, $arg);
    my @result = split(/:/, $front);
    my @back = ();
    if (defined $back) {
        @back = split(/:/, $back);
    }
    my $gaplen = 8 - scalar(@result) - scalar(@back);
    for (my $i = 0; $i < $gaplen; $i++) {
        push(@result, "0");
    }
    push(@result, @back);
    my @final;
    for (@result) {
        if (length($_) < 4) {
            push @final, ("0" x (4 - length($_))) . $_;
        } else {
            push @final, $_;
        }
    }
    return join(':', @final);
}
#------------------------------------------------------------------------------
#
# Subroutine    : ip_address_sort()
# Calls         : expand_ipv6
# Called by     : updateHostsFile
# Globals       : none
# Input Param   : IP key from iphost hash 
# Return Value  : comparision value of ip address (0, 1 or -1)
#
# This subroutine returns the sorted ip adresses from the arrary of ip IPs.
#
#------------------------------------------------------------------------------

sub
ip_address_sort{
    if ($a !~ /:/ or $b !~ /:/) {         # at least one ipv4
        if ($a !~ /:/ and $b !~ /:/) {    # both ipv4, sort normally
            my ($a1, $a2, $a3, $a4) = split(/\./, $a);
            my ($b1, $b2, $b3, $b4) = split(/\./, $b);
            if ($a1 == 127) {
                if ($b1 == 127) {
                   return 0;
                } else {
                    return -1;
                }
            } else {
                if ($b1 == 127) {
                    return 1;
                } else {
                    return ((($a1 * 256 + $a2) * 256 + $a3) * 256 + $a4) <=>
                        ((($b1 * 256 + $b2) * 256 + $b3) * 256 + $b4);
                }
            }
        } elsif ($a !~ /:/) {        # a is ipv4, it wins
            return -1;
        } else {                     # b is ipv4, it wins
            return 1;
        }
    } else {
            return &expand_ipv6($a) cmp &expand_ipv6($b);
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : updateHostsFile()
# Calls         : -
# Called by     : callConfigFunctions
# Globals       : %IPv4NodeInfoHash, %IPv6NodeInfoHash
# Input Param   : none
# Return Value  : none 
#
# This subroutine updates the /etc/hosts file in the system. It executed ip
# on every node provided as input to the script and populates their entries
#
#------------------------------------------------------------------------------

sub updateHostsFile { 

    my @currentData, my @lineData, my @fileData, my $lineNo;
    my $fileChange = 0, my $ret, my $cmd, my $op;
    my (%iphost, %qshost);
    my $ip = "", my $value;
    my $hash_size = 0;
    my (@output , @ipv6);
    my $hostname, my $msg;
    my $localhost = 0, my $localhost6 = 0; 
    my $error_count = 0, my $count = 0;
    my $is_newfile = 0, my $ipv4 = 1, my $ipv6 = 2;
    ++$Stage;
    $error_summary[$Stage] = $ERROR;
    if(!$update_host) {
        &printLog($error_text[$Stage]);
    }
    if($update_host) {
        &printLog("\n\tUpdating name resolution in /etc/hosts file.\n");
    }
    # Back up /etc/hosts to /etc/hosts.original
    &createBackup("/etc/hosts");
    $cmd = "perl -lne 'END { print \$. }' /etc/hosts 2> $errorFile";
    ($ret , $op) = runandCaptureCommand($cmd);
    if ($op == 0) { 
        push(@lineData, "# The entries are of the following form:\n");
        push(@lineData, "# <internet address>    <fully qualified domain name> <aliases>\n");
        $is_newfile = 1;
    } else {
        $is_newfile = 0;
    }
    my @my_output;
    foreach my $node (@hostList) {
        if ($node eq $shortHostName) {
            @output = `ip -4 -o addr show 2>$errorFile`;
        } else { 
            if($update_host) {
                @output = `$SGSBIN/cmexec $node ip -4 -o addr show 2>$errorFile`;
            } else {
                @output = `$ssh_cmd root\@$ipHostPair{$node} 'ip -4 -o addr show 2>$errorFile'`;
            }
        }
        if ($? != 0) {
            printLog("ERROR: Unable to find ip address on node $node\n\tError ".
            "while running ip command.", $ERROR);
            ++$error_count;
        }
        @output = removeUnwantedIP($ipv4, @output);
        foreach (@output) {
            chomp($_);
            $iphost{$_} = $node; 
        }
    }
    if ($error_count) {
        return ;
    }
    if ($is_quorum) {
        foreach my $key (keys %qsHostPair) {
            $qshost{$qsHostPair{$key}} = $key;
        }
    } 
    if ($DistroName =~ /SLES/i) {
        foreach my $host (@hostList) {
            if($update_host) {
                @output = `$SGSBIN/cmexec $host ip -6 -o addr show 2>$errorFile`;
            } else {
                @output = `$ssh_cmd root\@$ipHostPair{$host} 'ip -6 -o addr show 2>$errorFile'`;
            }
            @output = removeUnwantedIP($ipv6, @output);
            foreach(@output) {
                $iphost{$_} = $host;
            }
        }
    }

    #  Add 127.0.0.1 and ::1 entry to the new file
    if ($is_newfile == 1) {
        $msg = "127.0.0.1\t\tlocalhost\tloopback\n";
        push(@lineData, $msg);
        $msg = "::1\t\tlocalhost localhost6\tloopback6\n";
        push(@lineData, $msg);
    }

    my $ret_val = 0;

    # Check for 127.0.0.1 and ::1 entries in  existing /etc/hosts file, 
    # if it doesn't exist  add those entries into /etc/hosts file.

    if ($is_newfile == 0) {
        $cmd = "grep '^[^#]*127.0.0.1' /etc/hosts";
        $ret_val = runandCaptureCommand($cmd);
        if ($ret_val == 0) {
            $msg = "127.0.0.1\t\tlocalhost\tloopback\n";
            push(@lineData, $msg); 
        } 
        $cmd = "grep '^[^#]*::1' /etc/hosts";
        $ret_val = runandCaptureCommand($cmd);
        if ($ret_val == 0) {
            $msg = "::1\t\tlocalhost localhost6\tloopback6\n";
            push(@lineData, $msg);
        }
    }

    foreach  my $ipaddr (sort ip_address_sort(keys %iphost)) {
        my $node_name =  $iphost{$ipaddr};
        if ($is_newfile == 0) {
            $cmd = "grep '^[^#]*$ipaddr' /etc/hosts";
            $ret_val = runandCaptureCommand($cmd);
        }
        if (!$ret_val) {
            $ipaddr =~ s/\\\//\//;
            $msg = "$ipaddr\t\t$fullnameofhost{$node_name}\t$node_name\n";
            push(@lineData, $msg);
        }
    }
 
    while (($ip, $value) = each %qshost) {
        if ($is_newfile == 0 ) {
            $cmd = "perl -ni -e 'print unless /\\s*$ip\\s*$fullnameofquorum{$value}".
                   "\\s*$value\\s*/' /etc/hosts";
            runandCaptureCommand($cmd);
        }
        $msg = "$ip\t\t$fullnameofquorum{$value}\t$value\n";
        push(@lineData, $msg);
         
    }
    
    my $data_count = scalar(@lineData);
    my @commentdata;
    push(@commentdata, "# Added by Serviceguard on $timeStamp\n");

    if ($is_newfile == 0) {
        if($data_count >1) {
            unshift(@lineData, @commentdata);
        }
        if (&writeToFile(">>/etc/hosts",\@lineData)) {
            ++$error_count;
        }
    } else {
        if($data_count >1) {
            unshift(@lineData, @commentdata);
        }
        if (&writeToFile(">/etc/hosts",\@lineData)) {
            ++$error_count;
        }
    }

    if (!$error_count) {
        if($data_count >1) {
            &printLog("NOTE: Added the following entries in /etc/hosts.\n");
        }
        foreach (@lineData) {
            &printLog($_);
        }
        &printLog("/etc/hosts file updated successfully.\n");
        $error_summary[$Stage] = $PASS;

    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : createCmclnodelistFile()
# Calls         : none
# Called by     : main()
# Globals       : %IPv4NodeInfoHash, %IPv6NodeInfoHash
# Input Param   : none
# Return Value  : none
#
# This function creates the cmclnodelist file in the $SGCONF dir and updates it
# with the information provided. This file will then be copied to all the other
# nodes in the cluster.
#
#------------------------------------------------------------------------------

sub createCmclnodelistFile {

    my $lineData, my @fileData;
    my $cmClNodeListFile = "$Sgconf/cmclnodelist";
    my $cmClNodeListFile2 = "$Sgconf/cmclnodelist.tmp";
    my $fail = 0, my $is_copied = 0;
    ++$Stage;
    $error_summary[$Stage] = $ERROR;
    my $num = $Stage + 1;
    $error_text[$Stage] = "$num: Create $cmClNodeListFile\n";
    printLog($error_text[$Stage]);
    # initial comment in the cmclnodelist file
    $lineData = "\n".
    "############################################################\n". 
    "\n# This file is automatically generated\n". 
    "\n# Do not edit this file!\n". 
    "\n# Serviceguard uses this file only to authorize access to an\n". 
    "\n# unconfigured node. Once the node is configured,\n". 
    "\n# Serviceguard will not consult this file\n". 
    "\n############################################################\n\n";

    if (open (NODELIST, "$cmClNodeListFile")) {
        while (<NODELIST>) {
            next if $_ =~ m/^#/;
            next if /^(\s)*$/;
            $lineData = $lineData . $_;
        }
        $lineData = $lineData  . "\n";
        close NODELIST;
    }

    push(@fileData, $lineData);

    # make entry for each host in hosts with user as root
    # IPv4
    foreach (@hostList) {
        $_ =~ s/([a-zA-Z0-9-_]+)(\..+){0,3}/$1/;
        $lineData = $_ . "\troot\n";
        push(@fileData, $lineData);
    }

    # for each non-root user make corresponding entries with 
    # all hosts - both IPv4 and IPv6
    foreach my $i (@nonroot) {
        foreach (@hostList) {
            $_ =~ s/([a-zA-Z0-9-_]+)(\..+){0,3}/$1/;
            $lineData = $_ . "\t" . $i . "\n";
            push(@fileData, $lineData);
        }
    }
    if (!writeToFile(">$cmClNodeListFile", \@fileData)) {        
        &printLog("Created the $Sgconf/cmclnodelist file with the user".
                  " input.\n\n");
    } else {
        return 1;
    }

    my %seen = ();
    if (!open (TEMP_NLIST, ">$cmClNodeListFile2")) {
        &printLog("Cannot open file $cmClNodeListFile2 .\n", $ERROR);
        return 1;
    }
    if (!open (TEMP_NODELIST, "<$cmClNodeListFile")) {
        &printLog("Cannot open file $cmClNodeListFile .\n", $ERROR);
        return 1;
    }

    while(<TEMP_NODELIST>){
        if ($_ =~ m/^#/) {
            print TEMP_NLIST ;
        } else {
            $seen{$_}++;
            next if $seen{$_} > 1;
            print TEMP_NLIST ;
        }
     }

    close (TEMP_NODELIST);
    close (TEMP_NLIST);

    my $cmd = "mv $cmClNodeListFile2 $cmClNodeListFile";
    ($ret) = runandCaptureCommand($cmd);
    if ($ret != 0) {
        &printLog("Cannot rename $cmClNodeListFile2 to $cmClNodeListFile .\n", $ERROR);
        return 1;
    }

    foreach my $node (@hostList) {
        if ($node ne $shortHostName) {
            $is_copied = 1;
            $cmd = "scp $Sgconf/cmclnodelist root\@$ipHostPair{$node}:$Sgconf/."; 
            ($ret) = runandCaptureCommand($cmd);
            if ($ret != 0) {
                &printLog("ERROR: Couldn't copy $Sgconf/cmclnodelist file".
                " to $node.\n", $ERROR);
                $fail = 1;
            }
        }
    }
    if (!$fail) {
        if ($is_copied eq 1) {
            &printLog("$cmClNodeListFile is copied to all the nodes\n");
        }
        $error_summary[$Stage] = $PASS;
    }

}

#------------------------------------------------------------------------------
#
# Subroutine    : setManPathVariable()
# Calls         : none
# Called by     : callConfigFunctions
# Globals       : none
# Input Param   : none
# Return Value  : none
#
# This subroutine is to add the Serviceguard MANPATH value the /etc/manpath.config
# file in SLES and man.config file in RHEL.
#
#------------------------------------------------------------------------------

sub setManPathVariable {

    my $sgManPath, my $sgman;
    my $qsManPath;
    my $manPathFile;
    my @fileData, my $ret = 0;
    my $searchEntry, my $qssearchentry, my $cmd;
    my $sgFlag = 0, my $write_error = 0;
    my $qsFlag = 0, my $qsInstalled = 0;
    ++$Stage;
    $error_summary[$Stage] = $ERROR;
    &printLog($error_text[$Stage]);
    if ($DistroName eq "SLES") {
        $sgManPath = "MANDATORY_MANPATH \\/opt\\/cmcluster\\/doc\\/man";
        $manPathFile = "/etc/manpath.config";
        $searchEntry = "\\s*MANDATORY_MANPATH\\s*\\/opt\\/cmcluster\\/doc\\/man\\s*";
        if ($quorum_installed) {
            $qssearchentry = "\\s*MANDATORY_MANPATH\\s*\\/opt\\/qs\\/doc\\/man\\s*";
            $qsManPath = "MANDATORY_MANPATH \\/opt\\/qs\\/doc\\/man\n";
        }
    } elsif ($DistroName eq "RHEL") {
        if ($DistroVers >= 7) {
            $manPathFile = "/etc/man_db.conf";
            $sgManPath = "MANPATH_MAP     \\/usr\\/local\\/cmcluster\\/bin ".
                         "\\/usr\\/local\\/cmcluster\\/doc\\/man";
            $searchEntry = "\\s*MANPATH_MAP\\s*\\/usr\\/local\\/cmcluster\\/bin\\s*".
                           "\\/usr\\/local\\/cmcluster\\/doc\\/man";
        } else {
            $manPathFile = "/etc/man.config";
            $sgManPath = "MANPATH \\/usr\\/local\\/cmcluster\\/doc\\/man";
            $searchEntry = "\\s*MANPATH\\s*\\/usr\\/local\\/cmcluster\\/doc\\/man";
        }
        if ($quorum_installed) {
            if ($DistroVers >= 7) {
                $qssearchentry = "\\s*MANDATORY_MANPATH\\s*\\/usr\\/local\\/qs\\/doc\\/man\\s*";
                $qsManPath = "MANDATORY_MANPATH\t\t\t\\/usr\\/local\\/qs\\/doc\\/man";

            } else {
                $qssearchentry = "\\s*MANPATH\\s*\\/usr\\/local\\/qs\\/doc\\/man\\s*";
                $qsManPath = "MANPATH \\/usr\\/local\\/qs\\/doc\\/man\n";
            }
        }
    }
    if (-e $manPathFile) {
        &createBackup("$manPathFile");
    } else {
        &printLog("ERROR: The file $manPathFile is not present\n", $ERROR);
        return;
    }
    # Search and delete SG and Qs manpaths from the file if they exist
    &searchAndDelete($searchEntry, $manPathFile);
    if ($quorum_installed) {
        &searchAndDelete($qssearchentry, $manPathFile);
    }
 
    if ($sgManPath =~ /(\w+)\s*/) {
        $sgman = $1;
    } 
    # Insert the Serviceguard manpath in the file :
    $cmd = "perl -i -pe '\$a=1 if(!\$a && s/^\\s*$sgman\\s*\\//$sgManPath".
           "\n\$&/s)' $manPathFile 2>$errorFile";
    if (&executePerlCommands($cmd)) {
        &printLog("ERROR: Could not update the file $manPathFile with ".
                  "Serviceguard manpath.\n", $ERROR);
        $write_error++;
    }
   
    if ($quorum_installed) { 
        # Insert QS manpath in the file 
        if ($DistroVers >= 7) {
            if ($qsManPath =~ /(\w+)\s*/) {
                $sgman = $1;
            }
        }
        $cmd = "perl -i -pe '\$a=1 if(!\$a && s/^\\s*$sgman\\s*\\//$qsManPath".
               "\n\$&/s)' $manPathFile 2> $errorFile";
        if (&executePerlCommands($cmd)) {
            &printLog("ERROR: Could not update the file $manPathFile with".
                      "quorum server manpath.\n", $ERROR);
            $write_error++;
        }
    }

    if (!$write_error) {
        &printLog("Updated MANPATH in $manPathFile file.\n");
        $error_summary[$Stage] = $PASS
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : checkPortsAvailabiliy()
# Calls         : readNetstatPort(); verifyServiceFile();
# Called by     : callConfigFunctions
# Globals       : none
# Input Param   : none
# Return Value  : none
#
# This subroutine calls the readNetstatPort() and the verifyServiceFile()
# subroutines.
#
#------------------------------------------------------------------------------

sub checkPortsAvailability {

    my $ret = 0;
    $error_summary[$Stage] = $ERROR;
    # Get the netstat output in the %NstatHash
    &printLog($error_text[$Stage]);
    if (&readNetstatPort) {
        return 1;
    }

    # Parse and verrify the /etc/services file for the specific ports
    if (!&verifyServiceFile("/etc/services")) {
        &printLog("Confirmed that the ports required are free.\n");
        $error_summary[$Stage] = $PASS; 
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : readNetstatPort
# Calls         : none
# Called by     : checkPortAvailability()
# Globals       : %NstatHash
# Input Param   : none
# Return Value  : none
#
# This subroutine executes the netstat command and parses the output to extract
# the information about the tcp/udp ports and populates the global hash -
# NstatHash - with the data.
#
#------------------------------------------------------------------------------

sub readNetstatPort {
    my $cmd, my $ret = 0;
    # Execute the netstat command
    $cmd = "/bin/netstat > /tmp/nstat.txt 2>$errorFile";
    # Check the return value of the above command for failure
    ($ret) = runandCaptureCommand($cmd);
    if ($ret != 0) {
        &printLog("ERROR : netstat failed.\n", $ERROR);
        return 1;
    }
    
    # read the output for parsing
    if (!open (NSTAT, "/tmp/nstat.txt")) {
        &printLog("Cannot open temporary file nstat.txt.\n", $ERROR);
        return 1;
    }
    while (<NSTAT>) {
        # For ever relevant port detail, make entries appropriately.
        next unless (m/^\s-(tcp|udp)\s+/);
        my @fld = split(/\s+/);
        $fld[3] =~ s/^.+:(.+)$/$1/;
        # Populate the %NstatHash hash
        $NstatHash{"$fld[3]/$fld[0]"} = 1;
    }
    close NSTAT;
    return 0;
}

#------------------------------------------------------------------------------
#
# Subroutine    : verifyServiceFile
# Calls         : none
# Called by     : checkPortAvailability()
# Globals       : %NstatHash, %SGPortHash
# Input Param   : The file where port/service details are listed.
# Return Value  : none
#
# The function checks if the port number exists in the /etc/services file and
# if it does, confirms that it is reserved for SG. If there is no entry for
# these ports in the /etc/services file, then it checks in the global Hash
# that is already populated. If there are entries in the hash for the port(s)
# being looked for, and if they are being used by other programs, the script
# prompts the user about these ports and exits
#
#------------------------------------------------------------------------------

sub verifyServiceFile {

    my ($svcFile) = @_;
    my %sgHash = %SGPortHash;
    my $noOfError = 0;
    my $msgText = "";
    # Open the /etc/services file for parsing
    if (!open (SERVICE, $svcFile)) {
        &printLog("ERROR: Can't open file $svcFile.\n", $ERROR);
        return 1;
    }
    &printLog("Started verifying $svcFile file..... \n");
    while (<SERVICE>) {
        # Ignore comments and blank lines
        next unless (m/^[a-z]/);

        # Remove newline character
        chomp;
        my ($svcName, $portNo, $protoName, $alias) = split(/[\t ]+/);
        if (exists($sgHash{$portNo})) {
            if ($sgHash{$portNo} ne $svcName) {
                &printLog("ERROR: $portNo allocated to ".
                "another service '$svcName': supposed to be".
                " '$sgHash{$portNo}'.\n", $ERROR);
                $noOfError++;
            } else {
                # if port-service combination is as expected,
                # remove it from the hash
                delete($sgHash{$portNo});
            }
        }
    }
    close SERVICE;

    if ($noOfError) {
        my $msgText = "ERROR: There was $noOfError error(s) during ".
                      "verfication of service file.\n\tPlease specify the ports as".
                      " mentioned in Release Notes. Re-run this script.\n";

        printLog($msgText);
        return 1;
    }
    foreach my $port (sort keys %sgHash) {
        if (exists($NstatHash{$port})) {
            &printLog("ERROR: $port not exist in ". 
            "$svcFile file but currently being ".
            "used by another process.\n", $ERROR);
            $noOfError++;
        }
    }
    # If flag for error is set, then report error
    if ($noOfError) {
        my $msgText = "ERROR: There was $noOfError error(s) during ".
                      "verfication of service file.\n\tPlease specify the ports as".
                      " mentioned in Release Notes. Re-run this script.\n";

        &printLog($msgText);
        return 1;
    } else {
        # Else success
        &printLog("Verification of service file completed".
                  " successfully.\n");
        return 0;
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : validateSuperUser
# Calls         : none
# Called by     : main()
# Globals       : none
# Input Param   : command to be executed 
# Return Value  : none
#
# The script is required to be run by the superuser only. Validate this in this 
# subroutine and return 1 if some other user is trying to execute the script. 
# The main function exits if the return value of this subroutine is 1.
#
#------------------------------------------------------------------------------

sub validateSuperUser {

    my $command = "whoami";
    my $userInfo, my $ret;

    # Check the command whoami
    ($ret, $userInfo) = runandCaptureCommand($command);
    # Check the return value of the command for failure.
    &printLog("ERROR : $command failed.\n", $EXIT) if ($ret != 0);

    chomp $userInfo;

    # if user is not root, report error and return 1.
    if ($userInfo ne "root") {
        &printLog("ERROR: This script can be executed only by ".
                  "superuser. You are currently logged in as $userInfo.\n", $EXIT);
    } 
}

#------------------------------------------------------------------------------
#
# Subroutine    : printLog
# Calls         : none
# Called by     : main part of script and all other subroutines
# Globals       : none
# Input Param   : the message to be printed to the log file, [code for exit]
# Return Value  : exits from script if code = $EXIT
#
# This subroutine is for logging all messages.
#
#------------------------------------------------------------------------------

sub printLog {

    # Get message to be printed in the Log
    my ($msg, $code) = @_;
    my $logMsg = "";
    my $cntSpace = 0;
    my $space ="";
    my $is_prereq = 0;

    # remove the temporary error file and exit if $EXIT argument is passed
    if(defined $code) {
        if($code == $EXIT) {
            if( $msg =~ m/^ERROR.+/ ) {
                $no_of_errors++;
            }
            $space = "\t";
            if (!$check_pre_req && !$pre_req_error) {
                $msg .= "\n\tFAILURE: The system configuration failed with $no_of_errors".
                        " error(s) and $no_of_warnings warning(s)\n\n";
            }
        } elsif ($code == $ERROR) {
            $no_of_errors++;
            $space = "\t";
        } elsif ($code == $WARNING) {
            $no_of_warnings++;
            $space = "\t";
        }
    }
    elsif ( $msg =~ m/^([0-9]+): .+/ ){
        $space = "\n"." "x 4;
    } elsif ($msg =~ /^FAILURE:/) {
        $space = "\n\t";
    } else {
        $space = "\t";
    }
    $logMsg = $space.$msg;
    print LOG_FILE $logMsg;
    print STDOUT $logMsg;

    if (defined $code && ($code == $EXIT)) {
        if (-e $remoteFile) {
            print "$LogPathMsg";
            &printLog("$dash\n");
            cleanup();
            if (!$check_pre_req) {
                my $summary = join('#',@error_summary);
                print $summary;
            } elsif($check_pre_req || $pre_req_error) {
                my $summary = join('#', @error_prereq);
                print $summary;
            }
        } else {
            my $summary = join('#',@error_summary);
            # Do not print the summary if -p option is passed.
            if ($check_pre_req || $pre_req_error) {
                $is_prereq = 1;
                $summary = join('#',@error_prereq);
            } 
            if (defined $node_skip{$shortHostName}) {
                &consolidateErrors($summary, $shortHostName, $is_prereq);
            }
            &generateSummary($is_prereq);
            &printOutputSummary();
            print "$LogPathMsg";
            &printLog("$dash\n");
            if ($pre_req_error) {
                # Exiting since errors found while checking pre-requisites.
                # need to celanup all the nodes.
                cleanup_all_nodes();
                &cleanupAuthKeys();
            } else {
                cleanup();
            }
            
        }
        close(LOG_FILE);
        exit 1;
    } 
}

#------------------------------------------------------------------------------
#
# Subroutine    : ipTabRules
# Calls         : none
# Called by     : sgFirewallRules()
# Globals       : %IpRule
# Input Param   : String to be added to the iptables rules
# Return Value  : none
#
# This subroutine sets up the iptables and ip6tables for specified ports only
# for RHEL environment.
#
#------------------------------------------------------------------------------
sub ipTabRules {
    my $rule = shift;
    $IpRule .= "iptables $rule\n";
    $IpRule .= "ip6tables $rule\n";
}

#------------------------------------------------------------------------------
#
# Subroutine    : sgFirewallRules
# Calls         : ipTabRules, printLog
# Called by     : main()
# Globals       : %IPv4NodeInfoHash, %IPv6NodeInfoHash
# Input Param   : none
# Return Value  : none
#
# This subroutine sets up the iptables rules for all the required ports that 
# Serviceguard, and the related rpms use as mentioned in the documents.
# We create a separate rules-file.
#
# NOTE : Code block for SUSE needs revisiting. This piece of code is not tested
# with SUSE. Once the testing is done please remove this comment.
#
#------------------------------------------------------------------------------
sub sgFirewallRules{

    my $SG_FIREWALL_RULES = "/tmp/sg_firewall_rules";
    my $cmd , my $ret = 0;
    $IpRule = "";
    my $rule1 = "";
    my $space = "  ";
    my $newos = 0;
    ++$Stage;
    if ($Stage > 9) { $space = " "};
    $error_summary[$Stage] = $ERROR;
    printLog($error_text[$Stage]);
    
    # Dynamic Port Range
    $cmd = "cat /proc/sys/net/ipv4/ip_local_port_range";
    ($ret, my $temp) = runandCaptureCommand($cmd);
    my $lower; my $upper;

    if($temp =~ m/(\d+)[\s\t]+(\d+)/) {
        $lower = $1;
        $upper = $2;
    } 
    if (-f "/usr/bin/systemctl") {
        $newos = 1;
    }
    # Ports required by Serviceguard
    if ($DistroName eq "RHEL") {
        # check if firewall if enabled or disabled
        if ($newos) {
            $cmd = "systemctl status iptables > $errorFile 2>&1";
        } else {
            $cmd = "service iptables status > $errorFile 2>&1";
        }
        ($ret) = runandCaptureCommand($cmd);
        if ($newos) {
            $cmd = "systemctl status ip6tables > $errorFile 2>&1";
        } else {
            $cmd = "service ip6tables status > $errorFile 2>&1";
        }
        (my $ret_6) = runandCaptureCommand($cmd);
        if ($ret ne 0 || $ret_6 ne 0) {
            $firewall_disabled = 1;
        }
        # Delete the rules under Serviceguard chain and the 
        # chain itself if it exists.
        ipTabRules("-F Serviceguard 2>/dev/null");
        ipTabRules("-D INPUT -j Serviceguard 2>/dev/null");
        ipTabRules("-X Serviceguard 2>/dev/null");
        if ($newos) {
            $IpRule .= "/sbin/iptables-save > /etc/sysconfig/iptables\n";
            $IpRule .= "/sbin/ip6tables-save > /etc/sysconfig/ip6tables\n";
        } else {
            $IpRule .= "service iptables save\n";
            $IpRule .= "service ip6tables save\n";
        }
        &ipTabRules("-N Serviceguard 2>/dev/null");
        &ipTabRules("-I INPUT -j Serviceguard");

        # rules for ident, hacl-cfg and hacl-hb ports
        &ipTabRules("-A Serviceguard -p tcp --dport ident -j ACCEPT");
        &ipTabRules("-A Serviceguard -p tcp --dport hacl-cfg -j ACCEPT");
        &ipTabRules("-A Serviceguard -p tcp --dport hacl-hb -j ACCEPT");
        &ipTabRules("-A Serviceguard -p udp --dport hacl-cfg -j ACCEPT");
        &ipTabRules("-A Serviceguard -p udp --dport hacl-hb -j ACCEPT");
        &ipTabRules("-A Serviceguard -p tcp --dport hacl-local -j ACCEPT");

        # Config deamon probing will initiate connections from ephemeral
        # port range
        my $range = "$lower:$upper";
        &ipTabRules("-A Serviceguard -p udp --dport $range -j ACCEPT");
        # tcp with cmquerycl -c  
        &ipTabRules("-A Serviceguard -p tcp --dport $range -j ACCEPT");

    } elsif ($DistroName eq "SLES") {
        # Use the SuSEfirewall2 command for SLES
        # Using a separate external script in case of SLES can lead to 
        # a confusion about whether our rules need to be written to an 
        # existing external script or should our script be used as a 
        # base and allow other rules to be added later. 
        $IpRule .= "/sbin/SuSEfirewall2 open EXT TCP ident hacl-hb".
                   " hacl-cfg hacl-local $lower:$upper\n";
        $IpRule .= "/sbin/SuSEfirewall2 open EXT UDP hacl-hb hacl-cfg".
                   " $lower:$upper\n";
    }
    
    # iptables rules for ports required by cmsnmp
    $cmd = "rpm -q serviceguard-snmp &> /dev/null";
    ($ret) = runandCaptureCommand($cmd);
    if ($ret == 0) {
        if ($DistroName eq "RHEL") { 
            &ipTabRules("-A Serviceguard -p udp --dport snmp -j ACCEPT");
            &ipTabRules("-A Serviceguard -p udp --dport snmptrap -j ACCEPT");

        } elsif ($DistroName eq "SLES") {
            $IpRule .= "/sbin/SuSEfirewall2 open EXT UDP snmp snmptrap\n";
        }
    }
  
    # iptables rules for ports required by WBEM providers
    $cmd = "rpm -q serviceguard-providers &> /dev/null";
    ($ret) = runandCaptureCommand($cmd);
    if ($ret == 0) {
        if ($DistroName eq "RHEL") {
            &ipTabRules("-A Serviceguard -p tcp --dport wbem-https -j ACCEPT");
            &ipTabRules("-A Serviceguard -p tcp --dport wbem-http -j ACCEPT");

        } elsif ($DistroName eq "SLES") {
            $IpRule .="/sbin/SuSEfirewall2 open EXT TCP wbem-http wbem-https\n";
        }
    } 

    # iptables rules for ports required by Quorum Server
    $cmd = "rpm -q qs &> /dev/null";
    ($ret) = runandCaptureCommand($cmd);
    if ($ret == 0) {
        if ($DistroName eq "RHEL") {
            &ipTabRules("-A Serviceguard -p tcp --dport hacl-qs -j ACCEPT");

        } elsif ($DistroName eq "SLES") {
            $IpRule .= "/sbin/SuSEfirewall2 open EXT TCP hacl-qs\n";
        }
    }
   
    # iptables rules for ports required for connecting to SMH for SGMgr 
    $cmd = "rpm -q serviceguard-manager &> /dev/null";
    ($ret) = runandCaptureCommand($cmd);
    if ($ret == 0) {
        if ($DistroName eq "RHEL") {
            &ipTabRules("-A Serviceguard -p tcp --dport 5511 -j ACCEPT");
            &ipTabRules("-A Serviceguard -p tcp --dport 5522 -j ACCEPT");
        } elsif ($DistroName eq "SLES") {
            # The rules below are not listed in the firewall whitepaper.
            $IpRule .= "/sbin/SuSEfirewall2 open EXT TCP 5511".
                       " 5522\n";
        }
    }

    # Default firewall setting on SLES disables ssh. 
    # Keep it open for this script
    if ($DistroName eq "SLES") {
        $IpRule .= "/sbin/SuSEfirewall2 open EXT TCP ssh\n";
    } 
    if ($DistroName eq "RHEL" && $firewall_disabled eq 1) {
        if ($newos) {
            $IpRule .= "/sbin/iptables-save > /etc/sysconfig/iptables\n";
            $IpRule .= "/sbin/ip6tables-save > /etc/sysconfig/ip6tables\n";
            $IpRule .= "systemctl start iptables\n";
            $IpRule .= "systemctl start ip6tables\n";
        } else {
            $IpRule .= "service iptables save\n";
            $IpRule .= "service ip6tables save\n";
            $IpRule .= "service iptables start\n";
            $IpRule .= "service ip6tables start\n";
        }
    }

    if (!&writeToFile(">$SG_FIREWALL_RULES", \$IpRule)) {
        &printLog("Firewall rules generated successfully.\n");
        $error_summary[$Stage] = $PASS;
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : sgFirewallSetup
# Calls         : none
# Called by     : main()
# Globals       : none
# Input Param   : none
# Return Value  : none
#
# Make the file where the firewall rules have been generated, a script and
# save the rules on RHEL / start the service on SLES. 
# On RHEL, we also add the list of ports to the 
# /etc/sysconfig/system-config-securitylevel, to avoid the iptables rules that
# are generated for SG/LX on RHEL are not lost when user uses the GUI.
#
# NOTE : Code block for SUSE needs revisiting. This piece of code is not tested
# with SUSE. Once the testing is done please remove this comment.
#------------------------------------------------------------------------------
sub sgFirewallSetup {

    my $errorVal = 0;
    my $SG_FIREWALL_RULES = "/tmp/sg_firewall_rules";
    my $fwServ;
    my @fileData;
    my $cmd , my $ret = 0, my $op;
    my $port_value;
	
    ++$Stage;
    $error_summary[$Stage] = $ERROR;
    printLog($error_text[$Stage]);
    # make the file executable since it is a script
	$cmd = "chmod +x $SG_FIREWALL_RULES";
	($ret) = runandCaptureCommand($cmd);
    if ($ret != 0) {
        &printLog("ERROR : chmod +x $SG_FIREWALL_RULES failed.\n", $ERROR);
        $errorVal = 1;
    }
    if ($DistroName eq "RHEL") {
        $fwServ = "iptables";
        if ($firewall_disabled) {
            &printLog("WARNING: Firewall is disabled\n\tYou may consider ".
                  "starting the $fwServ service to setup the firewall.\n\t".
                  "The firewall rules for Serviceguard have been generated ".
                  "in $SG_FIREWALL_RULES file.\n\tPlease execute this script ".
                  "with the command:\n\t$SG_FIREWALL_RULES\n",
                  $WARNING);
             
            $errorVal = 1;
        } else {
            # execute the script to apply the rules.
            $cmd = "$SG_FIREWALL_RULES > $errorFile 2>&1";
            ($ret) = runandCaptureCommand($cmd);
            if ($ret != 0) {
                &printLog("ERROR : $SG_FIREWALL_RULES failed.\n", $ERROR);
                $errorVal = 1;
            }
            
            # save  the generated iptables rules.
            $cmd = "service iptables save 2>$errorFile";
            ($ret) = runandCaptureCommand($cmd);
            if ($ret != 0) {
                &printLog("ERROR : service iptables save failed.\n", $ERROR);
                $errorVal = 1;
            } else {
                &printLog("NOTE: Used the command 'service iptables save' to".
                          " save the firewall rules.\n");
            }
    
            # save  the generated ip6tables rules.
            $cmd = "service ip6tables save 2>$errorFile";
            ($ret) = runandCaptureCommand($cmd);
            if ($ret != 0) {
                &printLog("ERROR : service ip6tables save failed.\n", $ERROR);
                $errorVal = 1;
            } else {
                &printLog("NOTE: Used the command 'service ip6tables save' to".
                          " save the firewall rules.\n");
            }
        }
        
    } elsif ($DistroName eq "SLES") {
        $fwServ = "/sbin/SuSEfirewall2";

        # execute the script to apply the rules.
        # Suppressing all output of the command
        $cmd = "$SG_FIREWALL_RULES > $errorFile 2>&1";
        ($ret) = runandCaptureCommand($cmd);
        if ($ret != 0) {
            &printLog("ERROR : $SG_FIREWALL_RULES failed.\n", $ERROR);
            $errorVal = 1;
        }

        # Check if the firewall is enabled before setting the rules.
        $cmd = "$fwServ status 2>$errorFile";
        ($ret) = runandCaptureCommand($cmd);
        if ($ret != 0) {
            $op = `cat $errorFile`;
            if ($? != 0) {
                &printLog("ERROR: Cannot read file $errorFile.\n", $ERROR);
            }
 
            if ($op =~ m/SuSEfirewall2: SuSEfirewall2 not active/) {
                &printLog("WARNING: Firewall is disabled.\n\tYou may consider".
                    " starting the $fwServ service to setup the firewall using".
                    " the command:\n\t->$fwServ start\n\tThe Serviceguard ".
                    "specific firewall rules have been added to the firewall".
                    " service \n\tand will be activated when the firewall service".
                    " is started.\n", $WARNING);
                `rm -f $SG_FIREWALL_RULES`; 
                $errorVal = 1;
            }
        } else {
            # Start the firewall service 
            # Suppressing all output of the command
            $cmd = "/sbin/SuSEfirewall2 start 2>$errorFile &> /dev/null";
            ($ret) = runandCaptureCommand($cmd);
            if ($ret != 0) {
                &printLog("ERROR : /sbin/SuSEfirewall2 start failed.\n", $ERROR);
                $errorVal = 1;
            } else {
                &printLog("NOTE: Used the command '/sbin/SuSEfirewall2 start'".
                          " to restart the firewall service.\n");
            }
        }
    }

    if ($errorVal == 0) {
        &printLog("Firewall setup successfully.\n");
        #Remove the firewall file
        `rm -f $SG_FIREWALL_RULES`;
        $error_summary[$Stage] = $PASS;
    } else {
        &printLog("WARNING: Firewall script setup was not completely ".
        "successful.\n\tPlease check the firewall settings again.\n");
    }
}

#------------------------------------------------------------------------------
#
# Subroutine    : nsswitchConfSettings()
# Calls         : validateNsswitchConfEntry()
# Called by     : callConfigFunctions
# Globals       : none
# Input Param   : none
# Return Value  : none
#
# This subroutine checks the hosts entry in the /etc/nsswitch.conf file and
# confirms that "files" is the first value against the hosts. If it is not so
# then the change is made to make files the first value against the hosts.
#
#------------------------------------------------------------------------------
sub nsswitchConfSettings {

    ++$Stage;
    my $fname = "/etc/nsswitch.conf";
    my $hostentry, my $oldHost = "";
    $error_summary[$Stage] = $ERROR;
    printLog($error_text[$Stage]);

        # Populate host entry
        $hostentry = "hosts: files [NOTFOUND=continue UNAVAIL=continue] ";
        if (-e $fname) {
            &createBackup($fname);
        } else {
            &printLog("ERROR : File $fname is not present\n", $ERROR);
            return;
        } 
        # Get the host entry in the file 
        $cmd = "perl -ne '/^hosts/ && print' $fname 2> $errorFile";
        ($ret, $oldHost) = runandCaptureCommand($cmd); 

        if ($oldHost) {
            # Comment out the entry in the file
            $cmd = "perl -pi -e 's/$oldHost/#$oldHost/' $fname 2>$errorFile";
            runandCaptureCommand($cmd);
 
            if ($oldHost =~ m/nis/ && $oldHost =~ m/dns/) {
                $hostentry .= " dns [NOTFOUND=continue UNAVAIL=continue]";
                $hostentry .= " nis [NOTFOUND=return UNAVAIL=return]";
            }elsif ($oldHost =~ m/nis/) { 
                # Add the NOTFOUND and UNAVAIL values to the nis entry
                $hostentry .= " nis [NOTFOUND=return UNAVAIL=return]";
            }elsif ($oldHost =~ m/\s+dns/) {

                # Add the NOTFOUND and UNAVAIL values to the dns entry
                $hostentry .= " dns [NOTFOUND=return UNAVAIL=return]";
            }
            # Enter the entry in the file 
    
            $cmd = "perl -pi -e 's/#$oldHost/$hostentry\n\$&/' $fname";
        } else  {
            # If the hosts entry doesn't exist then add it to the file.
            # By default the resolution mechanism is DNS.
            $hostentry .= " dns [NOTFOUND=return UNAVAIL=return]";
            $cmd = "perl -pi -e 'print \"\n$hostentry\n\" if eof' $fname";
        }
        if (&executePerlCommands($cmd)) {
            &printLog("ERROR: Unable to update hosts entry in $fname.\n", $ERROR);        
        } else {
            &printLog("NOTE: The hosts entry in $fname has been".
                      " changed to\n\t$hostentry.\n");
            &printLog("The hosts entry in $fname is updated.\n");
            $error_summary[$Stage] = $PASS;
        }
          
}

#------------------------------------------------------------------------------
#
# Subroutine    : enableVGActivationProtection()
# Calls         : printLog
# Called by     : callConfigFunctions
# Globals       : none
# Input Param   : none
# Return Value  : none
#
# This subroutine adds the necessary entries to the /etc/lvm/lvm_<node>.conf
# file. It avoids making more than one entry for any host. This  subroutine 
# also makes entry in the volume_list if the root filesystem resides on a VG.
#
#------------------------------------------------------------------------------
sub enableVGActivationProtection {

    my $tags = "\\s*tags\\s*{\\s*hosttags\\s*=\\s*0\\s*}\\s*";
    my $uname = `uname -n`;
    chomp $uname;
    my $volmList = "activation { volume_list=[\"\@$uname\"] }\n";
    my $flag = 0, my $status = $PASS;
    my $fname = "/etc/lvm/lvm.conf", my $error_value = 0;
    my $cmd , my $ret = 0;
    my $entry, my $op = "";
    ++$Stage;
    $error_summary[$Stage] = $ERROR;
    printLog($error_text[$Stage]);
    if (-e $fname) {   
        &createBackup("$fname");
    } else {
        &printLog("ERROR: File $fname is not present\n", $ERROR);
        return 1;
    }
 
    # Search and delete hosttags entry if exists and make a new entry
    searchAndDelete($tags, $fname);
    $tags = "\\s*tags\\s*{\\s*hosttags\\s*=\\s*1\\s*}\\s*";
    searchAndDelete($tags, $fname);
    # Make a new entry for hosttags in the file
    $tags = "tags { hosttags = 1 }\n"; 
    if (!&writeToFile(">>$fname", \$tags)) {
        chomp $tags;
        &printLog("NOTE: Added $tags to $fname.\n");
    }else {
        $error_value = 1;
    }
    # Irrespective of whether /etc/lvm/lvm_$(uname -n).conf exists or not,
    # open it in write mode and make the activation entry
    if (&writeToFile(">/etc/lvm/lvm_$uname.conf", \$volmList)) {
        $error_value = 1;
    }

    # Check if root FileSystem is on a VG.
    # Get the device on which the root filesystem resides
    $op = `df -P / | sed '1d' | cut -f1 -d\" \"`;
    $cmd = "basename $op";
    ($ret , my $devname) = runandCaptureCommand($cmd);
    chomp $devname;
    # Removing the lvol information
    if ($devname =~ /(.*)-(.*)/) {
        $devname = $1;
    }
    # Check if this device is a VG or not
    $cmd = "vgs $devname >/dev/null 2>&1";
    ($ret) = runandCaptureCommand($cmd);
    if($ret == 0) {
        $cmd = "perl -ne '/^\\s*volume_list =/ && print' $fname";
        ($ret, $op) = runandCaptureCommand($cmd);
        my $appendEdit = 0;
        if ($op){ 
          #if already volume_list entry is there
          $op =~ /\[(.*)\]/;
          my $existing_vg = $1;
          if ($existing_vg !~ /$devname/){ 
             $entry = "volume_list = [$existing_vg, \"$devname\" ]";
             $appendEdit = 1;
             my $retVal = lvmFileOperation($entry,$appendEdit);
             if ($retVal eq 1) {
                   return; }
             }               
        } else {
              #this is 1st volume_list entry
              $entry = "volume_list = [ \"$devname\" ]";
              my $retVal = lvmFileOperation($entry,$appendEdit);
              if($retVal eq 1) {
                return; }
        }
    }
    # Give general comment to user for adding other VGs
    &printLog("NOTE: Add local VGs, that are required to be ".
              "activated at boot-time,\n\tto the volume_list in".
              " /etc/lvm/lvm.conf.\n");
    if (!$error_value) {
        &printLog("Updated the /etc/lvm/lvm.conf and /etc/lvm/lvm_$uname.".
                  "conf files.\n");
        $error_summary[$Stage] = $PASS;
    }
}
#------------------------------------------------------------------------------
#
# Subroutine    : lvmFileOperation()
# Calls         : printLog
# Called by     : enableVGActivationProtection()
# Globals       : -
# Input Params  : The lvm file need to be modified
# Return Value  : 0 or 1 
#
# This function opens a temp file with modified entry 
# and copy the content to lvm.conf. Also delete the temp file.
#
#------------------------------------------------------------------------------
sub lvmFileOperation {
    my $tmp_file = "/etc/lvm/lvm_temp.conf";
    my $fname = "/etc/lvm/lvm.conf";
    my $cmd , my $ret = 0;
    my ($myentry , $appendEdit) = @_;

    ###Append/Edit the new lvm list
    if($appendEdit !~ 1) {
       $cmd = "perl -ne '/^\\s*#\\s*volume_list =/ && print' $fname";
       ($ret , my $op) = runandCaptureCommand($cmd);
       if($op) {
           $cmd = "sed '/#[[:space:]]*volume_list[[:space:]]*=".
              "[[:space:]]*/a \Q      $myentry\E\' $fname > $tmp_file";
       } else {
           $cmd = "sed '/^\\s*activation\\s*{".
             "/a \Q      $myentry\E\' $fname > $tmp_file"; }
    } else {
        $cmd ="sed '/^[^#]*volume_list[[:space:]]*=".
          "[[:space:]]*/c \Q      $myentry\E\' $fname > $tmp_file";
    }
    ($ret) = runandCaptureCommand($cmd);
    if ( $ret eq 0 ) {
        $cmd = "mv $tmp_file $fname";
        ($ret) = runandCaptureCommand($cmd);
        &printLog("Adding boot volume group $myentry ".
                "to `volume_list` entry in /etc/lvm/lvm.conf \n");
        return 0;
    } else {
        &printLog("ERROR: Unable to update $fname. \n", $ERROR);
        return 1; }
}

