#!/bin/bash

SAUNAFS_URAFT_OK=0
SAUNAFS_URAFT_ERROR=1
SAUNAFS_DATA_DIR="/var/lib/saunafs"
METADATA_LOCK="/var/lib/saunafs/metadata.sfs.lock"
SAUNAFS_URAFT_CONFIG="/etc/saunafs/saunafs-uraft.cfg"
SAUNAFS_MASTER_CONFIG="/etc/saunafs/sfsmaster.cfg"

metadata_location=


print_missing_config() {
	echo "$1 file is missing, it looks like a clean installation."
	echo "You can use example configuration and fill it with appropriate data:"
	echo "cp '/usr/share/doc/saunafs-uraft/examples/$1' '/etc/saunafs/$1'"
	exit $SAUNAFS_URAFT_ERROR
}

load_config() {
	if [ -f $SAUNAFS_MASTER_CONFIG ] ; then
		. <(sed "s:\ *=\ *:=:g" $SAUNAFS_MASTER_CONFIG | grep "ADMIN_PASSWORD")
		. <(sed "s:\ *=\ *:=:g" $SAUNAFS_MASTER_CONFIG | grep "DATA_PATH")
	else
		print_missing_config $SAUNAFS_MASTER_CONFIG
	fi

	if [ -f $SAUNAFS_URAFT_CONFIG ] ; then
		. <(sed "s:\ *=\ *:=:g" $SAUNAFS_URAFT_CONFIG)
	else
		print_missing_config $SAUNAFS_URAFT_CONFIG
	fi

	matocl_host=${LOCAL_MASTER_ADDRESS:=0}
	matocl_port=${LOCAL_MASTER_MATOCL_PORT:=9421}

	ipaddr=$(getent hosts ${URAFT_FLOATING_IP} | awk '{print $1}')
	netmask=${URAFT_FLOATING_NETMASK}
	iface=${URAFT_FLOATING_IFACE}

	ipaddr2=${URAFT_FLOATING_IP_SECONDARY:=}
	netmask2=${URAFT_FLOATING_NETMASK_SECONDARY:=}
	iface2=${URAFT_FLOATING_IFACE_SECONDARY:=}

	SAUNAFS_DATA_DIR=${DATA_PATH:="/var/lib/saunafs"}

	if [[ $ipaddr == "" || $netmask == "" || $iface == "" ]] ; then
		echo "Configuration file $SAUNAFS_URAFT_CONFIG does not contain valid network information."
		echo "See example /usr/share/doc/saunafs-uraft/examples/saunafs-uraft.cfg file for reference."
		exit $SAUNAFS_URAFT_ERROR
	fi

	if [[ ! $ADMIN_PASSWORD ]] ; then
		echo "SaunaFS admin password must be set in order to authenticate to master server."
		echo "See example /usr/share/doc/saunafs-master/examples/sfsmaster.cfg file for reference."
		exit $SAUNAFS_URAFT_ERROR
	fi
}

log() {
	logger -t saunafs-uraft $@
}

saunafs_master() {
	sfsmaster -o ha-cluster-managed "$@"
}

saunafs_ha_master() {
	sfsmaster -o ha-cluster-managed -o initial-personality=shadow "$@"
}

load_and_validate_elector_mode() {
	if [ -f "${SAUNAFS_URAFT_CONFIG}" ]; then
		# Get only uncommented lines
		URAFT_ELECTOR_MODE=$(grep '^[[:space:]]*URAFT_ELECTOR_MODE[[:space:]]*=' "${SAUNAFS_URAFT_CONFIG}" | \
			sed 's/^[[:space:]]*URAFT_ELECTOR_MODE[[:space:]]*=[[:space:]]*//' | \
			sed 's/[[:space:]]*$//' | \
			tail -1)  # Take the last occurrence if multiple exist

		# Set ${URAFT_ELECTOR_MODE}="0" (default value) when ${URAFT_ELECTOR_MODE} is empty or unset
		if [[ -z "${URAFT_ELECTOR_MODE}" ]]; then
			URAFT_ELECTOR_MODE="0"
		fi
	else
		print_missing_config "${SAUNAFS_URAFT_CONFIG}"
	fi

	if [[ "${URAFT_ELECTOR_MODE}" != "0" && "${URAFT_ELECTOR_MODE}" != "1" ]]; then
		log "Error: Invalid URAFT_ELECTOR_MODE value: ${URAFT_ELECTOR_MODE}. Must be 0 or 1."
		return 1
	fi
	echo "${URAFT_ELECTOR_MODE}"
}

