#!/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 = "@(#)83   1.30   src/rsct/rm/ConfigRM/cli/bin/mkcomg.perl, configrmcli, rsct_rady, rady2035a 11/12/15 16:41:01"
######################################################################
#                                                                    #
# Module: mkcomg                                                     #
#                                                                    #
# Purpose:                                                           #
#   mkcomg - makes a new communication group definition for a cluster#
#                                                                    #
# Syntax:                                                            #
#   mkcomg  [-h] [-s Sensitivity] [-p Period] [-t Priority]          #
#           [-x {br}] [-e NIM_path] [-m NIM_parameters]              #
#	    [-N UseForNodeMembership]				     #
#           [-i n:Network_interface_name:[Node_name][,Network_in-    #
#           terface_name:[Node_name],...] |-S n:"Network_interface_- #
#           selection_string"] [-TV] Communication_group             #
#                                                                    #
# Flags:                                                             #
#   -h      Help. Writes the command's usage statement to standard   #
#           output.                                                  #
#   -s Sensitivity   The sensitivity number. This is the number of   #
#           missed heartbeats that constitute a failure.  The        #
#           sensitivity value is an integer greater than or equal to #
#           2.  The default value is 4.                              #
#   -p Period        The period number. This is the number of seconds# 
#           between heartbeats.   The value of  period is in seconds,#
#           can be an integer or floating point number, and is       #
#           significant to milliseconds.                             #
#   -g grace_period  Specifies the grace period used when heartbeats #
#           are no longer received. When a heartbeat is missed, an   #
#           echo packet is sent to the failed node. If the echo is   #
#           returned, then the grace period is initiated. The value  #
#           of grace_period is in seconds, can be an integer or      #
#           floating point number that is significant to             #
#           milliseconds, or can be one of the following values:     #
#            0          The grace period is disabled.                #
#            -1 or "d"  The default mode where Topology Services     #
#                       controls the grace period.                   #
#   -t Priority      The priority number.  The priority indicates    #
#           the importance of this communication group with respect  #
#           to others.  It is used to order the heartbeat rings. The #
#           lower the number the higher the priority.  The highest   #
#           priority is 1.  The default is 1 for IP networks and     #
#           255 for R232 networks.                                   #
#   -x      Exclude control for heartbeat mechanism.  This indicates #
#           that one or more controls for heartbeat mechanisms       #
#           should not be used even if the underlying media support  #
#           it.                                                      #
#           The following can be excluded:                           #
#           b   This specifies that broadcast should not be used     #
#               even if the underlying media support it.  If -x b    #
#               is not specifed,  broadcast will be used if the      #
#               underlying media support it.                         #
#           r   This specifies that source routing should not be     #
#               used even if the underlying media support it.  If    #
#               -x r is not specified, source routing will be used   #
#               if the underlying media support it.                  #
#           Excluding more than one control is specified by listing  #
#           the feature option letters consecutively (-x br).        #
#   -e NIM_path   NIM path name.  This is a character string that    #
#           specifies the path name to the Network Interface Module  #
#           (NIM) that supports the adapter types in the             #
#           communication group.                                     #
#   -m NIM_parameters   NIM start parameters.  This is a character   #
#           string that is passed to Network Interface Module (NIM)  #
#           when starting it.                                        #
#   -M MediaType  The MediaType. The following integers are valid:   #
#           0 (UserDefined media type)                               #
#           1 (IP media type)                                        #
#           2 (Disk media type)                                      #
#   -i      Assigns this communication group to one or more          #
#           interfaces by resource name.  The -i flag cannot be used #
#           with the -S flag.                                        #
#           n:Network_interface_name:[Node_name]   Assign this       #
#              communication group to the network interface with the #
#              resource name Network_interface_name on node          #
#              Node_name.  More than one can be specified, separated #
#              by commas.  If Node_name is not specified, the local  #
#              node is used.                                         #
#   -S      Assigns this communication group to one or more          #
#           interfaces by selection string.  The -S flag cannot be   #
#           used with the -i flag.                                   #
#           n:"Network_interface_selection_string"  The selection    #
#              string applies to the network interface class.  The   #
#              network interfaces found as a result of the selection #
#              string will use this communication group.             #
#   -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.                                         #
#   -6      use IPv6 interfaces in new CGs.  Default use IPv4.	     #
#   -N	    set UseForNodeMembership value			     #
#                                                                    #
# Operands:                                                          #
#   Communication_group   The name of the new communication group to #
#           be created for the online cluster. The name may contain  #
#           any printable character.                                 #
#                                                                    #
# Description:                                                       #
#   The mkcomg command creates a new communication group definition  #
#   with the name specified by the Communication_group operand for   #
#   an online cluster.  The communication group is used to define    #
#   heartbeat rings for use by HATS and to define the tunables for   #
#   each heartbeat ring.  The communication group determines the     #
#   network interfaces that are used in the cluster.                 #
#                                                                    #
#   The mkcomg command must be run on a node which is currently      #
#   online in the cluster where the communication group is to be     #
#   defined.  There can be more than one communication group in a    #
#   cluster.                                                         #
#                                                                    #
# 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 define the communication group ComGrp1 for the cluster     #
#      ApplCluster and nodeA is defined and online to ApplCluster,   #
#      run the following command on nodeA:                           #
#      mkcomg ComGrp1                                                #
#                                                                    #
#   2. To define the communication group ComGrp1 for the cluster     #
#      ApplCluster, using a sensitivity of 1 and  period of 3, and   #
#      nodeA is defined and online to ApplCluster, run the following #
#      command on nodeA:                                             #
#      mkcomg -s 1 -p 3 ComGrp1                                      #
#                                                                    #
#   3. To define the communication group ComGrp1 for the cluster     #
#      ApplCluster, not using broadcast, and nodeA is defined and    #
#      online to ApplCluster, run the following command on nodeA:    #
#      mkcomg -x b ComGrp1                                           #
#                                                                    #
#   4. To define the communication group ComGrp1 for the cluster     #
#      ApplCluster, not using broadcast, not using source routing,   #
#      and nodeA is defined and online to ApplCluster, run the       #
#      following command on nodeA:                                   #
#      mkcomg -x br ComGrp1                                          #
#                                                                    #
# Man Page:                                                          #
#   For the most current detailed description of this command see    #
#   the mkcomg man page in /opt/rsct/man.                       #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /opt/rsct/msgmaps/configrmcli.mkcomg.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: Update for checking values.                    #
#   011204 JAC 77315: Final version of comg commands.                #
#   011219 JAC 78807: Don't resolve node names or check if in cluster#
#   020204 JAC 80023: Use mkrsrc-api instead of mkrsrc.              #
#   020207 JAC 80121: Make printing of c-api results a trace msg.    #
#   020428 JAC 82316: Call process_exit_code to check $rc.           #
#   020503 JAC 82564: Set peer domain scope before calling mkrsrc-api#
#   020724 JAC 85045: Change -n to -e for command consistency.       #
#   040407 JAC 105863: Use escape_chars for "\" searches.            #
#   071210 JAC 147147: Use PeriodMillSec and add Grace option.       #
#   080404 JAC 150569: Use Period when -p is integer.                #
#   080821 JAC 153024: Fix quoting in select string.                 #
######################################################################

