#! /usr/bin/ksh
#
#pragma	ident	"@(#)rac_lib.ksh	1.7	04/10/19 SMI"
#
# Copyright 2002-2004 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
############################################################ 
#
# This library provides functions that are commonly used
# by RAC Framework related resources.
#
# This library is used by callback methods of 
# SUNW.rac_framework, SUNW.rac_udlm and SUNWrac_cvm resource 
# types.
#
############################################################ 

set -ufp
unset PATH
export PATH="/bin:/usr/cluster/bin:/usr/cluster/lib/sc:/usr/cluster/lib/ucmm"

typeset -r SCLOGGER=/usr/cluster/lib/sc/scds_syslog
typeset -r CLUSTM=/usr/cluster/lib/ucmm/clustm
typeset -r HALOCKRUN=/usr/cluster/bin/halockrun

MYDIR=`/bin/dirname $0`
MYNAME=`/bin/basename $0`
DEBUG_LEVEL=-1
DEBUG_LOG_FILE="/dev/null"

ARGUMENTS="$0 ${@:-""}"
SYSLOG_TAG="SC[RAC_framwork]"

MY_RS=""
MY_RT=""
MY_RG=""

#print "Arguments: $ARGUMENTS"

#############################################################
# Include ucmm_reconf.common library
# This library has functions to get RAC resource names
# and RAC RG
#############################################################

. /usr/cluster/lib/ucmm/ucmm_reconf.common

#############################################################
#  write_stderr <formattext> <parameters> 
#
#  This functions writes output to stderr
#  Expected usage is from validate method.
#  
#############################################################

write_stderr()
{
	typeset msg_format=${1:-""}
	typeset msg

	shift

	msg=$(/usr/bin/printf "${msg_format}" ${@:-} )

    	print -r -u2 -- "$msg" 
}

#############################################################
#  write_trace <text> <parameters> 
#   Other information: TAG format 
#############################################################

write_trace()
{
	typeset msg_text=${1:-""}
	typeset msg

	shift

	msg=$(/usr/bin/printf "${msg_text}" ${@:-} )
	print -r -- "$(date '+%b %d %T') ${SYSLOG_TAG}: $msg" >> $DEBUG_LOGFILE
}

#############################################################
#  read_arguments()
#  
#  Set Globals variables:
#     MY_RS, MY_RG, MY_RT
#
#############################################################
read_arguments()
{

	typeset rc=0
	typeset invalid_option=""
	typeset save_opt=""
	typeset save_optarg=""

	while getopts :R:T:G: opt
	do
  	case $opt in
		R)     	MY_RS="$OPTARG";;
		T)     	MY_RT="$OPTARG";;
		G)     	MY_RG="$OPTARG";;
		*)     	invalid_option="YES"; 
			save_opt="$opt"
			save_optarg="$OPTARG"
		 	;;
	esac
	done

	if [ -z "$MY_RS" \
		-o -z "$MY_RG" \
		-o -z "$MY_RT"  \
		-o -n "$invalid_option" ]; then

		scds_syslog -p error -t ${SYSLOG_TAG} -m \
	 		"Initialization failed. Invalid command line  %s %s" \
			"$save_optarg" "$save_opt"
		rc=1;
	fi
	
	return $rc

}

#############################################################
# check_resource_setup
#
#############################################################
check_resource_setup()
{
	typeset rc=0

	if [ -z "$MY_RS" -o -z "$MY_RG" -o -z "$MY_RT" ]; then

		scds_syslog -p error -t ${SYSLOG_TAG} -m \
		    "Initialization failed. Invalid command line %s" \
			"$ARGUMENTS"
		rc=1;
	fi
	
	return $rc
}


#############################################################
# extract_property_value()
#
#  For extended properties, API returns data type followed by value
#  This function discards first line (data type) and prints 
#	remaining lines.
#
#############################################################
extract_property_value()
{
	typeset type
	typeset value
	typeset rc=0
	
	read type 
	rc=$?
	[ $rc -ne 0 ] && return $rc

	while read value
	do
		print $value
	done
		
	rc=$?
	return $rc
}

