#!/bin/ksh93
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# 61haes_r720 src/43haes/lib/ksh93/hacmp/KLIB_HACMP_add_persistent_ip.sh 1.10 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 1990,2010 
# 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 
# @(#)40	1.10  src/43haes/lib/ksh93/hacmp/KLIB_HACMP_add_persistent_ip.sh, hacmp.assist, 61haes_r720, 1511A_hacmp720 2/25/15 14:15:07

#================================================
# 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_persistent_ip

=head1 VERSION

 Version Number:  1.10
 Last Extracted:  3/9/15 17:40:44
 Last Changed:    2/25/15 14:15:07

 Path, Component, Release(, Level):
 src/43haes/lib/ksh93/hacmp/KLIB_HACMP_add_persistent_ip.sh, hacmp.assist, 61haes_r720, 1511A_hacmp720

=head1 SYNOPSIS

 clmgr add persistent_ip <persistent_IP> \
             NETWORK=<network> \
             [ {NETMASK=<255.255.255.0 | PREFIX=1..128} ] \
             [ NODE=<node>   ]

 NOTE: the alias for "persistent_ip" is "pi".

=head1 DESCRIPTION

Adds a persistent IP label/address to the cluster configuration.

=head1 ARGUMENTS

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

 2. ipLabel [REQUIRED] [string]
    The IP address or label of the persistent IP that is to be created.

 3. network [REQUIRED] [string]
    The label of the network that this address will be bound to.

 4. node [OPTIONAL] [string]
    The label of the node that this address will be bound to, defaulting
    to the local node.

 5. netmask [OPTIONAL] [string]
    An alternate netmask to use for the service IP (the netmask of
    the underlying network is used by default).

 6. prefix [OPTIONAL] [string]
    An alternate prefix length to use for the service IP (the prefix
    of the underlying network is used by default).

