#!/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 = "@(#)80   1.30   src/rsct/rm/ConfigRM/cli/bin/lsrpnode.perl, configrmcli, rsct_rady, rady2035a 11/12/15 16:41:04"
######################################################################
#                                                                    #
# Module: lsrpnode                                                   #
#                                                                    #
# Purpose:                                                           #
#   lsrpnode  - lists information about the nodes defined in an      #
#               RSCT peer domain.                                    #
#                                                                    #
# Syntax:                                                            #
#   lsrpnode   [-h]  [-o|-O|-L]  [-l|-t|-d|-D Delimiter] [-x] [-i]   #
#              [-P]  [-Q]  [-B] [-TV]  [Node]                        #
#   lsrpnode   [-h]  [-l|-t|-d|-D Delimiter] [-x] [-p PeerDomain]    #
#              [-TV]                                                 # 
#                                                                    #
# Flags:                                                             #
#   -h      Help. Writes the command's usage statement to standard   #
#           output.                                                  #
#   -i      Include the node number and node ID in the output.       #
#           This information is helpful for debugging purposes.      #
#   -Q	    Indicates if the node participates in quorum decisions.  #
#           "yes" is displayed if the node participates in quorum    #
#           decisions.  "no" is displayed if the node does not       #
#           participate in quorum decisions.                         #     
#   -B	    Indicates if the node has access to the peer domain's    #
#           tiebreaker mechanism.  "yes" is displayed if the node    #
#           does has access to the peer domain's tiebreaker          #
#           mechanism. "no" is displayed if the node does not have   #
#           access to the peer domain's tiebreaker mechanism.        #
#   -P	    Indicates if the node is a group services group leader   #
#           candidate."yes" is displayed if the node can be a group  #
#           services group leader."no" is displayed if the node      #
#           cannot be a group service group leader.                  # 
#   -o      Online nodes.  List only information about the online    #
#           nodes in the peer domain.                                #
#   -O      Offline nodes.  List only information about nodes that   #
#           are offline (not online) in the peer domain.             #
#   -L      Local node. List the information about the local node.   #
#   -l      Long formatted output.  The information is displayed on  #
#           separate lines.                                          #
#   -t      Tabular formatted output.  The information is displayed  #
#           in separate columns.                                     #
#   -d      Delimiter-formatted output.  The default delimiter is a  #
#           colon (:).  Use the -D flag if you wish to change the    #
#           default delimiter.                                       #
#   -D Delimiter   Delimiter-formatted output that uses the specified#
#           delimiter.  Use this flag to specify something other     #
#           than the default colon (:).  An example is when the data #
#           to be displayed contains colons.  Use this flag to       #
#           specify a delimiter of one or more characters.           #
#   -x      Exlude header.  Suppress header printing.                #
#   -p PeerDomain. Lists the node names in the specified peer        #
#           domain.  This option does not require the peer domain to #
#           be online.                                               #   
#   -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:                                                          #
#   Node    The name of the node to be listed. Node may be a node    #
#           name or a substring of a node name.  When it is a        #
#           substring, any defined node name that contains the       #
#           substring will be listed.                                #
#                                                                    #
# Description:                                                       #
#   The lsrpnode command lists information about the nodes that are  #
#   in the online peer domain. The flags and operands can be used to #
#   specify what is listed.  When a node name is specified, the      #
#   listed information is limitted to that node information only.    #
#   The -o or -O flag also limits the listed information.  The -o    #
#   flag lists only the online node's  information.  The -O flag     #
#   lists information for nodes that are not online.                 #
#                                                                    #
#                                                                    #
# 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.                         #
#   6  CRM_CLI_NOT_FOUND     Node not defined in peer domain.        #
#                                                                    #
# Examples:                                                          #
#                                                                    #
# Man Page:                                                          #
#   For the most current detailed description of this command see    #
#   the lsrpnode man page in /opt/rsct/man.                     #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /opt/rsct/msgmaps/configrmcli.lsrpnode.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.                        #
#   011008 JAC 75442: Final version.                                 #
#   020201 JAC 79963: switch to use lsrsrc-api instead of lsrsrc.    #
#   020206 JAC 80077: Add NLS support for heading and xlate values.  #
#   020207 JAC 80121: Make printing of c-api results a trace msg.    #
#   020207 JAC 80133: Issue not found message if a node specified.   #
#   020227 JAC 80700: Get -a and -n flags to work.                   #
#   020412 JAC 81856: Add -L flag to list local node.                #
#   020417 JAC 81859: Fix usage message.                             #
#   020422 JAC 82248: Rename lsclnode to lsrpnode.                   #
#   020425 JAC 82405: Add -p flag to list domains from the registry. #
#   020428 JAC 82316: Call process_exit_code to check $rc.           #
#   020502 JAC 82649: Fix -p not found error to use $rc, not msg no. #
#   020503 JAC 82564: Set peer domain scope before calling lsrsrc-api#
#   020724 JAC 85045: Change -a|-n to -o|-O for cmd consistency.     #
#   021015 JAC 88041: Use lssrtbl-api instead of lssrtbl for -p.     #
#   021024 JAC 88405: Fix 88041 for missing line 1 perl directive.   #
#   030108 JAC 90576: Use new lssrctbl-api syntax.                   #
#   030203 JAC 89737: Continue to display data if there's an error.  #
#   030909 JAC 98648: Print "no nodes" msg for no resources found.   #
#   040407 JAC 105863: Use escape_chars for "\" searches.            #
#   040422 JAC 108194: Use escape_selstr for node name and use LIKE  #
#                      in select string.                             #
#   040726 JAC 109612: Add -i flag to show node names and node IDs.  #
#   050406 JAC 119510: Add rc when calling process_api_error.        #
#   071031 JAC 146610: Add -B Tiebreaker support.                    #
######################################################################