#--------------------------------------------------------------------#
# General Program Flow/Logic:                                        #
#                                                                    #
# 1. Parse command line flags and operands.                          #
# 2. Print usage if -h specified                                     #
# 3. Make sure the communication group name specified doesn't        #
#    already exist.                                                  #
# 4. Make sure the node is online in the cluster.                    #
# 5. Check and translate the sensitivity, period, and exclude        #
#    options for mkrsrc.                                             #
# 6. Call RMC command mkrsrc. Also pass along -VT if necessary.      #
# 7. Return back any errors.                                         #
#                                                                    #
#--------------------------------------------------------------------#

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

use CT_cli_utils qw(printIMsg
                    printEMsg);
use 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_locator_node
                     process_api_error
                     process_exit_code
                     check_for_name
		     get_IPv6Support);
use CRM_cli_include qw($TRUE $FALSE
                       $RMC_CLI_USER_ERROR
                       $RMC_CLI_RSRC_NOT_FOUND
                       $RSCOMG $PEER_DOMAIN_SCOPE
                       $SENSITIVITY_LOW $PERIOD_LOW
                       $PRIORITY_LOW $RSNETI $RSHBI
                       $BROADCAST $SRCROUTING
                       $CTBINDIR $CTDIR
		       $IPVerV4 $IPVerV6);

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