#############################################################
# read_extension_property()
# 
# This function reads extension property value of this resource
# [ -R <resource> ] [ -G <rg name> ] [ -n ] <property>
# 
#  If -R <resource> is not specified, MY_RS name is used
#  If -G <resource> is not specified, MY_RG name is used
#  -n To specify that error message was not logged in syslog
#     Default - error message is logged to syslog
#  <property> name of extension property
#############################################################
read_extension_property()
{

	typeset rc
	typeset RS_NAME="$MY_RS"
	typeset RG_NAME="$MY_RG"
	typeset property
	typeset log_flag="YES"

	#
	# Read optional arguments
	#

	while getopts :R:G:n opt
	do
  	case $opt in
		R)     	RS_NAME="$OPTARG";;
		G)     	RG_NAME="$OPTARG";;
		n)     	log_flag="NOLOG";;
		*)     	;;
	esac
	done

	shift `expr $OPTIND - 1`
	property="${1:-""}"

	scha_resource_get -O Extension \
		 -R "$RS_NAME" -G "$RG_NAME" "$property" \
		| extract_property_value

	rc=$?
	if [ $rc -ne 0 ]; then
                if [ "$log_flag" != "NOLOG" ]; then
			scds_syslog -p error -t ${SYSLOG_TAG} -m \
                	    "Error (%s) when reading extension property <%s>." "$rc" "${property}" 
		fi
	fi	
	return $rc
}

#############################################################
# read_standard_property_value
#
# This function reads property values of standard resource properties
#
#
#############################################################
read_standard_property_value()
{
	typeset rc=0
	typeset RS_NAME="$MY_RS"
	typeset RG_NAME="$MY_RG"
	typeset property
	typeset log_flag="YES"

	#
	# If resource of resource group name is specified, 
	# use that value
	while getopts :R:G:n opt
	do
  	case $opt in
		R)     	RS_NAME="$OPTARG";;
		G)     	RG_NAME="$OPTARG";;
		n)     	log_flag="NOLOG";;
		*)     	;;
	esac
	done

	# Shift parameters and remove optional parameters
	#

	shift `expr $OPTIND - 1`

	property="${1:-""}"
	
	scha_resource_get -O "$property" -R "$RS_NAME" -G "$RG_NAME"
	rc=$?
	if [ $rc -ne 0 ]; then
                if [ "$log_flag" != "NOLOG" ]; then
			scds_syslog -p error -t ${SYSLOG_TAG} -m \
                    	   "Error (%s) when reading standard property <%s>." "$rc" "${property}" 
		fi
	fi	
	return $rc
}

#############################################################
# set_status
#   Sets resource status
#	Parameter 1: <status> (not validated)
#		 OK,  DEGRADED,  FAULTED, UNKNOWN, or OFFLINE. 
#	Parameter 2: <message> (optional)
#############################################################
set_status()
{
	typeset rstatus="${1:-UNKNOWN}"
	typeset rmsg=${2:-""}

	if [ -n "${rmsg}" ]; then
		scha_resource_setstatus -s $rstatus -m "$rmsg" \
			-R $MY_RS -G $MY_RG
	else
		scha_resource_setstatus -s $rstatus \
			-R $MY_RS -G $MY_RG
	fi
}


#############################################################
# set_var()
#
#   Input : String of the type PARAM=VALUE
# 
#   This functions sets shell variable by looking at argument
#   passed to the function.
#
#############################################################
set_var()
{
	typeset -u param
	typeset val
	[ -z ${1:-""} ] && return

	val="${1#*=}"
	param="${1%%=*}"
	eval ${param}=\"${val}\"

	return;
}
#############################################################
# set_variables_from_args
#	Arguments:
#		Arguments passed to Validate/ Update method
#
# Property names and values are passed to teh methods as follows:
#	-r <name>=<value> - system defined resource properties
#	-x <name>=<value> - extension properties
#	-g <name>=<value> - group properties
#
#############################################################
set_variables_from_args()
{

     	while getopts ucR:T:G:r:x:g: opt
	do
	  case $opt in
		x|r|g)      set_var "$OPTARG";;
		*)      ;;
	  esac
     	done
}

#############################################################
# read_validate_arguments()
#
#
#  This function reads arguments passed to validate method 
#  and sets variables for all the argiuments passed on command 
#  line.
#
#  If operation is UPDATE operation, all the extension property
#  values are read in variable.
#
#  Also some of the RG properties requied in code are also read.
#
#############################################################
read_validate_arguments()
{

	typeset rc=0
	#
	# Convert extension property names to upper case
	#
	typeset -u ext_property
	typeset param
	typeset val

	typeset operation=""

     	while getopts ucR:T:G:r:x:g: opt
	do
	  case $opt in
		R)      MY_RS="$OPTARG";;
		T)      MY_RT="$OPTARG";;
		G)      MY_RG="$OPTARG";;
		c)      operation="VALIDATE";;
		u)      operation="UPDATE";;
		*)      ;;
	  esac
     	done

     	check_resource_setup || return $?

	if [ "${operation}" == "UPDATE" ]; then
		# This is update operation,
		# Read all extension properties of resource
		# and set variables
		scha_resource_get -O ALL_EXTENSIONS -R ${MY_RS} | \
		while read ext_property; do
			[ -z "${ext_property}" ] && continue
			val=$(read_extension_property  -R ${MY_RS} ${ext_property})
			param=${ext_property}
			eval ${param}=\"${val}\"
        	done

		# Read resourcegroup properties

		NODELIST=$(scha_resourcegroup_get -O NODELIST -G "$MY_RG")
		RG_MODE=$(scha_resourcegroup_get -O RG_MODE -G "$MY_RG")
		MAXIMUM_PRIMARIES=$(scha_resourcegroup_get -O MAXIMUM_PRIMARIES -G "$MY_RG")
		DESIRED_PRIMARIES=$(scha_resourcegroup_get -O DESIRED_PRIMARIES -G "$MY_RG")

	fi

	set_variables_from_args ${@:-}
	
	return $rc
}

