#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2001,2019 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# sccsid = "@(#)79   1.35   src/rsct/rm/ConfigRM/cli/bin/addrpnode.perl, configrmcli, rsct_rady, rady2035a 11/12/15 16:40:31"
######################################################################
#                                                                    #
# Module: addrpnode                                                  #
#                                                                    #
# Purpose:                                                           #
#   addrpnode - adds a node to an RSCT peer domain.                  #
#                                                                    #
# Syntax:                                                            #
#   addrpnode  [-h]  [-c] [-M]  [-TV] Node_name[@Host_name]          #
#                                [Node_name[@Host_name] ...]         #
#                                                                    #
#   addrpnode  [-h]  [-c] [-M]  -f File | -F File  [-TV]             #
#                                                                    #
# Flags:                                                             #
#   -h      Help. Writes the command's usage statement to standard   #
#           output.                                                  #
#   -c      Continue on error.  When adding multiple nodes to the    #
#           peer domain, do not stop when an error occurs but        #
#           continue processing the addrpnode command as long as at  #
#           least one node can be added to the peer domain.          #
#   -f File The file containing the nodes names to be added to the   #
#           peer domain. The node names are defined as Node_name     #
#           operands or in a file, not both.  When in a file, each   #
#           line of the file is scanned for one node name.  Comments #
#           may be placed on each line, but after the comment        #
#           character "#". Lines that start (col 1) with "#" or are  #
#           entirely blank are ignored.  Use -f "-" to specify STDIN #
#           as the input file.                                       #
#   -F File Same as -f.                                              #
#   -T      Trace. Writes the command's trace messages to standard   #
#           error. For your software-service organization's use only.#
#   -V      Verbose. Writes the command's verbose messages to        #
#           standard output.                                         #
#   -M      SecurityMode: Security mode of the node that needs to be #
#           added.                                                   #
#           If "-M" is given, node will be added to domain if node   #
#           has NIST compliant keys and domain is in NIST mode.      #
#                   if Domain is not in NIST node addition will fail.#
#           If this option is not specified and domain is in         #
#           NIST mode, node will be added to domain if               #
#              Node is already in NIST mode                          #
#              otherwise node addition will fail                     #
#                                                                    # 
#                                                                    #
# Operands:                                                          #
#   Node_name[@Host_name]                                            #
#               The node to be added to the peer domain definition.  #
#               The node name (the Host_name, if there is any        #
#               corresponding input) is the long or short version    #
#               of the DNS hostname. It must be resolvable to an IP  #
#               address. The node name can be specified as operands  #
#               or in a file using the -f option, but not both.      #
#               The "@Host_name" input is applicable for non-CAA     #
#               domain not applicable for CAA.                       #
#               Node_name will be populated in nodename attribute.   #
#               Host_name will be populated in hostname attribute,   #
#               if any, otherwise populated initially with           #
#               Node_name.                                           #
#                                                                    #
# Description:                                                       #
#   The addrpnode command adds the specified nodes to the online     #
#   peer domain of where the command is run.  The command must be    #
#   run on a node that is online to the peer domain in which the new #
#   nodes are to be added.  The added nodes are not brought online   #
#   in the peer domain.  Use the startrpnode command to bring the    #
#   added nodes online.                                              #
#                                                                    #
#   A node can be defined in multiple peer domains but only be       #
#   online in one.                                                   #
#                                                                    #
# Exit Values:                                                       #
#   0  CRM_CLI_SUCCESS       Command completed successfully.         #
#   1  CRM_CLI_RMC_ERROR     Command terminated due to an underlying #
#                            RMC error.                              #
#   2  CRM_CLI_ERROR         Command terminated due to an underlying #
#                            error in the command script.            #
#   3  CRM_CLI_BAD_FLAG      Command terminated due to user          #
#                            specifying an invalid flag.             #
#   4  CRM_CLI_BAD_OPERAND   Command terminated due to user          #
#                            specifying a bad operand.               #
#   5  CRM_CLI_USER_ERROR    Command terminated due to a user error, #
#                            for example specifying a name that      #
#                            already exists.                         #
#                                                                    #
# Examples:                                                          #
#   1. To add the nodes nodeB and nodeC to the peer domain ApplDomain#
#      where nodeA is already defined and online to ApplDomain, run  #
#      the following command on nodeA:                               #
#      addrpnode nodeB nodeC                                         #
#                                                                    #
#   2. To add the nodes nodeB and nodeC with hostnames HostB and HostC#
#      respectively in non-CAA domain, run the following command:                      #
#      addrpnode nodeB@HostB nodeC@HostC                             #
#                                                                    #
# Man Page:                                                          #
#   For the most current detailed description of this command see    #
#   the addrpnode man page in /opt/rsct/man.                    #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /opt/rsct/msgmaps/configrmcli.addrpnode.map -               # 
#       message mapping                                              #
#                                                                    #
# Outputs:                                                           #
#   stdout - none.                                                   #
#   stderr - any error message.                                      #
#                                                                    #
# External Ref:                                                      #
#   Commands: ctdspmsg                                               #
#   Modules:  CRM_cli_utils.pm, CRM_cli_rc.pm,                       #
#             CRM_cli_include.pm, CT_cli_utils.pm                    #
#   Perl library routines: Getopt::Std                               #
#                                                                    #
# Tab Settings:                                                      #
#   4 and tabs should be expanded to spaces before saving this file. #
#   in vi:  (:set ts=4  and   :%!expand -4)                          #
#                                                                    #
# Change Activity:                                                   #
#   010806 JAC 75435: Initial design & write.                        #
#   010827 JAC 75436: Don't add a node that's already in the cluster.#
#   011218 JAC 78807: Don't resolve node names or check if in cluster#
#   020131 JAC 79963: switch to use mkrsrc-api instead of mkrsrc.    #
#   020207 JAC 80121: Make printing of c-api results a trace msg.    #
#   020415 JAC 82055: Add -c for continue-on-error and get rid of    #
#                     looping to call mkrsrc-api (RM accepts >1).    #
#   020417 JAC 81859: Add -f file flag to read node names/nums.      #
#   020422 JAC 82248: Rename addclnode to addrpnode.                 #
#   020424 JAC 82385: Add quotes around string passed to mkrsrc-api. #
#   020428 JAC 82316: Call process_exit_code to check $rc.           #
#   020502 JAC 82564: Set peer domain scope before calling mkrsrc-api#
#   021111 JAC 88901: Fix for node_names empty for last verbose msg. #
#   030324 JAC 93122: Don't specify default command arguments.       #
#   030723 JAC 97439: Continue processing if unresolved nodes exist  #
#                     or nodes already in peer domain.               #
#   040319 JAC 105987: Add -F flag to do the same as -f.             #
#   040406 JAC 106712: Update usage for file options.                #
#   050406 JAC 119510: Add rc when calling process_api_error.        #
#   050506 JAC 119921: Fix verbose message when node list not known. #
#   071105 JAC 146610: Add tiebreaker support.                       #
######################################################################

