#!/bin/ksh93
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# 61haes_r714 src/43haes/usr/sbin/cluster/sa/nfs/sbin/clca_nfsutil.sh 1.9 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2007,2014 
# 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 
# @(#)65  1.9  src/43haes/usr/sbin/cluster/sa/nfs/sbin/clca_nfsutil.sh, hacmp.assist, 61haes_r714, 1449A_hacmp714 11/21/14 14:03:21

# Load the common clmgr/clvt functions library
. /usr/es/lib/ksh93/func_include

##
## Name: clca_nfsutil
##
## Purpose: A generic utility script used by the NFS configuration assistant
##          and various NFS related SMIT stanzas.
##
## Commands:
##
## addrg: 
##
## Used by the configuration assistant to add a new RG with NFS exports.
##
## Usage:
##            clca_nfsutil addrg -R <rg_name> -P <primary_node> 
##                               -T <takeover_nodes> -I <ip_label> 
##                               -V <vg_list> -n <NFS v2/v3 exp> 
##                               -N <NFSv4 exp> -C <client_mnt> 
##                               -S <stable_storage_path>
##                               -p <prefix length for IPv6 service IP>
##
## Return:
##           0 - Success.
##    non-zero - Failure
##
##
## modrg:
##
## Used by the configuration assistant to view/modify an existing RG
## with NFS exports.
##
## Usage:
##            clca_nfsutil modrg -R <rg_name> -I <ip_label>
##                               -V <vg_list> -n <NFS v2/v3 exp>
##                               -N <NFSv4 exp> -C <client_mnt>
##                               -S <stable_storage_path>
##                               -p <prefix length for IPv6 service IP>
##
## Return:
##           0 - Success.
##    non-zero - Failure
##
##
## delrg:
##
## Used by the configuration assistant to delete a RG with NFS exports.
##
## Usage:
##            clca_nfsutil delrg <rg_name>
##
## Arguments:
##            Resource Group Name.
##
## Return:
##           0 - Success.
##    non-zero - Failure
##
##
## SMIT stanza helper commands.
##
## add_discover 
##
## Used to discover SMIT screen entries for the configuration assistant's
## add screen.
##
## Usage: clca_nfsutil add_discover 
##
##
## modify_discover 
##
## Used to discover SMIT screen entries for the configuration assistant's
## view/change screen.        
## Usage: clca_nfsutil modify_discover 
##
##
## nfs_rgs 
## List all the  Resource Groups in that cluster that has any NFS exports.
##
## Usage: clca_nfsutil nfs_rgs 
##

function clca_nfs_usage {
    dspmsg -s 22 scripts.cat 13 "\nUsage:\n\tclca_nfsutil addrg -R <rg_name> -P <primary_node> -T <takeover_nodes> -I <ip_label> -V <vg_list> -n <NFS v2/v3 exp> -N <NFSv4 exp> -C <client_mnt> -S <stable_storage_path> -p <prefix length for IPv6 service IP>\n\tclca_nfsutil modrg -R <rg_name> -I <ip_label> -V <vg_list> -n <NFS v2/v3 exp> -N <NFSv4 exp>-C <client_mnt> -S <stable_storage_path> -p <prefix length for IPv6 service IP> \n\tclca_nfsutil delrg  <rg_name>\n\tclca_nfsutil add_discover\n\tclca_nfsutil modify_discover\n\tclca_nfsutil nfs_rgs\n\tclca_nfsutil list_ips\n"
}

# Function:
#   checkClusterNodes
#
# Purpose:
#   Is the node valid (exists as part of the HACMP cluster)
#
# Arguments:
#   (*) - list of nodes
#
# Returns:
#   0 on success
#   1 on failure
#
function checkClusterNodes {
    typeset nodelist="$@"
    typeset confNodes=$($CLVT query node)
    typeset missingNodes=""
    typeset -i rc=0

    for a in $nodelist; do
        found=0
        for b in $confNodes; do
            if [[ "$a" == "$b" ]]; then
                found=1
            fi
        done

        if (( $found != 1 )); then
            missingNodes="$a $missingNodes"
            rc=1
        fi
    done

    [[ $rc != "0" ]] && dspmsg -s 22 scripts.cat 8 "$PROGNAME: Following  nodes are specified, but they are not part of the cluster. \n\t $missingNodes \nPlease check the cluster configuration.\n" $PROGNAME $missingNodes
    return $rc
}

