#!/bin/sh
#
#                       RESTRICTED RIGHTS LEGEND
# Use, duplication, or disclosure by the U.S. Government is subject to
# restrictions as set forth in sub-paragraph (c)(1)(ii) of the Rights in
# Technical Data and Computer Software clause in DFARS 252.227-7013.
#
# RSF-1 (c) Copyright High-Availability.Com Ltd, 1995-2014. All Rights Reserved.
#
#                       High-Availability.com Ltd
#                        Suite B, Pentland House
#                         Village Way, Wilmslow
#                           Cheshire  SK9 2GH
#                            United  Kingdom
#
#                   http://www.high-availability.com
#
#                   For UK support call 0844 736 1974
#                   Outside UK call +44 800 756 1198
#                (or email support@high-availability.com)
#
# Script:	S14res_drives
#
# Description:	PGR3 reservation.
#
# Platform:	Unix
#
# Author:	High-Availability.Com Ltd
#
# Rev:          $Id: S14res_drives,v 1.53 2015/09/29 12:52:57 matt Exp $
#
. /opt/HAC/bin/rsf.sh
. /opt/HAC/bin/mhd.sh

service=${RSF_SERVICE:-"service_name"}
script="`basename $0`"
state=$1

RES_DRIVES_FILE=/opt/HAC/RSF-1/etc/.res_drives.${service}
DISK_LIST=/opt/HAC/RSF-1/bin/disk_list

#
# S21res_drives is a copy of S14res_drives but we want it to run
# the "refresh" code instead of the "start" code
#
if [ "${script}" = "S21res_drives" ]; then
	[ "${state}" = "start" ] && state="refresh"
fi

#
# If this is being run as part of the resource check routine,
# just exit RSF_CHECK_NORESRC because this script does not
# claim any resources
#
if [ "$state" = "check" ]; then
	exit ${RSF_CHECK_NORESRC}
fi

#
# Exit OK now if reservations are disabled
#
check_res_disabled ${RSF_OK}

#
# If RSF_RESERVATION_DRIVE_1 has been set in the config file, then
# the user has specified reservation drives - this should be honoured
# so this script has nothing to do.
#
if [ -n "${RSF_RESERVATION_DRIVE_1}${RSF_RESERVATON_DRIVE_1}" ]; then
	rc_exit ${service} ${RSF_OK} "Reservation drives configured in config file - nothing to do"
fi

#
# Calculate total number of reservation drives
#
if [ "${PROP_MHDC_USE_PGR3}" = "${BOOL_STR_TRUE}" ]; then
	NUM_RES_DRIVES=${PROP_PGR3_DRIVE_COUNT}
else
	NUM_RES_DRIVES=${PROP_SCSI2_DRIVE_COUNT}
fi

dated_echo "#########################################################"
dated_echo "RSF-1 DB settings for reservations:"
dated_echo "prop_mhdc_use_pgr3:             ${PROP_MHDC_USE_PGR3}"
dated_echo "prop_pgr3_drive_count:          ${PROP_PGR3_DRIVE_COUNT}"
dated_echo "prop_scsi2_drive_count:         ${PROP_SCSI2_DRIVE_COUNT}"
dated_echo "prop_abort_unfenced_import:     ${PROP_ABORT_UNFENCED_IMPORT}"
dated_echo "prop_res_drive_test_retries:    ${PROP_RES_DRIVE_TEST_RETRIES}"
dated_echo "prop_res_drive_test_delay:      ${PROP_RES_DRIVE_TEST_DELAY}"
dated_echo "#########################################################"
dated_echo "Target minimum res drive count: ${NUM_RES_DRIVES}"
dated_echo "#########################################################"

# Function to copy res_drives file to other nodes in the cluster
# Takes no arguments, just copies RES_DRIVES_FILE
distribute_res_drives_file(){
	dated_echo "About to copy file `basename ${RES_DRIVES_FILE}` to remote hosts"
	${PROG_STMFHA} rsync ${RES_DRIVES_FILE}
	retval=$?
	if [ $retval -ne 0 ]; then
		dated_echo "Copy failed with `basename ${PROG_STMFHA}`"
	fi
	return $retval
}

case "$state" in

# S14res_drives will use start and stop
# S21res_drives will use refresh and stop
# Reconfigure script from config change will call S14res_drives refresh