start_saunafs_ha_master() {
	local status=0

	URAFT_ELECTOR_MODE=$(load_and_validate_elector_mode)

	if [[ "${URAFT_ELECTOR_MODE}" == "1" ]]; then
		# Handle the scenario of a MDS becoming ELECTOR, so we need to stop the master if it's running
		if pgrep -x "sfsmaster" > /dev/null; then
			log "Stopping sfsmaster..."
			saunafs_ha_master stop || status=$?
			if [[ $status -ne 0 ]]; then
				log "Stopping sfsmaster failed with status $status."
				return $status
			fi
		fi
	fi

	if [[ "${URAFT_ELECTOR_MODE}" == "0" ]]; then
		log "Starting sfsmaster..."
		saunafs_ha_master start || status=$?
		if [[ $status -ne 0 ]]; then
			log "Starting sfsmaster failed with status $status."
			return $status
		fi
	fi

	return $status
}

stop_saunafs_ha_master() {
	local status=0

	URAFT_ELECTOR_MODE=$(load_and_validate_elector_mode)

	if [[ "${URAFT_ELECTOR_MODE}" == "0" ]]; then
		log "Stopping sfsmaster..."
		saunafs_ha_master stop || status=$?
		if [[ $status -ne 0 ]]; then
			log "Stopping sfsmaster failed with status $status."
			return $status
		fi
	fi

	return $status
}

reload_saunafs_ha_master() {
	local status=0

	URAFT_ELECTOR_MODE=$(load_and_validate_elector_mode)

	if [[ "${URAFT_ELECTOR_MODE}" == "0" ]]; then
		log "Reloading sfsmaster..."
		saunafs_ha_master reload || status=$?
		if [[ $status -ne 0 ]]; then
			log "Reloading sfsmaster failed with status $status."
			return $status
		fi
	fi

	return $status
}

saunafs_admin() {
	saunafs-admin "$@"
}

get_metadata_version_from_file() {
	local version=$(/usr/sbin/sfsmetarestore -g -d "${SAUNAFS_DATA_DIR}" | grep -v warn 2> /dev/null)
	if [[ $? == 0 && ${version} =~ ^[0-9]+$ ]]; then
		echo -n "${version}"
	else
		echo -n 0
	fi
}

saunafs_promote() {
	saunafs_metadata_version 2> /dev/null
	if [[ ${metadata_location} == "disk" ]] ; then
		log "metadata is on disk instead of ram"
		saunafs_master stop
		unlink "${METADATA_LOCK}"
		saunafs_master -o initial-personality=master -o auto-recovery restart
		if [[ $? != 0 ]]; then
			log "promotion to master failed"
			exit $SAUNAFS_URAFT_ERROR
		fi
	else
		echo -n "${ADMIN_PASSWORD}" | \
			saunafs_admin promote-shadow "${matocl_host}" ${matocl_port}
	fi
	saunafs_assign_ip
}

saunafs_demote() {
	# drop ip
	saunafs_drop_ip
	# restart shadow
	saunafs_master -o initial-personality=shadow restart
}

saunafs_quick_stop() {
	echo -n "${ADMIN_PASSWORD}" | \
			saunafs_admin stop-master-without-saving-metadata "${matocl_host}" ${matocl_port}
}

