#!/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 = "@(#)53   1.26   src/rsct/rm/ConfigRM/cli/bin/preprpnode.perl, configrmcli, rsct_rady, rady2035a 11/12/15 16:40:58"
######################################################################
#                                                                    #
# Module: preprpnode                                                 #
#                                                                    #
# Purpose:                                                           #
#   preprpnode - prepares a node to join an RSCT peer domain.        #
#                                                                    #
# Syntax:                                                            #
#   preprpnode [-h] [-k] [-TV] Node_name [Node_name ...]             #
#                                                                    #
#   preprpnode [-h] [-k] -f File | -F File  [-TV]                    #
#                                                                    #
# Flags:                                                             #
#   -h      Help. Writes the command's usage statement to standard   #
#           output.                                                  #
#   -f File The file containing the nodes names to be added to the   #
#           peer domain. The node names are defined as Node_name     #
#           operands or in a file, not both.  When in a file, each   #
#           line of the file is scanned for one node name.  Comments #
#           may be placed on each line, but after the comment        #
#           character "#". Lines that start (col 1) with "#" or are  #
#           entirely blank are ignored.  Use -f "-" to specify STDIN #
#           as the input file.                                       #
#   -F File Same as -f.                                              #
#   -k      Do not exchange public keys.                             #
#   -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_name   The node to prepare to join the peer domain.         #
#                                                                    #
# Description:                                                       #
#   The preprpnode command performs the proper actions to set up a   #
#   peer domain.                                                     #
#                                                                    #
#                                                                    #
#                                                                    #
# 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 prepare a node to join a peer domain, run:                 #
#      preprpnode abc.def.com                                        #
#                                                                    #
# Man Page:                                                          #
#   For the most current detailed description of this command see    #
#   the preprpnode man page in /opt/rsct/man.                   #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /opt/rsct/msgmaps/configrmcli.preprpnode.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:                                                   #
#   011225 JAC 77313: Initial design & write.                        #
#   020320 JAC 81275: Use lsrsrc-api and rework some acls updates.   #
#   020422 JAC 82248: Rename prepclnode to preprpnode.               #
#   020422 JAC 82249: Change cluster resource class name.            #
#   020428 JAC 82316: Call process_exit_code to check $rc.           #
#   020506 JAC 82449: Add -f flag and change acls processing.        #
#   020621 JAC 83646: Change rmcctrl rc check for only rc.           #
#   021205 JAC 89447: Put both name and IP addr in THL and ACLs.     #
#   030307 JAC 92297: Continue to next node instead of exit on error.#
#   030728 JAC 94796: Redo some ACL file processing due to           #
#                     corruption problems.                           #
#   030908 JAC 96488: Get Hostname from HostPublic class and add     #
#                     to rmc acls.                                   #
#   030929 JAC 99773: Add IPaddr of Hostname to rmcacls.             #
#   040319 JAC 105987: Add -F flag to do the same as -f.             #
#   040406 JAC 106712: Update usage for file options.                #
#   040502 JAC 108124: Add Hostname and it's IP addr to THL.         #
#   040524 JAC 101261: Use error msg with node name when lsrsrc fails#
#   050406 JAC 119510: Add rc when calling process_api_error.        #
#   050805 JAC 127228: Fix find_stanzas for tabs and when to mv file.#
#   050818 JAC 127309: Check print and close return codes.           #
#   050901 JAC 128532: Remove ACL file code and call chrmcacl.       #
#   060712 JAC 136899: Check for null PublicKey value.               #
######################################################################

#--------------------------------------------------------------------#
# General Program Flow/Logic:                                        #
#                                                                    #
# 1. Determine if public keys need to be exchanged (no -k)           #
# 2. Enable remote connections for rmc (rmcctrl -p)                  #
# 3. For each node,                                                  #
#    a. retrieve its public key (lsrsrc on IBM.HostPublic)           #   
#    b. add it to trusted host list (ctsthl cmd)                     #
#    c. add root@node to rmc acls giving it rw permission to         #
#       IBM.SharedResourceCluster                                    #
# 4. 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
                     resolve_node_names_ipaddrs
                     process_api_error
                     process_exit_code
                     remove_api_error
                     get_nodes_nums_from_file
                     get_opstate_by_name);