# 'start'   creates the initial res_drives file if it doesn't exist
# 'refresh' quickly creates a new res_drives file and checks to see if it is different to the old one
# 'stop'    does nothing

    'start')
	if [ -f ${RES_DRIVES_FILE} ] && [ `${DISK_LIST} -f ${RES_DRIVES_FILE} -n 1 | wc -l` -eq 0 ]; then
	    # res_drives.service exists but disk_list can't find anything inside
	    # need to log this and remove the file so it will be recreated
	    dated_echo "Existing file ${RES_DRIVES_FILE} contains no valid reservation drives" WARN
	    dated_echo "Contents of ${RES_DRIVES_FILE}:"
	    cat ${RES_DRIVES_FILE}
	    dated_echo "Removing ${RES_DRIVES_FILE}"
	    rm -f ${RES_DRIVES_FILE}
	fi

	# If the file is to be used for reservations, make sure the drives (at least one) are available
	if [ -f "${RES_DRIVES_FILE}" ]; then
	    # Test each drive to see if it exists
	    dated_echo "${RES_DRIVES_FILE} found. Checking drives exist"

	    TEST_ARG="${NUM_RES_DRIVES},${PROP_RES_DRIVE_TEST_RETRIES},${PROP_RES_DRIVE_TEST_DELAY}"

	    dated_echo "Running ${DISK_LIST} -P -f ${RES_DRIVES_FILE} -t ${TEST_ARG}"

	    if ! ${DISK_LIST} -P -f ${RES_DRIVES_FILE} -t "${TEST_ARG}"; then
		#
		# May need to abort depending on DB property
		#
		curl -k X POST -H 'Content-Type: application/json' -d '{"svcname":"'"${service}"'", "message":"S14res_drives: All reservation drives are either missing or return I/O error"}' https://localhost:13514/ScriptAlert
		abort_unfenced_if_prop_true ${service} ${RSF_SAFE} "All reservation drives are either missing or return I/O error"
		#
		# Didn't abort, just give warning
		#
		rc_exit ${service} ${RSF_WARN}
	    fi
	    dated_echo "Found some/all reservation drives - continuing"
	else
	    # Now, create the res_drives file if it doesn't already exist

	    dated_echo "${RES_DRIVES_FILE} not found - attempting to create one..."

	    ${DISK_LIST} -s ${service} -f ${RES_DRIVES_FILE}
	    if [ ! -f ${RES_DRIVES_FILE} ] || [ `${DISK_LIST} -f ${RES_DRIVES_FILE} -n 1 | wc -l` -eq 0 ]; then
		dated_echo "No drives available for persistent reservations!" ERROR
		if [ -f ${RES_DRIVES_FILE} ]; then
		    dated_echo "Contents of ${RES_DRIVES_FILE}:"
		    cat ${RES_DRIVES_FILE}
		    dated_echo "Removing ${RES_DRIVES_FILE}"
		    rm -f ${RES_DRIVES_FILE}
		fi

		#
		# May need to abort depending on DB property
		#
		curl -k X POST -H 'Content-Type: application/json' -d '{"svcname":"'"${service}"'", "message":"S14res_drives: All reservation drives are either missing or return I/O error"}' https://localhost:13514/ScriptAlert
		abort_unfenced_if_prop_true ${service} ${RSF_SAFE} "No drives available for persistent reservations"
		#
		# Didn't abort, just give warning
		#
		rc_exit ${service} ${RSF_WARN}
	    fi
	    # New res_drives file needs to be copied to other nodes in the cluster
	    distribute_res_drives_file
	fi
	;;

