#!/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_add_volume_group.sh 1.20
#  
# 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_add_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_add_volume_group

=head1 SYNOPSIS

 clmgr add volume_group [ <vgname> ] \
             NODES="<node#1>,<node#2>[,...>]" \
             PHYSICAL_VOLUMES="<disk#1>[,<disk#2>,...]" \
             [ TYPE={original|big|scalable|legacy} ] \
             [ RESOURCE_GROUP=<RESOURCE_GROUP> ] \
             [ PPART_SIZE={4|1|2|8|16|32|64|128|256|512|1024} ] \
             [ MAJOR_NUMBER=## ] \
             [ 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} ] \
             [ MIRROR_POOL_NAME="<mp_name>" ] \
             [ CRITICAL={false|true} ] \
                 [ FAILUREACTION={halt|notify|fence|
                                   stoprg|moverg} ] \
                 [ NOTIFYMETHOD=</file/to/invoke> ] \
                 [ LVM_PREFERRED_READ=<roundrobin|favorcopy|siteaffinity> ] \
             [ ENABLE_LV_ENCRYPTION={yes|no} ]

 NOTE: "MAX_PHYSICAL_PARTITIONS", "MAX_LOGICAL_VOLUMES",
       "MIRROR_POOL_NAME", and "MP_STRICTNESS", only
       apply to "scalable" volume groups.
 NOTE: specifying the volume group major number may result in the
       command being unable to execute successfully on a node
       that does not have the major number currently available.
       Please check for a commonly available major number on
       all nodes before changing this setting.

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

=head1 DESCRIPTION

Attempts to add a new volume group.

=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 label that is to be applied to this volume group. If not
    provided, a default label will be used (i.e. "vg01").

 3. nodes [REQUIRED] [string]
    One or more nodes to create this volume group on.

 4. disks [REQUIRED] [string]
    One or more shared (between the specified nodes) disks that are to
    be added to this volume group.

 5. type [OPTIONAL] [string]
    The type of volume group to create, defaulting to "Original".

    TYPE={Original,Big,Scalable,Legacy}

 6. rg [OPTIONAL] [string]
    A resource group to add this volume group to, or to create, if needed.
    By default "CONCURRENT_ACCESS" is "true" so the resource group is
    being created by this command will be created as a concurrent
    resource group (i.e. with a startup policy of OAAN).

 7. ppart_size [OPTIONAL] [integer]
    The size of the physical partitions used within the volume group,
    defaulting to 4 MB.

    PPARTITION_SIZE={4,1,2,8,16,32,64,128,256,512,1024}

 8. major [OPTIONAL] [integer]
    The major number to assign to this volume group.

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

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

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

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

    Accepted values:  false|one|pool|remove

 13. 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).

 14. 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.

 15. mp_name [OPTIONAL] [string]
    The name of the mirror pool to create.

 16. mp_strictness [OPTIONAL] [string]
    Mirror pool strictness, which can be used to enforce tighter restrictions
    on mirror pool use. Mirror pool strictness can have one of the following
    three values: off, on, super.

 17. 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".

 18. 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

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

 20. LVM_PREFERRED_READ [OPTIONAL] [string]
     This attribute controls LVM read operations when there are mirror pools.
     This option decides which copy should be used for reading the data.
     Supported options are roundrobin, favorcopy or siteaffinity.
     favorcopy - Choose if you would like to read from Flash storage.
     siteaffinity - Choose if you would like to read from 
     local storage path always based on the resource group location.
     siteaffinity option is available only for site based clusters.

 21. ENABLE_LV_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.


