#!/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 = "@(#)89   1.29   src/rsct/rm/ConfigRM/cli/bin/startrpdomain.perl, configrmcli, rsct_rady, rady2035a 11/12/15 16:40:27"
######################################################################
#                                                                    #
# Module: startrpdomain                                              #
#                                                                    #
# Purpose:                                                           #
#   startrpdomain - brings an already defined RSCT peer domain       #
#                   online.                                          #
#                                                                    #
# Syntax:                                                            #
#   startrpdomain  [-h] [-A|L] [-t Timeout]                          #
#                  [-Q QuorumTypeName|QuorumTypeValue]               #
#                  [-m Fanout] [-w [-s Seconds]]                     #
#                  [-TV] PeerDomain                                  #
#                                                                    #
# Flags:                                                             #
#   -h      Help. Writes the command's usage statement to standard   #
#           output.                                                  #
#   -A      Use the configuration of all nodes.  -A cannot be used   #
#           with -L. If -A or -L is not specified, the configuration #
#           of a quorum of nodes is used.                            #
#   -L      Use the configuration of the local node.  -L cannot be   #
#           used with -A. If -A or -L is not specified, the          #
#           configuration of a quorum of nodes is used.              #
#   -t Timeout  Timeout interval. The number of seconds to wait for  #
#           the peer domain configuration to be determined from the  #
#           nodes in the peer domain.  This value should be long     #
#           enough for the configutation from at least a quorum of   #
#           nodes to be determined.  The default value is 20 seconds.#
#   -Q QuorumTypeName|QuorumTypeValue   The Quorum Type to use.  The #
#           quorum type can be specified as a name or a value.  A    #
#           valid quorum name can be found from the                  #
#           AvailableQuorumTypes class attribute on IBM.PeerDomain.  #
#   -m Fanout   Max number of threads to use in parallel operations  #
#           for this start operation only.  Overrides default for    #
#           'PeerDomain' stored as persistent attr in its            #
#           IBM.PeerNode class.                                      #
#   -w      Wait. When -w is specified, the command will wait for    #
#           the peer domain to be online 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 the peer domain to be online. #
#           If the waiting exceeds the number of seconds, the        #
#           command returns, but the online operation continues. The #
#           default is 300 seconds (5 minutes).  Use 0 to specify    #
#           that the command should not return until the peer        #
#           domain is online (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.                                         #
#                                                                    #
# Operands:                                                          #
#   PeerDomain  The name of a previously defined peer domain to be   #
#               brought online.                                      #
#                                                                    #
# Description:                                                       #
#   The startrpdomain command brings a defined peer domain online by #
#   starting the resources on each node belonging to the peer domain.#
#                                                                    #
#   The command must be run on a node that is defined to the peer    #
#   domain.  The command invites all the nodes (whose nominal state  #
#   is online) defined to the peer domain to come online in the peer #
#   domain every time the command is run for the peer domain.  The   #
#   command can be executed more than once in the peer domain. If    #
#   all the nodes defined in the peer domain are already online, no  #
#   action is performed.                                             #
#                                                                    #
#   A node can only be online to one peer domain at a time.  The     #
#   startrpdomain command cannot be run on a node for a peer domain  #
#   when another peer domain is already online for that node.        #
#                                                                    #
# 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 peer domain ApplDomain online and nodeA is one   #
#      of the nodes defined to this peer domain, run the following   #
#      command on nodeA:                                             #
#      startrpdomain ApplDomain                                      #
#                                                                    #
# Man Page:                                                          #
#   For the most current detailed description of this command see    #
#   the startrpdomain man page in /opt/rsct/man.                #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /opt/rsct/msgmaps/configrmcli.startrpdomain.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 check for cluster already online.        #
#   011015 JAC 77328: Add timeout and config cmd arguments.          #
#   020108 JAC 79352: Pass both cmd args when at least 1 is specified#
#   020202 JAC 79963: weed out startrsrc error messages.             #
#   020207 JAC 80121: Make printing of c-api results a trace msg.    #
#   020421 JAC 82248: Rename startcluster to startrpdomain.          #
#   020428 JAC 82316: Call process_exit_code to check $rc.           #
#   020502 JAC 82564: Set local scope before calling startrsrc-api.  #
#   020530 JAC 82589: Use startrsrc-api instead of startrsrc.        #
#   020619 JAC 84268: Fix call to startrsrc-api to skip nodelist.    #
#   020724 JAC 85045: Change -a to -A for command consistency.       #
#   020822 JAC 86035: Add -L option.                                 #
#   020916 JAC 86909: Fix for setting OnlineCriteria option.         #
#   030324 JAC 93122: Don't specify default command arguments.       #
#   030421 JAC 93428: Rework 93122. Timeout passed as 0 by startrsrc.#
#   040329 JAC 102042: Add -Q quorum flag.                           #
#   040407 JAC 105863: Use escape_chars for "\" searches.            #
#   040602 JAC 109790: Don't pass Timeout if not specified. (0 ok)   #
#   040616 JAC 110694: Move where quorum type (-Q) is processed.     #
#   041118 JMS 104741: make ConfigRM RMC thread fanout configurable  #
#   050124 JMS 116382: -m instead of -n for fanout                   #
#   050406 JAC 119510: Add rc when calling process_api_error.        #
#   051215 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. Make sure the cluster exists.                                   #
# 4. Make sure there are no other clusters online.                   #
# 5. Call RMC command startrsrc. Also pass along -VT if necessary.   #
# 6. 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 CT_cli_input_utils qw(escape_chars);

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_source_node
                     process_api_error
                     process_exit_code
                     check_quorum_type
                     get_opstate_by_name
                     get_opstate_by_name_rc
                     get_domain_type);
