#!/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 = "@(#)82 1.67 src/rsct/rm/ConfigRM/cli/bin/mkrpdomain.perl, configrmcli, rsct_rady, rady2035a 11/12/15 16:40:52" ###################################################################### # # # Module: mkrpdomain # # # # Purpose: # # mkrpdomain - makes a new RSCT peer domain definition. # # # # Syntax: # # mkrpdomain [-h] [-t HATS_port] [-g HAGS_port] [-r RMC_port] # # [-k Key_type] [-r Interval_expr] [-u Admin_id] [-c] # # [-Q QuorumTypeName|QuorumTypeValue] # # [-G QuorumGroupName] [-m Fanout] [-6] # # [-p 0 | -p 1] # # [-S "none" | -S "nist_sp800_131a" ] [-TV] PeerDomain # # Node_name[@Host_name] [Node_name[@Host_name] ...] # # # # mkrpdomain [-h] [-t HATS_port] [-g HAGS_port] [-r RMC_port] # # [-k Key_type] [-r Interval_expr] [-u Admin_id] [-c] # # [-Q QuorumTypeName|QuorumTypeValue] # # [-G QuorumGroupName] [-m Fanout] [-6] [-p 0 | -p 1] # # [-S "none" | -S "nist_sp800_131a" ] # # -f File|-F File [-TV] PeerDomain # # # # mkrpdomain [-h] -C 0|1 -R RepositoryDisk[hdiskN,...] # # [-D sharedDisk[hdiskN,...] [-TV] # # [-p 0 | -p 1] [-S 0 | -S 1] PeerDomain # # Node_name[@Host_name] [Node_name[@Host_name] ...] # # # # Flags: # # -h Help. Writes the command's usage statement to standard # # output. # # -t HATS_port The HATS Port number. This is the UDP port that # # will be used for daemon-to-daemon communication. Any # # unused port in the range 1024 - 65535 may be assigned. # # The command will fail if the specified port is # # unavailable. The default is 12347. # # -g HAGS_port The HAGS Port number. This is the UDP port that # # will be used for daemon-to-daemon communication. Any # # unused port in the range 1024 - 65535 may be assigned. # # The command will fail if the specified port is # # unavailable. The default is 12348. # # -c Continue on error. Do not stop when an error occurs but # # continue processing the mkrpdomain command as long as at # # least one node can be included in the peer domain. # # -C Specify the domain type: # # 0 Traditional peer domain # # 1 Cluster Aware AIX (CAA) # # -D sharedDisk The CAA storage device shared between cluster # # nodes. # # -f File The file containing the nodes names to be defined 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 Key_type The peer domain shared secret key. # # The default is none (or CSSKTYPE_None). # # -r Interval_expr The cluster shared secret key refreh interval. # # The interval expression is in the for dd:hh:mm:ss. # # The leftmost of the expression must be specified. For # # example, -r 20 means 20 days. -r 0 means never refresh. # # The default is 1 day. # # -u Admin_id The administrator user ID. This user ID will be # # granted read/write permission to all resource classes # # on all nodes in the peer domain. # # -Q QuorumTypeName|QuorumTypeValue The Quorum Type to use. The # # quorum type can be specified as a name or a value. A # # valid quorum name can be found from the # # AvailableQuorumTypes class attribute on IBM.PeerDomain. # # -G QuorumGroupName The Quorum group name. Specifies the group # # name for the quorum. # # -m Fanout max number of threads to use in parallel operations # # for this RPD, stored as persistent attr in its # # IBM.PeerNode class. If unspecified, hardcoded default # # in ConfigRM is used. # # -6 allows IPv6 addresses to participate in peer domain # # internal operations and heartbeating even though the # # nodes in the peer domain use IPv4 addresses. If IPv6 # # addresses are configured and this flag is not specified, # # the IPv6 addresses will not be permitted to participate # # in peer domain internal operations or heartbeating. # # -R RepositoryDisk The CAA storage repository # # -p NamePolicy The NamePolicy determines the synchronization # # across nodename & hostname PeerNode attributes, this is # # optional. # # 0 ManualNameUpdate, This is the default in a # # traditional peer domain. # # 1 AutoHostNameSync, This is the default in the # # CAA-environment. # # -S SecurityMode Specifies Nist Compliance Mode of a PeerDomain # # read-only attribute of IBM.PeerDomain, this is # # optional. # # 0 Legacy Mode, this is the default value # # 1 NIST Compliance mode, i.e NIST_SP800_131A # # -T Trace. Writes the command's trace messages to standard # # error. For your software-service organization's use only.# # -V Verbose. Writes the command's verbose messages to # # standard output. # # # # Operands: # # PeerDomain The name of the new RSCT peer domain to be created. # # The name can be made up of the characters A-Z, a-z, # # 0-9, . (period), _ (underscore), and - (hyphen). # # The name cannot begin with a hyphen. The name must # # not be IW, . (period), or .. (two periods). # # Node_name[@Host_name] The node to include in this peer domain # # definition. The node name (the Host_name, if there # # is any input) is the long or short version of the # # DNS hostname. It must be resolvable to an IP # # address. At least one node name must be specified. # # The node names can be specifed as operands or in a # # file using the -f flag, but not both. # # The "@Host_name" input is applicable for non-CAA # # domain not applicable for CAA. # # Node_name will be populated in nodename attribute. # # Host_name will be populated in hostname attribute, # # if any, otherwise populated initially with # # Node_name. # # # # Description: # # The mkrpdomain command creates a new peer domain definition with # # the name specified by the PeerDomain operand. The nodes # # specified by Node_name, or node names defined in a file using # # the -f flag are defined to the new peer domain. A peer domain # # can be used to provide highly available services when # # configuring application and system resources. # # # # The mkrpdomain command does not bring the peer domain online # # automatically. To bring the ipeer domainr online, run the # # startrpdomain command. Nodes can be added to and removed from # # the peer domain cluster by using the addrpnode and rmrpnode # # commands. # # # # A node can be defined in more than one peer domain but it can be # # online in only one peer domain at a time. # # # # The conditions which must be met for a node to join a peer # # domain are: # # o rsct.basic must be installed # # o must be able to establish trust between nodes # # o must be in the same kerberos realm # # o must be able to exchange public key # # # # Only those nodes that meet the above requirements will be # # successfully defined to the peer domain. # # # # If the UDP port numbers for HATS, HAGS, and RMC are not available# # on all of the nodes to be defined to the peer domain, the # # mkrpdomain command will fail. Otherwise, the peer domain # # definition is created and the nodes are added to the peer domain # # if they meet the above conditions. The command will also fail # # if the cluster name is already being used. # # # # 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 a peer domain consisting of nodeA with the peer # # domain name ApplDomain, run the following command on nodeA: # # mkrpdomain ApplDomain # # # # 2. To define a peer domain consisting of nodeA, nodeB, and nodeC,# # with the peer domain name ApplDomain, run the following # # command on nodeA, nodeB, or NodeC: # # mkrpdomain ApplDomain nodeA nodeB nodeC # # # # 3. To define a peer domain (non-CAA) consisting of nodeA, nodeB, # # and nodeC,with hostnames hostA, hostB, and hostC respectively,# # with the peer domain name ApplDomain, run the following # # command on nodeA, nodeB, or NodeC: # # mkrpdomain ApplDomain nodeA[@hostA] nodeB[@hostB] # # nodeC[@hostC] # # # # 4. To define a peer domain consisting of nodeA and nodeB, with # # peer domain name ApplDomain, HATS Port number 1200, HAGS # # Port number 2400, and RMC Port number 3600, run the following # # command on nodeA or nodeB: # # mkrpdomain -t 1200 -g 2400 -r 3600 ApplDomain nodeA nodeB # # # # Man Page: # # For the most current detailed description of this command see # # the mkrpdomain man page in /opt/rsct/man. # # # #--------------------------------------------------------------------# # Inputs: # # /opt/rsct/msgmaps/configrmcli.mkrpdomain.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: Implement node operand, check name for spaces. # # 011218 JAC 78807: remove port checking and don't resolve names. # # 020107 JAC 77329: security feature, add -k, -r, and -u. # # 020131 JAC 79963: switch to use mkrsrc-api instead of mkrsrc. # # 020205 JAC 80060: require at least one node name for operand. # # 020207 JAC 80121: Make printing of c-api results a trace msg. # # 020327 JAC 81370: Add -f flag to read node names from a file. # # 020415 JAC 82055: Add -c for continue-on-error. # # 020417 JAC 81859: Add node numbers to file support. # # 020419 JAC 82248: Rename mkcluster to mkrpdomain. # # 020428 JAC 82316: Call process_exit_code to check $rc. # # 020502 JAC 82564: Set local scope before calling mkrsrc-api. # # 030324 JAC 93122: Don't specify default command arguments. # # 040319 JAC 105987: Add -F flag to do the same as -f. # # 040329 JAC 102042: Add -Q and -G quorum flags. # # 040407 JAC 105863: Use escape_chars for "\" searches. # # 040427 JAC 107926: Restrict the name to A-Z,a-z,0-9,.,_.. # # 041118 JMS 104741: make ConfigRM RMC thread fanout configurable # # 050124 JMS 116382: -m instead of -n for fanout # # 050406 JAC 119510: Add rc when calling process_api_error. # # 051025 JAC 125712: Use mkrsrc-api -f option when cmd too long. # # 061220 JAC 140962: Some changes for implementing -k and -r. # # 070222 JAC 141884: pass default refresh interval differently. # # 071105 JAC 146610: add tiebreaker support, -B in file. # # 071221 JAC 147708: allow - (hyphen) in name and check for . and..# # 080201 JAC 149153: correct the pattern match for prev defect. # # 080519 JAC 151136: add -6 flag for IPv6. # ###################################################################### #--------------------------------------------------------------------# # General Program Flow/Logic: # # # # 1. Parse command line flags and operands. # # 2. Print usage if -h specified # # 3. Make sure the cluster name specified is not IW, has no spaces, # # and doesn't already exist. # # 4. Verify the ports arguments. # # 5. Resolve any node names specified. # # 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 calc_cmdarg_length); 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 resolve_node_names process_api_error process_exit_code check_quorum_type form_quorum_attribute get_nodes_nums_from_file get_opstate_by_name); use CRM_cli_include qw($TRUE $FALSE $RSCLUSTER $RMC_CLI_USER_ERROR $LOCAL_SCOPE $CTBINDIR $CTDIR); #--------------------------------------------------------------------# # Global Variables # #--------------------------------------------------------------------# $Trace = $FALSE; # default - trace off $Verbose = $FALSE; # default - verbose turned off $ClusterType_PeerDomain = 0; # Traditional PeerDomain $ClusterType_CAA = 1; # ClusterType: CAA $ClusterType_CAAInt = 1; # Processing go through ConfigRM $ClusterType_CAAExt = 2; # Processing in mkrpdomain $Opt_Hatsport = $FALSE; # default - no HATS port $Opt_Hagsport = $FALSE; # default - no HAGS port $Opt_CSSKType = $FALSE; # default - no CSSK type $Opt_Continue = $FALSE; # default - no continue on error $Opt_CSSKRefresh = $FALSE; # default - no CSSK refresh $Opt_AdminUID = $FALSE; # default - no admin user id $Opt_File_Input = $FALSE; # default - no file $Opt_QuorumType = $FALSE; # default - no quorum type $Opt_GroupQ = $FALSE; # default - no group quorum name $Opt_Fanout = $FALSE; # default - no fanout specd $Opt_ShowIPv6 = $FALSE; # default - no IPv6 usage $Opt_ClusterType = $ClusterType_PeerDomain; # default - no cluster aware AIX $Opt_CAADisk = $FALSE; # default - no CAA share disk $Opt_CAARepos = $FALSE; # default - no CAA repository disk $Opt_DomainType_Flag = FALSE; # default - domain flag is not specified $Opt_Namepolicy = $FALSE; # default - as per cluster type # 0 ManualNameUpdate, This is the default in a traditional peer domain. # 1 AutoHostNameSync, This is the default in the CAA-environment. $Opt_SecurityMode = $FALSE; # $PROGNAME = "mkrpdomain"; # Program Name for messages $LSMSG = "$CTBINDIR/ctdspmsg"; # list / display message rtn $MSGCAT = "configrmcli.cat"; # msg catalogue for this cmd $ENV{'MSGMAPPATH'} = "$CTDIR/msgmaps"; # msg maps used by $LSMSG #--------------------------------------------------------------------# # Variables # #--------------------------------------------------------------------# my $cluster_name = ""; # cluster to create my $hats_port = 0; # HATS port my $hags_port = 0; # HAGS port my $cssk_type = ""; # CSSK type my $refresh_interval = ""; # CSSK refresh interval (char) my $admin_uid = ""; # administrator user id my @node_names = (); # nodes in cluster my @node_numbers = (); # nodes in cluster my @node_quorum_flags = (); # Quorum array my @node_gsgl_flags = (); # IsPreferredGSGLArray my @node_tiebreaker_flags = (); # Tiebreaker array my @clusters = (); # nodes in the hash my $resolved_node_names = ""; # resolved node names my $unresolved_node_names = ""; # unresolved node names my $resolved_node_list = ""; # resolved node list my $one_node = ""; # one from nodelist my $comma = ""; # used to build a list my %cluster_opstates = (); # shows cluster name and opstate my $translated_interval = 0; # translated refresh interval my $d = 0; # refresh days my $h = 0; # refresh hours my $m = 0; # refresh minutes my $s = 0; # refresh seconds my @cmd_out = (); # for mkrsrc output my $interval_invalid = $FALSE; # check for bad refresh interval my $file_name = ""; # file name for node names my $file_error = ""; # error with file my $node_names_file = ""; # reference to node names my $node_nums_file = ""; # reference to node numbers my $node_quorum_flags_file = ""; # reference to quorun array my $node_gsgl_flags_file = ""; # reference to IsPreferredGSGLArray my $node_tiebreaker_flags_file = ""; # reference to tiebreaker array my $node_quorum_arg = ""; # reference to quorum arg array my $node_num_list = ""; # for rmc command my $one_num = ""; # one from node num list my $q_type = ""; # quorum type my $q_group = ""; # quorum group name my $fanout = 0; # thread fanout my $parmlen = 0; # length of parms to pass to mkrsrc-api my $caa_flag = ""; # Domain type flag for CAA my $caa_disk = ""; # CAA shared disk my $caa_repos = ""; # CAA repository disk my $name_policy = 0; # Name policy my $name_policy_undef = -1; # Name policy undefined value my $node_names_list = ""; # Node name list my $host_names_list = ""; # Host name list my @nodenames_list = (); # Node names list my @nodenames = (); my @hostnames = (); my $tmp_node_name = ""; my $tmp_host_name = ""; my $last_host_name = ""; my $security_mode = "none"; # SecurityMode[Nist Compliance mode] my $passopts = ""; # TV options to pass to RMC CLI my $other_opts = ""; # parameters to pass to RMC CLI my $delimiter = "#:#"; #--------------------------------------------------------------------# # Main Code # #--------------------------------------------------------------------# my $rc = 0; my $config_rc = 0; # set local scope $ENV{CT_MANAGEMENT_SCOPE} = $LOCAL_SCOPE; # parse the command line, exit if there are errors ($rc, $hats_port, $hags_port, $cssk_type, $refresh_interval, $admin_uid, $file_name, $cluster_name, $q_type, $q_group, $caa_disk, $caa_repos, $name_policy,$security_mode, @node_names) = &parse_cmd_line; ($rc == 0) || error_exit($rc); if ($Verbose) { printIMsg("IMsgmkrpdomainStart",$cluster_name); } if ($Trace) { $passopts = $passopts." -T"; } if ($Verbose) { $passopts = $passopts." -V"; } # get the command line buffer length $CMD_LIMIT = calc_cmdarg_length; # Do some escaping on strings. Do the escape character first # for anything that's a string, escape any inner \ #$cluster_name =~ s/\\/\\\\/g; # for anything that's a string, escape any inner double quotes #$cluster_name =~ s/\"/\\\"/g; $cluster_name = escape_chars($cluster_name); # make sure cluster name is not IW if ($cluster_name eq "IW") { printEMsg("EMsgmkrpdomainNameIW"); exit (CRM_CLI_USER_ERROR); } # make sure cluster name is not . or .. if ( ($cluster_name eq ".") || ($cluster_name eq "..") ) { printEMsg("EMsgmkrpdomainNameDots"); exit (CRM_CLI_USER_ERROR); } # make sure cluster name does not contain spaces if ($cluster_name =~ / /) { printEMsg("EMsgmkrpdomainNameSpaces",$cluster_name); exit (CRM_CLI_USER_ERROR); } # make sure cluster name only contains A-Z, a-z, 0-9, . (period), _ (underscore), - (hyphen) # hyphen not allowed 1st but it looks like a flag on input so test really never happens if ($cluster_name !~ /^[A-Za-z0-9._]+[A-Za-z0-9._-]*$/) { printEMsg("EMsgmkrpdomainNameIsBad"); exit (CRM_CLI_USER_ERROR); } # get the node names from a file, if specified if ($Opt_File_Input) { # extract the node names from the file ($node_names_file, $node_nums_file, $node_quorum_flags_file, $node_gsgl_flags_file, $node_tiebreaker_flags_file) = get_nodes_nums_from_file($file_name); # copy to other array @node_names = @$node_names_file; @node_numbers = @$node_nums_file; @node_quorum_flags = @$node_quorum_flags_file; @node_tiebreaker_flags = @$node_tiebreaker_flags_file; @node_gsgl_flags = @$node_gsgl_flags_file; } # make sure cluster name doesn't already exist #HOW? or let RM do it (which it does, just means different rc) # make sure this node is not already in this cluster # get the cluster opstate info %cluster_opstates = get_opstate_by_name($RSCLUSTER); # get a list of the cluster names for this node @clusters = keys %cluster_opstates; # make sure the cluster name doesn't exist foreach $one_cluster (@clusters) { if ($one_cluster eq $cluster_name) { printEMsg("EMsgmkrpdomainClusterExists",$cluster_name); exit(CRM_CLI_USER_ERROR); } } # pass in HATS port if specified if ($Opt_Hatsport) { # -t used for hats port $other_opts = $other_opts."${delimiter}TSPort${delimiter}$hats_port"; } # pass in HAGS port if specified if ($Opt_Hagsport) { # -g used for hags port $other_opts = $other_opts."${delimiter}GSPort${delimiter}$hags_port"; } # pass in Admin user ID if specified if ($Opt_AdminUID) { # -u used for admin ID $other_opts = $other_opts."${delimiter}AdminID${delimiter}$admin_uid"; } # pass in quorum type, if specified if ($Opt_QuorumType) { # -Q used for quorum type $other_opts = $other_opts."${delimiter}QuorumType${delimiter}$q_type"; } # pass in the domain type, if specified if ($Opt_DomainType_Flag == $TRUE) { if ($Opt_ClusterType== $ClusterType_PeerDomain) { #Domain Type is traditional RPD $other_opts = $other_opts."${delimiter}DomainType${delimiter}${ClusterType_PeerDomain}"; } elsif($Opt_ClusterType == $ClusterType_CAAInt || $Opt_ClusterType == $ClusterType_CAAExt ) { #Domain Type is CAA $other_opts = $other_opts."${delimiter}DomainType${delimiter}${ClusterType_CAA}"; } } # the setting of attributes after this point is for command args # 144366 , move this block of code down to after Fanout # pass in quorum group, if specified #if ($Opt_GroupQ) { # -G used for quorum group name # $other_opts = $other_opts."${delimiter}QuorumGroupName${delimiter}$q_group"; #} # Hostname change support. # User has the flexibility to provide node name and corresponding hostname, like nodename[@hostname] # Hostname would be considered for resolving nodename otherwise node name will be used. $comma = ""; foreach $one_node (@node_names) { $tmp_node_name = ""; $tmp_host_name = ""; ($tmp_node_name, $tmp_host_name) = split /@/, $one_node; push (@nodenames, $tmp_node_name); $last_host_name = $tmp_host_name; if ($tmp_host_name eq "") { # Consider nodename as there is no Hostname push (@nodenames_list, $tmp_node_name); $host_names_list = $host_names_list.$comma.""; } else { # Consider hostname as there is input for corresponding nodename push (@nodenames_list, $tmp_host_name); push (@hostnames, $tmp_host_name); $host_names_list = $host_names_list.$comma.$tmp_host_name; } $comma = ","; } # resolve node names and hostnames that are to be added to the cluster # resolve any node names and hostnames to be in the cluster ($resolved_node_names, $unresolved_node_names) = resolve_node_names(@nodenames_list); # write an error message for each unresolved node name found # continue processes but set rc foreach $one_node (@$unresolved_node_names) { printEMsg("EMsgmkrpdomainUnresolvedNode",$one_node); $config_rc = CRM_CLI_USER_ERROR; } # exit if there were unresolved names if ($config_rc != 0) { exit ($config_rc); } # make a list of the node names to pass to rmc foreach $one_node (@nodenames, @hostnames) { if ((! $Opt_ShowIPv6) && ($one_node =~ /:/)) { printEMsg("EMsgmkrpdomainIPv6AddressNotPermitted",$one_node); exit CRM_CLI_USER_ERROR; } } $comma = ""; foreach $one_node (@nodenames) { $node_names_list = $node_names_list.$comma.$one_node; $comma = ","; } # make a list of the node numbers to pass to rmc $comma = ""; foreach $one_num (@node_numbers) { $node_num_list = $node_num_list.$comma.$one_num; $comma = ","; } # setup the nodelist $other_opts = $other_opts."${delimiter}${delimiter}NodeNames${delimiter}"."{$node_names_list}"; # setup node number list $other_opts = $other_opts."${delimiter}NodeNumbers${delimiter}"."{$node_num_list}"; # setup the IsQuorumNodeArray list #if ( $#node_quorum_flags >= 0 ) { # $other_opts = $other_opts . "${delimiter}IsQuorumNodeArray${delimiter}" . '{' . join( ',', @node_quorum_flags ) . '}'; #} # setup the IsQuorumNodeArray cmd arg from quorum and tiebreaker $node_quorum_arg = form_quorum_attribute(\@node_quorum_flags, \@node_tiebreaker_flags); if ( $#$node_quorum_arg >= 0 ) { $other_opts = $other_opts . "${delimiter}IsQuorumNodeArray${delimiter}" . '{' . join( ',', @$node_quorum_arg ) . '}'; } # setup the IsPreferredGSGLArray list if ( $#node_gsgl_flags >= 0 ) { $other_opts = $other_opts . "${delimiter}IsPreferredGSGLArray${delimiter}" . '{' . join( ',', @node_gsgl_flags ) . '}'; } # continue on error specified? if ($Opt_Continue) { $other_opts = $other_opts."${delimiter}ContinueIfError${delimiter}1"; } #93122 - comment out specifying the default command argument #else { # $other_opts = $other_opts."${delimiter}ContinueIfError${delimiter}0"; #} # pass in fanout, if specified if ($Opt_Fanout) { # -n used for thread fanout $other_opts = $other_opts."${delimiter}Fanout${delimiter}$fanout"; } #144366 # pass in quorum group, if specified if ($Opt_GroupQ) { # -G used for quorum group name $other_opts = $other_opts."${delimiter}QuorumGroupName${delimiter}$q_group"; } # pass in CSSK type if specified if ($Opt_CSSKType) { # -k used for key type $other_opts = $other_opts."${delimiter}CSSKType${delimiter}$cssk_type"; } # translate and pass in CSSK refresh interval if specified if ($Opt_CSSKRefresh) { # -r used for refresh # (-r requires -k) # parse out the refresh values days:hours:minutes:seconds ($d, $h, $m, $s) = split /:/, $refresh_interval; # if any are not used, set to 0 if (!defined $d ) { $d = 0;} if ($h eq "") { $h = 0;} if ($m eq "") { $m = 0;} if ($s eq "") { $s = 0;} # make sure they are digit values if ($d =~ /\D+/) { $interval_invalid = $TRUE;} if ($h =~ /\D+/) { $interval_invalid = $TRUE;} if ($m =~ /\D+/) { $interval_invalid = $TRUE;} if ($s =~ /\D+/) { $interval_invalid = $TRUE;} # exit if the interval is bad if ($interval_invalid) { printEMsg("EMsgmkrpdomainBadInterval"); exit (CRM_CLI_USER_ERROR); } else { # calculate the total number of seconds $translated_interval = ($d * 24 * 60 * 60) + ($h * 60 * 60) + ($m * 60) + $s; # if seconds are 0, set to -1 for never refresh # defect 141884 comments out the next line #if ($translated_interval == 0) { $translated_interval = -1;} # pass the interval attribute value $other_opts = $other_opts."${delimiter}CSSKRefreshInterval${delimiter}$translated_interval"; } } # if -k was specified but not -r, tell ConfigRM to use default by passing -1 # (141884) if ($Opt_CSSKType && !$Opt_CSSKRefresh) { $translated_interval = -1; $other_opts = $other_opts."${delimiter}CSSKRefreshInterval${delimiter}$translated_interval"; } # pass in to show IPv6 addresses, if specified 151136 if ($Opt_ShowIPv6) { # -6 used to allow IPv6 for RPD $other_opts = $other_opts."${delimiter}EnableIPv6Support${delimiter}1"; } # NamePolicy need to be passed which is required to maintain # the sync across nodename and hostname, if specified. if (!$Opt_Namepolicy) { $name_policy = $name_policy_undef; } $other_opts = $other_opts."${delimiter}NamePolicy${delimiter}$name_policy"; # Pass hostnames list if any. # # 190520: # If last hostname is found to be empty in $host_names_list, # append an additional "," to the end of $host_names_list for # calculating proper element count for hostnames list in mkrsrc-api $comma = ","; if ($hostnames[0] ne "") { if ($last_host_name eq "") { $host_names_list = $host_names_list.$comma; } $other_opts = $other_opts."${delimiter}HostNames${delimiter}"."{$host_names_list}"; } # Prepare to call either mkcluster or mkrsrc-api depending on domain type if ($Opt_ClusterType == $ClusterType_CAAExt) { $rc = &MakeCAACluster($cluster_name, $caa_disk, $caa_repos, @node_names); } elsif ($Opt_ClusterType == $ClusterType_CAAInt) { if ($Opt_CAADisk) { $other_opts = $other_opts."${delimiter}CAAClusterDisk${delimiter}$caa_disk"; } if ($Opt_CAARepos) { $other_opts = $other_opts."${delimiter}CAAReposDisk${delimiter}$caa_repos"; } } # SecurityMode , push this to other_opts if specified # SecurityMode specified for creating cluster in NIST compliance mode if ($Opt_SecurityMode) { # SecurityMode specified for creating cluster in NIST compliance mode $other_opts = $other_opts."${delimiter}SecurityMode${delimiter}$security_mode"; } # Prepare to call mkrsrc-api # get the total length for the command to see if the command buffer will be exceeded $parmlen = length($CTBINDIR) + length($RSCLUSTER) + length($cluster_name) + length($other_opts) + 50; # decide how to call mkrsrc-api based on the command length if ($parmlen > $CMD_LIMIT) { # use mkrsrc-api with file option $rc = mkrsrcapi_file($cluster_name, $other_opts); } else { # use mkrsrc-api using the command buffer $rc = mkrsrcapi_cmdline($cluster_name, $other_opts); } # 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); } if ($Verbose) { printIMsg("IMsgmkrpdomainEnd",$cluster_name); } 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. # # $cluster_name Name of cluster to create. # # $hats_port HATS port # # $hags_port HAGS port # # $cssk_type CSSK type # # $refresh_interval CSSK refresh interval # # $admin_uid Administrator user ID # # @node_names Nodes to belong to the cluster # # $file_name File containing node names # # $q_type Quorum type # # $q_group Quorum group name # # $name_policy Name policy # # # # Global Variables Modified: # # $Verbose output True (-V) turn Verbose mode on. # # $Trace output True (-T) turn Trace mode on. # # $Opt_Hatsport output True (-t) HATS port specified # # $Opt_Hagsport output True (-g) HAGS port specified # # $Opt_CSSKType output True (-k) CSSK type specified # # $Opt_CSSKRefresh output True (-r) CSSK refresh int specified # # $Opt_AdminUID output True (-u) Admin user ID specified # # $Opt_File_Input output True (-f|-F) file name specified # # $Opt_Continue output True (-c) Continue on error # # $Opt_QuorumType output True (-Q) Quorum type # # $Opt_GroupQ output True (-G) Quorum group name # # $Opt_ShowIPv6 output True (-6) IPv6 allowed on. # # $Opt_ClusterType output True (-C) Domain Type is CAA # # $Opt_Namepolicy output True (-p) Name policy specified, # # don't consider default value # $Opt_SecurityMode output True (-S) SecurityMode specified # #--------------------------------------------------------------------# sub parse_cmd_line { my(@original_argv) = @ARGV; my $cluster_name = ""; # cluster name my $hats_port = 0; # HATS port my $hags_port = 0; # HAGS port my $rmc_port = 0; # RMC port my $cssk_type = ""; # CSSK type my $refresh_interval = ""; # CSSK refresh interval my $admin_uid = ""; # Admin user ID my $file_name = ""; # file name my $q_type = ""; # quorum type my $q_group = ""; # quorum group name my @node_names = (); # node names my $name_policy = 0; # Name policy my $security_mode = "none"; # Security Mode ["none", "nist_sp800_131a"] my %opts = (); # Process the command line... if (!&getopts('C:ht:g:G:Q:k:r:R:D:cu:f:F:m:p:S:VT6', \%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...cluster name followed by node names if ($#ARGV >= 0) { # cluster name $cluster_name = shift @ARGV; # get cluster name } else { # cluster name not specified printCEMsg("EMsgConfigRMcliInvalidNumberOfOperands"); &print_usage; return CRM_CLI_BAD_OPERAND; } # Get the node names if ($#ARGV >=0) { # node names @node_names = @ARGV; # get node names } # make sure -f or -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{t}) { # -t for HATS port $hats_port = $opts{t}; $Opt_Hatsport = $TRUE; # -t flag specified } if (defined $opts{g}) { # -g for HAGS port $hags_port = $opts{g}; $Opt_Hagsport = $TRUE; # -g flag specified } if (defined $opts{k}) { # -k for CSSK type $cssk_type = $opts{k}; $Opt_CSSKType = $TRUE; # -k flag specified } if (defined $opts{r}) { # -r for refresh interval if (!defined $opts{k}) { # -k must be specified too printEMsg("EMsgmkrpdomainRNeedsK"); &print_usage; return CRM_CLI_BAD_FLAG; } $refresh_interval = $opts{r}; $Opt_CSSKRefresh = $TRUE; # -r flag specified } if (defined $opts{c}) { # -c for continue on error $Opt_Continue = $TRUE; # -c flag specified } if (defined $opts{u}) { # -u for admin ID $admin_uid = $opts{u}; $Opt_AdminUID = $TRUE; # -u flag specified } if (defined $opts{Q}) { # -Q for quorum group name $q_type = $opts{Q}; # turn quorum type into a number $q_type = check_quorum_type($q_type); $Opt_QuorumType = $TRUE; # -Q flag specified } if (defined $opts{G}) { # -G for quorum group name $q_group = $opts{G}; $Opt_GroupQ = $TRUE; # -G flag specified } if (defined $opts{m}) { # -m for thread fanout $fanout = $opts{m}; $Opt_Fanout = $TRUE; # -m flag specified } if (defined $opts{6}) { # -6 to allow IPv6 in RPDr $Opt_ShowIPv6 = $TRUE; # -6 flag specified } if (defined $opts{C}) { # -C to specify the cluster type $caa_flag = $opts{C}; $Opt_DomainType_Flag = $TRUE; # boolean domain flag was specified if(($caa_flag eq "") || ($caa_flag =~/-/)) { printCEMsg("EMsgConfigRMcliMissingRequiredFlagOperand", "-C"); &print_usage; return CRM_CLI_BAD_OPERAND; } else { if (($caa_flag == $ClusterType_CAAExt ) || ($caa_flag == $ClusterType_CAAInt) || ($caa_flag == $ClusterType_PeerDomain)) { $Opt_ClusterType = $caa_flag; } else { printCEMsg("EMsgConfigRMcliImproperUsageOperand", $caa_flag); &print_usage; return CRM_CLI_BAD_OPERAND; } } } # If -C command option and CT_CAA_CLUSTER_TYPE both specified, command option will be used # All need to be specified in same way # if CT_CAA_CLUSTER_TYPE=1 is not specified, CT_CAA_REPOSITORY and CT_CAA_SHARED_DISK will be ignored # if CT_CAA_CLUSTER_TYPE=1 is specified, CT_CAA_REPOSITORY should be specified elsif (defined $ENV{CT_CAA_CLUSTER_TYPE}) { $caa_flag = $ENV{CT_CAA_CLUSTER_TYPE}; if($caa_flag == $ClusterType_CAAInt) { $Opt_ClusterType = $caa_flag; if(defined $ENV{CT_CAA_SHARED_DISK}) { $caa_disk = $ENV{CT_CAA_SHARED_DISK}; $Opt_CAADisk = $TRUE; } if(defined $ENV{CT_CAA_REPOSITORY}) { $caa_repos = $ENV{CT_CAA_REPOSITORY}; $Opt_CAARepos = $TRUE; }else { # error - TBD return CRM_CLI_BAD_OPERAND; } } } # fail if CAA request and not AIX if ( $Opt_ClusterType != $ClusterType_PeerDomain ) { @sysname=`uname -s`; if ( $sysname[0] !~ /AIX/ ) { printEMsg( "EMsgmkrpdomainCAANotSup" ); &print_usage; return CRM_CLI_BAD_OPERAND; } } if (defined $opts{D}) { # -D to specify cluster shared Disks $caa_disk = $opts{D}; $Opt_CAADisk = $TRUE; if(($caa_disk eq "") || ($caa_disk =~/-/)) { printCEMsg("EMsgConfigRMcliMissingRequiredFlagOperand", "-D"); &print_usage; return CRM_CLI_BAD_OPERAND; } if($Opt_DomainType_Flag==0) { printEMsg("EMsgmkrpdomainRNeedsCR"); #must specify -C and -R for CAA &print_usage; return ERRM_CLI_BAD_FLAG; } } if (defined $opts{R}) { # -R to specify cluster repository disk $caa_repos = $opts{R}; $Opt_CAARepos = $TRUE; if(($caa_repos eq "") || ($caa_repos =~/-/)) { printCEMsg("EMsgConfigRMcliMissingRequiredFlagOperand", "-R"); &print_usage; return CRM_CLI_BAD_OPERAND; } if($Opt_DomainType_Flag==0) { printEMsg("EMsgmkrpdomainRNeedsC"); #must specify -C &print_usage; return ERRM_CLI_BAD_FLAG; } } else { # -R is required when the -C flag is used if($Opt_DomainType_Flag==$TRUE && $caa_flag != 0) { printEMsg("EMsgmkrpdomainC1NeedsR"); &print_usage; return CRM_CLI_BAD_FLAG; } } if (defined $opts{p}) { # -p is required to specify name policy $name_policy = $opts{p}; $Opt_Namepolicy = $TRUE; if (($name_policy eq "") || ($name_policy =~/-/)) { printCEMsg("EMsgConfigRMcliMissingRequiredFlagOperand", "-p"); &print_usage; return CRM_CLI_BAD_OPERAND; } } if($Opt_ClusterType==$ClusterType_PeerDomain) { if($Opt_CAARepos==$TRUE){ printCEMsg("EMsgConfigRMcliImproperUsageCombination", "-C 0","-R"); &print_usage; return CRM_CLI_BAD_OPERAND; } elsif($Opt_CAADisk==$TRUE) { printCEMsg("EMsgConfigRMcliImproperUsageCombination", "-C 0","-D"); &print_usage; return CRM_CLI_BAD_OPERAND; } } if (defined $opts{S}) { # -S is required to specify SecurityMode[Nist Compliance mode] $security_mode = $opts{S}; $Opt_SecurityMode = $TRUE; if (($security_mode eq "") || ($security_mode =~/-/)) { printCEMsg("EMsgConfigRMcliMissingRequiredFlagOperand", "-S"); &print_usage; return CRM_CLI_BAD_OPERAND; } } if (($Opt_SecurityMode) && ($Opt_ClusterType == $ClusterType_CAAExt || $Opt_ClusterType == $ClusterType_CAAInt)) { printCEMsg("EMsgConfigRMcliImproperUsageCombination", "-C", "-S"); &print_usage; return CRM_CLI_BAD_FLAG; } return(0, $hats_port, $hags_port, $cssk_type, $refresh_interval, $admin_uid, $file_name, $cluster_name, $q_type, $q_group, $caa_disk, $caa_repos, $name_policy, $security_mode, @node_names); # success } # end parse_cmd_line #--------------------------------------------------------------------# # print_usage : print the usage statement (syntax) to stdout. # #--------------------------------------------------------------------# sub print_usage { @sysname=`uname -s`; if ( $sysname[0] =~ /AIX/ ) { &printIMsg("IMsgmkrpdomainUsageIpv6CAA"); } else { &printIMsg("IMsgmkrpdomainUsageIpv6"); } } # end print_usage #--------------------------------------------------------------------# # mkrsrcapi_file - function to call the mkrsrc-api command after # # building an input file conataining the resource definition. # # This is called when the command line buffer is not big enough to # # handle all of the command input to be processed. # # # # Parameters: # # $cluster_name input The cluster name to create. # # $mk_options input The optional attributes to set. # # # # Return: # # $rc return code. # # # # Global References: # # Trace input Trace call & return of extension. # # Verbose input Print newly defined rsrc handle. # #--------------------------------------------------------------------# sub mkrsrcapi_file { my ($cluster_name, $mk_options) = @_; my $rc = 0; my $mk_cmd = ""; my @cmd_out = (); my $mytmpdir = ""; my $cttmpdir_exit = 0; my $mk_file = ""; my $mk_time = 0; my $mk_pid = $$; my $file_error = ""; # create the file name to be used $mk_time = time(); $mytmpdir = `/opt/rsct/bin/cttmpdir`; chomp $mytmpdir; $cttmpdir_exit = $?; if ($cttmpdir_exit != 0) { $mytmpdir = "/tmp"; } $mk_file = "${mytmpdir}/mkrpdomain_file_input.${mk_pid}.${mk_time}"; # create the text for the file input #$mk_cmd = "${RSCLUSTER}${delimiter}Name${delimiter}${cluster_name}${other_opts}\n"; $mk_cmd = "${RSCLUSTER}${delimiter}Name${delimiter}${cluster_name}${mk_options}\n"; if ($main::Trace) { print STDERR "Creating $mk_file to store resource information.\n";} if (open (MKFILE, ">$mk_file") ) { if (!(print MKFILE $mk_cmd)) { # get the error $file_error = $!; # print error message and exit &printEMsg("EMsgmkrpdomainTempFileError",$file_error); # try to delete the temp file in case it was created @cmd_out = `/bin/rm $mk_file 2>&1`; exit(CRM_CLI_USER_ERROR); } if (!(close(MKFILE))) { # get the error $file_error = $!; # print error message and exit &printEMsg("EMsgmkrpdomainTempFileError",$file_error); # try to delete the temp file in case it was created @cmd_out = `/bin/rm $mk_file 2>&1`; exit(CRM_CLI_USER_ERROR); } } # process error from open else { # get the error $file_error = $!; # print error message and exit &printEMsg("EMsgmkrpdomainTempFileError",$file_error); # try to delete the temp file in case it was created @cmd_out = `/bin/rm $mk_file 2>&1`; exit(CRM_CLI_USER_ERROR); } if ($main::Trace) { print STDERR "Calling mkrsrc-api:\n"; print STDERR "mkrsrc-api parameters:"; print STDERR "$mk_cmd\n"; } # call mkrsrc-api. -F causes the input file to be deleted @mk_out = `$CTBINDIR/mkrsrc-api -D"$delimiter" -I"$delimiter" -F $mk_file 2>&1`; # capture the return code from mkrsrc-api $rc = $?; $rc = process_exit_code($rc); if ($main::Trace) { print STDERR "mkrsrc-api results:\n"; print STDERR "mkrsrc-api returned $rc \n"; print STDERR "@mk_out"; } # show any errors if there was a bad rc if ($rc != 0) { process_api_error($delimiter,$rc,@mk_out); } # delete the temp file, just in case @cmd_out = `/bin/rm $mk_file 2>&1`; return $rc; } # end mkrsrcapi_file #--------------------------------------------------------------------# # mkrsrcapi_cmdline - function to call the mkrsrc-api command after # # building a command string to pass to the command. # # # # Parameters: # # $cluster_name input The cluster name to create. # # $mk_options input The optional attributes to set. # # # # Return: # # $rc return code. # # # # Global References: # # Trace input Trace call & return of extension. # # Verbose input Print newly defined rsrc handle. # #--------------------------------------------------------------------# sub mkrsrcapi_cmdline { my ($cluster_name, $mk_options) = @_; my $rc = 0; my @mk_out = (); if ($main::Trace) { print STDERR "Calling mkrsrc-api:\n";} @mk_out=`$CTBINDIR/mkrsrc-api -D"$delimiter" -I"$delimiter" "${RSCLUSTER}${delimiter}Name${delimiter}${cluster_name}${mk_options}" 2>&1`; # capture the return code from mkrsrc-api $rc = $?; $rc = process_exit_code($rc); if ($main::Trace) { print STDERR "mkrsrc-api results:\n"; print STDERR "mkrsrc-api returned $rc \n"; print STDERR "@mk_out"; } # show any errors if there was a bad rc if ($rc != 0) { process_api_error($delimiter,$rc,@mk_out); } return $rc; } # end mkrsrcapi_cmdline #--------------------------------------------------------------------# # MakeCAACluster - makes a command string to pass to CAA mkcluster # # command. Upon exit of this function, the CAA cluster will be # # created, and CAA services started. # # # # Parameters: # # $cluster_name input The cluster name to create. # # @node_names input The nodes in the cluster. # # $caa_disk input The cluster disk # # $caa_repos input The cluster repository disk # #--------------------------------------------------------------------# sub MakeCAACluster { my ($cluster_name, $caa_disk, $caa_repos, @node_names) = @_; my $rc = 0; my @mkcl_out = (); my $caa_nodes=""; #Build a comma separated list of the node names to pass to mkcluster for ($i = 0; $i <= $#node_names; $i++) { if($i==0) { $caa_nodes = $node_names[$i]; } else { $caa_nodes="$caa_nodes,$node_names[$i]"; } } if ($main::Trace) { print STDERR "**Invoking CAA mkcluster command\n"; } @mkcl_out=`/usr/sbin/mkcluster -n $cluster_name -d $caa_disk -m $caa_nodes -r $caa_repos`; print "@mkcl_out\n"; $rc = $?; if($rc != 0) { print STDERR "mkrpdomain failed: mkcluster rc = $rc\n"; } $rc = process_exit_code($rc); exit(); } # end MakeCAACluster