#--------------------------------------------------------------------#
# General Program Flow/Logic:                                        #
#                                                                    #
# 1. Parse command line flags and operands.                          #
# 2. Print usage if -h specified.                                    #
# 3. find the name of the node you're on and make sure it is online  #
#    to the cluster. It's an error if it isn't.                      #
# 4. Resolve the target node names.                                  #
# 5. Make sure the target node isn't already in the cluster.         #
# 6. Call RMC command mkrsrc. Also pass along -VT if necessary.      #
# 7. Return back any errors.                                         #
#                                                                    #
#--------------------------------------------------------------------#

#--------------------------------------------------------------------#
# Included Libraries and Extensions                                  #
#--------------------------------------------------------------------#
use lib "/opt/rsct/pm";
use locale;
use Getopt::Std;

use CT_cli_utils qw(printIMsg
                    printEMsg);

use CRM_cli_rc qw(CRM_CLI_SUCCESS CRM_CLI_RMC_ERROR
                  CRM_CLI_ERROR CRM_CLI_BAD_FLAG
                  CRM_CLI_BAD_OPERAND CRM_CLI_USER_ERROR);
use CRM_cli_utils qw(error_exit
                     printCIMsg
                     printCEMsg
                     get_opstate_by_name
                     check_for_nodes
                     process_api_error
                     process_exit_code
                     get_nodes_nums_from_file
                     form_quorum_attribute
                     resolve_node_names);
