#!/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 = "@(#)87   1.32   src/rsct/rm/ConfigRM/cli/bin/stoprpnode.perl, configrmcli, rsct_rady, rady2035a 1/31/18 10:16:57"
######################################################################
#                                                                    #
# Module: stoprpnode                                                 #
#                                                                    #
# Purpose:                                                           #
#   stoprpnode - brings a node offline to an RSCT peer domain.       #
#                                                                    #
# Syntax:                                                            #
#   stoprpnode  [-h] [-f] [-w [-s Seconds]] [-TV] [-m 0|1] Node_name #
#      [Node_name ...]                                               #
#                                                                    #
#   stoprpnode  [-h] [-f] -F File [-w [-s Seconds]] [-TV] [-m 0|1]   #
#                                                                    #
# Flags:                                                             #
#   -h      Help. Writes the command's usage statement to standard   #
#           output.                                                  #
#   -f      Force. Forces the subsystems to accept the stop request  #
#           when it otherwise would not, as when resources are       #
#           allocated, for example.                                  #
#   -F File The file containing the nodes names to be taken offline  #
#           in 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.                         #
#   -w      Wait. When -w is specified, the command will wait for    #
#           the all of the specified nodes to be offline before the  #
#           command completes.  Use the -s flag to specify how long  #
#           the command waits.                                       #
#   -s Seconds.  Used with the -w flag to specify how long in seconds#
#           the command is to wait for all of the specified nodes to #
#           be offline. If the waiting exceeds the number of seconds,#
#           the command returns, but the offline operation continues.#
#           The default is 300 seconds (5 minutes).  Use 0 to specify#
#           that the command should not return until all of the      #
#           specified nodes are offline (no timeout on waiting).     #
#   -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      Enable or disable application of global maintenance mode #
#           configuration, when stopping the specified node(s).      #
#           If [-m 0] is specified, then maintenance mode will not   #
#           be applied. If [-m 1] is specified, then maintenance     #
#           mode will be applied.                                    # 
#                                                                    #
# Operands:                                                          #
#   Node_name   The name of the node to be brought offline to the    #
#               peer domain.  The node name is the peer domain node  #
#               name of the node.  The peer domain node names can be #
#               displayed using the lsrpnode command.                #
#                                                                    #
# Description:                                                       #
#   The stoprpnode command brings an online node offline to a peer   #
#   domain. The peer domain is determined by the online peer domain  #
#   of where the command is run.  The command must be run from a     #
#   node that is online to the desired peer domain.                  #
#                                                                    #
#   The -f force flag must be used to override a subsystem's         #
#   rejection of the request to take a node offline.  A subsystem    #
#   may reject the request if a node resource is busy, for example.  #
#   Specifying the -f force flag in this situation indicates to the  #
#   sub systems that the node must be brought offline.               #
#                                                                    #
#   If this command is used to bring more than one node offline by   #
#   specifying more than one Node_name operand, and the node that    #
#   this command is running on is in the list, it will be brought    #
#   offline last.                                                    #
#                                                                    #
# 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 bring  the node nodeB,  which is online to ApplDomain,     #
#      offline, and nodeA is also online to ApplDomain, run the      #
#      following command on nodeA:                                   #
#      stoprpnode nodeB                                              #
#                                                                    #
#   2. To bring the node nodeB,  which is online to ApplDomain,      #
#      offline and force the offline request, and nodeA is also      #
#      online to ApplDomain, run the following command on nodeA:     #
#      stoprpnode -f nodeB                                           #
#                                                                    #
# Man Page:                                                          #
#   For the most current detailed description of this command see    #
#   the stoprpnode man page in /opt/rsct/man.                   #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /opt/rsct/msgmaps/configrmcli.stoprpnode.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: Change some initial checking.                  #
#   011219 JAC 78807: Don't resolve node names or check if in cluster#
#   020202 JAC 79963: weed out stoprsrc error messages.              #
#   020207 JAC 80121: Make printing of c-api results a trace msg.    #
#   020325 JAC 80304: Put back some checking to stop source last.    #
#   020422 JAC 82248: Rename stopclnode to stoprpnode.               #
#   020428 JAC 82316: Call process_exit_code to check $rc.           #
#   020503 JAC 82564: Set peer domain scope before calling stoprsrc. #
#   020530 JAC 82589: Use stoprsrc-api instead of stoprsrc.          #
#   020711 JAC 84621: Use NodeNameList instead of Name in stoprsrc.  #
#   030324 JAC 93122: Don't specify default command arguments.       #
#   040319 JAC 105987: Add -F flag for file input for node names.    #
#   040406 JAC 106712: Update usage for file options.                #
#   050218 JAC 111917: Use runact-api instead of stoprsrc.           #
#   050406 JAC 119510: Add rc when calling process_api_error.        #
#   050504 JAC 119931: Return $config_rc in stop_ functions.         #
#   050506 JAC 119921: Fix verbose message when node list not known. #
#   051218 JAC 131665: Add -w/-s to make command synchronous.        #
#   100604 NG  165141: Add check for CAA domain type                 #
######################################################################

