diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:52:36 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 07:52:36 +0000 |
commit | 7de03e4e519705301265c0415b3c0af85263a7ac (patch) | |
tree | 29d819c5227e3619d18a67d2a5dde963b3229dbe /heartbeat/ocf-shellfuncs.in | |
parent | Initial commit. (diff) | |
download | resource-agents-7de03e4e519705301265c0415b3c0af85263a7ac.tar.xz resource-agents-7de03e4e519705301265c0415b3c0af85263a7ac.zip |
Adding upstream version 1:4.13.0.upstream/1%4.13.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'heartbeat/ocf-shellfuncs.in')
-rw-r--r-- | heartbeat/ocf-shellfuncs.in | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/heartbeat/ocf-shellfuncs.in b/heartbeat/ocf-shellfuncs.in new file mode 100644 index 0000000..6852163 --- /dev/null +++ b/heartbeat/ocf-shellfuncs.in @@ -0,0 +1,1070 @@ +# +# +# Common helper functions for the OCF Resource Agents supplied by +# heartbeat. +# +# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée +# All Rights Reserved. +# +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# Build version: 204f146196774ded1581aed1b6acd0d562b4f958 + +# TODO: Some of this should probably split out into a generic OCF +# library for shell scripts, but for the time being, we'll just use it +# ourselves... +# + +# TODO wish-list: +# - Generic function for evaluating version numbers +# - Generic function(s) to extract stuff from our own meta-data +# - Logging function which automatically adds resource identifier etc +# prefixes +# TODO: Move more common functionality for OCF RAs here. +# + +# This was common throughout all legacy Heartbeat agents +unset LC_ALL; export LC_ALL +unset LANGUAGE; export LANGUAGE + +: ${HA_SBIN_DIR:=@sbindir@} + +__SCRIPT_NAME=`basename $0` + +if [ -z "$OCF_ROOT" ]; then + : ${OCF_ROOT=@OCF_ROOT_DIR@} +fi + +if [ "$OCF_FUNCTIONS_DIR" = ${OCF_ROOT}/resource.d/heartbeat ]; then # old + unset OCF_FUNCTIONS_DIR +fi + +: ${OCF_FUNCTIONS_DIR:=${OCF_ROOT}/lib/heartbeat} + +. ${OCF_FUNCTIONS_DIR}/ocf-binaries +. ${OCF_FUNCTIONS_DIR}/ocf-returncodes +. ${OCF_FUNCTIONS_DIR}/ocf-directories +. ${OCF_FUNCTIONS_DIR}/ocf-rarun +. ${OCF_FUNCTIONS_DIR}/ocf-distro + +# Define OCF_RESKEY_CRM_meta_interval in case it isn't already set, +# to make sure that ocf_is_probe() always works +: ${OCF_RESKEY_CRM_meta_interval=0} + +ocf_is_root() { + if [ X`id -u` = X0 ]; then + true + else + false + fi +} + +ocf_maybe_random() { + if test -c /dev/urandom; then + od -An -N4 -tu4 /dev/urandom | tr -d '[:space:]' + else + awk -v pid=$$ 'BEGIN{srand(pid); print rand()}' | sed 's/^.*[.]//' + fi +} + +# Portability comments: +# o The following rely on Bourne "sh" pattern-matching, which is usually +# that for filename generation (note: not regexp). +# o The "*) true ;;" clause is probably unnecessary, but is included +# here for completeness. +# o The negation in the pattern uses "!". This seems to be common +# across many OSes (whereas the alternative "^" fails on some). +# o If an OS is encountered where this negation fails, then a possible +# alternative would be to replace the function contents by (e.g.): +# [ -z "`echo $1 | tr -d '[0-9]'`" ] +# +ocf_is_decimal() { + case "$1" in + ""|*[!0-9]*) # empty, or at least one non-decimal + false ;; + *) + true ;; + esac +} + +ocf_is_true() { + case "$1" in + yes|true|1|YES|TRUE|True|ja|on|ON) true ;; + *) false ;; + esac +} + +ocf_is_hex() { + case "$1" in + ""|*[!0-9a-fA-F]*) # empty, or at least one non-hex + false ;; + *) + true ;; + esac +} + +ocf_is_octal() { + case "$1" in + ""|*[!0-7]*) # empty, or at least one non-octal + false ;; + *) + true ;; + esac +} + +__ocf_set_defaults() { + __OCF_ACTION="$1" + + # Return to sanity for the agents... + unset LANG + LC_ALL=C + export LC_ALL + + # TODO: Review whether we really should source this. Or rewrite + # to match some emerging helper function syntax...? This imports + # things which no OCF RA should be using... + + # Strip the OCF_RESKEY_ prefix from this particular parameter + if [ -z "$OCF_RESKEY_OCF_CHECK_LEVEL" ]; then + : ${OCF_CHECK_LEVEL:=0} + else + : ${OCF_CHECK_LEVEL:=$OCF_RESKEY_OCF_CHECK_LEVEL} + fi + + if [ ! -d "$OCF_ROOT" ]; then + ha_log "ERROR: OCF_ROOT points to non-directory $OCF_ROOT." + exit $OCF_ERR_GENERIC + fi + + if [ -z "$OCF_RESOURCE_TYPE" ]; then + : ${OCF_RESOURCE_TYPE:=$__SCRIPT_NAME} + fi + + if [ "x$__OCF_ACTION" = "xmeta-data" ]; then + : ${OCF_RESOURCE_INSTANCE:="RESOURCE_ID"} + fi + + if [ -z "$OCF_RA_VERSION_MAJOR" ]; then + : We are being invoked as an init script. + : Fill in some things with reasonable values. + : ${OCF_RESOURCE_INSTANCE:="default"} + return 0 + fi + + if [ -z "$OCF_RESOURCE_INSTANCE" ]; then + ha_log "ERROR: Need to tell us our resource instance name." + exit $OCF_ERR_ARGS + fi +} + +hadate() { + date "+${HA_DATEFMT}" +} + +set_logtag() { + if [ -z "$HA_LOGTAG" ]; then + if [ -n "$OCF_RESOURCE_INSTANCE" ]; then + HA_LOGTAG="$__SCRIPT_NAME($OCF_RESOURCE_INSTANCE)[$$]" + else + HA_LOGTAG="$__SCRIPT_NAME[$$]" + fi + fi +} + +__ha_log() { + local ignore_stderr=false + local loglevel + + [ "x$1" = "x--ignore-stderr" ] && ignore_stderr=true && shift + + [ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY="" + # if we're connected to a tty, then output to stderr + if tty >/dev/null; then + if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then + return 0 + elif [ "$ignore_stderr" = "true" ]; then + # something already printed this error to stderr, so ignore + return 0 + fi + if [ "$HA_LOGTAG" ]; then + echo "$HA_LOGTAG: $*" + else + echo "$*" + fi >&2 + return 0 + fi + + set_logtag + + if [ "x${HA_LOGD}" = "xyes" ] ; then + ha_logger -t "${HA_LOGTAG}" "$@" + if [ "$?" -eq "0" ] ; then + return 0 + fi + fi + + if + [ -n "$HA_LOGFACILITY" ] + then + : logging through syslog + # loglevel is unknown, use 'notice' for now + loglevel=notice + case "${*}" in + *ERROR*) loglevel=err;; + *WARN*) loglevel=warning;; + *INFO*|info) loglevel=info;; + esac + logger -t "$HA_LOGTAG" -p ${HA_LOGFACILITY}.${loglevel} "${*}" + fi + if + [ -n "$HA_LOGFILE" ] + then + : appending to $HA_LOGFILE + echo `hadate`" $HA_LOGTAG: ${*}" >> $HA_LOGFILE + fi + if + [ -z "$HA_LOGFACILITY" -a -z "$HA_LOGFILE" ] && ! [ "$ignore_stderr" = "true" ] + then + : appending to stderr + echo `hadate`"${*}" >&2 + fi + if + [ -n "$HA_DEBUGLOG" ] + then + : appending to $HA_DEBUGLOG + if [ "$HA_LOGFILE"x != "$HA_DEBUGLOG"x ]; then + echo "$HA_LOGTAG: "`hadate`"${*}" >> $HA_DEBUGLOG + fi + fi +} + +ha_log() +{ + __ha_log "$@" +} + +ha_debug() { + + if [ "x${HA_debug}" = "x0" ] || [ -z "${HA_debug}" ] ; then + return 0 + fi + if tty >/dev/null; then + if [ "$HA_LOGTAG" ]; then + echo "$HA_LOGTAG: $*" + else + echo "$*" + fi >&2 + return 0 + fi + + set_logtag + + if [ "x${HA_LOGD}" = "xyes" ] ; then + ha_logger -t "${HA_LOGTAG}" -D "ha-debug" "$@" + if [ "$?" -eq "0" ] ; then + return 0 + fi + fi + + [ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY="" + + if + [ -n "$HA_LOGFACILITY" ] + then + : logging through syslog + logger -t "$HA_LOGTAG" -p "${HA_LOGFACILITY}.debug" "${*}" + fi + if + [ -n "$HA_DEBUGLOG" ] + then + : appending to $HA_DEBUGLOG + echo "$HA_LOGTAG: "`hadate`"${*}" >> $HA_DEBUGLOG + fi + if + [ -z "$HA_LOGFACILITY" -a -z "$HA_DEBUGLOG" ] + then + : appending to stderr + echo "$HA_LOGTAG: `hadate`${*}: ${HA_LOGFACILITY}" >&2 + fi +} + +ha_parameter() { + local VALUE + VALUE=`sed -e 's%[ ][ ]*% %' -e 's%^ %%' -e 's%#.*%%' $HA_CF | grep -i "^$1 " | sed 's%[^ ]* %%'` + if + [ "X$VALUE" = X ] + then + + case $1 in + keepalive) VALUE=2;; + deadtime) + ka=`ha_parameter keepalive` + VALUE=`expr $ka '*' 2 '+' 1`;; + esac + fi + echo $VALUE +} + +ocf_log() { + # TODO: Revisit and implement internally. + if + [ $# -lt 2 ] + then + ocf_log err "Not enough arguments [$#] to ocf_log." + fi + __OCF_PRIO="$1" + shift + __OCF_MSG="$*" + + case "${__OCF_PRIO}" in + crit) __OCF_PRIO="CRIT";; + err) __OCF_PRIO="ERROR";; + warn) __OCF_PRIO="WARNING";; + info) __OCF_PRIO="INFO";; + debug)__OCF_PRIO="DEBUG";; + *) __OCF_PRIO=`echo ${__OCF_PRIO}| tr '[a-z]' '[A-Z]'`;; + esac + + if [ "${__OCF_PRIO}" = "DEBUG" ]; then + ha_debug "${__OCF_PRIO}: $__OCF_MSG" + else + ha_log "${__OCF_PRIO}: $__OCF_MSG" + fi +} + +# +# ocf_exit_reason: print exit error string to stderr +# Usage: Allows the OCF script to provide a string +# describing why the exit code was returned. +# Arguments: reason - required, The string that represents why the error +# occured. +# +ocf_exit_reason() +{ + local cookie="$OCF_EXIT_REASON_PREFIX" + local fmt + local msg + + # No argument is likely not intentional. + # Just one argument implies a printf format string of just "%s". + # "Least surprise" in case some interpolated string from variable + # expansion or other contains a percent sign. + # More than one argument: first argument is going to be the format string. + case $# in + 0) ocf_log err "Not enough arguments to ocf_log_exit_msg." ;; + 1) fmt="%s" ;; + + *) fmt=$1 + shift + case $fmt in + *%*) : ;; # ok, does look like a format string + *) ocf_log warn "Does not look like format string: [$fmt]" ;; + esac ;; + esac + + if [ -z "$cookie" ]; then + # use a default prefix + cookie="ocf-exit-reason:" + fi + + msg=$(printf "${fmt}" "$@") + printf >&2 "%s%s\n" "$cookie" "$msg" + __ha_log --ignore-stderr "ERROR: $msg" +} + +# +# ocf_deprecated: Log a deprecation warning +# Usage: ocf_deprecated [param-name] +# Arguments: param-name optional, name of a boolean resource +# parameter that can be used to suppress +# the warning (default +# "ignore_deprecation") +ocf_deprecated() { + local param + param=${1:-ignore_deprecation} + # don't use ${!param} here, it's a bashism + if ! ocf_is_true $(eval echo \$OCF_RESKEY_$param); then + ocf_log warn "This resource agent is deprecated" \ + "and may be removed in a future release." \ + "See the man page for details." \ + "To suppress this warning, set the \"${param}\"" \ + "resource parameter to true." + fi +} + +# +# Ocf_run: Run a script, and log its output. +# Usage: ocf_run [-q] [-info|-warn|-err] <command> +# -q: don't log the output of the command if it succeeds +# -info|-warn|-err: log the output of the command at given +# severity if it fails (defaults to err) +# +ocf_run() { + local rc + local output + local verbose=1 + local loglevel=err + local var + + for var in 1 2 + do + case "$1" in + "-q") + verbose="" + shift 1;; + "-info"|"-warn"|"-err") + loglevel=`echo $1 | sed -e s/-//g` + shift 1;; + *) + ;; + esac + done + + output=`"$@" 2>&1` + rc=$? + [ -n "$output" ] && output="$(echo "$output" | tr -s ' \t\r\n' ' ')" + if [ $rc -eq 0 ]; then + if [ "$verbose" -a ! -z "$output" ]; then + ocf_log info "$output" + fi + else + if [ ! -z "$output" ]; then + ocf_log $loglevel "$output" + else + ocf_log $loglevel "command failed: $*" + fi + fi + + return $rc +} + +ocf_pidfile_status() { + local pid pidfile="$1" + if [ ! -e "$pidfile" ]; then + # Not exists + return 2 + fi + pid=$(cat "$pidfile") + kill -0 "$pid" > /dev/null 2>&1 + if [ $? = 0 ]; then + return 0 + fi + + # Stale + return 1 +} + +# mkdir(1) based locking +# first the directory is created with the name given as $1 +# then a file named "pid" is created within that directory with +# the process PID +# stale locks are handled carefully, the inode of a directory +# needs to match before and after test if the process is running +# empty directories are also handled appropriately +# we relax (sleep) occasionally to allow for other processes to +# finish managing the lock in case they are in the middle of the +# business + +relax() { sleep 0.5; } +ocf_get_stale_pid() { + local piddir pid dir_inode + + piddir="$1" + [ -z "$piddir" ] && return 2 + dir_inode="`ls -di $piddir 2>/dev/null`" + [ -z "$dir_inode" ] && return 1 + pid=`cat $piddir/pid 2>/dev/null` + if [ -z "$pid" ]; then + # empty directory? + relax + if [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then + echo $dir_inode + else + return 1 + fi + elif kill -0 $pid >/dev/null 2>&1; then + return 1 + elif relax && [ -e "$piddir/pid" ] && [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then + echo $pid + else + return 1 + fi +} + +# There is a race when the following two functions to manage the +# lock file (mk and rm) are invoked in parallel by different +# instances. It is up to the caller to reduce probability of that +# taking place (see ocf_take_lock() below). + +ocf_mk_pid() { + mkdir $1 2>/dev/null && echo $$ > $1/pid +} +ocf_rm_pid() { + rm -f $1/pid + rmdir $1 2>/dev/null +} + +# Testing and subsequently removing a stale lock (containing the +# process pid) is inherently difficult to do in such a way as to +# prevent a race between creating a pid file and removing it and +# its directory. We reduce the probability of that happening by +# checking if the stale lock persists over a random period of +# time. + +ocf_take_lock() { + local lockdir=$1 + local rnd + local stale_pid + + # we don't want it too short, so strip leading zeros + rnd=$(ocf_maybe_random | sed 's/^0*//') + stale_pid=`ocf_get_stale_pid $lockdir` + if [ -n "$stale_pid" ]; then + sleep 0.$rnd + # remove "stale pid" only if it persists + [ "$stale_pid" = "`ocf_get_stale_pid $lockdir`" ] && + ocf_rm_pid $lockdir + fi + while ! ocf_mk_pid $lockdir; do + ocf_log info "Sleeping until $lockdir is released..." + sleep 0.$rnd + done +} + +ocf_release_lock_on_exit() { + trap "ocf_rm_pid $1" EXIT +} + +# returns true if the CRM is currently running a probe. A probe is +# defined as a monitor operation with a monitoring interval of zero. +ocf_is_probe() { + [ "$__OCF_ACTION" = "monitor" -a "$OCF_RESKEY_CRM_meta_interval" = 0 ] +} + +# returns true if the resource is configured as a clone. This is +# defined as a resource where the clone-max meta attribute is present. +ocf_is_clone() { + [ ! -z "${OCF_RESKEY_CRM_meta_clone_max}" ] +} + +# returns true if the resource is configured as a multistate +# (master/slave) resource. This is defined as a resource where the +# master-max meta attribute is present, and set to greater than zero. +ocf_is_ms() { + [ "${OCF_RESKEY_CRM_meta_promotable}" = "true" ] || { [ ! -z "${OCF_RESKEY_CRM_meta_master_max}" ] && [ "${OCF_RESKEY_CRM_meta_master_max}" -gt 0 ]; } +} + +# version check functions +# allow . and - to delimit version numbers +# max version number is 999 +# +ocf_is_ver() { + echo $1 | grep '^[0-9][0-9.-]*[0-9A-Za-z.\+-]*$' >/dev/null 2>&1 +} + +# usage: ocf_version_cmp VER1 VER2 +# version strings can contain digits, dots, and dashes +# must start and end with a digit +# returns: +# 0: VER1 smaller (older) than VER2 +# 1: versions equal +# 2: VER1 greater (newer) than VER2 +# 3: bad format +ocf_version_cmp() { + ocf_is_ver "$1" || return 3 + ocf_is_ver "$2" || return 3 + local v1=$1 + local v2=$2 + + sort_version="sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n" + older=$( (echo "$v1"; echo "$v2") | $sort_version | head -1 ) + + if [ "$v1" = "$v2" ]; then + return 1 + elif [ "$v1" = "$older" ]; then + return 0 + else + return 2 # -1 would look funny in shell ;-) + fi +} + +ocf_local_nodename() { + # use crm_node -n for pacemaker > 1.1.8 + which pacemakerd > /dev/null 2>&1 + if [ $? -eq 0 ]; then + local version=$(pacemakerd -$ | grep "Pacemaker .*" | awk '{ print $2 }') + version=$(echo $version | awk -F- '{ print $1 }') + ocf_version_cmp "$version" "1.1.8" + if [ $? -eq 2 ]; then + which crm_node > /dev/null 2>&1 + if [ $? -eq 0 ]; then + crm_node -n + return + fi + fi + fi + + # otherwise use uname -n + uname -n +} + +# usage: dirname DIR +dirname() +{ + local a + local b + + [ $# = 1 ] || return 1 + a="$1" + while [ 1 ]; do + b="${a%/}" + [ "$a" = "$b" ] && break + a="$b" + done + b=${a%/*} + [ -z "$b" -o "$a" = "$b" ] && b="." + + echo "$b" + return 0 +} + +# usage: systemd_is_running +# returns: +# 0 PID 1 is systemd +# 1 otherwise +systemd_is_running() +{ + [ "$(cat /proc/1/comm 2>/dev/null)" = "systemd" ] +} + +# usage: systemd_drop_in <name> <After|Before> <dependency.service> +systemd_drop_in() +{ + local conf_file + if [ $# -ne 3 ]; then + ocf_log err "Incorrect number of arguments [$#] for systemd_drop_in." + fi + + systemdrundir="/run/systemd/system/resource-agents-deps.target.d" + mkdir -p "$systemdrundir" + conf_file="$systemdrundir/$1.conf" + cat >"$conf_file" <<EOF +[Unit] +$2=$3 +EOF + # The information is accessible through systemd API and systemd would + # complain about improper permissions. + chmod o+r "$conf_file" + systemctl daemon-reload +} + +# move process to root cgroup if realtime scheduling is enabled +ocf_move_to_root_cgroup_if_rt_enabled() +{ + if [ -e "/sys/fs/cgroup/cpu/cpu.rt_runtime_us" ]; then + echo $$ >> /sys/fs/cgroup/cpu/tasks + + if [ "$?" -ne "0" ]; then + ocf_log warn "Unable to move PID $$ to the root cgroup" + fi + fi +} + +# usage: crm_mon_no_validation args... +# run crm_mon without any cib schema validation +# This is useful when an agent runs in a bundle to avoid potential +# schema validation errors when host and bundle are not perfectly aligned +# To be used, your shell must support on process substitution (e.g. bash) +# returns: +# <crm_mon error codes> +crm_mon_no_validation() +{ + # The subshell prevents parsing error with incompatible shells + "$SHELL" -c "CIB_file=<(${HA_SBIN_DIR}/cibadmin -Q | sed 's/validate-with=\"[^\"]*\"/validate-with=\"none\"/') \ + ${HA_SBIN_DIR}/crm_mon \$*" -- $* +} + +# +# pseudo_resource status tracking function... +# +# This allows pseudo resources to give correct status information. As we add +# resource monitoring, and better resource tracking in general, this will +# become essential. +# +# These scripts work because ${HA_RSCTMP} is cleaned on node reboot. +# +# We create "resource-string" tracking files under ${HA_RSCTMP} in a +# very simple way: +# +# Existence of "${HA_RSCTMP}/resource-string" means that we consider +# the resource named by "resource-string" to be running. +# +# Note that "resource-string" needs to be unique. Using the resource type +# plus the resource instance arguments to make up the resource string +# is probably sufficient... +# +# usage: ha_pseudo_resource resource-string op [tracking_file] +# where op is {start|stop|monitor|status|restart|reload|print} +# print is a special op which just prints the tracking file location +# user can override our choice of the tracking file location by +# specifying it as the third arg +# Note that all operations are silent... +# +ha_pseudo_resource() +{ + local ha_resource_tracking_file="${3:-${HA_RSCTMP}/$1}" + case $2 in + start|restart|reload) touch "$ha_resource_tracking_file";; + stop) rm -f "$ha_resource_tracking_file";; + status|monitor) + if + [ -f "$ha_resource_tracking_file" ] + then + return 0 + else + case $2 in + status) return 3;; + *) return 7;; + esac + fi;; + print) echo "$ha_resource_tracking_file";; + *) return 3;; + esac +} + +# usage: rmtempdir TMPDIR +rmtempdir() +{ + [ $# = 1 ] || return 1 + if [ -e "$1" ]; then + rmdir "$1" || return 1 + fi + return 0 +} + +# usage: maketempfile [-d] +maketempfile() +{ + if [ $# = 1 -a "$1" = "-d" ]; then + mktemp -d + return 0 + elif [ $# != 0 ]; then + return 1 + fi + + mktemp + return 0 +} + +# usage: rmtempfile TMPFILE +rmtempfile () +{ + [ $# = 1 ] || return 1 + if [ -e "$1" ]; then + rm "$1" || return 1 + fi + return 0 +} + +# echo the first lower supported check level +# pass set of levels supported by the agent +# (in increasing order, 0 is optional) +ocf_check_level() +{ + local lvl prev + lvl=0 + prev=0 + if ocf_is_decimal "$OCF_CHECK_LEVEL"; then + # the level list should be very short + for lvl; do + if [ "$lvl" -eq "$OCF_CHECK_LEVEL" ]; then + break + elif [ "$lvl" -gt "$OCF_CHECK_LEVEL" ]; then + lvl=$prev # the previous one + break + fi + prev=$lvl + done + fi + echo $lvl +} + +# usage: ocf_stop_processes SIGNALS WAIT_TIME PIDS +# +# we send signals (use quotes for more than one!) in the order +# given; if one or more processes are still running we try KILL; +# the wait_time is the _total_ time we'll spend in this function +# this time may be slightly exceeded if the processes won't leave +# +# returns: +# 0: all processes left +# 1: some processes still running +# +# example: +# +# ocf_stop_processes TERM 5 $pids +# +ocf_stop_processes() { + local signals="$1" + local wait_time="$(($2/`echo $signals|wc -w`))" + shift 2 + local pids="$*" + local sig i + test -z "$pids" && + return 0 + for sig in $signals KILL; do + kill -s $sig $pids 2>/dev/null + # try to leave early, and yet leave processes time to exit + sleep 0.2 + for i in `seq $wait_time`; do + kill -s 0 $pids 2>/dev/null || + return 0 + sleep 1 + done + done + return 1 +} + +# +# create a given status directory +# if the directory path doesn't start with $HA_VARRUN, then +# we return with error (most of the calls would be with the user +# supplied configuration, hence we need to do necessary +# protection) +# used mostly for PID files +# +# usage: ocf_mkstatedir owner permissions path +# +# owner: user.group +# permissions: permissions +# path: directory path +# +# example: +# ocf_mkstatedir named 755 `dirname $pidfile` +# +ocf_mkstatedir() +{ + local owner + local perms + local path + + owner=$1 + perms=$2 + path=$3 + + test -d $path && return 0 + [ $(id -u) = 0 ] || return 1 + + case $path in + ${HA_VARRUN%/}/*) : this path is ok ;; + *) ocf_log err "cannot create $path (does not start with $HA_VARRUN)" + return 1 + ;; + esac + + mkdir -p $path && + chown $owner $path && + chmod $perms $path +} + +# +# create a unique status directory in $HA_VARRUN +# used mostly for PID files +# the directory is by default set to +# $HA_VARRUN/$OCF_RESOURCE_INSTANCE +# the directory name is printed to stdout +# +# usage: ocf_unique_rundir owner permissions name +# +# owner: user.group (default: "root") +# permissions: permissions (default: "755") +# name: some unique string (default: "$OCF_RESOURCE_INSTANCE") +# +# to use the default either don't set the parameter or set it to +# empty string ("") +# example: +# +# STATEDIR=`ocf_unique_rundir named "" myownstatedir` +# +ocf_unique_rundir() +{ + local path + local owner + local perms + local name + + owner=${1:-"root"} + perms=${2:-"755"} + name=${3:-"$OCF_RESOURCE_INSTANCE"} + path=$HA_VARRUN/$name + if [ ! -d $path ]; then + [ $(id -u) = 0 ] || return 1 + mkdir -p $path && + chown $owner $path && + chmod $perms $path || return 1 + fi + echo $path +} + +# +# RA tracing may be turned on by setting OCF_TRACE_RA +# the trace output will be saved to OCF_TRACE_FILE, if set, or +# by default to +# $HA_VARLIB/trace_ra/<type>/<id>.<action>.<timestamp> +# e.g. $HA_VARLIB/trace_ra/oracle/db.start.2012-11-27.08:37:08 +# +# OCF_TRACE_FILE: +# - FD (small integer [3-9]) in that case it is up to the callers +# to capture output; the FD _must_ be open for writing +# - absolute path +# +# NB: FD 9 may be used for tracing with bash >= v4 in case +# OCF_TRACE_FILE is set to a path. +# +ocf_bash_has_xtracefd() { + [ -n "$BASH_VERSION" ] && [ ${BASH_VERSINFO[0]} -ge 4 ] +} +# for backwards compatibility +ocf_is_bash4() { + ocf_bash_has_xtracefd +} +ocf_trace_redirect_to_file() { + local dest=$1 + if ocf_bash_has_xtracefd; then + exec 9>$dest + BASH_XTRACEFD=9 + else + exec 2>$dest + fi +} +ocf_trace_redirect_to_fd() { + local fd=$1 + if ocf_bash_has_xtracefd; then + BASH_XTRACEFD=$fd + else + exec 2>&$fd + fi +} +__ocf_test_trc_dest() { + local dest=$1 + if ! touch $dest; then + ocf_log warn "$dest not writable, trace not going to happen" + __OCF_TRC_DEST="" + __OCF_TRC_MANAGE="" + return 1 + fi + return 0 +} +ocf_default_trace_dest() { + tty >/dev/null && return + if [ -n "$OCF_RESOURCE_TYPE" -a \ + -n "$OCF_RESOURCE_INSTANCE" -a -n "$__OCF_ACTION" ]; then + local ts=`date +%F.%T` + __OCF_TRC_DEST=${OCF_RESKEY_trace_dir}/${OCF_RESOURCE_TYPE}/${OCF_RESOURCE_INSTANCE}.${__OCF_ACTION}.$ts + __OCF_TRC_MANAGE="1" + fi +} + +ocf_start_trace() { + export __OCF_TRC_DEST="" __OCF_TRC_MANAGE="" + case "$OCF_TRACE_FILE" in + [3-9]) ocf_trace_redirect_to_fd "$OCF_TRACE_FILE" ;; + /*/*) __OCF_TRC_DEST=$OCF_TRACE_FILE ;; + "") ocf_default_trace_dest ;; + *) + ocf_log warn "OCF_TRACE_FILE must be set to either FD (open for writing) or absolute file path" + ocf_default_trace_dest + ;; + esac + if [ "$__OCF_TRC_DEST" ]; then + mkdir -p `dirname $__OCF_TRC_DEST` + __ocf_test_trc_dest $__OCF_TRC_DEST || + return + ocf_trace_redirect_to_file "$__OCF_TRC_DEST" + fi + if [ -n "$BASH_VERSION" ]; then + PS4='+ `date +"%T"`: ${FUNCNAME[0]:+${FUNCNAME[0]}:}${LINENO}: ' + fi + set -x + env=$( echo; printenv | sort ) +} +ocf_stop_trace() { + set +x +} + +# Helper functions to map from nodename/bundle-name and physical hostname +# list_index_for_word "node0 node1 node2 node3 node4 node5" node4 --> 5 +# list_word_at_index "NA host1 host2 host3 host4 host5" 3 --> host2 + +# list_index_for_word "node1 node2 node3 node4 node5" node7 --> "" +# list_word_at_index "host1 host2 host3 host4 host5" 8 --> "" + +# attribute_target node1 --> host1 +list_index_for_word() { + echo $1 | tr ' ' '\n' | awk -v x="$2" '$0~x {print NR}' +} + +list_word_at_index() { + echo $1 | tr ' ' '\n' | awk -v n="$2" 'n == NR' +} + +ocf_attribute_target() { + if [ x$1 = x ]; then + if [ x$OCF_RESKEY_CRM_meta_container_attribute_target = xhost -a x$OCF_RESKEY_CRM_meta_physical_host != x ]; then + echo $OCF_RESKEY_CRM_meta_physical_host + else + if [ x$OCF_RESKEY_CRM_meta_on_node != x ]; then + echo $OCF_RESKEY_CRM_meta_on_node + else + ocf_local_nodename + fi + fi + return + elif [ x"$OCF_RESKEY_CRM_meta_notify_all_uname" != x ]; then + index=$(list_index_for_word "$OCF_RESKEY_CRM_meta_notify_all_uname" $1) + mapping="" + if [ x$index != x ]; then + mapping=$(list_word_at_index "$OCF_RESKEY_CRM_meta_notify_all_hosts" $index) + fi + if [ x$mapping != x -a x$mapping != xNA ]; then + echo $mapping + return + fi + fi + echo $1 +} + +ocf_promotion_score() { + ocf_version_cmp "$OCF_RESKEY_crm_feature_set" "3.10.0" + res=$? + if [ $res -eq 2 ] || [ $res -eq 1 ] || ! have_binary "crm_master"; then + ${HA_SBIN_DIR}/crm_attribute -p ${OCF_RESOURCE_INSTANCE} $@ + else + ${HA_SBIN_DIR}/crm_master -l reboot $@ + fi +} + +__ocf_set_defaults "$@" + +: ${OCF_TRACE_RA:=$OCF_RESKEY_trace_ra} +: ${OCF_RESKEY_trace_dir:="$HA_VARLIB/trace_ra"} +ocf_is_true "$OCF_TRACE_RA" && ocf_start_trace + +# pacemaker sets HA_use_logd, some others use HA_LOGD :/ +if ocf_is_true "$HA_use_logd"; then + : ${HA_LOGD:=yes} +fi |