'refresh')
	{
	if [ -z "${RSF_SERVICE}" ]; then
		rc_exit ${service} ${RSF_OK} "RSF_SERVICE environment variable not set" >&2
	fi
	zpool status -x ${service} >/dev/null 2>&1
	if [ $? -eq 0 ]; then
		# Service running on this node - refresh .res_drives file
		dated_echo "Service running on this node - refreshing ${RES_DRIVES_FILE}..."

		# Create new .res_drives file
		if ! ${DISK_LIST} -s ${service} -f ${RES_DRIVES_FILE}.new; then
			rc_exit ${service} ${RSF_OK} "Failed to generate new res_drives file - exiting"
		fi

		# If the new file is the same as the old one, exit now
                new_sum=$(sed "$(printf '/^#/d;s/[\t ]*#.*$//')" ${RES_DRIVES_FILE}.new | sum)
                old_sum=$(sed "$(printf '/^#/d;s/[\t ]*#.*$//')" ${RES_DRIVES_FILE}     | sum)

		if [ "${new_sum}" = "${old_sum}" ]; then
			rm ${RES_DRIVES_FILE}.new
			rc_exit ${service} ${RSF_OK} "No changes to reservation drives needed"
		fi

		# Now get the new and old lists of reservation drives
		pgr3_old_drives=
		scsi2_old_drives=
		mhdc_cache_read ${service} pgr3_old_drives scsi2_old_drives x
		OLD_RES_DRIVES="`echo ${pgr3_old_drives} ${scsi2_old_drives}`"
		if [ -z "${pgr3_old_drives}" -a -z "${scsi2_old_drives}" ]; then
			rm ${RES_DRIVES_FILE}.new
			rc_exit ${service} ${RSF_OK} "Failed to read old reservation list"
		fi
		local_res_drives=
		remote_res_drives=
		res_drives=
		check_for_reservation_drives ${service} --res-file=${RES_DRIVES_FILE}.new --with-unknowns
		NEW_RES_DRIVES="`echo ${local_res_drives} ${remote_res_drives} ${res_drives}`"
		if [ -z "${NEW_RES_DRIVES}" ] || \
		   [ -z "${res_drives}" -a -z "${remote_res_drives}" ] || \
		   expr " ${NEW_RES_DRIVES} " : ".* unknown .*" >/dev/null; then
			rm ${RES_DRIVES_FILE}.new
			rc_exit ${service} ${RSF_OK} "Failed to read new reservation list"
		fi

		# Work out which disks need to be reserved/released
		TO_RESERVE=
		for disk in ${NEW_RES_DRIVES}; do
			if ! expr " ${OLD_RES_DRIVES} " : ".* ${disk} .*" >/dev/null; then
				TO_RESERVE="${TO_RESERVE} ${disk}"
			fi
		done
		TO_RELEASE=
		for disk in ${OLD_RES_DRIVES}; do
			if ! expr " ${NEW_RES_DRIVES} " : ".* ${disk} .*" >/dev/null; then
				TO_RELEASE="${TO_RELEASE} ${disk}"
			fi
		done

		# Exit now if there's nothing to do
		if [ -z "${TO_RESERVE}" -a -z "${TO_RELEASE}" ]; then
			rm ${RES_DRIVES_FILE}.new
			rc_exit ${service} ${RSF_OK} "Generated same list of reservation drives - nothing to do"
		fi

		# Copy the new res_drives file to other nodes
		# If it fails, stop because we can't have mismatches between nodes
		mv ${RES_DRIVES_FILE} ${RES_DRIVES_FILE}.old
		mv ${RES_DRIVES_FILE}.new ${RES_DRIVES_FILE}
		if ! distribute_res_drives_file; then
			mv ${RES_DRIVES_FILE}.old ${RES_DRIVES_FILE}
			rc_exit ${service} ${RSF_OK} "Failed to distribute new res_drives file - aborting refresh"
		fi

		# Now make the changes to local reservations
		#
		# Releases come first in case any of the drives to reserve are actually
		# the same drives as the drives to release (using a different device
		# link, etc.). In that case it would be better to release and reserve
		# it again rather than reserve it when it is already reserved and then
		# release it.
                #
                # PGG/MG 10/02/2023
                # Now need to 'clean' disk names as BSD includes a \x in their iscsi names
                # so we need to use sed to remove them - long live bsd (grrrrr)
		pgr3_releases=
		scsi2_releases=
                pgr3_old_drives_cleaned=`echo ${pgr3_old_drives}|sed 's/\\\//g'`
		for disk in ${TO_RELEASE}; do
                    disk_cleaned=`echo ${disk}|sed 's/\\\//g'`
		    if expr " ${pgr3_old_drives_cleaned} " : ".* ${disk_cleaned} .*" >/dev/null; then
			pgr3_releases="${pgr3_releases} ${disk}"
		    else
			scsi2_releases="${scsi2_releases} ${disk}"
		    fi
		done
		if [ -n "${pgr3_releases}" ]; then
		    release_drives ${service} PGR-3 ${BOOL_STR_TRUE} ${pgr3_releases}
		fi
		if [ -n "${scsi2_releases}" ]; then
		    release_drives ${service} SCSI-2 ${BOOL_STR_TRUE} ${scsi2_releases}
		fi
		# Now the reserves
		if [ -n "${TO_RESERVE}" ]; then
		    if ! reserve_drives ${service} ${TO_RESERVE}; then
			dated_echo "Failed to reserve drives - retrying with S15zfs_mhdc" ERROR
			`dirname $0`/S15zfs_mhdc start
		    fi
		else
		    dated_echo "Nothing to reserve"
		fi
	fi
	}&
	;;

'stop')
	rc_exit ${service} ${RSF_OK} "No stop actions to perform"
	;;

'check')
	exit ${RSF_CHECK_NORESRC}
	;;

*)
	rc_exit ${service} ${RSF_WARN} "usage: $0 <start|stop|check>"
	;;

esac

rc_exit ${service} ${RSF_OK}
