#! /usr/bin/ksh
#
# ident	"@(#)ucmm_reconf.sh	1.19	05/04/20 SMI"
#
# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# set some flags (NOTE: they are not set implicitly in subroutines)
#
# set -x # print commands as executed
# set -e # execute ERR trap on command error
# set -u # treat unset variables as an error
# set -f # disable file name generation

# defined for i18n
TEXTDOMAIN=TEXT_DOMAIN; export TEXTDOMAIN
TEXTDOMAINDIR=MESSAGE_DIR; export TEXTDOMAINDIR

#
# should be used only the reconf_framework
#
pre="SUNWscucm.ucmm_reconf"
RECONF_ROOT=/usr/cluster/lib/ucmm
export RECONF_DIR=${RECONF_ROOT}/reconf.d
export RECONF_SCRIPTS=/usr/cluster/lib/ucmm
reconf_error_file=${RECONF_ROOT}/.ucmm_reconf_error
ucmmd_error_file=${RECONF_ROOT}/.ucmmd_error
shutdown_file=${RECONF_ROOT}/.ucmmd_shutdown
CLUSTM=/usr/cluster/lib/ucmm/clustm
RECONF_REQUIRED_ERR=205

INCLUDE=.
${INCLUDE} ${RECONF_SCRIPTS}/ucmm_reconf.common


export CLUSTERSCBIN=/usr/cluster/lib/sc
export CLUSTERBIN=/usr/cluster/lib/ucmm
export CLUSTERVAR=/var/cluster/ucmm
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/cluster/lib
export PATH=/usr/sbin:/usr/bin/:/usr/cluster/bin:${CLUSTERSCBIN}:${CLUSTERBIN}:/usr/ccs/bin/:/bin/:/sbin

######################################################################
# Export the following variables at declaration and 
# subsequent assignments 
######################################################################
typeset -x CURRNODES=""
typeset -x LOCALNODEID""
typeset -x SEQNUM=""
typeset -x ALLNODES=""
typeset -x CLUSTNAME=""


######################################################################
# init - Initialize the Environment for execution of reconf programs.#
######################################################################
function init
{
	set -e
	#
	# set require path/common directories for execution	
	#
	numnodes_key=cluster.number.nodes;
	clustername_key=cluster.cluster_name;
	modnotconfig="";
	cdbfile=/dev/null;

	#
	# set it in a array to once so that allocation is reduced while checking
	#
	set -A MODULES "${modnotconfig}";

	CLUSTNAME=$(enmatch ${clustername_key});

	#
	# create temprorary file locations
	#
	logfile=${CLUSTERVAR}/ucmm_reconf.log
	rotate_log "$logfile" 100000

	tmpdir=${CLUSTERVAR}; 

	if [ ! -d $tmpdir ]; then 
		mkdir -p $tmpdir; 
	fi

	exec 1>> $logfile
	exec 2>> $logfile

}

#############################################################
# rotate_log()
#
#############################################################
rotate_log()
{
	typeset file_size=0
	typeset logfile="$1"
	typeset log_size="${2:-100000}"

	[ ! -f "$logfile" ] && return

	file_size=$(/bin/ls -l "$logfile" | /bin/awk '{print $5}')

	[ $file_size -lt $log_size ] && return

	[ -f ${logfile}.1 ] &&  /bin/rm -f "${logfile}.1"
	[ -f ${logfile}.0 ] && /bin/mv ${logfile}.0 ${logfile}.1
	/bin/mv "${logfile}" "${logfile}.0"
	/bin/touch "${logfile}"
}

#####################################################################
# validate_env_vars:                                                #
#  This function sets required variables by reading values from     #
#  clustm, 					                    #
#####################################################################
function validate_env_vars
{
	set -e

        CURRNODES=$(${CLUSTM} getcurrmembers $CLUSTNAME);
        LOCALNODEID=$(${CLUSTM} getlocalnodeid $CLUSTNAME);
	SEQNUM=$(${CLUSTM} getseqnum $CLUSTNAME);
        ALLNODES=$(${CLUSTM} getallnodes ${CLUSTNAME});

	log_info "${pre}" "Step: %s CURRNODES=%s" "${CURRSTEP}" "${CURRNODES}"

}
#############################################################
# enmatch : lookup a value in the sc configuration file    #
#############################################################
enmatch() {

	${CLUSTERBIN}/cfgmatch $* ${cdbfile} || \
       	(log_info "${pre}.enmatch" "cfgmatch $* failed"; \
                return 1)

}