# Function:
#   clca_list_ips
#
# Purpose:
#   To get the list of free service IPs or possible service IPs.
#
# Returns:
#   IP Labels.
#
function clca_list_ips {

    ExistingServiceIPs=$(KLIB_HACMP_get_unused_service_labels)
    typeset host tmp ip
    typeset -A labelXip
    for label in $ExistingServiceIPs; do
        /usr/bin/host "$label" | read host tmp ip
        ip=${ip//[\(\)]/}
        label=${label/\.*/}
        labelXip[$label]="$ip"
    done

    #
    # Print out discovered labels, if they're not already part of the
    # defined HACMP service labels
    #
    /usr/es/sbin/cluster/utilities/cl_harvestIP_scripts -a19 | grep -v none | sort -u | while read label ip; do
        ip=${ip//[\(\)]/}
        label=${label/\.*/}
        labelXip[$label]="$ip"
    done

    for label in $(
        for label in ${!labelXip[*]}; do
            echo $label
        done | sort); do
        ip=${labelXip[$label]}
        printf "    %-35s (%s)\n" $label $ip
    done

    return 0
}

#
# Function:
#   createStableStorage
#
# Purpose:
#   Creates a filesystem for the stable storage
#
# Arguments:
#   List of volume groups associated with the resource group.
#
# Output:
#   Filesystem mount point that is just created.
#
# Return:
#   Success - 0 
#   Failure - non-zero exit
#
function createStableStorage {
    typeset PS4_FUNC=$0
    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x
    typeset RG=$1
    typeset SVGS=$2
    typeset ret=1
    MOUNT_HEAD="/.SS"

    # Go through the available VGs and create SS on the first possible one.
    for vg in $SVGS; do
        typeset MOUNT_POINT="$MOUNT_HEAD/$vg"
        # Bug in cl_crlvfs causing the first FS creation to fail partially.
        # Work around is to create a dummy, work-around FS, then our SS-FS, 
        # after that delete the work-around FS.
        typeset WORK_AROUND_MOUNT_POINT="$MOUNT_HEAD/.work_around/$vg"

        # Check if there is a FS at this mnt point.. if so let us use it.
        vg_fs=$(/usr/sbin/lsvgfs "$vg")
        for fs in $vg_fs; do
            [[ "$fs" == "$MOUNT_POINT" ]] && {
                SSLOCATION="$MOUNT_POINT"
                return 0
            }
        done

        # Save and clear any debug/verbose logging while calling cl_crlvfs.
        TMP_VERBOSE_LOGGING=$VERBOSE_LOGGING
        TMP_DEBUG=$_DEBUG

        unset VERBOSE_LOGGING
        unset _DEBUG

        # crfs expects size in the number of 512 blocks.
        # The intent here is to go for 512 MB.   512 * 1048576 =  512MB.
        export _CSPOC_CALLED_FROM_SMIT=true
        $CL_CRLVFS -cspoc "-g $RG" -v "jfs2" -g "$vg" -m "$WORK_AROUND_MOUNT_POINT" -p "rw" -a "size=1048576"
        ret="$?"
        unset _CSPOC_CALLED_FROM_SMIT

        if (( $ret == 0 )); then 
            export _CSPOC_CALLED_FROM_SMIT=true
            $CL_CRLVFS -cspoc "-g $RG" -v "jfs2" -g "$vg" -m "$MOUNT_POINT" -p "rw" -a "size=1048576"
            # Save off the return value.
            ret="$?"
            unset _CSPOC_CALLED_FROM_SMIT

            # Now delete the work-around filesystem
            # It might have mounted, try to unmount it.
            # We don't want to fail the complete operation 
            # even if we are not successful in getting rid of
            # this workaround FS, hence we won't check return values.
            /usr/sbin/umount "$WORK_AROUND_MOUNT_POINT"
            export _CSPOC_CALLED_FROM_SMIT=true
            $SMITLVM -9 "$RG" "$WORK_AROUND_MOUNT_POINT" -r
            unset _CSPOC_CALLED_FROM_SMIT
        fi

        # Restore debug/verbose logging.
        export VERBOSE_LOGGING=$TMP_VERBOSE_LOGGING
        export _DEBUG=$TMP_DEBUG

        if (( $ret == 0 )); then 
            break;
        fi
    done

    # If ret is non-zero - we exhausted volume groups in trying to create
    # a new filesystem for stable storage. 
    if [[ "$ret" != "0" ]]; then
        dspmsg -s 22 scripts.cat 2 "$PROGNAME: An attempt to create the Stable Storage Filesystem through cl_crlvfs failed. Please check cspoc.log for more information on the failure. If this failure is persistent, an explicit Stable Storage can be specified instead of opting for AUTO_SELECT.\n" $PROGNAME
        return 1
    fi

    # Output the newly created Filesystem ; and return 0
    SSLOCATION="$MOUNT_POINT"
    return 0  
}
    
#
# Function:
#   getServiceNetwork
#
# Purpose:
#   Determine an appropriate network to place the service IP label
#
# Arguments:
#   (1) by reference - list of nodes to find interfaces for
#
# Output:
#   network name to place service IP label on
#
# Returns:
#   Success: Return 0 
#   Failure : Return non-zero value
#
function getServiceNetwork {
    typeset PS4_FUNC=$0
    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x
    typeset NODES_ARG="$@"
    typeset NETWORKS=$($CLVT query network | sort -u)
    typeset validNetworks # List of valid networks in the cluster
    typeset -A netXnodeCount # Array to keep the count

    # Gather the list of valid networks.
    for network in $NETWORKS; do
        alias=$($CLVT query network $network |
        awk -F= '$1 ~ /ALIAS/ && $1 !~ /ALIAS_/ { print $2 }' | 
        sed -e "s/\"//g")
        [[ "$alias" == "aliased" ]] && {
            # Add to the list
            validNetworks="$network $validNetworks"
        }
    done

    # Build a network_node array
    INTERFACES=$($CLVT query interface | sort -u)
    for interface in $INTERFACES; do
        node=$($CLVT query interface $interface |
            awk -F= '$1 ~ /NODE/ {print $2 }' | sed -e "s/\"//g")
        net=$($CLVT query interface $interface |
            awk -F= '$1 ~ /NETWORK/ {print $2 }' | sed -e "s/\"//g")
        [[ -n "$node" && -n "$net" ]] && {
            # Make the array.
            typeset -i count=${netXnodeCount[${net}_${node}]}
            (( count++ ))
            netXnodeCount[${net}_${node}]=$count
        }
    done

    typeset -i invalid=0
    # Walk through the network_node array and findout the first network
    # for the nodes passed in.
    for network in $validNetworks; do
        invalid=0
        for node in $NODES_ARG; do
            (( ${netXnodeCount[${network}_${node}]} == 0 )) && {
                invalid=1
            }
        done
        (( $invalid == 0 )) && {
            echo $network
            return 0
        }
    done

    return 1
}

# Function: get_volume_groups
#
# Purpose:  look up a list of all sharable volume groups
#
# Args:     RG name
#
# Output:   list of all VGs that could be shared by this group
#
# Return:   
#   Success: Return 0 
#   Failure: Return Non-Zero
#
function get_volume_groups {
    typeset PS4_FUNC=$0
    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x

    typeset GROUP="$1"
    typeset VGS
    typeset ret

    # clharvest_vg prints out additional debugging messages if _DEBUG is set.
    # clfind_shareable_vg does not parse these _DEBUG messages, so clear _DEBUG.
    typeset _DEBUG=

    # First harvest and populate the config dir.
    /usr/es/sbin/cluster/utilities/clharvest_vg -w >/dev/null 2>&1 || return 2

    VGS=$(/usr/es/sbin/cluster/utilities/clfind_shareable_vg "$GROUP" -s)
    ret=$?

    # Exit if we can't find any VGs.
    if [[ "$ret" != "0" || -z "$VGS" ]]; then 
        return 1
    fi
    echo $VGS
    return 0
}
# Function:
#   get_filesystems_for_export
#
# Purpose:
#   Return a list of filesystems on the specified VGs. Used for NFS exports.
#
# Args:     List of VGs
#
# Output:   list of non GEO Filesystems on those VGs
#

function get_filesystems_for_export {
    typeset PS4_FUNC=$0
    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x
    typeset SS_PATH="$1"
    typeset VOLUME_GROUPS=$2
    typeset FILE_SYSTEMS=""
    typeset TMP_LVs
    typeset ALL_LVs
    typeset LV
    typeset FS
    typeset gmd
    typeset PdDvLn

    for VG in $VOLUME_GROUPS; do
        TMP_LVs=$(odmget -q "name = $VG" CuDep | \
                grep "dependency ="| awk -F= '{print $2}'|sed -e "s/\"//g")
        ALL_LVs="$ALL_LVs $TMP_LVs"
    done

    if (lslpp -l 'hageo.*' >/dev/null 2>&1 ||
        lslpp -l 'geoRM.*' >/dev/null 2>&1) ; then
        # HAGEO is installed, take out HAGEO LVs.
        TMP_LVs=""
        for LV in $ALL_LVs; do
            PdDvLn=""
            gmd=$(odmget -q "attribute = local_device and value = /dev/r$LV" CuAt | grep 'name =' | cut -f3 -d' ' | tr -d '"')
            if [[ -n "$gmd" ]]; then
                PdDvLn=$(odmget -q "name = $gmd" CuDv |grep 'PdDvLn =' |cut -f3 -d' ' |tr -d '"')
            fi
            if [[ "$PdDvLn" != "geo_mirror/gmd/lgmd" ]]; then   
                TMP_LVs="$TMP_LVs $LV"
            fi
        done
        ALL_LVs="$TMP_LVs"
    fi

    # Now get the filesystems on each LV

    for LV in $ALL_LVs; do
        FS=$(lslv -L ${LV} 2>/dev/null | awk '/^MOUNT POINT:/ {print $3}')
        if [[ $FS != "N/A" && -n $FS ]]; then
            case $SS_PATH in
                $FS)   ;;    # Exact match skip it.
                $FS/*) ;;  #Stable storage is in this filesystem. Skip it.
                *)     FILE_SYSTEMS="$FS $FILE_SYSTEMS";;
            esac
        fi
    done

    echo $FILE_SYSTEMS
}


# Function:
#   clca_addrg
#
# Purpose:
#   Add a resource group with NFS exports. 
#   This is used by the NFS configuration assistant.
#
# Arguments:
#            R   = '<Resource Group Name>"
#            P   = '<Primary Node Name>'
#            T   = '<Space seperated list of takeover nodes>'
#            I   = '<Service ip label>'
#            V   = '<Space seperated list of volume groups>'
#            n   = '<Space seperated list of NFSv2/NFSv3 exports>'
#            N   = '<Space seperated list of NFSv4 exports>'
#            C   = '<Space seperated list of nfs client mounts 
#                    and/or cross mounts>'
#            S   = '<Stable Storage Path, vailid for NFSv4 only>'
#
# Returns:
#   Success: Return 0 
#   Failure : exit non-zero value
#
function clca_addrg {

    typeset PS4_FUNC=$0
    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x

    # Get options from the command line.
    while getopts R:P:T:I:p:V:n:N:C:S: option
    do
        case $option in
        R ) RG_NAME=$OPTARG;;
        P ) PRIMARY=$OPTARG;;
        T ) TAKEOVER=$OPTARG;;
        I ) IP_LABEL=$OPTARG;;
        p ) PREFIX=$OPTARG;;
        V ) VGS=$OPTARG;;
        n ) NFSV2V3=$OPTARG;;
        N ) NFSV4=$OPTARG;;
        C ) CLNTMNT=$OPTARG;;
        S ) SSLOCATION=$OPTARG;;
        esac
    done

    # Basic Checks.
    # Check if the mandatory agruments are passed-in.
    if [[ -z $RG_NAME || -z $PRIMARY || -z $TAKEOVER || -z $IP_LABEL || -z $VGS ]]; then
        dspmsg -s 22 scripts.cat 4 "$PROGNAME: Essential arguments are missing.\n" $PROGNAME
        clca_nfs_usage
        exit 1
    fi

    # Take care of value 'NONE'
    typeset -u TMP_NFSV4="$NFSV4"
    [[ "$TMP_NFSV4" == "NONE" ]] && NFSV4=""
    typeset -u TMP_NFSV2V3="$NFSV2V3"
    [[ "$TMP_NFSV2V3" == "NONE" ]] && NFSV2V3=""
    typeset -u TMP_CLNTMNT="$CLNTMNT"
    [[ "$TMP_CLNTMNT" == "NONE" ]] && CLNTMNT=""

    # IF NFSv4 exports are specified but no stable storage location; fail.
    if [[ "$NFSV4" && -z "$SSLOCATION" ]]; then
        dspmsg -s 22 scripts.cat 5 "$PROGNAME: Stable Storage is required for the NFSv4 exports.\n" $PROGNAME
        exit 2
    fi

    # If stable storage location is specified but not the NFSv4 exports; ignore.
    if [[ -n "$SSLOCATION" && -z "$NFSV4" ]]; then
        # Ignore Stable Stoage and make it NULL
        SSLOCATION=""
    fi

    # If no NFS exports; We don't have any work to do - Exit.
    if [[ -z "$NFSV4" && -z "$NFSV2V3" ]]; then
        dspmsg -s 22 scripts.cat 6 "$PROGNAME: This configuration assistant is designed for adding NFS exports to the cluster. Exiting because, the user did not specify any NFS exports. \n" $PROGNAME
        exit 3
    fi

    #
    # Check if the PRIMARY is present in SECONDARY, and if so take it out of
    # that list
    TMP_TAKEOVER=$TAKEOVER
    TAKEOVER=""

    for node in $TMP_TAKEOVER
    do
        [[ "$node" != "$PRIMARY" ]] && TAKEOVER="$TAKEOVER $node"
    done

    # Take out the leading space
    TAKEOVER=${TAKEOVER:1}

    # After cleaning, check if any nodes are left in TAKEOVER list.
    if [[ -z $TAKEOVER ]]; then
        dspmsg -s 22 scripts.cat 7 "$PROGNAME: Takeover list should have at least one node that is not the primary node.\n" $PROGNAME 
        exit 4
    fi

    # Determine if the cluster nodes entered are valid cluster nodes
    checkClusterNodes $PRIMARY $TAKEOVER
    if (( $? != 0 )); then
        exit 5
    fi

    # Add a Resource Group to the cluster
    $CLVT add resource_group "$RG_NAME" \
        PRIMARYNODES="$PRIMARY $TAKEOVER" \
        STARTUP="OHN" \
        FALLBACK="NFB" \
        FALLOVER="FNPN" || {
            # ERROR: Failed to create resource group: $RG_NAME
            dspmsg -s 22 scripts.cat 9 "$PROGNAME: clvt add resource_group failed.\n" $PROGNAME
            exit 6
        }
    
    # Process IP LABEL.
    serviceIP=$($CLVT query service_ip $IP_LABEL 2>/dev/null)
    [[ -z "$serviceIP" ]] && {
        # Create the service IP label, first find a network
        typeset nodes="$PRIMARY $TAKEOVER"
        network=$(getServiceNetwork "$nodes") 
        [[ -z $network ]] && {
            dspmsg -s 22 scripts.cat 10 "$PROGNAME: Specified IP label is not a service IP label. Attempted to make it service IP label but, could not find an appropriate network to place it. Please check the network configuration.\n" $PROGNAME
            #
            # We have failed. Delete the RG just created.
            #
            $CLVT delete resource_group $RG_NAME

            exit 7
        }
	PREFIX_ARG=""
	[[ -n $PREFIX ]] && PREFIX_ARG="PREFIX=$PREFIX"
        $CLVT add service_ip $IP_LABEL NETWORK="$network" $PREFIX_ARG || {
            # ERROR: Unable to create service IP label: %s\n
            dspmsg -s 22 scripts.cat 11 "$PROGNAME: Specified IP label is not a service IP label. Attempted to make it service IP label but, clvt add service_ip failed.\n" $PROGNAME
            #
            # We have failed. Delete the RG just created.
            #
            $CLVT delete resource_group $RG_NAME

            exit 8
        }
    }
    
    # Process Volume Groups
    # If the user asked us to find "ALL" volume groups; get the list.
    # Otherwise, user should have given a list of VGs.
    # We will leave the validity checking job to the clvt.
    typeset -u VGS_UC="$VGS"
    if [[ $VGS_UC == 'ALL' ]]; then
        VGS=$( get_volume_groups "$RG_NAME" )  
        if (( $? != 0 )); then 
            dspmsg -s 22 scripts.cat 3 "$PROGNAME: Could not detect any sharable volumes for the resource group : $RG_NAME\n" "$PROGNAME" "$RG_NAME"
            #
            # We have failed. Delete the RG just created.
            #
            $CLVT delete resource_group $RG_NAME
            exit 9
        fi
    fi

    # Make sure that rootvg is not part of the list.
    typeset TMP_VGS="$VGS"
    VGS=""
    for vg in $TMP_VGS; do
        [[ "$vg" != "rootvg" ]] && VGS="$VGS $vg"
    done

    # Takeout the leading space
    VGS=${VGS:1}

    # Add the list of VGs to the RG now.
    # We need to do this ahead of other resources, 
    # as some of the VG related CSPOC
    # utilities depend on the fact that the VG is part of RG.
    $CLVT modify resource_group "$RG_NAME" \
        VOLUME_GROUP="$VGS" \
        VG_AUTO_IMPORT="false" \
        FORCED_VARYON="false" || {
        typeset clvt_mod_ret=$?
        # Failed to modify the resource group $RG_NAME.
        dspmsg -s 22 scripts.cat 12 "$PROGNAME: clvt modify resource_group $RG_NAME failed.\n" $PROGNAME $RG_NAME
        #
        # We have failed. Delete the RG just created.
        #
        $CLVT delete resource_group $RG_NAME

        exit 10
    }

    # Process Stable Storage
    # If user asked us to create a Stable Storage through "AUTO_SELECT",
    # it is our job; or else user is expected to provide a stable storage path.
    # Validity/Sanity checks will be perofrmed in the later part of the 
    # processing.
    typeset -u TMP_SSLOCATION=$SSLOCATION
    if [[ $TMP_SSLOCATION == 'AUTO_SELECT' ]]; then
        # The function createStableStorage creates and sets SSLOCATION variable.
        createStableStorage "$RG_NAME" "$VGS" 
        if (( $? != 0 )); then 
            #
            # We have failed. Delete the RG just created.
            #
            $CLVT delete resource_group $RG_NAME
            exit 11
        fi
    fi

    # Process export filesystems
    if [[ $TMP_NFSV2V3 == 'ALL' ]]; then
        NFSV2V3=$(get_filesystems_for_export "$SSLOCATION" "$VGS" )
    fi

    if [[ $TMP_NFSV4 == 'ALL' ]]; then
        NFSV4=$(get_filesystems_for_export "$SSLOCATION" "$VGS" )
    fi

    # Modify the resource group with additional attributes.
    $CLVT modify resource_group "$RG_NAME" \
      SERVICE_LABEL="$IP_LABEL" \
      VOLUME_GROUP="$VGS" \
      VG_AUTO_IMPORT="false" \
      FORCED_VARYON="false" \
      FSCHECK_TOOL="fsck" \
      FS_BEFORE_IPADDR="true" \
      FILESYSTEM="" \
      EXPORT_FILESYSTEM="$NFSV2V3" \
      EXPORT_FILESYSTEM_V4="$NFSV4" \
      MOUNT_FILESYSTEM="$CLNTMNT" \
      STABLE_STORAGE_PATH="$SSLOCATION" || {
        typeset clvt_mod_ret=$?
        # Failed to modify the resource group $RG_NAME.
        dspmsg -s 22 scripts.cat 12 "$PROGNAME: clvt modify resource_group $RG_NAME failed.\n" $PROGNAME $RG_NAME
        #
        # We have failed. Delete the RG just created.
        #
        $CLVT delete resource_group $RG_NAME

        exit 12
    }

    # Run verification.
    # clsapost -v equivallent. 
    /usr/es/sbin/cluster/diag/clver -rt -C yes
    exit $?

    # Everything is good; let us go with dare.
    #/usr/es/sbin/cluster/utilities/cldare -rt  -V 'normal' 
}

# Function:
#   clca_modrg
#
# Purpose:
#   Modify a resource group with NFS exports.
#   This is used by the NFS configuration assistant.
#
# Arguments:
#            R   = '<Resource Group Name>"
#            I   = '<Service ip label>'
#            V   = '<Space seperated list of volume groups>'
#            n   = '<Space seperated list of NFSv2/NFSv3 exports>'
#            N   = '<Space seperated list of NFSv4 exports>'
#            C   = '<Space seperated list of nfs client mounts
#                    and/or cross mounts>'
#            S   = '<Stable Storage Path, vailid for NFSv4 only>'
#
# Returns:
#   Success: Return 0
#   Failure : exit non-zero value
#

function clca_modrg {
    typeset PS4_FUNC=$0
    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x

    # Get options from the command line.
    while getopts R:P:T:I:p:V:n:N:C:S: option
    do
        case $option in
        R ) RG_NAME=$OPTARG;;
        P ) PRIMARY=$OPTARG;;
        T ) TAKEOVER=$OPTARG;;
        I ) IP_LABEL=$OPTARG;;
        p ) PREFIX=$OPTARG;;
        V ) VGS=$OPTARG;;
        n ) NFSV2V3=$OPTARG;;
        N ) NFSV4=$OPTARG;;
        C ) CLNTMNT=$OPTARG;;
        S ) SSLOCATION=$OPTARG;;
        esac
    done

    # Basic Checks.
    # Check if the mandatory agruments are passed-in.
    if [[ -z $RG_NAME || -z $IP_LABEL || -z $VGS ]]; then
        dspmsg -s 22 scripts.cat 4 "$PROGNAME: Essential arguments are missing.\n" $PROGNAME
        clca_nfs_usage
        exit 20
    fi

    typeset -u TMP_NFSV4="$NFSV4"
    [[ "$TMP_NFSV4" == "NONE" ]] && NFSV4=""
    typeset -u TMP_NFSV2V3="$NFSV2V3"
    [[ "$TMP_NFSV2V3" == "NONE" ]] && NFSV2V3=""
    typeset -u TMP_CLNTMNT="$CLNTMNT"
    [[ "$TMP_CLNTMNT" == "NONE" ]] && CLNTMNT=""
    typeset -u TMP_SS_LOCATION="$SSLOCATION"
    [[ "$TMP_SS_LOCATION" == "NONE" ]] && SSLOCATION=""

    # Process Volume Groups
    # If the user asked us to find "ALL" volume groups; get the list.
    # Otherwise, user should have given a list of VGs.
    # We will leave the validity checking job to the clvt.
    typeset -u TMP_VGS=$VGS
    if [[ $TMP_VGS == 'ALL' ]]; then
        VGS=$( get_volume_groups "$RG_NAME" )
        if (( $? != 0 )); then 
            dspmsg -s 22 scripts.cat 3 "$PROGNAME: Could not detect any sharable volumes for the resource group : $RG_NAME\n" "$PROGNAME" "$RG_NAME"
            exit 21
        fi
    fi
    
    # Make sure that rootvg is not part of the list.
    unset TMP_VGS
    typeset TMP_VGS=$VGS
    VGS=""
    for vg in $TMP_VGS; do
        [[ "$vg" != "rootvg" ]] && VGS="$VGS $vg"
    done

    # Takeout the leading space
    VGS=${VGS:1}


    # Process export filesystems
    if [[ $TMP_NFSV2V3 == 'ALL' ]]; then
        NFSV2V3=$(get_filesystems_for_export "$SSLOCATION" "$VGS" )
    fi

    if [[ $TMP_NFSV4 == 'ALL' ]]; then
        NFSV4=$(get_filesystems_for_export "$SSLOCATION" "$VGS" )
    fi

    eval $($CLVT query resource_group $RG_NAME | egrep $(echo $RESOURCES|tr ' ' '|'))

    EXPORT_FILESYSTEM_V4="$NFSV4"
    EXPORT_FILESYSTEM="$NFSV2V3"
    MOUNT_FILESYSTEM="$CLNTMNT"
    VOLUME_GROUP="$VGS"

    # Prepare arguments for clvt
    arg_resources=""
    for res in $RESOURCES; do
        eval value=\$$res
        [[ -z "$value" ]] && continue
        # Check and set value to NULL indicating all filesystems.
        [[ "$res" == "FILESYSTEM" && "$value" == "ALL" ]] && value=""
        tmp_string="$res=$value"
        arg_resources="$arg_resources $tmp_string"
    done

    # Modify the resource group.
    $CLVT modify resource_group "$RG_NAME" $arg_resources || {
        # ERROR: Failed to modify the resource group $RG_NAME.
        dspmsg -s 22 scripts.cat 12 "$PROGNAME: clvt modify resource_group $RG_NAME failed.\n" $PROGNAME $RG_NAME
        exit 22
    }
    
    # 
    # Run verification.
    /usr/es/sbin/cluster/diag/clver -rt -C yes
    exit $?
}

function clca_delrg {
    typeset PS4_FUNC=$0
    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x
    RG=$1
    $CLVT delete resource_group "$RG"
    exit $?
}

#
# Function: 
#   clca_add_discover
#
# Input: 
#   None
# 
# Purpose:
#   Returns output suitable for NFS Configuration Assistant's Add screen.
#
# Output:
#   #primary
#   <Node-Name>
#
function clca_add_discover {

    primary=$(/usr/es/sbin/cluster/utilities/get_local_nodename)
    echo '#primary'
    echo "$primary:"
}

#
# Function: 
#   clca_mod_discover
#
# Input: 
#   None
# 
# Purpose:
#   Returns output suitable for NFS Configuration Assistant's Change/Show
#   screen.
#
# Output
#   #rgname:primary:takeover:ip:vgs:v2_v3_exports:v4_exports:cmount:ss
#   < Corresponding values with : delimiter >
#
function clca_mod_discover {
    # Define local variables using typeset
    typeset primary takeover
    typeset RG SERVICE_LABEL VOLUME_GROUP
    typeset EXPORT_FILESYSTEM MOUNT_FILESYSTEM 
    typeset MOUNT_FILESYSTEM STABLE_STORAGE_PATH

    # Automatically mark variables exported to the environment
    set -a

    RG=$1
    eval $($CLVT query resource_group $RG | egrep $(echo $RESOURCES|tr ' ' '|'))
    
    set +a

    echo $NODES | read primary takeover

    [[ -z $EXPORT_FILESYSTEM ]] && EXPORT_FILESYSTEM="NONE"
    [[ -z $EXPORT_FILESYSTEM_V4 ]] && EXPORT_FILESYSTEM_V4="NONE"
    [[ -z $MOUNT_FILESYSTEM ]] && MOUNT_FILESYSTEM="NONE"
    [[ -z $STABLE_STORAGE_PATH ]] && STABLE_STORAGE_PATH="NONE"
    # Output in the format needed for the SMIT screen
    echo "#rgname:primary:takeover:ip:vgs:v2_v3_exports:v4_exports:cmount:ss"
    echo "$RG:$primary:$takeover:$SERVICE_LABEL:$VOLUME_GROUP:$EXPORT_FILESYSTEM:$EXPORT_FILESYSTEM_V4:$MOUNT_FILESYSTEM:$STABLE_STORAGE_PATH"
}

#
# Function: 
#   clca_list_nfs_rgs
#
# Input: 
#   None
# 
# Purpose:
#   Generates a list of resource groups in the system with NFS exports.
#
# Output:
#   List of resource groups with nfs exports.
#
function clca_list_nfs_rgs {
    typeset PS4_FUNC=$0
    [[ "$VERBOSE_LOGGING" == "high" ]] && set -x
    odmget -q "name LIKE 'EXPORT_FILESYSTEM*'" HACMPresource | grep 'group = '| cut -d '"' -f2 | sort -u
}

#---------------------------------------------------------------------------
# Main Program
#---------------------------------------------------------------------------
RESOURCES="DISK VOLUME_GROUP CONCURRENT_VOLUME_GROUP FILESYSTEM FSCHECK_TOOL \
RECOVERY_METHOD EXPORT_FILESYSTEM APPLICATIONS MOUNT_FILESYSTEM SERVICE_LABEL \
TAKEOVER_LABEL NFS_HOST \
AIX_CONNECTIONS_SERVICES COMMUNICATION_LINKS AIX_FAST_CONNECT_SERVICES \
SHARED_TAPE_RESOURCES FS_BEFORE_IPADDR FORCED_VARYON \
PRINCIPAL_ACTION ASSOCIATE_ACTION AUXILLIARY_ACTION VG_RR_ACTION \
SIBLING_NODES FOLLOWER_ACTION PPRC_REP_RESOURCE GMD_REP_RESOURCE \
ERCMF_REP_RESOURCE SVCPPRC_REP_RESOURCE VG_AUTO_IMPORT FS_BEFORE_IPADDR \
OEM_VOLUME_GROUP OEM_FILESYSTEM GMVG_REP_RESOURCE \
EXPORT_FILESYSTEM_V4 STABLE_STORAGE_PATH NODES WPAR_NAME"

FPATH=/usr/es/lib/ksh93/hacmp
CLVT=/usr/es/sbin/cluster/utilities/clvt
CL_CRLVFS=/usr/es/sbin/cluster/sbin/cl_crlvfs
SMITLVM=/usr/es/sbin/cluster/cspoc/smitlvm
PROGNAME=$(basename $0)
COMMAND=$1

export PS4='${GROUPNAME:-}:${PROGNAME:-}[${PS4_FUNC:-}+$LINENO] '

if [[ "$COMMAND" == "add_discover" || "$COMMAND" == "mod_discover" ||
      "$COMMAND" == "nfs_rgs" ]]; then 
    # These are SMIT commands, SMIT gets confused with the out put of set -x
    # So, explicitly disable the logging.
    VERBOSE_LOGGING=""
fi

[[ "$VERBOSE_LOGGING" == "high" ]] && set -x
[[ "$VERBOSE_LOGGING" == "high" ]] && version='1.9'

# Take care of no arguments case.
if (( $# < 1 )) ; then 
    clca_nfs_usage
    exit 1
fi

shift

case $COMMAND in

    addrg ) 
        # 
        # Add a new resource group with NFS exports.
        #
        clca_addrg "$@"
        ;;

    modrg ) 
        # 
        # Show/Change an existing resource group with NFS exports.
        #
        clca_modrg "$@"
        ;;

    delrg ) 
        # 
        # Delete a resource group with NFS exports.
        #
        clca_delrg "$@"
        ;;

    add_discover )
        # 
        # Fill-in the configuration assistant's add SMIT screen.
        #
        clca_add_discover
        ;;

    mod_discover )
        # 
        # Fill-in the configuration assistant's show/change SMIT screen.
        #
        clca_mod_discover "$@"
        ;;

    nfs_rgs )
        #
        # Produce a list of resource groups with NFS exports.
        #
        clca_list_nfs_rgs
        ;;

    list_ips )
        #
        # List IP LABELs.
        #
        clca_list_ips
        ;;
    * ) # Default
        clca_nfs_usage;
        exit 1;
        ;;
esac

exit 0

# End of script.
