#!/bin/ksh93 # ALTRAN_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # Copyright (C) Altran ACT S.A.S. 2017,2018,2019,2020,2021. All rights reserved. # # ALTRAN_PROLOG_END_TAG # # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # 61haes_r721 src/43haes/lib/ksh93/func_include.sh 1.96 # # Licensed Materials - Property of IBM # # Restricted Materials of IBM # # COPYRIGHT International Business Machines Corp. 2006,2016 # 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 # @(#) 64cdf46 43haes/lib/ksh93/func_include.sh, 726, 2147A_aha726, May 05 2021 10:51 AM ######################################################################### # # COMPONENT_NAME: CLMGR # # FUNCTIONS: # # escape # alarm_exit # set_alarm # clear_alarm # isClusterDefined # log_return_msg # debug # get_local_node_label # get_interfaces # isEnterprise # getPVData # getDiskData # trim # search_properties # prune_indexes # list_disks # get_xml_attr # show_stack # add_caa_access # rm_caa_access # # verify_is_numeric # verify_numeric_range # verify_is_IPv4_address # verify_is_multicast_address # verify_is_absolute_path # verify_in_set # verify_disk # ######################################################################### #============================================================================== # Name: escape # # Description: adds escaping to potentially "dangerous" shell metacharacters # # Inputs: value The value to be escaped. # # Outputs: The processed value is displayed in STDOUT. # # Returns: Exits with a 0. #============================================================================== function escape { . $HALIBROOT/log_entry "$0()" "$CL" max typeset value=$1 value=${value//\\/\\\\\\} # This must be done first! # These characters can be dangerous, and result in # unexpected command execution during an eval command. value=${value//\`/\\\`} value=${value//\*/\\\*} value=${value//\?/\\\?} value=${value//\$/\\\$} # These characters can mess up regular expressions, when # one of them is desired to be matched literally. value=${value//\[/\\\[} value=${value//\]/\\\]} value=${value//\(/\\\(} value=${value//\)/\\\)} print -- "$value" log_return_msg 0 "$0()" "$LINENO" return $? } # End of "escape()" #============================================================================== # Name: alarm_exit # # Description: a signal handler to trap "unfriendly" acts, such as somebody # hitting Ctrl+C, or some such. Cleans up after the script, # doing anything from cleaning up processes, to removing any # lingering temporary files. # # Inputs: None. # # Outputs: None. # # Returns: Exits with a 1. #============================================================================== function alarm_exit { set +e if [[ $reaper == +([[:digit:]]) ]] && (( reaper > 1 )); then kill $reaper 2>/dev/null fi exit 1 } #============================================================================== # Name: set_alarm # # Description: sets an alarm in the background that will send an # interrupt signal (e.g. SIGINT) to the specified # process after the indicated number of seconds has # elapsed. # # Inputs: SECONDS The number of seconds to wait before sending a # signal to the specified process. # Defaults to 30 seconds. # # PROC The ID of the process to send a signal to if # the alarm is triggered (i.e. the number of # specified SECONDS elapses). # Defaults to the process ID of the currently # running script. # # SIGNAL The signal to send to PROC if the alarm is # triggered (i.e. the number of specified SECONDS # elapses). # Defaults to "ALRM" if PROC is the current script, # or "KILL" if PROC is some other process. # # MSG A message to display when the alarm is triggered # (i.e. the number of specified SECONDS elapses). # A default message is written to STDERR if no # message is provided. # # Outputs: If the alarm is triggered, an error message is written to # STDERR; either the message that was explicitly provided, or # a default message. We want to avoid having anonymous process # interruptions, but if you really want zero output, just pass # in an message that is all spaces (even just one space). # # Returns: 1 if the alarm was not set, otherwise 0 (zero). # In the latter case, the "reaper" environment # variable is exported with the reaper's process # ID, enabling the alarm to be cleared, if needed. # # Usage Examples: # # To set an alarm on some backgrounded command: # & # set_alarm $! KILL "" # # # sleep 5& # # set_alarm 3 $! KILL # # To set an alarm on the current script: # set_alarm # # # set_alarm 180 # # To set an alarm, then continue processing, combine this function # with a custom trap function: # # function my_alarm_func { # : Do something here... # return 1 # } # trap my_alarm_func USR1 # set_alarm $$ USR1 "" # #============================================================================== function set_alarm { return 0 # Disable for now typeset SECONDS=$1 typeset PROC=$2 typeset SIGNAL=$3 typeset MSG=$4 if [[ -z $SIGNAL ]]; then if [[ -z $PROC || $PROC != +([[:digit:]]) || $PROC == $$ ]]; then SIGNAL="ALRM" else SIGNAL="KILL" fi fi export SET_ALARM_SIGNAL=$SIGNAL # If somebody kills the parent process, clean up the reaper trap 'alarm_exit' INT TERM HUP KILL if [[ -z $MSG ]]; then MSG=$(/usr/bin/dspmsg -s 2 command.cat 132 'Warning: process "%1$s" has exceeded the maximum allowed run\n time of %2$s seconds. Aborting.\n' "$PROC" "$SECONDS") elif [[ $MSG == +([[:space:]]) ]]; then MSG="" fi [[ $SECONDS != +([[:digit:]]) ]] || (( ! SECONDS )) && SECONDS=30 [[ -z $PROC || $PROC != +([[:digit:]]) ]] && PROC=$$ ( ( sleep $SECONDS & echo $! wait $! 2>/dev/null && { [[ -n $MSG ]] && print -u2 "\n$MSG\n" kill -s $SIGNAL $PROC 2>/dev/null } ) & ) | read reaper typeset -i status=1 if [[ $reaper == +([[:digit:]]) ]] && (( reaper > 1 )); then export reaper status=0 fi return $status } #============================================================================== # Name: clear_alarm # # Description: if an alarm has been set, this function gets rid of it. # Also clears any trap that may have been associated with # the signal passed to set_alarm(). # # Inputs: SIGNALS An optional list of the signals to clear. # Defaults to whatever signal was last set # via set_alarm(). # # Outputs: None. # # Returns: Exits with the number of errors that occurred while # trying to clear the alarm(s). #============================================================================== function clear_alarm { return 0 # Disable for now typeset SIGNALS=$1 typeset -i errors=0 trap - INT TERM HUP KILL # Remove the reaper cleanup trap if [[ $SIGNALS != *([[:space:]]) ]]; then trap - $SIGNALS else if [[ -n $SET_ALARM_SIGNAL && $SET_ALARM_SIGNAL != *[[:space:]]* ]] then trap - $SET_ALARM_SIGNAL fi fi (( $? != 0 )) && (( errors++ )) typeset -i status=1 if [[ $reaper == +([[:digit:]]) ]] && (( reaper > 1 )); then kill $reaper 2>/dev/null (( $? != 0 )) && (( errors++ )) fi return $errors } # End of "clear_alarm()" ############################################################################### # # Name: isClusterDefined # # Description: Determines if a cluster has been defined on this system. # # Inputs: quiet Indicates that all output from this function should # be suppressed. # # Outputs: None. # # Returns: 0 if a cluster is detected on this host. # 1 if there is no cluster on this system. # ############################################################################### function isClusterDefined { . $HALIBROOT/log_entry "$0()" "$CL" max typeset quiet=$1 typeset -i rc=$RC_SUCCESS # NOTE: we used to use a call to "cllsclstr" here, but that became a # problem if the networking part of the cluster configuration # was damaged. The cllsclstr command would return non-zero, making # it appear that no cluster was configured, when in fact there was # a cluster, and it was simply damaged. So we switched to a more # low-level approach to improve reliability, and allow damaged # clusters to be removed. typeset ID=$(clodmget -n -f id HACMPcluster 2>/dev/null) ID=${ID##*= } ID=${ID//\"/} if [[ $ID != +([0-9]) ]]; then if [[ $CLMGR_PROGNAME == "clmgr" ]]; then dspmsg -s $CLMGR_SET $CLMGR_MSGS 35 "\nERROR: no cluster is defined.\n\n" 1>&2 fi rc=$RC_ERROR fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "isClusterDefined()" ############################################################################### # # Name: log_return_msg # # Description: provides a common, normalized function exit routine that # automatically displays a "LEAVING FUNCTION" sentinel message # if tracing is enabled, as well as restoring the PS4 settings # of the calling function. # # Inputs: rc The code number to be returned. # # funcname The name of the calling function (i.e. the # function that just completed running, and is # now returning). If this value is not explicitly # provided, then the "fn" environment variable is # used as a default (if it is set). # # caller_lineno This is the line number within the calling # function where this function, "log_return_msg()", # was invoked. With this, we can accurately log # the true line number of their actual "return()" # invocation in that function, which *should* be # on the very next line. Basically, this keeps the # "LEAVING FUNCTION" traces' line numbers accurate. # # minT The minimum CLMGR_LOGGING level which will # initiate tracing. # # Outputs: If logging is enabled, a "LEAVING FUNCTION" sentinel message # is written to STDERR. If the trace level is higher than "low", # then the return code is also written to STDERR. # # Returns: "$rc"; the specified return code # ############################################################################### function log_return_msg { typeset -i rc=$1 typeset funcname=$2 typeset caller_lineno=$3 typeset minT=$4 #=============================================================== # Avoid using this function if it not being called from within # the clmgr/clvt context. The PS4 format cannot be guaranteed # in that case, and might get a little... mangled. #=============================================================== [[ $PS4 != CL@(MGR|VT):* ]] && return $rc [[ -z $funcname ]] && funcname=$fn if [[ -n $CLMGR_TMPLOG ]]; then if [[ $rc != *(\-)+([[:digit:]]) ]]; then print -u2 "\nWarning: clmgr internal error (log_return_msg): missing return code ($funcname)\n" fi if [[ $caller_lineno != +([[:digit:]]) ]]; then print -u2 "\nWarning: clmgr internal error (log_return_msg): missing caller line number ($funcname)\n" fi fi [[ $minT == *([[:space:]]) ]] && minT="low" if [[ $CLMGR_LOGGING == @(low|med|high|max) ]] then typeset OUT=$(print -- ${PS4/\$?/$rc}) OUT=${OUT/\$LINENO/$caller_lineno} OUT=${OUT/\$SECONDS/$SECONDS} typeset TYPE="FUNCTION" [[ $funcname != *\(\) ]] && TYPE="FILE" print -u2 -- "${OUT}${TAB} LEAVING $TYPE \"$funcname\" at $(cltime)\n" fi #================================================== : Remove the current function from the call stack #================================================== if [[ $funcname != *([[:space:]]) ]]; then if [[ $CLMGR_STACK == *$funcname* ]]; then CLMGR_STACK=${CLMGR_STACK%$funcname*} CLMGR_STACK=${CLMGR_STACK%%+([[:space:]])} CLMGR_STACK=${CLMGR_STACK%@( |#)+([[:digit:]])} CLMGR_STACK=${CLMGR_STACK% at line*([[:space:]])} : If we updated the stack, we should update PS4, as well typeset PREV_FN=${CLMGR_STACK##*+([[:space:]])} typeset SENTINEL TID RC FUNC REM print -- "$PS4" |IFS=: read SENTINEL TID RC FUNC REM FUNC=${FUNC#*\[} # Remove old function FUNC="${PREV_FN}[${FUNC}" # Insert new function PS4="$SENTINEL:$TID:$RC:$FUNC:$REM" : Update the FFDC file [[ ! -d $FFDC_DIR ]] && /usr/bin/mkdir -p $FFDC_DIR if [[ -d $FFDC_DIR ]]; then print "\ ===================== clmgr Call Stack: ===================== $CLMGR_STACK" >$FFDC_CS_FILE fi fi fi return $rc } # End of "log_return_msg()" ############################################################################### # # Name: debug # # Description: a minor utility function used for generating a useful, # lightly formatted debug message. As the name implies, # this is useful for developers only, to be used in the # course of debugging code. Somewhat better than a simple # "echo". # # Inputs: Whatever debug message is desired. # # Outputs: The desired debug message is displayed on STDERR. Each # message is prefaces with "DEBUG: : ". # # Returns: No explicit return value. # ############################################################################### function debug { typeset CALLER=${CLMGR_STACK##*+([[:space:]])} [[ -z $TAB ]] && TAB=$'\t' print -u2 "DEBUG: $CALLER:$TAB$@" } # End of "debug()" ############################################################################### # # Name: get_local_node_label # # Description: invokes the "get_local_nodename" utility. Upon occasion, # that utility may fail, in which case a manual "best effort" # is made to determine the local node name. # # Inputs: $1 An optional input that is a string reference. If # this is provided, the local node label is returned # in the reference variable, rather than on STDOUT. # # Outputs: The local node name, if found, is displayed on STDOUT. # This is *not* done, however, in the "lnode" reference # is passed in (see Inputs, above). # # Returns: RC_SUCCESS if a node name was found, or else RC_ERROR. # ############################################################################### function get_local_node_label { . $HALIBROOT/log_entry "$0()" "$CL" max typeset -i USE_STDOUT=1 # Default return style if [[ -n $1 ]]; then typeset -n localnode=$1 lnode="" # Initialize USE_STDOUT=0 else typeset localnode="" fi typeset -i rc=$RC_UNKNOWN typeset CLMGR_LOG=$CLMGR_TMPLOG if [[ -z $CLMGR_LOG || ! -f $CLMGR_LOG || ! -w $CLMGR_LOG ]]; then CLMGR_LOG="$(clodmget -q name=clutils.log -f value -n HACMPlogs)/clutils.log" [[ ! -w $CLMGR_LOG ]] && CLMGR_LOG="/tmp/clutils.log" export CLMGR_LOG export CLVTLOG=$CLMGR_LOG # For backwards compatibility fi print "$0()[$LINENO]($SECONDS): get_local_nodename" >>$CLMGR_LOG # Always log commands localnode=$(VERBOSE_LOGGING="" get_local_nodename 2>>$CLMGR_LOG) print "$0()[$LINENO]($SECONDS): get_local_nodename RC: $?; localnode == \"$localnode\"" >>$CLMGR_LOG # Always log command result if [[ $localnode == *([[:space:]]) ]]; then print "$0()[$LINENO]($SECONDS): clodmget -n -f nodename HACMPcluster" >>$CLMGR_LOG localnode=$(clodmget -n -f nodename HACMPcluster 2>>$CLMGR_LOG) print "$0()[$LINENO]($SECONDS): clodmget RC: $? (localnode=$localnode)" >>$CLMGR_LOG if [[ $localnode == *([[:space:]]) ]]; then typeset FQDN= is= ADDR= REM= host $(hostname) | read FQDN is ADDR REM for localnode in $(clodmget -q object=COMMUNICATION_PATH -n -f value HACMPnode 2>>$CLMGR_LOG) do if [[ $localnode != *([[:space:]]) ]]; then if [[ ${localnode%%\.*} == ${FQDN%%\.*} || \ $localnode == $ADDR ]] then break else localnode="" fi fi done if [[ $localnode == *([[:space:]]) ]]; then localnode=$(/usr/bin/hostname) localnode=${localnode%%\.*} fi fi fi if [[ $localnode == *([[:space:]]) ]]; then localnode="" rc=$RC_ERROR else if (( USE_STDOUT )); then print -- $localnode fi rc=$RC_SUCCESS fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "get_local_node_label()" ############################################################################### # # Name: get_interfaces # # Description: uses ifconfig to retrieve the list of interfaces, and which # addresses are currently active on them. This is needed because # the base product only returns non-transient interface data, # from HACMPadapter. Since SIs/PIs are transient, can be moved # from interface to interface, they are not stored in # HACMPadapter. # # If is worth noting that "netstat -in" was also considered for # this solution, but was discarded because it truncates IPv6 # addresses. So "ifconfig -a" was used instead. # # Inputs: DATA an associative array reference, used to return data # node a specific node to get interface data from # interace a specific interface to retrieve # # Outputs: Returns the interfaces in the associative array reference # that was provided as an input. # # Returns: RC_SUCCESS if interface data was found, or else RC_ERROR. # ############################################################################### function get_interfaces { . $HALIBROOT/log_entry "$0()" "$CL" max typeset -n DATA=$1 typeset node=$2 typeset interface=$3 typeset -i rc=$RC_UNKNOWN typeset intf= data="" typeset ifconfig_data="" [[ -z $interface ]] && interface="-a" if [[ -n $node && $node != $LOCAL_NODE ]]; then ifconfig_data=$($CLRSH $node "/etc/ifconfig $interface" 2>>$CLMGR_TMPLOG) else ifconfig_data=$(/etc/ifconfig $interface) fi (( $? == RC_SUCCESS )) && rc=$RC_SUCCESS || rc=$RC_ERROR print -- "$ifconfig_data" |\ while IFS= read LINE; do if [[ $LINE != [[:space:]]* ]]; then intf=${LINE%%:[[:space:]]*} if [[ -n $intf ]];then DATA[$intf]="" fi elif [[ $LINE == *[[:space:]]@(inet|inet6)[[:space:]]* ]]; then addr=${LINE##*@(inet|inet6)+([[:space:]])} addr=${addr%%+([[:space:]])*} if [[ -n $intf && -n $addr ]]; then [[ -n ${DATA[$intf]} ]] && DATA[$intf]="${DATA[$intf]} " DATA[$intf]="${DATA[$intf]}$addr" fi fi done log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "get_interfaces()" ############################################################################### # # Name: isEnterprise # # Description: determines whether or not the locally installed SystemMirror # software is Enterprise Edition. # # Inputs: NODE (optional) The name of the node to test. # # Outputs: None. # # Returns: 1 if Enterprise edition, or 0 (zero) if not. # ############################################################################### function isEnterprise { . $HALIBROOT/log_entry "$0()" "$CL" max typeset NODE=$1 typeset -i ENTERPRISE=0 # Assume "STANDARD" edition if [[ -z $CL_SNAPSHOT ]] then print "$0()[$LINENO]($SECONDS): /usr/bin/lslpp -lc \"cluster.xd.*\"" >>$CLMGR_TMPLOG # Always log commands if [[ $NODE == *([[:space:]]) || \ ${NODE%%\.*} == ${LOCAL_NODE%%\.*} ]] then /usr/bin/lslpp -lc "cluster.xd.*" >/dev/null 2>&1 else $CLRSH $NODE /usr/bin/lslpp -lc "cluster.xd.*" >/dev/null 2>&1 fi typeset -i rc=$? (( $rc == 0 )) && ENTERPRISE=1 print "$0()[$LINENO]($SECONDS): lslpp RC: $rc" >>$CLMGR_TMPLOG # Always log command result else print "$0()[$LINENO]($SECONDS): grep -q \"^HACMPxd_storage_technology:\" $SNAPSHOTPATH/$CL_SNAPSHOT.odm" >>$CLMGR_TMPLOG if grep -q "^HACMPxd_storage_technology:" $SNAPSHOTPATH/$CL_SNAPSHOT.odm then ENTERPRISE=1 fi print "$0()[$LINENO]($SECONDS): ENTERPRISE: $ENTERPRISE" >>$CLMGR_TMPLOG fi log_return_msg "$ENTERPRISE" "$0()" "$LINENO" return $? } # End of "isEnterprise()" ############################################################################### # # Name: getPVData # # Description: Gets a list of the disks from a node within the set of # nodes provided (to ensure accuracy; can't just default # to the local node for this). # # Gets the list of disks from an appropriate node. # # Returns the node that the list was pulled from, along with the data itself. # # # Inputs: nodes The set of nodes that may be used to retrieve # the disk data. # node A reference to a string, used to return the node # that was ultimately used to successfully retrieve # the disk data. # PVDATA A reference to a string, used to return the disk # data that was retrieved. # vgname The optional name of a volume group, whose disks # are the only ones that we want to return. # # Outputs: None. # # Returns: 0 (zero) if the disk data was successfully retrieved, or # non-zero otherwise. # ############################################################################### function getPVData { . $HALIBROOT/log_entry "$0()" "$CL" max typeset nodes=$1 typeset -n node=$2 typeset -n PVDATA=$3 typeset vgname=$4 #======================================= # Declare and initialize all variables #======================================= typeset rc=$RC_UNKNOWN node= PVDATA= if [[ -n $LOCAL_NODE ]]; then if [[ " ${nodes//,/ } " == *\ $LOCAL_NODE\ * ]]; then node=$LOCAL_NODE print "$0()[$LINENO]($SECONDS): /usr/sbin/lspv" >>$CLMGR_TMPLOG PVDATA=$(/usr/sbin/lspv) rc=$? print "$0()[$LINENO]($SECONDS): lspv ($node) RC: $rc" >>$CLMGR_TMPLOG else : Try lspv on each node until it executes successfully for node in ${nodes//,/ }; do print "$0()[$LINENO]($SECONDS): $CLRSH $node /usr/sbin/lspv" >>$CLMGR_TMPLOG PVDATA=$($CLRSH $node /usr/sbin/lspv 2>>$CLMGR_TMPLOG) rc=$? print "$0()[$LINENO]($SECONDS): lspv ($node) RC: $rc" >>$CLMGR_TMPLOG (( $rc == RC_SUCCESS )) && break done fi fi if [[ -n $PVDATA && -n $vgname ]]; then typeset LINE= FINAL_DATA= devname= pvid= vg= rem= print -- "$PVDATA" |\ while IFS= read LINE; do IFS="$ORIG_IFS" print -- "$LINE" | read devname pvid vg rem if [[ $vg == $vgname ]]; then [[ -n $FINAL_DATA ]] && FINAL_DATA="$FINAL_DATA$NL" FINAL_DATA="$FINAL_DATA$LINE" fi done PVDATA="$FINAL_DATA" fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "getPVData()" ############################################################################## # # Name: getDiskData # # Description: Returns the device name, PVID, and UUID (if any) for the # specified disk. If the disk is specified via its device name, # and more than one node is also specified, then additional # checking is done to verify that the disk name refers to the # same disk on all the nodes. # # By default this function only considerd disks that are not # already in use. However, if desired, that can be disabled, # and even allocated disks can be checked, and their data # returned. # # Inputs: DISK The disk that is to be validated, and # identifiers retrieved. May be a device # name, PVID, or UUID. # # NODES One or more nodes to perform the disk # validation on. # # IDENTIFIERS A reference to a string variable. Used # to return the disk identifiers, in the form: # # NAME:PVID:UUID:REFNODE # # ALLOW_IN_USE Allows disks that are already in use # to be queried. # # Outputs: Any errors encountered will result in an error message # being written to STDERR. The IDENTIFIERS variable will # be populated with the disk data, if found. # # Returns: Zero for a successful data retrieval, or non-zero if # a problem is encountered. # ############################################################################## function getDiskData { . $HALIBROOT/log_entry "$0()" "$CL" max typeset DISK=$1 typeset NODES=$2 NODES=${NODES//,/ } typeset -n IDENTIFIERS=$3 IDENTIFIERS="" # Erase any pre-existing data typeset ALLOW_IN_USE=$4 if [[ $DISK == *([[:space:]]) ]]; then log_return_msg "$RC_MISSING_INPUT" "$0()" "$LINENO" return $? fi if [[ $NODES == *([[:space:]]) || \ $CLUSTER_TYPE != "LC" ]] then #================================================================ : If no nodes were specified, or we know that there can only be : one, common repository disk for all nodes, then we can safely : default to the local node, and expect accurate results. #================================================================ NODES=$LOCAL_NODE else #============================================================ : If the local node is included in the node list, make sure : to put it first in the list. The output/results are more : intuitive to the humans that way. #============================================================ if [[ " $NODES " == *\ $LOCAL_NODE\ * ]] then NODES="$LOCAL_NODE ${NODES/$LOCAL_NODE/}" fi fi #=================================== # Declare and initialize variables #=================================== typeset NODE="" ALL_DISKS="" typeset cand_name="" cand_pvid="" cand_vg="" cand_uuid="" rest="" typeset -u vg_uc="" typeset -i rc=$RC_UNKNOWN typeset -A ERROR_MAP for NODE in $NODES; do #======================================= : Retrieve all disks from node "$NODE" #======================================= if [[ $NODE == $LOCAL_NODE ]]; then ALL_DISKS=$(CL=$LINENO list_disks) else ALL_DISKS=$(CL=$LINENO list_disks $NODE) fi #================================================================ : Confirm that only one disk matches the user-specified disk ID #================================================================ typeset DISK_COUNT=$(print -- "$ALL_DISKS"|/bin/grep -w "$DISK"|wc -l) if (( DISK_COUNT == 0 )); then # # It is necessary to set ODMDIR here for the case where ODMDIR # has been redirected to a set of temporary ODMs restored from # a snapshot in order to construct a snapshot report. If we do # not do this, then clodmget will fail due to not being able to # find the CuAt ODM file. Of course, if ODMDIR has been set to # a specific location by some other entity, then use that value. # typeset QUERY_ODMDIR=$ODMDIR if [[ -z $ODMDIR || $ODMDIR == *clmgr.KHgca.snapshot* ]] then QUERY_ODMDIR=$DEFAULT_ODMDIR fi if [[ $NODE == $LOCAL_NODE ]]; then cand_name=$(ODMDIR=$QUERY_ODMDIR clodmget -n -f name -q "value like '$DISK*' AND attribute = 'pvid'" CuAt) else cand_name=$($CLRSH $NODE ODMDIR=$QUERY_ODMDIR $HAUTILS/clodmget -n -f name -q "value like '$DISK*' AND attribute = 'pvid'" CuAt 2>>$CLMGR_TMPLOG) fi if [[ -n $cand_name ]]; then cand_pvid=$DISK else if [[ $NODE == $LOCAL_NODE ]]; then cand_pvid=$(ODMDIR=$QUERY_ODMDIR clodmget -n -f value -q "name like '$DISK*' AND attribute = 'pvid'" CuAt) else cand_pvid=$($CLRSH $NODE ODMDIR=$QUERY_ODMDIR $HAUTILS/clodmget -n -f value -q "name like '$DISK*' AND attribute = 'pvid'" CuAt 2>>$CLMGR_TMPLOG) fi if [[ -n $cand_pvid ]]; then cand_name=$DISK fi fi if [[ -n $cand_name || -n $cand_pvid ]]; then typeset DISK_ARG="" if [[ -n $cand_name && -n $cand_pvid ]]; then DISK_ARG="$cand_name ($cand_pvid)" elif [[ -n $cand_pvid ]]; then DISK_ARG="$cand_pvid" else DISK_ARG="$cand_name" fi cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 195 '\nWarning: "%1$s" does not appear to be available on "%2$s".\n\n' "$DISK_ARG" "$NODE" 1>&2 else cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 268 '\nERROR: "%1$s" cannot be found on "%2$s".\n\n' "$DISK" "$NODE" 1>&2 fi dspmsg -s $CLMGR_SET $CLMGR_MSGS 153 "Available Physical Volumes:\n\n" 1>&2 print -- "$ALL_DISKS" |\ while read cand_name cand_pvid cand_vg cand_uuid rest; do if [[ -n $cand_name && -n $cand_pvid ]]; then print -n -u2 " $cand_name\t($cand_pvid" [[ -n $cand_uuid ]] && print -n -u2 ", $cand_uuid" print -u2 ")" fi done print -u2 "" rc=$RC_NOT_FOUND break # Break out of the NODES loop. We're toast. elif (( DISK_COUNT > 1 )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 269 '\nERROR: "%1$s" maps to different disks across %2$s:\n\n' "$DISK" "$NODE" 1>&2 print -- "$ALL_DISKS" | /bin/grep -w "$DISK" |\ while read cand_name cand_pvid cand_vg cand_uuid rest; do print -n -u2 "\t$cand_name\t($cand_pvid" [[ -n $cand_uuid ]] && print -n -u2 ", $cand_uuid" print -u2 ")" done cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 270 '\nEither select a different disk, or try specifying the disk that you want by a different identifier, such as a UUID.\n\n' 1>&2 rc=$RC_INCORRECT_INPUT break else #======================================================== : Extract the data for the user-specified disk, "$DISK" #======================================================== print -- "$ALL_DISKS" |\ /usr/bin/grep -w "$DISK" |\ read cand_name cand_pvid cand_vg cand_uuid rest fi if [[ $cand_vg != *([[:space:]]) ]]; then vg_uc=$cand_vg if [[ $vg_uc != "NONE" && -z $ALLOW_IN_USE ]]; then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 281 '\nERROR: disk "%1$s" is already in use within volume group "%2$s" on node "%3$s".\n\n' "$DISK" "$cand_vg" "$NODE" 1>&2 rc=$RC_INCORRECT_INPUT break # Break out of the NODES loop. We're toast. fi fi if [[ "$cand_name$cand_pvid$cand_uuid" != *([[:space:]]) ]]; then typeset -i LEADER=0 BANNER=0 BANNER=${#cand_name} # Default to device name (( LEADER = ${#NODE} + 7 )) if [[ $IDENTIFIERS == *([[:space:]]) ]]; then #================================================== # Automatically keep the data from the first node # since there is nothing to compare it to yet. #================================================== ERROR_MAP[$NODE]="$cand_name:$cand_pvid:$cand_uuid" IDENTIFIERS="$cand_name:$cand_pvid:$cand_uuid:$NODE" elif [[ $DISK == $cand_uuid ]]; then #============================================================= # If the customer provided a UUID, then only compare that # value, without regard to the PVID (which can be duplicated # in a PPRC/HyperSwap environment). If the UUID is wrong in # some way, there is problem in the storage framework. #============================================================= if [[ $IDENTIFIERS != *:*:$cand_uuid:* ]]; then ERROR_MAP[$NODE]="$cand_name:$cand_pvid:$cand_uuid" rc=$RC_INCORRECT_INPUT # Highlight the differing UUID (( LEADER += ${#cand_name} + ${#cand_pvid} + 4 )) BANNER=${#cand_uuid} fi elif [[ $DISK == $cand_pvid ]]; then #============================================================== # If the customer provided a PVID, then compare it *and* the # UUID. The UUID is the only reliable, unique disk identifier # (PVIDs can be duplicated in a PPRC/HyperSwap environment). #============================================================== if [[ $IDENTIFIERS != *:$cand_pvid:$cand_uuid:* ]]; then ERROR_MAP[$NODE]="$cand_name:$cand_pvid:$cand_uuid" rc=$RC_INCORRECT_INPUT if [[ $IDENTIFIERS != *:*:$cand_uuid:* ]]; then # Highlight the differing UUID (( LEADER += ${#cand_name} + ${#cand_pvid} + 4 )) BANNER=${#cand_uuid} else # Highlight the differing PVID (( LEADER += ${#cand_name} + 2 )) BANNER=${#cand_pvid} fi fi elif [[ $IDENTIFIERS != $cand_name:$cand_pvid:$cand_uuid:* ]]; then #============================================================== # The customer provided a device name; the most unreliable # disk identifier. Highlight the most obvious indicator that # "hdisk#" resolves to a different disk on $NODE, then report # that to the customer. #============================================================== ERROR_MAP[$NODE]="$cand_name:$cand_pvid:$cand_uuid" if [[ -n $cand_uuid && $IDENTIFIERS != *:*:$cand_uuid:* ]] then # Highlight the differing UUID (( LEADER += ${#cand_name} + ${#cand_pvid} + 4 )) BANNER=${#cand_uuid} elif [[ -n $cand_pvid && $IDENTIFIERS != *:$cand_pvid:* ]] then # Highlight the differing PVID (( LEADER += ${#cand_name} + 2 )) BANNER=${#cand_pvid} fi if [[ -z $cand_uuid ]]; then # Remove trailing colon ERROR_MAP[$NODE]=${ERROR_MAP[$NODE]%:*([[:space:]])} fi rc=$RC_INCORRECT_INPUT fi if (( $rc > 0 )); then #============================================================== # Add the error banner, underlining the exact identifier that # is different with a row of "^^^^^"s. Hopefully this will # eliminate any confusion about why a given disk identifier # has been rejected. #============================================================== typeset -i POS=0 ERROR_MAP[$NODE]="${ERROR_MAP[$NODE]}$NL" for (( POS=0; POS 1 )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 269 '\nERROR: "%1$s" maps to different disks across %2$s:\n\n' "$DISK" "${NODES//+([[:space:]])/,}" 1>&2 #============================================================= # Loop over NODES instead of ${!ERROR_MAP[*]} to ensure that # the error entry is always displayed last, for readability. #============================================================= for NODE in $NODES; do if [[ -n ${ERROR_MAP[$NODE]} ]]; then print -u2 " $NODE: ${ERROR_MAP[$NODE]//:/, }" fi done cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 270 '\nEither select a different disk, or try specifying the disk that you want by its UUID or PVID.\n\n' 1>&2 fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "getDiskData()" #============================================================================== # # Name: trim # # Description: eliminates all leading/trailing whitespace from # the input string, and normalizes all intermediate # whitespace, converting it to single space format. # # Inputs: STRING the string that is to be trimmed. # # Outputs: The trimmed string is returned on STDOUT. # # Returns: Zero. # #============================================================================== function trim { . $HALIBROOT/log_entry "$0()" "$CL" max typeset STRING=$1 : Replace tabs, newlines, and multiple, : contiguous spaces with a single space. STRING=${STRING//+([[:space:]])/ } : Eliminate any/all leading whitespace STRING=${STRING##+([[:space:]])} : Eliminate any/all trailing whitespace STRING=${STRING%%+([[:space:]])} print -- "$STRING" log_return_msg 0 "$0()" "$LINENO" return $? } # End of "trim()" #============================================================================== # # Name: search_properties # # Description: searches the provided properties using the criteria given in # the "queries" hash. # # Inputs: queries A reference to and associative array, whose keys # indicate what field(s) to search on, and whose # values indicate what to search for. Alternatively, # this can be a reference to a simple string of # ATTR=VALUE pairs, separated by spaces. # # properties An associative array containing the bulk values # that the search is to be performed on. This data # structure will be modified by the search. # # keys An optional list of key/attribute names that are # desired to be returned (i.e. all other properties # will be dropped). # # Outputs: There are no explicit STDOUT/STDERR outputs from this function. # However, the "properties" hash will be modified. # # Returns: 0 (zero) if the search was successful. # 6 if the search failed. # #============================================================================== function search_properties { . $HALIBROOT/log_entry "$0()" "$CL" max typeset -n queries=$1 typeset -n properties=$2 typeset keys=$3 #=================================== : Declare and initialize variables #=================================== typeset -i matches=0 rc=$RC_UNKNOWN typeset attr= key= value= base= expanded_attr= typeset -u uc_key= realVal= searchVal= if [[ -z ${!queries[*]} || ${!queries[*]} == 0 ]] && \ [[ $queries == *[_0-9a-zA-Z]=* ]] then #====================================================== : Looks like a string of searches was provided, and : not a hash of searches. Convert the list to a hash. #====================================================== typeset SEARCHES=$queries unset queries typeset -A queries for value in $SEARCHES; do if [[ $value == *=* ]]; then uc_key=${value%%=*} queries[$uc_key]="${value#*=}" fi done fi #==================================================== : Extract the list of properties, grouped by index. : If specific keys were requested, only keep those. #==================================================== typeset INDEXED_PROPS="" for key in ${!properties[*]}; do if [[ $key == *_V4*([[:digit:]]) ]]; then base="${key%_V4}_V4" else base=${key%%+([0-9])} # Covers most cases if [[ $base == *+([0-9])\. ]]; then base=${base%%+(+([0-9])\.)} fi fi INDEX=${key#$base} if [[ -n $keys ]]; then for uc_key in ${keys//,/ }; do if [[ $base == $uc_key ]]; then INDEXED_PROPS[$INDEX]="${INDEXED_PROPS[$INDEX]} $key" break fi done else INDEXED_PROPS[$INDEX]="${INDEXED_PROPS[$INDEX]} $key" fi done #======================================================= : If any queries were specified, check for them now. : Throw out the data if the queries are not satisfied. #======================================================= : Loop through each value set for INDEX in ${!INDEXED_PROPS[*]}; do matches=0 # Reset for the current index if [[ $INDEX == "0" ]]; then if [[ " ${INDEXED_PROPS[$INDEX]} " != *+([[:alnum:]])0\ * ]] then INDEX="" fi fi #=============================== : Loop through all the queries #=============================== for attr in ${!queries[*]}; do value="${queries[$attr]}" CL=$LINENO expand_keyword "$attr" _VAR_SUBSTITUTIONS expanded_attr if (( $? == RC_SUCCESS )); then [[ -n $expanded_attr ]] && attr=$expanded_attr else rc=$RC_SEARCH_FAILED if [[ $_ACTION == @(list|get|query) ]]; then display_simple_help else display_operation_syntax 1 (( $? != RC_SUCCESS )) && display_simple_help fi break fi #===================================== : Check for an abbreviated attribute #===================================== uc_key=$attr if [[ -n ${COMPAT[$uc_key]} ]]; then attr=${COMPAT[$uc_key]} fi : Check for the current query in the values for this set for key in ${INDEXED_PROPS[$INDEX]}; do if [[ $key != *([[:space:]]) ]]; then if [[ $key != $attr*(S)$INDEX*(\.+(0-9))* ]] then continue fi uc_key=$key : Perform the case insensitive search realVal=${properties[$key]} searchVal=$value [[ $realVal == $searchVal ]] && (( matches++ )) fi unset key done # End of the properties/values loop done # End of the search criteria loop if (( $rc == RC_SEARCH_FAILED )); then break fi #======================================================== : If the number of matches found is not equal to the : number of searches attempted, then the search has : failed. Delete the appropriate objects from the hash. #======================================================== if (( $matches < ${#queries[*]} )); then for key in ${!properties[*]}; do if [[ $key == *[a-zA-Z]$INDEX || \ $key == *_V4$INDEX || \ $key == *[a-zA-Z]$INDEX\.+([[:digit:]])* || \ $key == *_V4$INDEX\.+([[:digit:]])* ]] then unset properties[$key] fi done unset INDEXED_PROPS[$INDEX] elif [[ -n $keys ]]; then typeset -i found=0 for key in ${!properties[*]}; do if [[ $key == *[a-zA-Z]$INDEX || \ $key == *_V4$INDEX || \ $key == *[a-zA-Z]$INDEX\.+([[:digit:]])* || \ $key == *_V4$INDEX\.+([[:digit:]])* ]] then found=0 for uc_key in ${keys//,/ }; do if [[ $key == $uc_key* ]]; then found=1 break fi done (( ! found )) && unset properties[$key] fi done fi done # End of the value set loop if (( $rc == RC_UNKNOWN )); then (( ${#properties[*]} > 0 )) && rc=$RC_SUCCESS || rc=$RC_SEARCH_FAILED fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "search_properties()" #============================================================================= # # Name: prune_indexes # # Description: Examines the provided associative array to determine # if its data represents only one object. If so, the # trailing index number on each attribute is not useful, # and just makes the data harder to use. So if there is # only one object in the data set, them the trailing # index number is removed. This also preserves backward # compatibility with the smart assists. # # Inputs: hash the associative array to be examined and, if # need be, modified # # label the label to key off of to determine the number # of objects in the array. Defaults to "NAME". # # Outputs: There are no explicit outputs from this function. # # Returns: 0 (zero), if the specified label is found in the array, # otherwise 2 (RC_NOT_FOUND). # #============================================================================= function prune_indexes { . $HALIBROOT/log_entry "$0()" "$CL" max typeset -n hash=$1 typeset label=$2 [[ -z $label ]] && label="NAME" typeset -i rc=$RC_UNKNOWN if (( ${#hash[*]} > 0 )); then typeset -i OBJECT_COUNT=0 for key in ${!hash[*]}; do [[ $key == ${label}*([[:digit:]]) ]] && (( OBJECT_COUNT++ )) (( OBJECT_COUNT > 1 )) && break done if (( OBJECT_COUNT == 1 )); then for key in ${!hash[*]}; do if [[ $key != *+([[:digit:]])\.+([[:digit:]]) && $key == *+([[:digit:]]) && $key != +([[:digit:]]) ]] then value=${hash[$key]} unset hash[$key] if [[ $key == EXPORT_FILESYSTEM_V4* ]]; then key=${key%%+([[:digit:]])} hash[${key}4]=$value else hash[${key%+([[:digit:]])}]=$value fi fi done elif (( OBJECT_COUNT == 0 )); then rc=$RC_NOT_FOUND fi else rc=$RC_SUCCESS fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "prune_indexes()" #============================================================================= # # Name: list_disks # # Description: Displays disks in the format: NAME PVID VG UUID # # Inputs: NODE Optional. The name of a node to pull the list # of disks from, using clrsh. # # Outputs: The disks are displayed on STDOUT, one disk per line, # in the format "NAME PVID VG UUID". # # NOTE: A similar function is coded in the XD file, # "xd_generic/director/base_utils/clxd_list_disks_director". # If you find a substantive need to modify this function, # please also examine that one, to see if a similar fix # is warranted. # # Returns: 0 if no comm errors were detected, otherwise non-zero. # #============================================================================= function list_disks { . $HALIBROOT/log_entry "$0()" "$CL" max typeset NODE=$1 typeset -i rc=$RC_UNKNOWN typeset -i NUM=$RANDOM typeset TF1="$TMPDIR/clmgr.fi.ld.$$.$NUM.ld1" typeset TF2="$TMPDIR/clmgr.fi.ld.$$.$NUM.ld2" typeset TF3="$TMPDIR/clmgr.fi.ld.$$.$NUM.ld3" if [[ -z $NODE || $NODE == $LOCAL_NODE ]]; then # Local node LC_ALL=C lspv -L >$TF3 if (( $? == RC_SUCCESS )); then cat $TF3 |\ ( while read hdisk pvid vgname rest; do print $hdisk $pvid $vgname done ) | sort -b -k1,1 >$TF1 # # It is necessary to set ODMDIR here for the case where ODMDIR # has been redirected to a set of temporary ODMs restored from # a snapshot in order to construct a snapshot report. If we do # not do this, then cl_get_rdisks will fail due to not being able # to find the PdDv ODM file. Of course, if ODMDIR has been set to # a specific location by some other entity, then use that value. # typeset QUERY_ODMDIR=$ODMDIR if [[ -z $ODMDIR || $ODMDIR == *clmgr.KHgca.snapshot* ]] then QUERY_ODMDIR=$DEFAULT_ODMDIR fi ODMDIR=$QUERY_ODMDIR cl_get_rdisks | sort -b -k1,1 >$TF2 rc=$? else rm -f $TF1 $TF2 fi else $CLRSH $NODE LC_ALL=C lspv -L >$TF3 2>>$CLMGR_TMPLOG if (( $? == RC_SUCCESS )); then cat $TF3 |\ ( while read hdisk pvid vgname rest; do print $hdisk $pvid $vgname done ) | sort -b -k1,1 >$TF1 $CLRSH $NODE $HAUTILS/cl_get_rdisks 2>>$CLMGR_TMPLOG | sort -b -k1,1 >$TF2 rc=$? else rm -f $TF1 $TF2 fi fi if [[ ! -s $TF2 ]] then : This is a special condition where disks from EMC/HITACHI are available : in AIX but do not have a UUID set. So $TF2 above will be empty. cat $TF1 >$TF3 else [[ -f $TF3 ]] && rm -f $TF3 if [[ -f $TF1 && -s $TF1 && -f $TF2 && -s $TF2 ]]; then join -a 1 -1 1 -2 1 $TF1 $TF2 >$TF3 fi fi : Sort the output in a more usable manner. This only works : for standard AIX disk device names, such as "hdisk##". if [[ -f $TF3 && -s $TF3 ]]; then if [[ -n $(/bin/grep -v "^hdisk" $TF3) ]]; then cat $TF3 else cat $TF3 | sort -k1.6n fi fi /usr/bin/rm -f $TF1 $TF2 $TF3 2>/dev/null log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "list_disks()" #============================================================================= # # Name: get_xml_attr # # Description: Returns a specified attributes value from a particular # element in the provided XML data. # # Inputs: ATTR The attribute whose value is to be returned. # ELEMENT The element that contains the specified attribute. # XML Either an XML file, or direct XML data. This is # what will be searched for the element/attribute. # # Outputs: If found, the attribute value will be written to STDOUT. # # Returns: 0: the attribute value was found. # 1: some sort of error occurred. # 2: the attribute value was not found. # #============================================================================= function get_xml_attr { . $HALIBROOT/log_entry "$0()" "$CL" max typeset ATTR=$1 typeset ELEMENT=$2 typeset XML=$3 typeset CONDITION=$4 #=================================== : Declare and initialize variables #=================================== typeset CHUNK="" DATA="" integer rc=$RC_UNKNOWN FOUND=0 MORE_ATTR=0 ELEMENT=${ELEMENT#\<} ELEMENT=${ELEMENT#\>} ATTR=${ATTR%%=*} if [[ -f $XML ]]; then : The specified XML data is in a file if [[ -n $CONDITION ]]; then DATA=$(grep "$CONDITION" $XML | grep -p "<$ELEMENT") else DATA=$(grep -p "<$ELEMENT" $XML) fi else : The specified XML data is in a string if [[ -n $CONDITION ]]; then DATA=$(print -- "$XML" | grep "$CONDITION" | grep -p "<$ELEMENT") else DATA=$(print -- "$XML" | grep -p "<$ELEMENT") fi fi [[ $DATA == *([[:space:]]) ]] && rc=$RC_ERROR for CHUNK in $DATA; do if [[ $CHUNK == "<$ELEMENT" ]]; then FOUND=1 elif (( FOUND )); then if [[ $CHUNK == ${ATTR}=* ]]; then #============================================================= : The following trimming needs to happen in a specific order #============================================================= CHUNK=${CHUNK#*=} CHUNK=${CHUNK#\"} CHUNK=${CHUNK%\>*} CHUNK=${CHUNK%/} if [[ $CHUNK != *\" ]]; then MORE_ATTR=1 print -n -- "$CHUNK " else print -- "${CHUNK%\"}" rc=$RC_SUCCESS fi elif (( MORE_ATTR )); then if [[ $CHUNK != *\" ]]; then print -n -- "$CHUNK " else MORE_ATTR=0 print -- "${CHUNK%\"}" rc=$RC_SUCCESS fi elif [[ $CHUNK == *\> ]]; then FOUND=0 fi fi done (( rc == RC_UNKNOWN )) && rc=$RC_NOT_FOUND log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "get_xml_attr()" #============================================================================= # # Name: show_stack # # Description: A simple utility to display the current clmgr call stack # information in a readable format. # # Inputs: LNUM This is intended to be the line number where this # function is called from (e.g. $LINENO). # # Outputs: The current clmgr call stack is displayed on STDERR in a # top-down fashion. If the bottom-up fashion is desired, # do this: print -u2 "$CLMGR_STACK" # # Returns: 0 if the stack exists; 1 if it does not. # # Usage: show_stack $LINENO # #============================================================================= function show_stack { typeset -i LNUM=$1 (( ! LNUM )) && LNUM=-9999 typeset LINE="" typeset -i LCNT=0 rc=$RC_UNKNOWN if [[ $CLMGR_STACK != *([[:space:]]) ]]; then print -n -u2 " Call stack at line #$LNUM in " LINE=$(print -- "$CLMGR_STACK" | tail -1) print -u2 "${LINE%%+([[:space:]])}:" print -- "$CLMGR_STACK" | tail -r |\ while read LINE; do (( LCNT++ == 0 )) && continue print -u2 "\t$LINE" done print -u2 rc=$RC_SUCCESS else rc=$RC_ERROR fi return $rc } # End of "show_stack()" #============================================================================ # # Name: add_caa_access # # Description: Adds a node to the CAA cluster, but not to the SystemMirror # configuration. This is needed to establish clcomd # communication with the new node, so that discovery can be # run on the new node. # # clcomd can communicate with a node if: # + prior to cluster configuration, the host name appears # in /etc/cluster/rhosts # + after cluster configureion, the host name is a known # CAA node # # Inputs: commpath [REQUIRED] [string] # The communication path of the node that is to # be added. # # site [OPTIONAL] [string] # Only needed if sites are in use. # # repos [OPTIONAL] [string] # Only needed if new site is passed. # # Outputs: There are no explicit outputs other than any error # messages that might be needed. # # Returns: The result of the CAA operation. # #============================================================================ function add_caa_access { . $HALIBROOT/log_entry "$0()" "$CL" : INPUTS: $* typeset commpath=$1 typeset site=$2 typeset repos=$3 [[ $CLMGR_LOGGING == 'med' ]] && set +x # Only trace param values #=================================== : Declare and initialize variables #=================================== typeset comm_path_name="" rest="" site_name="" cname="" typeset comm_path_ip="" integer rc=$RC_UNKNOWN host $commpath | read comm_path_name rest comm_path_ip rest comm_path_ip=${comm_path_ip%,} cname=$(clodmget -f name -n HACMPcluster) #============================================ : Set up temporary clcomd communications to : the node so that discovery can be run. #============================================ print "$0()[$LINENO]($SECONDS): use the CAA /usr/lib/cluster/incluster utility to determine if a CAA cluster exists." >>$CLMGR_TMPLOG if /usr/lib/cluster/incluster then print "$0()[$LINENO]($SECONDS): a CAA cluster was found." >>$CLMGR_TMPLOG typeset SITES="LOCAL" LINE="" if [[ $CLUSTER_TYPE == "LC" ]]; then if [[ $site == *([[:space:]]) ]]; then #============================================================ : The new node, $commpath, must be added to a site that has : access to the repository disk for that site. Since we : cannot know which site that is, we have to try them all : until chcluster works. #============================================================ SITES="" lscluster -c | grep "^Multicast for site " |\ while read LINE; do LINE=${LINE%%:*} LINE=${LINE##* } [[ -n $LINE ]] && SITES="$SITES $LINE" done [[ $SITES == *([[:space:]]) ]] && SITES="LOCAL" else SITES="$site" # Use the specified site fi fi #=============================================== : There is already a CAA cluster in place. The : communication path must be added to the CAA : cluster in order to run discovery. #=============================================== if ! LC_ALL=C lscluster -c 2>/dev/null |\ grep -q "Primary IP address for node ${comm_path_name}[.|:]" then print "$0()[$LINENO]($SECONDS): the specified node, $commpath, needs to be added to the CAA cluster." >>$CLMGR_TMPLOG typeset site_parm="" typeset repos_parm="" typeset chcluster_out="" for SITE in $SITES; do print "$0()[$LINENO]($SECONDS): LC_ALL=C lscluster -c | grep -q \"Primary IP address for node ${comm_path_name}:\"" >> $CLMGR_TMPLOG if [[ $SITE == "LOCAL" ]]; then site_parm="" # CAA defaults to site the local node is in elif [[ $CLUSTER_TYPE == "LC" ]] && (( $(LC_ALL=C lscluster -c 2>/dev/null | grep "Multicast for site" | wc -l) == 1 )); then # We are here because cluster type migration from standard to Linked is in progress site_parm=" -S $SITE" repos_parm="-r $repos" else site_parm=" -S $SITE" # Try this specific site fi print "$0()[$LINENO]($SECONDS): CLUSTER_OVERRIDE=yes chcluster -n $cname$site_parm -m +$comm_path_name $repos_parm" >> $CLMGR_TMPLOG chcluster_out=$(CLUSTER_OVERRIDE=yes chcluster -n $cname$site_parm -m +$comm_path_name $repos_parm 2>&1) rc=$? print "$0()[$LINENO]($SECONDS): chcluster RC: $rc" >> $CLMGR_TMPLOG if (( $rc == RC_SUCCESS )); then break else print "$0()[$LINENO]($SECONDS): failed to add node \"$commpath\" to site \"$SITE\"." >>$CLMGR_TMPLOG fi done if (( $rc != RC_SUCCESS )); then : Could not add new node to the existing CAA cluster print "${chcluster_out}" | tee -a $CLMGR_TMPLOG fi else : "$comm_path_name" is already in the CAA cluster print "$0()[$LINENO]($SECONDS): the specified node, $commpath, is already in the CAA cluster." >>$CLMGR_TMPLOG rc=$RC_EXTRANEOUS fi else #====================================================== : There is no CAA cluster in place. Make sure the : given communication path is in /etc/cluster/rhosts. #====================================================== print "$0()[$LINENO]($SECONDS): there is no CAA cluster." >>$CLMGR_TMPLOG print "$0()[$LINENO]($SECONDS): verify that the new node, \"$comm_path_name\", is in /etc/cluster/rhosts." >>$CLMGR_TMPLOG if ! egrep -q "^$commpath|^$comm_path_name|^$comm_path_ip" /etc/cluster/rhosts then print "$0()[$LINENO]($SECONDS): the $comm_path_name communication path is not defined. Adding it." >>$CLMGR_TMPLOG print -- "$comm_path_name" >> /etc/cluster/rhosts rc=$? else print "$0()[$LINENO]($SECONDS): the $comm_path_name communication path is already defined." >>$CLMGR_TMPLOG rc=$RC_SUCCESS # The communication path is defined fi fi #================================== : Test to see if the above worked #================================== if (( $rc == $RC_SUCCESS )); then if /usr/lib/cluster/incluster then if LC_ALL=C lscluster -c 2>/dev/null |\ grep -q "Primary IP address for node ${comm_path_name}[.|:]" then : Add a communication thread for the new node to clcomd. print "$0()[$LINENO]($SECONDS): refresh -s clcomd" >>$CLMGR_TMPLOG refresh -s clcomd >>$CLMGR_TMPLOG 2>&1 rc=$? print "$0()[$LINENO]($SECONDS): refresh RC: $rc" >>$CLMGR_TMPLOG (( rc != RC_SUCCESS )) && rc=$RC_ERROR if (( rc == $RC_SUCCESS )); then #========================================================= : It can sometimes take a bit of time for communication : with a new node to start working, so loop for a bit to : test it. #========================================================= typeset -i try=0 tries=10 pause=6 while (( try < tries )); do (( try++ )) print -n "Node \"$comm_path_name\" communication test #$try... " >>$CLMGR_TMPLOG $CLRSH $comm_path_name /bin/hostname >>$CLMGR_TMPLOG 2>&1 if (( $? == RC_SUCCESS )); then print "PASS" >>$CLMGR_TMPLOG rc=$RC_SUCCESS break fi $CLRSH $commpath /bin/hostname >>$CLMGR_TMPLOG 2>&1 if (( $? == RC_SUCCESS )); then print "PASS" >>$CLMGR_TMPLOG rc=$RC_SUCCESS break fi print "FAIL" >>$CLMGR_TMPLOG rc=$RC_ERROR sleep $pause done fi else : Looks like chcluster failed to add the node for some reason rc=$RC_ERROR fi fi elif (( rc != RC_EXTRANEOUS )); then : Either chcluster failed, or could not write to the rhosts file rc=$RC_ERROR fi if (( rc == RC_ERROR )); then : Make sure to clean up if [[ -n $repos ]]; then CL=$LINENO rm_caa_access $commpath $site >>$CLMGR_TMPLOG 2>&1 else CL=$LINENO rm_caa_access $commpath >>$CLMGR_TMPLOG 2>&1 fi fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "add_caa_access()" #============================================================================ # # Name: rm_caa_access # # Description: Removes a node from the CAA cluster (not from the # SystemMirror configuration). # # Inputs: commpath [REQUIRED] [string] # The communication path of the node that is to # be removed. # # site [OPTIONAL] [string] # Only needed if new site is in use. # # Outputs: There are no explicit outputs other than any error # messages that might be needed. # # Returns: The result of the CAA operation. # #============================================================================ function rm_caa_access { . $HALIBROOT/log_entry "$0()" "$CL" : INPUTS: $* typeset commpath=$1 typeset site=$2 [[ $CLMGR_LOGGING == 'med' ]] && set +x # Only trace param values #=================================== : Declare and initialize variables #=================================== typeset comm_path_name="" rest="" cname="" integer rc=$RC_UNKNOWN i=0 typeset chcluster_out="" host $commpath | read comm_path_name rest cname=$(clodmget -f name -n HACMPcluster) if /usr/lib/cluster/incluster then if LC_ALL=C lscluster -c 2>/dev/null |\ grep -q "Primary IP address for node ${comm_path_name}[.|:]" then #================================================= : Remove the specified node from the CAA cluster #================================================= if [[ -n $site ]]; then # We come here, in case if we are in conversion of cluster type to linked. # Last node from a site cannot be removed using chcluster, so use rmcluster. print "$0()[$LINENO]($SECONDS): CLUSTER_OVERRIDE=yes rmcluster -S $site" >> $CLMGR_TMPLOG chcluster_out=$(CLUSTER_OVERRIDE=yes rmcluster -S $site) rc=$? else print "$0()[$LINENO]($SECONDS): CLUSTER_OVERRIDE=yes chcluster -n $cname -m -${comm_path_name}" >> $CLMGR_TMPLOG chcluster_out=$(CLUSTER_OVERRIDE=yes chcluster -n $cname -m -${comm_path_name}) rc=$? fi print "$0()[$LINENO]($SECONDS): RC: $rc" >> $CLMGR_TMPLOG print "${chcluster_out}" | tee -a $CLMGR_TMPLOG (( rc != RC_SUCCESS )) && rc=$RC_ERROR clusterconf -v 2>&1 >> $CLMGR_TMPLOG #============================================================= : Clear the communication thread for this node from clcomd. : For node removals, the full stop/start approach has proven : to be more reliable and effective than a simple refresh. #============================================================= print "stopsrc -s clcomd" >>$CLMGR_TMPLOG stopsrc -s clcomd >>$CLMGR_TMPLOG 2>&1 for (( i=0; i<5; i++ )); do if LC_ALL=C lssrc -s clcomd | grep -q inoperative; then break fi sleep 1 done if ! LC_ALL=C lssrc -s clcomd | grep -q inoperative; then print "stopsrc -cs clcomd" >>$CLMGR_TMPLOG stopsrc -cs clcomd >>$CLMGR_TMPLOG 2>&1 # Try a more forceful method sleep 2 fi print "startsrc -s clcomd" >>$CLMGR_TMPLOG startsrc -s clcomd >/dev/null >>$CLMGR_TMPLOG 2>&1 for (( i=0; i<5; i++ )); do if LC_ALL=C lssrc -s clcomd | grep -q active; then break fi sleep 1 done sleep 2 else : "$comm_path_name" is already not in the CAA cluster rc=$RC_SUCCESS fi fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "rm_caa_access()" #============================================================================= # # Name: verify_is_numeric # # Description: examines the provided value to determine if it is numeric. # Using the optional "range" parameter, you can limit what is # deemed an acceptable numeric value (i.e. non-neg, neg only, # pos only). # # Inputs: value The value that is to be validated. # # range Optional. If not specified, any valid numeric value # is accepted, including a single decimal point. # Otherwise, here are the recognized values: # # -2 Only allow a negative number. # -1 Allow a negative or a zero. # 0 Allow a zero only. # 1 Allow a zero or a positive. # 2 Only allow a positive number. # # x10 Any of the above values multiplied by ten # indicates that non-whole numbers are okay. # # attr Optional. This represents the name of the input # flag/option, and can be used to remove ambiguity # from the error messages. If this option is used, # "range" must be provided, even if it is just an # empty/null value. # # Outputs: If any problems are found, they will be written to STDERR. # If you prefer to use your own error messages, eschewing # the built-in messages, simply append "2>/dev/null" after # the function invocation. # # No output will be written to STDOUT. # # Returns: Zero is returned if the value is successfully validated. # Otherwise, an appropriate non-zero value is returned. # #============================================================================= function verify_is_numeric { . $HALIBROOT/log_entry "$0()" "$CL" max typeset value=$1 typeset range=$2 typeset attr=$3 typeset -i rc=$RC_UNKNOWN typeset ERRMSG="" case $range in -20) if [[ $value != \-+([[:digit:]])?(\.+([[:digit:]])) ]]; then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 49 '\nERROR: "%1$s" is not a negative number.\n\n' "$value") fi ;; -10) if [[ $value != \-+([[:digit:]])?(\.+([[:digit:]])) && \ $value != 0 ]] then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 50 '\nERROR: "%1$s" is neither a negative number, nor a zero.\n\n' "$value") fi ;; -2) if [[ $value != \-+([[:digit:]]) ]]; then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 49 '\nERROR: "%1$s" is not a negative number.\n\n' "$value") fi ;; -1) if [[ $value != \-+([[:digit:]]) && \ $value != 0 ]] then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 51 '\nERROR: "%1$s" is neither a negative whole number, nor a zero.\n\n' "$value") fi ;; 0) if [[ $value != 0 ]]; then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 52 '\nERROR: "%1$s" is not zero (zero was expected).\n\n' "$value") fi ;; 1) if [[ $value != +([[:digit:]]) && \ $value != 0 ]] then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 53 '\nERROR: "%1$s" is neither a positive whole number, nor a zero.\n\n' "$value") fi ;; 2) if [[ $value != +([[:digit:]]) || \ $value == 0 ]] then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 55 '\nERROR: "%1$s" is not a positive number.\n\n' "$value") fi ;; 10) if [[ $value != +([[:digit:]])?(\.+([[:digit:]])) && \ $value != 0 ]] then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 54 '\nERROR: "%1$s" is neither a positive number, nor a zero.\n\n' "$value") fi ;; 20) if [[ $value != +([[:digit:]])?(\.+([[:digit:]])) || \ $value == 0 ]] then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 55 '\nERROR: "%1$s" is not a positive number.\n\n' "$value") fi ;; "") if [[ $value != +([[:digit:]])?(\.+([[:digit:]])) && \ $value != \-+([[:digit:]])?(\.+([[:digit:]])) && \ $value != 0 ]] then ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 48 '\nERROR: "%1$s" is not a numeric value.\n\n' "$value") fi ;; *) ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 56 '\nERROR: internal error in verify_is_numeric(): invalid "range" value: "%1$s"\n\n' "$range") ;; esac if [[ -n $ERRMSG ]]; then [[ -n $attr ]] && ERRMSG=${ERRMSG/@($value\" )/\1($attr=\"$value\") } print -u2 "$ERRMSG" rc=$RC_INCORRECT_INPUT elif (( rc == RC_UNKNOWN )); then rc=$RC_SUCCESS fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "verify_is_numeric()" #============================================================================== # # Name: verify_numeric_range # # Description: examines the provided value to determine if it is numeric, # falls within the range of the provided lower and upper values, # inclusive. So if the lower boundary is 5 and the upper bound # is 8, then a value of 5, 6, 7, or 8 would pass the test. Any # other value would result in an appropriate error message and # non-zero return code. # # Inputs: value The numeric value to be verified. # # lower The lowest value that is acceptable. # # upper The highest value that is acceptable. # # attr Optional. The name of the input that the user provided # the value within. # # Outputs: If any problems are found, they will be written to STDERR. # If you prefer to use your own error messages, eschewing # the built-in messages, simply append "2>/dev/null" after # the function invocation. # # No output will be written to STDOUT. # # Returns: Zero is returned if the value is successfully validated. # Otherwise, an appropriate non-zero value is returned. # #============================================================================== function verify_numeric_range { . $HALIBROOT/log_entry "$0()" "$CL" max typeset value=$1 typeset -i lower=$2 typeset -i upper=$3 typeset attr=$4 CL=$LINENO verify_is_numeric "$value" "" $attr rc=$? if (( rc == RC_SUCCESS )); then if (( lower > upper )); then typeset num=$lower lower=$upper upper=$num unset num fi if (( $value >= $lower && $value <= $upper )); then rc=$RC_SUCCESS else typeset ERRMSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 57 '\nERROR: "%1$s" is not in the range of %2$s .. %3$s.\n\n' "$value" "$lower" "$upper") if [[ "$attr" == "NETWORK_FAILURE_DETECTION_TIME" ]]; then ERRMSG="$ERRMSG$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1141 'ERROR: Network Failure Detection value should be at least 5 seconds or 0 (unset).\n\n')" fi [[ -n $attr ]] && ERRMSG=${ERRMSG/@($value\" )/\1($attr=\"$value\") } print -u2 "$ERRMSG" rc=$RC_INCORRECT_INPUT fi fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "verify_numeric_range()" #============================================================================= # # Name: verify_is_IPv4_address # # Description: examines the provided value to determine if it is a # well-formed IPv4 address. # # Inputs: value The value that is to be validated. # # Outputs: If any problems are found, they will be written to STDERR. # No output will be written to STDOUT. # # Returns: Zero is returned if the value is successfully validated. # Otherwise, an appropriate non-zero value is returned. # #============================================================================= function verify_is_IPv4_address { . $HALIBROOT/log_entry "$0()" "$CL" max typeset value=$1 typeset -i rc=$RC_UNKNOWN : Check echo octet for a numeric value, in the valid IPv4 range typeset -i octet_count=0 for OCTET in ${value//\./ }; do (( octet_count++ )) CL=$LINENO verify_is_numeric "$OCTET" 1 if (( $? != 0 )); then (( rc == RC_UNKNOWN )) && rc=$RC_INCORRECT_INPUT else CL=$LINENO verify_numeric_range "$OCTET" 0 255 (( $? != 0 && rc == RC_UNKNOWN )) && rc=$RC_INCORRECT_INPUT fi done : An IPv4 address must have exactly four octets (( octet_count != 4 )) && rc=$RC_INCORRECT_INPUT : If no problems were detected, report a successful validation of $value. if (( rc == RC_UNKNOWN )); then rc=$RC_SUCCESS else cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 58 '\nERROR: invalid IPv4 address: "%1$s". A valid IPv4 address must have exactly four octets, "#.#.#.#", each in the range of 0 to 255.\n\n' "$value" 1>&2 fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "verify_is_IPv4_address() #============================================================================= # # Name: verify_is_multicast_address # # Description: examines the provided value to determine if it is a # well-formed multicast address. # # Inputs: value The value that is to be validated. # # Outputs: If any problems are found, they will be written to STDERR. # No output will be written to STDOUT. # # Returns: Zero is returned if the value is successfully validated. # Otherwise, an appropriate non-zero value is returned. # #============================================================================= function verify_is_multicast_address { . $HALIBROOT/log_entry "$0()" "$CL" max typeset value=$1 typeset -i rc=$RC_UNKNOWN typeset MC_RESERVED="\ # 224.0.0.0 Base address (reserved) # 224.0.0.1 The All Hosts multicast group that contains all systems on the same network segment # 224.0.0.2 The All Routers multicast group that contains all routers on the same network segment # 224.0.0.5 The Open Shortest Path First (OSPF) AllSPFRouters address. Used to send Hello packets to all OSPF routers on a network segment # 224.0.0.6 The OSPF AllDRouters address. Used to send OSPF routing information to OSPF designated routers on a network segment # 224.0.0.9 The RIP version 2 group address. Used to send routing information using the RIP protocol to all RIP v2-aware routers on a network segment # 224.0.0.10 EIGRP group address. Used to send EIGRP routing information to all EIGRP routers on a network segment # 224.0.0.13 PIM Version 2 (Protocol Independent Multicast) # 224.0.0.18 Virtual Router Redundancy Protocol # 224.0.0.19-21 IS-IS over IP # 224.0.0.22 IGMP Version 3 (Internet Group Management Protocol) # 224.0.0.102 Hot Standby Router Protocol Version 2 # 224.0.0.251 Multicast DNS address # 224.0.0.252 Link-local Multicast Name Resolution address # 224.0.1.1 Network Time Protocol address # 224.0.1.39 Cisco Auto-RP-Announce address # 224.0.1.40 Cisco Auto-RP-Discovery address # 224.0.1.41 H.323 Gatekeeper discovery address" CL=$LINENO verify_is_IPv4_address "$value" (( $? != RC_SUCCESS )) && rc=$RC_INCORRECT_INPUT #======================================== : See if this is in the multicast range #======================================== typeset first_octet=${value%%\.*} if [[ $first_octet == +([[:digit:]]) ]]; then CL=$LINENO verify_numeric_range $first_octet 224 239 if (( $? != 0 )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 410 '\nERROR: invalid multicast IP address: "%1$s". Multicast addresses must be in the range 224.0.0.0 - 239.255.255.255\n\n' "$value" 1>&2 rc=$RC_INCORRECT_INPUT fi fi #========================================== : Check for the use of a reserved address #========================================== print -- "$MC_RESERVED" |\ while read LINE; do [[ $LINE != \#\ 224* ]] && continue typeset RESERVED=${LINE#* } RESERVED=${RESERVED%% *} typeset DESC=${LINE#* } DESC=${DESC#*+([[:space:]])} if [[ $value == $RESERVED ]]; then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 412 '\nERROR: multicast IP address "%1$s" is reserved: %2$s.\n\n' "$value" "$DESC" 1>&2 rc=$RC_INCORRECT_INPUT break fi done # End of the reserved multicast address loop #============================================ : Make sure this is not a duplicate address #============================================ if (( $rc == RC_UNKNOWN )); then typeset EXISTING=$(CL=$LINENO trim "$(clodmget -n -f ip_address HACMPsircol)") if [[ " $EXISTING " == *\ $value\ * ]]; then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 430 '\nERROR: multicast IP address "%1$s" is already in use.\n\n' "$value" 1>&2 rc=$RC_INCORRECT_INPUT fi fi : If no problems were detected, report a successful validation of $value. (( rc == RC_UNKNOWN )) && rc=$RC_SUCCESS log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "verify_is_multicast_address()" #============================================================================= # # Name: verify_is_absolute_path # # Description: This common, utility validation function may be used to # verify that the provided value is in absolute path format. # Optionally, if an input attribute was used, that can be # specified to make the error messages a little more useful. # Also, if desired, the value can be checked for actual # existence on the local node. # # Inputs: value The value that is to be validated. # # exists A boolean value indicating if an existence check # should also be performed. Defaults to true. # # attr Optional. The name of the attribute being validated. # # Outputs: Any problems/errors found will be displayed to STDERR. # # Returns: Zero if no validation problems were discovered, otherwise # an appropriate non-zero value will be returned. # #============================================================================= function verify_is_absolute_path { . $HALIBROOT/log_entry "$0()" "$CL" max typeset value=$1 typeset -l exists=$2 typeset attr=$3 typeset -i rc=$RC_UNKNOWN [[ -z $exists || $exists != @(f|n)* ]] && exists=1 || exists=0 if [[ $value != *([[:space:]]) ]]; then if [[ $value != /* ]]; then MSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 106 '\nERROR: the specified path does not appear to be in absolute format:\n%1$s\n\n' "$value") MSG="$MSG ($attr @ $(LOCAL_NODE))" cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS -1 "${MSG//+([[:space:]])/ }" 1>&2 rc=$RC_INCORRECT_INPUT fi if [[ $value == *[[:space:]]* ]]; then MSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 186 '\nERROR: "%1$s" contains whitespace, which is not allowed.\n\n' "$value") MSG=${MSG%%+([[:space:]])} [[ -n $attr ]] && MSG="$MSG ($attr)" print -u2 "$MSG\n" rc=$RC_INCORRECT_INPUT fi if (( exists )); then if [[ ! -e $value ]]; then MSG=$(cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 107 '\nERROR: the specified path/file does not appear to exist on "%2$s": %1$s\n\n' "$value" "$LOCAL_NODE") MSG=${MSG%%+([[:space:]])} [[ -n $attr ]] && MSG="$MSG ($attr)" print -u2 "$MSG\n" rc=$RC_NOT_FOUND fi fi (( rc == RC_UNKNOWN )) && rc=$RC_SUCCESS else rc=$RC_INCORRECT_INPUT fi : If no problems were detected, report a successful validation of $value. (( rc == RC_UNKNOWN )) && rc=$RC_SUCCESS log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "verify_is_absolute_path()" #============================================================================= # # Name: verify_in_set # # Description: This common, utility validation function may be used to # verify that the provided value is found in a provided, # finite set of acceptable values. This verification is # performed in a case-insensitive manner, for improved # usability. An optional return vector is provided (via # arg #4, below), to expand partial inputs to their full # and valid value. So, as an example, if the user types # in a simple "f" for a Boolean input, but the value must # be "False", in camelcase, then using the fourth parameter # would allow that "False" to be returned. Here is a usage # example: # # verify_in_set A_BOOL "$a_bool" "True, False" a_bool # # If the user enters a matching value, then "A_BOOL" will # be overwritten with either "True" or "False". If the # input does not match the set, then an error message will # be emitted on STDERR, and "A_BOOL" will remain unchanged. # It should be noted that the 4th parameter need not match # the variable name used in the 2nd parameter. It is simply # convenient to do so. However, the 4th parameter can be any # variable name you like. # # Last, the user-provided input is also checked against the # entire possible valid set to ensure that it is not # ambiguous. For example, if the user enters "n", and the # valid set includes both "node" and "network"... which one # did they mean? No way to know! So an appropriate error # message is displayed, and an error code is returned. # # Inputs: attr The name of the input variable. Used strictly # for providing a more useful error message. # value The actual value provided by the customer, # and stored in the variable named in "$attr". # valid_set The set of acceptable values for "$attr", in # a case-sensitive format (only matters if the # 4th input parameter is used). # valid_value A reference to a string variable, in which the # matching, case-sensitive value from "$valid_set" # will be placed/returned. # # Outputs: Any problems/errors found will be displayed on STDERR. # If a proper reference is provided to "valid_value", then # it will be used to return the matching set value, if any. # # Returns: Zero if no validation problems were discovered, otherwise # an appropriate non-zero value will be returned. # #============================================================================= function verify_in_set { . $HALIBROOT/log_entry "$0()" "$CL" max typeset attr=$1 typeset value=$2 typeset valid_set=${3//[,:\|]/ } if [[ -n $4 ]]; then nameref valid_value=$4 else typeset valid_value="" fi typeset -i rc=$RC_UNKNOWN typeset set_value="" ambiguous="" typeset -l ACCEPTED="" lc_value="" if [[ $value != *([[:space:]]) ]]; then lc_value="$value" # Make sure to do case insensitive validation for set_value in $valid_set; do ACCEPTED=$set_value # Make sure to do case insensitive validation if [[ $ACCEPTED == $lc_value* ]]; then if (( rc == $RC_UNKNOWN)); then valid_value="$set_value" rc=$RC_SUCCESS else #=================================================== : Already thought we had a matching value. That : means the customer provide an ambiguous entry. : Like "n", which could match "node" or "network". #=================================================== if [[ -n $ambiguous ]]; then ambiguous="$ambiguous, $set_value" else ambiguous="$valid_value, $set_value" fi valid_value="$value" # Restore the original value rc=$RC_INCORRECT_INPUT fi fi done # End of set values loop if (( rc != RC_SUCCESS )); then if [[ -n $ambiguous ]]; then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 60 '\nERROR: "%1$s" is an ambiguous value for "%2$s", and could match any of these possible values:\n\n' "$value" "$attr" 1>&2 print -u2 "\t\t$ambiguous\n" else lc_value=$(CL=$LINENO trim "$valid_set") /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 '\nERROR: invalid value specified for "%1$s": "%2$s".\n' "$attr" "$value" 1>&2 /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 'Valid values: %1$s\n\n' "${lc_value//+([[:space:]])/, }" 1>&2 fi fi else rc=$RC_INCORRECT_INPUT fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "verify_in_set()" #============================================================================= # # Name: verify_disk # # Description: Using the logic from the C-SPOC "clgetpvid" utility, this # function checks the first few sectors of the provided disk, # looking for signatures that indicate that it might be in # use as a raw, Oracle ASM (R) disk. If so, it should not be # used. # # Inputs: DISK Required. The name, PVID, or UUID of the disk that # is to be validated. # # NODE Optional. The specific node to perform the validation # on. Defaults to the reference node returned by the # getDiskData() function. # # Outputs: Any problems encountered that render the disk unusable will # be displayed on STDERR. # # Returns: 0 (zero) if the disk can be used, or non-zero if it can't. # #============================================================================= function verify_disk { . $HALIBROOT/log_entry "$0()" "$CL" max typeset DISK=$1 # Required typeset NODE=$2 # Optional integer MINSIZE=0 integer MAXSIZE=0 (( $# > 2 )) && [[ $3 == +([0-9]) ]] && MINSIZE=$3 # Optional (( $# > 3 )) && [[ $4 == +([0-9]) ]] && MAXSIZE=$4 # Optional typeset ALLOW_IN_USE="" (( $# > 4 )) && [[ -n $5 ]] && ALLOW_IN_USE=$5 # Optional #=================================== : Declare and initialize variables #=================================== integer rc=$RC_UNKNOWN integer cmd_rc=$RC_UNKNOWN typeset ineligible_disk_criteria="" typeset IDENTIFIERS="" ORIG_DISK="$DISK" typeset NODES=$NODE typeset DEVNAME="" PVID="" UUID="" REFNODE="" typeset offset="" xlate="" content="" skip="" typeset LQUERYPV_DATA="" if [[ -z $DISK ]]; then rc=$RC_MISSING_INPUT else [[ -z $NODES ]] && NODES=$(clnodename) CL=$LINENO getDiskData "$DISK" "${NODES//+([[:space:]])/ }" IDENTIFIERS $ALLOW_IN_USE cmd_rc=$? if (( $cmd_rc == RC_SUCCESS )); then print -- "$IDENTIFIERS" |\ IFS=: read DEVNAME PVID UUID REFNODE skip fi if (( $cmd_rc == RC_SUCCESS )) && [[ -n $DEVNAME ]]; then if [[ -s $HAETC/cl_disk_tags.lst ]] then #============================================================= : Disks that match the criteria mentioned in the file should : not be considered to be available for use in any way. #============================================================= ineligible_disk_criteria=${TMPDIR}/clgetpvid_idc.$$ grep -v '^#' $HAETC/cl_disk_tags.lst |\ sort -b -k1,1 |\ cut -f1,2 -d' ' > $ineligible_disk_criteria #============================================================= : Check to see if this disk has a marker on it that makes it : ineligible for normal use. # # The careful reader will note that the following algorithm # assumes that the field to be searched for starts on a 16 # byte boundary, and is no more than 16 bytes long, and is a # character - as opposed to hex - string. This works, as of # this writing, for disks used by Oracle ASM (R). #============================================================= if [[ $REFNODE == $LOCAL_NODE ]]; then LQUERYPV_DATA=$(lquerypv -h /dev/$DEVNAME) else LQUERYPV_DATA=$($CLRSH $REFNODE "/usr/sbin/lquerypv -h /dev/$DEVNAME" 2>>$CLMGR_TMPLOG) fi print -- "$LQUERYPV_DATA" |\ join -1 1 -2 1 -o 2.1,1.2,2.6 $ineligible_disk_criteria - |\ while read offset content xlate do xlate=${xlate##\|} if [[ $xlate == ${content}* ]] then #=========================================== : This is a protected disk. Do not use it. #=========================================== typeset ALTERNATE=$PVID [[ $ORIG_DISK != $PVID ]] && ALTERNATE=$DEVNAME cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 190 '\nERROR: the specified disk, "%1$s" ("%2$s"), is not eligible for use.\n\n' "$ORIG_DISK" "$ALTERNATE" 1>&2 rc=$RC_ERROR break fi done if (( $rc == RC_UNKNOWN )) && (( MINSIZE > 0 )) then integer DISK_SIZE=0 if [[ $REFNODE == $LOCAL_NODE ]]; then DISK_SIZE=$(getconf DISK_SIZE /dev/$DEVNAME) else DISK_SIZE=$($CLRSH $REFNODE "/usr/bin/getconf DISK_SIZE /dev/$DEVNAME" 2>>$CLMGR_TMPLOG) fi (( ! MAXSIZE )) && MAXSIZE=$REPOS_MAX_SIZE if (( DISK_SIZE < MINSIZE || DISK_SIZE > MAXSIZE )) then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 190 '\nERROR: the specified disk, "%1$s" ("%2$s"), is not eligible for use.\n\n' "$DEVNAME" "$ORIG_DISK" 1>&2 cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 199 'ERROR: the specified disk, "%1$s" ("%2$s"), is %3$d MB in size, but must be between %4$d MB and %5$d MB in size.\n\n' "$DEVNAME" "$ORIG_DISK" "$DISK_SIZE" "$MINSIZE" "$MAXSIZE" 1>&2 rc=$RC_INCORRECT_INPUT fi fi #========================================================== : If no problems have been found, then this disk is valid #========================================================== (( $rc == RC_UNKNOWN )) && rc=$RC_SUCCESS else rc=$RC_SUCCESS fi else rc=$cmd_rc fi fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "verify_disk()" #################################################################################################### # This function checks connectivity of a node with a NovaLink. # This function checks is the node for which we want to perform the check is the local node or not, # and depending on that generate the right command to be run. # To check connectivity an attempt to run through ssh the command 'date' on the NovaLink is performed. # # Parameters # $1 is node name, the node for which we want to perform the check. # $2 is NovaLink label, the NovaLink we want to check the access through ssh. # $3 is nodes list containing node for which connectivity is ok, this list is built and # returned to the caller. # $4 is Username of the Novalink that we want to access. # $5 is Password of the Novalink that we want to access. # # Output # 0 if connectivity is ok, or was not checked. # 1 if connectivity is not ok. ##################################################################################################### function check_nova_connectivity_with_node { . $HALIBROOT/log_entry "$0()" "$CL" : INPUTS: $* typeset NODE=${1//\"/} NODE=${NODE//,/ } typeset NOVA_LABEL=${2//\"/} typeset -n NODES_LIST=$3 typeset NOVA_USER=${4//\"/} typeset NOVA_PSSWD=${5//\"/} typeset -i CHECK_NOVA_ERROR=0 #==================================================================================== : We do not check connectivity the same way for the local node and a remote node. : To be able to check connectivity between a remote node and the nova we must be able : to perform cl_rsh on the remote node. #==================================================================================== if [[ $LOCAL_NODE != $NODE ]]; then REMOTECOMMAND="cl_rsh $NODE" $REMOTECOMMAND date >>$CLMGR_TMPLOG 2>&1 if (( $? != RC_SUCCESS )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1150 "\nCannot verify connectivity between \"%1\$s\" node and \"%2\$s\" NovaLink, as \n either /etc/cluster/rhosts of \"%3\$s\" node does not contain authorization to execute commands from \"%4\$s\"\n or clcomd service not started on nodes of cluster.\n" "$NODE" "$NOVA_LABEL" "$NODE" "$LOCAL_NODE" 1>&2 cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1154 "\tConnectivity between \"%1\$s\" node and \"%2\$s\" NovaLink : failed\n" "$NODE" "$NOVA_LABEL" 1>&2 CHECK_NOVA_ERROR=1 fi else REMOTECOMMAND="" fi if (( CHECK_NOVA_ERROR == 0 )); then #=============================================================== : Validate against hostname, and IP address, either IPv4 or IPv6 : TO BE DONE in next iteration #=============================================================== #=================================================== : Attempt to communicate with the specified NovaLink #=================================================== print -- "$REMOTECOMMAND ssh -l $NOVA_USER -o BatchMode=yes -o ConnectTimeout=3 $NOVA_LABEL date" >>$CLMGR_TMPLOG $REMOTECOMMAND ssh -l $NOVA_USER -o BatchMode=yes -o ConnectTimeout=3 $NOVA_LABEL date >>$CLMGR_TMPLOG 2>&1 if (( $? != RC_SUCCESS )); then if (( $CLMGR_FORCE )); then #==================================================================================== : It is possible to force, in that case if connectivity fails we consider we continue anyway #==================================================================================== cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1151 "\nWARNING: could not communicate with NovaLink \"%1\$s\" via SSH from node \"%2\$s\". Check \"%1\$s\" is valid. Check the public key for \"%2\$s\" installed correctly on \"%1\$s\". Check for any network issues or name resolution problems or firewall problems.\n" "$NOVA_LABEL" "$NODE" 1>&2 cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1152 "\tChecking NovaLink connectivity between \"%1\$s\" node and \"%2\$s\" NovaLink : failed! But we force!\n" "$NODE" "$NOVA_LABEL" 1>&2 NODES_LIST="${NODES_LIST:+$NODES_LIST }${NODE%%\.*}" else #======================================================== : If we do not force, then if connectivity fails, we fail #======================================================== cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1153 "\nERROR: could not communicate with NovaLink \"%1\$s\" via SSH from node \"%2\$s\". Check \"%1\$s\" is valid. Check the public key for \"%2\$s\" installed correctly on \"%1\$s\". Check for any network issues or name resolution problems or firewall problems.\n" "$NOVA_LABEL" "$NODE" 1>&2 rc=$RC_ERROR cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1154 "\tConnectivity between \"%1\$s\" node and \"%2\$s\" NovaLink : failed\n" "$NODE" "$NOVA_LABEL" 1>&2 CHECK_NOVA_ERROR=1 fi else cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1155 "\tConnectivity between \"%1\$s\" node and \"%2\$s\" NovaLink : success\n" "$NODE" "$NOVA_LABEL" 1>&2 NODES_LIST="${NODES_LIST:+$NODES_LIST }${NODE%%\.*}" fi fi log_return_msg "$rc" "$0()" "$LINENO" return $? } #################################################################################################### # This function checks connectivity of a node with an HMC. # This function checks is the node for which we want to perform teh check is the local node or not, # and depending on that generate the right command to be run. # To check connectivity an attempt to run through ssh the command 'date' on the HMC is performed. # # Parameters # $1 is node name, the node for which we want to perform the check. # $2 is hmc label, the hmc we want to check the access through ssh. # $3 is nodes list containing node for which connectivity is ok, this list is built and # returned to the caller. # # Output # 0 if connectivity is ok, or was not checked. # 1 if connectivity is not ok. ##################################################################################################### function check_hmc_connectivity_with_node { . $HALIBROOT/log_entry "$0()" "$CL" : INPUTS: $* typeset NODE=${1//\"/} NODE=${NODE//,/ } typeset HMC_LABEL=${2//\"/} typeset -n NODES_LIST=$3 typeset HMC_USER=${4//\"/} typeset HMC_PSSWD=${5//\"/} typeset -i CHECK_HMC_ERROR=0 #==================================================================================== : We do not check connectivity the same way for the local node and a remote node. : To be able to check connectivity between a remote node and the hmc we must be able : to perform cl_rsh on the remote node. #==================================================================================== if [[ $LOCAL_NODE != $NODE ]]; then REMOTECOMMAND="cl_rsh $NODE" $REMOTECOMMAND date >>$CLMGR_TMPLOG 2>&1 if (( $? != RC_SUCCESS )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 814 "\nCannot verify connectivity between \"%1\$s\" node and \"%2\$s\" HMC, as \n either /etc/cluster/rhosts of \"%3\$s\" node does not contain authorization to execute commands from \"%4\$s\"\n or clcomd service not started on nodes of cluster.\n" "$NODE" "$HMC_LABEL" "$NODE" "$LOCAL_NODE" 1>&2 cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 816 "\tChecking HMC connectivity between \"%1\$s\" node and \"%2\$s\" HMC : failed!\n" "$NODE" "$HMC_LABEL" 1>&2 CHECK_HMC_ERROR=1 fi else REMOTECOMMAND="" fi #==================================================================================== : #==================================================================================== if (( CHECK_HMC_ERROR == 0 )); then #=============================================================== : Validate against hostname, and IP address, either IPv4 or IPv6 : TO BE DONE in next iteration #=============================================================== #============================================== : Attempt to communicate with the specified HMC #============================================== typeset -i CONN_TYPE=$(clodmget -n -f connection_type HACMPhmcparam) if (( $CONN_TYPE == 1 )); then if [[ -n $HMC_PSSWD ]]; then print -- "$REMOTECOMMAND $HAUTILS/clrest -o open -H ${HMC_LABEL} -u ${HMC_USER} -x ********" >>$CLMGR_TMPLOG else print -- "$REMOTECOMMAND $HAUTILS/clrest -o open -H ${HMC_LABEL} -u ${HMC_USER}" >>$CLMGR_TMPLOG fi $REMOTECOMMAND $HAUTILS/clrest -o open -H ${HMC_LABEL} -u ${HMC_USER} -x ${HMC_PSSWD} >>$CLMGR_TMPLOG 2>&1 else if [[ -z $HMC_USER ]];then print -- "$REMOTECOMMAND ssh -l hscroot -o BatchMode=yes -o ConnectTimeout=3 $HMC_LABEL date" >>$CLMGR_TMPLOG $REMOTECOMMAND ssh -l hscroot -o BatchMode=yes -o ConnectTimeout=3 $HMC_LABEL date >>$CLMGR_TMPLOG 2>&1 else print -- "$REMOTECOMMAND ssh -l $HMC_USER -o BatchMode=yes -o ConnectTimeout=3 $HMC_LABEL date" >>$CLMGR_TMPLOG $REMOTECOMMAND ssh -l $HMC_USER -o BatchMode=yes -o ConnectTimeout=3 $HMC_LABEL date >>$CLMGR_TMPLOG 2>&1 fi fi if (( $? != RC_SUCCESS )); then if (( $CLMGR_FORCE )); then #==================================================================================== : It is possible to force, in that case if connectivity fails we consider we continue anyway #==================================================================================== if (( $CONN_TYPE == 1 )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1175 "\nWarning: could not communicate with HMC \"%1\$s\" with given credentials from node \"%2\$s\". Check \"%1\$s\" is valid. Check for any network issues, name resolution problems, firewall problems or HMC version.\n" "$HMC_LABEL" "$NODE" 1>&2 else cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 751 "\n*** Warning: could not communicate with HMC \"%1\$s\" via SSH from node \"%2\$s\". Is \"%1\$s\" valid? Is the public key for \"%2\$s\" installed correctly on \"%1\$s\"? Any network issues? Name resolution problems? Firewall problems?\n" "$HMC_LABEL" "$NODE" 1>&2 fi cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 815 "\tChecking HMC connectivity between \"%1\$s\" node and \"%2\$s\" HMC : failed! But we force!\n" "$NODE" "$HMC_LABEL" 1>&2 NODES_LIST="${NODES_LIST:+$NODES_LIST }${NODE%%\.*}" else #======================================================== : If we do not force, then if connectivity fails, we fail #======================================================== if (( $CONN_TYPE == 1 )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1176 "\nERROR: could not communicate with HMC \"%1\$s\" with given credentials from node \"%2\$s\. Check \"%1\$s\" is valid. Check for any network issues, name resolution problems, firewall problems or HMC version.\n" "$HMC_LABEL" "$NODE" 1>&2 else cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 752 "\nERROR: could not communicate with HMC \"%1\$s\" via SSH from node \"%2\$s\". Is \"%1\$s\" valid? Is the public key for \"%2\$s\" installed correctly on \"%1\$s\"? Any network issues? Name resolution problems? Firewall problems?\n\n" "$HMC_LABEL" "$NODE" 1>&2 fi rc=$RC_ERROR cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 816 "\tChecking HMC connectivity between \"%1\$s\" node and \"%2\$s\" HMC : failed!\n" "$NODE" "$HMC_LABEL" 1>&2 CHECK_HMC_ERROR=1 fi else cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 817 "\tChecking HMC connectivity between \"%1\$s\" node and \"%2\$s\" HMC : success!\n" "$NODE" "$HMC_LABEL" 1>&2 NODES_LIST="${NODES_LIST:+$NODES_LIST }${NODE%%\.*}" fi fi log_return_msg "$rc" "$0()" "$LINENO" return $? } ######################################################################### ######################################################################### ## ## MAIN Main main ## ######################################################################### ######################################################################### . /usr/es/lib/ksh93/log_entry func_include "$CL" max # Set the location of the KLIB library routines [[ $FPATH != */usr/es/lib/ksh93/hacmp* ]] && FPATH=$FPATH:/usr/es/lib/ksh93/hacmp [[ $FPATH != */usr/es/lib/ksh93/util* ]] && FPATH=$FPATH:/usr/es/lib/ksh93/util [[ $FPATH != */usr/es/lib/ksh93/aix* ]] && FPATH=$FPATH:/usr/es/lib/ksh93/aix export FPATH export PATH="$(VERBOSE_LOGGING='' /usr/es/sbin/cluster/utilities/cl_get_path all)" export HADIAG=/usr/es/sbin/cluster/diag export HAETC=/usr/es/sbin/cluster/etc export HASBIN=/usr/es/sbin/cluster/sbin # keeping HAUTILS as it is used in many other files where func_include is included export HAUTILS=/usr/es/sbin/cluster/utilities export HAEVENTS=/usr/es/sbin/cluster/events export HACSPOC=/usr/es/sbin/cluster/cspoc export HASABIN=/usr/es/sbin/cluster/sa/sbin export HAXDCLI=/usr/es/sbin/cluster/xd_generic/xd_cli export HAXDWIZ=/usr/es/sbin/cluster/xd_generic/director/base_utils export HALIBROOT=/usr/es/lib/ksh93 typeset DEFAULT_ODMDIR=/etc/objrepos [[ -z $ODMDIR ]] && export ODMDIR=$DEFAULT_ODMDIR export CLVT_MSGS=command.cat # For backwards compatibility export CLMGR_MSGS=$CLVT_MSGS export CLVT_SET=2 # For backwards compatibility export CLMGR_SET=$CLVT_SET export CLMGR_RPT_SET=3 export TAB=$'\t' export NL=$'\n' if [[ -z $SNAPSHOTPATH || ! -d $SNAPSHOTPATH ]]; then SNAPSHOTPATH="/usr/es/sbin/cluster/snapshots" export SNAPSHOTPATH fi #=================================== # Set an appropriate tmp directory #=================================== for TMPDIR in "/var/hacmp/log/tmp" \ "/tmp/ibmsupt/hacmp" do [[ -e $TMPDIR && ! -d $TMPDIR ]] && mv -f $TMPDIR $TMPDIR.$$ [[ ! -d $TMPDIR ]] && mkdir -p $TMPDIR if [[ -d $TMPDIR ]]; then if [[ -z $(/bin/df -m $TMPDIR |\ awk '(NR != 1) { if ($3 < 10) print $3 }') ]] then break # More than 10 MB of free space was found in TMPDIR fi fi TMPDIR="" done [[ -z $TMPDIR || ! -d $TMPDIR ]] && TMPDIR="/tmp" # Last resort... :-/ export TMPDIR CLMGR_LOG=$(/usr/bin/odmget -q name=clutils.log HACMPlogs 2>/dev/null | grep "value = ") CLMGR_LOG=${CLMGR_LOG##* = \"} CLMGR_LOG=${CLMGR_LOG%\"} CLMGR_LOG="$CLMGR_LOG/clutils.log" [[ ! -w $CLMGR_LOG ]] && CLMGR_LOG="/tmp/clutils.log" export CLMGR_LOG export CLVTLOG=$CLMGR_LOG # For backwards compatibility if [[ $CLMGR_TMPLOG == *([[:space:]]) ]]; then if [[ -z $CLMGR_PROGNAME ]]; then [[ $0 == *clmgr* ]] && CLMGR_PROGNAME="clmgr" || CLMGR_PROGNAME="clvt" export CLMGR_PROGNAME export CLVT_PROGNAME=$CLMGR_PROGNAME # For backwards compatibility fi export DATESTR=$(/usr/bin/date '+%Y %m %d _ %H %M') DATESTR=${DATESTR// /} export FFDC_DIR="$TMPDIR" export FFDC_FILE="$FFDC_DIR/ffdc.clmgr.stdout.$$.$DATESTR" export FFDC_ERR_FILE="$FFDC_DIR/ffdc.clmgr.stderr.$$.$DATESTR" export FFDC_CS_FILE="$FFDC_DIR/ffdc.clmgr.cs.$$.$DATESTR" export CLMGR_TMPLOG="$FFDC_FILE" export CLMGR_TMPFILE="$TMPDIR/.${CLMGR_PROGNAME}.$$.data" export CLMGR_ERRLOG="$FFDC_ERR_FILE" fi CLRSH=cl_rsh CAA_CLRSH=/usr/sbin/clrsh CLREXEC=$HAUTILS/clrexec if [[ -z $CLMGR_MAX_PROCS || $CLMGR_MAX_PROCS != +([[:digit:]]) ]]; then export CLMGR_MAX_PROCS=10 fi export LOCAL_USER=$(/usr/bin/id -un) export LOCAL_GROUP=$(/usr/bin/id -gn) export LOCAL_NODE=$(CL=$LINENO get_local_node_label) export ORIG_IFS="$IFS" if [[ $TERMINAL_WIDTH != +([[:digit:]]) ]]; then export TERMINAL_WIDTH=$(/usr/bin/stty size 2>/dev/null) TERMINAL_WIDTH=${TERMINAL_WIDTH%%+([[:space:]])} TERMINAL_WIDTH=${TERMINAL_WIDTH##*+([[:space:]])} if [[ $TERMINAL_WIDTH != +([[:digit:]]) ]] || \ (( TERMINAL_WIDTH > 80 )) then TERMINAL_WIDTH=80 fi elif (( TERMINAL_WIDTH > 80 )); then TERMINAL_WIDTH=80 fi #============================================ # Establish the standard clmgr return codes #============================================ ## A result is not known. Useful as an initializer. export RC_UNKNOWN=-1 ## No errors were detected; the operation appears to have been successful export RC_SUCCESS=0 ## A general error has occurred export RC_ERROR=1 ## A specified resource does not exist, or could not be found export RC_NOT_FOUND=2 ## Some required input was missing export RC_MISSING_INPUT=3 ## Some detected input was incorrect in some way export RC_INCORRECT_INPUT=4 ## A required dependency does not exist export RC_MISSING_DEPENDENCY=5 ## A specified search failed to match any data export RC_SEARCH_FAILED=6 ## A given operation was not really necessary export RC_EXTRANEOUS=7 ## Chosen timeout value is exhausted export RC_TIMEOUT=8 ## Cluster override flag for CAA to allow commands to run. export CLUSTER_OVERRIDE=yes # Establishies the min/max supported sizes for repository disks, in MB export REPOS_MIN_SIZE=512 # 512 MB export REPOS_MAX_SIZE=460000 # 460 GB, roughly ## PowerHA SystemMirror only accepts 6 backup repositories ## - per cluster for NSC and SC cluster, ## - per site for LC cluster. ## Backup repositories are used by Automatic Repository Replacement (ARR) ## functionality. export MAX_NB_OF_BACKUP_REPOSITORIES=6 ## Here MAX_USER_RESOURCES value is taken from MAXUSERRESOURCES value in cluster.h file. ## MAX_USER_RESOURCES value indicates the maximum number of individual resources that can be ## configured in a resource group. Any change in the value of MAXUSERRESOURCES in cluster.h ## should be replicated here. export MAX_USER_RESOURCES=512 ## MAX_NETWORKS indicates the maximum number of networks allowed in a PowerHA cluster. ## This is similar to MAXNETWORKS defined in cluster.h file. export MAX_NETWORKS=48 export MAX_NAME_LENGTH=64 typeset -u UC_ARGV=$* if [[ -n $(clodmget -n -f name HACMPcluster 2>/dev/null) ]] then integer MSLC=$(clodmget -f multi_site_lc HACMPcluster 2>/dev/null) if [[ -n $MSLC ]]; then case $MSLC in 0) export CLUSTER_TYPE="SC" ;; 1) export CLUSTER_TYPE="LC" ;; -1) export CLUSTER_TYPE="NSC" ;; esac fi elif [[ $_ACTION == "add" && $_CLASS == "cluster" ]] then if [[ -n ${_ENV_ARGS[TYPE]} ]]; then export CLUSTER_TYPE="${_ENV_ARGS[TYPE]}" elif [[ " $UC_ARGV " == *\ TYPE=@(L|S|NS)C\ * ]]; then export CLUSTER_TYPE=" $UC_ARGV " CLUSTER_TYPE=${CLUSTER_TYPE##* TYPE=} CLUSTER_TYPE=${CLUSTER_TYPE%% *} else export CLUSTER_TYPE="NSC" fi else export CLUSTER_TYPE="NSC" fi unset MSLC unset UC_ARGV : version=@(#) 64cdf46 43haes/lib/ksh93/func_include.sh, 726, 2147A_aha726, May 05 2021 10:51 AM log_return_msg 0 func_include "$LINENO" #============================================================================== # The following, comment block attempts to enforce coding standards when this # file is edited via emacs or vim. This block _must_ appear at the very end # of the file, or the editor will not find it, and it will be ignored. #============================================================================== # Local Variables: # indent-tabs-mode: nil # tab-width: 4 # End: #============================================================================== # vim: tabstop=4 shiftwidth=4 expandtab #==============================================================================