#!/bin/ksh93 # ALTRAN_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # Copyright (C) Altran ACT S.A.S. 2017,2018,2019,2020,2021,2022. All rights reserved. # # ALTRAN_PROLOG_END_TAG # # @(#) 5881272 43haes/usr/sbin/cluster/events/resource_common.sh, 61aha_r726, 2205A_aha726, May 16 2022 12:15 PM #============================================================================= # # Name: SerializeAsArray # # Description: This function is called when the query action is invoked in # such a way that the data set is simple, and is stored in an # associative array. By default, the output is simply one value # per line, with the values doublequoted. # # Optionally, the user may request colon-delimited or XML- # formatted output. Also optionally, the user may specify one # or more specific attributes that they want to see, in which # case *only* those attributes are displayed. It should be noted # that the order in which the user specifies those attributes is # *not* honored. # # Inputs: array A reference to the pre-populated array that # contains the data to be formatted and displayed. # # class The class, or type, of the data that was passed in, # such as "cluster", "site", "node", etcetera. # # CLMGR_DELIMITER Optional. If set (to any value), the output # will be delimited by the value in this variable. # # CLMGR_COLON Optional. If set to non-zero, the output will be # colon-delimited. # # CLMGR_XML Optional. If set (to any value), the output will # be formatted in XML. # # Outputs: The data in the provided associative array is displayed in the # appropriate format. # # Returns: 0 if no problems are encountered. # 1 if something goes wrong. # #============================================================================= function SerializeAsArray { . $HALIBROOT/log_entry "$0()" "$CL" max typeset -n array=$1 typeset -u class=$2 if (( ${#array[*]} == 0 )); then log_return_msg "$RC_ERROR" "$0()" "$LINENO" return $? fi [[ -z $class ]] && class="ITEM" typeset -i COUNT=0 INDEX=0 typeset element= if [[ $CLMGR_DELIMITER == *([[:space:]]) ]] && \ (( CLMGR_COLON )) then CLMGR_DELIMITER=":" fi if (( CLMGR_JSON )); then : Since JSON output was requested, print our standard clmgr JSON header print "{\n \"clmgr\": {" fi # : For XML and JSON, a class header must be displayed before displaying : any of the object names. That also requires some minor spelling : improvements to the class names to correctly indicate plurality. # if (( CLMGR_XML )); then if [[ $class != "CLUSTER" ]] && (( ! CLMGR_VERBOSE )) then case $class in *SS) print "<${class}ES>" ;; *Y) print "<${class%Y}IES>" ;; *) print "<${class}S>" ;; esac fi elif (( CLMGR_JSON )); then typeset -l class_lc=$class case $class_lc in *ss) print -n " \"${class_lc}es\": [ " ;; *y) print -n " \"${class_lc%y}ies\": [ " ;; *) print -n " \"${class_lc}s\": [ " ;; esac fi # : Display all the object names for class $class # for (( INDEX=0; INDEX<${#array[*]}; INDEX++ )) do element=${array[$INDEX]} [[ -z $element ]] && continue if [[ $CLMGR_DELIMITER != *([[:space:]]) ]]; then (( COUNT )) && print -n "$CLMGR_DELIMITER" print -n -- $element elif (( CLMGR_XML )); then print " <$class>$element" elif (( CLMGR_JSON )); then print -n "\"$element\"" (( ( INDEX + 1 ) == ${#array[*]} )) && print " ]" || print -n ", " else print -- "$element" fi (( COUNT++ )) # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done if [[ $CLMGR_DELIMITER != *([[:space:]]) ]]; then print elif (( CLMGR_XML )); then # : For XML, a class closer must be displayed after displaying the object : names for the class. That also requires some minor spelling changes : to the class names to correctly indicate plurality. # case $class in *SS) print "" ;; *Y) print "" ;; *) print "" ;; esac elif (( CLMGR_JSON )); then # : JSON also requires a "closer" to indicate that all the properties : that need to be displayed, actually _have_ been displayed. # print " }\n}" fi log_return_msg 0 "$0()" "$LINENO" return $? } # End of "SerializeAsArray()" #============================================================================= # # Name: SerializeAsAssociativeArray # # Description: This function is called when the query action is invoked in # such a way that the data set is non-simple, and is stored in # an associative array. By default, the output is in standard # "ATTR=VALUE" pairs, one per line, with the values doublequoted. # Optionally, the user may request colon-delimited or XML- # formatted output. Also optionally, the user may specify one # or more specific attributes that they want to see, in which # case *only* those attributes are displayed. It should be noted # that the order in which the user specifies those attributes is # *not* honored. # # It is important to note the format of the input data in the # associative array. Since more than one object might be # represented in that data set, and those objects should be # displayed logically (i.e. together, and not interleaved with # the data of the other objects), there was a need to # differentiate the data sets within the array. Unfortunately, # ksh93 does not support multi-dimensional arrays, which are # the logical solution for this type of problem. So for better # or worse, the original designers of clvt chose to simulate # a multi-dimensional array with the input hash by optionally # appending index numbers to the keys. # # The index numbers indicate which object that key/value belongs # to, and are used to display the data together, in logical # groupings. This adds a ton of complexity to this function... # but seems to work. The only problem is when the key value # naturally ends in a digit itself (i.e. where the digit is # actually part of the attribute name), in which case it is # impossible to determine where the key value ends and the # assigned index number (if any), begins! To handle this # situation, a pound/comment/hash symbol can be inserted # between the core key value and its index number. For example, # given a key name of "SITE1" and an index value of "3", the # key would need to look like this: "SITE1#3" # # Inputs: array A reference to the pre-populated associative array # that contains the data to be formatted and displayed. # # class The class, or type, of the data that was passed in, # such as "cluster", "site", "node", etcetera. # # CLMGR_ATTRS Optional. A set of attributes to limit the output # to (no error occurs if they are not found). # # CLMGR_DELIMITER Optional. If set (to any value), the output # will be delimited by the value in this variable. # # CLMGR_COLON Optional. If set (to any value), the output will # be colon-delimited. # # CLMGR_XML Optional. If set (to any value), the output will # be formatted in XML. # # Outputs: The data in the provided associative array is displayed in the # appropriate format. # # Returns: 0 if no problems are encountered. # 1 if something goes wrong. # #============================================================================= function SerializeAsAssociativeArray { . $HALIBROOT/log_entry "$0()" "$CL" max typeset -n array=$1 typeset -u class=$2 typeset -l lcclass=$class typeset -u upper_key="" upper_attr="" typeset -l lower_key="" typeset -A CHILDREN typeset -i SNUM=0 COUNT=0 I=0 J=1 DATA_LAYER=-1 LAST_DATA_LAYER=-1 ATTR_COUNT=0 typeset key= value= attributes= INDENT= PARENTS= INDEXES= attr= if (( ${#array[*]} == 0 )); then : No data was provided. The properties array is empty. log_return_msg "$RC_ERROR" "$0()" "$LINENO" return $? # No data was provided! fi #================================================== # If conflicting settings, give preference to XML #================================================== (( CLMGR_XML && CLMGR_COLON )) && CLMGR_COLON=0 if [[ $CLMGR_DELIMITER == *([[:space:]]) ]] && \ (( CLMGR_COLON )) then CLMGR_DELIMITER=":" fi #========================================================== # Establish the appropriate attribute sets for this class #========================================================== typeset NEW_CLMGR_ATTRS= for value in $CLMGR_ATTRS; do if [[ $value != *=* ]]; then NEW_CLMGR_ATTRS="$NEW_CLMGR_ATTRS $value" fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done CLMGR_ATTRS=$NEW_CLMGR_ATTRS CLMGR_ATTRS=${CLMGR_ATTRS//+([[:space:]])/ } CLMGR_ATTRS=${CLMGR_ATTRS# } CLMGR_ATTRS=${CLMGR_ATTRS% } typeset -i matches=0 if [[ -n $CLMGR_ATTRS ]]; then for upper_attr in $CLMGR_ATTRS; do matches=0 typeset ATTR_SEQ=${_ATTR_ORDER[$lcclass]} if [[ $ATTR_SEQ == *([[:space:]]) || \ $CLMGR_DELIMITER != *([[:space:]]) ]] then ATTR_SEQ=${_COLON_ATTR_ORDER[$lcclass]} fi for upper_key in $ATTR_SEQ; do if [[ $upper_key == $upper_attr ]]; then attributes="$attributes $upper_key" (( matches++ )) (( ATTR_COUNT++ )) fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done attributes=${attributes# } else attributes=${_ATTR_ORDER[$lcclass]} if [[ $attributes == *([[:space:]]) || \ $CLMGR_DELIMITER != *([[:space:]]) ]] then attributes=${_COLON_ATTR_ORDER[$lcclass]} fi fi #================================================================== # Check for any dynamic/non-static attributes, meaning attributes # that are added at runtime, and are not pre-defined in the # ATTR_ORDER structure. One place where these are used is in the # snapshot query code, in KLIB_HACMP_get_snapshot_attributes. #================================================================== for key in ${!array[*]}; do if [[ $key == \~* ]]; then base=${key%%+([0-9])} # Covers most cases if [[ $base == *+([0-9])\. ]]; then base=${base%%+(+([0-9])\.)} fi if [[ " $attributes " != *\ $base\ * ]]; then attributes="$attributes $base" fi fi done #============================================================= # Extract the index numbers, if any, from the provided data. # The indexes are used to logically group different subsets # of data within the overall set. #============================================================= for key in ${!array[*]}; 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} [[ -z $index ]] && index=0 #================================================================= # All attributes definitions are *supposed* to be in uppercase. # However, this code *guarantees* that a typo will not break us. #================================================================= if [[ $key != \~* ]]; then value=${array[$key]} unset array[$key] upper_key=$base array[$upper_key$index]=$value else # Dynamic/Non-static keys are case-sensitive value=${array[$key]} unset array[$key] key=$base array[$key$index]=$value fi #====================================================================== # If any search criteria were specified (i.e. via CLMGR_ATTRS), then # get rid of any retrieved data that does *not* fit the criteria. # If the user didn't specifically ask for it, then we don't need it! # If no search is being performed, then store the indexes and base # attribute names with newline separators, so that they can be easily # sorted later (after this loop completes). #====================================================================== matches=0 for upper_key in $CLMGR_ATTRS; do [[ $base == $upper_key* ]] && (( matches++ )) done if [[ -n $CLMGR_ATTRS ]] && (( matches == 0 )); then (( CLMGR_JSON )) && [[ $base == *NAME ]] && continue # JSON _must_ have the object name unset array[$base$index] elif [[ -n $index ]]; then INDEXES="$INDEXES\n$index" fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done INDEXES=${INDEXES#\\n} #================================================================== # Sort the indexes (removing all duplicates), so that everything # gets displayed in logical order. Remember that the input data # is in an associative array, which always scrambles the order of # the keys. #================================================================== if [[ $INDEXES != *([[:space:]]) ]]; then print -- "$INDEXES" |\ sort -u -t. -k1,1n -k2,2n -k3,3n -k4,4n -k5,5n |\ while read INDEX; do [[ $INDEX == *([[:space:]]) ]] && continue INDEXES[$I]=$INDEX DOTS=${INDEX//[[:digit:]]/} (( DATA_LAYER = ${#DOTS} )) (( CHILDREN[$DATA_LAYER]++ )) (( I++ )) # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done fi #===================================================================== # To ease the somtimes monstrous complexity of displaying properly # formatted XML in a friendly-for-the-humans manner, establish the # appropriate parent element names in advance. Currently, the "node" # class is the only one that allows for multiple layers of depth. #===================================================================== if (( CLMGR_XML || CLMGR_JSON )); then case $class in STORAGE) PARENTS[0]="$class" ;; *SS) PARENTS[0]="${class}ES" ;; *Y) PARENTS[0]="${class%Y}IES" ;; *) PARENTS[0]="${class}S" ;; esac PARENTS[1]=$class if [[ ${!CHILDREN[*]} != *([[:space:]]) ]]; then for layer in ${!CHILDREN[*]}; do (( layer++ )) case $layer in 1) case $class in STORAGE) if [[ ${_ENV_ARGS[TYPE]} == "family" ]]; then PARENTS[$layer]="CLASS" else PARENTS[$layer]="PHYSICAL_VOLUME" fi ;; *) PARENTS[$layer]=$class ;; esac ;; 2) case $class in FILE_COLLECTION) PARENTS[$layer]=FILE_DATA ;; NODE) PARENTS[$layer]=ADAPTER ;; NETWORK) PARENTS[$layer]=INTERFACE_DATA ;; APPLICATION_CONTROLLER) PARENTS[$layer]=MONITORING_DATA ;; *) PARENTS[$layer]=$class$layer ;; esac ;; *) PARENTS[$layer]=$class$layer ;; esac # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done else PARENTS[1]=$class fi fi INDENT=" " if (( CLMGR_XML )) && [[ $class == "CLUSTER" ]] then INDENT="$INDENT " fi if (( CLMGR_JSON )) then if [[ $CLMGR_COMMAND != *\ -v\ *query\ cluster* ]] then : Since JSON output was requested, print our standard clmgr JSON header print "{\n$INDENT\"clmgr\": {" fi INDENT="$INDENT " # : Create an array, MAX_ATTR_COUNT, to store the maximum number of objects : found in the data set at each layer of the data, and for each object. : Confusing, right? It helps to think of the data visually, with child : elements indented below parent elements... with the added complication : that the number of child elements can vary from object to object. For : an example, consider the files for each file collection. One collection : might have many files and another might have only a few. : : For each level/layer/depth of indentation for each object, we count the : total number of objects in that location. This is really only useful for : the JSON output, though, and for that it is absolutely essential in order : to get the formatting right. The last object at any depth must not have a : trailing comma, but all preceding objects at that same depth _must_ have : a trailing comma. # integer CNT=0 for (( I=0; I<${#INDEXES[*]}; I++ )); do INDEX=${INDEXES[$I]} # : The way clmgr works, indented/child data is specified via indexes : that have decimal points. So if a parent object has an index of : 5, as an example, to display any indented child values for that : object, you would simply append the new index number to the parent : index, such as "5.1", "5.2", "5.3", ... Why not just use : multi-dimensional arrays you might ask... Well, because they did : not exist in the version of Korn shell shipped with older versions : of AIX, so the CLAM team came up with this. So for now, just be : aware of how it works. # DOTS=${INDEX//[[:digit:]]/} integer OBJNUM=${INDEX%\.*} # : For classes that support multiple "depths", meaning they support : sub-sections of attributes per object, the number of attributes : must be counted individually for each object. The reason for that : is that the sub-sections can have variable length. so for those : classes, we must count each unique index number to get an accurate : count. For all other classes, which are more simple, we can just : count the number of non-null attributes used in the output. # if [[ $class == @(FILE_COLLECTION|APPLICATION_CONTROLLER)* ]] then : Increment the count CNT=${MAX_ATTR_COUNT[$OBJNUM][${#DOTS}]} (( CNT++ )) MAX_ATTR_COUNT[$OBJNUM][${#DOTS}]=$CNT else for key in $attributes; do if [[ $key != \~* ]]; then upper_key=$key key=$upper_key fi if [[ ! ${!array[$key$INDEX]} || \ " ${!array[@]} " != *\ $key$INDEX\ * || \ ${!array[$key$INDEX]} == *null* ]] then continue fi CNT=${MAX_ATTR_COUNT[$OBJNUM][${#DOTS}]} (( CNT++ )) MAX_ATTR_COUNT[$OBJNUM][${#DOTS}]=$CNT done fi done fi if (( CLMGR_XML )) then if [[ $class != "CLUSTER" || $CLMGR_COMMAND == *\ -v\ * ]] then : Display the outermost opening XML element, which is the class name print "<${PARENTS[0]}>" else INDENT="" fi elif (( CLMGR_JSON )); then : Display the opening tree leaf, which is the class name typeset -l class_lc=${PARENTS[0]} [[ $class_lc == "clusters" ]] && class_lc=${class_lc%s} print "$INDENT\"$class_lc\": {" fi # : Count the total number of objects being displayed. "TOTAL_OBJECTS" is : a two-dimensional array used for counting the number of objects to be : displayed. The first element, when set to zero, counts the base objects. : When set to one, the optional second element then becomes the object : number and is used to count the number of child object groupings. As an : example of the most complicated case, consider file collections. The : "base objects" are obviously the file collections themselves, and we : add those up in TOTAL_OBJECTS[0]. However, they have child groupings : comprised of each file managed by that file collection, plus its size. : Since the number of those child groupings varies from collection to : collection, they are added up individually using the parent objects : object number via TOTAL_OBJECTS[1][$OBJNUM]. It is complicated... but : it really does all work. # typeset TOTAL_OBJECTS if (( CLMGR_JSON )) then if [[ ${PARENTS[0]} != CLUSTER* ]]; then COUNT=0 INDENT="$INDENT " [[ -z $CLMGR_ATTRS ]] && print -n "$INDENT\"objects\": [ " for key in ${!array[*]}; do # : Each class type that has child groupings must be procesed : a little differently from the "normal", flatter classes. # if [[ $class == "FILE_COLLECTION" && $key == FILE+([0-9])\.+([0-9]) ]] || \ [[ $class == "APPLICATION_CONTROLLER" && $key == RESOURCE_GROUP+([0-9])\.+([0-9]) ]] then # : Add up just one of the attributes from each child : grouping to determine the total number of sub-sections : that will be displayed for this class+object. # typeset OBJNUM=${key#@(FILE|RESOURCE_GROUP)} OBJNUM=${OBJNUM%%\.*} if [[ $OBJNUM == +([0-9]) ]] then TOTAL_OBJECTS[1][$OBJNUM]=$(( ${TOTAL_OBJECTS[1][$OBJNUM]} + 1 )) fi elif [[ $class == "HOST" && $key == HOSTNAME+([0-9]) ]] || \ [[ $class != "HOST" && $key == NAME+([0-9]) ]] then # : Add up just one of the attributes from each parent/base : object. For all classes except one, the attribute "NAME" : is provided and can be used for this. The exception is : the "host" class, which has "HOSTNAME" instead. # if [[ -z $CLMGR_ATTRS ]] then : Display the label/name of each base object (( COUNT > 0 )) && print -n ", " if [[ $class == "DEPENDENCY" ]] then INDEX=${key#NAME} print -n "\"${array[$key]} (${array[TYPE$INDEX]})\"" else print -n "\"${array[$key]}\"" fi fi (( COUNT++ )) TOTAL_OBJECTS[0]=$(( ${TOTAL_OBJECTS[0]} + 1 )) fi done [[ -z $CLMGR_ATTRS ]] && print " ]," else TOTAL_OBJECTS[0]=1 fi fi # # Indicates if there are "indented" values, such as with file collections. # For most object classes in clmgr, the depth is zero; there are no sub- # groupings of attributes for some parent object. Some exceptions to this # are file collections and application controllers. # # NOTE: Do not declare these as integers, even though that is ultimately # what they will contain. There is some code down below that actually # checks for a couple of them having a null value, and if they never # do, bad things happen (like the delimited output column headers # never get displayed). # typeset DATA_DEPTH="" typeset LAST_DATA_DEPTH="" typeset NEXT_DATA_DEPTH="" integer NEED_OPENER=1 typeset QUEUE="" # Used to provide accurate closing elements typeset NEXT_INDEX="" typeset DOTS="" # Used to determine DATA_DEPTH # # OBJ_COUNT is a two-dimensional array used to keep track of the total # number of object stanzas that are to be displayed at a particular # level. # typeset OBJ_COUNT # # CURRENT_ATTR_COUNT is a two-dimensional array used to keep track of # which object we are currently processing and displaying at a particular # data depth (usually zero). # typeset CURRENT_ATTR_COUNT for (( I=0; I<${#INDEXES[*]}; I++ )); do INDEX=${INDEXES[$I]} : Extract the unique object number from the index integer OBJNUM=${INDEX%\.*} if (( I <= ${#INDEXES[*]} )); then NEXT_INDEX=${INDEXES[$I+1]} DOTS=${NEXT_INDEX//[[:digit:]]/} NEXT_DATA_DEPTH=${#DOTS} else unset NEXT_INDEX unset NEXT_DATA_DEPTH fi LAST_DATA_DEPTH=$DATA_DEPTH DOTS=${INDEX//[0-9]/} # Remove index numbers, leaving only the "dot" separators (if any) DATA_DEPTH=${#DOTS} # The number of dots/decimals (if any) is the data depth. Usually zero. : Initialize the object counter for indent level $DATA_DEPTH CURRENT_ATTR_COUNT[$OBJNUM][$DATA_DEPTH]=0 if (( CLMGR_JSON )) then if [[ ${PARENTS[0]} == @(FILE_COLLECTION|APPLICATION_CONTROLLER)* ]] then if (( DATA_DEPTH == 1 && NEED_OPENER )) then # : For classes that provide indented child sections, : an appropriate opener must be displayed. # if [[ ${PARENTS[0]} == FILE_COLLECTION* ]] then : Begin the set of files for this collection INDENT="$INDENT " print "$INDENT\"FILES\": [" elif [[ ${PARENTS[0]} == APPLICATION_CONTROLLER* ]] then : Begin the set of resource groups for this application INDENT="$INDENT " print "$INDENT\"RESOURCE_GROUPS\": [" fi NEED_OPENER=0 elif (( DATA_DEPTH == 0 && ! NEED_OPENER )) then : Close out the set of child stanzas for this object INDENT=${INDENT% } print "$INDENT ]\n$INDENT}," NEED_OPENER=1 fi fi fi if (( CLMGR_XML || CLMGR_JSON )) then INDENT= for (( j=0; j<$DATA_DEPTH+1; j++ )); do if [[ $class != "CLUSTER" || $CLMGR_COMMAND == *\ -v\ * ]] then INDENT="$INDENT " fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done fi #=================================================== # Print an opening element and add it to the queue #=================================================== if (( CLMGR_XML )); then print "$INDENT<${PARENTS[$DATA_DEPTH+1]}>" if [[ ${PARENTS[$DATA_DEPTH+1]} == "CLUSTER" || $CLMGR_COMMAND == *\ -v\ * ]] then INDENT="$INDENT " fi if [[ -z $QUEUE ]]; then QUEUE=$INDEX else QUEUE="$QUEUE $INDEX" fi elif (( CLMGR_JSON )); then INDENT="$INDENT " if [[ ${PARENTS[0]} == CLUSTER* ]] then # : The cluster class is special, since it has no objects. It : _is_ the object. So it gets handled a little differently. # if [[ $CLMGR_COMMAND != *\ -v\ * ]] then INDENT="$INDENT " fi OBJ_COUNT[0]=$(( ${OBJ_COUNT[0]} + 1 )) elif (( DATA_DEPTH == 0 )) then INDENT="$INDENT " : Display the base object name if [[ $class == "DEPENDENCY" ]] then # : Dependency names are not unique without their type : being associated with them, so we have to do that : here. # print "$INDENT\"${array[NAME$INDEX]} (${array[TYPE$INDEX]})\": {" elif [[ $class == "HOST" ]] then # : The "host" class is the _only_ class that does not use : an identifying attribute of "NAME". So we display the : "HOSTNAME" attribute instead. ~sigh~ # print "$INDENT\"${array[HOSTNAME$INDEX]}\": {" else print "$INDENT\"${array[NAME$INDEX]}\": {" fi # : For each base object we display, bump the object counter by : one. This will be compared to the total possible objects to : determine how to close out each section, with or without a : trailing comma. # OBJ_COUNT[$DATA_DEPTH][0]=$(( ${OBJ_COUNT[$DATA_DEPTH][0]} + 1 )) elif [[ ${PARENTS[0]} == @(FILE_COLLECTION|APPLICATION_CONTROLLER)* ]] then : Begin a single child group, such as FILE and SIZE for file collections INDENT="$INDENT " print "$INDENT {" INDENT="$INDENT " fi if (( DATA_DEPTH > 0 )) then # : If an indented child grouping is currently being displayed, : such as the FILE and SIZE pairings for file collections, bump : the child grouping counter by one. This keeps track of the : groupings/stanzas themselves, and not each individual : attribute. It is used to determine if the final grouping in : the indentation has been reached or not. If so, no comma is : needed. Otherwise, we need a comma. # OBJ_COUNT[$DATA_DEPTH][$OBJNUM]=$(( ${OBJ_COUNT[$DATA_DEPTH][$OBJNUM]} + 1 )) fi elif [[ $CLMGR_DELIMITER != *([[:space:]]) ]]; then #================================================================== # Attempt to detect if the underlying data has fundamentally # changed based on upon the size of the current index, relative # to the last index. If changed, display the appropriate headers. #================================================================== if [[ -z $LAST_DATA_DEPTH ]] || \ (( $DATA_DEPTH != $LAST_DATA_DEPTH )) then [[ -n $LAST_DATA_DEPTH ]] && print if (( ! CLMGR_SUPPRESS )); then display_colon_headers "${!array[*]}" "$attributes" "$INDEX" fi fi elif [[ $CLMGR_DELIMITER == *([[:space:]]) && -n $LAST_DATA_DEPTH ]] && \ (( $DATA_DEPTH != $LAST_DATA_DEPTH )) then print # Default display; a little whitespace improves readability fi # : Determine which attributes will actually be displayed, which is : often a subset of the total available attributes for a given class, : and count them up. This information is used to determine when the : last attribute of a given stanza for an object has been displayed. # typeset TOTAL_ATTRS=0 # The total number of per-object attributes to be displayed for key in $attributes; do (( TOTAL_ATTRS++ )) done for key in $attributes; do if [[ $key != \~* ]]; then upper_key=$key key=$upper_key fi if [[ ! ${!array[$key$INDEX]} || \ " ${!array[@]} " != *\ $key$INDEX\ * || \ ${!array[$key$INDEX]} == *null* ]] then # : The dependency class is an odd ball, and does not follow : the rules like most of the other classes. As such, it has : a hard-coded total, set below, and we should not adjust it : here. # if [[ $class != @(DEPENDENCY|EVENT) ]] then # : Since this attribute is not present in the data set, do : not count it towards the total attribute count that will : actually be displayed. # (( TOTAL_ATTRS-- )) fi fi done #================================================================ # Display the complete set of data for the "$INDEX" element/set # The "$attributes" loop ensures that any user-specified attrs # are the only ones displayed. Also, in the case where the user # did not specify any attributes, and all attributes are used, # this loop guarantees the order the attributes are displayed. #================================================================ COUNT=0 for key in $attributes; do if [[ $key != \~* ]]; then upper_key=$key key=$upper_key fi # # Skip over any undefined attributes; have to control the output. # Just because the FPATH function stuffed something in the array # doesn't mean we should display it! Only _ATTR_ORDER gets to # decide what is displayed, and in what order. Had to add the # explicit test for "*null*" to accommodate the ksh93 shipped # with AIX 7.1. *sigh* # With ksh93 shipped with AIX 7.3, null check is not working. # if [[ ! ${!array[$key$INDEX]} || \ " ${!array[@]} " != *\ $key$INDEX\ * || \ ${!array[$key$INDEX]} == *null* ]] then continue fi value=${array[$key$INDEX]} value=${value##+([[:space:]])} value=${value%%+([[:space:]])} : Increment the object counter for indent level $DATA_DEPTH CURRENT_ATTR_COUNT[$OBJNUM][$DATA_DEPTH]=$(( ${CURRENT_ATTR_COUNT[$OBJNUM][$DATA_DEPTH]} + 1 )) # : For resource group dependencies, there is no consistency about : the total number of attributes to display per object, since it : depends on the type of the dependeny. So we override it with a : proper, hard-coded value here. The same thing is needed for : displaying events, custom versus pre-defined. # if [[ $class == "DEPENDENCY" && $key == TYPE* ]] then case $value in DIFF*) TOTAL_ATTRS=5 ;; SAME*) TOTAL_ATTRS=4 ;; STOP*) TOTAL_ATTRS=4 ;; START*) TOTAL_ATTRS=4 ;; PARENT*) TOTAL_ATTRS=4 ;; *) TOTAL_ATTRS=3 ;; esac elif [[ $class == "EVENT" && $key == TYPE* ]] then case $value in CUSTOM) TOTAL_ATTRS=4 ;; # Custom events have 4 output lines per event *) TOTAL_ATTRS=7 ;; # Pre-defined events have 7 output lines per event esac fi # : If this is a dynamic/non-static output, remove : the leading tilde that indicates that fact # key=${key#\~} if (( CLMGR_XML )); then print -- "$INDENT<${key%#}>$value" elif (( CLMGR_JSON )); then if (( COUNT < ( TOTAL_ATTRS - 1 ) )) then : This is just a normal, inline property, not the last one in the grouping print -- "$INDENT \"${key%#}\": \"$value\"," elif (( $DATA_DEPTH == 0 )) && [[ -n ${OBJ_COUNT[0][0]} ]] && \ (( ${OBJ_COUNT[0]} < ${TOTAL_OBJECTS[0]} )) && \ [[ $NEXT_INDEX != +([0-9])\.+([0-9]) ]] then # : The end of a base object has been detected, and there : are no indented child objects for this object, and this : is not the last of the base objects that will be : displayed. Display the closing bracket with a comma, : which indicates that another object will be displayed : after this one. # print -- "$INDENT \"${key%#}\": \"$value\"\n${INDENT}}," elif (( $DATA_DEPTH == 1 )) && [[ -n ${OBJ_COUNT[1][$OBJNUM]} ]] && \ (( ${OBJ_COUNT[1][$OBJNUM]} < ${TOTAL_OBJECTS[1][$OBJNUM]} )) then # : The end of an indented group of child stanzas has been : detected, but it is not the last stanza in the group. : Display the closing bracket with a comma, which indicates : that another grouping will be displayed after this one. # print -- "$INDENT \"${key%#}\": \"$value\"\n${INDENT}}," elif [[ $NEXT_INDEX != +([0-9])\.+([0-9]) ]] then # : The last base or indented, child object has just finished : being displayed. Close it out with a closing bracket with : no comma, to indicate that it is the last object. # print -- "$INDENT \"${key%#}\": \"$value\"\n$INDENT}" INDENT=${INDENT% } fi elif [[ $CLMGR_DELIMITER != *([[:space:]]) ]]; then (( COUNT )) && print -n "$CLMGR_DELIMITER" print -n -- "$value" else print -- "${key%\#}=\"$value\"" fi (( COUNT++ )) # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done # End of the "attributes" loop ROOT=${INDEX%\.*} [[ $ROOT == $INDEX ]] && ROOT=0 NEXT_ROOT=${NEXT_INDEX%\.*} [[ $NEXT_ROOT == $NEXT_INDEX ]] && NEXT_ROOT=0 #==================================================== # Print a closing element, if appropriate/necessary, # and check/clean-up the queue. #==================================================== # A missing "NEXT_INDEX" means all data has been # displayed, so close out all remaining elements # on the queue. A shallower data depth for the # next element to be displayed indicates that all # queued elements back to that depth should be # closed out. #==================================================== if [[ -z $NEXT_INDEX ]] || \ (( $NEXT_DATA_DEPTH < $DATA_DEPTH )) then if (( CLMGR_XML || CLMGR_JSON )); then for (( J=$DATA_DEPTH; J>=$NEXT_DATA_DEPTH; J-- )); do if (( CLMGR_XML )) then INDENT=${INDENT% } print "$INDENT" elif (( CLMGR_JSON )) then if (( ${OBJ_COUNT[0]} == ${TOTAL_OBJECTS[0]} )) then if [[ ${PARENTS[0]} == @(FILE_COLLECTION|APPLICATION_CONTROLLER)* ]] && (( J == 1 )) then : The indentation level here requires special handling print "${INDENT}]" INDENT=${INDENT% } # Finished a block. Reduce the indentation. print "${INDENT}}" elif [[ $class != "CLUSTER" ]] then if [[ ${PARENTS[0]} == @(FILE_COLLECTION|APPLICATION_CONTROLLER)* ]] then INDENT=${INDENT% } fi print "${INDENT}}" INDENT=${INDENT% } # Finished a block. Reduce the indentation. else INDENT=${INDENT% } fi fi fi QUEUE=${QUEUE%\ *} LAST=${QUEUE##*\ } DOTS=${LAST//[[:digit:]]/} DATA_DEPTH=${#DOTS} # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done fi #====================================================== # Same depth and same root means the current data set # has been displayed, and should be closed out. #====================================================== elif (( $DATA_DEPTH == $NEXT_DATA_DEPTH )) && \ [[ $ROOT == $NEXT_ROOT ]] then if (( CLMGR_XML )); then print "$INDENT" QUEUE=${QUEUE%\ *} # Remove last element from queue elif (( CLMGR_JSON )); then QUEUE=${QUEUE%\ *} # Remove last element from queue elif [[ $CLMGR_DELIMITER == *([[:space:]]) ]]; then if [[ -z $CLMGR_ATTRS ]] || (( ATTR_COUNT > 1 )); then print # Default display; whitespace improves readability fi fi fi if [[ $CLMGR_DELIMITER != *([[:space:]]) ]] && \ (( COUNT && ${#array[*]} > 0 )) then print fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .0001 done # : Close out the data display as appropriate for the output format. # For the default attr=value pair output, there is no close-out needed. # if (( CLMGR_XML )); then if [[ $class != "CLUSTER" || $CLMGR_COMMAND == *\ -v\ * ]] then : Display the outermost closing element print "" fi elif (( CLMGR_JSON )); then if [[ $CLMGR_COMMAND != *\ -v\ *query\ cluster* ]] then if [[ $class == "CLUSTER" && $CLMGR_COMMAND == *\ -v\ * ]] then : Display the closing bracket for the cluster data segment # NOTE: We don't need two here because the "CLUSTER" class # contains no objects; the cluster _is_ the object. print "}" else : Display the outermost closing brackets print " }\n}" fi fi elif [[ $CLMGR_DELIMITER != *([[:space:]]) ]]; then : For delimited data, we close out with a blank line print fi log_return_msg 0 "$0()" "$LINENO" return $? } # End of "SerializeAsAssociativeArray()" #============================================================================= # # Name: display_colon_headers # # Description: If colon-delimited output was requested, and column header # suppression was *not* asked for, then display the appropriate # column headers. # # Inputs: key_list a list of all keys from the data collected # for the specified object/class. # # attributes a list of all allowable attributes for the # class of object currently being processed. # # INDEX the current index # # Outputs: A colon-delimited header line is displayed on STDOUT. # # Returns: 0 if a header line was displayed, or # 1 if nothing was displayed # #============================================================================= function display_colon_headers { . $HALIBROOT/log_entry "$0()" "$CL" max typeset key_list=$1 typeset attributes=$2 typeset INDEX=$3 typeset -i COUNT=0 STATUS=1 typeset LINE= if [[ $CLMGR_DELIMITER == *([[:space:]]) ]] && \ (( CLMGR_COLON )) then CLMGR_DELIMITER=":" fi if [[ $CLMGR_DELIMITER == *([[:space:]]) ]] || \ (( CLMGR_SUPPRESS )) then return fi LINE="#" [[ $CLMGR_GUI != "SMIT" ]] && LINE="$LINE " for upper_key in $attributes; do [[ " $key_list " != *\ $upper_key$INDEX\ * ]] && continue upper_key=${upper_key#\~} upper_key=${upper_key%#} (( COUNT )) && LINE="$LINE$CLMGR_DELIMITER" LINE="$LINE$upper_key" (( COUNT++ )) # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done if (( ${#LINE} > 2 )); then print -- "$LINE" STATUS=0 fi log_return_msg "$STATUS" "$0()" "$LINENO" return $? } # End of "display_colon_headers()" ############################################################################### # # Name: runClassProcessor # # Description: # # Inputs: class The object class for the operation being # attempted. # substitutions A list of the arguments passed in. # # Outputs: # # Returns: # ############################################################################### function runClassProcessor { . $HALIBROOT/log_entry "$0()" "$CL" max typeset class=$1 typeset -n substitutions=$2 typeset -n _ENV_ARGS=$3 [[ -n $4 ]] && typeset -n properties=$4 typeset -i rc=-1 typeset value= CL=$LINENO . $HAEVENTS/class_processors "$_ACTION" typeset processor="${_CLASS_PROCESSORS[$class]} " if [[ $processor == *([[:space:]]) ]]; then [[ " $CLASSES " != *\ $class\ * ]] && class=${class#*_} print "$SENTINEL COMMAND ($CLMGR_TRANSACTION_ID): ${CLMGR_COMMAND/ -T $CLMGR_TRANSACTION_ID}" >>$CLMGR_TMPLOG print "$SENTINEL INTERNAL ($CLMGR_TRANSACTION_ID): NOT FOUND!!" >>$CLMGR_TMPLOG cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 183 '\nERROR: unrecognized %1$s operation attempted: %2$s\n\n' "$CLMGR_PROGNAME" "$CLMGR_PROGNAME $_ACTION $class" 1>&2 log_return_msg "$RC_INCORRECT_INPUT" "$0()" "$LINENO" return $? fi processor=${processor//+([[:space:]])/ } #======================================================= # Perform variable substitution on the class processor #======================================================= for var in $substitutions; do # Avoid removing the properties array reference! [[ $var == "properties" ]] && continue : Remove the "this is a required argument" indicator var=${var%+} #================================================================= # If this class' processor requires a resource name, but the # user failed to provide one, be sure to send an empty string # in its place. If not, then the class processor will blindly # take the first argument, and either fail, or product incorrect # results. #================================================================= if [[ $var == "RESOURCE_NAME" && \ ${_ENV_ARGS[$var]} == *([[:space:]]) ]] then _ENV_ARGS[$var]='""' fi [[ " $processor " != *\ $var?(\+)\ * ]] && continue value=${_ENV_ARGS[$var]} if [[ $value == *[[:space:]]* ]]; then processor=${processor/[[:space:]]${var}?(\+)[[:space:]]/ \"$value\" } else processor=${processor/[[:space:]]${var}?(\+)[[:space:]]/ $value } fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done if [[ $processor == *([[:space:]]) ]]; then [[ " $CLASSES " != *\ $class\ * ]] && class=${class#*_} print "$SENTINEL COMMAND ($CLMGR_TRANSACTION_ID): ${CLMGR_COMMAND/ -T $CLMGR_TRANSACTION_ID}" >>$CLMGR_TMPLOG print "$SENTINEL INTERNAL ($CLMGR_TRANSACTION_ID): NOT FOUND!!" >>$CLMGR_TMPLOG cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 183 '\nERROR: unrecognized %1$s operation attempted: %2$s\n\n' "$CLMGR_PROGNAME" "$CLMGR_PROGNAME $_ACTION $class" 1>&2 log_return_msg "$RC_INCORRECT_INPUT" "$0()" "$LINENO" return $? fi #================================================================ # Check for a cry for help from the customer! We accept h,H,*,? # to ask for help. We also accept an optional "v", but that is # intended for internal use only (because the text it displays # is *not* translated), and is not documented. #================================================================ typeset -l lcarg= lcargs= for lcarg in $_EVENT_SCRIPT_ARGS; do [[ $lcarg == *=* ]] && continue [[ $lcarg != -* && $lcarg != @(\*|\?) ]] && continue lcarg=${lcarg##*-} # Strip off any leading dashes lcargs="$lcargs $lcarg" # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done typeset -i CLMGR_HELP=0 for lcarg in $lcargs; do [[ $lcarg == *@(h|\*|\?)* && $_ACTION != "runcmd" ]] && CLMGR_HELP=1 [[ $lcarg == *v* ]] && CLMGR_VERBOSE=1 # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done if (( CLMGR_HELP )); then typeset file=${processor%%[[:space:]]*} if [[ $file != KLIB_HACMP_* && -f $HAEVENTS/resource_${_ACTION} ]] then typeset -i found=0 while read LINE; do if (( ! found )); then if [[ $LINE == function+([[:space:]])${file}*([[:space:]])* ]]; then found=1 fi else if [[ $LINE == *([[:space:]])KLIB_HACMP_* ]]; then file=${LINE#+([[:space:]])} file=${file%%[[:space:]]*} break fi fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done < $HAEVENTS/resource_${_ACTION} fi typeset POD= if (( CLMGR_VERBOSE )); then POD=$(which perldoc 2>&1) if [[ -z $POD || ! -f $POD && ! -x $POD ]]; then POD=$(which pod2text 2>&1) fi fi if [[ -f /usr/es/lib/ksh93/hacmp/$file && \ -n $POD && -f $POD && -x $POD ]] && \ (( CLMGR_VERBOSE )) then $POD /usr/es/lib/ksh93/hacmp/$file fi typeset action=$_ACTION [[ $action == @(get|list) ]] && action="query" typeset -u TYPE=${_CLASS_PROCESSORS[$class]} TYPE=${TYPE%%[[:space:]]*} TYPE=${TYPE#*_} typeset -l TYPE_LC=$TYPE if [[ " $CLASSES " != *\ $TYPE_LC\ * ]]; then TYPE=${TYPE#*_} TYPE_LC=$TYPE [[ " $CLASSES " != *\ $TYPE_LC\ * ]] && TYPE=${TYPE#*_} TYPE_LC=$TYPE fi case $TYPE in APPSERVER) TYPE="APPLICATION_CONTROLLER" ;; APPMONITOR) TYPE="APPLICATION_MONITOR" ;; RESOURCEGROUP) TYPE="RESOURCE_GROUP" ;; FILECOLLECTION) TYPE="FILE_COLLECTION" ;; esac TYPE_LC=$TYPE [[ -n $_SUB_ACTION ]] && TYPE_LC="$TYPE_LC $_SUB_ACTION" cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 4 '# Available options for "clvt %1$s %2$s":\n' "$action" "$TYPE_LC" "$CLMGR_PROGNAME" typeset ACTIONS_EXPR="${ACTIONS// /\|}|stop|start" if [[ $action == "manage" ]]; then ACTIONS_EXPR="$ACTIONS_EXPR|collect|discover|reset|restore|resume|unlock|undo|security|hmc|roha|suspend|nova|cloudroha" fi for VAR in ${_CLASS_PROCESSORS[$class]}; do [[ $VAR == "properties" ]] && continue # Internal use only! : Only display REPOSITORY if it is appropriate if [[ $VAR == *REPOS* && $_CLASS == @(cluster|site) ]] then if [[ $CLUSTER_TYPE == "LC" && $_CLASS == "cluster" ]] || \ [[ $CLUSTER_TYPE == *SC && $_CLASS == "site" ]] then continue fi fi if [[ $VAR == *@($ACTIONS_EXPR)_* ]]; then if [[ -z $TYPE ]]; then TYPE=${VAR#*@($ACTIONS_EXPR)_} if [[ $TYPE == APP@(MONITOR|SERVER) ]]; then TYPE=${TYPE/APP/APPLICATION_} fi fi elif [[ $VAR == RESOURCE_NAME?(\+) ]]; then if [[ $action != "collect" ]]; then if [[ $class == "cod" ]]; then VAR=${VAR/RESOURCE/APPLICATION_CONTROLLER} else VAR=${VAR/RESOURCE/$TYPE} fi if [[ $VAR == *\+ ]]; then print "<${VAR%+}>" # Required argument else print "[ <$VAR> ]" # Optional argument fi fi elif [[ $class != *_$VAR ]]; then if [[ $VAR != ?(-)@(h|H|\*|\?) ]]; then [[ $VAR != *\+ ]] && VAR="[ $VAR ]" print -- "${VAR%+}" fi fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done rc=0 else # Run the processor print "$SENTINEL COMMAND ($CLMGR_TRANSACTION_ID): ${CLMGR_COMMAND/ -T $CLMGR_TRANSACTION_ID}" >>$CLMGR_TMPLOG print "$SENTINEL INTERNAL ($CLMGR_TRANSACTION_ID): $processor" >>$CLMGR_TMPLOG : Running "$class" processor: $processor $_NAME_VALUE_PAIRS eval CL=$LINENO ${processor//\;/\\\;} ${_NAME_VALUE_PAIRS//\;/\\\;} rc=$? if (( $rc == 127 )); then [[ " $CLASSES " != *\ $class\ * ]] && class=${class#*_} cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 183 '\nERROR: unrecognized %1$s operation attempted: %2$s\n\n' "$CLMGR_PROGNAME" "$CLMGR_PROGNAME $_ACTION $class" 1>&2 rc=$RC_INCORRECT_INPUT fi print "$SENTINEL RETURN ($CLMGR_TRANSACTION_ID): $rc" >>$CLMGR_TMPLOG fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "runClassProcessor()" ############################################################################### # # Name: is_enterprise # # Description: determines whether or not the locally installed SystemMirror # software is Enterprise Edition. # # Inputs: None. # # Outputs: None. # # Returns: 0 if Enterprise edition, or 1 if not. # ############################################################################### function is_enterprise { . $HALIBROOT/log_entry "$0()" "$CL" max typeset -i ENTERPRISE=$RC_UNKNOWN CL=$LINENO isEnterprise (( $? == 1 )) && ENTERPRISE=$RC_SUCCESS || ENTERPRISE=$RC_ERROR log_return_msg "$ENTERPRISE" "$0()" "$LINENO" return $? } # End of "is_enterprise()" #============================================================================= # Name: expand_keyword # # Description: Parses the provided list of known, valid attributes for the # current resource class, looking for a match for the specified # keyword. A "match" is defined as an attribute that begins with # the keyword, irrespective of case. The *entire* list will be # searched, even if a match is found early on. This is to ensure # that there is exactly *one* match, and that the user has not # provided an ambiguous entry. # # Inputs: key The keyword that needs to be expanded. # list The list of known keywords for the current class. # expanded The expanded word. Optional. # # Outputs: If the keyword is found, and expands normally (or needs no # expansion at all), then there will be no output at all. # If the keyword is not found, or expands to more than one # known attribute, then an appropriate error message will be # written to STDERR. # # Returns: 0 if the keyword is found, and expands to a single match, # or is an exact match. # 1 if the keyword is not found. # 2 if the keyword is ambiguous, matching more than one attr. #============================================================================= function expand_keyword { . $HALIBROOT/log_entry "$0()" "$CL" max typeset key=$1 typeset -n list=$2 if [[ -n $3 ]]; then typeset -n expanded=$3 fi typeset -u attr_uc= key_uc=$key typeset match= typeset -i matches=0 rc=$RC_ERROR #===================================================================== # To preserve backwards compatibility with previous versions of clvt # establish a keyword compatibility map. Each key is the old way of # specifying an attribute, and each value is the new attribute name. # All this is necessary to avoid breaking migration (what happens if # the customer migrations the base product and clvt, but *not* their # existing Smart Assists?). Of course, perhaps even more important, # and certainly more likely to occur, is avoiding breaking any pre- # existing customer scripts! An angry customer is a scary customer!! #===================================================================== if [[ -n ${COMPAT[$key_uc]} ]]; then #=============================================================== # clmgr wants to treat "DISK" and "PHYSICAL_VOLUME" as one and # the same. However, one exception is for the "modify rg" cmd, # where "DISK" represents a raw disk (no LVM involvement). So # "DISK" needs to be accepted as-is for that one operation. #=============================================================== typeset -i CONVERT=1 if [[ $key_uc == DISK*(S) && \ $_ACTION == @(add|modify) && \ $RESOURCE_CLASS == @(mirror|resource)_group ]] then CONVERT=0 fi if (( CONVERT )); then _ENV_ARGS[${COMPAT[$key_uc]}]=${_ENV_ARGS[$key]} unset _ENV_ARGS[$key] key=${COMPAT[$key_uc]} key_uc=$key fi fi for attr_uc in $list; do attr_uc=${attr_uc%+} [[ $attr_uc == @(PROPERTIES|RESOURCE_NAME) ]] && continue if [[ $attr_uc == $key_uc ]]; then # Exact match matches=1 match="$attr_uc" break elif [[ $attr_uc == $key_uc* ]]; then # Possible match (( matches++ )) [[ -n $match ]] && match="$match " match="$match$attr_uc" fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done if (( matches == 1 )); then if [[ $match != $key_uc ]]; then expanded=$match #============================================== # Replace the short keyword with the expanded # keyword in the "_ENV_ARGS" data structure. #============================================== _ENV_ARGS[$match]=${_ENV_ARGS[$key]} unset _ENV_ARGS[$key] #============================================== # Replace the short keyword with the expanded # keyword in the "_NAME_VALUE_PAIRS" string. #============================================== for akey in $_NAME_VALUE_PAIRS; do [[ $akey != *=* ]] && continue akey=${akey%%=*} akey=${akey##*+([[:space:]])} typeset -u akey_uc=$akey if [[ $akey_uc == $key ]]; then _NAME_VALUE_PAIRS=${_NAME_VALUE_PAIRS//$akey=/$match=} break fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done fi rc=$RC_SUCCESS elif (( matches > 1 )); then rc=$RC_INCORRECT_INPUT dspmsg -s $CLMGR_SET $CLMGR_MSGS 31 "\nERROR: an ambiguous \"%1\$s\" option was detected: \"%2\$s\"\n Matching Options: %3\$s\n" "$_ACTION ${RESOURCE_CLASS#${_ACTION}_}" "$key" "${match//+([[:space:]])/, }" 1>&2 unset _ENV_ARGS[$key] else rc=$RC_INCORRECT_INPUT if [[ $_ACTION == "list" && ${RESOURCE_CLASS#${_ACTION}_} == query_* ]]; then _ACTION="query" fi class=${RESOURCE_CLASS#${_ACTION}_} dspmsg -s $CLMGR_SET $CLMGR_MSGS 32 "\nERROR: an unrecognized \"%1\$s\" option was detected: %2\$s\n" "$_ACTION $class" "$key" 1>&2 unset _ENV_ARGS[$key] fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "expand_keyword()" #============================================================================= # Name: display_simple_help # # Description: Displays a simple list of available input options for the # current operation. An attempt is made to indicate if the # options are required or optional, based upon information # gleaned from the class_processors file. However, that # information can't always be perfectly accurate, so caveat # emptor. # # Inputs: None of the inputs for this function are explicit. Meaning # they are not passed in directly, but rather are global # environment variables. # # CLMGR_HELP A Boolean indicating whether or not # this function is being invoked due # to an explicit request for help. If # not, then it is assumed that an error # has occurred. # # _ACTION The action (i.e. "add", "modify", ...) # being attempted/queried. # # RESOURCE_CLASS The class that the action is being run # on (i.e. "cluster", "resource_group", ...). # # Outputs: If this is a help request, the help text is displayed on # STDOUT. If it is not a help request, then an error condition # is assumed, and the help text is displayed on STDERR. # # Returns: 0 #============================================================================= function display_simple_help { typeset -i FH=1 (( ! CLMGR_HELP )) && FH=2 typeset action=$_ACTION [[ $action == @(get|list) ]] && action="query" if [[ -z ${!_COLON_ATTR_ORDER[*]} ]]; then print "$0()[$LINENO]($SECONDS): Loading: . $HAEVENTS/resource_common $RESOURCE_CLASS" >>$CLMGR_TMPLOG . $HAEVENTS/resource_common $RESOURCE_CLASS fi typeset class=$RESOURCE_CLASS typeset -u TYPE=$class typeset -l TYPE_LC=$TYPE if [[ " $CLASSES " != *\ $TYPE_LC\ * ]]; then TYPE=${TYPE#*_} TYPE_LC=$TYPE if [[ " $CLASSES " != *\ $TYPE_LC\ * ]]; then TYPE=${TYPE#*_} TYPE_LC=$TYPE if [[ " $CLASSES " != *\ $TYPE_LC\ * ]]; then TYPE=${TYPE#*_} TYPE_LC=$TYPE fi fi fi case $TYPE in APPSERVER) TYPE="APPLICATION_CONTROLLER" ;; APPMONITOR) TYPE="APPLICATION_MONITOR" ;; RESOURCEGROUP) TYPE="RESOURCE_GROUP" ;; FILECOLLECTION) TYPE="FILE_COLLECTION" ;; esac print "$0()[$LINENO]($SECONDS): TYPE == $TYPE" >>$CLMGR_TMPLOG TYPE_LC=$TYPE [[ -n $_SUB_ACTION ]] && TYPE_LC="$TYPE_LC $_SUB_ACTION" (( ! CLMGR_HELP )) && print -u2 if (( CLMGR_HELP )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 4 '# Available options for "clvt %1$s %2$s":\n' "$action" "$TYPE_LC" "$CLMGR_PROGNAME" print "" else cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 4 '# Available options for "clvt %1$s %2$s":\n' "$action" "$TYPE_LC" "$CLMGR_PROGNAME" 1>&2 print -u2 "" fi typeset attr_list="${_CLASS_PROCESSORS[$class]}" attr_list=${attr_list#*[[:space:]]} # Remove script/func name if [[ $_ACTION == "manage" ]]; then attr_list=${_CLASS_PROCESSORS[${_SUB_ACTION}_$class]} fi TYPE=$class # Restore the original class, in caps case "$TYPE" in QUERY_*) TYPE=${TYPE#QUERY_} if [[ -n ${!_ATTR_ORDER[${class#query_}]} ]] && \ ! [[ ${!_ATTR_ORDER[${class#query_}]} == *null* || ${!_ATTR_ORDER[@]} != *${class#query_}* ]] then attr_list="${_ATTR_ORDER[${class#query_}]}" else attr_list="${_COLON_ATTR_ORDER[${class#query_}]}" fi ;; LIST_*) TYPE=${TYPE#LIST_} ;; esac typeset ACTIONS_EXPR="${ACTIONS// /\|}|stop|start" if [[ $action == "manage" ]]; then ACTIONS_EXPR="$ACTIONS_EXPR|collect|discover|reset|restore|resume|unlock|undo|security|hmc|roha|suspend|nova|cloudroha" fi for VAR in $attr_list; do [[ $VAR == "properties" ]] && continue # Internal use only! : Only display REPOSITORY if it is appropriate if [[ $VAR == *REPOS* && $_CLASS == @(cluster|site) ]] then if [[ $CLUSTER_TYPE == "LC" && $_CLASS == "cluster" ]] || \ [[ $CLUSTER_TYPE == *SC && $_CLASS == "site" ]] then continue fi fi if [[ $VAR == *@($ACTIONS_EXPR)_* ]]; then if [[ -z $TYPE ]]; then TYPE=${VAR#*@($ACTIONS_EXPR)_} if [[ $TYPE == APP@(MONITOR|SERVER) ]]; then TYPE=${TYPE/APP/APPLICATION_} fi fi elif [[ $VAR == RESOURCE_NAME*(\+) ]]; then if [[ $class == "cod" ]]; then VAR=${VAR/RESOURCE/APPLICATION_CONTROLLER} else VAR=${VAR/RESOURCE/$TYPE} fi if [[ $VAR == *\+ ]]; then # Ending with a "+" means this is a required argument print -u$FH "<${VAR%+}>" else # Ending without a "+" means this is an optional argument print -u$FH "[ <$VAR> ]" fi else if [[ $VAR != ?(-)@(h|H|\*|\?) ]]; then [[ $VAR != *\+ ]] && VAR="[ $VAR ]" print -u$FH -- "${VAR%+}" fi fi # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done (( ! CLMGR_HELP )) && print -u2 print "$0()[$LINENO]($SECONDS): Final return code: 0" >>$CLMGR_TMPLOG return 0 } # End of "display_simple_help()" #============================================================================= # Name: display_operation_syntax # # Description: Attempts to display true syntactical help for the current # operation, if the appropriate FPATH function file can be # identified, and successfully parsed. # # Inputs: Most of the inputs for this function are implicit. Meaning # they are not passed in directly, but rather are global # environment variables. There is only one explicit input, # "BAD_OPTS". # # BAD_OPTS A Boolean value indicating whether or # not this function is being invoked due # to an explicit request for help, or in # response to a detection of bad input. # # _ACTION The action (i.e. "add", "modify", ...) # being attempted/queried. # # class The class that the action is being run # on (i.e. "cluster", "resource_group", ...). # # Outputs: If this is a help request, the help text is displayed on # STDOUT. If it is not a help request, then an error condition # is assumed, and the help text is displayed on STDERR. # # Returns: 0 if the sytnax could be found and displayed. # 2 if the syntax was not displayed for some reason. #============================================================================= function display_operation_syntax { typeset -i BAD_OPTS=$1 typeset -i rc=$RC_UNKNOWN typeset FH=1 (( BAD_OPTS )) && FH=2 typeset ORIG_CLASS=$class convert_for_filename ORIG_CLASS=${ORIG_CLASS#query_} typeset action=$_ACTION [[ $action == @(get|list) ]] && action="query" if [[ $_ACTION == @(sync|verify) && $ORIG_CLASS == *cluster ]] then class="cluster" ORIG_CLASS=$class elif [[ $_ACTION == @(modify|sync) && $ORIG_CLASS == *file_collection ]] then class="file_collection" ORIG_CLASS=$class fi if [[ $SRC_FILE == *filecollection ]]; then SRC_FILE=${SRC_FILE/filecollection/file_collection} elif [[ $SRC_FILE == *sync_sync_file_collection ]]; then SRC_FILE=${SRC_FILE/sync_sync/sync} fi if [[ ! -f $SRC_FILE ]]; then SRC_FILE=$(print -- "$SRC_FILE" | sed 's/\(.*\)_/\1/') fi print "$0()[$LINENO]($SECONDS): SRC_FILE == $SRC_FILE" >>$CLMGR_TMPLOG if [[ -f $SRC_FILE ]]; then rc=$RC_SUCCESS typeset LINE="" SYNTAX="" if [[ $_ACTION == @(sync|verify) && $class == *cluster ]] then typeset -u ACTION_UC=$_ACTION print "$0()[$LINENO]($SECONDS): podselect -section SYNOPSIS_$ACTION_UC $SRC_FILE | sed -e '1d'" >>$CLMGR_TMPLOG SYNTAX=$(podselect -section SYNOPSIS_$ACTION_UC $SRC_FILE | sed -e '1d') print "$0()[$LINENO]($SECONDS): SYNOPSIS_$ACTION_UC length: ${#SYNTAX}" >>$CLMGR_TMPLOG else print "$0()[$LINENO]($SECONDS): podselect -section SYNOPSIS $SRC_FILE | sed -e '1d'" >>$CLMGR_TMPLOG SYNTAX=$(podselect -section SYNOPSIS $SRC_FILE | sed -e '1d') print "$0()[$LINENO]($SECONDS): SYNOPSIS length: ${#SYNTAX}" >>$CLMGR_TMPLOG fi if [[ -n $SYNTAX ]]; then # : For the "add/modify cluster" syntax, there are values required : that vary based on the level of AIX. So make an attempt to look : them up here, and if found, replace the textual placeholders in : the syntax message with the actual values. # if [[ $class == "cluster" && $_ACTION == @(add|modify) ]] then typeset GP_MIN="" GP_MAX="" JUNK="" clctrl -tune -L node_down_delay 2>>$CLMGR_TMPLOG | grep -w node_down_delay | read JUNK JUNK GP_MIN GP_MAX JUNK if [[ $GP_MIN == +([0-9]) && $GP_MAX == +([0-9]) ]] then (( GP_MIN /= 1000 )) (( GP_MAX /= 1000 )) SYNTAX=${SYNTAX// GRACE_PERIOD=\<+([[:alnum:]])+(\.)+([[:alnum:]])\>/ GRACE_PERIOD=\<$GP_MIN..$GP_MAX\>} fi typeset HF_MIN="" HF_MAX="" clctrl -tune -L node_timeout 2>>$CLMGR_TMPLOG | grep -w node_timeout | read JUNK JUNK HF_MIN HF_MAX JUNK if [[ $HF_MIN == +([0-9]) && $HF_MAX == +([0-9]) ]] then (( HF_MIN /= 1000 )) (( HF_MAX /= 1000 )) SYNTAX=${SYNTAX//HEARTBEAT_FREQUENCY=\<+([[:alnum:]])+(\.)+([[:alnum:]])\>/HEARTBEAT_FREQUENCY=\<$HF_MIN..$HF_MAX\>} fi fi typeset FIRST_WORD="" print -- "$SYNTAX" |\ while IFS="" read -r LINE; do #============================================================= # Do not display non-syntax lines. A syntax line must adhere # to the following "rules": # # * ends with a "\", "]", "}", or "|" (trailing spaces # are ignored) # * contains a list and continuation char: a|b|c \ # * contains a VAR=VAL pair #============================================================= if [[ $LINE != *[\\\]\}\|]*([[:space:]]) && \ $LINE != *+([a-zA-Z0-9_])\|*(*([[:space:]])\\)*([[:space:]]) && \ $LINE != +([[:space:]])+([a-zA-Z0-9_])=* && \ $LINE != *([[:space:]])clmgr\ * ]] then continue fi if [[ $FIRST_WORD == *([[:space:]]) ]]; then FIRST_WORD=${LINE##+([[:space:]])} FIRST_WORD=${FIRST_WORD%%+([[:space:]])*} fi if [[ $LINE == *([[:space:]])$FIRST_WORD\ * ]]; then print -u$FH "" if [[ $FIRST_WORD != "clmgr" && \ " $ACTIONS " == *\ $FIRST_WORD\ * ]] then LINE=${LINE/$FIRST_WORD/clmgr $FIRST_WORD} fi fi [[ $LINE != \ * ]] && LINE=" $LINE" print -u$FH -- "$LINE" done if (( ! BAD_OPTS )); then typeset -A _ALIAS_MAP _ALIAS_MAP=( [add]="build, bld, create, make, mk" [compare]="co, dif" [discover]="dis" [delete]="erase, remove, rm" [list]="ls" [manage]="mg" [modify]="change, set" [move]="mv" [offline]="stop, deactivate" [online]="start, activate" [query]="get, show" [recover]="rc" [replace]="rp, swap, switch" [sync]="propagate" [verify]="validate, test" [view]="cat" [application_controller]="ac, app, appctl" [application_monitor]="am, mon, appmon" [backup_files]="bf, backup_f" [cluster]="cl" [cod]="cuod, dlpar, roha" [dependency]="de" [event]="ev" [fallback_timer]="fa, ft, timer" [file_collection]="fc" [file_system]="fs" [group]="gp" [interface]="in, if" [ldap_client]="lc" [ldap_server]="ls" [logical_volume]="lv" [method]="me" [mirror_group]="mig" [mirror_pair]="mip" [mirror_pool]="mp, pool" [network]="ne, nw" [node]="no" [persistent_ip]="pi" [physical_volume]="pv, disk" [repository]="rp" [resource_group]="rg" [service_ip]="si" [site]="st" [smart_assist]="sm, sa" [snapshot]="sn, ss" [storage_agent]="sta" [storage_system]="sts" [tape]="tp" [user]="ur" [volume_group]="vg" [backup_profile]="bp, backup_p, replication_profile" ) if [[ -n ${_ALIAS_MAP[$action]} ]]; then print -u$FH "$NL $action => ${_ALIAS_MAP[$action]}" if [[ -n ${_ALIAS_MAP[$ORIG_CLASS]} ]]; then [[ $class == "changes" ]] && class=$ORIG_CLASS print -u$FH " $ORIG_CLASS => ${_ALIAS_MAP[$ORIG_CLASS]}$NL" else print -u$FH "" fi elif [[ -n ${_ALIAS_MAP[$ORIG_CLASS]} ]]; then print -u$FH "$NL $class => ${_ALIAS_MAP[$ORIG_CLASS]}$NL" else print -u$FH "" fi unset _ALIAS_MAP else print -u$FH "" fi else rc=$RC_NOT_FOUND fi else rc=$RC_NOT_FOUND fi print "$0()[$LINENO]($SECONDS): Final return code: $rc" >>$CLMGR_TMPLOG return $rc } # End of "display_operation_syntax()" #============================================================================= # Name: display_operation_man_page # # Description: Attempts to display the section of the man page that is # relevant for the current operation, if the appropriate # information can be found in the man page. # # Inputs: None of the inputs for this function are explicit. Meaning # they are not passed in directly, but rather are global # environment variables. # # _ACTION The action (i.e. "add", "modify", ...) # being attempted/queried. # # class The class that the action is being run # on (i.e. "cluster", "resource_group", ...). # # Outputs: If successful, opens the clmgr man page into a pager, # either "less" or "more", and jumps to the appropriate # section of the document for the current operation. # # Returns: 0 if the man page was found, and successfully loaded/displayed. # 2 if the man page was not displayed for some reason. #============================================================================= function display_operation_man_page { typeset -i rc=$RC_UNKNOWN typeset MANFILE="/usr/share/man/info/EN_US/a_doc_lib/cmds/powerha_cmds/clmgr.htm" if [[ -f $MANFILE ]]; then typeset PATTERN="" export MANPATH="/usr/share/man:/usr/man:$MANPATH" # Validate any user-specified pager if [[ $PAGER != *([[:space:]]) ]]; then print "$0()[$LINENO]($SECONDS): User-specified PAGER is \"$PAGER\"." >>$CLMGR_TMPLOG if [[ $PAGER == */* && ! -x $PAGER ]]; then print "$0()[$LINENO]($SECONDS): ERROR: the current PAGER value, \"$PAGER\", is invalid." >>$CLMGR_TMPLOG print "$0()[$LINENO]($SECONDS): \"$PAGER\" is either not executable, or does not exist at all." >>$CLMGR_TMPLOG print "$0()[$LINENO]($SECONDS): An attempt will be made to find an alternate pager." >>$CLMGR_TMPLOG PAGER="" elif ! type -a ${PAGER%% *} >/dev/null 2>&1 then print "$0()[$LINENO]($SECONDS): ERROR: the current PAGER value, \"$PAGER\", is invalid." >>$CLMGR_TMPLOG print "$0()[$LINENO]($SECONDS): \"$PAGER\" could not be found." >>$CLMGR_TMPLOG print "$0()[$LINENO]($SECONDS): An attempt will be made to find an alternate pager." >>$CLMGR_TMPLOG PAGER="" fi fi # Select a pager based from worst to best if [[ $PAGER == *([[:space:]]) ]]; then PAGER=/usr/bin/pg [[ -f /usr/bin/more ]] && PAGER="/usr/bin/more" [[ -f /usr/bin/less ]] && PAGER="/usr/bin/less" fi if [[ $PAGER == *?(/)@(more|less) ]]; then PAGER="${PAGER%% *} -p" fi # Perform any necessary class conversion, to # fit what exists in the man page information. case $class in query_*) _ACTION=${class%%_*} class=${class#*_} ;; esac integer FOUND=0 for PATTERN in "clmgr $_ACTION ${class}s" \ "clmgr $_ACTION $class" do typeset LINE="" if [[ -n $_SUB_ACTION ]]; then LINE=$(egrep "$PATTERN" $MANFILE |\ egrep $_SUB_ACTION |\ egrep "^[[:space:]]+clmgr ") else LINE=$(egrep "$PATTERN" $MANFILE) fi LINE=${LINE%%${NL}*} if [[ -n $LINE ]]; then (( FOUND++ )) if [[ $PAGER == *pg ]]; then PATTERN="+/$PATTERN/" elif [[ $PAGER == *more* ]]; then PATTERN="/$PATTERN" fi print "$0()[$LINENO]($SECONDS): man clmgr | $PAGER \"$PATTERN\"" >>$CLMGR_TMPLOG man clmgr | $PAGER "$PATTERN" rc=$? print "$0()[$LINENO]($SECONDS): $PAGER RC: $rc" >>$CLMGR_TMPLOG break fi done if (( ! FOUND )); then print "$0()[$LINENO]($SECONDS): man clmgr | ${PAGER%% *}" >>$CLMGR_TMPLOG man clmgr | ${PAGER%% *} rc=$? print "$0()[$LINENO]($SECONDS): ${PAGER%% *} RC: $rc" >>$CLMGR_TMPLOG (( rc != RC_SUCCESS )) && rc=$RC_NOT_FOUND fi else rc=$RC_NOT_FOUND fi print "$0()[$LINENO]($SECONDS): Final return code: $rc" >>$CLMGR_TMPLOG return $rc } # End of "display_operation_man_page()" #============================================================================= # Name: convert_for_filename # # Description: A helper function that is used to try to help identify # the matching source file for the current operation. # # Inputs: None of the inputs for this function are explicit. Meaning # they are not passed in directly, but rather are global # environment variables. # # _ACTION The action (i.e. "add", "modify", ...) # being attempted/queried. # # _SUB_ACTION A secondary action, used in conjunction # with the "manage" action. # # class The class that the action is being run # on (i.e. "cluster", "resource_group", ...). # # Outputs: Changes the "class" environment variable, if needed. # Sets the SRC_FILE environment variable to the absolute # path of the FPATH function file for this operation. # # Returns: 0 if the source file is found. Otherwise, 2. #============================================================================= function convert_for_filename { if [[ $_ACTION == "manage" ]]; then case $_SUB_ACTION in collect) class="${class}s" ;; resume) class="app_monitoring" ;; suspend) class="app_monitoring" ;; discover) [[ $class == "cluster" ]] && _SUB_ACTION="run" && class="discovery" ;; rehearsals) [[ $class == "cluster" ]] && _SUB_ACTION="cluster" && class="rehearsals" ;; security) _SUB_ACTION="modify" && class="cluster_security" ;; hmc) _SUB_ACTION="modify" && class="cluster_hmc" ;; roha) _SUB_ACTION="modify" && class="cluster_roha" ;; nova) _SUB_ACTION="modify" && class="cluster_nova" ;; cloudroha) _SUB_ACTION="modify" && class="cluster_cloudroha" ;; undo_changes) [[ $class == "node" ]] && _SUB_ACTION="undo_cfg" && class="changes" ;; esac SRC_FILE=$HALIBROOT/hacmp/KLIB_HACMP_${_SUB_ACTION}_${class} elif [[ $_ACTION == @(sync|verify) && $class == *cluster ]]; then : KLIB_HACMP_verify_and_sync handles verification and synchronization SRC_FILE=$HALIBROOT/hacmp/KLIB_HACMP_verify_and_sync elif [[ $_ACTION == "discover" ]]; then : KLIB_HACMP_run_discovery handles all discovery SRC_FILE=$HALIBROOT/hacmp/KLIB_HACMP_run_discovery else class=${class#query_} case $class in file_collection) class="filecollection" ;; application_controller) class="appserver" ;; application_monitor) class="appmonitor" ;; esac case $_ACTION in query) if [[ $class == "backup_files" ]] then _ACTION="list" # As there is no file called KLIB_HACMP_get_smart_assist making SRC_FILE name as KLIB_HACMP_list_smart_assists # while querying for smart assists elif [[ $class == "smart_assist" ]] then _ACTION="list" class="${class}s" else [[ $class == "resource_group" ]] && class="rg" _ACTION="get" && class="${class}_attributes" fi ;; esac SRC_FILE=$HALIBROOT/hacmp/KLIB_HACMP_${_ACTION}_${class} fi typeset -i rc=$RC_SUCCESS [[ -z $SRC_FILE || ! -f $SRC_FILE ]] && rc=$RC_NOT_FOUND return $rc } # End of "convert_for_filename()" ######################################################################### ######################################################################### ## ## MAIN Main main ## ######################################################################### ######################################################################### export HALIBROOT=/usr/es/lib/ksh93 . $HALIBROOT/log_entry resource_common "$CL" max #============================================================= : Attempt to avoid redundant loads of the func_include file. : It is large, and runs some operations upon load that make : redundant loads an unwelcome performance hit. #============================================================= type prune_indexes >/dev/null 2>&1 if (( $? != 0 )) then CL=$LINENO . $HALIBROOT/func_include fi export CLMGR_MSGS=command.cat export CLVT_MSG=$CLMGR_MSGS export CLMGR_SET=2 export CLVT_SET=$CLMGR_SET typeset key typeset -u upper_key typeset -A _ENV_ARGS #============================================ # Establish the standard clmgr return codes. # Note these values should match those with # the same string representation in clmgr.h #============================================ ## 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 #============================================================================== # These lists, _COLON_ATTR_ORDER and _ATTR_ORDER, are used to specify the # ordering of the displayed attributes. The only difference between the # two is that _COLON_ATTR_ORDER is intended for colon-delimited output, # in which order is critical, and may NEVER BE CHANGED. New attributes # *must* be appended, not inserted!! The _ATTR_ORDER array is meant for # use where we badly wanted to change the output order... just not for # colon-delimited output (can't do it there!) So if a class needs a new # attribute, for example, but it logically fits in the middle of the # display of the existing attributes for that class, do this: # # 1. Append the new attribute to the end of the attributes list # in the _COLON_ATTR_ORDER array. # 2. Copy that line from _COLON_ATTR_ORDER and insert it into # the _ATTR_ORDER, defined below. Then in that array only, # move the new attribute to wherever you need it to be. # 3. Test the output in normal and XML format, confirming # the new attribute is in the desired position. # 4. Test the output in colon format, confirming that the new # attribute appears at the very end of the output. # # Only the attributes listed in these arrays will be displayed, even if # additional attributes are retrieved in the "getter" functions (those # will be ignored, even though present). All attributes *must* be defined # in _COLON_ATTR_ORDER, but _ATTR_ORDER is only necessary if the attribute # output sequence needs to be changed. # # NOTE: if any attributes have a numeric value as the last character(s), such # as the case with "EXPORT_FILESYSTEM_V4", then some code will need to # be added to "SerializeAsAssociativeArray()" and "search_properties()" # to properly handle them. Without this, the attributes are unlikely to # be displayed. #============================================================================== _COLON_ATTR_ORDER=( [cluster]="CLUSTER_NAME CLUSTER_ID STATE VERSION VERSION_NUMBER EDITION CLUSTER_IP UNSYNCED_CHANGES FC_SYNC_INTERVAL RG_SETTLING_TIME RG_DIST_POLICY MAX_EVENT_TIME MAX_RG_PROCESSING_TIME DAILY_VERIFICATION VERIFICATION_NODE VERIFICATION_HOUR VERIFICATION_DEBUGGING LEVEL ALGORITHM MECHANISM CERTIFICATE PRIVATE_KEY HEARTBEAT_FREQUENCY GRACE_PERIOD SITE_POLICY_FAILURE_ACTION SITE_POLICY_NOTIFY_METHOD SITE_HEARTBEAT_CYCLE SITE_GRACE_PERIOD SPLIT_POLICY MERGE_POLICY NFS_QUORUM_SERVER LOCAL_QUORUM_DIRECTORY REMOTE_QUORUM_DIRECTORY QUARANTINE_POLICY CRITICAL_RG ACTION_PLAN TIEBREAKER NOTIFY_METHOD NOTIFY_INTERVAL MAXIMUM_NOTIFICATIONS DEFAULT_SURVIVING_SITE APPLY_TO_PPRC_TAKEOVER HEARTBEAT_TYPE RESPONSE_NEEDED TEMP_HOSTNAME REPOSITORIES TYPE DEFAULT_HMC_TIMEOUT DEFAULT_HMC_RETRY_COUNT DEFAULT_HMC_RETRY_DELAY DEFAULT_HMCS_LIST ALWAYS_START_RG ADJUST_SPP_SIZE FORCE_SYNC_RELEASE AGREE_TO_COD_COSTS ONOFF_DAYS LPM_POLICY HEARTBEAT_FREQUENCY_DURING_LPM NETWORK_FAILURE_DETECTION_TIME AUTOMATIC_REPOSITORY_REPLACEMENT RESOURCE_ALLOCATION_ORDER HMC_CONNECTION_TYPE CAA_AUTO_START_DR DEFAULT_NOVA_TIMEOUT DEFAULT_NOVA_RETRY_COUNT DEFAULT_NOVA_RETRY_DELAY NOVA_CONNECTION_TYPE CAA_REPOS_MODE CAA_CONFIG_TIMEOUT LVM_PREFERRED_READ CRIT_DAEMON_RESTART_GRACE_PERIOD SKIP_EVENT_PROCESSING_MANAGE_MODE BUCKET_NAME CLOUD_SERVICE USE_EXISTING_BUCKET ONPREM_HYBRID_CLOUD" [repository]="NAME NODE SITE PVID UUID BACKUP TYPE DESCRIPTION SIZE AVAILABLE CONCURRENT ENHANCED_CONCURRENT_MODE STATUS" [site]="NAME GID STATE NODES SITE_IP RECOVERY_PRIORITY DOMINANT REPOSITORIES HMCS" [node]="NAME HOSTNAME GID STATE RAW_STATE COMMPATH VERSION VERSION_NUMBER EDITION AIX_LEVEL UNSYNCED_CHANGES IPLABEL IPADDRESS TYPE NETWORK NETTYPE NETMASK START_ON_BOOT BROADCAST_ON_START CLINFO_ON_START COMMUNICATION CAA_STATE LOCALHOST ENABLE_LIVE_UPDATE HMCS ENABLE_CAA_AFTER_MERGE NOVAS HOST_LABEL PERSISTENT_IPS CRIT_DAEMON_RESTART_GRACE_PERIOD CLOUD_BASED" [host]="HOSTNAME IPADDRESS HAVERSION VERSION_NUMBER HAEDITION AIX_LEVEL LOCALHOST" [network]="NAME GID NETMASK NET_FAMILY NETWORK_ID ATTR ALIAS RESOURCE_DIST_PREF POLLINTERVAL TYPE GLOBALNAME ALIAS_HB_NETMASK ALIAS_HB_ADDR MONITOR_METHOD PUBLIC INTERFACE INTERFACENAME IPADDR NODE INTERFACETYPE GLOBALNAME HADDR SOURCE_IP UNSTABLE_THRESHOLD UNSTABLE_PERIOD BOOT_ADAPTERS SERVICE_ADAPTERS PERSISTENT_ADAPTERS SINGLE_ADAPTER_NETWORK" [interface]="NAME INTERFACE IPADDR NETMASK PREFIX NETWORK NODE NETTYPE TYPE ATTR HWADDR STATE" [file_collection]="NAME DESCRIPTION SYNC_WITH_CLUSTER SYNC_WHEN_CHANGED FILES FILE SIZE" [log]="NAME DESCRIPTION DIRECTORY DEFAULT TYPE SIZE REMOTE_FS TRACE_LEVEL FORMATTING" [snapshot]="NAME DESCRIPTION METHODS CAPTURE_DATE CAPTURE_NODE CLUSTER_NAME CLUSTER_TYPE NODES SITES HOSTS SNAPSHOTPATH PAGER SDIFF_OUTPUT_WIDTH SDIFF_FLAGS MISC_INFO AVAILABLE_SECTIONS" [resource_group]="NAME CURRENT_NODE NODES STATE TYPE APPLICATIONS STARTUP FALLOVER FALLBACK NODE_PRIORITY_POLICY DISK VOLUME_GROUP FORCED_VARYON FILESYSTEM FSCHECK_TOOL RECOVERY_METHOD EXPORT_FILESYSTEM SHARED_TAPE_RESOURCES AIX_CONNECTIONS_SERVICES AIX_FAST_CONNECT_SERVICES COMMUNICATION_LINKS MOUNT_FILESYSTEM SERVICE_LABEL MISC_DATA SSA_DISK_FENCING VG_AUTO_IMPORT INACTIVE_TAKEOVER CASCADE_WO_FALLBACK FS_BEFORE_IPADDR NFS_NETWORK MOUNT_ALL_FS WLM_PRIMARY WLM_SECONDARY FALLBACK_AT EXPORT_FILESYSTEM_V4 STABLE_STORAGE_PATH WPAR_NAME VARYON_WITH_MISSING_UPDATES DATA_DIVERGENCE_RECOVERY SECONDARYNODES SECONDARY_STATE RELATIONSHIP SITE_POLICY MIRROR_GROUP NODE_PRIORITY_POLICY_SCRIPT NODE_PRIORITY_POLICY_TIMEOUT CURRENT_SECONDARY_NODE" [application_controller]="NAME ASSOCIATEDMONITORS MONITORS STARTSCRIPT STOPSCRIPT CPU_USAGE_MONITOR PROCESS_TO_MONITOR_CPU_USAGE CPU_USAGE_MONITOR_INTERVAL RESOURCE_GROUP MONITORING_STATUS RAW_MONITORING_STATUS STARTUP_MODE" [application_monitor]="NAME APPLICATIONS TYPE MODE MONITORMETHOD MONITORINTERVAL PROCESSES OWNER INSTANCECOUNT HUNGSIGNAL STABILIZATION RESTARTCOUNT RESTARTINTERVAL FAILUREACTION NOTIFYMETHOD CLEANUPMETHOD RESTARTMETHOD MONITORRETRYCOUNT AMLOGGING" [dependency]="NAME TYPE GROUPS ACQUIRE_SERIALLY ACQUIRE_IN_PARALLEL RELEASE_SERIALLY RELEASE_IN_PARALLEL PARENT CHILD STOP START AFTER HIGH INTERMEDIATE LOW" [fallback_timer]="NAME REPEATS YEAR MONTH MONTH_NUM DAY_OF_MONTH DAY_OF_WEEK DAY_OF_WEEK_NUM HOUR MINUTE" [file_system]="NAME TYPE VFS NODES VOLUME_GROUP RESOURCE_GROUP DEVICE SIZE OPTIONS AUTOMOUNT DISK_ACCOUNTING BLOCK_SIZE LV_FOR_LOG INLINE_LOG_SIZE EXT_ATTR_FORMAT ENABLE_QUOTA_MGMT EFS MOUNTGUARD FRAGMENT_SIZE BYTES_PER_INODE ALLOC_GROUP_SIZE PERMISSIONS LOGICAL_VOLUME" [logical_volume]="NAME NODES VOLUME_GROUP RESOURCE_GROUP MOUNT_POINT LABEL PERMISSION VG_STATE LV_STATE TYPE MAX_LPS COPIES LPS PPS PP_SIZE STALE_PPS WRITE_VERIFY MIRROR_WRITE_CONSISTENCY INTER-POLICY INTRA-POLICY RELOCATABLE UPPER_BOUND SCHED_POLICY BB_POLICY EACH_LP_COPY_ON_A_SEPARATE_PV SERIALIZE_IO IDENTIFIER FIRST_COPY_MIRROR_POOL SECOND_COPY_MIRROR_POOL THIRD_COPY_MIRROR_POOL PHYSICAL_VOLUMES SIZE ENCRYPTION" [method]="NAME TYPE DESCRIPTION FILE SOURCE CONTACT EVENTS NODES RETRY TIMEOUT" [persistent_ip]="NAME IPADDR NODE NETMASK PREFIX NETWORK INTERFACE NETTYPE TYPE ATTR GLOBALNAME HADDR" [physical_volume]="NAME PVID TYPE DESCRIPTION SIZE AVAILABLE CONCURRENT ENHANCED_CONCURRENT_MODE STATUS UUID VOLUME_GROUP SCSIPR_CAPABLE" [service_ip]="NAME IPADDR NODE NETMASK PREFIX NETWORK INTERFACE NETTYPE TYPE ATTR GLOBALNAME HWADDR SITE" [tape]="NAME DESCRIPTION DEVICE STARTSCRIPT START_SYNCHRONOUSLY STOPSCRIPT STOP_SYNCHRONOUSLY" [volume_group]="NAME TYPE NODES LOGICAL_VOLUMES PHYSICAL_VOLUMES MIRROR_POOLS STRICT_MIRROR_POOLS RESOURCE_GROUP AUTO_ACTIVATE QUORUM CONCURRENT_ACCESS CRITICAL FAILUREACTION NOTIFYMETHOD MIGRATE_FAILED_DISKS SYNCHRONIZE LOGICAL_TRACK_GROUP_SIZE MAX_PHYSICAL_PARTITIONS PPART_SIZE MAX_LOGICAL_VOLUMES MAJOR_NUMBER IDENTIFIER TIMESTAMP SCSIPR_CAPABLE LVM_PREFERRED_READ ENCRYPTION" [mirror_pool]="NAME VOLUME_GROUP PHYSICAL_VOLUMES STORAGE_LOCATION SUPER_STRICT MODE ASYNC_CACHE_LV ASYNC_CACHE_HW_MARK ASYNC_MIRROR_STATE ASYNC_CACHE_VALID ASYNC_DATA_DIVERGENCE ASYNC_CACHE_EMPTY ASYNC_CACHE_UTILIZATION CACHE_FREE_SPACE" [ldap_client]="SERVERS ADMIN_DN SUFFIX PORT SSL_KEY AUTH_TYPE" [ldap_server]="SERVERS ADMIN_DN BASE_DN SSL_KEY PORT SCHEMA VERSION" [user]="NAME ID ADMINISTRATIVE PRIMARY GROUPS ADMIN_GROUPS ROLES \ SWITCH_USER SU_GROUPS HOME SHELL INFO EXPIRATION LOCKED LOGIN \ REMOTE_LOGIN SCHEDULE MAX_FAILED_LOGINS AUTHENTICATION \ ALLOWED_TTYS DAYS_TO_WARN PASSWORD_VALIDATION_METHODS \ PASSWORD_FILTERS MIN_PASSWORDS REUSE_TIME LOCKOUT_DELAY \ MAX_PASSWORD_AGE MIN_PASSWORD_AGE MIN_PASSWORD_LENGTH \ MIN_PASSWORD_ALPHAS MIN_PASSWORD_OTHERS MAX_PASSWORD_REPEATED_CHARS\ MIN_PASSWORD_DIFFERENT REGISTRY UMASK AUDIT_CLASSES TRUSTED_PATH \ PRIMARY_AUTH SECONDARY_AUTH PROJECTS KEYSTORE_ACCESS \ ADMIN_KEYSTORE_ACCESS KEYSTORE_MODE ALLOW_MODE_CHANGE \ KEYSTORE_ENCRYPTION FILE_ENCRYPTION" [group]="NAME ID USERS ADMINS ADMINISTRATIVE REGISTRY PROJECTS KEYSTORE_MODE \ KEYSTORE_ENCRYPTION KEYSTORE_ACCESS" [efs]="MODE VOLUME_GROUP SERVICE_IP" [storage_agent]="NAME TYPE ADDRESSES USER PASSWORD \ ATTRIBUTES" [storage_system]="NAME TYPE VENDOR_ID WWNN SITE \ AGENTS ATTRIBUTES ROLE ADDRESSES PARTNER USER BACKUP_PROFILE SVC_CLUSTER_ID" [mirror_pair]="NAME FIRST_DISK SECOND_DISK" [mirror_group]="NAME TYPE RESOURCE_GROUP MODE RECOVERY STORAGE_SYSTEMS \ CONSISTENT VENDOR_ID ATTRIBUTES MG_TYPE HYPERSWAP_ENABLED \ HYPERSWAP_PRIORITY UNPLANNED_HS_TIMEOUT VOLUME_GROUPS RAW_DISKS \ SYS_MG_NODE REPOSITORY_MG_SITES REPOSITORY_MG_HS_DISK \ REPOSITORY_MG_NONHS_DISK MIRROR_PAIRS HORCM_INSTANCE \ HORCM_TIMEOUT PAIR_EVENT_TIMEOUT RESYNC STATE" [event]="NAME TYPE FILE DESCRIPTION NOTIFY_COMMAND \ PRE_EVENT_COMMAND POST_EVENT_COMMAND PREPOSTFAILS" [hmc]="NAME TIMEOUT RETRY_COUNT RETRY_DELAY NODES SITES \ STATUS VERSION USER_NAME PASSWORD" [cod]="NAME USE_DESIRED OPTIMAL_MEM OPTIMAL_CPU OPTIMAL_PU OPTIMAL_VP" [nova]="NAME TIMEOUT RETRY_COUNT RETRY_DELAY NODES STATUS \ VERSION USER_NAME PASSWORD MANAGED_SYSTEM" [backup_profile]="RESOURCE_GROUP ENABLE_BACKUP VOLUME_GROUP REPLICATED_RESOURCES \ STORAGE_NAME BUCKET_NAME TARGET_LOCATION \ BACKUP_METHOD CLOUD_SERVICE COMPRESSION BACKUP_FREQUENCY \ BACKUP_SCHEDULE INC_BACKUP_FREQ NOTIFYMETHOD ENCRYPTION \ BACKUP_PROGRESS NEXT_BACKUP_SCHEDULE" ) _COLON_ATTR_ORDER[filecollection]="${_COLON_ATTR_ORDER[file_collection]}" #======================================================================== : This structure is not used for colon-delimited output, so the order : of the attributes may be safely adjusted, as-needed. Make sure all : attributes added to this list are *also* added to the colon-delimited : sequence specified in _COLON_ATTR_ORDER, defined above. #======================================================================== _ATTR_ORDER=( [cluster]="CLUSTER_NAME CLUSTER_ID STATE TYPE HEARTBEAT_TYPE CLUSTER_IP REPOSITORIES VERSION VERSION_NUMBER EDITION UNSYNCED_CHANGES FC_SYNC_INTERVAL RG_SETTLING_TIME RG_DIST_POLICY MAX_EVENT_TIME MAX_RG_PROCESSING_TIME TEMP_HOSTNAME DAILY_VERIFICATION VERIFICATION_NODE VERIFICATION_HOUR VERIFICATION_DEBUGGING LEVEL ALGORITHM MECHANISM CERTIFICATE PRIVATE_KEY HEARTBEAT_FREQUENCY GRACE_PERIOD SITE_POLICY_FAILURE_ACTION SITE_POLICY_NOTIFY_METHOD SITE_HEARTBEAT_CYCLE SITE_GRACE_PERIOD SPLIT_POLICY MERGE_POLICY NFS_QUORUM_SERVER LOCAL_QUORUM_DIRECTORY REMOTE_QUORUM_DIRECTORY QUARANTINE_POLICY CRITICAL_RG ACTION_PLAN TIEBREAKER NOTIFY_METHOD NOTIFY_INTERVAL MAXIMUM_NOTIFICATIONS DEFAULT_SURVIVING_SITE APPLY_TO_PPRC_TAKEOVER RESPONSE_NEEDED DEFAULT_HMC_TIMEOUT DEFAULT_HMC_RETRY_COUNT DEFAULT_HMC_RETRY_DELAY DEFAULT_HMCS_LIST ALWAYS_START_RG ADJUST_SPP_SIZE FORCE_SYNC_RELEASE AGREE_TO_COD_COSTS ONOFF_DAYS LPM_POLICY HEARTBEAT_FREQUENCY_DURING_LPM NETWORK_FAILURE_DETECTION_TIME AUTOMATIC_REPOSITORY_REPLACEMENT RESOURCE_ALLOCATION_ORDER HMC_CONNECTION_TYPE CAA_AUTO_START_DR DEFAULT_NOVA_TIMEOUT DEFAULT_NOVA_RETRY_COUNT DEFAULT_NOVA_RETRY_DELAY NOVA_CONNECTION_TYPE CAA_REPOS_MODE CAA_CONFIG_TIMEOUT LVM_PREFERRED_READ CRIT_DAEMON_RESTART_GRACE_PERIOD SKIP_EVENT_PROCESSING_MANAGE_MODE BUCKET_NAME CLOUD_SERVICE USE_EXISTING_BUCKET ONPREM_HYBRID_CLOUD" [site]="NAME GID STATE NODES SITE_IP REPOSITORIES RECOVERY_PRIORITY DOMINANT HMCS" [node]="NAME HOSTNAME GID STATE RAW_STATE CAA_STATE COMMUNICATION COMMPATH LOCALHOST PERSISTENT_IPS VERSION VERSION_NUMBER EDITION AIX_LEVEL UNSYNCED_CHANGES IPLABEL IPADDRESS TYPE NETWORK NETTYPE NETMASK START_ON_BOOT BROADCAST_ON_START CLINFO_ON_START ENABLE_LIVE_UPDATE HMCS ENABLE_CAA_AFTER_MERGE NOVAS HOST_LABEL CRIT_DAEMON_RESTART_GRACE_PERIOD CLOUD_BASED" [host]="HOSTNAME IPADDRESS LOCALHOST HAVERSION VERSION_NUMBER HAEDITION AIX_LEVEL" [interface]="NAME INTERFACE IPADDR STATE NETMASK PREFIX NETWORK NODE NETTYPE TYPE ATTR HWADDR" [resource_group]="NAME CURRENT_NODE NODES STATE TYPE APPLICATIONS STARTUP FALLOVER FALLBACK NODE_PRIORITY_POLICY NODE_PRIORITY_POLICY_SCRIPT NODE_PRIORITY_POLICY_TIMEOUT DISK VOLUME_GROUP FORCED_VARYON FILESYSTEM FSCHECK_TOOL RECOVERY_METHOD EXPORT_FILESYSTEM SHARED_TAPE_RESOURCES AIX_CONNECTIONS_SERVICES AIX_FAST_CONNECT_SERVICES COMMUNICATION_LINKS MOUNT_FILESYSTEM SERVICE_LABEL MISC_DATA SSA_DISK_FENCING VG_AUTO_IMPORT INACTIVE_TAKEOVER CASCADE_WO_FALLBACK FS_BEFORE_IPADDR NFS_NETWORK MOUNT_ALL_FS WLM_PRIMARY WLM_SECONDARY FALLBACK_AT EXPORT_FILESYSTEM_V4 STABLE_STORAGE_PATH WPAR_NAME VARYON_WITH_MISSING_UPDATES DATA_DIVERGENCE_RECOVERY CURRENT_SECONDARY_NODE SECONDARYNODES SECONDARY_STATE RELATIONSHIP SITE_POLICY MIRROR_GROUP" [logical_volume]="NAME TYPE NODES VOLUME_GROUP PHYSICAL_VOLUMES RESOURCE_GROUP MOUNT_POINT LABEL PERMISSION VG_STATE LV_STATE MAX_LPS COPIES LPS PPS PP_SIZE STALE_PPS WRITE_VERIFY MIRROR_WRITE_CONSISTENCY INTER-POLICY INTRA-POLICY RELOCATABLE UPPER_BOUND SCHED_POLICY BB_POLICY EACH_LP_COPY_ON_A_SEPARATE_PV SERIALIZE_IO IDENTIFIER FIRST_COPY_MIRROR_POOL SECOND_COPY_MIRROR_POOL THIRD_COPY_MIRROR_POOL SIZE ENCRYPTION" [file_system]="NAME TYPE VFS NODES VOLUME_GROUP RESOURCE_GROUP LOGICAL_VOLUME DEVICE SIZE PERMISSIONS OPTIONS AUTOMOUNT DISK_ACCOUNTING BLOCK_SIZE LV_FOR_LOG INLINE_LOG_SIZE EXT_ATTR_FORMAT ENABLE_QUOTA_MGMT EFS MOUNTGUARD FRAGMENT_SIZE BYTES_PER_INODE ALLOC_GROUP_SIZE" [mirror_group]="NAME TYPE MG_TYPE STATE RESOURCE_GROUP MODE RECOVERY RESYNC STORAGE_SYSTEMS CONSISTENT VENDOR_ID HYPERSWAP_ENABLED HYPERSWAP_PRIORITY UNPLANNED_HS_TIMEOUT VOLUME_GROUPS RAW_DISKS SYS_MG_NODE REPOSITORY_MG_SITES REPOSITORY_MG_HS_DISK REPOSITORY_MG_NONHS_DISK MIRROR_PAIRS HORCM_INSTANCE HORCM_TIMEOUT PAIR_EVENT_TIMEOUT ATTRIBUTES" [network]="NAME GID NETMASK NET_FAMILY NETWORK_ID BOOT_ADAPTERS SERVICE_ADAPTERS PERSISTENT_ADAPTERS SINGLE_ADAPTER_NETWORK ATTR ALIAS RESOURCE_DIST_PREF SOURCE_IP POLLINTERVAL TYPE GLOBALNAME ALIAS_HB_NETMASK ALIAS_HB_ADDR MONITOR_METHOD PUBLIC INTERFACE INTERFACENAME IPADDR NODE INTERFACETYPE GLOBALNAME HADDR UNSTABLE_THRESHOLD UNSTABLE_PERIOD" [physical_volume]="NAME PVID UUID VOLUME_GROUP TYPE DESCRIPTION SIZE AVAILABLE CONCURRENT ENHANCED_CONCURRENT_MODE STATUS SCSIPR_CAPABLE" [service_ip]="NAME IPADDR SITE NODE NETMASK PREFIX NETWORK INTERFACE NETTYPE TYPE ATTR GLOBALNAME HWADDR" [snapshot]="NAME DESCRIPTION METHODS CAPTURE_DATE CAPTURE_NODE CLUSTER_NAME CLUSTER_TYPE NODES SITES HOSTS SNAPSHOTPATH PAGER SDIFF_OUTPUT_WIDTH SDIFF_FLAGS AVAILABLE_SECTIONS" [hmc]="NAME TIMEOUT RETRY_COUNT RETRY_DELAY NODES SITES STATUS VERSION USER_NAME PASSWORD" [cod]="NAME USE_DESIRED OPTIMAL_MEM OPTIMAL_CPU OPTIMAL_PU OPTIMAL_VP" [nova]="NAME TIMEOUT RETRY_COUNT RETRY_DELAY NODES STATUS VERSION USER_NAME PASSWORD MANAGED_SYSTEM" ) CL=$LINENO isEnterprise if (( $? == 1 )); then _ATTR_ORDER[resource_group]="NAME CURRENT_NODE NODES STATE CURRENT_SECONDARY_NODE SECONDARYNODES SECONDARY_STATE TYPE APPLICATIONS STARTUP FALLOVER FALLBACK NODE_PRIORITY_POLICY NODE_PRIORITY_POLICY_SCRIPT NODE_PRIORITY_POLICY_TIMEOUT DISK VOLUME_GROUP FORCED_VARYON FILESYSTEM FSCHECK_TOOL RECOVERY_METHOD EXPORT_FILESYSTEM SHARED_TAPE_RESOURCES AIX_CONNECTIONS_SERVICES AIX_FAST_CONNECT_SERVICES COMMUNICATION_LINKS MOUNT_FILESYSTEM SERVICE_LABEL MISC_DATA SSA_DISK_FENCING VG_AUTO_IMPORT INACTIVE_TAKEOVER CASCADE_WO_FALLBACK FS_BEFORE_IPADDR NFS_NETWORK MOUNT_ALL_FS WLM_PRIMARY WLM_SECONDARY FALLBACK_AT RELATIONSHIP SITE_POLICY MIRROR_GROUP EXPORT_FILESYSTEM_V4 STABLE_STORAGE_PATH WPAR_NAME VARYON_WITH_MISSING_UPDATES DATA_DIVERGENCE_RECOVERY" _COLON_ATTR_ORDER[resource_group]="NAME CURRENT_NODE NODES STATE SECONDARYNODES SECONDARY_STATE TYPE APPLICATIONS STARTUP FALLOVER FALLBACK NODE_PRIORITY_POLICY DISK VOLUME_GROUP FORCED_VARYON FILESYSTEM FSCHECK_TOOL RECOVERY_METHOD EXPORT_FILESYSTEM SHARED_TAPE_RESOURCES AIX_CONNECTIONS_SERVICES AIX_FAST_CONNECT_SERVICES COMMUNICATION_LINKS MOUNT_FILESYSTEM SERVICE_LABEL MISC_DATA SSA_DISK_FENCING VG_AUTO_IMPORT INACTIVE_TAKEOVER CASCADE_WO_FALLBACK FS_BEFORE_IPADDR NFS_NETWORK MOUNT_ALL_FS WLM_PRIMARY WLM_SECONDARY FALLBACK_AT RELATIONSHIP SITE_POLICY MIRROR_GROUP EXPORT_FILESYSTEM_V4 STABLE_STORAGE_PATH WPAR_NAME VARYON_WITH_MISSING_UPDATES DATA_DIVERGENCE_RECOVERY NODE_PRIORITY_POLICY_SCRIPT NODE_PRIORITY_POLICY_TIMEOUT CURRENT_SECONDARY_NODE" fi #===================================================================== # To preserve backwards compatibility with previous versions of clvt # establish a keyword compatibility map. Each key is the old way of # specifying an attribute, and each value is the new attribute name. # All this is necessary to avoid breaking migration (what happens if # the customer migrations the base product and clvt, but *not* their # existing Smart Assists?). Of course, perhaps even more important, # and certainly more likely to occur, is avoiding breaking any pre- # existing customer scripts! An angry customer is a scary customer!! #===================================================================== typeset -A COMPAT COMPAT=( [PRIMARYNODES]="NODES" [NEWNAME]="NAME" [RG]="RESOURCE_GROUP" [VG]="VOLUME_GROUP" [CONCURRENT_VOLUME_GROUP]="VOLUME_GROUP" [LV]="LOGICAL_VOLUME" [PV]="PHYSICAL_VOLUME" [NW]="NETWORK" [IF]="INTERFACE" [DISK]="PHYSICAL_VOLUME" [DISKS]="PHYSICAL_VOLUMES" [FS]="FILE_SYSTEM" [PREFIXLENGTH]="PREFIX" [RESOURCE_MODE]="MODE" [APPLICATIONMONITORTYPE]="TYPE" [NETWORKTYPE]="TYPE" [ASSOCIATED_MONITORS]="MONITORS" [ASSOCIATEDMONITORS]="MONITORS" [NODENAME]="NODE" [ISPROPOGATEFILEDURINGSYNC]="SYNC_WITH_CLUSTER" [ISPROPOGATEDFILEDURINGSYNC]="SYNC_WITH_CLUSTER" [ISPROPOGATEAUTODETECTED]="SYNC_WHEN_CHANGED" [ISPROPOGATEDAUTODETECTED]="SYNC_WHEN_CHANGED" [ISPROPOGATEAUTOWHENDETECTED]="SYNC_WHEN_CHANGED" [PRIMARY_NODES]="PRIMARYNODES" [SECONDARY_NODES]="SECONDARYNODES" [TIMER]="FALLBACK_AT" [UPPER_BOUND]="MAX_PVS_FOR_NEW_ALLOC" [INTER_POLICY]="PV_RANGE" [INTRA_POLICY]="POSITION" [LPS]="LOGICAL_PARTITIONS" [PPS]="PHYSICAL_PARTITIONS" [COPIES]="LPART_COPIES" [MAX_LPS]="MAX_LPARTS" [SCHED_POLICY]="SCHEDULING_POLICY" [BB_POLICY]="BAD_BLOCK_RELOCATION" [WRITE_VERIFY]="VERIFY_WRITES" [EACH_LP_COPY_ON_A_SEPARATE_PV]="LPARTS_ON_SEPARATE_PVS" [RELOCATABLE]="RELOCATE" [MIRROR_WRITE_CONSISTENCY]="WRITE_CONSISTENCY" [SYS_MG_NODE]="NODE" [RAW_DISKS]="DISKS" [REPOSITORY_MG_SITES]="SITE" [REPOSITORY_HS_DISK]="HS_DISK" [REPOSITORY_NONHS_DISK]="NON_HS_DISK" [REPOSITORY]="REPOSITORIES" [RESTART_COUNT]="RESTARTCOUNT" [FAILURE_ACTION]="FAILUREACTION" [MONITOR_INTERVAL]="MONITORINTERVAL" [HUNG_SIGNAL]="HUNGSIGNAL" [HUNG_MONITOR_SIGNAL]="HUNGSIGNAL" [RESTART_INTERVAL]="RESTARTINTERVAL" [NOTIFY_METHOD]="NOTIFYMETHOD" [CLEANUP_METHOD]="CLEANUPMETHOD" [RESTART_METHOD]="RESTARTMETHOD" [MONITOR_RETRY_COUNT]="MONITORRETRYCOUNT" [AM_LOGGING]="AMLOGGING" [ENABLE_EFS]="EFS" ); # # The API will supply default values to be used when an imprecise definition # is given # typeset PROGNAME=${0##*/} if [[ -f $CLMGR_TMPLOG ]] then # Let's log the warnings if we can export PATH="$(/usr/es/sbin/cluster/utilities/cl_get_path all 2>>$CLMGR_TMPLOG)" else export PATH="$(/usr/es/sbin/cluster/utilities/cl_get_path all 2>/dev/null)" fi export CLMGR_DEFAULT_CLUSTER_NAME="$(uname -n)_cluster" typeset STATUS=0 typeset -l RESOURCE_CLASS if [[ -n $1 && $1 != *=* ]]; then RESOURCE_CLASS=$1 shift if [[ -n $1 && $1 != *=* ]]; then # Resource name is optional for operations such as "add cluster" RESOURCE_NAME=$1 shift fi fi CL=$LINENO . $HAEVENTS/class_processors "$_ACTION" #======================================================================= # This is the global set of *all* accepted attributes. If an attribute # is *not* in this list, then clvt will *not* recognize it. #======================================================================= typeset _VAR_SUBSTITUTIONS= if [[ $_ACTION == "manage" ]]; then _VAR_SUBSTITUTIONS=${_CLASS_PROCESSORS[${_SUB_ACTION}_${RESOURCE_CLASS}]} elif [[ $_ACTION == @(query|list) ]]; then _VAR_SUBSTITUTIONS=${_CLASS_PROCESSORS[$RESOURCE_CLASS]} _VAR_SUBSTITUTIONS="$_VAR_SUBSTITUTIONS ${_COLON_ATTR_ORDER[${RESOURCE_CLASS#@(query|list)_}]}" else _VAR_SUBSTITUTIONS=${_CLASS_PROCESSORS[$RESOURCE_CLASS]} _VAR_SUBSTITUTIONS=${_VAR_SUBSTITUTIONS#*[[:space:]]} # Remove script/func name fi _VAR_SUBSTITUTIONS=${_VAR_SUBSTITUTIONS#+([[:space:]])/ } #================================================================== # Determine if this processor requires a specified resource label #================================================================== resourceIsRequired=0 if [[ ${_CLASS_PROCESSORS[$RESOURCE_CLASS]} == *[[:space:]]RESOURCE_NAME* ]] then resourceIsRequired=0 fi export resourceIsRequired #========================================================================== # For the "query" action, in order to support filtering, it is necessary # to know what attributes are available to filter/search on. Rather than # force each attribute to be added to the class processor for the class' # query action (defined in "events/class_processors"), which is a bit # redundant and cumbersome , the available attributes are retrieved here, # then automatically appended to the class processor for the query. #========================================================================== if [[ $_ACTION == "query" && \ $RESOURCE_CLASS == query_* && \ -n ${_CLASS_PROCESSORS[$RESOURCE_CLASS]} ]] then typeset classname=${RESOURCE_CLASS#${_ACTION}_} for attr in ${_ATTR_ORDER[$classname]}; do _VAR_SUBSTITUTIONS="$_VAR_SUBSTITUTIONS $attr" done fi #==================================== # Make sure there are no duplicates #==================================== typeset _VAR= _TEMP= for _VAR in $_VAR_SUBSTITUTIONS; do [[ " $_TEMP " == *\ $_VAR\ * ]] && continue [[ -n $_TEMP ]] && _TEMP="$_TEMP " _TEMP="$_TEMP$_VAR" done _VAR_SUBSTITUTIONS=$_TEMP unset _VAR _TEMP FPATH=/usr/es/lib/ksh93/hacmp FPATH=$FPATH:/usr/es/lib/ksh93/aix FPATH=$FPATH:/usr/es/lib/ksh93/util #================================================================ # Check for a cry for help from the customer! We accept h,H,*,? # to ask for help. We also accept an optional "v", but that is # intended for internal use only (because the text it displays # is *not* translated), and is not documented. #================================================================ typeset -l lcarg= lcargs= for lcarg in $_EVENT_SCRIPT_ARGS; do [[ $lcarg == *=* ]] && continue [[ $lcarg != -* && $lcarg != @(\*|\?) ]] && continue lcarg=${lcarg##*-} # Strip off any leading dashes lcargs="$lcargs $lcarg" # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done for lcarg in $lcargs; do [[ $lcarg == *@(h|\*|\?)* && $_ACTION != "runcmd" ]] && CLMGR_HELP=1 [[ $lcarg == *v* ]] && CLMGR_VERBOSE=1 # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done # Construct the inline replacement hash array for the optional arguments typeset -i BAD_OPTS=0 if (( ! CLMGR_HELP )); then CL=$LINENO KLIB_UTIL_parse_arguments _ENV_ARGS "$@" if (( $? != RC_SUCCESS )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 61 "\nERROR: an error occurred whiile parsing the inputs for the operation.\n\n" 1>&2 [[ -n $CLMGR_COMMAND ]] && print -u2 " $CLMGR_COMMAND\n" log_return_msg "$RC_ERROR" resource_common "$LINENO" exit $? fi if [[ $_ACTION != "runcmd" ]] && \ [[ $_ACTION != "query" && $RESOURCE_CLASS != "class" ]] then for key in ${!_ENV_ARGS[*]}; do [[ $key == @(RESOURCE_NAME|RESOURCE_CLASS|properties) ]] && continue CL=$LINENO expand_keyword "$key" _VAR_SUBSTITUTIONS (( $? != 0 )) && (( BAD_OPTS++ )) # Slow down the loop a fraction, to avoid spiking the CPU LANG=C sleep .001 done fi fi #========================================================= : If bad options were detected, or help was requested by : the customer display the available options, then exit. : Emphasis on the latter... if these conditions are met, : the code wil *always* exit here. #========================================================= if (( BAD_OPTS || CLMGR_HELP )); then typeset class=$RESOURCE_CLASS typeset SRC_FILE="" print "$SENTINEL COMMAND ($CLMGR_TRANSACTION_ID): ${CLMGR_COMMAND/ -T $CLMGR_TRANSACTION_ID}" >>$CLMGR_TMPLOG if (( ! CLMGR_VERBOSE || BAD_OPTS )); then print "$SENTINEL INTERNAL ($CLMGR_TRANSACTION_ID): [display_operation_syntax || display_simple_help]" >>$CLMGR_TMPLOG display_operation_syntax $BAD_OPTS (( $? != RC_SUCCESS )) && display_simple_help else print "$SENTINEL INTERNAL ($CLMGR_TRANSACTION_ID): [display_operation_man_page || display_operation_syntax || display_simple_help]" >>$CLMGR_TMPLOG display_operation_man_page if (( $? != RC_SUCCESS )); then display_operation_syntax $BAD_OPTS (( $? != RC_SUCCESS )) && display_simple_help fi fi CLMGR_STACK="" # Avoid writing the stack to the log (implies an error) (( CLMGR_HELP )) && exit $RC_SUCCESS || exit $RC_INCORRECT_INPUT fi _ENV_ARGS[RESOURCE_NAME]=$RESOURCE_NAME _ENV_ARGS[RESOURCE_CLASS]=$RESOURCE_CLASS # If the user doesn't specify a cluster name, use the default name if [[ "$RESOURCE_CLASS" == "cluster" ]]; then [[ -z "$RESOURCE_NAME" ]] && _ENV_ARGS[RESOURCE_NAME]=$CLMGR_DEFAULT_CLUSTER_NAME fi log_return_msg "$rc" resource_common "$LINENO" return $? #============================================================================== # 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 #==============================================================================