#!/usr/bin/ksh93
#  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
#
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# 61haes_r714 src/43haes/usr/sbin/cluster/sa/db2/sbin/cl_db2smadd.sh 1.10 
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2005,2008 
# 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/sa/db2/sbin/cl_db2smadd.sh, 726, 2147A_aha726, Feb 05 2021 09:50 PM

##
## NAME:	cl_db2smadd
##
## DESCRIPTION:	This script is called when the user adds a new DB2
##		instance to the cluster definition.  This script
##		calls the discovery routine on each specified node
##		and performs pre-verification to ensure the 
##		discovered DB2 instances are available and accessible
##		on the remote nodes.
##
##		Note: This script is only called via SMIT
##
## ARGUMENTS:
##		-D turn debugging on (set -x)
##		
##		-n <nodelist> where each node is separated by ':'
##		   The list of nodes to run discovery on
##
##		-F flush discovery file, remove the discovery file
##
##		-f <filename> use the specified discovery file name (not in use)
##
##		-v Turn verbose logging on for console discovery logging,
##		   otherwise this script will output information useful 
##		   for SMIT cmd_to_discover (header / values output)
##
##		-M add nodes for mutual takeover (requires the user to enter
##		   two nodes, and discovery must have two nodes discovered)
##
##		-A add nodes for add a single instance, must have one instance
##		   discovered, and only one node can be entered.
##
##		-N diable summary reporting, this flag only has meaning if
##		   used in combination with the -v flag.
##
##
## RETURN:
##		0 on success
##		1 or higher on failure
##
##
## Internal Functions:
##
##	run_discovery
##	prevalidation
##	dump_discovery_info
##	check_db2_software_levels
##
## KLIB Functions:
##
##	KLIB_DB2_disc_get_instance_value
##
##	KLIB_UTIL_word_count
##	KLIB_UTIL_LIST_unique_key_list
##	KLIB_UTIL_LIST_hasharray_datadumper
##
##	KLIB_AIX_get_fs_freespace
##
##	KLIB_HACMP_is_known_node
##	KLIB_HACMP_discover_nodes
##
##----------------------------------------------------------------------------

. /usr/es/lib/ksh93/func_include
version='1.10'

function run_discovery
{
	typeset -n nodelist=$1
	typeset -n flags=$2
	typeset filename=$3

	unset PID2NODE
	typeset -A PID2NODE
	for node in $nodelist; do

		address=$(clgetaddr $node)
	
		# If we were unable to contact the node, abort discovery
		[[ -z $address ]] && {
			echo 
			echo 
			abort 10 $node
		}

		if [[ -n "$DSE_INSTALL_DIR" ]]; then
			cl_rsh $node $DB2SA_BIN_DIR/cl_db2discovery $flags -p $DSE_INSTALL_DIR > $DB2_ETC_PATH/$node.$$ 2>/dev/null &
		else
			cl_rsh $node $DB2SA_BIN_DIR/cl_db2discovery $flags > $DB2_ETC_PATH/$node.$$ 2>/dev/null &
		fi
		RC=$?
		PID=$!

		PIDS="$PIDS $PID"
		PID2NODE[$PID]=$node
	done

	#
	# Wait for discovery to complete on all available nodes
	#
	typeset -i count=0
	for pid in $PIDS; do
		typeset alive=$(/bin/ps -ef | grep $pid | grep -v grep)
		while [[ -n $alive ]]; do
			alive=$(/bin/ps -ef | grep $pid | grep -v grep)
			$VERBOSE && print -n "."
			sleep 4
			(( count=$count + 1 ))
			
			# If we've waiting for 2 minutes for this node, then abort discovery just on this node
			# its likely that the other nodes have already completed.
			(( $count == 30 )) && alive="ENDTHIS"
		done
	done

	if $FLUSH_DISCOVERY_FILE && [[ -f $filename ]]; then
		rm -f $filename
	fi

	#
	# Append the discovery information to the local discovery file
	#
	for node in $nodelist; do
		echo "CLUSTER_NODE=\"$node\"" >> $filename
		cat $DB2_ETC_PATH/$node.$$ >> $filename
		rm -f $DB2_ETC_PATH/$node.$$
	done
	return 0
}

