#!/bin/ksh93
# @(#)  7d4c34b 43haes/lib/ksh93/hacmp/KLIB_HACMP_add_nova.sh, 726, 2147A_aha726, Feb 05 2021 09:50 PM
#  ALTRAN_PROLOG_BEGIN_TAG                                                    
#  This is an automatically generated prolog.                                  
#                                                                              
#  Copyright (C) Altran ACT S.A.S. 2017,2021.  All rights reserved.  
#                                                                              
#  ALTRAN_PROLOG_END_TAG                                                      
#                                                                              

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

 clmgr add nova [NovaLink] \
 USER_NAME=<###> \
 [ TIMEOUT=<###> ] \
 [ RETRY_COUNT=<###> ] \
 [ RETRY_DELAY=<###> ] 

=head1 DESCRIPTION

 Attempts to create the specified NovaLink object within the PowerHA SystemMirror
 configuration.
 When creating a NovaLink into PowerHA SystemMirror configuration, it can be associated with nodes
 that belongs to same managed system.
 Each node will try to communicate with its own NovaLink to perform Resource
 Optimized High Availability operations.

=head1 ARGUMENTS

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

 2. NovaLink [REQUIRED] [string]
 Host Name or IP address for the NovaLink.
 IPv4 and IPv6 format address are supported.

 3. TIMEOUT [OPTIONAL] [int]
 Timeout set on NovaLink operations.
 This value is expressed in minutes.
 The default TIMEOUT value is 10.

 4. RETRY_COUNT [OPTIONAL] [int]  
 Number of retries set on NovaLink operations.
 The NovaLink operation which gets times out then will be retried this number of times. 
 The default RETRY_COUNT value is 5.

 5. RETRY_DELAY [OPTIONAL] [int]  
 Duration of delay between two retries set on NovaLink operations.
 This value is expressed in [seconds]. 
 The NovaLink operation which gets times out, then will be retried 'RETRY_COUNT'number of times, with 'RETRY_DELAY' seconds between each retry. 
 The default value is 10. 

 6. USER_NAME [REQUIRED] [string]
 User name for NovaLink. 
 This USER_NAME option is mandatory for communication with NovaLink.
  
 7. PASSWORD [OPTIONAL] [string]
 Password for NovaLink.
 This PASSWORD option is not mandatory for SSH based communication with NovaLink.

=head1 CONFIGURATION/SETUP

1. If necessary, generate the SSH keys for your AIX node:

 1. cd ~/.ssh/
 2. ssh-keygen -t rsa

 This will create the private key (id_rsa) and public key (id_rsa.pub).

2. Add your node's public key to the NovaLink's SSH "allow" list:

 1. scp USER_NAME@<NovaLink>:.ssh/authorized_keys2 /tmp/NOVA_PUB_KEYS
 2. cat ~/.ssh/id_rsa.pub >> /tmp/NOVA_PUB_KEYS
 3. scp /tmp/NOVA_PUB_KEYS USER_NAME@<NovaLink>:.ssh/authorized_keys2
 4. rm -f /tmp/NOVA_PUB_KEYS
 5. Verify it (i.e. ssh USER_NAME@<NovaLink> date). No password should be required.

=head1 RETURN

 0: no errors were detected; the operation appears to have been successful
 1: a general error has occurred
 2: a specified resource does not exist, or could not be found
 3: some required input was missing
 4: some detected input was incorrect in some way
 5: a required dependency does not exist
 6: a specified search failed to match any data

=cut
} # End of POD-formatted documentation.

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

    typeset -n properties=$1
    typeset nova_label=${2//\"/}
    typeset timeout=${3//\"/}
    typeset retry_count=${4//\"/}
    typeset retry_delay=${5//\"/}
    typeset username=${6//\"/}
    typeset password=${7//\"/}

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

    #===================================
    # Declare and initialize variables
    #===================================
    typeset -i rc=$RC_UNKNOWN
    typeset -i CHECK_NOVA_ERROR=
    typeset -i checkRC=$RC_SUCCESS
	
    #================================================================
    : Check for a defined cluster. No need to continue without one.
    #================================================================
    CL=$LINENO isClusterDefined
    if (( $? != RC_SUCCESS )); then
        rc=$RC_MISSING_DEPENDENCY
    fi

    #===============
    : Validate input
    #===============
    : nova_label
    typeset NOVA_LABEL=
    if [[ -z $nova_label ]]; then
        CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 100 "\nERROR: IP address must be provided.\n\n" 1>&2
        rc=$RC_MISSING_INPUT
    else
        CL=$LINENO KLIB_HACMP_is_known_nova nova_label
        if (( $? == RC_SUCCESS )); then
            CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 229 "\nERROR: the specified object already exists: \"%1\$s\"\n\n" "$nova_label" 1>&2
            rc=$RC_INCORRECT_INPUT
        else
            NOVA_LABEL=$nova_label
        fi
    fi
        
		
    if (( $rc == RC_UNKNOWN )); then
        #===================
        : username parameter
        #===================
        typeset USER_NAME=
        if [[ -n $username ]]; then
            if [[ $username == [\-\+\~\@]* ]]; then
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 103 \
'\nERROR: one or more invalid characters were detected in "%1$s":  "%2$s".\n\n' $username "[-+~@]" 1>&2
                rc=$RC_INCORRECT_INPUT
            else
                USER_NAME=$username
            fi
        else
            dspmsg -s $CLMGR_SET $CLMGR_MSGS 1143 \
"\nERROR: USER_NAME must be provided for NovaLink Creation.\n\n" 1>&2
            rc=$RC_INCORRECT_INPUT 			
        fi
    
        #===================
        : password parameter
        #===================
        typeset PASSWORD=
        if [[ -n $password ]]; then
            PASSWORD=$(print $password | clencodearg)
        fi

        
        #==================
        : timeout parameter
        #==================
        typeset TIMEOUT=
        if [[ -n $timeout ]]; then
            if [[ $timeout == "-1" ]]; then
                TIMEOUT=-1
            elif [[ $timeout != +([[:digit:]]) ]];  then
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 809 "\nERROR: one or more invalid characters were detected in \"%1\$s\" (\"%2\$s\").\n\nValid input include either \"-1\" or a positive integer value.\n\n" "$timeout" "[-1|[0-9]*}" 1>&2
                rc=$RC_INCORRECT_INPUT
            else
                TIMEOUT=$timeout
            fi
        else
            TIMEOUT=-1
        fi

        #======================
        : retry count parameter
        #======================
        typeset RETRY_COUNT=
        if [[ -n $retry_count ]]; then
            if [[ $retry_count == "-1" ]]; then
                RETRY_COUNT=-1
            elif [[ $retry_count != +([[:digit:]]) ]] then
                    dspmsg -s $CLMGR_SET $CLMGR_MSGS 809 "\nERROR: one or more invalid characters were detected in \"%1\$s\" (\"%2\$s\").\n\nValid input include either \"-1\" value or a positive integer value.\n\n" "$retry_count" "[-1|[0-9]*}" 1>&2
                rc=$RC_INCORRECT_INPUT
            else
                RETRY_COUNT=$retry_count
            fi
        else
            RETRY_COUNT=-1
        fi

        #======================
        : retry delay parameter
        #======================
        typeset RETRY_DELAY=
        if [[ -n $retry_delay ]]; then
            if [[ $retry_delay == "-1" ]]; then
                RETRY_DELAY=-1
            elif [[ $retry_delay != +([[:digit:]]) ]] then
                dspmsg -s $CLMGR_SET $CLMGR_MSGS 809 "\nERROR: one or more invalid characters were detected in \"%1\$s\" (\"%2\$s\").\n\nValid input include either \"-1\" value or a positive integer value.\n\n" "$retry_delay" "[-1|[0-9]*}" 1>&2
                rc=$RC_INCORRECT_INPUT
            else
                RETRY_DELAY=$retry_delay
            fi
        else
            RETRY_DELAY=-1
        fi

    fi

    if (( $rc == RC_UNKNOWN )); then
        #===================================================================================================================
        : This NovaLink should be reachable and password less SSH communication should work from the local node. 
        :  Get associated managed system for this NovaLink and build the list of nodes from this managed system.
        :  If any error during the connectivity check, just break unless we  ask to force.
        #===================================================================================================================
        typeset NODES= managed_system= nodelist= nova_cmd=
        print -- "ssh -l $USER_NAME -o BatchMode=yes -o ConnectTimeout=3 $NOVA_LABEL date" >>$CLMGR_TMPLOG
        ssh -l $USER_NAME -o BatchMode=yes -o ConnectTimeout=3 $NOVA_LABEL date >>$CLMGR_TMPLOG 2>&1
        if (( $? == RC_SUCCESS )); then
            nova_cmd="pvmctl sys list --hide-label -d mtms_str"
            print -- "$0()[$LINENO]($SECONDS): ssh -l $USER_NAME -o BatchMode=yes -o ConnectTimeout=3 $NOVA_LABEL $nova_cmd" >>$CLMGR_TMPLOG # Always log commands
            managed_system=$(ssh -l $USER_NAME -o BatchMode=yes -o ConnectTimeout=3 $NOVA_LABEL $nova_cmd)
            checkRC=$?
            print -- "$0()[$LINENO]($SECONDS): ssh -l $USER_NAME -o BatchMode=yes -o ConnectTimeout=3 $NOVA_LABEL $nova_cmd rc=$checkRC" >>$CLMGR_TMPLOG # Always log commands
            if (( $checkRC != RC_SUCCESS )) || [[ -z $managed_system ]]; then
                CL=$LINENO dspmsg -s $CLMGR_SET $CLMGR_MSGS 1164 "\nERROR: failed to get managed system for NovaLink \"%1\$s\".\n\n" "$NOVA_LABEL" 1>&2
                rc=$RC_ERROR
            fi
        else
            cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1165 "\nERROR: could not communicate with NovaLink \"%1\$s\" via SSH from local node. Check \"%1\$s\" is valid. Check the public key for local node installed correctly on \"%1\$s\". Check for any network issues or name resolution problems or firewall problems.\n" "$NOVA_LABEL" 1>&2
            rc=$RC_ERROR
        fi

        if (( $rc == RC_UNKNOWN )); then
            for node in $(clnodename); do
                print -- "$0()[$LINENO]($SECONDS): cl_dynresop -n "${node%%\.*}" -C 2>/dev/null" >>$CLMGR_TMPLOG # Always log commands
                if [[ $managed_system == $(cl_dynresop -n "${node%%\.*}" -C 2>/dev/null) ]]; then
                    nodelist="${nodelist:+$nodelist }${node%%\.*}"
                fi
            done
        
        fi

        if [[ -n $nodelist ]]; then
            if (( $rc == RC_UNKNOWN )); then
                for node in $nodelist; do
                    print "check_nova_connectivity_with_node $node $NOVA_LABEL NODES=$NODES" >>$CLMGR_TMPLOG
                    check_nova_connectivity_with_node $node $NOVA_LABEL NODES $USER_NAME $PASSWORD
                    checkRC=$?
                    print "check_nova_connectivity_with_node $node $NOVA_LABEL NODES=$NODES rc=$checkRC" >>$CLMGR_TMPLOG
                    if (( $checkRC != 0 )); then
                        CHECK_NOVA_ERROR=1
                        break
                    fi
                done
            fi
        fi
    fi

    if (( $rc == RC_UNKNOWN )); then
        #===========================================================
        : Define the NOVA if no input errors have been detected
        #===========================================================
        if (( $CHECK_NOVA_ERROR != 1 )); then
            #=============================================================
            : Define the NOVA if no connectivity errors have been detected
            #=============================================================

            #===========================================================
            : Firstly, update the NOVA object
            #===========================================================
            typeset nOpt= tOpt= rOpt= dOpt= uOpt= pOpt= mopt=
            [[ -z $NOVA_LABEL ]]   && nOpt=" "     || nOpt=" -n $NOVA_LABEL"
            [[ -z $TIMEOUT ]]     && tOpt=" -t 0" || tOpt=" -t $TIMEOUT"
            [[ -z $RETRY_COUNT ]] && rOpt=" -c 0" || rOpt=" -c $RETRY_COUNT"
            [[ -z $RETRY_DELAY ]] && dOpt=" -d 0" || dOpt=" -d $RETRY_DELAY"
            [[ -z $username ]] && uOpt="" || uOpt=" -u $username"
            [[ -z $PASSWORD ]] && pOpt="" || pOpt=" -p $PASSWORD"
            mopt=" -m $managed_system"

            print -- "$0()[$LINENO]($SECONDS): claddnova $nOpt$tOpt$rOpt$dOpt$uOpt$pOpt$mopt" >>$CLMGR_TMPLOG
            claddnova $nOpt$tOpt$rOpt$dOpt$uOpt$pOpt$mopt
            rc=$?
            print -- "$0()[$LINENO]($SECONDS): claddnova $nOpt$tOpt$rOpt$dOpt$uOpt$pOpt$mopt rc=$rc" >>$CLMGR_TMPLOG # Always log command result

            if (( $rc != RC_SUCCESS )); then
                CL=$LINENO dspmsg -s $CLMGR_SET $CLMGR_MSGS 201 "\nERROR: failed to add \"%1\$s\".\n\n" "$NOVA_LABEL" 1>&2
                rc=$RC_ERROR
            else
                #===============================================================
                : Now update the nodes objects
                :  - for each node, we must update the list of novas already set
                #===============================================================
                if (( $rc == RC_SUCCESS )); then
                    if [[ -n $NODES ]]; then
                        #==============
                        : Loop on nodes
                        #==============
                        for NODE in $NODES; do
                            typeset NOpt= kvOpt=                            

                            kvOpt="-c NOVA_MANAGED_SYSTEM=$managed_system"
                            NOpt=" -n $NODE"
                            print -- "$0()[$LINENO]($SECONDS): clchnovaparam $NOpt $kvOpt" >>$CLMGR_TMPLOG
                            clchnovaparam $NOpt $kvOpt
                            rc=$?
                            print -- "$0()[$LINENO]($SECONDS): clchnovaparam $NOpt $kvOpt rc=$rc" >>$CLMGR_TMPLOG # Always log command result
                            if (( $rc != RC_SUCCESS )); then
                                CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 1144 "\nERROR: failed to associate \"%1\$s\" NODES and \"%2\$s\" NovaLink.\n\n" "$NODE" "$NOVA_LABEL" 1>&2
                                rc=$RC_ERROR
                                break
                            fi
                        done
                    fi
                fi                                
            fi

            #=========================================================
            : If output from this operation was requested, retrieve it
            #=========================================================
            if (( $rc == RC_SUCCESS )); then
                if (( $CLMGR_VERBOSE )) || [[ -n $CLMGR_ATTRS ]]; then
                    CL=$LINENO KLIB_HACMP_get_nova_attributes "$NOVA_LABEL" properties
                fi
            fi

        else
            rc=$RC_INCORRECT_INPUT 
        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 nova -h" "NOVA:" 1>&2
    fi

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