#--------------------------------------------------------------------#
# General Program Flow/Logic:                                        #
#                                                                    #
# 1. Setup attributes to list with the command.                      #
# 2. Parse command line flags and operands.                          #
# 3. Print usage if -h specified                                     #
# 4. Setup a selection string if a node name operand was specified.  #
# 5. List the node information used the RMC lsrsrc command.          #
#    Also pass along -VT if necessary.                               #
# 6. Return back any errors from lsrsrc.                             #
# 7. Format the output into a 2D array so a utility can be used.     #
# 8. Set the type of output to display (long, table, ...).           #
# 9. Call the set_display utility to display the output.             #
#                                                                    #
#--------------------------------------------------------------------#

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

use CT_cli_utils qw(printIMsg
                    printEMsg);

use CT_cli_display_utils qw(set_display);

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_NOT_FOUND
                  CRM_CLI_BAD_OPERAND CRM_CLI_USER_ERROR);
use CRM_cli_utils qw(error_exit
                     printCIMsg
                     printCEMsg
                     getIMsg
                     process_api_error
                     process_exit_code
                     remove_api_error
                     get_source_node
                     escape_selstr
                     lookup_opstate
                     lookup_yesno);
use CRM_cli_include qw($TRUE $FALSE
                       $RSNODE $RMC_CLI_USER_ERROR
                       $RMC_OPSTATE_ONLINE
                       $RMC_CLI_RSRC_NOT_FOUND
                       $DELIMITER $PEER_DOMAIN_SCOPE
                       $CTBINDIR $CTDIR);

#--------------------------------------------------------------------#
# Global Constants                                                   #
#--------------------------------------------------------------------#

#$attrs = "Name OpState ConfigChanged NodeList RSCTVersion ClassVersions";
# attribute names
$Attr_name = "Name";
$Attr_rsctversion = "RSCTVersion";
$Attr_nodelist = "NodeList";
#$Attr_classvers = "ClassVersions";
$Attr_opstate = "OpState";
#$Attr_configch = "ConfigChanged";
$Attr_nodeIDs = "NodeIDs";
$Attr_isquorum = "IsQuorumNode";
$Attr_isprefGSGL = "IsPreferredGSGL";

$lsr_attr_names = $Attr_name;
$lsr_attr_names .= "::$Attr_opstate";
$lsr_attr_names .= "::$Attr_rsctversion";

# override DELIMITER so IPv6 address node names display properly
$DELIMITER = "#:#";