use CRM_cli_include qw($TRUE $FALSE
                       $RMC_OPSTATE_ONLINE
                       $RSNODE $RSNETI $RMC_CLI_USER_ERROR
                       $PEER_DOMAIN_SCOPE
                       $CTBINDIR $CTDIR);

#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
$Trace = $FALSE;                        # default - trace off
$Verbose = $FALSE;                      # default - verbose turned off
$Opt_Continue = $FALSE;                 # default - no continue on error
$Opt_File_Input = $FALSE;               # default - no file
$Opt_SecurityMode = $FALSE;             # default - no SecurityMode specified

$PROGNAME = "addrpnode";                # Program Name for messages
$LSMSG = "$CTBINDIR/ctdspmsg";          # list / display message rtn
$MSGCAT = "configrmcli.cat";            # msg catalogue for this cmd
$ENV{'MSGMAPPATH'} = "$CTDIR/msgmaps";  # msg maps used by $LSMSG

#--------------------------------------------------------------------#
# Variables                                                          #
#--------------------------------------------------------------------#
my @node_names = ();                    # nodes in cluster
my @node_names_v = ();                  # nodes in cluster for -V
my %node_opstates = ();                 # nodes in cluster 
my @node_info = ();                     # nodes in cluster 
my $resolved_node_names = "";           # resolved nodelist
my $unresolved_node_names = "";         # unresolved nodelist
my @node_names_to_add = ();             # node names to add to cluster
my $in_list = "";                       # reference to array
my $not_in_list = "";                   # reference to array
my $one_node = "";                      # one from nodelist
my $first_node = "";                    # first one in the list
my @cmd_out = ();                       # for mkrsrc output
my @nnodes = ();                        # number of nodes
my @node_quorum_flags = ();             # array reference for Quorum specification
my @node_tiebreaker_flags = ();         # array reference for Tiebreaker specification
my @node_gsgl_flags = ();               # array reference for PreferredGSGL specification
my $comma = "";                         # used to build a list
my $node_name_list = "";                # list of node names
my $file_name = "";                     # input file name
my $node_names_file = "";               # array reference
my $node_nums_file = "";                # array reference
my $node_quorum_flags_file = "";        # array reference for Quorum specification
my $node_tiebreaker_flags_file = "";    # array reference for Tiebreaker specification
my $node_gsgl_flags_file = "";          # array reference for PreferredGSGL specification
my @node_numbers = ();                  # nodes in cluster
my $node_num_list = "";                 # for rmc command
my $one_num = "";                       # one from node num list
my $one_flag = "";                      # one from IsQuorumNode/IsPrefferedGSGL list
my $first_num = "";                     # first one in the list
my $first_quorum_flag = "";             # first one in the list
my $first_gsgl_flag = "";               # first one in the list
my $node_opts_list = "";                # for rmc command
my $node_quorum_arg = "";               # reference to quorum arg array
my @nodenames_list = ();                # Node names (List of hostname, if any, otherwise nodename) list
my @nodenames = ();                     # Input nodenames list
my @hostnames = ();                     # Input hostnames list
my $tmp_node_name = "";
my $tmp_host_name = "";
my $first_host_name = "";
my $host_names_list = "";               # Host name list
my @copy_of_node_names = ();            # copy of @node_names

my $passopts = "";                      # TV options to pass to RMC CLI
my $other_opts = "";                    # parameters to pass to RMC CLI
my $delimiter = "#:#";
my $IPv6Support = 0;
my $verify_secmode = 0;                  #  if -M is specified, verify_secmode will be set to TRUE 
#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#
my $rc = 0;
my $config_rc = 0;

# set peer domain scope
$ENV{CT_MANAGEMENT_SCOPE} = $PEER_DOMAIN_SCOPE;

# parse the command line, exit if there are errors 
($rc, $file_name, $verify_secmode, @node_names) = &parse_cmd_line;
($rc == 0) || error_exit($rc);

if ($Verbose) { printIMsg("IMsgaddrpnodeStarted"); }

if ($Trace) { $passopts = $passopts." -T"; }
if ($Verbose) { $passopts = $passopts." -V"; }

# get the nodes opstate info
%node_opstates = get_opstate_by_name($RSNODE);

# make sure there are nodes in the cluster
@nnodes = keys %node_opstates;
if ($#nnodes <0) {
   printCEMsg("EMsgConfigRMcliNoCluster");
   exit (CRM_CLI_USER_ERROR);
}

