1b4de21
#!/bin/bash
1b4de21
#
1b4de21
#
1b4de21
#		OCF Resource Agent compliant drbd resource script.
1b4de21
#
1b4de21
# Copyright (c) 2009 LINBIT HA-Solutions GmbH,
1b4de21
# Copyright (c) 2009 Florian Haas, Lars Ellenberg
1b4de21
# Based on the Heartbeat drbd OCF Resource Agent by Lars Marowsky-Bree
1b4de21
# (though it turned out to be an almost complete rewrite)
1b4de21
#
1b4de21
#					 All Rights Reserved.
1b4de21
#
1b4de21
# This program is free software; you can redistribute it and/or modify
1b4de21
# it under the terms of version 2 of the GNU General Public License as
1b4de21
# published by the Free Software Foundation.
1b4de21
#
1b4de21
# This program is distributed in the hope that it would be useful, but
1b4de21
# WITHOUT ANY WARRANTY; without even the implied warranty of
1b4de21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1b4de21
#
1b4de21
# Further, this software is distributed without any warranty that it is
1b4de21
# free of the rightful claim of any third person regarding infringement
1b4de21
# or the like.	Any license provided herein, whether implied or
1b4de21
# otherwise, applies only to this software file.  Patent licenses, if
1b4de21
# any, provided herein do not apply to combinations of this program with
1b4de21
# other software, or any other product whatsoever.
1b4de21
#
1b4de21
# You should have received a copy of the GNU General Public License
1b4de21
# along with this program; if not, write the Free Software Foundation,
1b4de21
# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
1b4de21
#
1b4de21
#
1b4de21
1b4de21
# OCF instance parameters
1b4de21
#	OCF_RESKEY_drbd_resource
1b4de21
#	OCF_RESKEY_drbdconf
1b4de21
#	OCF_RESKEY_stop_outdates_secondary
1b4de21
#	OCF_RESKEY_adjust_master_score
1b4de21
#
1b4de21
# meta stuff this agent looks at:
1b4de21
#	OCF_RESKEY_CRM_meta_clone_max
1b4de21
#	OCF_RESKEY_CRM_meta_clone_node_max
1b4de21
#	OCF_RESKEY_CRM_meta_master_max
1b4de21
#	OCF_RESKEY_CRM_meta_master_node_max
1b4de21
#
1b4de21
#	OCF_RESKEY_CRM_meta_interval
1b4de21
#
1b4de21
#	OCF_RESKEY_CRM_meta_notify
1b4de21
#	OCF_RESKEY_CRM_meta_notify_active_uname
1b4de21
#	OCF_RESKEY_CRM_meta_notify_demote_uname
1b4de21
#	OCF_RESKEY_CRM_meta_notify_master_uname
1b4de21
#	OCF_RESKEY_CRM_meta_notify_operation
1b4de21
#	OCF_RESKEY_CRM_meta_notify_promote_uname
1b4de21
#	OCF_RESKEY_CRM_meta_notify_slave_uname
1b4de21
#	OCF_RESKEY_CRM_meta_notify_start_uname
1b4de21
#	OCF_RESKEY_CRM_meta_notify_stop_uname
1b4de21
#	OCF_RESKEY_CRM_meta_notify_type
1b4de21
#
1b4de21
1b4de21
#######################################################################
1b4de21
# Initialization:
1b4de21
1b4de21
# Resource-agents have moved their ocf-shellfuncs file around.
1b4de21
# There are supposed to be symlinks or wrapper files in the old location,
1b4de21
# pointing to the new one, but people seem to get it wrong all the time.
1b4de21
# Try several locations.
1b4de21
1b4de21
if test -n "${OCF_FUNCTIONS_DIR}" ; then
1b4de21
	if test -e "${OCF_FUNCTIONS_DIR}/ocf-shellfuncs" ; then
1b4de21
		. "${OCF_FUNCTIONS_DIR}/ocf-shellfuncs"
1b4de21
	elif test -e "${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs" ; then
1b4de21
		. "${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs"
1b4de21
	fi
1b4de21
else
1b4de21
	if test -e "${OCF_ROOT}/lib/heartbeat/ocf-shellfuncs" ; then
1b4de21
		. "${OCF_ROOT}/lib/heartbeat/ocf-shellfuncs"
1b4de21
	elif test -e "${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs"; then
1b4de21
		. "${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs"
1b4de21
	fi
1b4de21
fi
1b4de21
1b4de21
# Defaults
1b4de21
OCF_RESKEY_drbdconf_default="/etc/drbd.conf"
1b4de21
1b4de21
# The passed in OCF_CRM_meta_notify_* environment
1b4de21
# is not reliably with pacemaker up to at least
1b4de21
# 1.0.10 and 1.1.4. It should be fixed later.
1b4de21
# Until that is fixed, the "self-outdating feature" would base its actions on
1b4de21
# wrong information, and possibly not outdate when it should, or, even worse,
1b4de21
# outdate the last remaining valid copy.
1b4de21
# Disable.
1b4de21
OCF_RESKEY_stop_outdates_secondary_default="false"
1b4de21
1b4de21
  OCF_RESKEY_adjust_master_score_default="5 10 1000 10000"
1b4de21
#   ignored  |  Consistent    |  Unknown -'  |   |     |
1b4de21
#   ignored  |  NOT UpToDate  |  UpToDate ---'   |     |
1b4de21
# Secondary  |  UpToDate      |  unknown --------'     |
1b4de21
#   ignored  |  UpToDate      |  known   --------------+
1b4de21
#   Primary  |  UpToDate      |  ignored --------------'
1b4de21
1b4de21
: ${OCF_RESKEY_drbdconf:=${OCF_RESKEY_drbdconf_default}}
1b4de21
: ${OCF_RESKEY_stop_outdates_secondary:=${OCF_RESKEY_stop_outdates_secondary_default}}
1b4de21
: ${OCF_RESKEY_adjust_master_score:=${OCF_RESKEY_adjust_master_score_default}}
1b4de21
1b4de21
# Defaults according to "Configuration 1.0 Explained",
1b4de21
# "Multi-state resource configuration options"
1b4de21
: ${OCF_RESKEY_CRM_meta_clone_node_max=1}
1b4de21
: ${OCF_RESKEY_CRM_meta_master_max=1}
1b4de21
: ${OCF_RESKEY_CRM_meta_master_node_max=1}
1b4de21
#######################################################################
1b4de21
# for debugging this RA
1b4de21
DEBUG_LOG_DIR=/tmp/drbd.ocf.ra.debug
1b4de21
DEBUG_LOG=$DEBUG_LOG_DIR/log
1b4de21
USE_DEBUG_LOG=false
1b4de21
ls_stat_is_dir_0700_root() {
1b4de21
	set -- $(command ls -ldn "$1" 2>/dev/null);
1b4de21
	case "$1/$3" in
1b4de21
	drwx?-??-?/0|\
1b4de21
	drwx?-??-?./0)	true ;;
1b4de21
	*)	false ;;
1b4de21
	esac
1b4de21
}
1b4de21
# try to avoid symlink vuln.
1b4de21
if ls_stat_is_dir_0700_root $DEBUG_LOG_DIR &&
1b4de21
   [[ -w "$DEBUG_LOG" && ! -L "$DEBUG_LOG" ]]
1b4de21
then
1b4de21
	USE_DEBUG_LOG=true
1b4de21
	exec 9>>"$DEBUG_LOG"
1b4de21
	date >&9