use CRM_cli_include qw($TRUE $FALSE
                       $RSCLUSTER $RMC_CLI_USER_ERROR
                       $RMC_OPSTATE_ONLINE
                       $LOCAL_SCOPE
                       $CTBINDIR $CTDIR);

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

$Opt_Timeout = $FALSE;                  # default - no timeout value
$Opt_All_Criteria = $FALSE;             # default - not all nodes      
$Opt_Local_Criteria = $FALSE;           # default - not local node      
$Opt_QuorumType = $FALSE;               # default - no quorum type
$Opt_Fanout = $FALSE;                   # default - no fanout
$Opt_Synchronous = $FALSE;              # default - no waiting

$PROGNAME = "startrpdomain";            # 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 $cluster_name = "";                  # cluster to start
my $timeout = 0;                        # timeout value     
my %cluster_opstates = ();              # cluster opstates 
my %cluster_opstates_new = ();          # cluster opstates refreshed
my @clusters = ();                      # clusters of the node
my $one_cluster = "";                   # one cluster name
my $local_node = "";                    # node you're on
my $found = 0;                          # boolean value
my $i = 0;                              # counter
my @cmd_out = ();                       # output from startrsrc
my $q_type = "";                        # quorum type
my $fanout = 0;	                        # fanout
my $wait_timeout = 300;                 # wait timeout value
my $wait_poll_int = 30;                 # wait polling interval
my $TIMED_OUT = $FALSE;                 # check timeout 

my $passopts = "";                      # TV options to pass to RMC CLI
my $other_opts = "";                    # parameters to pass to RMC CLI
#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#
my $rc = 0;
my $state_rc = 0;

# set local scope
$ENV{CT_MANAGEMENT_SCOPE} = $LOCAL_SCOPE;

# parse the command line, exit if there are errors 
($rc, $cluster_name, $timeout, $q_type, $fanout, 
      $wait_timeout) = &parse_cmd_line;
($rc == 0) || error_exit($rc);

if ($Verbose) { printIMsg("IMsgstartrpdomainStart",$cluster_name); }

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

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