$Opt_Sensitivity = $FALSE;              # default - no Sensitivity 
$Opt_Period = $FALSE;                   # default - no Period
$Opt_Grace  = $FALSE;                   # default - no Grace
$Opt_Priority = $FALSE;                 # default - no Priority
$Opt_Exclude = $FALSE;                  # default - exclude comm ftr
$Opt_NIMpath = $FALSE;                  # default - no NIM path name
$Opt_NIMparms = $FALSE;                 # default - no NIM parameters
$Opt_iSelect = $FALSE;                  # default - no interface sel str
$Opt_Interface = $FALSE;                # default - no interface flag -i
$Use_IPv6 = $FALSE;			# default - no -6 IPv6 select
$Opt_UseForNodeMbrship = $FALSE;	# default - useNodeMbrship not spec'd

$PROGNAME = "mkcomg";                   # 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 $comg_name = "";                     # communication group to create
my $sensitivity = 0;                    # sensitivity for -s
my $period = 0;                         # period for -p
my $priority = 0;                       # priority for -t
my $exclude = "";                       # no broadcast, no source routing
my $NIMpathname = "";                   # NIM path name
my $NIMparms = "";                      # NIM start parameters
my $MediaType = 1;                      # MediaType
my $INames = "";                        # interface names from -i
my $SelString = "";                     # select string from -S
my $i = 0;                              # counter
my $name_exists = $FALSE;               # boolean
my $found = $FALSE;                     # boolean
my $exclude_cnt = 0;                    # count how many to exclude
my $iname = "";                         # an interface name
my $locator = "";                       # an interface node for a name
my $inames = "";                        # interface name/node
my @inames_a = ();                      # interface name/node array
my $resource = "";                      # an interface name/node
my $inter_class = "";                   # interface resource class
my @cmd_out = ();                       # for mkrsrc output

my $passopts = "";                      # TV options to pass to RMC CLI
my $other_opts = "";                    # parameters to pass to RMC CLI
my $delimiter = "#:#";

my $IPVerSel = "";			# IP version select stuff
my $UseForNodeMbrship = -1;		# useForNodeMbrship unspec

#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#
my $rc = 0;

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

# parse the command line, exit if there are errors 
($rc, $sensitivity, $period, $grace, $priority, $exclude, $NIMpathname,
      $NIMparms, $INames, $SelString, $comg_name, $MediaType, $UseForNodeMbrship) = &parse_cmd_line;
($rc == 0) || error_exit($rc);

if ($Verbose) { printIMsg("IMsgmkcomgStart",$comg_name); }

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

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

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

$comg_name = escape_chars($comg_name);
$NIMpathname = escape_chars($NIMpathname);
$NIMparms = escape_chars($NIMparms);

# make sure the communication group name doesn't already exist
$name_exists = check_for_name($comg_name, $RSCOMG);

if ($name_exists) {
   printEMsg("EMsgmkcomgNameExists",$comg_name);
   exit(CRM_CLI_USER_ERROR);
}

# if IPv6 interfaces were selected, check if IPv6 is supported
if ( $Use_IPv6 ) {
   if ( get_IPv6Support() == $FALSE ) {
      printEMsg( "EMsgmkcomgNoIPv6" );
      exit( CRM_CLI_USER_ERROR );
   } else {
      # now build a useful select substring for use below
      $IPVerSel="IPVersion=$IPVerV6";
   }
} else {
   $IPVerSel="IPVersion=$IPVerV4";
}