#--------------------------------------------------------------------#
# General Program Flow/Logic:                                        #
#                                                                    #
# 1. Parse command line flags and operands.                          #
# 2. Print usage if -h specified                                     #
# 3. Set the force option if specified.                              #
# 4. Resolve the name of the node the command will run on and make   #
#    sure that it's online in the cluster.                           #
# 5. If no target is specified, make the node you're on the target.  #
# 6. Resolve the target node names and make sure they are in the     #
#    cluster and online                                              #
# 7. If the node we're on is a target, make sure it's processed last.#
# 8. Call RMC command startrsrc. Also pass along -VT if necessary.   #
# 9. 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
                     check_node_state
                     check_for_nodes
                     get_source_node
                     process_api_error
                     process_exit_code
                     get_nodes_nums_from_file
                     get_opstate_by_name
                     get_opstate_by_name_rc
                     get_domain_type);
use CRM_cli_include qw($TRUE $FALSE
                       $RMC_CLI_USER_ERROR
                       $RMC_OPSTATE_ONLINE $RSNODE
                       $RMC_OPSTATE_OFFLINE
                       $RMC_CLI_API_SCOPE_ERROR
                       $PEER_DOMAIN_SCOPE
                       $CTBINDIR $CTDIR $DELIMITER);

#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
$Trace = $FALSE;                        # default - trace off
$Verbose = $FALSE;                      # default - verbose turned off

$Opt_Force = $FALSE;                    # default - no Force option
$Opt_File_Input = $FALSE;               # default - no file
$ACTION_NOT_ALLOWED_RC = 32;            # retruned by configrm
$Opt_Synchronous = $FALSE;              # default - no waiting

$PROGNAME = "stoprpnode";               # 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 to be stopped
my %node_opstates = ();                 # nodes in cluster
my %node_opstates_new = ();             # nodes in cluster refreshed
my $resolved_node_names = "";           # resolved nodelist
my $unresolved_node_names = "";         # unresolved nodelist
my $source_node_name = "";              # cluster node name of source node
my $add_source_node = $FALSE;           # is source node in stop list
my @online_node_names = ();             # online nodes to stop
my $one_node = "";                      # one from nodelist
my $source_online = $FALSE;             # boolean
my $node_online = $FALSE;               # boolean
my $node_offline = $FALSE;              # boolean
my $in_list = "";                       # reference to array
my $not_in_list = "";                   # reference to array
my @nnodes = ();                        # number of nodes
my $file_name = "";                     # file name for node names
my $file_error = "";                    # error with file
my $node_names_file = "";               # reference to node names
my $node_nums_file = "";                # reference to node numbers
my @node_numbers = ();                  # nodes in cluster
my $Use_stoprsrc = $FALSE;              # is env var set to use old way
my $force_element = "";                 # force for runact
my $wait_timeout = 300;                 # wait timeout value
my $wait_poll_int = 30;                 # wait polling interval
my $TIMED_OUT = $FALSE;                 # check timeout
my $NODES_OFFLINE = $FALSE;             # check nodes state
my $maintenance_mode = "";              # maintenance mode for runact
my $Use_DefaultMMode = $TRUE;           # assume maintenance mode flag unspecified