use CRM_cli_include qw($TRUE $FALSE
                       $RSCLUSTER $RMC_CLI_USER_ERROR
                       $DELIMITER
                       $CTBINDIR $CTDIR);

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

$Opt_NoKeys = $FALSE;                   # default - skip key exchange
$Opt_File_Input = $FALSE;               # default - no file

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

#--------------------------------------------------------------------#
# Variables                                                          #
#--------------------------------------------------------------------#
my @node_names = ();                    # nodes in cluster
my $one_node = "";                      # one from nodelist
my $resolved_node_names = "";           # resolved node names
my $DNS_or_IP_names = "";               # DNS/IP hash reference 
my $unresolved_node_names = "";         # unresolved node names
my $resolved_hostnames = "";            # resolved Hostnames
my $DNS_or_IP_of_hostnames = "";        # DNS/IP hash for Hostnames 
my $unresolved_hostnames = "";          # unresolved Hostnames
my $resolved_node_list = "";            # resolved node list
my @lsr_out = ();                       # output from lsrsrc to get key
my @cmd_out = ();                       # output from a command
my $method = "";                        # trusted key method
my $keyvalue = "";                      # trusted key value
my $file_name = "";                     # input file name
my $node_names_file = "";               # array reference
my $node_nums_file = "";                # array reference
my @dns_or_ip_names = ();               # dns or IP names 
my $one_dns_or_ip_name = "";            # one from dns_or_ip_names
my $pubkey = "";                        # public key from HostPublic
my $hostname = "";                      # hostname from HostPublic
my @Lhostnames = ();                    # list of hostnames
my @Lhostnames_unique = ();             # list of new hostnames
my @THL_names = ();                     # list to put in THL
my $i = 0;
my $j = 0;
my $found = $FALSE;

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;

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

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

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

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

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

   # copy to other array (don't care about node numbers)
   @node_names = @$node_names_file;
}

# enable remote rmc connections
if ($Trace) { print STDERR "$PROGNAME: calling rmcctrl\n";}

@cmd_out = `$CTBINDIR/rmcctrl -p 2>&1`;

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

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

# process any errors from rmcctrl
if ($rc != 0) {
   print STDERR @cmd_out;
   printEMsg("EMsgpreprpnodermcctrlErr",$rc);
   exit(CRM_CLI_RMC_ERROR);
}

# rmcctrl ok but check for output
else {
   if ($#cmd_out >= 0) { 
      print @cmd_out;
   }
}

# resolve the node names 
# (rmc acls format requires it so might as well get it over with)
($resolved_node_names, $DNS_or_IP_names, $unresolved_node_names) = 
       resolve_node_names_ipaddrs(@node_names);

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