# get the node names and numbers from a file, if specified
if ($Opt_File_Input) {

   # extract the node names from the file
   ($node_names_file, $node_nums_file, $node_quorum_flags_file,
            $node_gsgl_flags_file, $node_tiebreaker_flags_file) 
              = get_nodes_nums_from_file($file_name);

   # copy to other array
   @node_names = @$node_names_file;
   @node_numbers = @$node_nums_file;
   @node_quorum_flags = @$node_quorum_flags_file;
   @node_gsgl_flags = @$node_gsgl_flags_file;
   @node_tiebreaker_flags = @$node_tiebreaker_flags_file;
}

if ($Verbose) { 
   printIMsg("IMsgaddrpnodeNodeList");
   foreach $one_node (@node_names) {
      print(" ", $one_node);
   }
   print("\n");
}

# consolidate quorum and tiebreaker arrays
$node_quorum_arg = form_quorum_attribute(\@node_quorum_flags, \@node_tiebreaker_flags);

# Hostname change support.
# User has the flexibility to provide node name and corresponding hostname, like nodename[@hostname]
# Hostname would be considered for resolving nodename otherwise node name will be used.
$comma = "";
foreach $one_node (@node_names) {
    $tmp_node_name = "";
    $tmp_host_name = "";

    ($tmp_node_name, $tmp_host_name) = split /@/, $one_node;

    push (@nodenames, $tmp_node_name);

    if ($tmp_host_name eq "") {
        # Consider nodename as there is no Hostname
        push (@nodenames_list, $tmp_node_name);
    }
    else {
        # Consider hostname as there is input for corresponding nodename
        push (@nodenames_list, $tmp_host_name);
        push (@hostnames, $tmp_host_name);
    }
    $comma = ",";
}


# resolve node names and hostnames that are to be added to the cluster
($resolved_node_names, $unresolved_node_names) = resolve_node_names(@nodenames_list);

# write an error message for each unresolved node name found
# continue processing but set rc
foreach $one_node (@$unresolved_node_names) {
   printEMsg("EMsgaddrpnodeUnresolvedNode",$one_node);
   $config_rc = CRM_CLI_USER_ERROR;
}

# remove unresolved node names from node name list 
# &reset_node_names(\@node_names, \@node_numbers, $unresolved_node_names);
&reset_node_opts(\@nodenames_list,
            \@node_numbers,
            $node_quorum_arg,
            \@node_gsgl_flags,
            $unresolved_node_names);

# make sure target nodes are not already in the cluster
($in_list,$not_in_list) = check_for_nodes(\@nodenames_list,\%node_opstates);

# for nodes in the list (and already in cluster), print error message
foreach $one_node (@$in_list) {
   printEMsg("EMsgaddrpnodeAlreadyAdded",$one_node);
   $config_rc = CRM_CLI_USER_ERROR;
}

# remove nodes that are already in cluster from node name list 
# &reset_node_names(\@node_names, \@node_numbers, $in_list);
&reset_node_opts(\@nodenames_list,
            \@node_numbers,
            $node_quorum_arg,
            \@node_gsgl_flags,
            $in_list);

# get ready to pass stuff to configrm
# first, make sure there are nodes to process, in case of 
# previous errors.  Exit if there are no nodes to process.
if ($#nodenames_list < 0) {
   if ($Verbose) { printIMsg("IMsgaddrpnodeEnded"); }
   exit($config_rc); 
} 

# determine whether IPv6 addresses are permitted as node names
$IPv6Supported = `$CTBINDIR/lsrsrc-api -c "${RSNETI}::IPv6Support" 2>&1`;
chomp $IPv6Supported;

if ("" eq $IPv6Supported) {
   # something went wrong getting $IPv6Supported - default to being restrictive
   $IPv6Supported = 0;
}

# make a list of the remaining node names to pass to rmc
foreach $one_node (@nodenames, @hostnames) {
   if ((! $IPv6Supported) && ($one_node =~ /:/)) {
      printEMsg("EMsgaddrpnodeIPv6AddressNotPermitted",$one_node);
      exit CRM_CLI_USER_ERROR; 
   }
}

# get the first node name. it's used in Name attribute
$first_node = shift @nodenames;