my $passopts = "";                      # TV options to pass to RMC CLI
my $other_opts = "";                    # parameters to pass to RMC CLI
my $delimiter = "#:#";
$DELIMITER = $delimiter; # override so utils don't choke on IPv6 "::"
#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#
my $rc = 0;
my $config_rc = 0;
my $tmp_rc = 0;
my $state_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, $wait_timeout, @node_names) = &parse_cmd_line;
($rc == 0) || error_exit($rc);

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

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

# if CT_CONFIGRMCLI_METHOD is set, use stoprsrc-api (old way)
if (defined $ENV{CT_CONFIGRMCLI_METHOD}) { $Use_stoprsrc = $TRUE; }

# set the wait polling interval in case it's used
if (defined $ENV{CT_CONFIGRMCLI_POLL_INTERVAL}) {
   if ( ($ENV{CT_CONFIGRMCLI_POLL_INTERVAL} =~ /^[0-9]+$/) &&
        ($ENV{CT_CONFIGRMCLI_POLL_INTERVAL} >=1) ) {
      $wait_poll_int = $ENV{CT_CONFIGRMCLI_POLL_INTERVAL};
   }
}

# set the force option, if specified
if ($Opt_Force) {
   $other_opts = "${delimiter}Force${delimiter}1";
   $force_element = "${delimiter}ForcedFlag${delimiter}1";
}
#93122 - comment out specifying the default command argument
#else { $other_opts = "${delimiter}Force${delimiter}0"; }

# set default value 
if ($Use_DefaultMMode) {
    $maintenance_mode = "${delimiter}MaintenanceModeFlag${delimiter}2";
}

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

# find the source name
$source_node_name = get_source_node;

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

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

   # copy to other array
   @node_names = @$node_names_file;
   @node_numbers = @$node_nums_file;
}

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

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

# for nodes not in the list (not in the cluster), print error message
foreach $one_node (@$not_in_list) {
   printEMsg("EMsgstoprpnodeNotInCluster",$one_node);
   $config_rc = CRM_CLI_USER_ERROR;
}

# for nodes in the list (in the cluster),
# make sure they're online and then add to list for stopping
foreach $one_node (@$in_list) {
   $node_online = check_node_state($one_node,$RMC_OPSTATE_ONLINE, %node_opstates);
   if (!$node_online) {
      printEMsg("EMsgstoprpnodeAlreadyStopped",$one_node);
      $config_rc = CRM_CLI_USER_ERROR;
   }
   # it's online so add to list to stop
   else {
      
      # add it to the list unless it's the source node.
      # add that one last.
      if ($one_node eq $source_node_name) {
         $add_source_node = $TRUE;
      }
      else {
         push (@online_node_names, $one_node);
      }
   }
}

# add the source node last, if needed.
if ($add_source_node) {
   push (@online_node_names, $source_node_name);
}  

# 201252 -- support stoprpnode in CAA from RSCT 3.2.1.0 onwards 
#if(get_domain_type()) {
#    #printCEMsg("EMsgConfigRMCmdNotSupported");
#    exit (CRM_CLI_SUCCESS);
#}

# decide whether old way or new way
# if CT_CONFIGRMCLI_METHOD is set, do old way
if ($Use_stoprsrc) {

   $tmp_rc = stop_by_stoprsrc($other_opts, \@online_node_names);
}

# else use new way
else {

   $tmp_rc = stop_by_action($force_element, $maintenance_mode, $other_opts, \@online_node_names);
}