# exit if there are no node names left to process
if ($#$resolved_node_names < 0) { exit($config_rc); } 

# exchange public key, if required (which is the default)
if (!$Opt_NoKeys) {

   # set the security to unauthenticated
   $ENV{CT_SEC_MECH} = "none";

   # set the rmc management scope to local
   $ENV{CT_MANAGEMENT_SCOPE} = 1;

   # for each node passed to preprpnode, get its public key
   # and add it to the local node's trusted host list
   foreach $one_node (@$resolved_node_names) {

      # get its public key
      # set CT_CONTACT to access remote node
      $ENV{CT_CONTACT} = $one_node;

      # run lsrsrc to get the public key from the remote node
      if ($Trace) { print STDERR "$PROGNAME: calling lsrsrc-api for $one_node\n";}

      # Use a DELIMITER that isn't in IPv6-looking addresses, otherwise process_api_error()
	  # (called if remove_hostname_not_found() fails) won't output messages correctly
	  # if the hostname just so happens to be an IPv6 address.

      $DELIMITER = "#:#";

      @lsr_out = `$CTBINDIR/lsrsrc-api -D $DELIMITER -o IBM.HostPublic::::::PublicKey::Hostname 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";}

      # remove any Hostname attribute not found error 
      ($rc, @lsr_out) = &remove_hostname_not_found($rc, $DELIMITER, @lsr_out);

      # show any errors if there was a bad rc
      if ($rc != 0)  {
         process_api_error($DELIMITER,$rc,@lsr_out);
         printEMsg("EMsgpreprpnodeKeyUnavail",$one_node);
      }

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

      # if lsrsrc failed for something else, print RMC error message and exit
      if ($rc != 0) {
         #exit(CRM_CLI_RMC_ERROR);
         $config_rc = CRM_CLI_RMC_ERROR;
         next;
      }

      # split the output into the public key and the hostname
      ($pubkey, $hostname) = split /$DELIMITER/, $lsr_out[0];
      chomp($pubkey);
      chomp($hostname);

      # make sure the public key isn't null (not been set)
      if ($pubkey =~ /^\[,\]$/) {
         printEMsg("EMsgpreprpnodeKeyIsBadErr",$one_node);
         $config_rc = CRM_CLI_RMC_ERROR;
         next;
      }

      # clear out list of node to put in THL to rebuild it
      @THL_names = ();

      # if there's a hostname, save it to add to acls
      if ($hostname !~ /^$/ ) {
         $hostname =~ s/\"//g;
         push (@Lhostnames,$hostname);
         push (@THL_names, $hostname);
      }

      # get the IP address of the Hostname from IBM.HostPublic
      ($resolved_hostnames, $DNS_or_IP_of_hostnames, $unresolved_hostnames) =
          resolve_node_names_ipaddrs($hostname);

      # add the IP addrs to the hostname list and THL list
      @dns_or_ip_names = keys %$DNS_or_IP_of_hostnames;
      foreach $one_dns_or_ip_name (@dns_or_ip_names) {
          push (@Lhostnames,$$DNS_or_IP_of_hostnames{$one_dns_or_ip_name});
          push (@THL_names,$$DNS_or_IP_of_hostnames{$one_dns_or_ip_name});
      }

      # add the remote node to THL
      push (@THL_names, $one_node);

      # if it's an IP addr, add the DNS name too, and vice versa
      if (defined $$DNS_or_IP_names{$one_node}) {

         push (@THL_names, $$DNS_or_IP_names{$one_node});

      }

      # update THL with the list of names/IP addrs in @THL_names
      $rc = &add_host_to_thl($pubkey, @THL_names);

      # set config_rc if it's not already set
      if ($config_rc == 0) { $config_rc = $rc; }

   }  #end foreach to exchange key

}  # end if exchange keys required

# Update ACLs

# update the rmc acls file
# use all resolved nodes plus any additional DNS names
@node_names = @$resolved_node_names;
@dns_or_ip_names = keys %$DNS_or_IP_names;

foreach $one_dns_or_ip_name (@dns_or_ip_names) {
   push (@node_names,$$DNS_or_IP_names{$one_dns_or_ip_name});
}

# add any hostnames found from HostPublic class to @node_names
# but first make sure they're not already on the list
for ($i=0; $i<=$#Lhostnames; $i++) {
   $found = $FALSE;
   for ($j=0; (($j<=$#node_names) && !$found); $j++) {
      if ($Lhostnames[$i] eq $node_names[$j]) {
         $found = $TRUE;
      }
   }
   if (!$found) {
      push(@Lhostnames_unique, $Lhostnames[$i]);
   }
}
   
# add the unique hostnames found from HostPublic class
foreach $hostname (@Lhostnames_unique) {
   push (@node_names,$hostname);
}

&update_acls_file (@node_names);

# refresh RMC to read in the new acls
if ($Trace) { print STDERR "$PROGNAME: calling refresh\n";}
@cmd_out = `refresh -s ctrmc 2>&1`;
$rc = $?;
$rc = process_exit_code($rc);

if ($Trace) { print STDERR "$PROGNAME: refresh returned $rc\n";}
# if it failed, print error message and exit
if ($rc != 0) {
   print STDERR @cmd_out;
   printEMsg("EMsgpreprpnodeRefrErr");
   exit(CRM_CLI_RMC_ERROR);
}

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

if ($config_rc == 0) { exit($rc); }
else { exit($config_rc); } 

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


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

# Process the command line...
if (!&getopts('hf:F:kVT', \%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;
}

# Get the arguments...
# Operands:  one or more node names
if ($#ARGV >= 0) {                      # at least one node name
   @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}||defined $opts{F})) {
    printCEMsg("EMsgConfigRMcliInvalidNumberOfOperands");
    &print_usage;
    return CRM_CLI_BAD_OPERAND;
}

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

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

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

if (defined $opts{k}) {                 # -k for no key exchange
    $Opt_NoKeys = $TRUE;                # -k flag specified
}

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

}   # end parse_cmd_line


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


#--------------------------------------------------------------------#
# add_host_to_thl:                                                   #
#     Adds the hosts included in the call to the THL file. The       #
#     inputs are the public key attribute (from IBM.HostPublic) and  #
#     the list of host names or IP addrs to use as the -n host for   #
#     the ctsthl command. ctsthl is run one or more times, once for  #
#     each host name or IP address.                                  #
#                                                                    #
# Paramaters:                                                        #
#   $pubkey         The public key attribute from IBM.HostPublic.    #
#   @thl_hosts      The list of host names/IP adds to add to THL.    #
#                                                                    #
# Returns:                                                           #
#   $configrc       =0 if all adds are successful.                   #
#                   !0 if any errors.                                #
#                                                                    #
#--------------------------------------------------------------------#
sub add_host_to_thl
{
my $pubkey = shift(@_);
my @thl_hosts = @_;

my $method = "";                        # trusted key method
my $keyvalue = "";                      # trusted key value
my $one_host = "";                      # a single host/IP addr
my @cmd_out = ();                       # output from ctsthl
my $rc = 0;                             # initialize rc
my $configrc = 0;                       # initialize rc to return

# parse the public key which is an SD of method and key
($method, $keyvalue) = split /,/, $pubkey;

# do some cleaning up of the punctuation around the values
$method =~ s/\"//g;
$method =~ s/\[//g;
$method =~ s/\]//g;
$method =~ s/://g;
$keyvalue =~ s/\"//g;
$keyvalue =~ s/\[//g;
$keyvalue =~ s/\]//g;
$keyvalue =~ s/://g;

# loop through each host/IP addr and add it to THL
foreach $one_host (@thl_hosts) {

    # add this host/IP addr to local node's trusted host list
    if ($Trace) { print STDERR "$PROGNAME: calling ctsthl for $one_host\n";}

    @cmd_out = `$CTBINDIR/ctsthl -a -n $one_host -m $method -p $keyvalue 2>&1`;

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

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

    # process any errors from ctsthl
    if ($rc != 0) {
       print STDERR @cmd_out;
       printEMsg("EMsgpreprpnodectsthlErr",$one_host,$rc);
       #exit(CRM_CLI_RMC_ERROR);
       $configrc = CRM_CLI_RMC_ERROR;
       next;
    }
}

return ($configrc);
}   # end add_host_to_thl


#--------------------------------------------------------------------#
# update_acls_file - Make updates to the acls file for configrm.     #
#      Use the @node_names array to give access to root at these     #
#      nodes.                                                        #
#                                                                    #
# Input:                                                             #
#   @node_names              The nodes to be used for preprpnode.    #
#                                                                    #
# Returns:                                                           #
#   None.                                                            #
#--------------------------------------------------------------------#
sub update_acls_file
{

my $i = -1;                               # acl file counter
my $node_name = "";
my $linenum;
my @cmd_out = ();
my @acl_stanza_lines = ();
my $acl_stanza_line = "";
my $UPDACL_error = $FALSE;

# get the node list passed in
my @node_names = @_;

# create the ACL stanza lines that preprpnode needs to add
# first for the Peer Domain class
$acl_stanza_lines[++$i] = "$RSCLUSTER    // Peer Domain class - preprpnode\n";
$acl_stanza_lines[++$i] = "    none:root      *   rw   // root on any node of active cluster - preprpnode\n";
$acl_stanza_lines[++$i] = "    none:any_root  *   rw   // root on any node of any cluster that this node is defined to - preprpnode\n";

# add the node names
foreach $node_name (@node_names) {
   $acl_stanza_lines[++$i] = "    root\@$node_name *   rw   // cluster node - preprpnode\n";
}

# second, add the access for the DEFAULT class
$acl_stanza_lines[++$i] = "DEFAULT       // DEFAULT stanza - preprpnode\n";
$acl_stanza_lines[++$i] = "     root\@LOCALHOST   *   rw   // default authority for root - preprpnode\n";
$acl_stanza_lines[++$i] = "     LOCALHOST         *   r    // default authority for other user - preprpnode\n";
$acl_stanza_lines[++$i] = "     none:root        *   rw   // give root on any node access to all - preprpnode\n";
$acl_stanza_lines[++$i] = "     none:clusteruser  *   r    // give non-root on any node read access to all - preprpnode\n";

# call chrmcacl using a pipe to pass the ACL info
if ($Trace) { print STDERR "$PROGNAME: calling chrmcacl\n";}
if ( open(UPDACL,"|/opt/rsct/install/bin/chrmcacl -a ") ) {

   if ( print UPDACL @acl_stanza_lines ) { 

      if (!( close(UPDACL) )) { 

         # close failed 
         $UPDACL_error = $TRUE; 
      }
   }

   # print failed 
   else { $UPDACL_error = $TRUE; }
}

# open failed 
else { $UPDACL_error = $TRUE; }

# check the return code in case of error from chrmacl 
$rc = $?;
$rc = process_exit_code($rc);

if ( ($rc != 0) && $Trace ) {
   print STDERR "$PROGNAME: chrmcacl returned $rc\n";
}

# if anything failed, print error message and exit
if ( ($rc != 0) || $UPDACL_error ) {
   printEMsg("EMsgpreprpnodeChangeAclErr");
   exit(CRM_CLI_RMC_ERROR);
}

}   # end update_acls_file


#--------------------------------------------------------------------#
# remove_hostname_not_found - Scans the input varaible for error     #
#   messages thought to be from the hostname attribute not found     #
#   error.  This error os deleted from the input variable, and if    #
#   there are no other error, the rc variable is changed to 0.       #
#                                                                    #
# Parameters:                                                        #
#   $rc               Return code from lsrsrc-api.                   #
#   $delim            Delimiter for lsrsrc-api output.               #
#   @command_output   The output from the command.                   #
#                                                                    #
# Returns:                                                           #
#   @errorless_output The original @command_output array contents    #
#                     with the hostname not found errors removed.    #
#                                                                    #
# Global Variables:                                                  #
#--------------------------------------------------------------------#
sub remove_hostname_not_found
{
my $rc = shift @_;                      # get the return code
my $delim = shift @_;                   # get the delimiter
my @command_output = @_;                # command output to scan
my @new_output = ();                    # Hostname errors removed
my $line = "";                          # for each output line
my @err_msg = ();                       # split error message
my $num_err = 0;                        # number of errors found
my $num_host_err = 0;                   # number of hostname errors found

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

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

      # put it in the errorless array
      push @new_output, $line;
   }

   # so it's an error message
   else {
      $num_err++;

      # parse the message
      # if there's a rc=5, class is HostPublic, and there's "Hostname" in the 
      # message text, it's probably the Hostname not found error.
      # message format: ERROR::IBM.HostPublic::::::5::::0::lsrsrc-api: 2612-018 Attribute name 
      #                 Hostname is not a valid attribute name.
      @err_msg = split /$delim/, $line;
      if (  ($err_msg[1] =~ /^IBM.HostPublic/ )  &&
            ($err_msg[2] == 5 )  &&
            ($err_msg[5] =~ /Hostname/) ) {

            $num_host_err++;
         }

      else {
         # put it in the new output array
         push @new_output, $line;
      }
   
   }
}

# reset rc if needed
if ( ($num_err > 0)   &&  ($num_err == $num_host_err) ) {
   $rc = 0;
}

return ($rc, @new_output);
}   #  end of remove_hostname_not_found