#!/bin/ksh93
#  ALTRAN_PROLOG_BEGIN_TAG                                                    
#  This is an automatically generated prolog.                                  
#                                                                              
#  Copyright (C) Altran ACT S.A.S. 2018,2021.  All rights reserved.  
#                                                                              
#  ALTRAN_PROLOG_END_TAG                                                      
#                                                                              
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# 61haes_r721 src/43haes/usr/sbin/cluster/events/acquire_service_addr.sh 1.74.1.5 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 1990,2016 
# 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 
# @(#)  7d4c34b 43haes/usr/sbin/cluster/events/acquire_service_addr.sh, 726, 2147A_aha726, Feb 05 2021 09:50 PM
#########################################################################
#
# Name:         acquire_service_addr
#
# Description:  This script is called when the local node joins the
#               cluster or a remote node leaves the cluster and
#               does the initial process of identifying where any service
#               IP addressed need to be acquired.
#
# Arguments:    service address, if called for serial processing, otherwise
#               none
#
# Returns:      0   success
#               1   fatal error
#               2   bad argument
#               11  recoverable error
#
#########################################################################

#########################################################################
#
# Name:        get_list_head and get_list_tail
#
# Description: utility functions to pull items from the head or tail
#              of a colon delimited list
#
# Inputs:      the list to work on
#
# Outputs:     head or tail list of items
#
# Returns:     none
#
#########################################################################
function get_list_head {
    echo $* | cut -f 1 -d : | sed 's/,/ /g' |read LIST
    print $LIST
} # End of "get_list_head()"


function get_list_tail {
    case $* in
        *:* )
            echo $* | cut -f 2- -d : |read LIST
            print $LIST
            return
        ;;
        * )
            echo ""
        ;;
    esac
} # End of "get_list_tail()"