#
# Name:         prevalidation
#
# Description:  This script will validate the various components of DB2
#               to ensure the DB2 instance meets the following criteria
#
#               1. DB2 Instance Home Directory Must reside on a filesystem
#                  that is on shared storage (shared VG)
#
#               2. The node must have DB2 installed either 8.1 or 8.2
#
#               3. Shared volume groups must be accessible on all nodes
#                  where a particular instance might reside in the cluster
#                  This requires all hdisks for the instance VG(s) to exist
#                  on all participating nodes.
#
#               4. DB2 Instance Users & Groups must be consistent across
#                  the participating nodes in the cluster.  The UID & GIDs
#		   must be identical across all participating nodes.
#
# Arguments:	None
#
# Returns:	calls abort, and exits this script with an errorcode == dspmsg
#		number
#
function prevalidation
{
	# Check to ensure the software levels of all the DB2 discovered
	# instances are running at the same levels, abort and report
	# the error if the level is incorrect
	#
	check_db2_software_levels
	if (( $? != 0 )); then
		abort 5
	fi

	# Check to ensure we have at least one DB2 instance available for
	# the user to select from
	if [[ -z $DB2_INSTANCES ]]; then
		echo
		abort 6 !DISCOVERY_NODES
	fi
}
	
#
# Name:		rewrite_discovery_file
#
# Description:	This function rewrites the discovery file, joining
# 		current discovery data together from all of the nodes
#		specified to this command.
#
# Arguments:	None
#
# Returns:	None
#
function dump_discovery_info
{
	typeset DATE
	typeset key
	typeset instance
	typeset NEW_INST_LIST
	typeset insthome

	DATE=$(date +"%m/%d/%y %H:%M:%S")
	echo "#\n# This ksh93 source file was generated on $DATE"
	echo "# Do NOT alter the content of this file\n#"

	#
	# Remove those instances that don't have available collected information
	#
	for instance in $DB2_INSTANCES; do
		insthome=$(KLIB_DB2_disc_get_instance_value $instance "INSTHOME")
		if [[ -n $insthome ]]; then
			NEW_INST_LIST="$NEW_INST_LIST $instance"
		fi
	done
	DB2_INSTANCES=$NEW_INST_LIST

	#
	# Remove duplicate instance keys from DB2_INSTANCES
	#
	KLIB_UTIL_LIST_unique_key_list DB2_INSTANCES $DB2_INSTANCES

	# Iterate over all the variables that start with DB2
	for key in ${!DB2*}; do

		#
		# Remove duplicate nodes from the list of nodes in the nodes
		# for each instance array.
		#
		if [[ ${key:0:10} == "DB2_NODES_" ]]; then
			typeset -n array=$key
			KLIB_UTIL_LIST_unique_key_list array $array
		fi

		#
		# If the variable is not an instance associative array,
		# then print out the value
		#
		if [[ "${key:0:13}" != "DB2_INSTANCE_" && "${key:0:11}" != "DB2_ERRORS_" ]]; then
			echo "$key=\"$(eval echo \$$key)\""
		fi
	done

	# Dump each of the DB2 instance gathered from the nodes
	# the last copy will overwrite the pre-existing copy
	for instance in $DB2_INSTANCES; do
		KLIB_UTIL_LIST_hasharray_datadumper "DB2_INSTANCE_$instance"
	done

	# Dump the volume group info from lspv from all cluster nodes
	# into one hash table AIX_DISKS

	# Save off the old discovery info
	typeset -A AIX_DISKS_OLD
	for key in ${!AIX_DISKS[*]}; do
		AIX_DISKS_OLD[$key]="${AIX_DISKS[$key]}"
	done

	unset AIX_DISKS
	typeset -A AIX_DISKS
	for variable in ${!AIX_DISKS_*}; do
		typeset -n disks=$variable
		for key in ${!disks[*]}; do
			AIX_DISKS[$key]="${disks[$key]}"
		done
		unset disks
	done

	# Dump the list of AIX_DISKS generated above for all
	# discovered instances
	KLIB_UTIL_LIST_hasharray_datadumper AIX_DISKS

	# Dump the DB2_ERRORS that occurred during the prevalidation phase
	KLIB_UTIL_LIST_hasharray_datadumper DB2_ERRORS
}