1b4de21
	echo "$*" >&9
1b4de21
	env | grep OCF_ | sort >&9
1b4de21
else
1b4de21
	exec 9>/dev/null
1b4de21
fi
1b4de21
# end of debugging aid
1b4de21
#######################################################################
1b4de21
1b4de21
meta_data() {
1b4de21
	cat <
1b4de21
1b4de21
1b4de21
<resource-agent name="drbd">
1b4de21
<version>1.3</version>
1b4de21
1b4de21
<longdesc lang="en">
1b4de21
This resource agent manages a DRBD resource as a master/slave resource.
1b4de21
DRBD is a shared-nothing replicated storage device.
1b4de21
Note that you should configure resource level fencing in DRBD,
1b4de21
this cannot be done from this resource agent.
1b4de21
See the DRBD User's Guide for more information.
1b4de21
http://www.drbd.org/docs/applications/
1b4de21
</longdesc>
1b4de21
1b4de21
<shortdesc lang="en">Manages a DRBD device as a Master/Slave resource</shortdesc>
1b4de21
1b4de21
<parameters>
1b4de21
<parameter name="drbd_resource" unique="1" required="1">
1b4de21
<longdesc lang="en">
1b4de21
The name of the drbd resource from the drbd.conf file.
1b4de21
</longdesc>
1b4de21
<shortdesc lang="en">drbd resource name</shortdesc>
1b4de21
<content type="string"/>
1b4de21
</parameter>
1b4de21
1b4de21
<parameter name="drbdconf">
1b4de21
<longdesc lang="en">
1b4de21
Full path to the drbd.conf file.
1b4de21
</longdesc>
1b4de21
<shortdesc lang="en">Path to drbd.conf</shortdesc>
1b4de21
<content type="string" default="${OCF_RESKEY_drbdconf_default}"/>
1b4de21
</parameter>
1b4de21
1b4de21
<parameter name="adjust_master_score">
1b4de21
<longdesc lang="en">
1b4de21
Space separated list of four master score adjustments for different scenarios:
1b4de21
 - only access to 'consistent' data
1b4de21
 - only remote access to 'uptodate' data
1b4de21
 - currently Secondary, local access to 'uptodate' data, but remote is unknown
1b4de21
 - local access to 'uptodate' data, and currently Primary or remote is known
1b4de21
1b4de21
Numeric values are expected to be non-decreasing.
1b4de21
1b4de21
Default are the previously hardcoded values.
1b4de21
1b4de21
Set the first value to 0 (and configure proper fencing methods)
1b4de21
to prevent pacemaker from trying to promote while it is unclear
1b4de21
whether the data is really the most recent copy.
1b4de21
(DRBD knows it is "consistent", but is unsure about "uptodate"ness).
1b4de21
1b4de21
Advanced use: Adjust the other values to better fit into complex
1b4de21
dependency score calculations.
1b4de21
</longdesc>
1b4de21
<shortdesc lang="en">master score adjustments</shortdesc>
1b4de21
<content type="string" default="${OCF_RESKEY_adjust_master_score_default}"/>
1b4de21
</parameter>
1b4de21
1b4de21
<parameter name="stop_outdates_secondary">
1b4de21
<longdesc lang="en">
1b4de21
Recommended setting: leave at default (disabled).
1b4de21
1b4de21
Note that this feature depends on the passed in information in
1b4de21
OCF_RESKEY_CRM_meta_notify_master_uname to be correct, which unfortunately is
1b4de21
not reliable for pacemaker versions up to at least 1.0.10 / 1.1.4.
1b4de21
1b4de21
If a Secondary is stopped (unconfigured), it may be marked as outdated in the
1b4de21
drbd meta data, if we know there is still a Primary running in the cluster.
1b4de21
Note that this does not affect fencing policies set in drbd config,
1b4de21
but is an additional safety feature of this resource agent only.
1b4de21
You can enable this behaviour by setting the parameter to true.
1b4de21
1b4de21
If this feature seems to not do what you expect, make sure you have defined
1b4de21
fencing policies in the drbd configuration as well.
1b4de21
</longdesc>
1b4de21
<shortdesc lang="en">outdate a secondary on stop</shortdesc>
1b4de21
<content type="boolean" default="${OCF_RESKEY_stop_outdates_secondary_default}"/>
1b4de21
</parameter>
1b4de21
</parameters>
1b4de21
1b4de21
<actions>
1b4de21
<action name="start"   timeout="240" />
1b4de21
<action name="promote"	 timeout="90" />
1b4de21
<action name="demote"	timeout="90" />
1b4de21
<action name="notify"	timeout="90" />
1b4de21
<action name="stop"    timeout="100" />
1b4de21
<action name="monitor" depth="0"  timeout="20" interval="20" role="Slave" />
1b4de21
<action name="monitor" depth="0"  timeout="20" interval="10" role="Master" />
1b4de21
<action name="meta-data"  timeout="5" />
1b4de21
<action name="validate-all"  timeout="30" />
1b4de21
</actions>
1b4de21
</resource-agent>
1b4de21
END
1b4de21
}
1b4de21
1b4de21
do_cmd() {
1b4de21
	# Run a command, return its exit code, capture any output, and log
1b4de21
	# everything if appropriate.
1b4de21
	local cmd="$*" cmd_out ret
1b4de21
	ocf_log debug "$DRBD_RESOURCE: Calling $cmd"
1b4de21
	cmd_out=$( "$@" )
1b4de21
	ret=$?
1b4de21
1b4de21
	if [ $ret != 0 ]; then
1b4de21
		ocf_log err "$DRBD_RESOURCE: Called $cmd"
1b4de21
		ocf_log err "$DRBD_RESOURCE: Exit code $ret"
1b4de21
		ocf_log err "$DRBD_RESOURCE: Command output: $cmd_out"
1b4de21
	else
1b4de21
		ocf_log debug "$DRBD_RESOURCE: Exit code $ret"
1b4de21
		ocf_log debug "$DRBD_RESOURCE: Command output: $cmd_out"
1b4de21
	fi
1b4de21
1b4de21
	echo "$cmd_out"
1b4de21
1b4de21
	return $ret
1b4de21
}
1b4de21
1b4de21
do_drbdadm() {
1b4de21
	local ret
1b4de21
	# Run drbdadm with appropriate command line options, and capture
1b4de21
	# its output.
1b4de21
	# $DRBDADM is defined during drbd_validate as "drbdadm" plus
1b4de21
	# appropriate command line options
1b4de21
	do_cmd $DRBDADM "$@"
1b4de21
	ret=$?
1b4de21
1b4de21
	# having the version mismatch warning once per RA invokation
1b4de21
	# should be enough.
1b4de21
	export DRBD_DONT_WARN_ON_VERSION_MISMATCH=
1b4de21
1b4de21
	return $ret
1b4de21
}
1b4de21
1b4de21
set_master_score() {
1b4de21
	# Use quiet mode (-Q) to quench logging. Actual score updates
1b4de21
	# will get logged by attrd anyway
1b4de21
	if [[ $1 -le 0 ]]; then
1b4de21
		remove_master_score
1b4de21
	else
1b4de21
		do_cmd ${HA_SBIN_DIR}/crm_master -Q -l reboot -v $1
1b4de21
	fi
1b4de21
}
1b4de21
1b4de21
remove_master_score() {
1b4de21
	do_cmd ${HA_SBIN_DIR}/crm_master -l reboot -D
1b4de21
}
1b4de21
1b4de21
_sh_status_process() {
1b4de21
	# _volume not present should not happen,
1b4de21
	# but may help make this agent work even if it talks to drbd 8.3.
1b4de21
	: ${_volume:=0}
1b4de21
	# not-yet-created volumes are reported as -1
1b4de21
	(( _volume >= 0 )) || _volume=$[1 << 16]
1b4de21
	DRBD_ROLE_LOCAL[$_volume]=${_role:-Unconfigured}
1b4de21
	DRBD_ROLE_REMOTE[$_volume]=${_peer:-Unknown}
1b4de21
	DRBD_CSTATE[$_volume]=$_cstate
1b4de21
	DRBD_DSTATE_LOCAL[$_volume]=${_disk:-Unconfigured}
1b4de21
	DRBD_DSTATE_REMOTE[$_volume]=${_pdsk:-DUnknown}
1b4de21
}
1b4de21
drbd_set_status_variables() {
1b4de21
	# drbdsetup sh-status prints these values to stdout,
1b4de21
	# and then prints _sh_status_process.
1b4de21
	#
1b4de21
	# if we eval that, we do not need several drbdadm/drbdsetup commands
1b4de21
	# to figure out the various aspects of the state.
1b4de21
	local _minor _res_name _known _cstate _role _peer _disk _pdsk
1b4de21
	local _volume
1b4de21
	local _flags_susp _flags_aftr_isp _flags_peer_isp _flags_user_isp
1b4de21
	local _resynced_percent
1b4de21
1b4de21
	DRBD_ROLE_LOCAL=()
1b4de21
	DRBD_ROLE_REMOTE=()
1b4de21
	DRBD_CSTATE=()
1b4de21
	DRBD_DSTATE_LOCAL=()
1b4de21
	DRBD_DSTATE_REMOTE=()
1b4de21
1b4de21
	if $DRBD_HAS_MULTI_VOLUME ; then
1b4de21
		eval "$($DRBDSETUP sh-status "$DRBD_RESOURCE")"
1b4de21
	else
1b4de21
		# without "MULTI_VOLUME", the DRBD_DEVICES array
1b4de21
		# should contain exactly one value
1b4de21
		eval "$($DRBDSETUP "$DRBD_DEVICES" sh-status)"
1b4de21
	fi
1b4de21
1b4de21
	# if there was no output at all, or a weird output
1b4de21
	# make sure the status arrays won't be empty.
1b4de21
	[[ ${#DRBD_ROLE_LOCAL[@]}    != 0 ]] || DRBD_ROLE_LOCAL=(Unconfigured)
1b4de21
	[[ ${#DRBD_ROLE_REMOTE[@]}   != 0 ]] || DRBD_ROLE_REMOTE=(Unknown)
1b4de21
	[[ ${#DRBD_CSTATE[@]}        != 0 ]] || DRBD_CSTATE=(Unconfigured)
1b4de21
	[[ ${#DRBD_DSTATE_LOCAL[@]}  != 0 ]] || DRBD_DSTATE_LOCAL=(Unconfigured)
1b4de21
	[[ ${#DRBD_DSTATE_REMOTE[@]} != 0 ]] || DRBD_DSTATE_REMOTE=(DUnknown)
1b4de21
1b4de21
1b4de21
	: == DEBUG == DRBD_ROLE_LOCAL    == ${DRBD_ROLE_LOCAL[@]} ==
1b4de21
	: == DEBUG == DRBD_ROLE_REMOTE   == ${DRBD_ROLE_REMOTE[@]} ==
1b4de21
	: == DEBUG == DRBD_CSTATE        == ${DRBD_CSTATE[@]} ==
1b4de21
	: == DEBUG == DRBD_DSTATE_LOCAL  == ${DRBD_DSTATE_LOCAL[@]} ==
1b4de21
	: == DEBUG == DRBD_DSTATE_REMOTE == ${DRBD_DSTATE_REMOTE[@]} ==
1b4de21
}
1b4de21
1b4de21
# This is not the only fencing mechanism.
1b4de21
# But in addition to the drbd "fence-peer" handler, which should be configured,
1b4de21
# and is expected to place some appropriate constraints, this is used to
1b4de21
# actually store the Outdated information in DRBD on-disk meta data.
1b4de21
#
1b4de21
# called after stop, and from post notification events.
1b4de21
maybe_outdate_self()
1b4de21
{
1b4de21
	# if you claim your right to go online with stale data,
1b4de21
	# there you are.
1b4de21
	ocf_is_true $OCF_RESKEY_stop_outdates_secondary || return 1
1b4de21
1b4de21
	local host stop_uname
1b4de21
	# We ignore $OCF_RESKEY_CRM_meta_notify_promote_uname here
1b4de21
	# because: if demote and promote for a _stacked_ resource
1b4de21
	# (or a "floating" one, where DRBD sits on top of some SAN)
1b4de21
	# happen in the same transition, demote will see the promote
1b4de21
	# hostname here, and voluntarily outdate itself. Which would
1b4de21
	# result in promote failure, as it is using the same meta
1b4de21
	# data, which would then be outdated.
1b4de21
	# If that is not sufficient for you, you probably need to
1b4de21
	# configure fencing policies in the drbd configuration.
1b4de21
	host=$(printf "%s\n" $OCF_RESKEY_CRM_meta_notify_master_uname |
1b4de21
		grep -vix -m1 -e "$HOSTNAME" )
1b4de21
	if [[ -z $host ]] ; then
1b4de21
		# no current master host found, do not outdate myself
1b4de21
		return 1
1b4de21
	fi
1b4de21
	for stop_uname in $OCF_RESKEY_CRM_meta_notify_stop_uname; do
1b4de21
		[[ $host == "$stop_uname" ]] || continue
1b4de21
		# post notification for stop on that host.
1b4de21
		# hrmpf. crm passed in stale master_uname :(
1b4de21
		# ignore
1b4de21
		return 1
1b4de21
	done
1b4de21
1b4de21
	# e.g. post/promote of some other peer.
1b4de21
	# Should not happen, fencing constraints should take care of that.
1b4de21
	# But in case it does, scream out loud.
1b4de21
	case "${DRBD_ROLE_LOCAL[*]}" in
1b4de21
	*Primary*)
1b4de21
		# I am Primary.
1b4de21
		# The other one is Primary (according to OCF_RESKEY_CRM_meta_notify_master_uname).
1b4de21
		# But we cannot talk to each other :( (otherwise this function was not called)
1b4de21
		# One of us has to die.
1b4de21
		# Which one, however, is not ours to decide.
1b4de21
1b4de21
		ocf_log crit "resource internal SPLIT BRAIN: both $HOSTNAME and $host are Primary for $DRBD_RESOURCE, but the replication link is down!"
1b4de21
		return 1
1b4de21
	esac
1b4de21
1b4de21
	# OK, I am not Primary, but there is an other node Primary
1b4de21
	# Outdate myself
1b4de21
	ocf_log notice "outdating $DRBD_RESOURCE: according to OCF_RESKEY_CRM_meta_notify_master_uname, '$host' is still master"
1b4de21
	do_drbdadm outdate $DRBD_RESOURCE
1b4de21
1b4de21
	# on some pacemaker versions, -INFINITY may cause resource instance stop/start.
1b4de21
	# But in this case that is ok, it may even clear the replication link
1b4de21
	# problem.
1b4de21
	set_master_score -INFINITY
1b4de21
1b4de21
	return 0
1b4de21
}
1b4de21
1b4de21
drbd_update_master_score() {
1b4de21
	set -- $OCF_RESKEY_adjust_master_score
1b4de21
	local only_consistent=$1 only_remote=$2 local_ok=$3 as_good_as_it_gets=$4
1b4de21
	# NOTE
1b4de21
	# there may be constraint scores from rules on role=Master,
1b4de21
	# that in some ways can add to the node attribute based master score we
1b4de21
	# specify below. If you think you want to add personal preferences,
1b4de21
	# in case the scores given by this RA do not suffice, this is the
1b4de21
	# value space you can work with:
1b4de21
	# -INFINITY: Do not promote. Really. Won't work anyways.
1b4de21
		# Too bad, at least with current (Oktober 2009) Pacemaker,
1b4de21
		# negative master scores cause instance stop; restart cycle :(
1b4de21
	# missing, zero: Do not promote.
1b4de21
	#        I think my data is not good enough.
1b4de21
	#        Though, of course, you may try, and it might even work.
1b4de21
	#     5: please, do not promote, unless this is your only option.
1b4de21
	#    10: promotion is probably a bad idea, our local data is no good,
1b4de21
	#        you'd probably run into severe performance problems, and risk
1b4de21
	#        application crashes or blocking IO in case you lose the
1b4de21
	#        replication connection.
1b4de21
	#  1000: Ok to be promoted, we have good data locally (though we don't
1b4de21
	#        know about the peer, so possibly it has even better data?).
1b4de21
	#        You sould use the crm-fence-peer.sh handler or similar
1b4de21
	#        mechanism to avoid data divergence.
1b4de21
	# 10000: Please promote me/keep me Primary.
1b4de21
	#        I'm confident that my data is as good as it gets.
1b4de21
	#
1b4de21
	# For multi volume, we need to compare who is "better" a bit more sophisticated.
1b4de21
	# The ${XXX[*]//UpToDate}, without being in double quotes, results in a single space,
1b4de21
	# if all are UpToDate.
1b4de21
	: == DEBUG == ${DRBD_ROLE_LOCAL[*]}/${DRBD_DSTATE_LOCAL[*]//UpToDate/ }/${DRBD_DSTATE_REMOTE[*]//UpToDate/ }/ ==
1b4de21
	case ${DRBD_ROLE_LOCAL[*]}/${DRBD_DSTATE_LOCAL[*]//UpToDate/ }/${DRBD_DSTATE_REMOTE[*]//UpToDate/ }/ in
1b4de21
	*Primary*/\ /*/)
1b4de21
		# I am Primary, all local disks are UpToDate
1b4de21
		set_master_score $as_good_as_it_gets
1b4de21
		;;
1b4de21
	*/\ /*DUnknown*/)
1b4de21
		# all local disks are UpToDate,
1b4de21
		# but I'm not Primary,
1b4de21
		# and I'm not sure about the peer's disk state(s).
1b4de21
		# We may need to outdate ourselves?
1b4de21
		# But if we outdate in a MONITOR, and are disconnected
1b4de21
		# secondary because of a hard primary crash, before CRM noticed
1b4de21
		# that there is no more master, we'd make us utterly useless!
1b4de21
		# Trust that the primary will also notice the disconnect,
1b4de21
		# and will place an appropriate fencing constraint via
1b4de21
		# its fence-peer handler callback.
1b4de21
		set_master_score  $local_ok
1b4de21
		;;
1b4de21
	*/\ /*/)
1b4de21
		# We know something about our peer, which means that either the
1b4de21
		# replication link is established, or it was not even
1b4de21
		# consistent last time we talked to each other.
1b4de21
		# Also all our local disks are UpToDate, which means even if we are
1b4de21
		# currently synchronizing, we do so as SyncSource.
1b4de21
		set_master_score $as_good_as_it_gets
1b4de21
		;;
1b4de21
1b4de21
	*/*/\ /)
1b4de21
		# At least one of our local disks is not up to date.
1b4de21
		# But our peer is ALL OK.
1b4de21
		# We can expect to have access to useful
1b4de21
		# data, but must expect degraded performance.
1b4de21
		set_master_score $only_remote
1b4de21
		;;
1b4de21
1b4de21
	*/*Attaching*/*/|\
1b4de21
	*/*Negotiating*/*/)
1b4de21
		# some transitional state.
1b4de21
		# just don't do anything
1b4de21
		: ;;
1b4de21
1b4de21
	Unconfigured*|\
1b4de21
	*/*Diskless*/*/|\
1b4de21
	*/*Failed*/*/|\
1b4de21
	*/*Inconsistent*/*/|\
1b4de21
	*/*Outdated*/*/)
1b4de21
		# ALWAYS put the cluster in MAINTENANCE MODE
1b4de21
		# if you add a volume to a live replication group,
1b4de21
		# because the new volume will typically come up as Inconsistent
1b4de21
		# the first time, which would cause a monitor to revoke the
1b4de21
		# master score!
1b4de21
		#
1b4de21
		# At least some of our local disks are not really useable.
1b4de21
		# Our peer is not all good either (or some previous case block
1b4de21
		# would have matched).  We have no access to useful data.
1b4de21
		# DRBD would refuse to be promoted, anyways.
1b4de21
		#
1b4de21
		# set_master_score -INFINITY
1b4de21
		# Too bad, at least with current (Oktober 2009) Pacemaker,
1b4de21
		# negative master scores cause instance stop; restart cycle :(
1b4de21
		# Hope that this will suffice.
1b4de21
		remove_master_score
1b4de21
		;;
1b4de21
	*)
1b4de21
		# All local disks seem to be Consistent.
1b4de21
		# They _may_ be up to date, or not.
1b4de21
		# We hope that fencing mechanisms have put constraints in
1b4de21
		# place, so we won't be promoted with stale data.
1b4de21
		# But in case this was a cluster crash,
1b4de21
		# at least allow _someone_ to be promoted.
1b4de21
		set_master_score $only_consistent
1b4de21
		;;
1b4de21
	esac
1b4de21
1b4de21
	: "$OCF_SUCCESS = OCF_SUCCESS"
1b4de21
	return $OCF_SUCCESS
1b4de21
}
1b4de21
1b4de21
is_drbd_enabled() {
1b4de21
	test -f /proc/drbd
1b4de21
}
1b4de21
1b4de21
#######################################################################
1b4de21
1b4de21
drbd_usage() {
1b4de21
	echo "\
1b4de21
usage: $0 {start|stop|monitor|validate-all|promote|demote|notify|meta-data}
1b4de21
1b4de21
Expects to have a fully populated OCF RA-compliant environment set."
1b4de21
}
1b4de21
1b4de21
drbd_status() {
1b4de21
	local rc
1b4de21
	local dev
1b4de21
	rc=$OCF_NOT_RUNNING
1b4de21
1b4de21
	# NOT local! but "return values"
1b4de21
	# since 8.4 supports multi volumes per resource,
1b4de21
	# these are shell arrays.
1b4de21
	#
1b4de21
	# Initialize to "Unconfigured", in case this returns early.
1b4de21
	# They will be re-initialized and properly populated in drbd_set_status_variables.
1b4de21
	DRBD_ROLE_LOCAL=(Unconfigured)
1b4de21
	DRBD_ROLE_REMOTE=(Unknown)
1b4de21
	DRBD_CSTATE=(Unconfigured)
1b4de21
	DRBD_DSTATE_LOCAL=(Unconfigured)
1b4de21
	DRBD_DSTATE_REMOTE=(DUnknown)
1b4de21
1b4de21
	is_drbd_enabled || return $rc
1b4de21
1b4de21
	# Not running, if no block devices exist.
1b4de21
	#
1b4de21
	# FIXME what if some do, and some do not exist?
1b4de21
	# Adding/removing volumes to/from existing resources should only be
1b4de21
	# done with maintenance-mode enabled.
1b4de21
	# If someone does manually kill/remove only some of the volumes,
1b4de21
	# we tolerate that here.
1b4de21
	for dev in ${DRBD_DEVICES[@]} ""; do
1b4de21
		test -b $dev && break
1b4de21
	done
1b4de21
	[[ $dev ]] || return $rc
1b4de21
1b4de21
	# ok, module is loaded, block device nodes exist.
1b4de21
	# lets see the status
1b4de21
	drbd_set_status_variables
1b4de21
	case "${DRBD_ROLE_LOCAL[*]}" in
1b4de21
	*Primary*)
1b4de21
		rc=$OCF_RUNNING_MASTER
1b4de21
		;;
1b4de21
	*Secondary*)
1b4de21
		rc=$OCF_SUCCESS
1b4de21
		;;
1b4de21
	*Unconfigured*)
1b4de21
		rc=$OCF_NOT_RUNNING
1b4de21
		;;
1b4de21
	*)
1b4de21
		ocf_log err "Unexpected role(s) >>${DRBD_ROLE_LOCAL[*]}<<"
1b4de21
		rc=$OCF_ERR_GENERIC
1b4de21
	esac
1b4de21
1b4de21
	return $rc
1b4de21
}
1b4de21
1b4de21
# I'm sorry, but there is no $OCF_DEGRADED_MASTER or similar yet.
1b4de21
drbd_monitor() {
1b4de21
	local status
1b4de21
	drbd_status
1b4de21
	status=$?
1b4de21
1b4de21
	if [[ $status = $OCF_NOT_RUNNING ]] && ocf_is_probe ; then
1b4de21
		# see also linux-ha mailing list archives,
1b4de21
		# From: Andrew Beekhof
1b4de21
		# Subject: Re: pacemaker+drbd promotion delay
1b4de21
		# Date: 2012-04-13 01:47:37 GMT
1b4de21
		# e.g.: http://thread.gmane.org/gmane.linux.highavailability.user/37089/focus=37163
1b4de21
		# ---
1b4de21
		: "do nothing" ;
1b4de21
	else
1b4de21
		drbd_update_master_score
1b4de21
	fi
1b4de21
1b4de21
	case $status in
1b4de21
	(0) : "OCF_SUCCESS" ;;
1b4de21
	(1) : "OCF_ERR_GENERIC" ;;
1b4de21
	(2) : "OCF_ERR_ARGS" ;;
1b4de21
	(3) : "OCF_ERR_UNIMPLEMENTED" ;;
1b4de21
	(4) : "OCF_ERR_PERM" ;;
1b4de21
	(5) : "OCF_ERR_INSTALLED" ;;
1b4de21
	(6) : "OCF_ERR_CONFIGURED" ;;
1b4de21
	(7) : "OCF_NOT_RUNNING" ;;
1b4de21
	(8) : "OCF_RUNNING_MASTER" ;;
1b4de21
	(9) : "OCF_FAILED_MASTER" ;;
1b4de21
	(*) : " WTF? $status " ;;
1b4de21
	esac
1b4de21
1b4de21
	return $status
1b4de21
}
1b4de21
1b4de21
figure_out_drbd_peer_uname()
1b4de21
{
1b4de21
	# depending on whether or not the peer is currently
1b4de21
	# configured, slave, master, or about to be started,
1b4de21
	# it may be mentioned in various variables (or not at all)
1b4de21
	local x
1b4de21
	# intentionally not cared for stop_uname
1b4de21
	x=$(printf "%s\n" \
1b4de21
		$OCF_RESKEY_CRM_meta_notify_start_uname \
1b4de21
		$OCF_RESKEY_CRM_meta_notify_promote_uname \
1b4de21
		$OCF_RESKEY_CRM_meta_notify_master_uname \
1b4de21
		$OCF_RESKEY_CRM_meta_notify_slave_uname \
1b4de21
		$OCF_RESKEY_CRM_meta_notify_demote_uname |
1b4de21
		grep -vix -m1 -e "$HOSTNAME" )
1b4de21
	DRBD_TO_PEER=${x:+ --peer $x}
1b4de21
}
1b4de21
1b4de21
my_udevsettle()
1b4de21
{
1b4de21
	for dev in ${DRBD_DEVICES[@]}; do
1b4de21
		while ! test -b $dev; do
1b4de21
			sleep 1;
1b4de21
		done
1b4de21
	done
1b4de21
	return 0
1b4de21
}
1b4de21
create_device_udev_settle() {
1b4de21
	local dev
1b4de21
	if $DRBD_HAS_MULTI_VOLUME; then
1b4de21
		if do_drbdadm new-resource $DRBD_RESOURCE &&
1b4de21
		   do_drbdadm new-minor $DRBD_RESOURCE; then
1b4de21
			my_udevsettle
1b4de21
		else
1b4de21
			return 1
1b4de21
		fi
1b4de21
	elif do_drbdadm syncer $DRBD_RESOURCE ; then
1b4de21
		my_udevsettle
1b4de21
	else
1b4de21
		return 1
1b4de21
	fi
1b4de21
}
1b4de21
1b4de21
drbd_start() {
1b4de21
	local rc
1b4de21
	local status
1b4de21
	local first_try=true
1b4de21
1b4de21
	rc=$OCF_ERR_GENERIC
1b4de21
1b4de21
	if ! is_drbd_enabled; then
1b4de21
		do_cmd modprobe -s drbd `$DRBDADM sh-mod-parms` || {
1b4de21
			ocf_log err "Cannot load the drbd module.";
1b4de21
			: "$OCF_ERR_INSTALLED = OCF_ERR_INSTALLED"
1b4de21
			return $OCF_ERR_INSTALLED
1b4de21
		}
1b4de21
		ocf_log debug "$DRBD_RESOURCE start: Module loaded."
1b4de21
	fi
1b4de21
1b4de21
	# Keep trying to bring up the resource;
1b4de21
	# wait for the CRM to time us out if this fails
1b4de21
	while :; do
1b4de21
		drbd_status
1b4de21
		status=$?
1b4de21
		case "$status" in
1b4de21
		$OCF_SUCCESS)
1b4de21
			# Just in case we have to adjust something, this is a
1b4de21
			# good place to do it.  Actually, we don't expect to be
1b4de21
			# called to "start" an already "running" resource, so
1b4de21
			# this is probably dead code.
1b4de21
			# Also, ignore the exit code of adjust, as we are
1b4de21
			# "running" already, anyways, right?
1b4de21
			figure_out_drbd_peer_uname
1b4de21
			do_drbdadm $DRBD_TO_PEER adjust $DRBD_RESOURCE
1b4de21
			rc=$OCF_SUCCESS
1b4de21
			break
1b4de21
			;;
1b4de21
		$OCF_NOT_RUNNING)
1b4de21
			# Check for offline resize. If using internal meta data,
1b4de21
			# we may need to move it first to its expected location.
1b4de21
			$first_try && do_drbdadm check-resize $DRBD_RESOURCE
1b4de21
			figure_out_drbd_peer_uname
1b4de21
			if ! create_device_udev_settle; then
1b4de21
				# We cannot even create the objects
1b4de21
				exit $OCF_ERR_GENERIC
1b4de21
			fi
1b4de21
			if ! do_drbdadm $DRBD_TO_PEER attach $DRBD_RESOURCE ; then
1b4de21
				# If we cannot up it, even on the second try,
1b4de21
				# it is unlikely to get better.  Don't wait for
1b4de21
				# this operation to timeout, but short circuit
1b4de21
				# exit with generic error.
1b4de21
				$first_try || exit $OCF_ERR_GENERIC
1b4de21
				sleep 1
1b4de21
			fi
1b4de21
			;;
1b4de21
		$OCF_RUNNING_MASTER)
1b4de21
			ocf_log warn "$DRBD_RESOURCE already Primary, demoting."
1b4de21
			do_drbdadm secondary $DRBD_RESOURCE
1b4de21
		esac
1b4de21
		$first_try || sleep 1
1b4de21
		first_try=false
1b4de21
	done
1b4de21
	# in case someone does not configure monitor,
1b4de21
	# we must at least call it once after start.
1b4de21
	drbd_update_master_score
1b4de21
1b4de21
	return $rc
1b4de21
}
1b4de21
1b4de21
drbd_promote() {
1b4de21
	local rc
1b4de21
	local status
1b4de21
	local first_try=true
1b4de21
1b4de21
	rc=$OCF_ERR_GENERIC
1b4de21
1b4de21
	# Keep trying to promote the resource;
1b4de21
	# wait for the CRM to time us out if this fails
1b4de21
	while :; do
1b4de21
		drbd_status
1b4de21
		status=$?
1b4de21
		case "$status" in
1b4de21
		$OCF_SUCCESS)
1b4de21
			do_drbdadm primary $DRBD_RESOURCE
1b4de21
			if [[ $? = 17 ]]; then
1b4de21
				# All available disks are inconsistent,
1b4de21
				# or I am consistent, but failed to fence the peer.
1b4de21
				# Cannot become primary.
1b4de21
				# No need to retry indefinitely.
1b4de21
				ocf_log crit "Refusing to be promoted to Primary without UpToDate data"
1b4de21
				break
1b4de21
			fi
1b4de21
			;;
1b4de21
		$OCF_NOT_RUNNING)
1b4de21
			ocf_log error "Trying to promote a resource that was not started"
1b4de21
			break
1b4de21
			;;
1b4de21
		$OCF_RUNNING_MASTER)
1b4de21
			rc=$OCF_SUCCESS
1b4de21
			break
1b4de21
		esac
1b4de21
		$first_try || sleep 1
1b4de21
		first_try=false
1b4de21
	done
1b4de21
1b4de21
	# avoid too tight pacemaker driven "recovery" loop,
1b4de21
	# if promotion keeps failing for some reason
1b4de21
	if [[ $rc != 0 ]] && (( $SECONDS < 15 )) ; then
1b4de21
		delay=$(( 15 - SECONDS ))
1b4de21
		ocf_log warn "promotion failed; sleep $delay # to prevent tight recovery loop"
1b4de21
		sleep $delay
1b4de21
	fi
1b4de21
	return $rc
1b4de21
}
1b4de21
1b4de21
drbd_demote() {
1b4de21
	local rc
1b4de21
	local status
1b4de21
	local first_try=true
1b4de21
1b4de21
	rc=$OCF_ERR_GENERIC
1b4de21
1b4de21
	# Keep trying to demote the resource;
1b4de21
	# wait for the CRM to time us out if this fails
1b4de21
	while :; do
1b4de21
		drbd_status
1b4de21
		status=$?
1b4de21
		case "$status" in
1b4de21
		$OCF_SUCCESS)
1b4de21
			rc=$OCF_SUCCESS
1b4de21
			break
1b4de21
			;;
1b4de21
		$OCF_NOT_RUNNING)