#########################################################################
#
# Name: best_boot_addr
#
# This routine finds the best boot interface for hosting an aliased service
# address. This routine exercises the resource distribution preference for
# service labels. The available options are:
#	anti-collocation - the default - all UP interfaces are sorted
#		according to the number of addresses currently on the interface.
#		The interface with the least number is selected.
#	collocation - put all service on the same interface
#	collocation with persistent - put all service on the same interface
#		as the one holding the persistent
#	anti-collocation with persistent - avoid putting service on the
#		same interface as the persistent
#
# Arguments: network name
# 	     List of candidate boot addresses on that network
#
# Returns: network address of the selected boot interface
#
#########################################################################
function best_boot_addr {
    typeset best_candidate=NONE
    typeset best_aliases=0
    typeset ip_family="" service="" services=""
    typeset MA="" 

    [[ $VERBOSE_LOGGING == "high" ]] && set -x
    typeset NETWORK=$1
    shift
    typeset candidate_boots=$*

    # if there is only one boot then thats the candidate - regardless of
    # state or distribution preference
    integer num_candidates=$(echo $candidate_boots | tr " " "\n" | wc -l)
    if (( $num_candidates == 1 )); then
        echo $candidate_boots
        return
    fi

    # find the (optional) service label distribution ppreference for this
    # network
    MA=$(odmget -q"network = $NETWORK AND function = shared" HACMPadapter |\
         grep "max_aliases" | awk '{print $3}' | sort -nu | tail -1)
    # decode the setting
    case $MA in
        1|771 )     # simple collocation
        # if there is already a service on any of the candidates, then
        # that is the candidate for this service

        # find all services on this network
        services=$(cllsif -J "$OP_SEP" -Si $LOCALNODENAME | awk -F"$OP_SEP" -v NET="$NETWORK" '{if ($2 == "service" && $3 == NET) print $1}' | sort)
        service_if=""
        for service in $services
        do
            # is it on this node ?
            service_if=$(LC_ALL=C clgetif -a $service 2>/dev/null | cut -f1)
            [[ -n "$service_if" ]] && break  #found it
        done
        # if there is a service, scan list of candidates
        if [[ -n "$service_if" ]]
        then
            for candidate in $candidate_boots
            do
                boot_if=$(LC_ALL=C clgetif -a $candidate 2>/dev/null | cut -f1)
                if [[ -n "$boot_if" && $boot_if == $service_if ]]
                then
                    best_candidate=$candidate
                    break
                fi
            done
        fi

        # if the loop above did not identify a candidate, then the
        # candidate is the first UP boot
        if [[ $best_candidate == "NONE" ]]; then
            for candidate in $candidate_boots
            do
                LC_ALL=C cllsif -J "$OP_SEP" -Sn $candidate | 
                         cut -f7,15 -d"$OP_SEP" | 
                         IFS="$OP_SEP" read candidate_dot_addr ip_family

                # find the state of this candidate
                if [[ $ip_family == "AF_INET" ]]; then
                    addr=\$i${candidate_dot_addr//\./x}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                else
                    addr=\$i${candidate_dot_addr//:/y}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                fi
                candidate_state=$(eval print $addr)

                if [[ $candidate_state == "UP" ]]; then
                    best_candidate=$candidate
                    break  # all done
                fi
            done
        fi
        ;;
        259|1283)   # collocation with persistent
        # if there is a persistent label on any of the candidates, then
        # that is the candidate for this service
        persistent=$(cllsif -J "$OP_SEP" -Sp $LOCALNODENAME | awk -F"$OP_SEP" -v NET="$NETWORK" '{if ($2 == "persistent" && $3 == NET) print $1}' | sort)
        persistent_if=""
        if [[ -n "$persistent" ]]
        then
            # is it on this node ?
            persistent_if=$(LC_ALL=C clgetif -a $persistent 2>/dev/null | cut -f1)
        fi
        # if there is a persistent, scan list of candidates
        if [[ -n "$persistent_if" ]]
        then

            # Find any service labels already present on this node
            LC_ALL=C clgetif -a $(cllssvcs 2>/dev/null | cut -f1) 2>/dev/null | 
                     read ALIVE_SERV_IF

            for candidate in $candidate_boots
            do

                LC_ALL=C cllsif -J "$OP_SEP" -Sn $candidate | \
                cut -f7,15 -d"$OP_SEP" | 
                IFS="$OP_SEP" read candidate_dot_addr ip_family

                # find the state of this candidate
                if [[ $ip_family == "AF_INET" ]]; then
                    addr=\$i${candidate_dot_addr//\./x}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                else
                    addr=\$i${candidate_dot_addr//:/y}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                fi
                candidate_state=$(eval print $addr)

                boot_if=$(LC_ALL=C clgetif -a $candidate 2>/dev/null | cut -f1)

                # If the interface has any other service or
                # persistent label and the interface is UP, then its our
                # best candidate
                if [[ $candidate_state == "UP" && -n $boot_if && \
                      ( $boot_if == $persistent_if || \
                        $boot_if == $ALIVE_SERV_IF ) ]]
                then
                    best_candidate=$candidate
                    break
                fi
            done
        fi

        # if the loop above did not identify a candidate, then go through
        # the same calculation as we use for simple anti-collocation
        if [[ $best_candidate == "NONE" ]]; then
            for candidate in $candidate_boots
            do
                LC_ALL=C cllsif -J "$OP_SEP" -Sn $candidate | 
                cut -f7,15 -d"$OP_SEP" | 
                IFS="$OP_SEP" read candidate_dot_addr ip_family

                # find the state of this candidate
                if [[ $ip_family == "AF_INET" ]]; then
                    addr=\$i${candidate_dot_addr//\./x}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                else
                    addr=\$i${candidate_dot_addr//:/y}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                fi
                candidate_state=$(eval print $addr)

                if [[ $candidate_state == "UP" ]]; then
                    # this interface is UP - now lets check how many
                    # addresses it is already hosting
                    candidate_if=$(LC_ALL=C clgetif -a $candidate_dot_addr 2>/dev/null | cut -f1)
                    candidate_aliases=$(LC_ALL=C ifconfig $candidate_if | grep -c -w "inet[6]\{0,1\}")

                    if [[ $best_candidate == "NONE" ]] || \
                       (( $candidate_aliases < $best_aliases ))
                    then
                        best_candidate=$candidate
                        best_aliases=$candidate_aliases
                    fi
                fi
            done
        fi
        ;;
        515|1539 )   # anti-collocation with persistent
        # if there is a persistent label on any of the candidates, then
        # that candidate should be marked DOWN to avoid its selection
        persistent=$(cllsif -J "$OP_SEP" -Sp $LOCALNODENAME | awk -F"$OP_SEP" -v NET="$NETWORK" '{if ($2 == "persistent" && $3 == NET) print $1}' | sort)
        save_if="NONE"
        persistent_if=""
        if [[ -n "$persistent" ]]
        then
            # is it on this node ?
            persistent_if=$(LC_ALL=C clgetif -a $persistent 2>/dev/null | cut -f1)
        fi
        # if there is a persistent, scan list of candidates
        if [[ -n "$persistent_if" ]]
        then
            for candidate in $candidate_boots
            do
                boot_if=$(LC_ALL=C clgetif -a $candidate 2>/dev/null | cut -f1)
                if [[ -n $boot_if && $boot_if == $persistent_if ]]
                then
                    LC_ALL=C cllsif -J "$OP_SEP" -Sn $candidate | 
                    cut -f7,15 -d"$OP_SEP" | 
                    IFS="$OP_SEP" read candidate_dot_addr ip_family

                    # find the state of this candidate
                    if [[ $ip_family == "AF_INET" ]]; then
                        addr=\$i${candidate_dot_addr//\./x}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                    else
                        addr=\$i${candidate_dot_addr//:/y}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                    fi
                    candidate_state=$(eval print $addr)

                    # if the interface is up, save it away just in case
                    # its our only up interface
                    [[ $candidate_state == "UP" ]] && save_if=$candidate_dot_addr

                    # set state to down
                    if [[ $ip_family == "AF_INET" ]]; then
                        addr=i${candidate_dot_addr//\./x}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                    else
                        addr=i${candidate_dot_addr//:/y}_${LOCALNODENAME//-/$HA_DASH_CHAR}
                    fi

                    export $addr="DOWN"
                    break
                fi
            done
        fi

        # now that any interface holding the persistent is marked as DOWN,
        # go through the same calculation as we use for simple
        # anti-collocation
        for candidate in $candidate_boots
        do
            LC_ALL=C cllsif -J "$OP_SEP" -Sn $candidate | 
            cut -f7,15 -d"$OP_SEP" | 
            IFS="$OP_SEP" read candidate_dot_addr ip_family

            # find the state of this candidate
            if [[ $ip_family == "AF_INET" ]]; then
                addr=\$i${candidate_dot_addr//\./x}_${LOCALNODENAME//-/$HA_DASH_CHAR}
            else
                addr=\$i${candidate_dot_addr//:/y}_${LOCALNODENAME//-/$HA_DASH_CHAR}
            fi
            candidate_state=$(eval print $addr)

            if [[ $candidate_state == "UP" ]]; then

                # this interface is UP - now lets check how many addresses
                # it is already hosting
                candidate_if=$(LC_ALL=C clgetif -a $candidate_dot_addr 2>/dev/null | cut -f1)
                candidate_aliases=$(LC_ALL=C ifconfig $candidate_if | grep -c -w "inet[6]\{0,1\}")
                if [[ $best_candidate == "NONE" ]] || \
                   (( $candidate_aliases < $best_aliases ))
                then
                    best_candidate=$candidate
                    best_aliases=$candidate_aliases
                fi
            fi
        done
        # if the loop above did not find a candidate then we cannot
        # exercise the preference - try setting the candidate to the
        # same interface as has the persistent
        [[ $best_candidate == "NONE" ]] && best_candidate=$save_if

        ;;
        * )     # default to rdp_anti_collocation
        # test all the candidates in the list
        for candidate in $candidate_boots
        do
            LC_ALL=C cllsif -J "$OP_SEP" -Sn $candidate | 
            cut -f7,15 -d"$OP_SEP" | 
            IFS="$OP_SEP" read candidate_dot_addr ip_family

            # find the state of this candidate
            if [[ $ip_family == "AF_INET" ]]; then
                addr=\$i${candidate_dot_addr//\./x}_${LOCALNODENAME//-/$HA_DASH_CHAR}
            else
                addr=\$i${candidate_dot_addr//:/y}_${LOCALNODENAME//-/$HA_DASH_CHAR}
            fi
            candidate_state=$(eval print $addr)

            if [[ $candidate_state == "UP" ]]; then

                # this interface is UP - now lets check how many addresses
                # it is already hosting
                candidate_if=$(LC_ALL=C clgetif -a $candidate_dot_addr 2>/dev/null | cut -f1)
                candidate_aliases=$(LC_ALL=C ifconfig $candidate_if | grep -c -w "inet[6]\{0,1\}")
                if [[ $best_candidate == "NONE" ]] || \
                   (( $candidate_aliases < $best_aliases ))
                then
                    best_candidate=$candidate
                    best_aliases=$candidate_aliases
                fi
            fi
        done
        ;;
    esac

    # once here, the best candidate should have been selected
    if [[ "$best_candidate" != "NONE" ]]; then
        echo $best_candidate
        return
    fi

    # Should never get here
    dspmsg scripts.cat 342 "Unable to determine local boot/service interface.\n"
    exit 1
} # End of "best_boot_addr()"


#########################################################################
#
# MAIN Main main
#
#########################################################################

PROGNAME=${0##*/}
export PATH="$(/usr/es/sbin/cluster/utilities/cl_get_path all)"
OP_SEP="$(cl_get_path -S)"

# these variable control the telinit wait loop
TELINIT=false
TELINIT_FILE="/usr/es/sbin/cluster/.telinit"
# try this number of times
typeset -i telinit_wait_count=36
# waiting this much between tries
DELAY=5
# by default produces a wait of 3 minutes for telinit to complete

STATUS=0
PROC_RES=false

ALLSRVADDRS="All_service_addrs"
ALLNOERRSRV="All_nonerror_service_addrs"

#
# Turn tracing on after initialization
#
if [[ $VERBOSE_LOGGING == "high" ]]; then
    set -x
    version='1.74.1.5'
fi

#
# if JOB_TYPE is set, and it doesn't equal to "GROUP", then
# we are processing for process_resources
#
if [[ ${JOB_TYPE:-0} != 0 && $JOB_TYPE != "GROUP" ]]; then
    PROC_RES=true
else
    # check arguments for serial case only. Otherwise everything comes
    # from the environment.
    if (( $# <= 0 ))
    then
        cl_echo  1028 "Usage: $PROGNAME service_address\n" $PROGNAME
        exit 2
    fi

    export RESOURCE_GROUPS=$GROUPNAME
fi

#
# Change NSORDER to avoid hangs due to NIS
#
saveNSORDER=${NSORDER:-UNDEFINED}
NSORDER=local
export NSORDER

# update the resource manager with this operation
cl_RMupdate resource_acquiring $ALLSRVADDRS $PROGNAME

#
# go through all resource groups
#
for GROUPNAME in $RESOURCE_GROUPS; do

    export GROUPNAME

    #
    # if called from process_resources, the list will be in the env, otherwise
    # the list is passed as en axplicit arg
    #
    if [[ $PROC_RES == true ]]; then
        get_list_head $IP_LABELS | read SERVICELABELS
        get_list_tail $IP_LABELS | read IP_LABELS
    else
        SERVICELABELS=$*
    fi

    for service in $SERVICELABELS
    do
        #
        # Determine if address is already configured.  If not, try to
        # acquire it.
        #
        LC_ALL=C clgetif -a $service 2>/dev/null
        if (( $? != 0 ))
        then

            # Get the list of boot addresses on this node which are on the same 
            # network as the service address
            NETWORK=$(cllsif -J "$OP_SEP" -Sn $service | cut -d"$OP_SEP" -f3 | uniq)
            boot_list=$(cllsif -J "$OP_SEP" -Si $LOCALNODENAME | awk -F"$OP_SEP" -v NET="$NETWORK" '{if ($2 == "boot" && $3 == NET) print $1}' | sort)

            if [[ -z "$boot_list" ]]
            then
                #
                # If this node has no boot addresses on this network - it 
                # cannot host the service address
                #
                continue
            fi

            # 
            # now find the best candidate out of the list
            #
            boot_addr=$(best_boot_addr $NETWORK $boot_list)
            if (( $? != 0 ))
            then
                    echo "$PROGNAME: Could not find any active boot adapter in network $NETWORK on node $LOCALNODENAME\n"
                    cl_RMupdate resource_error $service $PROGNAME
                    if [[ $PROC_RES == true ]]; then
                        STATUS=11
                    else
                        STATUS=1
                    fi
                    continue
            fi

            INTERFACE=$(LC_ALL=C clgetif -a $boot_addr 2>/dev/null | cut -f1)
            # get the interface and ip of the boot
            cllsif -J "$OP_SEP" -Sn $boot_addr | cut -f7,9 -d"$OP_SEP" | 
                IFS="$OP_SEP" read boot_dot_addr INTERFACE 

            if [[ -z "$INTERFACE" ]]
            then
                echo "$PROGNAME: Could not find interface name for boot adapter $boot_addr\n"
                cl_RMupdate resource_error $service $PROGNAME

                if [[ $PROC_RES == true ]]; then
                    STATUS=11
                    continue
                else
                    STATUS=1
                fi
            else

                # We have a boot interface defined.  Get netmask for the service
                # (which can be different from the boot) and the address then 
                # call swap ip address to do the actual work
      
                cllsif -J "$OP_SEP" -Sn $service | cut -f7,11,15 -d"$OP_SEP" | uniq |
                IFS="$OP_SEP" read service_dot_addr NETMASK INET_FAMILY
                
                if [[ $INET_FAMILY == "AF_INET6" ]]
                then
                    cl_swap_IPv6_address rotating acquire $INTERFACE $service_dot_addr $boot_dot_addr
                else
                    cl_swap_IP_address rotating acquire $INTERFACE $service_dot_addr $boot_dot_addr $NETMASK
                fi
                # Check the return code.
                RC=$?
                
                if (( $RC != 0 ))
                then
                    if [[ $PROC_RES == false ]]; then
                        STATUS=1
                    else
                        # the error code has to be propagated to process_resources
                        STATUS=$RC
                    fi
    
                else
                    if [[ $PROC_RES == false ]]; then
                        STATUS=0
                        TELINIT=true
                    fi
                fi

            fi # if INTERFACE is non-empty
        fi # if clgetif service successful
    done # for service in $SERVICELABELS

    # update the resource manager with the results
    cl_RMupdate resource_up $ALLNOERRSRV $PROGNAME

done #for GROUPNAME


if [[ $saveNSORDER != UNDEFINED ]]; then
       export NSORDER=$saveNSORDER
else
       export NSORDER=""
fi

if [[ $PROC_RES == false ]]; then
       if [[ $TELINIT == true ]]
       then
           cl_telinit
       fi

fi #endof PROC_RES

exit $STATUS

#==============================================================================
# The following, comment block attempts to enforce coding standards when this
# file is edited via emacs or vim. This block _must_ appear at the very end
# of the file, or the editor will not find it, and it will be ignored.
#==============================================================================
# Local Variables:
# indent-tabs-mode: nil
# tab-width: 4
# End:
#==============================================================================
# vim: tabstop=4 shiftwidth=4 expandtab
#==============================================================================