##########################################################
# Usage:  Echo the correct usage of the program.         #
##########################################################
_usage() {

	#
        # The following are not public entrypoints
	#
	lmsg="\
	%s [cmmstart|cmmstop|cmmabort]\n\
	%s [cmmstep0-9|cmmreturn]\n\
	%s [validate|verify_rac_rg]\n\
	The [cmmstart|cmmstepN|cmmstop|cmmabort|cmmreturn]\n\
	commands are invoked by the cluster membership\n\
	monitor during cluster state transitions"
	printf "${lmsg}\n" "${prog}" "${prog}" "${prog}" "${prog}"
        exit 2

}

###################################################################
# Reconfiguration FrameWork for executing reconf programs         #
###################################################################
function check_execution_error
{
        case $2 in

           0)   # success

		lmsg="%s %s %s completed successfully in %s"
		printf "${lmsg}\n" "$(/bin/date)" "${pre}" "$1" "${CURRSTEP}"
                return 0;;
 
           ${RECONF_REQUIRED_ERR})
		scds_syslog -p error -t ${pre} -m \
			"%s requests reconfiguration in step %s" \
			"$1" ${CURRSTEP};

		lmsg="%s %s %s requests reconfiguration (error %s) in %s"
		printf "${lmsg}\n" "$(/bin/date)" "${pre}" "$1" "$2" "${CURRSTEP}"


		if [[ "${CURRSTEP}" = "cmmabort" ]] ; then
			scds_syslog -p error -t ${pre} -m \
				"%s exited with error %s in step %s" \
				"$1" "${RECONF_REQUIRED_ERR}" ${CURRSTEP};
			sleep 1
			exit 1
		elif [[ "${CURRSTEP}" != "cmmstart" && \
			"${CURRSTEP}" != "cmmreturn" ]] ; then
                   # Reconfigure Status
                   # Means that after the step completes, send
                   # 205 to cmm so that it can reconfigure.
		    reconf_required=1
		fi
		return 0;;
 
           200 | *)   # fail all other cases.
		scds_syslog -p error -t ${pre} -m \
			"%s exited with error %s in step %s" \
			"$1" "$2" ${CURRSTEP};

		lmsg="%s %s %s completed with error %s in %s"
		printf "${lmsg}\n" "$(/bin/date)" "${pre}" "$1" "$2" "${CURRSTEP}"

		if [[ "${CURRSTEP}" != "validate" ]]; then

			if [[ "${CURRSTEP}" != "cmmabort" ]] ; then
				# Write error to log file
				print "$1 exited with error $2 in step ${CURRSTEP}" \
			     		>> ${reconf_error_file} 2>/dev/null;
			fi

			set_framework_status FAULTED "$1 exited with error $2 in step ${CURRSTEP}"
		else
			set_framework_status FAULTED "Validation error in $1."
		fi

                exit 1;;
        esac
 
}