1b4de21
			ocf_log error "Trying to promote a resource that was not started"
1b4de21
			break
1b4de21
			;;
1b4de21
		$OCF_RUNNING_MASTER)
1b4de21
			do_drbdadm secondary $DRBD_RESOURCE
1b4de21
		esac
1b4de21
		$first_try || sleep 1
1b4de21
		first_try=false
1b4de21
	done
1b4de21
1b4de21
	return $rc
1b4de21
}
1b4de21
1b4de21
drbd_stop() {
1b4de21
	local rc=$OCF_ERR_GENERIC
1b4de21
	local first_try=true
1b4de21
1b4de21
	# Keep trying to bring down the resource;
1b4de21
	# wait for the CRM to time us out if this fails
1b4de21
	while :; do
1b4de21
		drbd_status
1b4de21
		status=$?
1b4de21
		case "$status" in
1b4de21
		$OCF_SUCCESS)
1b4de21
			do_drbdadm down $DRBD_RESOURCE
1b4de21
			;;
1b4de21
		$OCF_NOT_RUNNING)
1b4de21
			# Just in case, down it anyways, in case it has been
1b4de21
			# deconfigured but not yet removed.
1b4de21
			# Relevant for >= 8.4.
1b4de21
			do_drbdadm down $DRBD_RESOURCE
