#!/bin/bash
#
# Description:  Manages a Squid Server provided by NTT OSSC as an 
#               OCF High-Availability resource under Heartbeat/LinuxHA control
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# Copyright (c) 2008 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
#
#######################################################################
# OCF parameters:
#   OCF_RESKEY_squid_exe    : Executable file
#   OCF_RESKEY_squid_conf   : Configuration file
#   OCF_RESKEY_squid_pidfile: Process id file
#   OCF_RESKEY_squid_port   : Port number
#   OCF_RESKEY_debug_mode   : Debug mode
#   OCF_RESKEY_debug_log    : Debug log file
#   OCF_RESKEY_squid_stop_timeout:
#                             Number of seconds to await to confirm a
#                             normal stop method
#
#   OCF_RESKEY_squid_exe, OCF_RESKEY_squid_conf, OCF_RESKEY_squid_pidfile
#   and OCF_RESKEY_squid_port must be specified. Each of the rests
#   has its default value or refers OCF_RESKEY_squid_conf to make
#   its value when no explicit value is given.
###############################################################################

: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs

usage() 
{
	cat <<-!
usage: $0 action

action:
        start       : start a new squid instance

        stop        : stop the running squid instance

        status      : return the status of squid, run or down

        monitor     : return TRUE if the squid appears to be working.

        meta-data   : show meta data message

        validate-all: validate the instance parameters
!
	return $OCF_ERR_ARGS
}

metadata_squid()
{
    cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="Squid">
<version>1.0</version>

<longdesc lang="en">
The resource agent of Squid.
This manages a Squid instance as an HA resource.
</longdesc>
<shortdesc lang="en">Manages a Squid proxy server instance</shortdesc>

<parameters>

<parameter name="squid_exe" required="1" unique="0">
<longdesc lang="en">
This is a required parameter. This parameter specifies squid's
executable file.
</longdesc>
<shortdesc>Executable file</shortdesc>
<content type="string" default=""/>
</parameter>

<parameter name="squid_conf" required="1" unique="1">
<longdesc lang="en">
This is a required parameter. This parameter specifies a configuration file
for a squid instance managed by this RA.
</longdesc>
<shortdesc>Configuration file</shortdesc>
<content type="string" default=""/>
</parameter>

<parameter name="squid_pidfile" required="1" unique="1">
<longdesc lang="en">
This is a required parameter. This parameter specifies a process id file
for a squid instance managed by this RA.
</longdesc>
<shortdesc>Pidfile</shortdesc>
<content type="string" default=""/>
</parameter>

<parameter name="squid_port" required="1" unique="1">
<longdesc lang="en">
This is a required parameter. This parameter specifies a port number
for a squid instance managed by this RA. If plural ports are used,
you must specifiy the only one of them.
</longdesc>
<shortdesc>Port number</shortdesc>
<content type="integer" default=""/>
</parameter>

<parameter name="squid_stop_timeout" unique="0">
<longdesc lang="en">
This is an omittable parameter.
On a stop action, a normal stop method is firstly used.
and then the confirmation of its completion iserr "$RESOURCE: Called $cmd"
		ocf_log err "$RESOURCE: Exit code $ret"
		ocf_log err "$RESOURCE: Command output: $cmd_out"
	else
		ocf_log debug "$RESOURCE: Exit code $ret"
		ocf_log debug "$RESOURCE: Command output: $cmd_out"
	fi
	
	echo $cmd_out
	
	return $ret
}

do_drbdadm() {
	local cmd="$DRBDADM -c $DRBDCONF $*"
	ocf_log debug "$RESOURCE: Calling $cmd"
	local cmd_out
	cmd_out=$($cmd 2>&1)
	ret=$?
	# Trim the garbage drbdadm likes to print when using the node
	# override feature:
	local cmd_ret=$(echo $cmd_out | sed -e 's/found __DRBD_NODE__.*<<//;')
	
	if [ $ret -ne 0 ]; then
		ocf_log err "$RESOURCE: Called $cmd"
		ocf_log err "$RESOURCE: Exit code $ret"
		ocf_log err "$RESOURCE: Command output: $cmd_ret"
	else
		ocf_log debug "$RESOURCE: Exit code $ret"
		ocf_log debug "$RESOURCE: Command output: $cmd_ret"
	fi
	
	echo $cmd_ret
	
	return $ret
}