#
# Name:		check_db2_software_levels
#
# Description:	Check the discovered software levels on all of the instances
#		discovered.  This verifies that all instances are running
#		the same software level of DB2.  Verify and sync will validate
#		that each node in the cluster runs the same level of DB2 software.
#
# Arguments:	None (uses the discovery information sourced into our environment)
#
# Returns:	0 - if successfully validated same levels of DB2 on all nodes
#		1 - if unsuccessful
#
function check_db2_software_levels
{
	typeset instance
	typeset tmpLevel
	typeset level=''

	for instance in $DB2_INSTANCES; do
		tmpLevel=$(KLIB_DB2_disc_get_instance_value $instance "SOFTWARE_LEVEL")
		if [[ -z $level ]]; then
			level=$tmpLevel
		fi
		if [[ "$level" != "$tmpLevel" ]]; then
			return 1
		fi
	done
	return 0
}

#########
# MAIN
#########

# Read in the message catalog entries
. /usr/es/sbin/cluster/sa/db2/cat/cl_db2smadd

# Read in the init functions, abort, require, errmsg, etc.
. /usr/es/lib/ksh93/common_functions.ksh93

# Read in the DB2 definitions
. /usr/es/sbin/cluster/sa/db2/etc/db2_definitions

umask -S u=rw,g=,o=

# Set the FPATH for all DB2 / HACMP functions
FLIB=/usr/es/lib/ksh93
FPATH=$FLIB/utils:$FLIB/hacmp:$FLIB/db2:$FLIB/db2/vg:$FLIB/util:$FLIB/util/list:$FLIB/aix/:$FLIB/aix/odm/
PATH=$PATH:/usr/es/sbin/cluster/sa/db2/sbin/:/usr/es/sbin/cluster/utilities/:/usr/es/sbin/cluster/

DB2SA_BIN_DIR=/usr/es/sbin/cluster/sa/db2/sbin
MUTUAL_TAKEOVER=false
typeset -i MAXNODES=32
typeset -i MINNODES=1

DB2_ETC_PATH=/usr/es/sbin/cluster/sa/db2/etc
DISCOVERY_FILE=$DB2_ETC_PATH/db2.disc
FLUSH_DISCOVERY_FILE=false
VERBOSE=false
DISC_FLAGS=
RUN_DISCOVERY=true
SUMMARY_REPORTING=true

typeset -i ncount=0

while getopts vDAMcFNf:n:m option
do
	case $option in
        v)
			VERBOSE=true
			DISC_FLAGS=-v
			RUN_DISCOVERY=true ;;
		D)
			VERBOSE_LOGGING=high ;;
	    F)
			FLUSH_DISCOVERY_FILE=true ;;
		f)
			DISCOVERY_FILE=$OPTARG ;;
        n)
            DISCOVERY_NODES=$(echo $OPTARG | sed -e "s/\:/ /g")
	        for node in $DISCOVERY_NODES; do
				(( ncount=$ncount+1 ))
			done
            ;;
        A)
            # -A tells us we're adding a single instance, only one
			# node should be specified.
            ACTION=add_single
			MINNODES=1
			MAXNODES=1
            ;;
	    M)
			# -M tells us the user is performing a SMIT mutual takeover
			# add
			ACTION=mutual_takeover
			MINNODES=2
			MAXNODES=2
		    ;;
	    N)
			# Disable summary reporting, default is enabled
			SUMMARY_REPORTING=false
			;;
            m)
                        DISC_FLAGS="$DISC_FLAGS -q"
                        ;;
    esac
done

[[ "$VERBOSE_LOGGING" == "high" ]] && set -x

# Verifiy there is sufficient space (4MB) in the following filesystems
# /var, /usr, /tmp prior to allowing the user to run this command

typeset -i SF
for fs in /tmp /var /usr; do
	SF=$(KLIB_AIX_get_fs_freespace $fs)
	(( $SF < 4 )) && abort 11 $fs $SF