# Do some escaping on strings.  Do the escape character first
# for anything that's a string, escape any inner \
#$cluster_name =~ s/\\/\\\\/g;

# for anything that's a string, escape any inner double quotes
#$cluster_name =~ s/\"/\\\"/g;

$cluster_name = escape_chars($cluster_name);

# if either online criteria, timeout, or quorum is specified,
# pass as command arguments to startrsrc
if ($Opt_All_Criteria || $Opt_Local_Criteria || $Opt_Timeout ||
    $Opt_QuorumType || $Opt_Fanout) {

   # skip over nodelist
   $other_opts = "::";

   # set the OnlineCriteria option
   # (1=all nodes, 2=local node, otherwise default to quorum 0)
   if ($Opt_All_Criteria) {
      $other_opts = $other_opts . "::OnlineCriteria::1";
   }
   if ($Opt_Local_Criteria) {
      $other_opts = $other_opts . "::OnlineCriteria::2";
   }
   #93122 - comment out specifying the default command argument
   #else { $other_opts = $other_opts . "::OnlineCriteria::0"; }

   # set the Timeout option
   if ($Opt_Timeout) {
      # make sure timeout value is positive integer
      if ($timeout <= 0) {
         printEMsg("EMsgstartrpdomainInvTime",$timeout);
         exit(CRM_CLI_USER_ERROR);
      }
      $other_opts = $other_opts . "::Timeout::$timeout";
   }
   #93122 - comment out specifying the default command argument
   #93248 - timeout needs to be specified or else startrsrc-api sets it to 0
   #109790 - back to not including if not specified. 0 means 120 to RM
   #else { $other_opts = $other_opts . "::Timeout::120"; }

   # pass in quorum type, if specified
   if ($Opt_QuorumType) {                # -Q used for quorum type
      $other_opts = $other_opts."::StartQuorumType::$q_type";
   }

   # pass in fanout, if specified
   if ($Opt_Fanout) {                    # -n used for fanout
      $other_opts = $other_opts."::Fanout::$fanout";
   }
}

# get the cluster opstate info
%cluster_opstates = get_opstate_by_name($RSCLUSTER);

# get a list of the cluster names for this node
@clusters = keys %cluster_opstates;

# make sure the cluster to start exists
$found = $FALSE;
$i = 0;
while (!$found && ($i <= $#clusters)){
   if ($clusters[$i] eq $cluster_name) { $found = $TRUE;}
   $i++;
}

# print error message if the cluster name wasn't in the list
if (!$found) {
    printEMsg("EMsgstartrpdomainClusterNotFound",$cluster_name);
    exit(CRM_CLI_USER_ERROR);
}

# make sure there are no online clusters - besides this one
# (ok to "restart". restart invites all nodes to come online
foreach $one_cluster (@clusters) {
   if (($cluster_opstates{$one_cluster} == $RMC_OPSTATE_ONLINE) && ($cluster_name ne $one_cluster)) {
      # get the resolved name of the node you're on
      $local_node = get_source_node;
      # print error message if another cluster is online
      printEMsg("EMsgstartrpdomainAlreadyOnline",$cluster_name,$local_node,$one_cluster);
      exit(CRM_CLI_USER_ERROR);
   }
}

# Quit here for CAA domain type
if(get_domain_type()) {
    #printCEMsg("EMsgConfigRMCmdNotSupported");
    exit (CRM_CLI_SUCCESS);
}

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

#@cmd_out = `$CTBINDIR/startrsrc $passopts -s "Name==\\\"$cluster_name\\\"" $RSCLUSTER $other_opts 2>&1`;

@cmd_out=`$CTBINDIR/startrsrc-api -s ${RSCLUSTER}::Name==\\\"${cluster_name}\\\"${other_opts} 2>&1`;

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

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

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

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

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

# if startrsrc failed for something else, print RMC error message and exit
if ($rc != 0) {
#   printCEMsg("EMsgConfigRMcliUnExpectRMCrc",$rc);
    exit(CRM_CLI_RMC_ERROR);
    }

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

   # check opstate until online or timeout exceeded
   while ( ($cluster_opstates{$cluster_name} != $RMC_OPSTATE_ONLINE) &&
           (!$TIMED_OUT) )  {
      sleep($wait_poll_int);
      $i = $i + $wait_poll_int;
      ($state_rc, %cluster_opstates_new) = get_opstate_by_name_rc($RSCLUSTER);
      if ($state_rc == 0) {
         %cluster_opstates = %cluster_opstates_new;
      }
      if ( ($cluster_opstates{$cluster_name} != $RMC_OPSTATE_ONLINE) &&
           ($i > $wait_timeout) && ($wait_timeout != 0) ){
              printCEMsg("EMsgConfigRMcliWaitTimedOut");
              $rc = CRM_CLI_ERROR;
              $TIMED_OUT = $TRUE; 
           } 
   }
}