#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
$Trace = $FALSE;                        # default - trace off
$Verbose = $FALSE;                      # default - verbose turned off
$Opt_Delm_Format = $FALSE;              # default - no delimited output
                                        #         -d, -D flags
$Opt_Delm_Str = ":";                    # default - delimiter character
                                        # can be overridden with -D flag
$Opt_Long_Format = $FALSE;              # default - long formatted output
$Opt_Table_Format = $TRUE;              # default - tabular format 
$Opt_No_HDR = $FALSE;                   # default - do not supress heading
                                        # output, -x flag
$Opt_Only_Online = $FALSE;              # default - do not list only      
                                        # the online cluster
$Opt_Only_NotOnline = $FALSE;           # default - do not list only the
                                        # clusters that are not online
$Opt_Local = $FALSE;                    # default - list all nodes in cluster
$Opt_Cluster = $FALSE;                  # default - list peer nodes
$Opt_ShowNodeNums = $FALSE;             # default - no node nums/IDs
$Opt_ShowQuorum = $FALSE;               # default - no quorum 
$Opt_ShowTieBreaker = $FALSE;           # default - no tiebreaker
$Opt_ShowPrefGSGL = $FALSE;             # default - no group service group leader perferred

$PROGNAME = "lsrpnode";                 # 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_name = "";                     # node to list
my $node_name_orig = "";                # node to list
my $name_select = "";                   # selection string for name
my @lsr_out = ();                       # output from lsrsrc            
my @lsr_out2 = ();                      # copy of output from lsrsrc            
my @lof_nodes = ();                     # 2D array to be displayed      
my $attr_name = "";                     # an attribute name
my @attr_names = ();                    # all attribute names
my $attr_val = "";                      # an attribute value
my @attr_vals = ();                     # all attribute values
my $line = "";                          # one line of output
my $i = 0;                              # array index
my $j = 0;                              # array index
my $row_count = 0;                      # number of display rows
my $col_count = 0;                      # number of display columns
my $display_type = "";                  # type of display call
my $opstate_index = 2;                  # output index for opstate
my $nodeID_index = 0;                   # output index for nodeID
my $name_index = 1;                     # output index for name
my $quorum_index = 0;                   # output index for IsQuorumNode
my $tiebreaker_index = 0;               # output index for IsQuorumNode
my $prefGSGL_index = 0;                 # output index for IsPreferredGSGL
my @heading = ();                       # heading for listed data
my @opstatetext = ();                   # for translating opstate
my @noyestext = ();                     # for translating no/yes
my %op_states = ();                     # hash for opstate descriptions
my $local_node = "";                    # node you're on (-L)
my $cluster_name = "";                  # for -p to list nodes from reg.
my @reg_names = ();                     # node names from registry

my $passopts = "";                      # TV options to pass to RMC CLI
my $other_opts = "";                    # parameters to pass to RMC CLI
#--------------------------------------------------------------------#
# 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, $node_name, $cluster_name) = &parse_cmd_line;
($rc == 0) || error_exit($rc);

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

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

# if -i was specified, add two more attributes to get
if ($Opt_ShowNodeNums) {
    $lsr_attr_names .= "::$Attr_nodelist";
    $lsr_attr_names .= "::$Attr_nodeIDs";
}
# if -Q was specified, add IsQuorumNode attribute to get
if($Opt_ShowQuorum) {
   $lsr_attr_names .= "::$Attr_isquorum";
}

#if -P was specified, add IsPreferredGSGL attribute to get
if($Opt_ShowPrefGSGL) {
   $lsr_attr_names .= "::$Attr_isprefGSGL";
}

# if -B was specified, add IsQuorumNode attribute to get
# if -Q didn't already get it
if($Opt_ShowTieBreaker) {
   if(!$Opt_ShowQuorum) {
      $lsr_attr_names .= "::$Attr_isquorum";
   }
}

