#!/bin/ksh93
#  ALTRAN_PROLOG_BEGIN_TAG                                                    
#  This is an automatically generated prolog.                                  
#                                                                              
#  Copyright (C) Altran ACT S.A.S. 2017,2018,2019,2021.  All rights reserved.  
#                                                                              
#  ALTRAN_PROLOG_END_TAG                                                      
#
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# 61haes_r721 src/43haes/lib/ksh93/hacmp/KLIB_HACMP_modify_volume_group.sh 1.16 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2011 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG                                                                              
# @(#)  7b36ae1 43haes/lib/ksh93/hacmp/KLIB_HACMP_modify_volume_group.sh, 726, 2147A_aha726, Sep 08 2021 09:17 PM 

# Start of POD-formatted documentation. Viewing suggestions:
#      perldoc <FILENAME>
#      pod2text -c <FILENAME>
#      pod2text -c --code <FILENAME>
#      pod2html <FILENAME>
function devDoc {
    : <<'=cut' >/dev/null 2>&1

=head1 NAME

 KLIB_HACMP_modify_volume_group

=head1 SYNOPSIS

 clmgr modify volume_group <vgname> \
             [ ADD=<disk>,[<disk#n>,...] [ MIRROR_POOL_NAME="<mp_name>" ] ] \
             [ REMOVE=<disk>,[<disk#n>,...] ] \
             [ TYPE={big|scalable} ] \
             [ ACTIVATE_ON_RESTART={false|true} ] \
             [ QUORUM_NEEDED={true|false} ] \
             [ LTG_SIZE=### ] \
             [ MIGRATE_FAILED_DISKS={false|one|pool|remove} ] \
             [ MAX_PHYSICAL_PARTITIONS={32|64|128|256|
                                        512|768|1024} ] \
             [ MAX_LOGICAL_VOLUMES={256|512|1024|2048} ] \
             [ STRICT_MIRROR_POOLS={no|yes|super} ] \
             [ CRITICAL={false|true} ] \
                 [ FAILUREACTION={halt|notify|fence|
                                      stoprg|moverg} ] \
                 [ NOTIFYMETHOD="</file/to/invoke>" ] \
             [ SCSIPR_ACTION=<clear> ] \
             [ LVM_PREFERRED_READ=<roundrobin|favorcopy|siteaffinity> ] \
             [ ENABLE_LV_ENCRYPTION = {yes|no} ] 

 NOTE: "MAX_PHYSICAL_PARTITIONS", "MAX_LOGICAL_VOLUMES",
       "MIRROR_POOL_NAME", and "STRICT_MIRROR_POOLS", only
       apply to "scalable" volume groups.

 NOTE: an alias for "volume_group" is "vg".

=head1 DESCRIPTION

Attempts to modify a volume_group to conform to the specified parameters.

=head1 ARGUMENTS

 1. properties [REQUIRED] [hash ref]
    An associative array within which data about the
    created object can be returned to the caller.

 2. volume_group [OPTIONAL] [string]
    The name of the volume group that is to be modified.

 3. add [OPTIONAL] [string]
    One or more disks to add to the volume group - if the supplied name is
    specific to a node, then the format is <disk>@<node>. Multiple, comma
    separated entries are allowed.

 4. remove [OPTIONAL] [string]
    One or more disks to remove from the volume group - if the supplied name is
    specific to a node, then the format is <disk>@<node>.

 5. mp_name [OPTIONAL] [string]
    The name of the mirror pool to add disks to.

 6. new_type [OPTIONAL] [string]
    Requests that the volume group be converted to the indicated type,
    either "Big" or "Scalable".

 7. activate [OPTIONAL] [boolean]
    Indicates if the volume group should be activated after the system
    is restarted.

 8. quorum [OPTIONAL] [boolean]
    Indicates whether or not a quorum of the disks in the volume group are
    needed to keep the volume group online.

 9. ltg_size [OPTIONAL] [integer]
    Sets the logical track group size, in number of kilobytes.

 10. migrate [OPTIONAL] [string]
    Indicates whether or not to automatically migrate failed disks.

    Accepted values:  false|one|pool|remove

 11. max_pparts [OPTIONAL] [integer]
    For "Scalable" volume group types only, this setting indicates the 
    the maximum number of physical partitions for the volume group, using
    units of 1024. Defaults to 32 (32 * 1024 = 32768 partitions).

 12. max_lvs [OPTIONAL] [integer]
    For "Scalable" volume group types only, this setting indicates the 
    the maximum number of logical volumes for the volume group. Defaults
    to 256.

 13. mp_strictness [OPTIONAL] [string]
    Mirror pool strictness can have one of the following three values: 
    no, yes, "super".
    When strictness is enabled, any logical volume in the volume group must 
    have mirror pools enabled for every copy of the logical volume
    and all of the logical volumes in the volume group must use mirror pools.
    A "super" strict allocation policy specifies that the partitions allocated 
    for one mirror cannot share a physical volume with the partitions from 
    another mirror.

 14. critical [OPTIONAL] [boolean]
    Indicates if this is a critical volume group. Used to satisfy the
    quorum requirements of Oracle RAC.

    Defaults to "false", or "no".

 15. failure_action [OPTIONAL] [boolean]
    Defines the action to take upon loss of access to the volume group.

    Valid values are: halt, notify, fence, stoprg, moverg

 16. notify_method [OPTIONAL] [string]
    Defines the full path to an executable file that will be run whenever
    any failure action is triggered.

 17. scsipr_action [OPTIONAL] [string]
     To clear the volume group of scsi persistent reserve registrations.

 18. encryption [OPTIONAL] [boolean]
     Enables the data encryption option in the volume group.
     Default value is "yes".
=head1 RETURN

 0: no errors were detected; the operation appears to have been successful
 1: a general error has occurred
 2: a specified resource does not exist, or could not be found
 3: some required input was missing
 4: some detected input was incorrect in some way
 5: a required dependency does not exist
 6: a specified search failed to match any data

=cut
} # End of POD-formatted documentation.


function KLIB_HACMP_modify_volume_group {
    . $HALIBROOT/log_entry "$0()" "$CL"
    : INPUTS: $*

    typeset -n properties=$1
    typeset volume_group=${2//\"/}
    shift; shift

    typeset add=${1//\"/}
            add=${add//+([[:space:]])/,}
    typeset remove=${2//\"/}
            remove=${remove//,/ }
    typeset mp_name=${3//\"/}
    typeset new_type=${4//\"/}
    typeset -l activate=${5//\"/}
    typeset -l quorum=${6//\"/}
    typeset ltg_size=${7//\"/}
    typeset migrate=${8//\"/}
    typeset max_pparts=${9//\"/}
    typeset max_lvs=${10//\"/}
    typeset -l mp_strictness=${11//\"/}
    typeset -l critical=${12//\"/}
    typeset -l failure_action=${13//\"/}
    typeset notify_method=${14//\"/}
    typeset scsipr_action=${15//\"/}
    typeset preferred_read=${16//\"/}
    typeset -l encryption=${17//\"/}
    [[ $CLMGR_LOGGING == 'med' ]] && set +x  # Only trace param values

    #================================================================
    : Check for a defined cluster. No need to continue without one.
    #================================================================
    CL=$LINENO isClusterDefined
    if (( $? != RC_SUCCESS )); then
        log_return_msg "$RC_MISSING_DEPENDENCY" "$0()" "$LINENO"
        return $?
    fi

    #===================================
    : Declare and initialize variables
    #===================================
    typeset -i rc=$RC_UNKNOWN
    typeset typeOpt= POpt= vOpt= pOpt= hOpt= sOpt= QOpt= aOpt= LOpt=
    typeset INPUT= ATTR= VALUE= ORDER= nodes= RGNAME= devname= pvid= rest=

    typeset LEGACY= ORIGINAL= BIG= SCALABLE= REM=
    typeset LINE=$(dspmsg -s 43 cspoc.cat 40 "Legacy Original Big Scalable")
    print -- "$LINE" |read LEGACY ORIGINAL BIG SCALABLE REM
    typeset -l BIG_LC=$BIG
    typeset -l SCALABLE_LC=$SCALABLE
    if [[ $LANG == @(|C|en_US) ]]; then
        BIG_LC=b
        SCALABLE_LC=s
    fi
    
    typeset cur_oslevel=$(oslevel -r|sed 's/-//g')
    typeset -i min_req_ver=730000

    #
    # Set so we can invoke cspoc utilities using smit style
    #
    [[ -z $_CSPOC_CALLED_FROM_SMIT ]] && _CSPOC_CALLED_FROM_SMIT=true
    export _CSPOC_CALLED_FROM_SMIT

    #================================================================
    : Assuming an object was specified, see if it is a known object
    #================================================================
    if [[ $volume_group != *([[:space:]]) ]]; then
        CL=$LINENO KLIB_HACMP_is_known_volume_group "$volume_group"
        (( $? != RC_SUCCESS )) && rc=$RC_NOT_FOUND
    fi

    #=================
    : Validate input
    #=================
    if [[ -z $volume_group ]]; then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 100 "\nERROR: a name/label must be provided.\n\n" 1>&2
        rc=$RC_MISSING_INPUT

    elif (( $rc == RC_NOT_FOUND )); then
        # vg name given but does not exist - show whats available
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 102 "\nERROR: \"%1\$s\" does not appear to exist!\n\n" "$volume_group" 1>&2
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 160 "Available Volume Groups:\n\n" 1>&2

        typeset available
        CL=$LINENO KLIB_HACMP_list_volume_groups available
        for (( i=0; i<${#available[*]}; i++ )); do
            if [[ ${available[$i]} != *([[:space:]]) ]]; then
                print -u2 "\t${available[$i]}"
            fi
        done
        print -u2 ""

        rc=$RC_NOT_FOUND
    else
        # Found the specified vg - lookup any mirror pool configuration
        LC_ALL=C cl_lsmpvgs -V |\
        while IFS= read LINE; do
            LINE=${LINE##+([[:space:]])}      # Remove any leading whitespace
            typeset vgname=${LINE%%[[:space:]]*}

            if [[ $vgname == $volume_group ]]; then
                typeset vgtype=
                LINE=${LINE/\<*\>/\<Not_in_a_Resource_Group\>}
                print -- "$LINE" | read vgname RGNAME nodes vgtype
                [[ $RGNAME == *\<* ]] && RGNAME=${RGNAME//_/ }
                nodes=${nodes//+([[:space:]])/,}
                break
            fi
        done
    fi

    # Can only add disks to a mirror pool, so if mp is given, there must also
    # be disks specified
    if [[ -n $mp_name  &&  -z $add ]]; then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 449 "\nERROR:no disks were specified for \"%1\$s\"\n" "$mp_name" 1>&2
        rc=$RC_INCORRECT_INPUT
    fi

    if [[ -n $new_type ]]; then
        # Caller wants to change the vg type - map the input to the NLS equivalent
        typeset -l type_lc=$new_type
        case $type_lc in
            $BIG_LC*)      typeOpt="-B" ;;
            $SCALABLE_LC*) typeOpt="-G" ;;
            *) dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 "\nERROR: invalid value specified for \"%1\$s\":  \"%2\$s\"\n" TYPE "$new_type" 1>&2
               dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "$BIG, $SCALABLE" 1>&2
               rc=$RC_INCORRECT_INPUT
            ;;
        esac
    fi

    #========================================================
    : Attempt to validate all provided disks, attempting to
    : convert them from PVID form into devnames, if needed.
    #========================================================
    if [[ -n $add || -n $remove ]]; then
        typeset disks="$add $remove"
        typeset -i cmd_rc=$RC_UNKNOWN
        for disk in ${disks//,/ }; do
            [[ $disk == *([[:space:]]) ]] && continue

            # disks can be specified with a specific node using the form
            # <disk>@<node>
            # get the data from that node, if specified
            if [[ $disk == *@* ]]; then
                print -- "$disk" | IFS=@ read disk node
                CL=$LINENO getDiskData "$disk" "$node" IDENTIFIERS true
                cmd_rc=$rc
            else
                # just get the data from the local node
                if [[ " ${nodes//,/ } " == *\ $LOCAL_NODE\ * ]]; then
                    CL=$LINENO getDiskData "$disk" "$LOCAL_NODE" IDENTIFIERS true
                    cmd_rc=$?
                    if (( $cmd_rc != RC_SUCCESS )); then
                        rc=$RC_INCORRECT_INPUT
                        cmd_rc=$rc
                        break
                    fi
                fi
            fi
            if (( $cmd_rc != RC_SUCCESS )); then
                rc=$RC_NOT_FOUND  # No such disk found
                break
            else
                # found the disk - make sure it is known to this node
                print -- "$IDENTIFIERS" | IFS=: read devname pvid uuid refnode rest
                CL=$LINENO verify_disk "$pvid" "$refnode" 0 0 true
                cmd_rc=$?
                (( $cmd_rc != RC_SUCCESS )) && rc=$cmd_rc
            fi
        done
    fi

    # Validate mirror pool strictness setting
    if [[ $mp_strictness != *([[:space:]]) ]]; then
        case $mp_strictness in
            @(n|f)*) mp_strictness="n" ;;
            @(y|t)*) mp_strictness="y" ;;
                 s*) mp_strictness="s" ;;
             *) dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 "\nERROR: invalid value specified for \"%1\$s\":  \"%2\$s\".\n" "STRICT_MIRROR_POOLS" "$mp_strictness" 1>&2
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "no, yes, super" 1>&2
                rc=$RC_INCORRECT_INPUT
            ;;
        esac
    fi

    # Failure action specifies what to do when the critical volume group fails
    if [[ $failure_action != *([[:space:]]) ]]; then
        case $failure_action in
            h*) failure_action="halt"     ;;
            n*) failure_action="notify"   ;;
            f*) failure_action="fence"    ;;
            s*) failure_action="shutdown" ;;
            m*) failure_action="fallover" ;;
             *) dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 "\nERROR: invalid value specified for \"%1\$s\":  \"%2\$s\".\n" "FAILURE_ACTION" "$failure_action" 1>&2
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "halt, notify, fence, stoprg, moverg" 1>&2
                rc=$RC_INCORRECT_INPUT
            ;;
        esac
    fi

    
    # chvg can be used to migrate data off of failed disks or pools, or just 
    # remove them
    if [[ $migrate != *([[:space:]]) ]]; then
        case $migrate in
            @(f|n)*) migrate="n" ;;
            @(o|1)*) migrate="y" ;;
                  y) migrate="y" ;;
            @(p|P)*) migrate="Y" ;;
                  Y) migrate="Y" ;;
            @(r|R)*) migrate="r" ;;
             *) dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 "\nERROR: invalid value specified for \"%1\$s\":  \"%2\$s\".\n" "MIGRATE_FAILED_DISKS" "$migrate" 1>&2
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "false, one, pool, remove" 1>&2
                rc=$RC_INCORRECT_INPUT
            ;;
        esac
    fi

    #
    : Validate the integer inputs
    #
    for INPUT in LTG_SIZE@$ltg_size \
                 MAX_PHYSICAL_PARTITIONS@$max_pparts \
                 MAX_LOGICAL_VOLUMES@$max_lvs
    do
        print -- "$INPUT" | IFS=@ read ATTR VALUE
        if [[ -n $VALUE && $VALUE != +([[:digit:]]) ]]; then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 111 "\nERROR: \"%1\$s\" requires a positive, integer value.\n\n" "$ATTR" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    done

    #
    : Validate the boolean inputs
    #
    for INPUT in ACTIVATE@$activate@false,true \
                 QUORUM@$quorum@true,false \
                 CRITICAL@$critical@false,true \
                 ENABLE_LV_ENCRYPTION@$encryption@no,yes
    do
        print -- "$INPUT" | IFS=@ read ATTR VALUE ORDER
        if [[ -n $VALUE && $VALUE != @(y|t|n|f)* ]]; then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 "\nERROR: invalid value specified for \"%1\$s\":  \"%2\$s\".\n" "$ATTR" "$VALUE" 1>&2
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "$ORDER" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    done

    #===============================================================
    : check for aix version 7.3 if lvm encryption option is provided
    #===============================================================
    if [[ $encryption == @(y|t)* ]];then
        hdcrypt_version=$(LC_ALL=C lslpp -L | grep -w hdcrypt | awk '{print $2}')
        if [[ -z $hdcrypt_version || $cur_oslevel < $min_req_ver ]]
        then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 1480 "\nERROR: Installed AIX level %1\$s is not supported to use LVM Encryption,\n\
minimum level required to use AIX LVM Encryption is AIX %2\$s.\n\n" "$cur_oslevel" "$min_req_ver" 1>&2
            rc=$RC_MISSING_DEPENDENCY
        fi
    fi

    #
    : Determine the current type of the vg by looking at the pv data
    #
    typeset CURRENT_TYPE="UNKNOWN"
    typeset vgnode= PVDATA=
    CL=$LINENO getPVData "$nodes" vgnode PVDATA $volume_group
    if [[ -n $PVDATA ]]; then
        print -- "$PVDATA" |\
        while IFS= read LINE; do
            typeset devname pvid vgname rest
            IFS=$ORIG_IFS print -- "$LINE" | read devname pvid vgname rest
            [[ $volume_group != $vgname ]] && continue

            # The max number of PVs determines the vg type (original, big, etc)
            print "$0()[$LINENO]($SECONDS): LC_ALL=C lqueryvg -Atp $devname | grep -i \"^MAX PVs:\"" >>$CLMGR_TMPLOG
            max_pvs=$(LC_ALL=C lqueryvg -Atp $devname |\
                   grep -i "^MAX PVs:")
            print "grep RC: $?; MAX PVs == \"$max_pvs\"" >>$CLMGR_TMPLOG
            max_pvs=${max_pvs##*+([[:space:]])}
            if [[ $max_pvs == +([[:digit:]]) ]]; then
                case $max_pvs in
                    32) CURRENT_TYPE="ORIGINAL" ;;
                   128) CURRENT_TYPE="BIG"      ;;
                  1024) CURRENT_TYPE="SCALABLE" ;;
                esac
            fi
        done
    fi
    # Check for allowed modifications for scalable vgs (or vgs about to
    # become scalable because we are changing the type)
    if [[ $typeOpt == "-G" || $CURRENT_TYPE != "SCALABLE" ]]; then
        for INPUT in MAX_PHYSICAL_PARTITIONS@$max_pparts \
                     MAX_LOGICAL_VOLUMES@$max_lvs \
                     STRICT_MIRROR_POOLS@$mp_strictness \
                     MIRROR_POOL_NAME@$mp_name \
                     LVM_PREFERRED_READ@$preferred_read
        do
            print -- "$INPUT" | IFS=@ read ATTR VALUE
            if [[ $VALUE != *([[:space:]]) ]]; then
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 1166 "\nERROR: Cannot update %1\$s, unless the current type of Volume group is  \"%2\$s\".\n Volume group type changes cannot be combined with any other operation.\n" "$ATTR" "$SCALABLE" 1>&2
                rc=$RC_INCORRECT_INPUT
            fi
        done
    fi

    #=========================================================
    : Mirror pool names must conform to the following rules:
    :     * Can only contains alphanumeric characters.
    :     * Must be less than or equal to 15 characters.
    :     * Must be unique in the volume group.
    #=========================================================
    if [[ $mp_name != *([[:space:]]) ]]; then
        if [[ -n "${mp_name//[a-zA-Z0-9_-]/}" ]]; then
            cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 105 "\nERROR: one or more invalid characters were detected in \"%1\$s\" (\"%2\$s\").\n\nValid characters include letters, numbers, and underscores only.\n\n" "$mp_name" "${mp_name//[a-zA-Z0-9_-]/}" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
        if (( ${#mp_name} > 15 )); then
            cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 43 'ERROR: "%1\$s" has exceeded the maximum allowed length of %2\$s.\n\n' "$mp_name" 15 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    fi

    # 
    # If a notify method has been specified, check that it exists on this
    # node - clverify will check the rest of the nodes at sync time, but
    # we try to catch any simple error here.
    #
    if [[ $notify_method != *([[:space:]]) && ! -x $notify_method ]]; then
        cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 107 '\nERROR: the specified path/file does not appear to exist on "%2\$s": %1$s\n\n' "$notify_method" "$LOCAL_NODE" 1>&2
        rc=$RC_NOT_FOUND
    fi

    # Notify method can only be specified for critical vgs
    if [[ $notify_method != *([[:space:]]) ]] &&
       [[ -z $critical || $critical == @(f|n)* ]]
    then
        typeset -i bad_combo=1
        if [[ -z $critical  ]]; then
            cl_ls_shared_vgs -c -C -O | grep -wq $volume_group
            (( $? == 0 )) && bad_combo=0
        fi

        if (( bad_combo )); then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 37 "\nERROR: option \"%1\$s\" is required when any of the following option(s) are used: %2\$s\n\n" "CRITICAL=true" "NOTIFY_METHOD" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    fi
    # Failure action can only be specified for critical vgs
    if [[ $failure_action != *([[:space:]]) ]] &&
       [[ -z $critical || $critical == @(f|n)* ]]
    then
        typeset -i bad_combo=1
        if [[ -z $critical  ]]; then
            cl_ls_shared_vgs -c -C -O | grep -wq $volume_group
            (( $? == 0 )) && bad_combo=0
        fi

        if (( bad_combo )); then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 37 "\nERROR: option \"%1\$s\" is required when any of the following option(s) are used: %2\$s\n\n" "CRITICAL=true" "FAILURE_ACTION" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    fi
    
    #==============================================================
    : Verify preferred read option is valid or not, if any value is specified.
    #==============================================================
    if [[ ${preferred_read} != *([[:space:]]) ]]; then
       if [[ $CLUSTER_TYPE == "NSC" ]]; then
           CL=$LINENO verify_in_set LVM_PREFERRED_READ "$preferred_read" "roundrobin, favorcopy" preferred_read
       else
           # siteaffinity option is available only for site based cluster.
           CL=$LINENO verify_in_set LVM_PREFERRED_READ "$preferred_read" "roundrobin, favorcopy, siteaffinity" preferred_read
       fi
       if (( $? != RC_SUCCESS )); then
            rc=$RC_INCORRECT_INPUT
       fi 
    fi
    #================================================================
    : Modify the volume group if no input errors have been detected
    #================================================================
    if (( $rc == RC_UNKNOWN )) &&
       [[ -n "$new_type$activate$quorum$ltg_size$migrate$max_pparts$max_lvs$mp_strictness$preferred_read$encryption" ]]
    then
        typeset aOpt= QOpt= LOpt= hOpt= POpt= vOpt= MOpt=
        [[ $activate == @(y|t)* ]] && aOpt="-a y" || aOpt="-a n"
        [[ $quorum   == @(y|t)* ]] && QOpt="-Q y" || QOpt="-Q n"
        [[ -n $ltg_size ]]         && LOpt="-L $ltg_size"
        [[ -n $migrate ]]          && hOpt="-h $migrate"
        [[ -n $max_pparts && $max_pparts == +([[:digit:]]) ]] && POpt="-P $max_pparts"
        [[ -n $max_lvs && $max_lvs == +([[:digit:]]) ]] && vOpt="-v $max_lvs"
        [[ -n $mp_strictness ]] && MOpt="-M $mp_strictness"
        [[ -n $encryption && $encryption == @(y|t)* ]] && kOpt="-k y"
        [[ -n $encryption && $encryption == @(n|f)* ]] && kOpt="-k n"

        [[ -n $preferred_read ]] && rOpt="-r $preferred_read"

         print -- "$0()[$LINENO]($SECONDS): cl_chvg -cspoc \"-n $nodes\" $aOpt $QOpt $typeOpt $LOpt $hOpt $POpt $vOpt $MOpt $rOpt $kOpt $volume_group" >>$CLMGR_TMPLOG  # Always log commands
        cl_chvg -cspoc "-n $nodes" \
                        $aOpt \
                        $QOpt \
                        $typeOpt \
                        $LOpt \
                        $hOpt \
                        $POpt \
                        $vOpt \
                        $MOpt \
                        $rOpt \
                        $kOpt \
                        $volume_group
        rc=$?
        print "cl_chvg RC: $rc" >>$CLMGR_TMPLOG  # Always log command result
    fi

    if (( $rc == RC_UNKNOWN ||  $rc == RC_SUCCESS )) && [[ -n $critical ]]; then
        # If here, either there were no modifications or the specified 
        # modifications were successful - check for changes to critical vg.
        # Critical vg is an attribute stored by PowerHA so there are no lvm
        # level changes made in this block.
        if [[ $critical == @(y|t)* ]]; then
            # see if the vg is already marked as critical
            cl_ls_shared_vgs -c -C -O | grep -wq $volume_group
            if (( $? == 0 )); then
                rc=$RC_SUCCESS  # Already marked as CRITICAL
            else
                # only concurrent vgs in an OAAN resource group can be marked
                # critical - see if the specified vg is a candidate
                if ! clodmget -q value=$volume_group HACMPresource | 
                     grep -wq "CONCURRENT_VOLUME_GROUP"
                then
                    cl_dspmsg -s 24 cspoc.cat 34 "Warning: Marking volume group %1\$s as crtitcal is not allowed on non-concurrent resource group. To mark VG as critical, add VG to a concurrent resource group.\n" "$volume_group"
                    rc=$RC_INCORRECT_INPUT
                else
                    # change the designation in odm
                    if ! odmget -q value=$volume_group HACMPresource | sed -n '/name =/s/CONCURRENT_VOLUME_GROUP/CRITICAL_VG/p;/group =/p;/value =/p;/HACMPresource/p' | odmadd                                             
                    then
                        cl_dspmsg -s 24 cspoc.cat 30 "%1\$s: Marking volume group %2\$s in resource group %3\$s as CRITICAL failed." clmgr "$volume_group" "$RGNAME" 1>&2
                        rc=$RC_ERROR
                    else
                        # PowerHA config changed - reset the cluster handle to
                        # force a sync
                        print "HACMPcluster: handle=0" | odmchange -o HACMPcluster

                        cl_dspmsg -s 43 cspoc.cat 36 "%1\$s: The cluster configuration has been changed - %2\$s %3\$s has been added.  The configuration must be synchronized to make this change effective across the cluster." clmgr 'CRITICAL Volume Group' "$volume_group"
                        rc=$RC_SUCCESS
                    fi
                fi
            fi

        else
            # critical was specified as "no" - see if our vg is currently
            # marked as critical
            cl_ls_shared_vgs -c -C -O | grep -wq $volume_group
            if (( $? != 0 )); then
                rc=$RC_SUCCESS  # Already not CRITICAL
            else
                # Currently marked as critical - delete the entry
                if ! odmdelete -q "value = $volume_group and name = CRITICAL_VG" -o HACMPresource                                                 
                then
                    CL=$LINENO cl_dspmsg -s 24 cspoc.cat 30 "%1\$s: Marking volume group %2\$s in resource group %3\$s as non-CRITICAL failed." clmgr "$volume_group" "$RGNAME" 1>&2
                    rc=$RC_ERROR
                else
                    # PowerHA config changed - reset the cluster handle to
                    # force a sync
                    print "HACMPcluster: handle=0" | odmchange -o HACMPcluster

                    cl_dspmsg -s 24 cspoc.cat 31 "%1\$s: The cluster configuration has been changed - %2\$s %3\$s is no longer marked CRITICAL. The configuration must be synchronized to make this change effective across the cluster." clmgr 'CRITICAL Volume Group' "$volume_group"
                    rc=$RC_SUCCESS
                fi
            fi
        fi
    fi

    #
    : Add new disks to the volume group
    #
    if (( $rc == RC_UNKNOWN || $rc == RC_SUCCESS )) && [[ -n $add ]]
    then
        typeset vgnode= PVDATA=
        CL=$LINENO getPVData "$nodes" vgnode PVDATA $volume_group
        compatibleDisks=$(/usr/es/sbin/cluster/sbin/cl_lsfreedisks $volume_group | awk -F' ' '{print $1 " " $3}')

        # loop through all specified disks
        for disk in ${add//,/ }; do
            # first check if it is already in the vg
            typeset -i alreadyInVG=0
            typeset -i isCompatible=0
            typeset devname pvid vgname rest
            print -- "$PVDATA" |\
            while IFS= read LINE; do
                IFS=$ORIG_IFS print -- "$LINE" | read devname pvid vgname rest
                [[ $volume_group != $vgname ]] && continue
                if [[ $disk == $devname || $disk == $pvid ]]; then
                    alreadyInVG=1
                    break
                fi
            done
            
            # Check $add disks for compatibility.
            # Should ignore dedicated disks for a shared VG.
            # Should ignore disks which are already part of other VGs.
            if (( ! alreadyInVG )); then
                isCompatible=0
                print -- "$compatibleDisks" | while IFS=" " read pvid diskname
                do
                    if [[ $disk == $pvid || $disk == $diskname ]]; then
                        isCompatible=1
                    fi
                done
                
                if (( ! isCompatible )); then
                    cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1433 "\nWarning: Ignoring \"%1\$s\".\nEither it's a dedicated disk which cannot be used to extend the shared VG:\"%2\$s\"\nor this disk is already part of some other VG.\n\n" "$disk" "$volume_group"
                    continue
                fi
             fi
             
            if (( ! alreadyInVG )); then
                #
                # Get a valid reference node - disks can be specified with
                # a reference node using the form <disk>@<node>
                #
                typeset refnode=
                if [[ $disk == *@* ]]; then
                    # already have a reference node - get the data there
                    print -- "$disk" | IFS=@ read disk refnode
                    CL=$LINENO getDiskData "$disk" "$refnode" IDENTIFIERS true
                else
                    # no explicit reference node given - try the local if its
                    # among the candidates
                    if [[ " ${nodes//,/ } " == *\ $LOCAL_NODE\ * ]]; then
                        CL=$LINENO getDiskData "$disk" "$LOCAL_NODE" IDENTIFIERS true
                        if (( $? != RC_SUCCESS )); then
                            rc=$RC_INCORRECT_INPUT
                            break
                        fi
                        print -- "$IDENTIFIERS" | IFS=: read devname pvid uuid refnode rest
                    fi

                    # try all nodes
                    CL=$LINENO getDiskData "$pvid" "$nodes" IDENTIFIERS true
                fi
                if (( $? == RC_SUCCESS )); then
                    print -- "$IDENTIFIERS" | IFS=: read devname pvid uuid refnode rest
                    disk=$pvid
                fi

                print -- "$0()[$LINENO]($SECONDS): _CSPOC_CALLED_FROM_SMIT=true cl_extendvg -cspoc \"-n $nodes\" -R \"$refnode\" $volume_group $disk" >>$CLMGR_TMPLOG
                _CSPOC_CALLED_FROM_SMIT=true cl_extendvg -cspoc "-n $nodes" \
                                    -R "$refnode"      \
                                    "$volume_group"    \
                                    $disk
                rc=$?
                print "cl_extendvg RC: $rc" >>$CLMGR_TMPLOG
                (( $rc != RC_SUCCESS )) && break
            else
                CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 417 "Warning: the specified disk, \"%1\$s\", is already in volume group \"%2\$s\".\n\n" "$disk" "$volume_group"
                rc=$RC_SUCCESS
            fi

            #================================================
            : If the customer also specified a mirror pool,
            : add the newly added disk to it now.
            #================================================
            if (( $rc == RC_SUCCESS )) && [[ $mp_name != *([[:space:]]) ]]
            then
                print "$0()[$LINENO]($SECONDS): cl_mp_disks -cspoc \-n $nodes\" -p \"$mp_name\" \"$volume_group\" $pvid" >>$CLMGR_TMPLOG  # Log the command
                cl_mp_disks -cspoc "-n $nodes" \
                                    -p "$mp_name" \
                                    "$volume_group" \
                                    $pvid
                rc=$?
                print "cl_mp_disks RC: $rc" >>$CLMGR_TMPLOG
            fi
        done
    fi # done adding disks to vg and mp

    #
    : Remove disks from the volume group
    #
    if (( $rc == RC_UNKNOWN || $rc == RC_SUCCESS )) && [[ -n $remove ]]
    then
        for disk in ${remove//,/ }; do
            #Convert PVID to DiskName as cl_reducevg expects disk name
            disk=$(LC_ALL=C lspv -u | grep -w $disk | awk -F" " '{print $1}')
            typeset refnode=${nodes%%,*}
            print -- "$0()[$LINENO]($SECONDS): _CSPOC_CALLED_FROM_SMIT=true cl_reducevg -cspoc \"-n $nodes\" -f -R \"$refnode\" $volume_group $disk" >>$CLMGR_TMPLOG  # Log the command
            _CSPOC_CALLED_FROM_SMIT=true cl_reducevg -cspoc "-n $nodes" \
                                -f -R "$refnode" \
                                "$volume_group"  \
                                $disk
            rc=$?
            print "cl_reducevg RC: $rc" >>$CLMGR_TMPLOG # Log command result
            (( $rc != RC_SUCCESS )) && break
        done
    fi

    #
    : Check for updates to notify method or failure action
    #
    if (( $rc == RC_UNKNOWN || $rc == RC_SUCCESS )) && \
       [[ -n $failure_action || -n $notify_method ]]
    then
        typeset fa= nm=
        [[ -n $failure_action ]] && fa="FAILURE_ACTION=$failure_action"
        [[ -n $notify_method  ]] && nm="NOTIFY_METHOD=$notify_method"

        print -- "$0()[$LINENO]($SECONDS): clchappmon MONITOR_TYPE=selective_fallover $fa $nm name=$volume_group" >>$CLMGR_TMPLOG  # Always log commands
        clchappmon MONITOR_TYPE=selective_fallover $fa $nm name=$volume_group
        rc=$?
        print "clchappmon RC: $rc" >>$CLMGR_TMPLOG  # Always log command result
    fi

    #
    : Check for updates to scsi persistent reserve configuration
    #
    if (( $rc == RC_UNKNOWN || $rc == RC_SUCCESS )) && [[ -n $scsipr_action ]];then
       if [[ $scsipr_action == clear ]];then

           # Check if VG is reserved or not , if it is not reserved then throw 
           # an error else go ahead and clear the reservation
           print "$0()[$LINENO]($SECONDS): clpr_ReadRes_vg $volume_group" >>$CLMGR_TMPLOG
           clpr_ReadRes_vg $volume_group 2>>$CLMGR_TMPLOG
           rc=$?
           print "$0()[$LINENO]($SECONDS): clpr_ReadRes_vg RC: $rc" >>$CLMGR_TMPLOG
           if (( $rc != RC_SUCCESS ));then
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 399 "\nERROR:volume group \"%1\$s\" is not reserved , this operation\nmust be run to clear the reservation of volume group if it is reserved.\n\n" "$volume_group" 1>&2
                rc=RC_ERROR
           else
               #
               : Clear the registrations from the volume group
               #
               print "$0()[$LINENO]($SECONDS): clpr_clear_vg $volume_group" >>$CLMGR_TMPLOG
               clpr_clear_vg $volume_group 2>>$CLMGR_TMPLOG
               rc=$?
               print "$0()[$LINENO]($SECONDS): clpr_clear_vg RC: $rc" >>$CLMGR_TMPLOG
               if (( $rc != RC_SUCCESS ));then
                    dspmsg -s $CLMGR_SET $CLMGR_MSGS 109 "\nERROR: the operation appears to have failed.\n\n" 1>&2
                    rc=RC_ERROR
               fi
           fi
       else # set
           dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 "\nERROR: invalid value specified for \"%1\$s\":  \"%2\$s\".\n" SCSIPR_ACTION "$scsipr_action" 1>&2
           dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n" "clear" 1>&2
           rc=RC_INCORRECT_INPUT
       fi
    fi # done processing scsi pr updates

    #
    # All done with the updates - make sure there were some
    #
    if (( $rc == RC_UNKNOWN )); then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 401 "\nERROR: no valid modifications were specified for \"%1\$s\".\n\n" "$volume_group" 1>&2
        rc=$RC_MISSING_INPUT

    elif (( $rc != RC_SUCCESS )); then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 400 "\nERROR: failed to modify \"%1\$s\".\n\n" "$volume_group" 1>&2
        rc=$RC_ERROR

    #===========================================================
    : If output from this operation was requested, retrieve it
    #===========================================================
    else
        if (( CLMGR_VERBOSE )) || [[ -n $CLMGR_ATTRS ]]; then
           CL=$LINENO KLIB_HACMP_get_volume_group_attributes "$volume_group" properties
        fi
    fi

    #=======================================================================
    : If a user input error was detected, provide some helpful suggestions
    #=======================================================================
    if (( $rc == RC_MISSING_INPUT || $rc == RC_INCORRECT_INPUT )) && \
       [[ $CLMGR_GUI == *([[:space:]]) ]]
    then
        CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 104 "For more information about available options and syntax, try \"$HAUTILS/clmgr %1\$s\". As an alternative, if the PowerHA SystemMirror man pages have been installed, invoke \"$HAUTILS/clmgr -hv\" (or \"/usr/bin/man clmgr\"), searching for \"%2\$s\" in the displayed text.\n\n" \
        "add volume_group -h" "VOLUME GROUP:" "$CLMGR_PROGNAME" 1>&2
    fi

    log_return_msg "$rc" "$0()" "$LINENO"
    return $?
} # End of "KLIB_HACMP_modify_volume_group()"

#================================================
# The following, commented line enforces coding
# standards when this file is edited via vim.
#================================================
# vim:tabstop=4:shiftwidth=4:expandtab:smarttab
#================================================