if ($Verbose) { printIMsg("IMsgstartrpdomainEnd",$cluster_name); }

exit($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.      #
#   $cluster_name            Name of cluster to start.               #
#   $timeout                 Timeout value.                          #
#   $q_type                  Quorum type.                            #
#   $fanout                  Fan out value.                          #
#   $wait_timeout            Wait timeout value.                     #
#                                                                    #
# Global Variables Modified:                                         #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#   $Trace             output   True (-T) turn Trace mode on.        #
#   $Opt_Timeout       output   True (-t) timeout specified.         #
#   $Opt_All_Criteria  output   True (-A) all nodes criteria spec.   #
#   $Opt_Local_Criteria output  True (-L) local node criteria spec.  #
#   $Opt_QuorumType    output   True (-Q) Quorum type                #
#   $Opt_Fanout        output   True (-m) Fanout.                    #
#   $Opt_Synchronous   output   True (-w) Wait.                      #
#--------------------------------------------------------------------#
sub parse_cmd_line 
{
my(@original_argv) = @ARGV;
my $cluster_name = "";                  # cluster name    
my $timeout = 0;                        # timeout value   
my $q_type = "";                        # quorum type
my $fanout = 0;                         # fanout value   
my $wait_timeout = 300;                 # wait timeout value   
my %opts = ();

# Process the command line...
if (!&getopts('ht:ALQ:m:ws: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{t}) {                 # -t timeout value
    $timeout = $opts{t};
    $Opt_Timeout = $TRUE;               # -t flag specified
}

if (defined $opts{A}) {                 # -A online criteria all nodes
    $Opt_All_Criteria = $TRUE;          # -A flag specified
}

if (defined $opts{L}) {                 # -L online criteria local node
    # error if -A is set
    if ($Opt_All_Criteria) {
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-A","-L");
       &print_usage;
       return CRM_CLI_BAD_FLAG;
    }
    $Opt_Local_Criteria = $TRUE;        # -L flag specified
}

if (defined $opts{Q}) {                 # -Q for quorum group name
    $q_type = $opts{Q};
    # turn quorum type into a number
    $q_type = check_quorum_type($q_type);
    $Opt_QuorumType = $TRUE;            # -Q flag specified
}

if (defined $opts{m}) {                 # -m for fanout
    $fanout = $opts{m};
    $Opt_Fanout = $TRUE;                # -m 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("EMsgstartrpdomainInvTimeoutSeconds","$opts{s}");
       &print_usage;
       return CRM_CLI_BAD_FLAG;
    }
}

# Get the arguments...
# Operands:  cluster name
if ($#ARGV == 0) {                      # cluster name 
   $cluster_name = shift @ARGV;         # get cluster name
}

else {            
    # cluster name not specified or invalid operands
    printCEMsg("EMsgConfigRMcliInvalidNumberOfOperands");
    &print_usage;
    return CRM_CLI_BAD_OPERAND;
}

return(0, $cluster_name, $timeout, $q_type, $fanout, $wait_timeout);

}   # end parse_cmd_line


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