drbd_init() {
	check_binary $DRBDADM
	CRM_MASTER="${HA_SBIN_DIR}/crm_master -l reboot "
	
	RESOURCE="$OCF_RESKEY_drbd_resource"
	CLONE_NO="$OCF_RESKEY_CRM_meta_clone"
	DRBDCONF="${OCF_RESKEY_drbdconf:=/etc/drbd.conf}"

	if [ ! -f "$DRBDCONF" ]; then
		ocf_log err "drbd.conf not installed."
		if [ "$ACTION" = 'monitor' ]; then
			exit $OCF_NOT_RUNNING
		else
			exit $OCF_ERR_INSTALLED
		fi
	fi

	case "$OCF_RESKEY_clone_overrides_hostname" in
	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
		__DRBD_NODE__="node_${CLONE_NO}"
		export __DRBD_NODE__
		ocf_log info "$RESOURCE: Using hostname $__DRBD_NODE__"
		;;
	esac

}


#######################################################################

drbd_usage() {
	cat <<END
usage: $0 {start|stop|monitor|validate-all|promote|demote|notify|meta-data}

Expects to have a fully populated OCF RA-compliant environment set.
END
}

is_drbd_enabled () {
	if [ -f /proc/drbd ]; then
		return 0
	fi
	return 1
}

get_drbd_ver() {
	# returns 2 for unsupported (not 0.7.x or < 8.x)
	# returns 1 for versions prior to 8.3.x
	# returns 0 otherwise
	drbdadm  | grep Version | awk '{print $2}' |
	awk -F. '
		{
			if( $1 == "0" ) # 0.7.x
				{ v1 = $2; v2 = $3; }
			else
				{ v1 = $1; v2 = $2; }
		}
		v1 < 7 { exit 2; }
		v1 == 7 || (v1 == 8 && v2 < 3) { exit 1; } # use state
		# otherwise use role
	'
}
get_status_cmd() {
	get_drbd_ver
	rc=$?
	if [ $rc -ge 2 ]; then
		ocf_log err "Cannot parse output of 'drbdadm | grep Version'"
		exit $OCF_ERR_GENERIC
	elif [ $rc -eq 1 ]; then
		echo state
	else
		echo role
	fi
}
drbd_get_status() {
	cmd=`get_status_cmd`
	DRBD_STATE=$(do_drbdadm "$cmd" $RESOURCE)
	DRBD_STATE_LOCAL=$(echo $DRBD_STATE | sed -e 's#/.*##')
	DRBD_STATE_REMOTE=$(echo $DRBD_STATE | sed -e 's#.*/##')
	DRBD_CSTATE=$(do_drbdadm cstate $RESOURCE)

	# Sanitize the various states, drbdadm is quite annoying; so if it
	# outputs something which doesn't make sense, translate it into
	# a harmless state:

	case "$DRBD_STATE_LOCAL" in
		"Not configured"|"Primary"|"Secondary") ;;
		*)	DRBD_STATE_LOCAL="Not configured" ;;
	esac

	case "$DRBD_STATE_REMOTE" in
		"Primary"|"Secondary"|"Unknown") ;;
		*)	DRBD_STATE_REMOTE="Not configured" ;;
	esac

	case "$DRBD_CSTATE" in
		Unconfigured|StandAlone|Unconnected|Timeout|BrokenPipe) ;;
		NetworkFailure|WFConnection|WFReportParams|Connected|SkippedSyncS) ;;
		SkippedSyncT|WFBitMapS|WFBitMapT|SyncSource|SyncTarget) ;;
		PausedSyncS|PausedSyncT) ;;
		*) DRBD_CSTATE="Unconfigured" ;;
	esac

	ocf_log debug "$RESOURCE status: $DRBD_STATE $DRBD_STATE_LOCAL $DRBD_STATE_REMOTE $DRBD_CSTATE"
}