# pass in Sensitivity if specified
if ($Opt_Sensitivity) {                 # -s used for sensitivity
      # is it a valid value
      if (($sensitivity < $SENSITIVITY_LOW) || 
          ($sensitivity != int($sensitivity))) {
         printEMsg("EMsgmkcomgInvalidSensitivity",$sensitivity);
         exit(CRM_CLI_USER_ERROR);
      }
      $other_opts = $other_opts."${delimiter}Sensitivity${delimiter}$sensitivity";
}

# pass in Period if specified
if ($Opt_Period) {                      # -p used for period
      # is it a numeric value
      if ($period !~ /^[0-9]*\.?[0-9]*$/) {
         printEMsg("EMsgmkcomgInvalidPeriod",$period);
         exit(CRM_CLI_USER_ERROR);
      }

      # if period is an integer, set the Period attribute
      if ( $period == int($period) ) {
         $period = int ($period);
         $other_opts = $other_opts."${delimiter}Period${delimiter}$period";
      }

      else {
         # multiply by 1000 to get milliseconds and round up if not 0
         if ($period != 0) {
            $period = int ($period * 1000 + 0.5);
         }

         $other_opts = $other_opts."${delimiter}PeriodMilliSec${delimiter}$period";
      }
}

# pass in Grace if specified
if ($Opt_Grace) {                      # -g used for period

      # if grace is a "d", then set a -1
      if ($grace =~ /^d$/) {
         $grace = -1;
      }

      # is it a numeric value (or -1)
      if ( ($grace !~ /^-?[0-9]*\.?[0-9]*$/) ||
           ($grace < -1) ) {
         printEMsg("EMsgmkcomgInvalidGrace",$grace);
         exit(CRM_CLI_USER_ERROR);
      }

      # multiply by 1000 to get milliseconds and round up if not 0 or -1
      if ( $grace != 0 && $grace != -1 ) {
         $grace = int ($grace * 1000 + 0.5);
      }

      $other_opts = $other_opts."${delimiter}PingGracePeriodMilliSec${delimiter}$grace";
}

# pass in Priority if specified
if ($Opt_Priority) {                    # -t used for priority
      # is it a valid value
      if (($priority < $PRIORITY_LOW) ||
          ($priority != int($priority))) {
         printEMsg("EMsgmkcomgInvalidPriority",$priority);
         exit(CRM_CLI_USER_ERROR);
      }
      $other_opts = $other_opts."${delimiter}Priority${delimiter}$priority";
}

# pass in No-Broadcast/No-Source Routing if specified
if ($Opt_Exclude) {                     # -x used to exclude
   # count how many to exclude
   $exclude_cnt = 0;
   
   # look for b for (no) broadcast
   if ($exclude =~ /$BROADCAST/) {
      $exclude_cnt++;
      $other_opts = $other_opts."${delimiter}UseBroadcast${delimiter}0";
   }

   # look for r for (no) source routing
   if ($exclude =~ /$SRCROUTING/) {
      $exclude_cnt++;
      $other_opts = $other_opts."${delimiter}UseSourceRouting${delimiter}0";
   }

   # anything else?
   if (($exclude_cnt <= 0) || (length($exclude) > $exclude_cnt)) {
      printEMsg("EMsgmkcomgInvalidExclude",$exclude);
      exit(CRM_CLI_USER_ERROR);
   }
}

# pass in NIM path name if specified
if ($Opt_NIMpath) {                      # -e used for NIM path
      $other_opts = $other_opts."${delimiter}NIMPathName${delimiter}\"$NIMpathname\"";
}

# pass in NIM parameters if specified
if ($Opt_NIMparms) {                     # -m used for NIM parms
      $other_opts = $other_opts."${delimiter}NIMParameters${delimiter}\"$NIMparms\"";
}

# If "-i h:hbi:node,..." was specified, get the CommGroup of each,
# verify they're the same, quit if not, or proceed by creating the
# CommGroup with *that* MediaType.

