#!/bin/sh
#
#	$Id: ResourceManager,v 1.10 2001/02/07 07:10:21 alan Exp $
#
#	New haresources format:
#
#	machine resource resource resource resource
#
#	Where a resource can be an IP address or a scriptname, or a scriptname
#	and single argument.
#
#	When it's a scriptname with an argument, the argument is connected to
#	the scriptname with "::".  Another way of expressing an IP address is
#	via IPaddr::ip-address, since the script name IPaddr is the one assumed
#	for resources which are spelled like an IP address.
#
#	As an illustration, the following two lines are identical in effect:
#
#	node1	123.45.67.89 httpd
#	node1	IPaddr::123.45.67.89 httpd
#
#       One can also pass multiple arguments to a script by separating each
#       argument with a double colon:
#
#       node1  10.0.0.170 Filesystem::/dev/sda1::/data1::ext2
#
#	Note:  The first resource on the line must be unique in the haresources
#	file.  Maybe I should add a resource type called Unique which can
#	put on the front of a line to meet this requirement.
#
#

# set -x
: 
: 
: ==================== Starting ResourceManager $* ==========================
trap ': exiting $0; exit' 0
HA_DIR=/etc/ha.d
HA_FUNCS=$HA_DIR/shellfuncs
export HA_DIR HA_FUNCS
INITD=/etc/rc.d/init.d
USAGE="usage: $0 listkeys machine | takegroup key | givegroup key|status resource";
. $HA_FUNCS

isip() {
  case $1 in
    [0-9]*.[0-9]*.[0-9]*.[0-9]*)	true;;
    *)					false;;
  esac
}

reverseargs() {
  L=""
  for arg in $*
  do
    case $L in
      ?*)	L="$arg $L";;
      "")	L=$arg;;
    esac
  done
  echo $L
}

#
#	Remove comments and extra blanks from haresources
#	translate all white space into single blanks.  Each line ends with
#	a blank, making parsing it easier for dumb shell scripts.
#
ipres() {
  if
    [ $# -lt 1 ]
  then
    arg='.'
  else
    arg=$1
  fi
  cat $HA_DIR/haresources |
	#
	#	Explanation of Sed -e expressions below:
	#
	#	1) Strip out comments
	#	2) Append a blank to the end of the line
	#	3) Compress multiple blanks/tabs into a single blank
	#	4) Strip off a leading space (if any)
	#
	sed						\
		-e  's%#.*%%'				\
		-e :a -e '/\\$/N; s/\\\n//; ta'		\
		-e  's%$% %'				\
		-e  's%[	 ][	 ]*% %g' 	\
		-e  's%^ %%' 				|
  	egrep "$arg"
}

ipresline() {
	ipres " $1 "
}

KeyResources() {
	ipres "^$1 "  | cut -d' ' -f2
}

canonname() {
  if
    isip $1
  then
    echo "IPaddr::$1"
  else
    echo $1
  fi
}

resource2script() {
  case `canonname $1` in
    *::*)	echo $1 | sed 's%::.*%%'
		;;
    *)		echo $1;;
  esac
}

# Return the list of arguments after the script name.
# multiple arguments are separated by :: delimiters
resource2arg() {
  case `canonname $1` in
    *::*)	echo $1 | sed 's%[^:]*::%%' | sed 's%::% %g'
		;;
  esac
}

scriptpath() {
  script=`canonname $1`
  script=`resource2script $script`
  for dir in $HA_RESOURCEDIR $INITD
  do
    if
      [ -f $dir/$script -a -x $dir/$script ]
    then
      echo $dir/$script;
      return 0;
    fi
  done
  ha_log "ERROR: Cannot locate resource script $script"
  false
}

we_own_resource() {
  arg=`resource2arg $1`
  spath=`scriptpath $1`;

  case `$spath $arg status` in
    *[Rr]unning*|*OK*)	return 0;;
    *)			return 1;;
  esac
}


doscript() {
  script=`resource2script $1`
  arg=`resource2arg $1`

  spath=`scriptpath $script`
  if
    [ -f "$spath" -a -x "$spath" ]
  then
    ha_log "info: Running $spath $arg $2"
    ha_debug "debug: Starting $spath $arg $2"
    $spath $arg "$2" 2>>$HA_DEBUGLOG
    RC=$?
    ha_debug "debug: $spath $arg $2 done. RC=$RC"
    case $RC in
      0);;
      *) ha_log "warning: Return code $RC from $spath";;
    esac
    return 0;
  fi
  ha_log "ERROR: Cannot locate resource script $script"
  return 1;
}


# Arguments are: nodename resource1 resource2 ...
giveupresourcegroup() {
  if
    we_own_resource $2
  then
    : We own the resource.  Give it up!
  else
    return 0;  # Nothing to give up
  fi

  ha_log "info: Releasing resource group: $*"
  shift
  for j in `reverseargs $*`
  do
    doscript $j stop >>$HA_DEBUGLOG 2>&1
  done
}

# Arguments are: nodename resource1 resource2 ...
acquireresourcegroup() {
  if
    we_own_resource $2
  then
    return 0; # We already have it.
  fi

  ha_log "info: Acquiring resource group: $*"
  shift
  for j in $*
  do
    doscript $j start
  done
}