# process the -p flag separately 
if ($Opt_Cluster) {

   # get node names in registry for this cluster
   @reg_names = &list_cluster_nodes($cluster_name);

   # form 2D array to pass to display utils

   # get heading from message file  
   @heading = getIMsg("IMsglsrpnodeHeadingName");

   # form heading
   @attr_names = split(/::/,$heading[0]);
   $i = 1;
   foreach $attr_name (@attr_names) {
      $lof_nodes[0][$i] = $attr_name;
      $i++;
   }

   # form each line of output
   $i = 1;
   foreach $line (@reg_names) {
      chomp($line);

      # parse the output
      @attr_vals = split(/$DELIMITER/, $line);

      # put into array
      $lof_nodes[$i][0] = " ";
      $j = 1;
      foreach $attr_val (@attr_vals) {

         # get rid of quotes
         $attr_val =~ s/^\"//; 
         $attr_val =~ s/\"$//; 

         # put output into the output array
         $lof_nodes[$i][$j] = $attr_val;
         $j++;
      }
      $i++;
   }
}

# process everything else but -p flag
else {

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

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

$node_name_orig = $node_name;
$node_name = escape_chars($node_name);
$node_name = escape_selstr($node_name);

# if a node name was entered, build a select clause
if ($node_name ne "") {
   # $name_select = "-s ${RSNODE}::\"Name ?= \\\"$node_name\\\"\"";
   $name_select = "-s ${RSNODE}::\"Name LIKE \\\"%${node_name}%\\\"\"";
}
# for no name, use o flag for list
else {
   $name_select = "-o ${RSNODE}";
}

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

#@lsr_out = `$CTBINDIR/lsrsrc -x -D $DELIMITER $passopts $name_select $RSNODE $lsr_attr_names`;
@lsr_out = `$CTBINDIR/lsrsrc-api -D "$DELIMITER" ${name_select}::::::${lsr_attr_names} 2>&1`;

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

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

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

# show any errors if there was a bad rc
if (($rc != 0) && ($rc != $RMC_CLI_RSRC_NOT_FOUND)) {
   process_api_error($DELIMITER,$rc,@lsr_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);}

# return OK if resource not found error, unless a node was specified
if ($rc == $RMC_CLI_RSRC_NOT_FOUND) {
#  if ($node_name eq "") { exit(0);}
   if ($node_name eq "") {
      printCEMsg("EMsgConfigRMcliNoCluster");
      exit(CRM_CLI_USER_ERROR);
    }
   else {
      printEMsg("EMsglsrpnodeNodeNotFound",$node_name_orig);
      exit(CRM_CLI_NOT_FOUND);
    }
}

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

# limit the output if -o, -O, or -L  was specified
if ($Opt_Only_Online || $Opt_Only_NotOnline || $Opt_Local) {
   @lsr_out2 = @lsr_out;
   @lsr_out = ();

   # find the local node name for -L
   $local_node = get_source_node;

   # look at each line to see if -o, -O, or -L applies
   foreach $line (@lsr_out2) {
      chomp($line);

      # parse the output
      @attr_vals = split(/$DELIMITER/, $line);

      if ($Opt_Only_Online && ($attr_vals[$opstate_index-1] == $RMC_OPSTATE_ONLINE)) {
         push (@lsr_out,$line);
      }
      if ($Opt_Only_NotOnline && ($attr_vals[$opstate_index-1] != $RMC_OPSTATE_ONLINE)) {
         push (@lsr_out,$line);
      }
      if ($Opt_Local && ($attr_vals[$name_index-1] eq $local_node)) {
         push (@lsr_out,$line);
      }
   }
}

# if -Q for quorum and -B for tiebreaker are both specified,
# copy the quorum column of data to create a tiebreaker column
if ($Opt_ShowQuorum && $Opt_ShowTieBreaker) {

   # look at each line to break up the attribute values
   $i = 0;
   foreach $line (@lsr_out) {
      chomp($line);

      # parse the output
      @attr_vals = split(/$DELIMITER/, $line);

      # if -P was specified, PreferredGSGL is last column and QuorumNode is last-1
      # if -P was not specified, QuorumNode is last column
      if ($Opt_ShowPrefGSGL) {
         $lsr_out[$i] .= $DELIMITER . $attr_vals[$#attr_vals-1];
      }
      else {
         $lsr_out[$i] .= $DELIMITER . $attr_vals[$#attr_vals];
      }

      $i++
   }
}

# form 2D array to pass to display utils

# get the appropriate heading from the message file
if ($Opt_ShowNodeNums) {
    $nodeID_index = 5; # indicate the position for offset nodeID
    @heading = getIMsg("IMsglsrpnodeHeadingIFlag");
}
else {
    @heading = getIMsg("IMsglsrpnodeHeading");
}

# form heading, $i is the index of attr 
@attr_names = split(/::/,$heading[0]);
$i = 1;
foreach $attr_name (@attr_names) {
   $lof_nodes[0][$i] = $attr_name;
   $i++;
} 

# form heading for IsQuorumNode - Quorum
if ( $Opt_ShowQuorum ) {
   @heading = getIMsg("IMsglsrpnodeHeadingQ");
   $lof_nodes[0][$i] = $heading[0];
   $quorum_index = $i;
   $i++;
}

# form heading for IspreferredGSGL
if ( $Opt_ShowPrefGSGL ) {
   @heading = getIMsg("IMsglsrpnodeHeadingP");
   $lof_nodes[0][$i] = $heading[0];
   $prefGSGL_index =$i;
   $i++;
}

# form heading for IsQuorumNode - Tiebreaker
if ( $Opt_ShowTieBreaker ) {
   @heading = getIMsg("IMsglsrpnodeHeadingB");
   $lof_nodes[0][$i] = $heading[0];
   $tiebreaker_index = $i;
   $i++;
}

# get from message file what to use for opstates
@opstatetext = getIMsg("IMsglsrpnodeOpState");
@opstatetext = split(/::/,$opstatetext[0]);

# fill in the hash for opstates
for ($op_cnt=0;$op_cnt<=$#opstatetext;$op_cnt=$op_cnt+2) {
   $op_states{$opstatetext[$op_cnt]} = $opstatetext[$op_cnt+1];
}
# get from message file what to use for yes/no
@noyestext = getIMsg("IMsglsrpnodeNoYes");
@noyestext = split(/::/, $noyestext[0]);

# form each line of output
$i = 1;
foreach $line (@lsr_out) {
   chomp($line);

   # parse the output
   @attr_vals = split(/$DELIMITER/, $line);

   # put into array
#  $lof_nodes[$i][0] = "\nnode $i:";
   $lof_nodes[$i][0] = " ";
   $j = 1;
   foreach $attr_val (@attr_vals) {
      # translate opstate
      if ($j == $opstate_index) {
          $attr_val = lookup_opstate($attr_val,%op_states);
      }

      elsif ($j == $nodeID_index) {
          $attr_val =~ s/^{//;
          $attr_val =~ s/}$//;
          $attr_val = &xlate_dec2hex_hardway($attr_val);
      }

      # translate yes/no for IsQuorumNode - Quorum
      elsif ($j == $quorum_index) {
         $attr_val = &lookupQuorum_yesno($attr_val,@noyestext);
      }

      # translate yes/no for IsPreferredGSGL
      elsif ($j == $prefGSGL_index) {
         $attr_val = lookup_yesno($attr_val,@noyestext);
      }
      
      # translate yes/no for IsQuorumNode - Tiebreaker
      elsif ($j == $tiebreaker_index) {
         $attr_val = &lookupTieBreaker_yesno($attr_val,@noyestext);
      }

      # put output into the output array 
      $lof_nodes[$i][$j] = $attr_val;
      $j++;
   }
   $i++;
}

}    # end else (process everything but -p)

# set how many rows of data there is
$row_count = $#lof_nodes; 
$col_count = scalar(@{$lof_nodes[0]});

# set formatting string needed by set_display
($Opt_Long_Format)  && ($display_type = "long");
($Opt_Table_Format) && ($display_type = "column");
($Opt_Delm_Format)  && ($display_type = "delim");

# print out results, if any
if ($row_count > 0) {
   $rc = set_display($display_type, $Opt_No_HDR, $row_count, $col_count,
                     \@lof_nodes, $Opt_Delm_Str);
}

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

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_name               Name of node to list.                   #
#                                                                    #
# Global Variables Modified:                                         #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#   $Trace             output   True (-T) turn Trace mode on.        #
#   $Opt_Delm_Format   output   True (-d,-D) delimited output        #
#   $Opt_Delm_Str      output   char (-D) delimiter character        #
#   $Opt_Long_Format   output   True (-l) long formatted output      #
#   $Opt_Table_Format  output   True (-t) tabular formatted output   #
#   $Opt_No_HDR        output   True (-x) supress heading            #
#   $Opt_Only_Online   output   True (-o) list only online cluster   #      
#   $Opt_Only_NotOnline output  True (-O) list clusters not online   #
#   $Opt_Local         output   True (-L) list local node only       #
#   $Opt_Cluster       output   True (-p) list cluster from registry #
#   $Opt_ShowNodeNums  output   True (-i) show node nums and IDs     #
#   $Opt_ShowQuorum    output   True (-P) show attr IsQurorumNode    #
#   $Opt_ShowTieBreaker output  True (-B) show attr IsQurorumNode    #
#   $Opt_ShowPrefGSGL  output   True (-Q) show attr IsPreferredGSGL  #
#--------------------------------------------------------------------#
sub parse_cmd_line 
{
my(@original_argv) = @ARGV;
my $node_name = "";                     # node name    
my $cluster_name = "";                  # cluster name for -p
my %opts = ();

# Process the command line...
if (!&getopts('hioOLltdD:xp:QBPVT', \%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{o}) {                 # -o for online cluster only
    $Opt_Only_Online = $TRUE;
}

if (defined $opts{O}) {                 # -O for not online clusters
                                        #    only
    if ($Opt_Only_Online) {             # -o already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-o","-O");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    $Opt_Only_NotOnline = $TRUE;
}

if (defined $opts{L}) {                 # -L for list local only
    if ($Opt_Only_Online) {             # -o already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-o","-L");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    if ($Opt_Only_NotOnline) {          # -O already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-O","-L");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    $Opt_Local = $TRUE;
}

if (defined $opts{D}) {                 # -D delimited output with specified
    $Opt_Delm_Format = $TRUE;           #    delimiter
    $Opt_Delm_Str = $opts{D};
    $Opt_Long_Format = $FALSE;
}

if (defined $opts{d}) {                 # -d delimited output
    $Opt_Delm_Format = $TRUE;
    $Opt_Delm_Str = ":";
    $Opt_Long_Format = $FALSE;
}

if (defined $opts{t}) {                 # -t table formatted output
    $Opt_Table_Format = $TRUE;
    $Opt_Long_Format = $FALSE;
    $Opt_Delm_Format = $FALSE;
}

if (defined $opts{l}) {                 # -l long formatted output
    $Opt_Long_Format = $TRUE;
    $Opt_Table_Format = $FALSE;
    $Opt_Delm_Format = $FALSE;
}

if (defined $opts{x}) {                 # -x supress headings
    $Opt_No_HDR = $TRUE;
}

if (defined $opts{i}) {                 # -i show nodenum and nodeid
    $Opt_ShowNodeNums = $TRUE;
}

if (defined $opts{Q}) {                 # -Q show IsQuorumNode
    $Opt_ShowQuorum = $TRUE;
}

if (defined $opts{B}) {                 # -B show IsQuorumNode
    $Opt_ShowTieBreaker = $TRUE;
}

if (defined $opts{P}) {                 # -P show IsPreferredGSGL
    $Opt_ShowPrefGSGL = $TRUE;
}
# Get the arguments...
# Operands:  node name      
if ($#ARGV <= 0) {                      # node name 
   if ($#ARGV == 0) {
      $node_name = shift @ARGV;         # get node name
   }
}

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

if (defined $opts{p}) {                 # -p show cluster from registry
    if ($Opt_Only_NotOnline) {          # -O already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-O","-p");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    if ($Opt_Only_Online) {             # -o already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-o","-p");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    if ($Opt_Local) {                   # -L already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-L","-p");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    if ($Opt_ShowNodeNums) {            # -i already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-i","-p");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    if ($Opt_ShowQuorum) {              # -Q already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-Q","-p");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    if ($Opt_ShowTieBreaker) {          # -B already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-B","-p");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    if ($Opt_ShowPrefGSGL) {            # -P already specified
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-P","-p");
       &print_usage;
       exit(CRM_CLI_BAD_FLAG);
    }
    if ($node_name ne "") {             # node name specified
       printCEMsg("EMsgConfigRMcliInvalidNumberOfOperands");
       &print_usage;
       exit(CRM_CLI_BAD_OPERAND);
    }
    $Opt_Cluster = $TRUE;
    $cluster_name = $opts{p};
}

return(0, $node_name, $cluster_name);    # success

}   # end parse_cmd_line


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


#--------------------------------------------------------------------#
# list_cluster_nodes : Returns an array of cluster node name for the #
#   cluster name specified.  The information is taken from the system#
#   registry.                                                        #
#                                                                    #
# Parameters:                                                        #
#   $cluster_name    The name of the cluster for which the cluster   #
#                    node names are to be found.                     #
#                                                                    #
# Returns:                                                           #
#   @node_names      Array of cluster node names for the cluster.    #
#                                                                    #
#--------------------------------------------------------------------#
sub list_cluster_nodes
{
my $cluster_name = shift(@_);           # cluster name to look up
my $trace_opt = "";                     # don't trace lssrtbl
my @listout2 = ();                      # lsrsrc-api output
my @listout = ();                       # sorted lsrsrc-api output

$main::Trace && print STDERR "$main::PROGNAME: calling lssrtbl-api\n";
$main::Trace && ($trace_opt = " -T ");

# export the appropriate environment variable
$ENV{CT_SR_FS_HOME} = "/var/ct/$cluster_name/registry/local_tree";

# list the nodes for the cluster from the registry
@listout2 = `$CTBINDIR/lssrtbl-api $trace_opt -D $DELIMITER -s /IBM/PeerNode/Resources::::Name 2>&1`;

$rc = $?;
$rc = process_exit_code($rc);
$main::Trace && print STDERR "$main::PROGNAME: lssrtbl-api returned $rc\n";

# see if no cluster was found
if ($rc == $RMC_CLI_USER_ERROR) { 
   # print not found error
   printEMsg("EMsglsrpnodeClusterNotFound",$cluster_name);
   exit(CRM_CLI_USER_ERROR);
}

# if list command failed, print RMC error message and exit
if ($rc != 0) { 
   # print the error
   process_api_error($DELIMITER,$rc,@listout2);
   exit(CRM_CLI_RMC_ERROR); 
}

# sort the cluster node names returned 
@listout = sort @listout2;

# return the cluster node names
return(@listout);
}   #  end of list_cluster_nodes

#--------------------------------------------------------------------#
# xlate_dec2hex_hardway : Returns a hex string from a decimal value  #
#   that is stored in a string.  The builtin "hex" function was not  #
#   used because it doesn't work if the hex value is longer than 8   #
#   places. Also, not all supported perl releases support 64 bit     #
#   integers.  So do it the hard way....                             #
#                                                                    #
# Parameters:                                                        #
#   $decimal_str     The decimal value to convert to hex.            #
#                                                                    #
# Returns:                                                           #
#   $hex_val         The hexidecimal value that represents the       #
#                    decimal value.                                  #
#                                                                    #
#--------------------------------------------------------------------#
sub xlate_dec2hex_hardway
{
use POSIX;
my $decimal_val = shift(@_);            # get the value to convert
my $hex_val = "";                       # the final haex value
my @remainders = ();                    # the list of remainders
my $i = 0;                              # remainder counter
my $j = 0;                              # place in $decimal_val
my $done = $FALSE;                      # is the dividing done?
my $dec_len = 0;                        # length of $decimal_val
my $tmp = "";                           # what is currently being divided
my $tmp2 = "";                          # builds quotient
my $tmp_q = 0;                          # intermediate quotient
my $tmp_r = 0;                          # intermediate remainder

# the idea is to simulate long division
# divide by 16 one place at a time, left to right

# this while loop continues until no more divisions by 16 are possible
while (!$done) {
   $j = 0;

   $dec_len = length $decimal_val;

   # this while loop does the long division, left to right
   while ($j < $dec_len) {

      # get the next digit to the right 
      $tmp .= substr($decimal_val,$j,1);

      $tmp_q = floor ($tmp / 16);
      $tmp_r = $tmp % 16;

      # Use the remainder in the next division unless it couldn't be divided
      # at all, then you need to keep it for the next time around
      if ($tmp_q != 0) {$tmp = $tmp_r;}

      # tmp2 builds the quotient (to use for next time around outer loop)
      $tmp2 .= $tmp_q;
      $j++;
   }

   # save the remainder for the entire division by 16. it's part of the final answer.
   $remainders[$i] = $tmp_r;
   $i++;

   # if there's nothing left to divide, stop
   if ($tmp2 == 0) { $done = $TRUE;}
   # build next value to divide by 16
   else {
      $tmp2 =~ s/^0*//;
      $decimal_val = $tmp2;
      $tmp2 = "";
      $tmp = "";
   }

} # end while not done

# form the hex value from the remainders
for ($i=$#remainders; $i >= 0; $i--) {

    if ($remainders[$i] >= 10) {
        # $remainders[$i] = chr($remainders[$i] + 87);
        $remainders[$i] = chr( ord("a") + ($remainders[$i] - 10) );
    }

    $hex_val .= $remainders[$i];
}

return($hex_val);
}   #  end of xlate_dec2hex_hardway


#--------------------------------------------------------------------#
# lookupQuorum_yesno : Translates the quorum value into "no" or "yes"#
#                                                                    #
# Parameters:                                                        #
#   $trans_value     Value to translate.                             #
#   @trans_array     Values used to translate                        #
#                                                                    #
# Returns:                                                           #
#   $descr           "yes" if quorum, "no" if not.                   #
#                                                                    #
# Global Variables:                                                  #
#--------------------------------------------------------------------#
sub lookupQuorum_yesno
{
my $trans_value = 0;                    # value to translate
my $descr = "?";                        # default - don't know
my $use_quotes = $FALSE;                # boolean flag
my @trans_array =();                    # array with the values

# get inputs
$trans_value = shift @_;
@trans_array = @_;

# figure out if quotes are needed around strings
if ( (defined $ENV{CT_CLI_QUOTE_STRING}) &&
    ($ENV{CT_CLI_QUOTE_STRING} == 1) ) {
    $use_quotes = $TRUE;
}


# translate the value
# 1 or 3 is a yes
if (($trans_value == 1) || ($trans_value == 3)) {
   $descr = $trans_array[1];
   }
else {
   # set to no
   $descr = $trans_array[0];
}

# add quotes if necessary
if ($use_quotes) {
   $descr = "\"".$descr."\"";
}

return($descr);
}   #  end of lookupQuorum_yesno

#--------------------------------------------------------------------#
# lookupTieBreaker_yesno : Translates the tiebreaker value into "no" #
#                          or "yes"                                  #
#                                                                    #
# Parameters:                                                        #
#   $trans_value     Value to translate.                             #
#   @trans_array     Values used to translate                        #
#                                                                    #
# Returns:                                                           #
#   $descr           "yes" if tiebreaker, "no" if not.               #
#                                                                    #
# Global Variables:                                                  #
#--------------------------------------------------------------------#
sub lookupTieBreaker_yesno
{
my $trans_value = 0;                    # value to translate
my $descr = "?";                        # default - don't know
my $use_quotes = $FALSE;                # boolean flag
my @trans_array =();                    # array with the values

# get inputs
$trans_value = shift @_;
@trans_array = @_;

# figure out if quotes are needed around strings
if ( (defined $ENV{CT_CLI_QUOTE_STRING}) &&
    ($ENV{CT_CLI_QUOTE_STRING} == 1) ) {
    $use_quotes = $TRUE;
}


# translate the value
# 1 or 2 is a yes
if (($trans_value == 1) || ($trans_value == 2)) {
   $descr = $trans_array[1];
   }
else {
   # set to no
   $descr = $trans_array[0];
}

# add quotes if necessary
if ($use_quotes) {
   $descr = "\"".$descr."\"";
}

return($descr);
}   #  end of lookupTieBreaker_yesno