if ($Opt_Interface && substr($INames,0,2) eq "h:") {
	my $currentInterfaceMediaType = "";

	# get the names/nodes
	my $inames = substr($INames,2);
   
	# make it an array of names/nodes
	my @inames_a = split /,/,$inames;
   
	# add each resource to select string
	foreach $resource (@inames_a) {
		my $result;
		my $interfaceMediaType;

		# get locator, if present
		my ($iname, $locator) = get_locator_node($resource);

		if ("" eq $locator) {
			$result = `$CTBINDIR/lsrsrc-api -i -s ${RSHBI}::"Name == \\\"$iname\\\""::MediaType::HeartbeatActive 2>&1`;
		} else {
			$result = `$CTBINDIR/lsrsrc-api -i -s ${RSHBI}::"(Name == \\\"$iname\\\" && NodeNameList|<{\\\"$locator\\\"})"::MediaType::HeartbeatActive 2>&1`;
		}

		if (! $result) {
			printEMsg("EMsgmkcomgHBIDeviceNotFound");
			exit(CRM_CLI_USER_ERROR);
		}

		chomp $result;

		my ($interfaceMediaType, $heartbeatActive) = split(/::/, $result);

		if ("0" ne $heartbeatActive) {
			printEMsg("EMsgmkcomgHBIDevicesActive");
			exit(CRM_CLI_USER_ERROR);
		}

		if ("" ne $currentInterfaceMediaType) {
			if ($currentInterfaceMediaType ne $interfaceMediaType) {
				printEMsg("EMsgmkcomgDifferentMediaType");
				exit(CRM_CLI_USER_ERROR);
			}
		} else {
			$currentInterfaceMediaType = $interfaceMediaType;
		}
	}

	# At this point we're sure all specified interfaces have the same
	# CommGroup, so set the global MediaType to their MediaType.

	$MediaType = $currentInterfaceMediaType;
}

# MediaType is always included to default value is 1 via mkcomg
$other_opts = $other_opts."${delimiter}MediaType${delimiter}\"$MediaType\"";

if ($Opt_UseForNodeMbrship) {
	$other_opts = $other_opts."${delimiter}UseForNodeMembership${delimiter}\"$UseForNodeMbrship\"";
}

# call mkrsrc to create the communincation group
# (process the interface stuff only if this works)
if ($Trace) { print STDERR "$PROGNAME: calling mkrsrc-api\n";}

#`$CTBINDIR/mkrsrc $passopts $RSCOMG Name="$comg_name" $other_opts`;
@cmd_out=`$CTBINDIR/mkrsrc-api -D"$delimiter" -I"$delimiter" ${RSCOMG}${delimiter}Name${delimiter}${comg_name}${other_opts} 2>&1`;

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

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

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

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

# 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 mkrsrc failed for something else, print RMC error message and exit
if ($rc != 0) {
#   printCEMsg("EMsgConfigRMcliUnExpectRMCrc",$rc);
    exit(CRM_CLI_RMC_ERROR);
    }

# communication group now exists. hook the interface to it