1b4de21
			# But ignore any return codes,
1b4de21
			# we are not running, so stop is successfull.
1b4de21
			rc=$OCF_SUCCESS
1b4de21
			break
1b4de21
			;;
1b4de21
		$OCF_RUNNING_MASTER)
1b4de21
			ocf_log warn "$DRBD_RESOURCE still Primary, demoting."
1b4de21
			do_drbdadm secondary  $DRBD_RESOURCE
1b4de21
		esac
1b4de21
		$first_try || sleep 1
1b4de21
		first_try=false
1b4de21
	done
1b4de21
1b4de21
	# if there is some Master (Primary) still around,
1b4de21
	# outdate myself in drbd on-disk meta data.
1b4de21
	maybe_outdate_self
1b4de21
1b4de21
	# do not let old master scores laying around.
1b4de21
	# they may confuse crm if this node was set to standby.
1b4de21
	remove_master_score
1b4de21
1b4de21
	return $rc
1b4de21
}
1b4de21
1b4de21
1b4de21
drbd_notify() {
1b4de21
	local n_type=$OCF_RESKEY_CRM_meta_notify_type
1b4de21
	local n_op=$OCF_RESKEY_CRM_meta_notify_operation
1b4de21
1b4de21
	# active_* and *_resource not really interessting
1b4de21
	# : "== DEBUG == active  = $OCF_RESKEY_CRM_meta_notify_active_uname"
1b4de21
	: "== DEBUG == slave   = $OCF_RESKEY_CRM_meta_notify_slave_uname"
1b4de21
	: "== DEBUG == master  = $OCF_RESKEY_CRM_meta_notify_master_uname"
1b4de21
	: "== DEBUG == start   = $OCF_RESKEY_CRM_meta_notify_start_uname"
1b4de21
	: "== DEBUG == promote = $OCF_RESKEY_CRM_meta_notify_promote_uname"
1b4de21
	: "== DEBUG == stop    = $OCF_RESKEY_CRM_meta_notify_stop_uname"
1b4de21
	: "== DEBUG == demote  = $OCF_RESKEY_CRM_meta_notify_demote_uname"
1b4de21
1b4de21
	case $n_type/$n_op in
1b4de21
	*/start)