done


#
# If the user is attempting to add too many nodes or too few
# abort
#
if (( $ncount > $MAXNODES || $ncount < $MINNODES )); then
	if (( $MINNODES == $MAXNODES )); then
		abort 1 $MINNODES
	else
		nodes_present=false
		odmget HACMPnode | while read line; do
			nodes_present=true
		done

		$nodes_present && 
			abort 2 $MINNODES $MAXNODES !DISCOVERY_NODES
		$nodes_present ||
			abort 12
	fi
fi

#
# Check each cluster node name against the current cluster configuration,
# if we find that a specified node isn't in the list, assume its a communication
# path and use clmodclstr to add the node to the cluster configuration.
#
for node in $DISCOVERY_NODES; do

	KLIB_HACMP_is_known_node $node
	RC=$?

	# If we don't know this node, run discovery
	if (( $RC != 0 )); then
		RUN_DISCOVERY=true
		break
	fi
done

$RUN_DISCOVERY || {
	typeset -i inst_count=0

	# Determine the number of currently defined instances
	DB2_INSTANCES=$(cl_db2smdisc -i)

	# Count the number of instances discovered
	KLIB_UTIL_word_count inst_count $DB2_INSTANCES

	# If the number of currently discovered DB2 instances is less than
	# 2 and we're attempting to configure mutual takeover, run discovery
	[[ "$ACTION" == "mutual_takeover" ]] && (( $inst_count < 2 )) &&
		RUN_DISCOVERY=true

	# If the number of discovered instances is less than 1, then run discovery
	# if the user is attempting to add a new instance to the HACMP configuration
	#
	[[ "$ACTION" == "add_single" ]] && (( $inst_count < 1 )) &&
		RUN_DISCOVERY=true

	# If there are no DB2 instances discovered, run discovery
	(( $inst_count == 0 )) && RUN_DISCOVERY=true
}

CLUSTER_NODES=$(KLIB_HACMP_discover_nodes $DB2_DEFAULT_CLUSTER_NAME DISCOVERY_NODES)

#
# If we were unable to add one or more specified nodes, or the list of nodes
# provided didn't exist in the cluster configuration then report an error
#
if (( ${#CLUSTER_NODES[@]} == 0 || ${#CLUSTER_NODES[@]} != ${#DISCOVERY_NODES[@]} )); then
	abort 3 DISCOVERY_NODES
fi

$RUN_DISCOVERY && {
	$VERBOSE && errmsg 8 !DISCOVERY_NODES
	run_discovery DISCOVERY_NODES DISC_FLAGS $DISCOVERY_FILE
}

#
# Source the discovery info into our environment, then
# redump the discovery info back into the discovery file
# this removes duplicate entries
#
[[ -f $DISCOVERY_FILE ]] && . $DISCOVERY_FILE
dump_discovery_info > $DISCOVERY_FILE

# Perform validation on the accessible instances
prevalidation

instances=$(cl_db2smdisc -i)
if [[ -z $instances ]]; then
	abort 4
fi

KLIB_UTIL_word_count inst_count $instances
KLIB_UTIL_word_count node_count $CLUSTER_NODES

$VERBOSE && {
	echo
	echo
	instances=$(eval echo $instances)
	errmsg 9 !instances $inst_count !CLUSTER_NODES
}

# If there are an insufficient number of instances for the number
# of nodes specified (mutual takeover requires 2 instances)
# then abort and report an error
#
if [[ "$ACTION" == "mutual_takeover" ]]; then
	if (( $inst_count < 2 )); then
		abort 7
	fi
fi

# If verbose logging is enabled, and summary
if $VERBOSE; then
	#
	# If summary reporting is enabled, then print a summary of
	# each DB2 instance as its been discovered.
	#
	$SUMMARY_REPORTING && {
		for instance in $DB2_INSTANCES; do
			cl_db2smdisc -s $instance
		done
	}
else
	echo "#firstnode:takeovernode"
	echo $CLUSTER_NODES | read first rest
	echo "$first:$rest"
fi

exit 0
