#!/bin/ksh93
#  ALTRAN_PROLOG_BEGIN_TAG                                                     
#  This is an automatically generated prolog.                                  
#                                                                              
#  Copyright (C) Altran ACT S.A.S. 2021.  All rights reserved.         	
#                                                                              
#  ALTRAN_PROLOG_END_TAG                                                       
#                                                                              
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2012,2013 
# 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_logical_volume.sh, 726, 2147A_aha726, Sep 08 2021 09:17 PM

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

# 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_logical_volume

=head1 VERSION

 Version Number:  1.5
 Last Extracted:  1/31/14 04:42:06
 Last Changed:    8/6/13 16:54:53

 Path, Component, Release(, Level):
 src/43haes/lib/ksh93/hacmp/KLIB_HACMP_add_logical_volume.sh, hacmp.assist, 61haes_r714

=head1 SYNOPSIS

 clmgr add logical_volume [ <lvname> ] \
             VOLUME_GROUP=<vgname> \
             LOGICAL_PARTITIONS=## \
             [ PHYSICAL_VOLUMES="<disk#1>[,<disk#2>,...]" ] \
             [ TYPE={jfs|jfs2|sysdump|paging|
                     jfslog|jfs2log|aio_cache|boot} ] \
             [ POSITION={outer_middle|outer_edge|center|
                         inner_middle|inner_edge  } ] \
             [ PV_RANGE={minimum|maximum} ] \
             [ MAX_PVS_FOR_NEW_ALLOC=## ] \
             [ LPART_COPIES={1|2|3} ] \
             [ WRITE_CONSISTENCY={active|passive|off} ] \
             [ LPARTS_ON_SEPARATE_PVS={yes|no|superstrict} ] \
             [ RELOCATE={yes|no} ] \
             [ LABEL="<lv_label>" ] \
             [ MAX_LPARTS=#### ] \
             [ BAD_BLOCK_RELOCATION={yes|no} ] \
             [ SCHEDULING_POLICY={parallel|sequential|
                                  parallel_sequential|
                                  parallel_round_robin} ] \
             [ VERIFY_WRITES={false|true} ] \
             [ ALLOCATION_MAP=<file> ] \
             [ STRIPE_SIZE={4K|8K|16K|32K|64K|128K|256K|512K|
                            1M|2M|4M|8M|16M|32M|64M|128M} ] \
             [ SERIALIZE_IO={false|true} ] \
             [ FIRST_BLOCK_AVAILABLE={false|true} ] \
             [ FIRST_COPY_MIRROR_POOL=<mirror_pool> ] \
             [ SECOND_COPY_MIRROR_POOL=<mirror_pool> ] \
             [ THIRD_COPY_MIRROR_POOL=<mirror_pool> ] \
             [ GROUP=<group> ] \
             [ PERMISSIONS=#### ] \
             [ NODE=<reference_node_in_vg> ] \
             [ ENABLE_LV_ENCRYPTION={yes|no} ] \
             [ AUTH_METHOD={keyserv|pks} ] \
             [ METHOD_DETAILS=<key server ID> ] \
             [ AUTH_METHOD_NAME=<Alias name for auth method> ]

 NOTE: STRIPE_SIZE may not be used with LPARTS_ON_SEPARATE_PVS,
       PV_RANGE, or SCHEDULING_POLICY.

 NOTE: an alias for "logical_volume" is "lv".

=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. logical_volume [OPTIONAL] [string]
    The logical volume label. The default label is None.
    The maximum size of the Label variable is 127
    characters. Note: If the logical volume is going to
    be used as a journaled file system (jfs), then the
    JFS will use this field to store the mount point of
    the file system on the logical volume for future
    reference.                

 3. volume_group [REQUIRED] [string]
    The name of the volume group to add this logical
    volume to.

 4. logical_partitions [REQUIRED] [string]
    The minimum number of logical partitions to allocate
    to the logical volume. The minimum value must be
    equal to or less than the maximum.

 5. physical_volumes [REQUIRED] [string]
    The name(s) of the physical volume(s) to build this
    logical volume on. Physical volume names are typically
    in the form "hdiskx" where x is a system wide unique
    number. This name is assigned when the disk is detected
    for the first time on a system startup or when the
    system management commands are used at runtime to add a
    disk to the system.

 6. type [OPTIONAL] [set]
    Sets the logical volume type. The default type is "jfs"
    ("journaled file system").

    Valid values: jfs, jfs2, sysdump, paging, jfslog, jfs2log,
                  aio_cache, boot

 7. position [OPTIONAL] [set]
    The Intra-Physical Volume Allocation Policy to use for
    choosing physical partitions on a physical volume.

    "EDGE" and "INNER EDGE" - allocates partitions to the edges
    of the physical volume. These partitions have the slowest
    average seek times, which generally result in longer response
    times for any application that uses them.

    "MIDDLE" and "INNER MIDDLE" - allocates partitions away from
    the edges of the physical volume and out of the center. These
    strategies allocate reasonably good locations for partitions
    with reasonably good average seek times. Most of the
    partitions on a physical volume are available for allocation
    using these strategies.

    "CENTER" - allocates partitions to the center section of
    each physical volume. These partitions have the fastest
    average seek times, which generally result in the best
    response time for any application that uses them. There
    are fewer partitions on a physical volume that satisfy the
    "CENTER" strategy than for any other general strategy.

    Valid values: outer_edge, outer_middle, center,
                  inner_middle, inner_edge

 8. pv_range [OPTIONAL] [set]
    Specifies which Inter-Physical Volume Allocation Policy to
    use for choosing physical devices to allocate the physical
    partitions of a logical volume:

    "MINIMUM" - with 1 copy specified, indicates that, if
    possible, one physical volume should contain all the
    physical partitions of this logical volume. Otherwise,
    use the minimum number possible consistent with other
    parameters. This policy provides the greatest reliability,
    without mirrored copies, for a logical volume.

    "MINIMUM" - with 2 or 3 copies specified, indicates that
    as many physical volumes as there are copies should be
    used. Otherwise, the minimum number of physical volumes
    possible are used to hold all the physical partitions. At
    all times, the constraints imposed by other parameters are
    observed.

    "MAXIMUM" - indicates that the physical partitions of this
    logical volume should be spread over as many physical
    volumes as possible. This is a performance-oriented option
    and should be used with copies to improve availability. If
    a logical volume is not copied and is spread across multiple
    physical volumes, the loss of any physical volume containing
    a physical partition from that logical volume is enough to
    cause the logical volume to be incomplete.

    Valid values: minimum, maximum

 9. max_pvs_for_new_alloc [OPTIONAL] [integer]
    Sets the maximum number of physical volumes for new
    allocation. The value should be between one and the
    total number of physical volumes in the volume group.

 10. lpart_copies [OPTIONAL] [integer]
    The number of physical partitions allocated for each
    logical partition. The value can be from 1 to 3; the
    default is 1. A value of 2 or 3 indicates a mirrored
    logical volume.

    Valid values: 1, 2, 3

 11. write_consistency [OPTIONAL] [set]
    Specifies whether to ensure data consistency among
    mirrored copies of a logical volume during normal
    I/O processing.

    Valid values: active, passive, off

 12. lparts_on_sep_pvs [OPTIONAL] [set]
    Select "Yes" to follow a strict allocation policy. Copies
    for a logical partition cannot share the same physical
    volume.

    Select "No" to not follow a strict allocation policy.
    Copies for a logical partition can share the same physical
    volume.             

    Select "Superstrict" to follow a super strict allocation
    policy. Partitions allocated for one mirror cannot share
    a physical volume with the partitions from another mirror.

    Valid values: no, yes, superstrict

 13. relocate [OPTIONAL] [boolean]
    Specifies whether to allow the relocation of the logical
    volume during reorganization. Defaults to yes.

 14. max_lparts [OPTIONAL] [integer]
    Sets the maximum number of logical partitions that can be
    allocated to the logical volume.

 15. bad_block_reloc [OPTIONAL] [boolean]
    Specifies whether to use Bad Block Relocation. Bad-Block
    Relocation redirects read-write requests from a disk block
    that can no longer retain data to one that can. The process
    is transparent; the application does not receive notice
    that requests directed to a physical block are actually
    serviced by a different block. Defaults to yes.

 16. sched_policy [OPTIONAL] [set]
    The scheduling policy to use for the mirrored (copies > 1)
    logical volume:

    Sequential - performs copied write procedures in order:
    primary, secondary, tertiary. This policy waits for the
    write operation to complete for the previous physical
    partition before starting the write operation to the next
    one.

    For read operations, the primary copy is read. If the read
    operation is unsuccessful, the next copy is read. The
    primary copy is then fixed by turning the read operation
    into a write operation with hardware relocation specified
    on the call to the physical device driver.

    Parallel - starts the write operation for all the physical
    partitions in a logical partition at the same time. When
    the write operation to the physical partition that takes
    the longest to complete finishes, the write operation
    returns.

    Valid values: parallel, sequential, parallel/sequential,
                  parallel/round robin

 17. verify_writes [OPTIONAL] [set]
    Specifies whether to verify all writes to the logical
    volume with a follow-up read. Defaults to no.

 18. allocation_map [OPTIONAL] [abspath]
    File containing the exact physical partitions to allocate.
    See the documentation on the mklv command for more
    information on map files.

 19. stripe_size [OPTIONAL] [integer]
    The size of the stripe for a striped logical volume.
    This value is also known as stripe unit size. If no
    size is specified, the logical volume is assumed to
    be a non-striped logical volume.

    Valid values: 4 KB, 16 KB, 32 KB, 64 KB, 128 KB, 256K,
                  512K, 1M, 2M, 4M, 8M, 16M, 32M, 64M, 128M

 20. serialize_io [OPTIONAL] [boolean]
    Turn on or off serialization of overlapping I/Os. If
    serialization is turned on then overlapping IOs are not
    allowed on a block range and only a single IO in a block
    range is processed at any one time. Most applications
    like file systems and databases do serialization so
    serialization should be turned off. Defaults to no.

 21. first_block_avail [OPTIONAL] [boolean]
    For big vg format volume groups, this option indicates
    that the logical volume control block will not occupy
    the first block of the logical volume. Therefore, the
    space is available for application data. Defaults to no.

 22. mpool_copy1 [OPTIONAL] [string]
    Assigns the first copy of the logical volume to the
    specified mirror pool. Logical volume copies that are
    assigned to a mirror pool will only allocate partitions
    from the physical volumes in that mirror pool. This
    provides the ability to restrict the disks that a
    logical volume copy can use.

 23. mpool_copy2 [OPTIONAL] [string]
    Assigns the second copy of the logical volume to the
    specified mirror pool. Logical volume copies that are
    assigned to a mirror pool will only allocate partitions
    from the physical volumes in that mirror pool. This
    provides the ability to restrict the disks that a
    logical volume copy can use.

 24. mpool_copy3 [OPTIONAL] [string]
    Assigns the third copy of the logical volume to the
    specified mirror pool. Logical volume copies that are
    assigned to a mirror pool will only allocate partitions
    from the physical volumes in that mirror pool. This
    provides the ability to restrict the disks that a
    logical volume copy can use.

 25. group [OPTIONAL] [string]
    Specifies group ID for the logical volume special file.

 26. permissions [OPTIONAL] [set]
    Sets the access permission to read-write or read-only.

    Valid values:

        "w": Sets the access permission to read-write. 
        "r": Sets the access permission to read-only. 

    Note: Mounting a JFS file system on a read-only logical
          volume is not supported.

 27. label [OPTIONAL] [string]
    Sets the logical volume label. The default label is
    "None". The maximum size of the label is 127 characters.
    Note: If the logical volume is going to be used as a
    journaled file system (JFS), then the JFS will use this
    field to store the mount point of the file system on that
    logical volume for future reference.

 28. refnode [OPTIONAL] [string]
    A reference node to use to resolve any specified physical
    volumes.

 29. ENABLE_LV_ENCRYPTION [OPTIONAL] [boolean]
     Enables the data encryption option for the logical volume.
     To enable the encryption, auth_method should be passed. 

 30. AUTH_METHOD [OPTIONAL]
     Supported authentication methods are pks or keyserv.
     For pks authentication, AUTH_METHOD_NAME should be passed.
     For keyserv authentication, METHOD_DETAILS and AUTH_METHOD_NAME should be passed.

 31. METHOD_DETAILS [OPTIONAL] 
     If keyserv is configured as an authentication methods,
     key server ID should be passed to this attribute.
 
 32. AUTH_METHOD_NAME [OPTIONAL]
     Alias name should be passed for both pks and keyserv 
     authentication methods.
 
=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

=head1 COPYRIGHT

COPYRIGHT International Business Machines Corp. 2005,2010
All Rights Reserved

=cut
} # End of POD-formatted documentation.


function KLIB_HACMP_add_logical_volume {
    LINENO=2 . $HALIBROOT/log_entry "$0()" "$CL"
    : INPUTS: $*

    typeset -n properties=$1
    typeset logical_volume=${2//\"/}
    typeset volume_group=${3//\"/}
    typeset logical_partitions=${4//\"/}
    typeset physical_volumes=${5//\"/}
    typeset type=${6//\"/}
    typeset position=${7//\"/}
    typeset pv_range=${8//\"/}
    typeset max_pvs_for_new_alloc=${9//\"/}
    typeset lpart_copies=${10//\"/}
    typeset write_consistency=${11//\"/}
    typeset -l lparts_on_sep_pvs=${12//\"/}
    typeset relocate=${13//\"/}
    typeset max_lparts=${14//\"/}
    typeset bad_block_reloc=${15//\"/}
    typeset sched_policy=${16//\"/}
    typeset -l verify_writes=${17//\"/}
    typeset allocation_map=${18//\"/}
    typeset stripe_size=${19//\"/}
    typeset -l serialize_io=${20//\"/}
    typeset -l first_block_avail=${21//\"/}
    typeset mpool_copy1=${22//\"/}
    typeset mpool_copy2=${23//\"/}
    typeset mpool_copy3=${24//\"/}
    typeset group=${25//\"/}
    typeset permissions=${26//\"/}
    typeset label=${27//\"/}
    typeset refnode=${28//\"/}
    typeset -l enable_encryption=${29//\"/}
    typeset -l auth_method=${30//\"/}
    typeset method_details=${31//\"/}
    typeset auth_method_name=${32//\"/}
    typeset enable_encrypt_support="true"
    typeset cur_oslevel=$(oslevel -r|sed 's/-//g')
    typeset -i min_req_ver=730000

    [[ $CLMGR_LOGGING == 'med' ]] && set +x  # Only trace param values

    #===================================
    : Declare and initialize variables
    #===================================
    typeset -i rc=$RC_UNKNOWN
    typeset typeOpt="" nodes=""

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

    #=================
    : Validate input
    #=================
    if [[ -n $logical_volume ]]; then
        CL=$LINENO KLIB_HACMP_is_known_logical_volume $logical_volume
        if (( $? == RC_SUCCESS )); then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 229 "\nERROR: the specified object already exists: \"%1\$s\"\n\n" "$logical_volume" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    fi

    if [[ -n $type ]]; then
        typeset -l type_lc=$type
        case $type_lc in
            a*)     typeOpt=" -t aio_cache" ;;
            b*)     typeOpt=" -t boot"      ;;
            jfs)    typeOpt=" -t jfs"       ;;
            jfs2)   typeOpt=" -t jfs2"      ;;
            jfsl*)  typeOpt=" -t jfslog"    ;;
            jfs2l*) typeOpt=" -t jfs2log"   ;;
            p*)     typeOpt=" -t paging"    ;;
            s*)     typeOpt=" -t sysdump"   ;;
            *) CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 "\nERROR: invalid value specified for \"%1\$s\":  \"%2\$s\"\n" TYPE "$type" 1>&2
               CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "aio_cache, boot, jfs, jfs2, jfslog, jfs2log, paging, sysdump" 1>&2
               rc=$RC_INCORRECT_INPUT
            ;;
        esac
    fi

    if [[ -z $volume_group ]]; then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 101 "\nERROR: this operation requires the \"%1\$s\" attribute.\n\n" VOLUME_GROUP 1>&2
        rc=$RC_MISSING_INPUT
    else
        CL=$LINENO KLIB_HACMP_is_known_volume_group $volume_group >/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" "$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
            : Get the nodes for the volume group $volume_group
            typeset -A vgProps
            CL=$LINENO KLIB_HACMP_get_volume_group_attributes $volume_group vgProps
            nodes=${vgProps[NODES]}
        fi
    fi

    if [[ -n $physical_volumes ]]; then
        : Only need a reference node if disks are specified explicitly
        if [[ -z $nodes ]]; then
            [[ -z $refnode ]] && refnode=$LOCAL_NODE  # Best we can do...
        else
            if [[ -n $refnode ]]; then
                : Make sure the specified node is valid
                if [[ " ${nodes//,/ } " != *\ $refnode\ * ]]; then
                    CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 279 '\nERROR: the specified reference node, "%1$s", does not appear to host the given volume group, "%2$s".\n\n' "$refnode" "$volume_group" 1>&2
                    dspmsg -s $CLMGR_SET $CLMGR_MSGS 151 "Available Nodes:\n\n" 1>&2
                    typeset node=""
                    for node in ${nodes//,/ }; do
                        print -u2 "\t$node"
                    done
                    print -u2 ""
                    rc=$RC_INCORRECT_INPUT
                fi
            else
                refnode=$LOCAL_NODE
                if [[ " ${nodes//,/ } " != *\ $LOCAL_NODE\ * ]]; then
                    refnode=${nodes%%,*}  # Hope for the best...
                fi
            fi
        fi
    fi

    if [[ -z $logical_partitions ]]; then
        dspmsg -s $CLMGR_SET $CLMGR_MSGS 101 "\nERROR: this operation requires the \"%1\$s\" attribute.\n\n" LOGICAL_PARTITIONS 1>&2
        rc=$RC_MISSING_INPUT
    fi

    #==============================
    : Validate the integer inputs
    #==============================
    for INPUT in LOGICAL_PARTITIONS@$logical_partitions \
                 MAX_PVS_FOR_NEW_ALLOC@$max_pvs_for_new_alloc \
                 MAX_LPARTS@$max_lparts \
                 PERMISSIONS@$permissions
    do
        print -- "$INPUT" | IFS=@ read ATTR VALUE
        if [[ -n $VALUE ]]; then
            if [[ $VALUE != +([[:digit:]]) ]] || \
               (( $VALUE == 0 ))
            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
        fi
    done

    #==============================
    : Validate the boolean inputs
    #==============================
    for INPUT in RELOCATE@$relocate@yes, no \
                 BAD_BLOCK_RELOCATION@$bad_block_reloc@yes, no \
                 SERIALIZE_IO@$serialize_io@no, yes \
                 FIRST_BLOCK_AVAILABLE@$first_block_avail@no, yes \
                 ENABLE_LV_ENCRYPTION@$enable_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 [[ $enable_encryption == @(y|t)* || -n $auth_method || -n $method_details || -n $auth_method_name ]];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
            enable_encrypt_support="false"
            rc=$RC_MISSING_DEPENDENCY
        fi
        if [[ $auth_method == "pks" ]];then
            print -- "$0()[$LINENO]($SECONDS): check for pks support by executing command: hdcryptmgr pksshow" >>$CLMGR_TMPLOG
            pks_status=$(hdcryptmgr pksshow >>$CLMGR_TMPLOG 2>&1)
            if (( $? != 0 ));then
                # Get clutils.log fullpath
                clutils_log_dir=$(clodmget -q "name = clutils.log" -f value -n HACMPlogs)
                [[ -z $clutils_log_dir ]] &&
                    clutils_log_dir=$(clodmget -q "name = clutils.log" -f defaultdir -n HACMPlogs)
                clutils_log_dir=${clutils_log_dir:-"/var/hacmp/log"}

                clutils_log=${clutils_log_dir}/clutils.log
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 1481 "\nERROR: %1\$s authentication is not supported on node %2\$s, enable %1\$s authentication and rerun the command.\n\
Check log file %3\$s for more details.\n" "pks" "$LOCAL_NODE" "$clutils_log" 1>&2
                enable_encrypt_support="false"
                rc=$RC_MISSING_DEPENDENCY
            fi 
        fi
    fi

    #==========================
    : Validate the set inputs
    #==========================
    typeset ACCEPTED="" ATTR="" VALUE=""
    typeset -l ACCEPTED_LC="" VALUE_LC=""
    for INPUT in TYPE@$type@jfs,jfs2,sysdump,paging,jfslog,jfs2log,aio_cache,boot \
                 POSITION@$position@outer_middle,outer_edge,center,inner_middle,inner_edge \
                 PV_RANGE@$pv_range@minimum,maximum \
                 LPART_COPIES@$lpart_copies@1,2,3 \
                 WRITE_CONSISTENCY@$write_consistency@active,passive,off \
                 LPARTS_ON_SEPARATE_PVS@$lparts_on_sep_pvs@yes,no,superstrict \
                 SCHEDULING_POLICY@$sched_policy@parallel,sequential,parallel_sequential,parallel_round_robin \
                 STRIPE_SIZE@$stripe_size@none,4K,8K,16K,32K,64K,128K,256K,512K,1M,2M,4M,8M,16M,32M,64M,128M
    do
        print -- "$INPUT" | IFS=@ read ATTR VALUE ACCEPTED

        ACCEPTED=${ACCEPTED//$NL/,}
        ACCEPTED_LC=$ACCEPTED

        typeset -i INVALID=0
        for VALUE in ${VALUE//,/ }; do
            VALUE_LC=$VALUE
            if [[ ",$ACCEPTED_LC," != *,$VALUE_LC,* ]]; then
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 "\nERROR: invalid value specified for \"%1\$s\":  \"%2\$s\".\n" "$ATTR" "$VALUE" 1>&2
                INVALID=1
            fi
        done

        if (( INVALID )); then
            CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "$ACCEPTED" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    done


    typeset -l auth_method_lc=$auth_method
    if [[ -n $auth_method && $enable_encrypt_support == "true" && $enable_encryption == @(y|t)* ]];then
        CL=$LINENO verify_in_set auth_method "$auth_method_lc" "keyserv pks" auth_method_lc
        (( $? != RC_SUCCESS )) && rc=$RC_INCORRECT_INPUT
    fi

    #==============================================================
    # Verify all arguments are passed for LV encryption or not. 
    #==============================================================
    if [[ $rc != $RC_INCORRECT_INPUT && $enable_encryption == @(y|t)* && $enable_encrypt_support == "true" ]]
    then
        if [[ -z $auth_method ]];then
            cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1478 "\nERROR: Enabling logical volume encryption requires %1\$s attribute.\n" "AUTH_METHOD" 1>&2
            rc=$RC_INCORRECT_INPUT
        elif [[ $auth_method == "keyserv" ]] && [[ -z $auth_method_name || -z $method_details ]]
        then
            cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1476 "\nERROR: Enabling logical volume encryption using %1\$s authentication method requires %2\$s and %3\$s attributes.\n" "keyserv" "AUTH_METHOD_NAME" "METHOD_DETAILS" 1>&2
            rc=$RC_INCORRECT_INPUT   
        elif [[ $auth_method == "pks" && -z $auth_method_name ]]
        then
            cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1475 "\nERROR: Enabling logical volume encryption using %1\$s authentication method requires %2\$s attribute.\n" "pks" "AUTH_METHOD_NAME" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    fi

    if [[ $rc != $RC_INCORRECT_INPUT  && $enable_encryption != @(y|t)* && $enable_encrypt_support == "true" ]]
    then
        if [[ -n $auth_method || -n $auth_method_name || -n $method_details ]];then
             cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1479 "\nERROR: To use the authentication attributes, %1\$s should be set with yes.\n" "ENABLE_LV_ENCRYPTION" 1>&2
             rc=$RC_INCORRECT_INPUT
        fi
    fi  
     
    #============================================
    # Validate any mirror pool copy assignments
    #============================================
    if [[ -n $volume_group ]]; then
        typeset -i NEED_LIST=0
        for PAIR in FIRST_COPY_MIRROR_POOL@$mpool_copy1 \
                    SECOND_COPY_MIRROR_POOL@$mpool_copy2 \
                    THIRD_COPY_MIRROR_POOL@$mpool_copy3
        do
            print -- "$PAIR" | read ATTR VALUE
            if [[ -n $VALUE ]]; then
                typeset FOUND=$(CL=$LINENO KLIB_HACMP_is_known_mirror_pool "$mirror_pool" $volume_group)
                if (( $? != RC_SUCCESS )); then
                    dspmsg -s $CLMGR_SET $CLMGR_MSGS 102 '\nERROR: "%1$s" does not appear to exist!\n\n' "$mirror_pool" 1>&2
                    rc=$RC_NOT_FOUND
                    NEED_LIST=1
                else
                    typeset pool_name pool_vg
                    print -- "$FOUND" | read pool_name pool_vg
                    pool_vg=${pool_vg#\(}
                    pool_vg=${pool_vg%\)}

                    dspmsg -s $CLMGR_SET $CLMGR_MSGS 280 '\nERROR: "%1$s" resides within a different volume group, "%2$s".\n\n' "$mirror_pool" "$pool_vg" 1>&2
                    rc=$RC_INCORRECT_INPUT
                fi
            fi
        done

        if (( NEED_LIST )); then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 173 "Available Mirror Pools:\n\n" 1>&2

            typeset available
            CL=$LINENO KLIB_HACMP_list_mirror_pools available VOLUME_GROUP=$volume_group
            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
        fi
    fi

    #============================================
    # Validate the allocation map, if specified
    #============================================
    if [[ $allocation_map != *([[:space:]]) ]]; then
        if [[ $allocation_map == *[[:space:]]* ]]; then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 108 "\nERROR: the \"%1\$s\" attribute's value contains whitespace,\nwhich is not allowed: \"%2\$s\"\n\n" ALLOCATION_MAP "$allocation_map" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi

        if [[ $allocation_map != /* ]]; then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 106 "\nERROR: the specified path does not appear to be in absolute format:\n%1\$s\n\n" "$allocation_map" 1>&2
            rc=$RC_INCORRECT_INPUT

        elif [[ ! -e $allocation_map ]]; 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' "$allocation_map" "$LOCAL_NODE" 1>&2
            rc=$RC_NOT_FOUND
        fi
    fi

    if [[ $group != *([[:space:]]) ]]; then
        CL=$LINENO KLIB_HACMP_is_known_group $group
        if (( $? != RC_SUCCESS )); then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 102 "\nERROR: \"%1\$s\" does not appear to exist!\n\n" "$group" 1>&2
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 161 "Available Groups:\n\n" 1>&2

            typeset available
            CL=$LINENO KLIB_HACMP_list_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_INCORRECT_INPUT
        fi
    fi

    #====================================================================
    : If no errors have been detect in the input, perform the operation
    #====================================================================
    if (( $rc == RC_UNKNOWN )); then
        typeset yOpt="" tOpt="" aOpt="" eOpt="" uOpt="" wOpt="" sOpt="" rOpt=""
        typeset LOpt="" xOpt="" bOpt="" dOpt="" vOpt="" mOpt="" SOpt="" TOpt=""
        typeset pOpt="" GOpt="" POpt="" ROpt="" kOpt="" typeOpt="" mdOpt="" anOpt="" 

        [[ $lpart_copies      != *([[:space:]]) ]] && cOpt=" -c $lpart_copies"
        [[ $label             != *([[:space:]]) ]] && LOpt=" -L $label"
        [[ $max_lparts        != *([[:space:]]) ]] && xOpt=" -x $max_lparts"
        [[ $logical_volume    != *([[:space:]]) ]] && yOpt=" -y $logical_volume"
        [[ $type              != *([[:space:]]) ]] && tOpt=" -t $type"
        [[ $allocation_map    != *([[:space:]]) ]] && mOpt=" -m $allocation_map"
        [[ $first_block_avail != *([[:space:]]) ]] && TOpt=" -T O"
        [[ $group             != *([[:space:]]) ]] && GOpt=" -G $group"
        [[ $enable_encryption == @(y|t)* ]] && kOpt=" -k y" 
        [[ $auth_method != *([[:space:]]) ]] && typeOpt=" -t $auth_method"
        [[ $method_details != *([[:space:]]) ]] && mdOpt=" -m $method_details"
        [[ $auth_method_name != *([[:space:]]) ]] && anOpt=" -n $auth_method_name"
    
        if [[ $stripe_size != *([[:space:]]) && \
              $strip_size == +([0-9])@(K|M) ]]
        then
            SOpt=" -S $stripe_size"
        fi

        if [[ $max_pvs_for_new_alloc != *([[:space:]]) ]]; then
            uOpt=" -u $max_pvs_for_new_alloc"
        fi
        if [[ $relocate != *([[:space:]]) ]]; then
            [[ $relocate == @(y|t)* ]] && rOpt=" -r y" || rOpt=" -r n"
        fi
        if [[ $bad_block_reloc != *([[:space:]]) ]]; then
            [[ $bad_block_reloc == @(y|t)* ]] && bOpt=" -b y" || bOpt=" -b n"
        fi
        if [[ $verify_writes != *([[:space:]]) ]]; then
            [[ $verify_writes == @(y|t)* ]] && vOpt=" -v y" || vOpt=" -v n"
        fi

        if [[ $position != *([[:space:]]) ]]; then
            case $position in
                center)       aOpt=" -a c"  ;;
                inner_middle) aOpt=" -a im" ;;
                inner_edge)   aOpt=" -a ie" ;;
                outer_edge)   aOpt=" -a e"  ;;
                outer_middle) aOpt=" -a m"  ;;
            esac
        fi

        if [[ $pv_range != *([[:space:]]) ]]; then
            case $pv_range in
                min*) eOpt=" -e m"  ;;
                max*) eOpt=" -e x"  ;;
            esac
        fi

        if [[ $write_consistency != *([[:space:]]) ]]; then
            case $write_consistency in
                o*) wOpt=" -w n"  ;;
                p*) wOpt=" -w p"  ;;
                a*) wOpt=" -w a"  ;;
            esac
        fi

        if [[ $lparts_on_sep_pvs != *([[:space:]]) ]]; then
            case $lparts_on_sep_pvs in
                y*) sOpt=" -s y"  ;;
                n*) sOpt=" -s n"  ;;
                s*) sOpt=" -s s"  ;;
            esac
        fi

        if [[ $sched_policy != *([[:space:]]) ]]; then
            case $sched_policy in
                parallel)             dOpt=" -d p"  ;;
                sequential)           dOpt=" -d s"  ;;
                parallel_sequential)  dOpt=" -d ps" ;;
                parallel_round_robin) dOpt=" -d pr" ;;
            esac
        fi

        if [[ $mpool_copy1 != *([[:space:]]) ]]; then
            pOpt=" -p copy1=$mpool_copy1"
        fi
        if [[ $mpool_copy2 != *([[:space:]]) ]]; then
            pOpt="$pOpt -p copy2=$mpool_copy2"
        fi
        if [[ $mpool_copy3 != *([[:space:]]) ]]; then
            pOpt="$pOpt -p copy3=$mpool_copy3"
        fi

        [[ -n $refnode ]] && ROpt=" -R $refnode"

        typeset isVG_encrypted=""
        #
        : Get volume group properties before creating the logical volume. 
        # 
        typeset -A vgProps
        CL=$LINENO KLIB_HACMP_get_volume_group_attributes $volume_group vgProps
        isVG_encrypted=${vgProps[ENCRYPTION]} 
        if [[ -z $logical_volume ]];then
            lvs=${vgProps[LOGICAL_VOLUMES]}
        fi

        print -- "$0()[$LINENO]($SECONDS): _CSPOC_MODE=both $HASBIN/cl_mklv -cspoc \"-n $nodes\"$ROpt$aOpt$bOpt$cOpt$dOpt$eOpt$GOpt$LOpt$mOpt$pOpt$POpt$rOpt$sOpt$SOpt$tOpt$TOpt$uOpt$vOpt$wOpt$xOpt$yOpt $volume_group $logical_partitions ${physical_volumes//,/ }" >>$CLMGR_TMPLOG  # Always log commands
        _CSPOC_MODE=both cl_mklv -cspoc "-n $nodes"$ROpt$aOpt$bOpt$cOpt$dOpt$eOpt$GOpt$LOpt$mOpt$pOpt$POpt$rOpt$sOpt$SOpt$tOpt$TOpt$uOpt$vOpt$wOpt$xOpt$yOpt $volume_group $logical_partitions ${physical_volumes//,/ }
        rc=$?
        print "cl_mklv RC: $rc" >>$CLMGR_TMPLOG  # Always log command result

        if (( $rc != RC_SUCCESS )); then
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 201 "\nERROR: failed to create \"%1\$s\".\n\n" "$logical_volume" 1>&2
            rc=$RC_ERROR

        #===========================================================
        : If output from this operation was requested, retrieve it
        #===========================================================
        else
            if (( CLMGR_VERBOSE )) || [[ -n $CLMGR_ATTRS || -z $logical_volume ]]; then
               CL=$LINENO KLIB_HACMP_get_volume_group_attributes "$volume_group" properties
               if [[ -z $logical_volume ]];then
                  #
                  : Fetch logical volumes list which should have newly created logical volume to find out the name of LV name if name is not passed.
                  # 
                  lvs_after=${properties[LOGICAL_VOLUMES]}
                  logical_volume=${lvs_after//$lvs/}
                  logical_volume=${logical_volume//,/}
               fi
            fi
            if [[ $enable_encryption == @(y|t)* && -n $logical_volume ]]
            then
                 print "$0()[$LINENO]($SECONDS): cl_enable_encryption -a authadd -l $logical_volume -v $volume_group $typeOpt $mdOpt $anOpt $kOpt" >>$CLMGR_TMPLOG  # Always log commands
                 cl_enable_encryption -a authadd -l $logical_volume -v $volume_group $typeOpt $mdOpt $anOpt $kOpt
                 rc1=$?
                 print "cl_enable_encryption RC: $rc1" >>$CLMGR_TMPLOG  # Always log command result
                 if (( $rc1 != RC_SUCCESS )) && [[ $isVG_encrypted == "yes" ]]; then
                    dspmsg -s $CLMGR_SET $CLMGR_MSGS 1477 "\nWARNING: Failed to add authentication for \"%1\$s\".\nYou can run \"%2\$s %1\$s [..]\" or\n\
\"use Change a Logical Volume from %3\$s menu\" to provide the authentication.\n\
If the problem persists, please contact IBM support.\n" "$logical_volume" "clmgr modify lv" "smitty cl_lv" 1>&2
                 fi
            elif [[ $enable_encryption != @(y|t)* && -n $logical_volume ]] && [[ $isVG_encrypted == "yes" ]];then
                 dspmsg -s 32 cspoc.cat 6 "\nWARNING: Encryption for volume group \"%1\$s\" is enabled, but the logical volume \"%2\$s\" is not encrypted.\n\
To enable the encryption for logical volume,\n\
You can run \"%3\$s %2\$s [...] or\n\
use Change a Logical Volume from %4\$s menu\".\n\n" "$volume_group" "$logical_volume" "clmgr modify lv" "smitty cl_lv" 1>&2
            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_logical_volume()"