drbd_start() {
	if is_drbd_enabled; then
	    : OK
	else
	    do_cmd modprobe -s drbd `$DRBDADM sh-mod-parms` || {
		    ocf_log err "Cannot load the drbd module."$'\n';
		    return $OCF_ERR_GENERIC
	    }
	    ocf_log debug "$RESOURCE start: Module loaded."
	fi

	drbd_get_status

	if [ "$DRBD_STATE_LOCAL" != "Not configured" ]; then
		ocf_log debug "$RESOURCE start: already configured."
		return $OCF_SUCCESS
	fi

	if do_drbdadm up $RESOURCE ; then
		drbd_get_status
		if [ "$DRBD_STATE_LOCAL" != "Secondary" ]; then
			ocf_log err "$RESOURCE start: not in Secondary mode after start."
			return $OCF_ERR_GENERIC
		fi

		ocf_log debug "$RESOURCE start: succeeded."
		return $OCF_SUCCESS
	else
		ocf_log err "$RESOURCE: Failed to start up."
		return $OCF_ERR_GENERIC
	fi
}

drbd_update_prefs() {
	drbd_get_status

	# TODO: This is probably way too complex.
	case $DRBD_CSTATE in
	Connected)
		do_cmd $CRM_MASTER -v 75
		;;
	SyncSource|PausedSyncS|WFBitMapS|SkippedSyncS)
		do_cmd $CRM_MASTER -v 100
		;;
        # TODO:
        # (Inconsistent || Diskless && WFConnection) should be -infinity
	# This one implies we'll try to promote even on disconnected
	# nodes, but that might not work.
	WFConnection)
		do_cmd $CRM_MASTER -v 10
		;;
	*)
		do_cmd $CRM_MASTER -v 5
		;;
	esac

	return $OCF_SUCCESS
}

drbd_stop() {
	# Do not bother if drbd is not enabled
	if is_drbd_enabled; then
		drbd_get_status
		
		# Clear preference for becoming master
		do_cmd $CRM_MASTER -D
	
		if [ "$DRBD_STATE_LOCAL" = "Not configured" ]; then
			ocf_log debug "$RESOURCE stop: already unconfigured."
			return $OCF_SUCCESS
		fi
		
		# TODO: this is a _force_ operation. we may need to kill higher
		# levels to be able to down drbd. figure out how...
		if do_drbdadm down $RESOURCE ; then
			ocf_log debug "$RESOURCE stop: drbdadm down succeeded."
			
			# TODO: If drbdadm propagated error codes, this
			# wouldn't be needed.
			drbd_get_status

			if [ "$DRBD_STATE_LOCAL" = "Not configured" ]; then
				return $OCF_SUCCESS
			else
				ocf_log err "$RESOURCE stop: Not stopped."
			fi
		else
			ocf_log err "$RESOURCE stop: Failed with exit code: $?"
		fi
		return $OCF_ERR_GENERIC
	else
		ocf_log debug "$RESOURCE stop: drbd not loaded."
	fi

	return $OCF_SUCCESS
}

drbd_monitor() {
	# TODO: Think about how to monitor drbd and what constitutes
	# failure cases...
	# diskless etc?
	# A secondary node which is supposed to be primary?

	# TODO: we ought to update the preferences here occasionally,
	# but that causes transitions right now ...

	if is_drbd_enabled; then
	    : OK
	else
	    ocf_log warn "$RESOURCE monitor: drbd module not loaded"
	    return $OCF_NOT_RUNNING
	fi

	drbd_get_status

	if [ "$DRBD_STATE_LOCAL" = "Not configured" ]; then
	    ocf_log debug "$RESOURCE monitor: resource not configured"
	    return $OCF_NOT_RUNNING
	elif [ "$DRBD_STATE_LOCAL" = "Primary" ]; then
		if [ "$OCF_RESKEY_CRM_meta_interval" -eq 0 ]; then
			# Restore the master setting during probes
			ocf_log debug "$RESOURCE monitor: restoring master setting during probe"
			drbd_update_prefs
		fi
	    return $OCF_RUNNING_MASTER
	elif [ "$DRBD_STATE_LOCAL" = "Secondary" ]; then
#	    drbd_update_prefs
	    return $OCF_SUCCESS
	else
	    ocf_log err "$RESOURCE mo