#############################################################
#
# sync_conf_file 
#    Synchronize configuration file with resource properties
#
#	Param 1: Resource name
#	Param 2: template file name
#	Param 3: Output file name
#
# return 
#	0 - success
#	255 - Synchronization not required
#	1 - Error  in writing file
#
# Error messages will be logged from this routine in syslog
#
#############################################################
sync_conf_file()
{
	#
	# Run the follwoing code in sub shell so that 
	# variables set in this function will not affect
	# current shell
	(
	typeset rs_name=${1:-""}
	typeset template_file=${2:-""}
	typeset output_file=${3:-""}
	typeset rg_name=${4:-""}
	typeset var_prefix="PROPERTY"
	typeset file_cksum=0
	typeset new_cksum=0

	
	#
	# Convert extension property names to upper case
	#
	typeset -u ext_property
	typeset param
	typeset val

	resource_configured "$rs_name"
	if [ $? -ne 0 ]; then
		scds_syslog -p error -t ${SYSLOG_TAG} -m \
               	   "Resource (%s) not configured." "$rs_name" 
		return 1
	fi

	if [ ! -f "$template_file" ]; then
		scds_syslog -p error -t ${SYSLOG_TAG} -m \
               	   "Cannot read template file (%s)." "$template_file" 
		return 1
	fi

	#
	# export resource name
	#
	param=${var_prefix}_RESOURCE_NAME
	eval ${param}=\"${rs_name}\"
	export ${param}

	#
	# export resource group name
	#
	if [ -z "$rg_name" ]; then
		# Obtain RG name, if not passed
		rg_name=$($SCHA_RS_GET -O GROUP -R "${rs_name}")
	fi
	export RESOURCE_GROUP_NAME="${rg_name}"

	#
	# export all extension properties of resource
	#
	scha_resource_get -O ALL_EXTENSIONS -R ${rs_name} | \
	while read ext_property; do
		[ -z "${ext_property}" ] && continue
		val=$(read_extension_property  -R ${rs_name} ${ext_property})
		param=${var_prefix}_${ext_property}
		eval ${param}=\"${val}\"
		export ${param}
	done

	# Set cluster variables
	#
	#  CLUSTER_NAME
	#  CLUSTER_NODE_<n>_HAHOST
	#

	export CLUSTER_NAME=$(cfgmatch 'cluster.cluster_name' /dev/null)

	typeset -i i=0

	while (( i < 15 )); do
		param=CLUSTER_NODE_${i}_HAHOST
		val=$(cfgmatch cluster.node.${i}.hahost /dev/null)
		eval ${param}=\"${val}\"
		export ${param}

		(( i = i + 1 ))
	done


	# take checksum of output file  
	#
	if [ -f ${output_file} ]; then
		file_cksum=$(/bin/grep -v "^#" ${output_file} | /bin/cksum)
	else
		file_cksum=0
	fi

	# Write the outputfile under advisory lock so that 
	# programs that read configuration file using 'cfgmatch'
	# of cdbmatch function will not get inconsistent data
	#

	new_contents=$(
	# Write the heder with modification timestamp:

	print "# Modified by ${MYNAME:-"rac_lib"} on $(date)"

	while read a_line; do

		if [[ "$a_line" == \#* ]]; then
			#
			# Print comment lines unchanged
			#
                	print -r -- "${a_line}"
		else	

                	val="print -r -- \"$a_line\""
                	eval "$val"
		fi
	done < $template_file ) 

	new_cksum=$(echo "${new_contents}"  |/bin/grep -v "^#" | /bin/cksum)

	if [ "${file_cksum}" == "${new_cksum}" ]; then
		scds_syslog -p info -t ${SYSLOG_TAG} -m \
               	    "Configuration file %s synchronization not required" \
		    "$output_file"

		return 255
	fi

	#
	# halockrun program will get lock on file before invoking 'cat' to copy file.
	# This is to ensure that any readder of the configuration file
	# will not get inconsistent data.
	#
	
	echo "${new_contents}" | \
		$HALOCKRUN $output_file /usr/bin/cat > $output_file 
		
	scds_syslog -p info -t ${SYSLOG_TAG} -m \
	    "Configuration file %s written from template file %s." \
	    "$output_file" "$template_file" 

	return 0
	)
}
###################################################
# ucmmd_running
# 
#	returns code
#		0 - ucmmd is running
#		1 - ucmmd is not running
###################################################
ucmmd_running()
{
	typeset UCMMD_NAME=ucmmd
	typeset pidlist

	pidlist=$(/usr/bin/pgrep -u 0 ${UCMMD_NAME})
	if [ $? -ne 0 ] ; then
		return 1
	fi

	return 0
	
}
###################################################
# ucmmd_sighup
#
#	Send SIGHUP to ucmmd
###################################################
ucmmd_sighup()
{


	typeset UCMMD_NAME=ucmmd

	/usr/bin/pkill -HUP -u 0 ${UCMMD_NAME}
	if [ $? -ne 0 ] ; then
		return
	fi
	
	scds_syslog -p info -t ${SYSLOG_TAG} -m \
		"Sent HUP signal to ucmmd"

	return 0

}
###################################################
# stop_method_message
#
# Write message to syslog and update resource status 
# indicating framework is not be stopped.
###################################################
stop_method_message()
{

	#
	#  If ucmmd is not running, do not give any message
	#

	ucmmd_running || return 0
	
	scds_syslog -p notice -t ${SYSLOG_TAG} -m \
 		"Resource state of %s is changed to offline. \
Note that RAC framework will not be stopped by STOP method." "${MY_RS}" 

	set_status UNKNOWN "RAC framework is running"

	return 0
}

###################################################
# check_rac_framework_packages
#
#	This function validates RAC framework packages
#	If required packages are not installed
#	on the node, then error message is logged
#
#	returns code
#		0 - Package check passed
#		1 - error in package check
#
###################################################
check_rac_framework_packages()
{

	# on Solaris x86, Oracle UDLM is not available.
	# SUNWudlm package is not required.
	# Skipping the check for x86

	if [[ "$(uname -p)" = "i386" ]]; then
		return 0
	fi

	#
	# SUNWscucm SUNWudlm and SUNWudlmr packages 
	# must be installed on this node
	# This library is installed as a part of SUNWscucm
	# Hence SUNWscucm package is not checked
	#

	package_installed SUNWudlm || return 1
	package_installed SUNWudlmr || return 1

	return 0
}

###################################################
# check_storage_components
#
#	This function performs checks related to 
#	storage component of RAC framework.
#	Currently validations are done for the following
#	components:
#		CVM
#		Hardware RAID
#
# 	if incompatible packages are present on the node 
#	(e.g. SUNWcvmr and SUNWschwr together) then error
#	message is logged
#
#	returns code
#		0 - Package check passed
#		1 - error in package check
#
###################################################
check_storage_components()
{
	typeset sunwcvm_installed=0
	typeset sunwcvmr_installed=0
	typeset sunwschwr_installed=0

	# If SUNWcvmr package in installed, SUNWcvm must
	# also be installed.
	# SUNWcvmr and SUNWschwr are mutually exclusive
	# Both of these packages should not exist at 
	# a time

	/usr/bin/pkginfo -q SUNWcvmr
	sunwcvmr_installed=$?

	/usr/bin/pkginfo -q SUNWcvm
	sunwcvm_installed=$?

	/usr/bin/pkginfo -q SUNWschwr
	sunwschwr_installed=$?

	if [ ${sunwcvmr_installed} -eq 0 -a ${sunwschwr_installed} -eq 0 ]; then

		scds_syslog -p error -t ${SYSLOG_TAG} -m \
 		"Invalid configuration. Both SUNWcvmr and SUNWschwr packages \
are installed on this node. Only one of these package must be installed." 

		write_stderr "$(gettext \
 		"Invalid configuration. Both SUNWcvmr and SUNWschwr packages \
are installed on this node. Only one of these package must be installed." )"

		return 1
	fi

	if [ ${sunwcvmr_installed} -eq 0 -a ${sunwcvm_installed} -eq 1 ]; then
		scds_syslog -p error -t ${SYSLOG_TAG} -m \
 		"Invalid configuration. SUNWcvmr and SUNWcvm packages \
must be installed on this node when using Veritas Volume Manager \
for shared disk groups." 

		write_stderr "$(gettext \
 		   "Invalid configuration. SUNWcvmr and SUNWcvm packages \
must be installed on this node when using Veritas Volume Manager \
with Oracle RAC." )"

		return 1
	fi
	return 0
}


###############################################################
# package_installed
# 
#  Checks is package is installed. If package is not installed
#  syslog message is logged
#
#	returns code
#		0 - Package is installed
#		1 - Package is not installed
#
###############################################################
package_installed()
{
	typeset pkg="${1:-}"

	/usr/bin/pkginfo -q "${pkg}"
	if [ $? -ne 0 ]; then
		scds_syslog -p error -t ${SYSLOG_TAG} -m \
 		    "Required package %s is not installed on this node." \
		    "${pkg}" 

		write_stderr "$(gettext \
 		    "Required package %s is not installed on this node.")" \
		    "${pkg}" 

		return 1
	fi

	return 0
}

###############################################################
# validate_rg_properties
# 
#  RAC Framework resources must be created in the RG that is scalable
#  and has <desired_primaries> = <maximum_primaries> =<nodes in nodelist>
#  
#  If attempt is made to change RG properties that do not satisfy above
#  condition, error will be give.
#
#  returns code
#		0 - success
#		1 - failure
#
###############################################################
validate_rg_properties()
{

	[ -z "${NODELIST:-}" ] && \
		NODELIST=$(scha_resourcegroup_get -O NODELIST -G "$MY_RG")

	[ -z "${RG_MODE:-}" ] && \
		RG_MODE=$(scha_resourcegroup_get -O RG_MODE -G "$MY_RG")

	[ -z "${MAXIMUM_PRIMARIES:-}" ] && \
		MAXIMUM_PRIMARIES=$(scha_resourcegroup_get -O MAXIMUM_PRIMARIES -G "$MY_RG")

	[ -z "${DESIRED_PRIMARIES:-}" ] && \
		DESIRED_PRIMARIES=$(scha_resourcegroup_get -O DESIRED_PRIMARIES -G "$MY_RG")


	if [ "${RG_MODE}" != "Scalable" ]; then
		scds_syslog -p error -t ${SYSLOG_TAG} -m \
 		    "Incorrect resource group properties for RAC framework \
resource group %s. Value of RG_mode must be Scalable." "$MY_RG"

		write_stderr "$(gettext \
 		    "Incorrect resource group properties for RAC framework \
resource group %s. Value of RG_mode must be Scalable." )" "$MY_RG"

		return 1

	fi


	if [ "${DESIRED_PRIMARIES}" -ne "${MAXIMUM_PRIMARIES}" ]; then

		scds_syslog -p error -t ${SYSLOG_TAG} -m \
 		    "Incorrect resource group properties for RAC framework \
resource group %s. Value of Maximum_primaries(%s) is not equal to \
Desired_primaries(%s)."  \
		    "$MY_RG" "${MAXIMUM_PRIMARIES:-}" "${DESIRED_PRIMARIES:-}" 

		write_stderr "$(gettext \
 		    "Incorrect resource group properties for RAC framework \
resource group %s. Value of Maximum_primaries(%s) is not equal to \
Desired_primaries(%s)."  )" \
		    "$MY_RG" "${MAXIMUM_PRIMARIES:-}" "${DESIRED_PRIMARIES:-}" 


		return 1
	fi

	typeset -i nodecount
	nodecount=$(print "$NODELIST" | sed 's/,/ /g' | wc -w)

	if [ "${MAXIMUM_PRIMARIES}" -ne "${nodecount}" ]; then

		scds_syslog -p error -t ${SYSLOG_TAG} -m \
 		    "Incorrect resource group properties for RAC framework \
resource group %s. Value of Maximum_primaries(%s) is not equal to \
number of nodes in nodelist(%s)."  \
		    "$MY_RG" "${MAXIMUM_PRIMARIES:-}" "${nodecount:-}" 

		write_stderr "$(gettext \
 		    "Incorrect resource group properties for RAC framework \
resource group %s. Value of Maximum_primaries(%s) is not equal to \
number of nodes in nodelist(%s)." )" \
		    "$MY_RG" "${MAXIMUM_PRIMARIES:-}" "${nodecount:-}" 

		return 1
	fi

	return 0
}

###############################################################
#
# verify_installation()
#
#  This function verifies installation
#  It checsk that required packages exist on this node and 
#  RAC Framework RG has correct property values
#
#  returns code
#		0 - success
#		1 - failure
#
###############################################################
verify_installation()
{
	check_rac_framework_packages || return 1

	check_storage_components || return 1

	validate_rg_properties || return 1
}