saunafs_metadata_version() {
	# metadata-version can take arguments or read config file
	if [[ $# == 3 ]] ; then
		matocl_host=$2
		matocl_port=$3
	else
		load_config
	fi
	local admin_result=$(saunafs_admin metadataserver-status --porcelain "${matocl_host}" "$matocl_port")
	if [[ $? != 0 ]] ; then
		log "failed to query SaunaFS master status"
		return $SAUNAFS_URAFT_ERROR
	fi
	local personality=$(echo "$admin_result" | cut -f1)
	local connection=$(echo "$admin_result" | cut -f2)
	local metadata_version_in_ram=$(echo "$admin_result" | cut -f3)
	case "$personality/$connection" in
		master/running)
			metadata_location="ram"
			echo -n ${metadata_version_in_ram}
			return $SAUNAFS_URAFT_OK
		;;
		shadow/connected|shadow/disconnected)
			if (( metadata_version_in_ram > 0 )) ; then
				metadata_location="ram"
				echo -n ${metadata_version_in_ram}
				return $SAUNAFS_URAFT_OK
			else
				local metadata_version_on_disk=$(get_metadata_version_from_file)
				# Failing to read version from file results in metadata version 0,
				# which means that no metadata is available.
				metadata_location="disk"
				echo -n ${metadata_version_on_disk}
				return $SAUNAFS_URAFT_OK
			fi
		;;
		*)
			log "unexpected output from saunafs-admin: $admin_result"
			return $SAUNAFS_URAFT_OK
		;;
	esac
}

is_ip_present() {
	local ip="${1}"
	local iface="${2:-}"
	local -a ip_command_extra_args=()
	if [ -n "${iface}" ]; then
		ip_command_extra_args+=("dev" "${iface}")
	fi
	ip addr show "${ip_command_extra_args[@]}" | grep -wq -m1 "${ip}"
}

saunafs_is_floating_ip_present() {
	load_config
	if is_ip_present "${ipaddr}" "${iface}"; then
		echo -n alive
		return 0
	fi
	if [ -n "${ipaddr2}" ] && is_ip_present "${ipaddr2}" "${iface2}"; then
		echo -n alive
		return 0
	fi
	echo -n dead
	return 1
}

saunafs_isalive() {
	saunafs_master isalive
	if [[ $? == 0 ]] ; then
		echo -n alive
	else
		echo -n dead
	fi
}

saunafs_assign_ip() {
	load_config
	sudo ip -f inet addr add $ipaddr/$netmask dev $iface
	iface_base=$(echo $iface | cut -f1 -d':') # for alias handling
	arping -q -U -c5 -w1 $ipaddr -I $iface_base

	if [[ "$ipaddr2" != "" ]]; then
		sudo ip -f inet addr add $ipaddr2/$netmask2 dev $iface2
		iface_base2=$(echo $iface2 | cut -f1 -d':') # for alias handling
		arping -q -U -c5 -w1 $ipaddr2 -I $iface_base2
	fi
}

saunafs_drop_ip() {
	load_config
	sudo ip -f inet addr delete $ipaddr/$netmask dev $iface
	if [[ "$ipaddr2" != "" ]]; then
		sudo ip -f inet addr delete $ipaddr2/$netmask2 dev $iface2
	fi
}

saunafs_dead() {
	saunafs_drop_ip
}

print_help() {
	echo "SaunaFS uraft helper script"
	echo "Available commands:"
	echo "isalive"
	echo "metadata-version"
	echo "quick-stop"
	echo "promote"
	echo "demote"
	echo "assign-ip"
	echo "drop-ip"
	echo "dead"
	echo "is-floating-ip-alive"
	echo "start-saunafs-ha-master"
	echo "stop-saunafs-ha-master"
	echo "reload-saunafs-ha-master"
}

case "$1" in
	isalive)           saunafs_isalive;;
	metadata-version)  saunafs_metadata_version $@;;
	quick-stop)        saunafs_quick_stop;;
	promote)           saunafs_promote;;
	demote)            saunafs_demote;;
	assign-ip)         saunafs_assign_ip;;
	drop-ip)           saunafs_drop_ip;;
	dead)              saunafs_dead;;
	is-floating-ip-alive) saunafs_is_floating_ip_present;;
	start-saunafs-ha-master) start_saunafs_ha_master;;
	stop-saunafs-ha-master) stop_saunafs_ha_master;;
	reload-saunafs-ha-master) reload_saunafs_ha_master;;
	*)                 print_help;;

esac