1b4de21
		# We do not get a /pre/ start notification for ourself.
1b4de21
		# but we get a /pre/ start notification for the other side, unless both
1b4de21
		# are started from the same transition graph.  If there are only two
1b4de21
		# peers (the "classic" two-node DRBD), this adjust is usually a no-op.
1b4de21
		#
1b4de21
		# In case of more than one _possible_ peer, we may still be StandAlone,
1b4de21
		# or configured for a meanwhile failed peer, and should now adjust our
1b4de21
		# network settings during pre-notification of start of the other node.
1b4de21
		#
1b4de21
		# We usually get /post/ notification for ourself and the peer.
1b4de21
		# In both cases adjust should be a no-op.
1b4de21
		drbd_set_status_variables
1b4de21
		figure_out_drbd_peer_uname
1b4de21
		do_drbdadm $DRBD_TO_PEER -v adjust $DRBD_RESOURCE
1b4de21
		;;
1b4de21
	post/*)
1b4de21
		# After something has been done is a good time to
1b4de21
		# recheck our status:
1b4de21
		drbd_set_status_variables
1b4de21
		drbd_update_master_score
1b4de21
1b4de21
		: == DEBUG == ${DRBD_DSTATE_REMOTE[*]} ==
1b4de21
		case ${DRBD_DSTATE_REMOTE[*]} in
1b4de21
		*DUnknown*)
1b4de21
			# Still not communicating.
1b4de21
			# Maybe someone else is primary (too)?
1b4de21
			maybe_outdate_self
1b4de21
		esac
1b4de21
	esac
1b4de21
1b4de21
	: "$OCF_SUCCESS = OCF_SUCCESS"
1b4de21
	return $OCF_SUCCESS
1b4de21
}
1b4de21
1b4de21
# "macro" to be able to give useful error messages
1b4de21
# on clone resource configuration error.
1b4de21
meta_expect()
1b4de21
{
1b4de21
	local what=$1 whatvar=OCF_RESKEY_CRM_meta_${1//-/_} op=$2 expect=$3
1b4de21
	local val=${!whatvar}
1b4de21
	if [[ -n $val ]]; then
1b4de21
		# [, not [[, or it won't work ;)
1b4de21
		[ $val $op $expect ] && return
1b4de21
	fi
1b4de21
	ocf_log err "meta parameter misconfigured, expected $what $op $expect, but found ${val:-unset}."
1b4de21
	exit $OCF_ERR_CONFIGURED
1b4de21
}
1b4de21
1b4de21
ls_stat_is_block_maj_147() {
1b4de21
	set -- $(command ls -L -l "$1" 2>/dev/null)
1b4de21
	[[ $1 = b* ]] && [[ $5 == 147,* ]]
1b4de21
}
1b4de21
1b4de21
check_crm_feature_set()
1b4de21
{
1b4de21
	set -- ${OCF_RESKEY_crm_feature_set//[!0-9]/ }
1b4de21
	local a=${1:-0} b=${2:-0} c=${3:-0}
1b4de21
	
1b4de21
	(( a > 3 )) ||
1b4de21
	(( a == 3 && b > 0 )) ||
1b4de21
	(( a == 3 && b == 0 && c > 0 )) ||
1b4de21
	ocf_log warn "You may be disappointed: This RA is intended for pacemaker 1.0 or better!"
1b4de21
}
1b4de21
1b4de21
drbd_validate_all () {
1b4de21
	DRBDADM="drbdadm"
1b4de21
	DRBDSETUP="drbdsetup"
1b4de21
	DRBD_HAS_MULTI_VOLUME=false
1b4de21
1b4de21
	# these will _exit_ if they don't find the binaries
1b4de21
	check_binary $DRBDADM
1b4de21
	check_binary $DRBDSETUP
1b4de21
	# XXX I really take cibadmin, sed, grep, etc. for granted.
1b4de21
1b4de21
	local VERSION DRBD_KERNEL_VERSION_CODE=0
1b4de21
	if VERSION="$($DRBDADM --version 2>/dev/null)"; then
1b4de21
		eval $VERSION
1b4de21
	fi
1b4de21
	if (( $DRBD_KERNEL_VERSION_CODE == 0x0 )) ; then
1b4de21
		# Maybe the DRBD module was not loaded (yet).
1b4de21
		# I don't want to load the module here,
1b4de21
		# maybe this is just a probe or stop.
1b4de21
		# It will be loaded on "start", though.
1b4de21
		# Instead, look at modinfo output.
1b4de21
		# Newer drbdadm does this implicitly, but may reexec older
1b4de21
		# drbdadm versions for compatibility reasons.
1b4de21
		DRBD_KERNEL_VERSION_CODE=$(printf "0x%02x%02x%02x" $(
1b4de21
			modinfo -F version drbd |
1b4de21
			sed -ne 's/^\([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\).*$/\1 \2 \3/p'))
1b4de21
	fi
1b4de21
	if (( $DRBD_KERNEL_VERSION_CODE >= 0x080400 )); then
1b4de21
		DRBD_HAS_MULTI_VOLUME=true
1b4de21
	fi
1b4de21
	check_crm_feature_set
1b4de21
1b4de21
	# Check clone and M/S options.
1b4de21
	meta_expect clone-max -le 2
1b4de21
	meta_expect clone-node-max = 1
1b4de21
	meta_expect master-node-max = 1
1b4de21
	meta_expect master-max -le 2
1b4de21
1b4de21
	# Rather than returning $OCF_ERR_CONFIGURED, we sometimes return
1b4de21
	# $OCF_ERR_INSTALLED here: the local config may be broken, but some
1b4de21
	# other node may have a valid config.
1b4de21
1b4de21
	# check drbdconf plausibility
1b4de21
	case "$OCF_RESKEY_drbdconf" in
1b4de21
	"")
1b4de21
		# this is actually ok. drbdadm has its own builtin defaults.
1b4de21
		# but as long as we assign an explicit default above,
1b4de21
		# this cannot happen anyways.
1b4de21
		: ;;
1b4de21
	*[!-%+./0-9:=@A-Z_a-z]*)
1b4de21
		# no, I do not trust the configurable cib parameters.
1b4de21
		ocf_log err "drbdconf name must only contain [-%+./0-9:=@A-Z_a-z]"
1b4de21
		: "$OCF_ERR_CONFIGURED = OCF_ERR_CONFIGURED"
1b4de21
		return $OCF_ERR_CONFIGURED
1b4de21
		;;
1b4de21
	*)
1b4de21
		# Check if we can read the configuration file.
1b4de21
		if [ ! -r "${OCF_RESKEY_drbdconf}" ]; then
1b4de21
			ocf_log err "Configuration file ${OCF_RESKEY_drbdconf} does not exist or is not readable!"
1b4de21
			: "$OCF_ERR_INSTALLED = OCF_ERR_INSTALLED"
1b4de21
			return $OCF_ERR_INSTALLED
1b4de21
		fi
1b4de21
		DRBDADM="$DRBDADM -c $OCF_RESKEY_drbdconf"
1b4de21
	esac
1b4de21
1b4de21
	# check drbd_resource plausibility
1b4de21
	case "$OCF_RESKEY_drbd_resource" in
1b4de21
	"")
1b4de21
		ocf_log err "No resource name specified!"
1b4de21
		: "$OCF_ERR_CONFIGURED = OCF_ERR_CONFIGURED"
1b4de21
		return $OCF_ERR_CONFIGURED
1b4de21
		;;
1b4de21
	*[!-%+./0-9:=@A-Z_a-z]*)
1b4de21
		# no, I do not trust the configurable cib parameters.
1b4de21
		ocf_log err "Resource name must only contain [-%+./0-9:=@A-Z_a-z]"
1b4de21
		: "$OCF_ERR_CONFIGURED = OCF_ERR_CONFIGURED"
1b4de21
		return $OCF_ERR_CONFIGURED
1b4de21
	esac
1b4de21
	# exporting this is useful for "drbdsetup show".
1b4de21
	# and it makes it all a little bit more readable.
1b4de21
	export DRBD_RESOURCE=$OCF_RESKEY_drbd_resource
1b4de21
1b4de21
	# The resource should appear in the config file,
1b4de21
	# otherwise something's fishy
1b4de21
	# NOTE
1b4de21
	# since 8.4 has multi volume support,
1b4de21
	# DRBD_DEVICES will be a shell array!
1b4de21
	# FIXME we should double check that we explicitly restrict the set of
1b4de21
	# valid characters in device names...
1b4de21
	if DRBD_DEVICES=($($DRBDADM --stacked sh-dev $DRBD_RESOURCE 2>/dev/null)); then
1b4de21
		# apparently a "stacked" resource. Remember for future DRBDADM calls.
1b4de21
		DRBDADM="$DRBDADM -S"
1b4de21
	elif DRBD_DEVICES=($($DRBDADM sh-dev $DRBD_RESOURCE 2>/dev/null)); then
1b4de21
		: # nothing to do.
1b4de21
	else
1b4de21
		if [[ $__OCF_ACTION = "monitor" && $OCF_RESKEY_CRM_meta_interval = 0 ]]; then
1b4de21
			# ok, this was a probe. That may happen on any node,
1b4de21
			# to enforce configuration.
1b4de21
			: "$OCF_NOT_RUNNING = OCF_NOT_RUNNING"
1b4de21
			return $OCF_NOT_RUNNING
1b4de21
		else
1b4de21
			# hm. probably misconfigured constraint somewhere.
1b4de21
			# sorry. don't retry anywhere.
1b4de21
			ocf_log err "DRBD resource ${DRBD_RESOURCE} not found in configuration file ${OCF_RESKEY_drbdconf}."
1b4de21
			remove_master_score
1b4de21
			: "$OCF_ERR_INSTALLED = OCF_ERR_INSTALLED"
1b4de21
			return $OCF_ERR_INSTALLED
1b4de21
		fi
1b4de21
	fi
1b4de21
1b4de21
	# check for master-max and allow-two-primaries on start|promote only,
1b4de21
	# so it could be stopped still, if someone re-configured while running.
1b4de21
	case $__OCF_ACTION:$OCF_RESKEY_CRM_meta_master_max in
1b4de21
	start:2|promote:2)
1b4de21
		if ! $DRBDADM -d -v dump $DRBD_RESOURCE 2>/dev/null |
1b4de21
			grep -q -Ee '^[[:space:]]*allow-two-primaries([[:space:]]+yes)?;$'
1b4de21
		then
1b4de21
			ocf_log err "master-max = 2, but DRBD resource $DRBD_RESOURCE does not allow-two-primaries."
1b4de21
			: "$OCF_ERR_CONFIGURED = OCF_ERR_CONFIGURED"
1b4de21
			return $OCF_ERR_CONFIGURED
1b4de21
		fi
1b4de21
	esac
1b4de21
1b4de21
	# detect whether notify is configured or not.
1b4de21
	# for probes, the meta_notify* namespace is not exported.
1b4de21
	case $__OCF_ACTION in
1b4de21
	monitor|validate-all)
1b4de21
		:;;
1b4de21
	*)
1b4de21
		# Test if the environment variables for either the notify
1b4de21
		# enabled, or one of its effects, are set.
1b4de21
		# If both are unset, we complain.
1b4de21
		if ! ocf_is_true ${OCF_RESKEY_CRM_meta_notify} &&
1b4de21
		   [[ ${OCF_RESKEY_CRM_meta_notify_start_uname- NOT SET } = " NOT SET " ]]; then
1b4de21
			ocf_log err "you really should enable notify when using this RA"
1b4de21
			: "$OCF_ERR_CONFIGURED = OCF_ERR_CONFIGURED"
1b4de21
			return $OCF_ERR_CONFIGURED
1b4de21
		fi
1b4de21
	esac
1b4de21
1b4de21
	local i j n=0 fallback=false
1b4de21
	for i in $OCF_RESKEY_adjust_master_score; do
1b4de21
		[[ $i = *[!0-9]* ]]   && fallback=true && ocf_log err "BAD adjust_master_score value $i ; falling back to default"
1b4de21
		[[ $j && $i -lt $j ]] && fallback=true && ocf_log err "BAD adjust_master_score value $j > $i ; falling back to default"
1b4de21
		j=$i
1b4de21
		n=$(( n+1 ))
1b4de21
	done
1b4de21
	[[ $n != 4 ]] && fallback=true && ocf_log err "Not enough adjust_master_score values ($n != 4); falling back to default"
1b4de21
	$fallback && OCF_RESKEY_adjust_master_score=$OCF_RESKEY_adjust_master_score_default
1b4de21
1b4de21
	# we use it in various places,
1b4de21
	# just make sure it contains what we expect.
1b4de21
	HOSTNAME=`uname -n`
1b4de21
1b4de21
	: "$OCF_SUCCESS = OCF_SUCCESS"
1b4de21
	return $OCF_SUCCESS
1b4de21
}
1b4de21
1b4de21
#######################################################################
1b4de21
1b4de21
if [ $# != 1 ]; then
1b4de21
	drbd_usage
1b4de21
	exit $OCF_ERR_ARGS
1b4de21
fi
1b4de21
1b4de21
# if $__OCF_ACTION = monitor, but meta_interval not set,
1b4de21
# this is a "probe". we could change behaviour.
1b4de21
: ${OCF_RESKEY_CRM_meta_interval=0}
1b4de21
1b4de21
case $__OCF_ACTION in
1b4de21
meta-data)
1b4de21
	meta_data
1b4de21
	exit $OCF_SUCCESS
1b4de21
	;;
1b4de21
usage)
1b4de21
	drbd_usage
1b4de21
	exit $OCF_SUCCESS
1b4de21
esac
1b4de21
1b4de21
if $USE_DEBUG_LOG ; then
1b4de21
	exec 2>&9
1b4de21
	set -x
1b4de21
fi
1b4de21
1b4de21
# Everything except usage and meta-data must pass the validate test
1b4de21
drbd_validate_all || exit
1b4de21
1b4de21
case $__OCF_ACTION in
1b4de21
start)
1b4de21
	drbd_start
1b4de21
	;;
1b4de21
stop)
1b4de21
	drbd_stop
1b4de21
	;;
1b4de21
notify)
1b4de21
	drbd_notify
1b4de21
	;;
1b4de21
promote)
1b4de21
	drbd_promote
1b4de21
	;;
1b4de21
demote)
1b4de21
	drbd_demote
1b4de21
	;;
1b4de21
status)
1b4de21
	drbd_status
1b4de21
	;;
1b4de21
monitor)
1b4de21
	drbd_monitor
1b4de21
	;;
1b4de21
validate-all)
1b4de21
	;;
1b4de21
*)
1b4de21
	drbd_usage
1b4de21
	exit $OCF_ERR_UNIMPLEMENTED
1b4de21
esac
1b4de21
# exit code is the exit code (return code) of the last command (shell function)