=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_persistent_ip {
    LINENO=2 . $HALIBROOT/log_entry "$0()" "$CL"
    : version=1.10, src/43haes/lib/ksh93/hacmp/KLIB_HACMP_add_persistent_ip.sh, hacmp.assist, 61haes_r720, 1511A_hacmp720
    : INPUTS: $*

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

    typeset network=${1//\"/}
    typeset node=${2//\"/}
    typeset netmask=${3//\"/}  #optional
    typeset prefix=${4//\"/}   #optional

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

    #===================================
    : Declare and initialize variables
    #===================================
    typeset -i rc=$RC_UNKNOWN
    typeset NET_FAMILY="UNKNOWN"
    typeset ADDR_FAMILY="UNKNOWN"
    typeset ODM_MASK=""

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

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

    else
        CL=$LINENO KLIB_HACMP_is_known_persistent_ip $ipLabel
        if (( $? == RC_SUCCESS )); then
            /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 229 "\nERROR: the specified object already exists: \"%1\$s\"\n\n" "$ipLabel" 1>&2
            rc=$RC_INCORRECT_INPUT
        fi
    fi

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

    else
        CL=$LINENO KLIB_HACMP_is_known_network "$network" >/dev/null 2>&1
        if (( $? != RC_SUCCESS )); then
            rc=$RC_INCORRECT_INPUT
            /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 102 "\nERROR: \"%1\$s\" does not appear to exist!\n\n" "$network" 1>&2

            /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 150 "Available Networks:\n\n" 1>&2
            typeset available
            CL=$LINENO KLIB_HACMP_list_networks 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 $netmask && -n $prefix ]]; then
        /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 36 "\nERROR: conflicting options were provided,\n\       \"%1\$s\" versus \"%2\$s\".\n\n" NETMASK PREFIX 1>&2
        rc=$RC_INCORRECT_INPUT
    fi

    #====================================================================
    : If the specified address and the targeted network are of the same
    : type, no netmask/prefix is needed. If they differ, then a netmask
    : or prefix is *mandatory*. In the latter case, make an effort to
    : infer the intentions of the customer, in the case where a typo
    : has occurred, such as using PREFIX instead of NETMASK.
    #====================================================================
    if [[ -n $ipLabel ]]; then

        #
        : find the family of the underlying network
        #
        print -- "$0()[$LINENO]($SECONDS): $HAUTILS/clodmget -n -q name=$network -f net_family HACMPnetwork" >>$CLMGR_TMPLOG
        if [[ $($HAUTILS/clodmget -n -q name=$network -f net_family HACMPnetwork) == 1 ]]
        then
            NET_FAMILY="AF_INET"
        else
            NET_FAMILY="AF_INET6"
        fi
        ODM_MASK=$(clodmget -n -q name=$network -f netmask HACMPnetwork)
        print -- "$0()[$LINENO]($SECONDS): Network $network is $NET_FAMILY, netmask/prefix is $ODM_MASK" >>$CLMGR_TMPLOG

        #
        : find the family of the specified ip or label
        #
        typeset NAME=\"\" IS=\"\" ADDR=\"\" REM=\"\"
        LC_ALL=C /usr/bin/host $ipLabel | read NAME IS ADDR REM
	ADDR=$(echo "$ADDR" | sed "s/,//g")
        if [[ $ADDR == +([0-9])\.+([0-9])\.+([0-9])\.+([0-9]) ]]
        then
            ADDR_FAMILY="AF_INET"
        else
            ADDR_FAMILY="AF_INET6"
        fi

        #
        # Check (any) user specified netmask or prefix in a foregiving manner:
        # accept either input and just match it to what's required, e.g.
        # if a netmask argument is provided with an ipv6 address, just treat 
        # the input as a prefix length (assuming the netmask argument is
        # a decimal)
        #
        if [[ -n $netmask || -n $prefix ]]; then
            if [[ $ADDR_FAMILY == "AF_INET" && $NET_FAMILY == "AF_INET6" ]]; then
                : The address, $ipLabel, is IPv4
                if [[ $prefix != *([[:space:]]) ]]; then
                    netmask=$prefix
                    prefix=""
                fi

            elif [[ $ADDR_FAMILY == "AF_INET6" && $NET_FAMILY == "AF_INET" ]]; then
                : The address, $ipLabel, is IPv6
                if [[ $netmask != *([[:space:]]) ]]; then
                    prefix=$netmask
                    netmask=""
                fi

            elif [[ ($ADDR_FAMILY == "AF_INET"  && $NET_FAMILY == "AF_INET") || \
                    ($ADDR_FAMILY == "AF_INET6" && $NET_FAMILY == "AF_INET6") ]]
            then
                #====================================================
                : The network and address are of the same type so we 
                : silently ignore any Netmask/Prefix provided, as
                : long as it is the same as whats already in use.
                : But if they do give us a different netmask, let them
                : know that their input is being ignored.
                #====================================================
                if [[ (-n $prefix && $prefix != $ODM_MASK) ||
                      (-n $netmask && $netmask != $ODM_MASK) ]]
                then
                    typeset TYPE="IPv4"
                    [[ $ADDR_FAMILY == "AF_INET6" ]] && TYPE="IPv6"
                    CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 779 'The specified address "%1$s", is of the same family (%2$s) as network "%3$s". The specified netmask/prefix will not be used, and will instead be taken from "%3$s".\n' "$ipLabel" "$TYPE" "$network"
                fi
                netmask=""
                prefix=""
            fi

        elif [[ -z $netmask && -z $prefix ]]; then
            #
            : Caller did not specify a netmask or prefix -  we do not need one
            : as long as the incoming address is the same family as the 
            : underlying network
            #
            if [[ ($ADDR_FAMILY == "AF_INET"  && $NET_FAMILY == "AF_INET6") || \
                  ($ADDR_FAMILY == "AF_INET6" && $NET_FAMILY == "AF_INET") ]]
            then
                typeset TYPE="IPv4"
                [[ $ADDR_FAMILY == "AF_INET6" ]] && TYPE="IPv6"
                CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 780 '\nERROR: the specified address, "%1$s", is of a different type (%2$s) than the specified network, "%3$s". So either a netmask (IPv4) or prefix length (IPv6) must be provided.\n\n' "$ipLabel" "$TYPE" "$network" 1>&2
                rc=$RC_MISSING_INPUT
            fi
        fi
    fi

    #
    : Validate the netmask and or prefix length
    #
    if [[ $prefix != *([[:space:]]) ]] && \
         ( [[ $prefix != +([[:digit:]]) ]] || (( prefix < 1 || prefix > 128 )) )
    then
        /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 113 "\nERROR: an invalid IPv6 prefix length was specified: %1\$s\n\n" "$prefix" 1>&2
        /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "1 .. 128" 1>&2
        rc=$RC_INCORRECT_INPUT
    fi
    if [[ -n $netmask ]]; then
            typeset nodots=${netmask//\./}
            if (( ${#netmask} - ${#nodots} != 3 )); then
                /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 115 "\nERROR: an invalid IPv4 netmask was specified: %1\$s\n\n" "$netmask" 1>&2
                /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "<###.###.###.###>; \"###\" must be in the range 0 .. 255." 1>&2
                rc=$RC_INCORRECT_INPUT
            else
                typeset value=$netmask
                for (( i=0; i<4; i++ )); do
                    typeset octet=${value%%.*}
                    value=${value#*.}
                    if [[ $octet == *([[:space:]]) || \
                          $octet != +([[:digit:]]) ]] || \
                       (( octet < 0 || octet > 255 ))
                    then
                         /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 115 "\nERROR: an invalid IPv4 netmask was specified: %1\$s\n\n" "$netmask" 1>&2
                         /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 "Valid values: %1\$s\n\n" "<###.###.###.###>; \"###\" must be in the range 0 .. 255." 1>&2
                         rc=$RC_INCORRECT_INPUT
                         break
                    fi
                done
            fi
    fi


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

    else
        #
        : If a node was specified, verify that they it is a known node.
        #
        CL=$LINENO KLIB_HACMP_is_known_node "$node" >/dev/null 2>&1
        if (( $? != RC_SUCCESS )); then
            rc=$RC_INCORRECT_INPUT
            /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 102 "\nERROR: \"%1\$s\" does not appear to exist!\n\n" "$node" 1>&2

            /usr/bin/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

    #=================================================================
    : Create the persistent IP if no input errors have been detected
    #=================================================================
    if (( $rc == RC_UNKNOWN )); then
        typeset netarg=""
        if [[ -n $netmask ]]; then
            netarg=$netmask
        elif [[ -n $prefix ]]; then
            netarg=$prefix
        fi

        typeset NETTYPE=$(clodmget -q name=$network -n -f nimname HACMPnetwork)
        print -- "$0()[$LINENO]($SECONDS): $HAUTILS/claddnode -a \"$ipLabel\":\"$NETTYPE\":\"$network\"::\"persistent\":::$netarg -n\"$node\"" >>$CLMGR_TMPLOG  # Always log commands
        $HAUTILS/claddnode -a $ipLabel:$NETTYPE:$network::persistent:::$netarg -n"$node"
        rc=$?
        print "claddnode RC: $rc" >>$CLMGR_TMPLOG  # Always log command result
        if (( $rc != RC_SUCCESS )); then
            rc=$RC_ERROR
            /usr/bin/dspmsg -s $CLMGR_SET $CLMGR_MSGS 201 "\nERROR: failed to create \"%1\$s\".\n\n" "$ipLabel" 1>&2

        #===========================================================
        : If output from this operation was requested, retrieve it
        #===========================================================
        else
            if (( CLMGR_VERBOSE )) || [[ -n $CLMGR_ATTRS ]]; then
                CL=$LINENO KLIB_HACMP_get_persistent_ip_attributes "$ipLabel" 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 persistent_ip -h" "PERSISTENT IP/LABEL:" "$CLMGR_PROGNAME" 1>&2
    fi

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