########################################################################
#								       #
# Execute Reconfiguration Programs:                                    #
#                                                                      #
# Parameter 1: Directory From which we have to execute the             #
#              Reconfiguration Programs.                               #
# Variables  :                                                         #
#   reconf_prog:                                                       #
#        Reconfiguring Programs for StepN                              #
#   current_reconf_prog:                                               #
#        Reconfiguration Programs for Current executionsequence        #
#   cur_seqno,seqno: Execution sequence Numbers.                       #
#   count: count is the number of programs for concurrent execution.   #
#                                                                      #
########################################################################
function execute_reconf
{
	integer i
	integer count=0

	typeset reconfdir=$1
	typeset step_number=${2:-"?"}
	set +e

	#
	# check for the existence of the script file.
	#
	if [ ! -d ${reconfdir} ]; then
		#echo "No actions for this step"
		return 0
	fi
	#
	# Get  the complete listing og the actions to be 
	# to be executed for step N
	cd $reconfdir   
	CURDIR=${PWD}
	set -A reconf_prog $(/bin/ls ??_* 2>/dev/null)

	if [ ${#reconf_prog[*]} -eq 0 ]; then
		#echo "No actions for this step"
		return 0
	fi

	log_info "${pre}" \
		"ucmm reconfiguration step ${step_number} started"
	
	reconf_required=0

	# set the index to start of the array
	i=0
	while [ ! -z  ${reconf_prog[i]:-""} ]
	do
		prog=${reconf_prog[i]}
		#
		# extract sequence number and module name.
		#
		component=${prog#*_}
		cur_seqno=${prog%%_*}
		count=1
		current_reconf_prog=${reconf_prog[i]}
		i=i+1

		# collect the items with the same sequence number.
		while [ ! -z ${reconf_prog[i]:-""} ]
        	do
			prog=${reconf_prog[i]}
			seqno=${prog%%_*}
			component=${prog#*_}
			if [ ${cur_seqno} -eq ${seqno} ]
			then
				current_reconf_prog="${current_reconf_prog} ${reconf_prog[$i]}"
				count=count+1
			else
				# No more reconf programs with same seqno.
				break;
			fi
			i=i+1
		done

		# Now execute the reconf programs.
		if [ ${count} -eq 1 ] 
		then
			component=${current_reconf_prog#*_}
			lmsg="%s %s %s started in %s"
			printf "${lmsg}\n" "$(/bin/date)" "${pre}" \
				"${component}" "${CURRSTEP}"
			eval ${CURDIR}/${current_reconf_prog}
			error=$?
			check_execution_error ${component} ${error}
		else
			# Multiple reconfiguration programs to be 
			# executed.
			set -A execute_items ${current_reconf_prog}
			integer execute=0
			while [ ! -z ${execute_items[execute]:-""} ]
			do 
				prog=${execute_items[execute]}
				component=${prog#*_}
				# execute the items
				/bin/rm -rf ${tmpdir}/${component}.${CURRSTEP}
				lmsg="%s %s %s started in %s"
				printf "${lmsg}\n" "$(/bin/date)" "${pre}" \
					"${component}" "${CURRSTEP}"
				(eval ${CURDIR}/${prog} ||  \
					echo $? > ${tmpdir}/${component}.${CURRSTEP}) &
				execute=execute+1
			done   
			# wait for the result
			wait
			# check for the results using the log file.
			execute=0
			while [ ! -z ${execute_items[execute]:-""} ]
			do 
				prog=${execute_items[execute]}
				component=${prog#*_}
				error=0
				if [ -f ${tmpdir}/${component}.${CURRSTEP} ]
				then
					error=$(cat ${tmpdir}/${component}.${CURRSTEP})
				fi
				check_execution_error ${component} ${error}
				execute=execute+1
			done
		fi
		# 
		# if reconfiguration was required by the component
		# then do exeuctue any more components in the step
		# and exit with RECONF_REQUIRED_ERR;
		#
		if [ ${reconf_required} -eq 1 ]; then
			log_info ${pre} \
				"ucmm reconfiguration step ${step_number} completed"
			exit ${RECONF_REQUIRED_ERR};
		fi

	done
	
	log_info "${pre}" \
		"ucmm reconfiguration step ${step_number} completed"

	return 0
}

############################################################
#  Handle All CMM-Transactions requested by ucmmd          #
############################################################

########################################################################
# called from cluster membership monitor "stop" transition
########################################################################
function cmmstop_cmd
{
	export CURRSTEP=cmmstop

	validate_env_vars

	# Handle RcK.d Trasactions
	execute_reconf ${RECONF_DIR}/rcK.d/ stop
}

########################################################################
# called from cluster membership monitor "abort" transition
########################################################################
function cmmabort_cmd
{
	export CURRSTEP=cmmabort

	validate_env_vars

	# Handle RcA.d Trasactions
	execute_reconf ${RECONF_DIR}/rcA.d/ abort
}

function cmmreturn_cmd
{
	validate_env_vars

	# Handle rcR.d
	execute_reconf ${RECONF_DIR}/rcR.d/ return

}

function cmmstart_cmd
{
	/bin/rm -rf ${reconf_error_file}
	/bin/rm -rf ${ucmmd_error_file}
	/bin/rm -rf ${shutdown_file}

	validate_env_vars

	# Handle rcS.d transactions
	execute_reconf ${RECONF_DIR}/rcS.d/ start
}

function common_cmmstep
{

	step=$1
	CMMPREFIX=cmmstep
	#
	# Extract "number"
	#
	step_number=${step##$CMMPREFIX}
	#
	# Extract the number of Steps for CDB
	#
	validate_env_vars

	reconfdir=${RECONF_DIR}/rc${step_number}.d/
	execute_reconf ${reconfdir} ${step_number}

	if [ ${step_number} == "10" ]; then
		set_framework_status OK ""
	fi	
}

########################################################################
# called from initucmm to validate state of various modules
# If any module fails validation, exit status is non 0
#
# If any reconfiguration step had failed previously with error code 
# other than 0 or 205, errors are stored in reconf_error_file
# If this file contains errors, then validate method logs messages
# and returns non zero error code.
# 
########################################################################
function validate_cmd
{

	typeset rc=0
	typeset a_file=""
	typeset a_line=""
	typeset error_text=""
	typeset shutdown_detected=0

	log_info "${pre}" "Step: %s" "${CURRSTEP}"

	# Handle validate option
	execute_reconf ${RECONF_DIR}/rcS.d/ validate

	for a_file in  ${reconf_error_file} ${ucmmd_error_file}; do

		if [ ! -f "${a_file}" ]; then
			continue
		fi

		if [ -f "${shutdown_file}" ]; then
			log_info "${pre}" \
			    "Ignoring error detected in previous reconfiguration due to shutdown"
			shutdown_detected=1
		fi

		# error file exists and it's size is non zero

		error_text="$(/bin/cat ${a_file})"
		print "${error_text}" | while read a_line; do

			if [ ${shutdown_detected} -eq 0 ]; then
				scds_syslog -p error -t ${pre} -m \
				    "Error was detected in previous reconfiguration: \"%s\"" \
				    "${a_line}"
			fi

			log_info "${pre}" \
			    "Error was detected in previous reconfiguration: \"%s\"" \
			    "${a_line}"

			[ -z "${a_line}" ] && continue

			if [ ${shutdown_detected} -eq 0 ]; then
				rc=1
			fi

		done
	done

	# Remove reconfiguration error file and ucmmd error file
	/bin/rm -rf ${reconf_error_file}
	/bin/rm -rf ${ucmmd_error_file}
	/bin/rm -rf ${shutdown_file}

	if [ $rc -ne 0 ]; then	
		scds_syslog -p error -t ${pre} -m \
		    "The ucmmd daemon will not be started due to errors in previous reconfiguration."

		set_framework_status FAULTED "Error in previous reconfiguration."

		exit 1
	fi

	return 0
}


#############################################################
#  set_framework_status()
#
#       Parameter 1: <status> (not validated)
#                OK,  DEGRADED,  FAULTED, UNKNOWN, or OFFLINE.
#       Parameter 2: <message>
#       Parameter 3: <rs_name> (optional)
#		If Resource name is not specified, 
#		Framework resource name is obtained using 
#		scha_* calls
#
#	Set the resource status in background
#############################################################
set_framework_status()
{
    (
	typeset rs_status="${1:-UNKNOWN}"
	typeset msg=${2:-""}
	typeset rs_name=""
	typeset group=""

	typeset conf_file=/usr/cluster/lib/ucmm/rt/rac_framework/etc/rac_framework.conf
	typeset framework_rt="SUNW.rac_framework:*";
        typeset rs_name_key="rac_framework.resource_name";
        typeset rg_name_key="rac_framework.resource_group_name";

	rs_name=$(${CLUSTERBIN}/cfgmatch ${rs_name_key} ${conf_file});

	if [ -z "${rs_name}" ]; then
		# resource is not configured.
		# Entry not found in conf file
		# Don't update status
		return 0;
	fi

	group=$(${CLUSTERBIN}/cfgmatch ${rg_name_key} ${conf_file});

	if [ -z "${group}" ]; then
		# Cannot find group name
		# Don't update status
		return 0;
	fi

	$SCHA_RS_SETSTATUS -R ${rs_name} -G ${group} -s ${rs_status} -m "${msg}"

  	return 0;
    ) &

}


########################################################################
# This function is called when ucmm_reconf script is invoked from 
# initucmm script.
# If RAC RG has been configured, this function exits 0
# If RAC RG has not been configured, this function exits 1
#
########################################################################
function verify_rac_rg_cmd
{
	rac_rg=$(get_rac_rg)

	if [ -z "${rac_rg}" ]; then
		log_info "${pre}" "Step: %s RAC Resource group not configured on cluster." "${CURRSTEP}"
		exit 1
	fi

	log_info "${pre}" "Step: %s RAC Resource group %s configured on cluster." "${CURRSTEP}" "${rac_rg}"

	exit 0
}


#typeset -ft $(typeset +f)
#
#  End of All Functions 
#
# get program options
prog=$0
cmd=$1

export CURRSTEP=$cmd

init $*

#
# Dispatch the call.
#
# file descriptor #3 is the original stdout if you need to send
# messages to the interactive user.
#
case ${cmd} in
 
        # async commands - not interactive
	cmmstop)	cmmstop_cmd	3>&1 ;;

	cmmabort)	cmmabort_cmd	3>&1 ;;

	cmmstart | cmmreturn ) \
		eval ${cmd}_cmd  3>&1 ;;

	cmmstep[1-9]| cmmstep[0-9][0-9])
		common_cmmstep $cmd 3>&1 ;;

	verify_rac_rg)
		verify_rac_rg_cmd 3>&1 ;;

	validate)
		validate_cmd	3>&1 ;;

	*)	_usage ;;
esac
exit 0