# HostName of first NodeName if available
# Note: HostName field should not be passed unless it is provided
($tmp_node_name, $tmp_host_name) = split /@/, $node_names[0];
if ($tmp_host_name ne "") {
    $first_host_name = "${delimiter}HostName${delimiter}${tmp_host_name}";
}

@copy_of_node_names = @node_names;
shift @copy_of_node_names;

$comma = "";
foreach $one_node (@copy_of_node_names) {
    ($tmp_node_name, $tmp_host_name) = split /@/, $one_node;

    $node_name_list = $node_name_list.$comma.$tmp_node_name;

    if ($tmp_host_name ne "") {
        $host_names_list = $host_names_list.$comma.$tmp_host_name;
    } else {
        $host_names_list = $host_names_list.$comma."";
    }

    $comma = ",";
}

# setup the nodelist
$other_opts = $other_opts."${delimiter}${delimiter}NodeNames${delimiter}"."{$node_name_list}";

# get the first num. it's used in NodeList attribute
if ($#node_numbers >=0) { $first_num = shift @node_numbers;}

# if there are node numbers, save the first for mkrsrc call
if ($first_num ne "") {$first_num = "${delimiter}NodeList${delimiter}{$first_num}";}

# make a list of the remaining node numbers to pass to rmc
$comma = "";
foreach $one_num (@node_numbers) {
   $node_num_list = $node_num_list.$comma.$one_num;
   $comma = ",";
}

# setup node number list
$other_opts = $other_opts."${delimiter}NodeNumbers${delimiter}"."{$node_num_list}";

# set the first Quorum value. It's used in IsQuorumNode attribute
if ( $#$node_quorum_arg >= 0 ) { $first_quorum_flag = shift( @$node_quorum_arg ); }
# if there are Quorum specifications, save the first for mkrsrc call
if ( $first_quorum_flag ne "" ) {
   $first_quorum_flag = "${delimiter}IsQuorumNode${delimiter}${first_quorum_flag}";
}
# setup node Quorum list
if ( $#$node_quorum_arg >= 0 ) {
   $comma = "";
   $node_opts_list = "";
   foreach $one_flag (@$node_quorum_arg) {
      $node_opts_list = $node_opts_list.$comma.$one_flag;
      $comma = ",";
   }

   $other_opts = $other_opts."${delimiter}IsQuorumNodeArray${delimiter}{$node_opts_list}";
}
# set the first Preferred Group leader value. It's used in IsPreferredGSGL attribute
if ( $#node_gsgl_flags >= 0 ) { $first_gsgl_flag = shift( @node_gsgl_flags ); }
# if there are Preferred Group leader specifications, save the first for mkrsrc call
if ( $first_gsgl_flag ne "" ) {
   $first_gsgl_flag = "${delimiter}IsPreferredGSGL${delimiter}${first_gsgl_flag}";
}
# setup node Preferred Group leader list
if ( $#node_gsgl_flags >= 0 ) {
   $comma = "";
   $node_opts_list = "";
   foreach $one_flag (@node_gsgl_flags) {
      $node_opts_list = $node_opts_list.$comma.$one_flag;
      $comma = ",";
   }

   $other_opts = $other_opts."${delimiter}IsPreferredGSGLArray${delimiter}{$node_opts_list}";
}
# continue on error specified?
if ($Opt_Continue) {
   $other_opts = $other_opts."${delimiter}ContinueIfError${delimiter}1";
}
#93122 - comment out specifying the default command argument
#else {
#   $other_opts = $other_opts."${delimiter}ContinueIfError${delimiter}0";
#}

# setup hostnames list if any
if ($host_names_list ne "") {
    $other_opts = $other_opts."${delimiter}HostNames${delimiter}"."{$host_names_list}";
}

$persistent_attrs = "${delimiter}Name${delimiter}${first_node}${first_num}${first_quorum_flag}${first_gsgl_flag}${first_host_name}";

#SecurityMode option is specified

if( $Opt_SecurityMode) { 
    $other_opts = $other_opts."${delimiter}VerifySecurityMode${delimiter}$verify_secmode";
}


# call mkrsrc to add the nodes to the cluster

if ($Trace) { print STDERR "$PROGNAME: calling mkrsrc-api\n";}

#`$CTBINDIR/mkrsrc $passopts $RSNODE Name="$one_node"`;
#@cmd_out=`$CTBINDIR/mkrsrc-api ${RSNODE}${delimiter}Name${delimiter}${one_node} 2>&1`;   
#@cmd_out=`$CTBINDIR/mkrsrc-api "${RSNODE}${delimiter}Name${delimiter}${first_node}${first_num}${other_opts}" 2>&1`;   
@cmd_out=`$CTBINDIR/mkrsrc-api -D"$delimiter" -I"$delimiter" "${RSNODE}${persistent_attrs}${other_opts}" 2>&1`;   

# capture the return code from mkrsrc
$rc = $?;
$rc = process_exit_code($rc);

if ($Trace) { print STDERR "mkrsrc-api results:\n";
              print STDERR "@cmd_out";}

if ($Trace) { print STDERR "$PROGNAME: mkrsrc-api returned $rc\n";}

# show any errors if there was a bad rc
if ($rc != 0) {
   process_api_error($delimiter,$rc,@cmd_out);
}

# save rc as ConfigRM CLI user error if it's an RMC CLI user error
if ($rc == $RMC_CLI_USER_ERROR) {
   $config_rc = CRM_CLI_USER_ERROR;
}

# if mkrsrc failed, print RMC error message and save rc
if (($rc != 0) && ($rc != $RMC_CLI_USER_ERROR)) {
#  printCEMsg("EMsgConfigRMcliUnExpectRMCrc",$rc);
   $config_rc = CRM_CLI_RMC_ERROR;
}

if ($Verbose) { printIMsg("IMsgaddrpnodeEnded"); }

# exit with appropriate return code
if ($config_rc == 0) { exit($rc); }
else { exit($config_rc); } 

#--------------------------------------------------------------------#
# End Main Code                                                      #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# parse_cmd_line - Parse the command line for options and operands.  #
#   Set appropriate global variables as outlined below, make sure we #
#   have a valid combination of arguments / options.                 #
#                                                                    #
# Return:                                                            #
#   $rc   0                  Command line parsed fine, no problem.   #
#         CRM_CLI_BAD_FLAG   Command line contained a bad flag.      #
#   @node_names              Nodes to add to the cluster             #
#                                                                    #
# Global Variables Modified:                                         #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#   $Trace             output   True (-T) turn Trace mode on.        #
#   $Opt_File_Input    output   True (-f|-F) file name specified     #
#   $Opt_Continue      output   True (-c) Continue on error          #
#   $Opt_SecurityMode  output   True (-M) Security Mode specified    #
#--------------------------------------------------------------------#
sub parse_cmd_line 
{
my(@original_argv) = @ARGV;
my @node_names = ();                    # node names
my %opts = ();
my $file_name = "";
my $verify_secmode = 0;

# Process the command line...
if (!&getopts('hcf:F:VTM', \%opts)) {    # Gather options; 
                                        # if errors
    &print_usage;                       # display proper usage
    return CRM_CLI_BAD_FLAG;            # return bad rc - bad flag 
}

# process h flag
if (defined $opts{h}) {                 # -h, help request
    &print_usage;                       # print usage statement
    exit(0);                            # all done with good return!
}

if (defined $opts{T}) {                 # -T turn trace on
    $Trace = $TRUE;
}

if (defined $opts{V}) {                 # -V turn verbose mode on
    $Verbose = $TRUE;
}

if (defined $opts{c}) {                 # -c for continue on error
    $Opt_Continue = $TRUE;              # -c flag specified
}

# Get the arguments...
# Operands:  one or more node names
if ($#ARGV >= 0) {                      # cluster name and maybe more
      @node_names = @ARGV;              # get node names
}

# make sure -f or -F for file used if there are no node names
if (($#node_names < 0) && !(defined $opts{f}||defined $opts{F})) {
    printCEMsg("EMsgConfigRMcliInvalidNumberOfOperands");
    &print_usage;
    return CRM_CLI_BAD_OPERAND;
}

if (defined $opts{f}) {                 # -f for file

    # make sure both -f and -F not used together
    if (defined $opts{F}) {
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-f","-F");
       &print_usage;
       return CRM_CLI_BAD_FLAG;
   }
    $Opt_File_Input = $TRUE;
    $file_name = $opts{f};
    # make sure file and cmd line not both used for node names
    if ($#node_names >= 0) {
       printCEMsg("EMsgConfigRMcliInvalidNumberOfOperands");
       &print_usage;
       return CRM_CLI_BAD_OPERAND;
   }
}

if (defined $opts{F}) {                 # -F for file
    $Opt_File_Input = $TRUE;
    $file_name = $opts{F};
    # make sure file and cmd line not both used for node names
    if ($#node_names >= 0) {
       printCEMsg("EMsgConfigRMcliInvalidNumberOfOperands");
       &print_usage;
       return CRM_CLI_BAD_OPERAND;
   }
}

if( defined $opts{M}) {
     $verify_secmode =  $TRUE;
     $Opt_SecurityMode = $TRUE;
}

return(0, $file_name, $verify_secmode, @node_names );                # success

}   # end parse_cmd_line


#--------------------------------------------------------------------#
# print_usage : print the usage statement (syntax) to stdout.        #
#--------------------------------------------------------------------#
sub print_usage
{
&printIMsg("IMsgaddrpnodeUsageF");
}   # end print_usage

#--------------------------------------------------------------------#
# reset_node_names : Removes from the node name list the node names  #
#   provided in the list of nodes to remove.  If the node number     #
#   list exists, update it as well.                                  #
#                                                                    #
# Parameters:                                                        #
#   $node_names    in/out   Reference to an array of node names.     #
#   $node_nums     in/out   Reference to an array of node numbers.   #
#   $remove_nodes  input    Reference to an array of node names that #
#                           need to be removed.                      #
#                                                                    #
# Global Variables:                                                  #
#--------------------------------------------------------------------#
sub reset_node_names
{
my $node_names = "";                    # ref to array of node names
my $node_nums = "";                     # ref to array of node nums
my $remove_nodes = "";                  # ref to nodes to remove
my @node_names_new = ();                # temp new name list
my @node_nums_new = ();                 # temp new num list
my $num_update_req = $FALSE;            # update num array
my $i = 0;                              # loop counter
my $j = 0;                              # loop counter
my $remove_cnt = 0;                     # loop counter
my $remove = $FALSE;                    # search switch

# get inputs
$node_names = shift @_;
$node_nums = shift @_;
$remove_nodes = shift @_;

# simply exit if there's no nodes to remove or no node names 
# to begin with
if ( ($#$node_names < 0) || ($#$remove_nodes < 0) ) {
   return;
}

# determine (once) if node number array is present
if ( $#$node_nums >=0 ) {
   $num_update_req = $TRUE;
}

# loop through node name list seeing if it needs to be 
# removed (or really not included in the new list)
for ($i=0; (($i<=$#$node_names) && ($remove_cnt<=$#$remove_nodes)); $i++) {

   # set remove flag to false to say keep this node name
   $remove = $FALSE;   

   # see if this node is a node in the remove list
   for ($j=0; (($j<=$#$remove_nodes) && (!$remove)); $j++) {

      # check if this is a node to dump
      if ($$remove_nodes[$j] eq $$node_names[$i]) {
         
         # set boolean switch
         $remove = $TRUE;
         $remove_cnt++;
      }
   }

   # if it's not to be removed, add to new list
   if (!$remove) {
      push @node_names_new, $$node_names[$i];

      # update the node number list if necessary
      if ($num_update_req) {
         push @node_nums_new, $$node_nums[$i];
      }
   }

}

# transfer rest of list
if ($i<=$#$node_names) {
   for ($j=$i; $j<=$#$node_names; $j++) {
      push @node_names_new, $$node_names[$j];

      # update the node number list if necessary
      if ($num_update_req) {
         push @node_nums_new, $$node_nums[$j];
      }
   }
}

# copy new lists to old
@$node_names = @node_names_new;
@$node_nums = @node_nums_new;


return;
}   #  end of reset_node_names

#--------------------------------------------------------------------#
# reset_node_opts : Removes from the node name list the node names   #
#   provided in the list of nodes to remove.  If the other node      #
#   option lists exists, update it as well.                          #
#                                                                    #
# Parameters:                                                        #
#   $node_names    in/out   Reference to an array of node names.     #
#   $node_nums     in/out   Reference to an array of node numbers.   #
#   $node_quorum_flags in/out Reference to an array of IsQuroum.     #
#   $node_gsgl_flags in/out   Reference to an array of IsPreferedGSGL.#
#   ...            in/out   ...                                      #
#   $remove_nodes  input    Reference to an array of node names that #
#                           need to be removed.                      #
#                                                                    #
# Global Variables:                                                  #
#--------------------------------------------------------------------#

sub reset_node_opts
{
my $node_names = "";                    # ref to array of node names
my @node_opts = ();                     # ref to array of node number, IsPreferredGSGL, IsQuroumNode
my $remove_nodes = "";                  # ref to nodes to remove
my @node_names_new = ();                # temp new name list
my @node_opts_new = ();                 # temp new num list
my $i = 0;                              # loop counter
my $j = 0;                              # loop counter
my $n = 0;                              # loop counter
my $remove_cnt = 0;                     # loop counter
my $remove = $FALSE;                    # search switch
my $opts_req = 0;                       # other options update
my @opts_update_reqs = ();              # other options update list
my $temp_src_ref = "";                  # temp ref to a array
my $temp_dst_ref = "";                  # temp ref to a array


# get inputs
$node_names = shift @_;
$remove_nodes = pop @_;
@node_opts = @_;

# simply exit if there's no nodes to remove or no node names 
# to begin with
if ( ($#$node_names < 0) || ($#$remove_nodes < 0) ) {
   return;
}

#determine if there are none-empty option arrays
#option arrays include node number list,Quorum flag list, PreferredGSGL flag list 
if ( $#node_opts >= 0 ) {
   $opts_req = 1;       

   # Initialize new array corresponding to the option array
   # Set if the option array is empty  
   foreach $i (@node_opts) {

      my @temp_array = ();
      push ( @node_opts_new, \@temp_array );

      if ( $#$i >= 0 ) {
         push ( @opts_update_reqs, 1 );
      } else {
         push ( @opts_update_reqs, 0 );
      }
   } # end of "foreach $i"
} # end of "if ( $#node_opts >= 0 )"

# loop through node name list seeing if it needs to be 
# removed (or really not included in the new list)
for ($i=0; (($i<=$#$node_names) && ($remove_cnt<=$#$remove_nodes)); $i++) {

   # set remove flag to false to say keep this node name
   $remove = $FALSE;   

   # see if this node is a node in the remove list
   for ($j=0; (($j<=$#$remove_nodes) && (!$remove)); $j++) {

      # check if this is a node to dump
      if ($$remove_nodes[$j] eq $$node_names[$i]) {
         
         # set boolean switch
         $remove = $TRUE;
         $remove_cnt++;
      } 
   } # end of "for $j"

   # if it's not to be removed, add to new list
   if ( !$remove ) {
      push @node_names_new, $$node_names[$i];

      if ( !$opts_req ) { next; }

      # update other lists if necessary
      for( $n = 0; $n <= $#node_opts; $n++ ) {

         if ( $opts_update_reqs[$n] ) {

            $temp_src_ref = $node_opts[$n];
            $temp_dst_ref = $node_opts_new[$n];

            push @$temp_dst_ref, $$temp_src_ref[$i];
         }

      } # end of "for $n < $#node_opts".
   } # end of "if ( !$remove )"

} # end of " for $i"

# transfer rest of list
if ( $i <= $#$node_names ) {

   for ( $j = $i; $j <= $#$node_names; $j++) {
      push @node_names_new, $$node_names[$j];

      if ( !$opts_req )  { next; }

      # update other lists if necessary
      for( $n = 0; $n <= $#node_opts; $n++ ) {
         if ( $opts_update_reqs[$n] ) {

            my $temp_src_ref = $node_opts[$n];
            my $temp_dst_ref = $node_opts_new[$n];

            push @$temp_dst_ref, $$temp_src_ref[$j];
         } 
      } # end of " for $n < $#node_opts"
   } # end of " for $j<=$#$node_names "
} # end of "if ( $i <= $#$node_names )"

# copy new lists to old
@$node_names = @node_names_new;

if ( $opts_req ) {
 
   for( $n = 0; $n <= $#node_opts; $n++ ) {
      if ( $opts_update_reqs[$n] ) {

         my $temp_src_ref = $node_opts[$n];
         my $temp_dst_ref = $node_opts_new[$n];

         @$temp_src_ref = @$temp_dst_ref;
      }  
   } # end of " for $n < $#node_opts"
} # end of "if ( $opts_req )"


return;
}   #  end of reset_node_opts