if ($Opt_iSelect || $Opt_Interface) {
   # if -i specified, determine class to use and selection string
   if ($Opt_Interface) {
   
      if (substr($INames,0,2) eq "n:") {
         $inter_class = $RSNETI;     # set class to NetworkInterface
      } elsif (substr($INames,0,2) eq "h:") {
         $inter_class = $RSHBI;      # set class to HeartbeatInterface
      } else {
         # success must be all or nothing, so remove the newly created comm group
         @rmrsrc_cmd_out=`$CTBINDIR/rmrsrc-api -s ${RSCOMG}::"Name==\\\"$comg_name\\\"" 2>&1`;
         printEMsg("EMsgmkcomgInvalidInterfaceFlg",substr($INames,0,1),"i");
         exit(CRM_CLI_USER_ERROR);
      }
   
      # get the names/nodes
      $inames = substr($INames,2);
   
      # make it an array of names/nodes
      @inames_a = split /,/,$inames;
   
      # initialize the select string being built
      $SelString = "";
   
      # add each resource to select string
      foreach $resource (@inames_a) {
   
         # get locator, if present
         ($iname,$locator) = get_locator_node($resource);
   
         # add to select string
         # 1st add OR if not the first one in select string
         if ($SelString ne "") { $SelString .= " || ";}
   
         # add name/node to select string
	 # 160039: allow for intfX:Y suffix, user must specify only
	 # base name
	 my $INamePat="(Name==\\\"$iname\\\" || Name LIKE \\\"${iname}:%\\\")";
         if ($locator eq "") {
	    $SelString .= $INamePat;
	 } else {
	    $SelString .= "($INamePat && NodeNameList|<{\\\"$locator\\\"})";
	 }
      }
   }       # end -i
   
   # if -S specified, determine class to use and selection string
   if ($Opt_iSelect) {
   
      if (substr($SelString,0,2) eq "n:") {
         $inter_class = $RSNETI;     # set class to NetworkInterface
      } elsif (substr($SelString,0,2) eq "h:") {
         $inter_class = $RSHBI;      # set class to HeartbeatInterface
      } else {
         # success must be all or nothing, so remove the newly created comm group
         @rmrsrc_cmd_out=`$CTBINDIR/rmrsrc-api -s ${RSCOMG}::"Name==\\\"$comg_name\\\"" 2>&1`;
         printEMsg("EMsgmkcomgInvalidInterface",substr($SelString,0,1));
         exit(CRM_CLI_USER_ERROR);
      }
   
      # set the actual selection string
      $SelString = substr($SelString,2);
   
      # escape some things in the select string
      #$SelString =~ s/\\/\\\\/g;
      #$SelString =~ s/\"/\\\"/g;
      $SelString = escape_chars($SelString);
      
      # now massage it so that we can accomodate Sun IPMP
      my ( $ante, $namePat, $post ) = split /Name=[\'\"](.*)[\'\"]/, $SelString;
      if ( defined $namePat ) {
	 $SelString = $ante."(Name == '$namePat' || Name like '$namePat:%')".$post;
      }
   }       # end -S
   
   # select IP version if changing network interfaces
   if ( $inter_class eq $RSNETI ) {
      $SelString = "$IPVerSel and ( $SelString )";
   }
   
   # call chrsrc to modify communication group attribute in interface resource
   if ($Trace) { print STDERR "$PROGNAME: calling chrsrc-api\n";}
   
   @cmd_out=`$CTBINDIR/chrsrc-api -D"$delimiter" -I"$delimiter" -s ${inter_class}${delimiter}\"${SelString}\"${delimiter}CommGroup${delimiter}$comg_name 2>&1`;
   
   # capture the return code from chrsrc-api
   $rc = $?;
   $rc = process_exit_code($rc);
   
   if ($Trace) { print STDERR "chrsrc-api results:\n";
                 print STDERR "@cmd_out";}
   
   if ($Trace) { print STDERR "$PROGNAME: chrsrc-api returned $rc\n";}
   
   if ($rc) {
      # success must be all or nothing, so remove the newly created comm group
      @rmrsrc_cmd_out=`$CTBINDIR/rmrsrc-api -s ${RSCOMG}::"Name==\\\"$comg_name\\\"" 2>&1`;
   }
   
   # show any errors if there was a bad rc
   if (($rc != 0) && ($rc != $RMC_CLI_RSRC_NOT_FOUND)) {
      process_api_error($delimiter,$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);}
   
   # return ConfigRM CLI user error if it's an RMC CLI not found
   if ($rc == $RMC_CLI_RSRC_NOT_FOUND) { 
       printEMsg("EMsgmkcomgNoInterfaceFound");
       exit(CRM_CLI_USER_ERROR)
   }
   
   # if mkrsrc failed for something else, print RMC error message and exit
   if ($rc != 0) {
   #      printCEMsg("EMsgConfigRMcliUnExpectRMCrc",$rc);
       exit(CRM_CLI_RMC_ERROR);
       }
}

if ($Verbose) { printIMsg("IMsgmkcomgEnd",$comg_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.      #
#   $comg_name               Name of communication group to create   #
#   $sensitivity             Heartbeat sensitivity                   #
#   $period                  Heartbeat period                        #
#   $priority                Heartbeat priority                      #
#   $exclude                 Exclude communucation feature           #
#   $NIMpathname             NIM path name to start NIM              #
#   $NIMparms                NIM parms to start NIM                  #
#   $INames                  Resource interface names                #
#   $SelString               Resource interface selection string     #
#                                                                    #
# Global Variables Modified:                                         #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#   $Trace             output   True (-T) turn Trace mode on.        #
#   $Opt_Sensitivity   output   True (-s) Sensitivity specified      #
#   $Opt_Period        output   True (-p) Period specified           #
#   $Opt_Grace         output   True (-g) Grace specified            #
#   $Opt_Priority      output   True (-t) Priority specified         #
#   $Opt_Exclude       output   True (-x) Exclude communication ftr  #
#   $Opt_NIMpath       output   True (-e) NIM path name specified    #
#   $Opt_NIMparms      output   True (-m) NIM parameters specified   #
#   $Opt_Interface     output   True (-i) interface name specified   #
#   $Opt_iSelect       output   True (-S) interface select string    #
#--------------------------------------------------------------------#
sub parse_cmd_line 
{
my(@original_argv) = @ARGV;
my $comg_name = "";                     # communication group name
my $sensitivity = 0;                    # heartbeat sensitivity
my $period = 0;                         # heartbeat period
my $grace = 0;                          # period grace
my $priority = 0;                       # heartbeat priority 
my $exclude = "";                       # heartbeat controls
my $NIMpathname = "";                   # NIM path name
my $NIMparms = "";                      # NIM start parameters
my $INames = "";                        # -i names
my $SelString = "";                     # -S selection string
my $UseForNodeMbrship = 1;		# -N UseForNodeMembership
my %opts = ();

# Process the command line...
if (!&getopts('hs:p:x:t:e:m:M:S:i:g:VT6N:', \%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:  communication group
if ($#ARGV == 0) {                      # communication group name
   $comg_name = shift @ARGV;            # get the name
}

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

if (defined $opts{s}) {                 # -s for sensitivity
    $sensitivity = $opts{s};         
    $Opt_Sensitivity = $TRUE;           # -s flag specified
}

if (defined $opts{p}) {                 # -p for period
    $period = $opts{p}; 
    $Opt_Period = $TRUE;                # -p flag specified
}

if (defined $opts{g}) {                 # -g for period
    $grace = $opts{g}; 
    $Opt_Grace = $TRUE;                 # -g flag specified
}

if (defined $opts{t}) {                 # -t for priority
    $priority = $opts{t}; 
    $Opt_Priority = $TRUE;              # -t flag specified
}

if (defined $opts{x}) {                 # -x for exclude com ftr
    $exclude = $opts{x}; 
    $Opt_Exclude = $TRUE;               # -x flag specified
} 

if (defined $opts{e}) {                 # -e for NIM path name
    $NIMpathname = $opts{e};
    $Opt_NIMpath = $TRUE;               # -e flag specified
}

if (defined $opts{m}) {                 # -m for NIM parameters
    $NIMparms = $opts{m};
    $Opt_NIMparms = $TRUE;              # -m flag specified
}

if (defined $opts{M}) {                 # -M for MediaType
    $MediaType = $opts{M};
}

if (defined $opts{i}) {                 # -i for interface name
    $INames = $opts{i};
    $Opt_Interface = $TRUE;             # -i flag specified
}

if (defined $opts{S}) {                 # -S for interface selection
    # error if -i specified 
    if ($Opt_Interface) {
       printCEMsg("EMsgConfigRMcliImproperUsageCombination","-i","-S");
       &print_usage;
       return CRM_CLI_BAD_FLAG;
    }
    $SelString = $opts{S};
    $Opt_iSelect = $TRUE;               # -S flag specified
}

if ( defined $opts{6} ) {		# -6 flag selects IPv6 interfaces
    $Use_IPv6 = $TRUE;
}

if ( defined $opts{N} ) {		# -N UseForNodeMembership valie
    $UseForNodeMbrship = $opts{N};
    $Opt_UseForNodeMbrship = $TRUE;
}

return(0, $sensitivity, $period, $grace, $priority, $exclude, $NIMpathname,
          $NIMparms, $INames, $SelString, $comg_name, $MediaType, $UseForNodeMbrship);  # success

}   # end parse_cmd_line


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