#============================================================================
#
# Name:        KLIB_HACMP_add_volume_group
#
# Description: This is the main, FPATH function that is invoked by clmgr
#              to add a volume group to the specified cluster nodes.
#
# Inputs:      See the "devDoc()" function, above.
#
# Outputs:     The properties hash is populated. The only other outputs are
#              any error messages that might be needed.
#
# Returns:     Zero if no errors are detected. Otherwise, an appropriate
#              non-zero value is returned. Refer to the "RETURN" section
#              of the "devDoc()" function, above, for the standard return
#              code values/meanings for clmgr.
#
#============================================================================
function KLIB_HACMP_add_volume_group {
    . $HALIBROOT/log_entry "$0()" "$CL"
    : version=@(#)  7b36ae1 43haes/lib/ksh93/hacmp/KLIB_HACMP_add_volume_group.sh, 726, 2147A_aha726, Sep 08 2021 09:17 PM
    : INPUTS: $*

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

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

    #===================================
    : Declare and initialize variables
    #===================================
    typeset -i rc=$RC_UNKNOWN
    typeset typeOpt= rOpt= yOpt= OOpt= POpt= vOpt= pOpt=
    typeset MOpt= hOpt= sOpt= QOpt= aOpt=
    typeset INPUT= ATTR= VALUE= ORDER= PVIDs=
    typeset rg_startup_pref="-E"

    typeset cur_oslevel=$(oslevel -r|sed 's/-//g')
    typeset -i min_req_ver=730000

    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 ORIGINAL_LC=$ORIGINAL
    typeset -l BIG_LC=$BIG
    typeset -l SCALABLE_LC=$SCALABLE
    typeset -l LEGACY_LC=$LEGACY
    if [[ $LANG == @(|C|en_US) ]]; then
        ORIGINAL_LC=o
        BIG_LC=b
        SCALABLE_LC=s
        LEGACY_LC=l
    fi

    [[ -z $_CSPOC_CALLED_FROM_SMIT ]] && _CSPOC_CALLED_FROM_SMIT=true
    export _CSPOC_CALLED_FROM_SMIT

    typeset existing
    CL=$LINENO KLIB_HACMP_list_volume_groups existing 2>>$CLMGR_TMPLOG

    #==========================================
    : Make sure the system requisites are met
    #==========================================
    lslpp -lc bos.clvm.enh >>$CLMGR_TMPLOG 2>&1
    if (( $? != RC_SUCCESS )); then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 59 '\nERROR: missing requisite fileset: "%1$s"\n' bos.clvm.enh 1>&2
        rc=$RC_MISSING_DEPENDENCY
    fi

    #=================
    : Validate input
    #=================
    if [[ -n $type ]]; then
        typeset -l type_lc=$type
        case $type_lc in
            $ORIGINAL_LC*) typeOpt=""   ;;
            $BIG_LC*)      typeOpt="-B" ;;
            $SCALABLE_LC*) typeOpt="-S" ;;
            $LEGACY_LC*)   typeOpt="-I" ;;
            *) dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 "\nERROR: invalid value specified for \"%1\$s\":  \"%2\$s\"\n" TYPE "$type" 1>&2
               dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "$ORIGINAL, $BIG, $SCALABLE, $LEGACY" 1>&2
               rc=$RC_INCORRECT_INPUT
            ;;
        esac
    elif [[ $cur_oslevel -ge $min_req_ver ]];then
        typeOpt="-S"
    fi

    if [[ -z $nodes ]]; then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 101 "\nERROR: this operation requires the \"%1\$s\" attribute.\n\n" NODES 1>&2
        rc=$RC_MISSING_INPUT
    else
        typeset node=
        typeset -i invalid_nodes=0
        for node in ${nodes//,/ }; do
            CL=$LINENO KLIB_HACMP_is_known_node $node >/dev/null
            if (( $? != RC_SUCCESS )); then
                (( invalid_nodes++ ))
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 102 "\nERROR: \"%1\$s\" does not appear to exist!\n\n" "$node" 1>&2
                rc=$RC_NOT_FOUND
            fi
        done

        if (( invalid_nodes > 0 )); then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 151 "Available Nodes:\n\n" 1>&2

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

    if [[ -n $volume_group && " ${existing[*]} " == *\ $volume_group\ * ]]; then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 229 "\nERROR: the specified object already exists: \"%1\$s\"\n\n" "$volume_group" 1>&2
        rc=$RC_INCORRECT_INPUT
    fi

    if [[ -z $disks ]]; then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 101 "\nERROR: this operation requires the \"%1\$s\" attribute.\n\n" PHYSICAL_VOLUMES 1>&2
        rc=$RC_MISSING_INPUT
    else
        #========================================================
        : Attempt to validate all provided disks, attempting to
        : convert them into PVID form, if needed.
        #========================================================
        typeset free_disks=$(LANG=C LC_ALL=C $HACSPOC/cllspvids -O -n $nodes)
        for disk in $disks; do
            typeset node=
            if [[ $disk == *@* ]]; then
                print -- "$disk" | IFS=@ read disk node
            else
                if [[ " ${nodes//,/ } " == *\ $LOCAL_NODE\ * ]]; then
                    node=$LOCAL_NODE
                else
                    node=${nodes%%,*}
                fi
            fi

            #==================================================================
            # "$free_disks" format is:
            #
            #     pvid  (<hdisk> on node <node> [at site <site>])
            #
            # Or, if the same hdisk name is used on multiple nodes:
            #
            #     pvid  (<hdisk> on nodes node1[,node2,...] [at site <site>])
            #
            # Or, if the hdisk name is the same across the given list of nodes:
            #
            #     pvid  (hdisk_name on all cluster nodes)
            #
            #==================================================================
            typeset LINE= PVID=
            : See if $disk is already a PVID
            print -- "$free_disks" |\
            while read LINE; do
                if [[ $LINE == $disk\ * ]]; then
                    PVID=$disk
                    break
                fi
            done

            if [[ -z $PVID ]]; then
                typeset site_list=$(/usr/es/sbin/cluster/utilities/clodmget -f name -n HACMPsite)
                if [[ -n $site_list ]]; then
                    for site in $site_list
                    do
                    nodelist=$(/usr/es/sbin/cluster/utilities/clodmget -q "name = $site" -f nodelist -n HACMPsite)
                    if [[ -n $(print -- "$nodelist" | /usr/bin/grep -w "$node") ]]; then
                        LINE=$(print -- "$free_disks"       | \
                                   /usr/bin/grep -w "$disk" | \
                                   /usr/bin/grep -we "$node" -we "$site" -we "on all")
                        break
                    fi
                    done
                else
                    LINE=$(print -- "$free_disks"       | \
                               /usr/bin/grep -w "$disk" | \
                               /usr/bin/grep -we "$node" -we "on all")
                fi
                [[ -n $LINE ]] && PVID=${LINE%%[[:space:]]*}
            fi

            if [[ -n $PVID ]]; then
                PVIDs="$PVIDs $PVID"
            else
                : Use $disk as-is
                CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 243 "\nWarning: unable to verify that \"%1\$s\" is free/available, and shared between the specified nodes, \"%2\$s\".\n" "$disk" "$nodes" 1>&2
                PVIDs="$PVIDs $disk"
            fi
        done
    fi

    #===================================================================
    : If a resource group was given, it may be an existing group, or
    : it may be a new group. So we can only check for well-formedness.
    #===================================================================
    if [[ $rg != *([[:space:]]) ]]; then
        if [[ -n "${rg//[a-zA-Z0-9_]/}" ]]; then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 105 "\nERROR: one or more invalid characters were detected in \"%1\$s\" (\"%2\$s\").\n\nValid characters includeletters, numbers, and underscores only.\n\n" "$rg" "${rg//[a-zA-Z0-9_]/} " 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    fi

    if [[ $mp_strictness != *([[:space:]]) ]]; then
        case $mp_strictness in
            @(n|f)*) mp_strictness="" ;;
            @(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" "MP_STRICTNESS" "$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

    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

    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 MAJOR_NUMBER@$major \
                 LTG_SIZE@$ltg_size \
                 PPART_SIZE@$ppart_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

    #==============================================================
    : Check for options that apply only to Scalable volume groups
    #==============================================================
    if [[ $typeOpt != "-S" ]]; 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 [[ -n $VALUE ]]; then
                CL=$LINENO cl_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" "TYPE=$SCALABLE" "$ATTR" 1>&2
                rc=$RC_INCORRECT_INPUT
            fi
        done
    fi

    if [[ $notify_method != *([[:space:]]) && ! -e $notify_method ]]; then
        CL=$LINENO 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

    if [[ $notify_method != *([[:space:]]) ]] &&
       [[ -z $critical || $critical == @(f|n)* ]]
    then
        CL=$LINENO cl_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
    if [[ $failure_action != *([[:space:]]) ]] &&
       [[ -z $critical || $critical == @(f|n)* ]]
    then
        CL=$LINENO cl_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

    if [[ -n $rg ]]; then
        typeset RG_STARTUP_POLICY=$($HAUTILS/clodmget -q "group=$rg" -n -f startup_pref HACMPgroup)
        if [[ $RG_STARTUP_POLICY == "OAAN" ]]; then
            # concurrent Resource Group.
            rg_startup_pref="-C"
        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  
    #====================================================================
    : If no errors have been detect in the input, perform the operation
    #====================================================================
    if (( $rc == RC_UNKNOWN )); then
        [[ $rg != *([[:space:]]) ]]            && rOpt="-r $rg"
        [[ $volume_group != *([[:space:]]) ]]  && yOpt="-y $volume_group"
        [[ $major == *([[:space:]]) ]]         && major=$($HACSPOC/cl_getmajor)
        [[ $max_pparts == +([[:digit:]]) ]]    && POpt="-P $max_pparts"
        [[ $max_lvs == +([[:digit:]]) ]]       && vOpt="-v $max_lvs"
        [[ $critical == @(y|t)* ]]             && OOpt="-O"
        [[ -n $ppart_size ]]                   && sOpt="-s $ppart_size"
        [[ $mp_name != *([[:space:]]) ]]       && pOpt="-p $mp_name"
        [[ -n $encryption && $encryption == @(y|t)* ]] && kOpt="-k y"
        [[ -n $encryption && $encryption == @(n|f)* ]] && kOpt="-k n"
        if [[ $mp_strictness != *([[:space:]]) && $mp_strictness != "no" ]]
        then
            MOpt="-M $mp_strictness"
        fi
        

        #=====================================================
        : Make sure there are no duplicates in the PVID list
        #=====================================================
        PVIDs=$(print "${PVIDs//+([[:space:]])/$NL}" | sort -u)
        PVIDs=${PVIDs//+([[:space:]])/ }

        print -- "$0()[$LINENO]($SECONDS): $HASBIN/cl_mkvg -f -n $typeOpt -cspoc \"-n $nodes\" $rOpt $yOpt $sOpt -V\"$major\" $OOpt $MOpt $pOpt -l false $rg_startup_pref $POpt $vOpt $kOpt $PVIDs" >>$CLMGR_TMPLOG  # Always log commands
        $HASBIN/cl_mkvg -f -n $typeOpt \
                           -cspoc "-n $nodes" \
                                  $rOpt \
                                  $yOpt \
                                  $sOpt \
                                  -V"$major" \
                                  $OOpt \
                                  $MOpt \
                                  $pOpt \
                                  -l false \
                                  $rg_startup_pref \
                                  $POpt \
                                  $vOpt \
                                  $kOpt \
                                  $PVIDs > $TMPDIR/clmgr.KHav.addvg.$$
        rc=$?
        print "$0()[$LINENO]($SECONDS): cl_mkvg RC: $rc" >>$CLMGR_TMPLOG  # Always log command result
        cat $TMPDIR/clmgr.KHav.addvg.$$

        #===============================================================
        : If no volume group name was explicitly specified, attempt to
        : determine the automatically assigned name of the new VG.
        #===============================================================
        if [[ $volume_group == *([[:space:]]) ]]; then
            typeset DATA=$(/usr/bin/cat $TMPDIR/clmgr.KHav.addvg.$$ 2>>$CLMGR_TMPLOG)
                    DATA=${DATA##*\ vg}
                    DATA=${DATA%%[\ \.]*}
            if [[ $DATA != *([[:space:]]) ]]; then
                volume_group="vg$DATA"
            fi
        fi
        /usr/bin/rm -f $TMPDIR/clmgr.KHav.addvg.$$

        if (( $rc != RC_SUCCESS )); then
            if [[ $volume_group == *([[:space:]]) ]]; then
                volume_group="vg##"
            fi
            rc=$RC_ERROR

        #===================================================================
        : Some options require the cl_chvg command. If any were specified,
        : and the call to cl_mkvg was successful, construct/invoke the
        : command here.
        #===================================================================
        elif [[ -n "$ltg_size$quorum$activate$migrate$preferred_read" ]]; then
            LOpt="" QOpt="" aOpt="" hOpt=""

            [[ -n $ltg_size ]]         && LOpt="-L $ltg_size"
            [[ $quorum   == @(y|t)* ]] && QOpt="-Q y" || QOpt="-Q n"
            [[ $activate == @(y|t)* ]] && aOpt="-a y" || aOpt="-a n"
            [[ -n $migrate ]]          && hOpt="-h $migrate"
            [[ -n $preferred_read ]] && rOpt="-r $preferred_read"       
            print -- "$0()[$LINENO]($SECONDS): $HASBIN/cl_chvg -cspoc \"-n $nodes\" $LOpt $QOpt $hOpt $rOpt $volume_group" >>$CLMGR_TMPLOG  # Always log commands
            $HASBIN/cl_chvg -cspoc "-n $nodes" \
                            $LOpt \
                            $QOpt \
                            $hOpt \
                            $rOpt \
                            $volume_group
            rc=$?
            print "$0()[$LINENO]($SECONDS): cl_chvg RC: $rc" >>$CLMGR_TMPLOG  # Always log command result
        fi

        if (( $rc != RC_SUCCESS )); then
            rc=$RC_ERROR

        elif [[ -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): $HAUTILS/clchappmon MONITOR_TYPE=selective_fallover $fa $nm name=$volume_group" >>$CLMGR_TMPLOG  # Always log commands
            $HAUTILS/clchappmon MONITOR_TYPE=selective_fallover $fa $nm name=$volume_group
            rc=$?
            print "cl_chvg RC: $rc" >>$CLMGR_TMPLOG  # Always log command result
        fi

        if (( $rc != RC_SUCCESS )); then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 201 "\nERROR: failed to create \"%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
    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\n\"$HAUTILS/clmgr %1\$s\". As an\nalternative, if the PowerHA SystemMirror man pages have been installed, invoke\n\"$HAUTILS/clmgr -hv\" (or \"/usr/bin/man clmgr\"),\nsearching 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_add_volume_group()"

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