#
#	We may be given a resource to give up that we don't own...
#
GiveUpGroup() {
  #	Get the list of resources we've been requested to give up...
  haresources=`ipresline $1`
  if
    [ ! -z "$haresources" ]
  then
    giveupresourcegroup $haresources
  fi
}

TakeGroup() {
  #	Get the list of resources we've been requested to take...
  haresources=`ipresline $1`
  if
    [ ! -z "$haresources" ]
  then
    acquireresourcegroup $haresources
  fi
}

#
# Determine the status of all the resources in a resource group
#
# Results are echoed to stdout:
#
# NONE:	None of the resources in the resource group are held
#      (or there no such resource group)
# ALL:	All of the resources in the resource group are held
# SOME:	Some of the resources in the resource group are held
#
StatGroup() {
  result="FirstTime"

  for resource in `ipresline $1`
  do
    if
      we_own_resource $resource
    then
      case $result in
        FirstTime)	result=ALL;;
        NONE)		echo SOME; return 0;;
        SOME|ALL)		;;
      esac
    else
      case $result in
        FirstTime)	result=NONE;;
        ALL)		echo SOME; return 0;;
        SOME|NONE)	;;
      esac
    fi
  done

  case $result in
    FirstTime)	echo NONE;;
    *)		echo $result;;
  esac
}

#
#	Verify that all resources in the resource group are idle
#

VerifyAllIdle() {
  rc=0
  for rsc in `KeyResources ".*"`
  do
    if
      we_own_resource $rsc
    then
      echo "ERROR: Resource $rsc is active, and should not be." >&2
      ha_log "ERROR: Resource $rsc is active, and should not be."
      rc=`expr $rc + 1`
    fi
  done
  if
    [ $rc -ne 0 ]
  then
    ha_log "WARNING: Non-idle resources will affect resource takeback"
  fi
  exit $rc
}

case $1 in

  listkeys)	KeyResources "$2";;

  status)	doscript $2 status;;

  givegroup)	GiveUpGroup $2 >>$HA_DEBUGLOG 2>&1 ;;

  takegroup)	TakeGroup $2 >>$HA_DEBUGLOG 2>&1 ;;

  statgroup)	StatGroup $2 >>$HA_DEBUGLOG 2>&1 ;;

  verifyallidle)VerifyAllIdle ;;

  *)		echo "$USAGE\n" >&2
		exit 1;;
esac

#
#	$Log: ResourceManager,v $
#	Revision 1.10  2001/02/07 07:10:21  alan
#	Added code to verify that all resources are idle when starting heartbeat.
#	
#	Revision 1.9  2001/02/07 07:01:02  alan
#	Added a verifyallidle action to the ResourceManger for use in
#	startup scripts.
#	
#	Revision 1.8  2001/02/01 11:52:05  alan
#	Change things to that things occur in the right order.
#	We need to not start timing message reception until we're completely started.
#	We need to Stonith the other guy before we take over their resources.
#	
#	Revision 1.7  2000/12/06 09:11:31  jacob
#	Resources in ha.d/haresources can be split over more lines by concatenating
#	the lines with \ .
#	
#	Revision 1.6  2000/10/25 19:49:33  eric
#	Added the ability to specify multiple arguments to a resource script.
#	
#	Revision 1.5  2000/07/26 04:14:10  alan
#	Fixed a bug in a sed expression in ResourceManager
#	There was a missing 'g'.
#	
#	Revision 1.4  2000/06/12 06:11:09  alan
#	Changed resource takeover order to left-to-right
#	Added new version of nice_failback.  Hopefully it works wonderfully!
#	Regularized some error messages
#	Print the version of heartbeat when starting
#	Hosts now have three statuses {down, up, active}
#	SuSE compatability due to Friedrich Lobenstock and alanr
#	Other minor tweaks, too numerous to mention.
#	
#	Revision 1.3  2000/05/03 20:23:59  alan
#	Fixed a bug where the code was RedHat dependent.  It now allows the standard
#	SuSE OK response for status requests from scripts.
#	
#	Revision 1.2  1999/11/10 20:33:04  alan
#	Deleted /proc/ha directory from build list
#	Added #!/bin/sh to lots (all?) of the scripts...
#	
#	Revision 1.1.1.1  1999/09/23 15:31:24  alanr
#	High-Availability Linux
#	
#	Revision 1.7  1999/09/01 05:32:12  alanr
#	Put in a fix from Gregor Howey <ghowey@bremer-nachrichten.de>
#	where Gregor found that I had stripped off the ::resourceid
#	part of a string resulting in some bad calls later on.
#
#	It looks like he either always used IPaddr:xxx.yy.zzz or he actually
#	read the code and understood it.  Amazing!
#
#	Revision 1.6  1999/08/17 13:25:08  alanr
#	more comment changes.
#
#	Revision 1.5  1999/08/17 13:22:47  alanr
#	Fixed up comments on the format of the ipresources file.
#
#	Revision 1.4  1999/08/17 13:17:05  alanr
#	fixed up white space.
#
#	Revision 1.3  1999/08/17 13:16:07  alanr
#	Tidied up logging when running resource scripts.
#	Also added log messages.
#
#