if ($tmp_rc != 0) { $config_rc = $tmp_rc};

# if wait until started selected
if ($Opt_Synchronous) {
   # use $i to check for timeout exceeded
   $i = 0;
   $TIMED_OUT = $FALSE;
   $NODES_OFFLINE = $FALSE;

   # check opstate until all nodes offline or timeout exceeded
   while ( (!$NODES_OFFLINE) && (!$TIMED_OUT) )  {
      sleep($wait_poll_int);
      $i = $i + $wait_poll_int;

      # get new opstates
      ($state_rc, %node_opstates_new) = get_opstate_by_name_rc($RSNODE);
      if ($state_rc == 0) {
         %node_opstates = %node_opstates_new;
      }

      # see if everybody's online yet
      $NODES_OFFLINE = $TRUE;

      # $state_rc = $RMC_CLI_API_SCOPE_ERROR if this node goes offline
      # then we can figure all nodes are offline and be done

      if ( $state_rc != $RMC_CLI_API_SCOPE_ERROR ) {
         foreach $one_node (@online_node_names) {
            $node_offline = check_node_state($one_node,$RMC_OPSTATE_OFFLINE, %node_opstates);
            if (!$node_offline) {
               $NODES_OFFLINE = $FALSE;
            }
         }

         # check to see if we've timed out
         if ( (!$NODES_OFFLINE) && ($i > $wait_timeout) && ($wait_timeout != 0) ){
                 printCEMsg("EMsgConfigRMcliWaitTimedOut");
                 $config_rc = CRM_CLI_ERROR;
                 $TIMED_OUT = $TRUE;
         }
      }
   }
}

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

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.      #
#   $wait_timeout            Wait timeout value.                     #
#   @node_names              Node names to be started.               #
#                                                                    #
# Global Variables Modified:                                         #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#   $Trace             output   True (-T) turn Trace mode on.        #
#   $Opt_Force         output   True (-f) turn force option on.      #
#   $Opt_File_Input    output   True (-F) file name specified        #
#   $Opt_Synchronous   output   True (-w) Wait.                      #
#--------------------------------------------------------------------#
sub parse_cmd_line 
{
my(@original_argv) = @ARGV;
my @node_names = ();                    # nodes to be started
my $file_name = "";                     # file name
my $wait_timeout = 300;                 # wait timeout value
my %opts = ();

# Process the command line...
if (!&getopts('hfF:ws:m:VT', \%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{f}) {                 # -f turn on force
    $Opt_Force = $TRUE;                 # -f flag specified
}

if (defined $opts{w}) {                 # -w for wait
    $Opt_Synchronous = $TRUE;           # -w flag specified
}

if (defined $opts{s}) {                 # -s for wait timeout
    # error if -w is not
    if (!$Opt_Synchronous) {
       printCEMsg("EMsgConfigRMcliUnexpectFlag","-s","-w");
       &print_usage;
       return CRM_CLI_BAD_FLAG;
    }
    #check the timeout value
    if ( ($opts{s} =~ /^[0-9]+$/) && ($opts{s} >=0)) {
       $wait_timeout = $opts{s};
    }
    else {
       printEMsg("EMsgstoprpnodeInvTimeoutSeconds","$opts{s}");
       &print_usage;
       return CRM_CLI_BAD_FLAG;
    }
}

# Get the arguments...
# Operands:  node names
if ($#ARGV >= 0) {                      # node names   
   @node_names = @ARGV;                 # get node names
}

# make sure -F for file used if there are no node names
if (($#node_names < 0) && (!defined $opts{F})) {
    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}) {
    $Use_DefaultMMode = $FALSE;
    my $flag_value = $opts{m};
    $maintenance_mode = "${delimiter}MaintenanceModeFlag${delimiter}${flag_value}";
}

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

}   # end parse_cmd_line


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

#--------------------------------------------------------------------#
# stop_by_stoprsrc - Stop nodes by using stoprsrc-api.               #
#                                                                    #
# Return:                                                            #
#   $rc                      Return code from stoprsrc-api.          #
#                                                                    #
# Input:                                                             #
#   $options                 Options to use on stoprsrc-api.         #
#   \@nodes_to_stop          Reference to a list of nodes to stop.   #
#                                                                    #
#--------------------------------------------------------------------#
sub stop_by_stoprsrc
{
my $rc = 0;                             # return code
my $config_rc = 0;                      # return code for all
my $options = shift(@_);                # options
my $nodes_to_stop = shift(@_);          # nodes to remove (array ref)
my $one_node = "";                      # single node
my @cmd_out = ();                       # stoprsrc command result

# for each node to stop, call stoprsrc-api
foreach $one_node (@$nodes_to_stop) {

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

   #@cmd_out = `$CTBINDIR/stoprsrc $passopts -s "Name==\\\"$one_node\\\"" $RSNODE $other_opts 2>&1`;

   #@cmd_out=`$CTBINDIR/stoprsrc-api -s ${RSNODE}::Name==\\\"${one_node}\\\"${other_opts} 2>&1`;
   @cmd_out=`$CTBINDIR/stoprsrc-api -I"$delimiter" -s "${RSNODE}${delimiter}NodeNameList|<{\\\"${one_node}\\\"}${options}" 2>&1`;

   # capture the return code from stoprsrc-api
   $rc = $?;
   $rc = process_exit_code($rc);

   if ($Trace) { print STDERR "stoprsrc-api results:\n";
                 print STDERR "@cmd_out";
                 print STDERR "$PROGNAME: stoprsrc-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 stoprsrc 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;
   }
}

return ($config_rc);

}   # end stop_by_stoprsrc

#--------------------------------------------------------------------#
# stop_by_action - Stop nodes by using runact-api.                   #
#                                                                    #
# Return:                                                            #
#   $rc                      Return code from runact-api.            #
#                                                                    #
# Input:                                                             #
#   $options                 Options to use on runact-api.           #
#   \@nodes_to_stop          Reference to a list of nodes to stop.   #
#                                                                    #
#--------------------------------------------------------------------#
sub stop_by_action
{
my $rc = 0;                             # return code
my $config_rc = 0;                      # return code for all
my $action_rc = 0;                      # return code from action 
my $options = shift(@_);                # options
my $maintenance_mode_opts = shift(@_);  # maintenance mode options
my $stoprsrc_opts = shift(@_);          # options in case stoprsrc used
my $nodes_to_stop = shift(@_);          # nodes to stop (array ref)
my $one_node = "";                      # single node
my $stop_nodes = "";                    # list of nodes to stop
my $comma_needed = $FALSE;              # add comma to list?
my @cmd_out = ();                       # runact output

# build a string containing the options to be passed to the runact-api command
my $runact_options = "${options}${maintenance_mode_opts}";

# build a string containing the list of node names to stop
$stop_nodes = "";
$comma_needed = $FALSE;
foreach $one_node (@$nodes_to_stop) {
   if ( $comma_needed ) {
      $stop_nodes .= "," . $one_node;
   }
   else {
      $stop_nodes .= $one_node;
      $comma_needed = $TRUE;
   }
}

# call runact-api to stop the nodes if there are nodes to stop
if ($#$nodes_to_stop >= 0) {

   # Defect 161752
   #@cmd_out=`$CTBINDIR/runact-api -D"$delimiter" -I"$delimiter" -c "${RSNODE}${delimiter}${delimiter}StopMultipleNodes${delimiter}NodeNames${delimiter}{${stop_nodes}}${runact_options}" 2>&1`;
   my $cmd = "$CTBINDIR/runact-api -D\"$delimiter\" -I\"$delimiter\" -c ${RSNODE}${delimiter}${delimiter}StopMultipleNodes${delimiter}NodeNames${delimiter}"."\"{${stop_nodes}}\"${runact_options}";
   if ($Trace) { print STDERR "$PROGNAME: calling runact-api\n";}
   if ($Trace) { print STDERR "$PROGNAME: $cmd\n";}
   @cmd_out=`$cmd 2>&1`;


   # capture the return code from runact-api
   $rc = $?;
   $rc = process_exit_code($rc);

   if ($Trace) { print STDERR "runact-api results:\n";
                 print STDERR "@cmd_out";
                 print STDERR "$PROGNAME: runact-api returned $rc\n";}

   # show any errors if there was a bad rc
   if ($rc != 0) {
      $action_rc = process_api_error_stop_action($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 runact-api failed, print RMC error message and save rc
   if (($rc != 0) && ($rc != $RMC_CLI_USER_ERROR) && ($action_rc != $ACTION_NOT_ALLOWED_RC) ) {
   #  printCEMsg("EMsgConfigRMcliUnExpectRMCrc",$rc);
      $config_rc = CRM_CLI_RMC_ERROR;
   }

   # if the action failed because the active version is not at the correct level
   # do the stop the old way
   if ($action_rc == $ACTION_NOT_ALLOWED_RC) {
     $config_rc = stop_by_stoprsrc($stoprsrc_opts, $nodes_to_stop);
   }
}

return ($config_rc);

}   # end stop_by_action

#--------------------------------------------------------------------#
# process_api_error_stop_action - Scans the input varaible for error #
#   messages found by the c-api command. The c-api command errors are#
#   found by searching each output line for "ERROR" at the           #
#   beginning of the line.  The error message printed is the last    #
#   double colon delimited string.                                   #
#                                                                    #
# This version checks for api rc=32. This means the action can't     #
# run on this configuration and we need to go back and use           #
# stoprsrc-api.                                                      #
#                                                                    #
#                                                                    #
#                                                                    #
# Parameters:                                                        #
#   $delimiter        The delimiter used in the error string.        #
#   $rc               The return code from the c-api call.           #
#   @command_output   The output from the command.                   #
#                                                                    #
# Returns:                                                           #
#   0                 If everything went ok.                         #
#   32                If action failed due to api rc=32.             #
#                                                                    #
# Global Variables:                                                  #
#--------------------------------------------------------------------#
sub process_api_error_stop_action
{
my $rc = 0;                             # returned rc
my $delimiter = shift(@_);              # output delimiter
my $api_rc    = shift(@_);              # rc from c-api call
my @command_output = @_;                # command output to scan
my $error_count = 0;                    # number of error messages
my $line = "";

# scan each line for ERROR
foreach $line (@command_output) {

   # does it start with ERROR?
   if ($line =~ /^ERROR.*/) {

      # split it apart based on the delimiter
      # last one should be error message
      @error_parts = split /$delimiter/, $line;

      # check for scope error
      if ($error_parts[$#error_parts -1] == $RMC_SCOPE_ERROR) {
          printCEMsg("EMsgConfigRMcliNoCluster");
          exit(CRM_CLI_USER_ERROR);
      }

      $error_count++;

      # check for action not allowed error
      if ($error_parts[$#error_parts -1] == $ACTION_NOT_ALLOWED_RC) {
          $rc = $ACTION_NOT_ALLOWED_RC;
      }

      # output the message (except for scope/action)
      else {

          # replace any escaped new lines with new lines
          $error_parts[$#error_parts] =~ s/\\n/\n/g;

          # last one should be error message. print it to STDERR.
          print STDERR $error_parts[$#error_parts];
      }
   }
}

# print unexpected error if api rc was not 0 and no messages were displayed
if (($error_count == 0) && ($api_rc != 0)) {
   printCEMsg("EMsgConfigRMcliUnexpectErrorRC",$api_rc);
}

return ($rc)
}   #  end of process_api_error_stop_action