#!/bin/bash
#################################################################################
#                                                                               #
# iscsi-ha - High Availability framework for iSCSI cluster used in conjunction  #
# with XAPI based Xen Virtualization Environment (Xen Cloud Platform/XenServer) #
# Copyright 2021 Salvatore Costantino                                           #
# ha@pulsesupply.com                                                            #
#                                                                               #
#                                                                               #
#    iscsi-ha is free software: you can redistribute it and/or modify           #
#    it under the terms of the GNU General Public License as published by       #
#    the Free Software Foundation, either version 3 of the License, or          #
#    (at your option) any later version.                                        #
#                                                                               #
#    iscsi-ha 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 General Public License for more details.                               #
#                                                                               #
#    You should have received a copy of the GNU General Public License          #
#    along with iscsi-ha.  If not, see <http://www.gnu.org/licenses/>.          #
#                                                                               #
#################################################################################
#exec 2>/dev/null
CONF=/etc/iscsi-ha/iscsi-ha.conf
ENVIRO=/etc/iscsi-ha/iscsi-ha.load
STATUS_EXEC=/etc/iscsi-ha/scripts/status
CONFIG_EXEC=/etc/iscsi-ha/scripts/conif_manager

#################################
# Function get retrieves all
# params from configuration file
# and displays.
#################################
function get () {
	#clear
	if [ -a $CONF ]
	then
		echo -e "Displaying iscsi-ha configuration for $HOSTNAME\r\n"
		cat $CONF | grep -v '^#' | grep -v ^$
	else
		echo -e "iscsi-ha configuration file missing or not installed: $CONF"
	fi
} #End function get

###################################
# Function status - display
# running status and associated 
# application statuses
###################################
function display_status () {
	if [ -e $STATUS_EXEC ]
	then
		eval $STATUS_EXEC
	else
		red
		echo "Error: Missing file - $STATUS_EXEC needed for selected action. Check configuration"
		normal
	fi
} #End function status

##################################
# Function to view iscsi-ha real
# time log output
##################################
function display_log () {
	echo "This will display a real time log session for iscsi-ha"
	echo "Control + C to exit."
	sleep 2
	clear
	tail -f /var/log/user.log | grep iscsi-ha
} # End function display_log

function green () {
	echo -e -n "\E[32m$1"
}

function red () {
	echo -e -n "\E[31m$1"
}

function yellow () {
	echo -e -n "\E[33m$1"
}

function normal () {
	echo -e -n '\E[0m'
}

#################################
# Function to check if iscsi-ha
# service is running.
# Return 0 on true (running)
# Return 1 on false (stopped)
#################################
function check_service_run_state () {
	source /etc/iscsi-ha/iscsi-ha.load
	source /etc/iscsi-ha/iscsi-ha.conf
	source /etc/iscsi-ha/iscsi-ha.func
	service_execute $PROG_NAME status > /dev/null	
	RETVAL=$?
	if [ $RETVAL -ne 0 ]
	then
		return 1
	else
		return 0
	fi
} #end function check_service_run_state

#################################
# Function to enter manual mode
#################################
function manual_mode_enable () {

	# Make sure we are not already in manual mode
	source /etc/iscsi-ha/iscsi-ha.load
	if [ -e $IHA_STATE_PATH/manual ] 
	then
		echo "Manual mode already enabled - exiting"
		return 1
	fi
	
	# Enter manual mode
	MY_POOL_ROLE=$(cat /etc/xensource/pool.conf | awk -F ':' {'print $1'} | tr -d [[:space:]])
	if [ $MY_POOL_ROLE = "master" ]
	then
		echo "become_primary" > $IHA_STATE_PATH/manual
	else
		echo "become_secondary" > $IHA_STATE_PATH/manual
	fi

	if [ $? -eq 0 ]
	then
		echo "iscsi-ha now in manual mode"
		yellow
		echo "Note: High Availability should be disabled if any hosts will be shutdown or rebooted"
		normal
		return 0
	else
		echo "Failed to enter manual mode - reverting changes"
		chkconfig iscsi-ha on && chkconfig iscsi-ha-watchdog on && rm -f $IHA_STATE_PATH/manual > /dev/null
		return 1
	fi
} #end function manual_mode_enable

#################################
# Function to exit manual mode
#################################
function manual_mode_disable () {
	source /etc/iscsi-ha/iscsi-ha.load

	##########################################
	# Prevent exiting manual mode if current
	# role does not match role of host in pool
	# eg. master/primary slave/secondary
	##########################################
	MY_DRBD_ROLE=$(drbdadm role all | awk -F '/' {'print $1'} | tr -d [[:space:]])
	MY_POOL_ROLE=$(cat /etc/xensource/pool.conf | awk -F ':' {'print $1'} | tr -d [[:space:]])
	if [ "$MY_DRBD_ROLE" = "Primary" ] && [ $MY_POOL_ROLE != "master" ]
	then
		echo "This host's storage is in Primary mode but this is not the pool master"
		echo "demote this host's storage to Secondary before disabling manual mode"
		echo "Exiting.."
		exit 1
	fi
	
	if [ "$MY_DRBD_ROLE" = "Secondary" ] && [ $MY_POOL_ROLE != "slave" ]
	then
	        echo "This host's storage is in Secondary mode but this is not the pool slave"
	        echo "promote this host's storage to Primary before disabling manual mode"
		echo "Exiting.."
	        exit 1
	fi
	
	rm -f $IHA_STATE_PATH/manual > /dev/null
	
	if [ $? -eq 0 ]
	then
		echo "iscsi-ha exited manual mode"
		###################
		# Start services
		###################
		check_service_run_state
		RETVAL=$?
		if [ $RETVAL -eq 0 ]
		then
	        	echo "NOTICE: iscsi-ha is running"
		else
	        	service iscsi-ha start
		fi
		return $?
	else
		echo "Error detected while exiting manual mode"
		exit 1
	fi

} #end function manual_mode_disable

####################################
# Function become_primary - requires
# iscsi-ha to be in manual mode AND
# peer to be in secondary state
####################################
function become_primary () {
	source /etc/iscsi-ha/iscsi-ha.load
	source /etc/iscsi-ha/iscsi-ha.conf
	#Make sure we are in manual mode before proceeding
	if [ -e $IHA_STATE_PATH/manual ] 
	then
		###############################
		# Read configuration parameters
		# and functions library
		###############################
		source /etc/iscsi-ha/iscsi-ha.conf
		source /etc/iscsi-ha/iscsi-ha.func
	
		############################
		# Make sure peer host is not
		# in DRBD primary mode
		############################
		RESTORE_IFS=$IFS
		IFS=":"
		for i in ${DRBD_RESOURCES[@]}
		do
			ROLES=`drbdadm role $i`
			PEER_ROLE=`echo $ROLES | awk -F '/' {'print $2'} | tr -d [[:space:]]`
			if [ "$PEER_ROLE" = "Primary" ]
			then
				echo "Peer DRBD role detected as $PEER_ROLE"
				echo "Try making the peer secondary first"
				echo "Exiting.."
				exit 1
			fi
		done
		IFS=$RESTORE_IFS

		###########################
		# Make sure floating IP is
		# not present on network
		###########################
		local_ip_list
		if [ $? -eq 0 ]
		then
			for IPADDR in  ${LOCAL_IP_LIST[@]}
			do
				if [ $IPADDR = "$DRBD_VIRTUAL_IP" ]
				then
					VIP_IS_LOCAL=1
					break
				fi
			done
	
	                if [ "$VIP_IS_LOCAL" != "1" ]
	                then
				#################################
				# Make sure virtual IP is
				# not live on other host
				#################################
				check_ip_health $DRBD_VIRTUAL_IP 1
				if [ $? -eq 0 ]
				then
					echo "Virtual IP $DRBD_VIRTUAL_IP detected on network"
					echo "Make sure peer host is in manual/secondary mode"
					echo "Try running the following commands on the peer first"
					echo "iscsi-cfg manual-mode-enable"
					echo "iscsi-cfg become-secondary"
					echo "Exiting.."
					exit 1
				fi
			fi
		else
			echo "Error while detecting local IP addresses"
			exit 1
		fi

		########################
		# Apply the primary role
		########################
		/etc/iscsi-ha/iscsi-ha.sh become_primary &> /dev/null
		echo "become_primary" > $IHA_STATE_PATH/manual
		display_status
		return 0
	else
		red
		echo "Host must be in manual mode in order to perform this action"
		echo "Try 'iscsi-cfg manual-mode-enable' first"
		normal
		echo "Exiting.."
		exit 1
	fi

} #End function become_primary

####################################
# Function become_secondary - requires
# iscsi-ha to be in manual mode
####################################
function become_secondary () {
	source /etc/iscsi-ha/iscsi-ha.load
	source /etc/iscsi-ha/iscsi-ha.conf
	#Make sure we are in manual mode before proceeding
	if [ -e $IHA_STATE_PATH/manual ] 
	then
		###############################
		# Read configuration parameters
		# and functions library
		###############################
		source /etc/iscsi-ha/iscsi-ha.conf
		source /etc/iscsi-ha/iscsi-ha.func
		##########################
		# Apply the secondary role
		##########################
		/etc/iscsi-ha/iscsi-ha.sh become_secondary &> /dev/null
		echo "become_secondary" > $IHA_STATE_PATH/manual
		display_status
		return 0
	else
		red
		echo "Host must be in manual mode in order to perform this action"
		echo "Try 'iscsi-cfg manual-mode-enable' first"
		normal
		echo "Exiting.."
		exit 1
	fi	
} #End function become_secondary

##############################
# Function inserts default
# global parameters into xapi
# custom settings
##############################

function insert () {
	source /etc/iscsi-ha/iscsi-ha.load
	source /etc/iscsi-ha/iscsi-ha.func
	if [ -e "$THIS_POOL_UUID_FILE" ]
	then
		local POOL_UUID=$(< $THIS_POOL_UUID_FILE)
	else
		local POOL_UUID=$(xe pool-list --minimal)
	fi

	for i in $(cat /etc/iscsi-ha/scripts/install.params)
	do
		PARAM=$(echo $i | awk -F "=" '{print $1}')
		VALUE=$(echo $i | awk -F "=" '{print $2}')
		PARAM_EXISTS=$(xe pool-param-get uuid=$POOL_UUID param-name=other-config param-key=$PARAM &>/dev/null)
		if [ $? -ne 0 ]
		then
			xe pool-param-add uuid=$POOL_UUID param-name=other-config $PARAM=$VALUE
			echo "Inserting [$PREFIX$PARAM=$VALUE]"
		fi
	done
} #End function insert

##################################
# function manage_db_manual_mode
# Helper function to manage
# stateful mode used for maintenance
# operations. Stores a key=value
# pair in DB of
# host_uuid=[primary|secondary|enabled]
#
# Args passed in:
# arg1: "primary" or "secondary" or "enable"
#
# A host can be in 1 of 3 states when manual mode is enabled. 
# primary    follows iscsi-cfg become-primary
# secondary  follows iscsi-cfg become-secondary
# enable     follows iscsi-cfg manual-mode-enable
#
#
# Returns:
# 0 on success
# 1 on general error
#################################
function manage_db_manual_mode ()
{
	source /etc/iscsi-ha/iscsi-ha.load
	source /etc/iscsi-ha/iscsi-ha.func
	local NEW_MODE=$1
	if [ -e "$THIS_HOST_UUID_FILE" ]
	then
		local THIS_HOST_UUID=$(< $THIS_HOST_UUID_FILE)
	else
		local THIS_HOST_UUID=$($XE host-list hostname=$HOST --minimal)
	fi

	if [ -e "$THIS_POOL_UUID_FILE" ]
	then
		local THIS_POOL_UUID=$(< $THIS_POOL_UUID_FILE)
	else
		local THIS_POOL_UUID=$($XE pool-list --minimal)
	fi

	if [ ${#THIS_HOST_UUID} -ne 36 ]
	then
		echo "Error determining ID of this host"
		return 1
	fi
	
	##################################
	## Fetch current contents
	##################################
	local MANUAL_MODE_LIST=$($XE pool-param-get uuid=$THIS_POOL_UUID param-name=other-config param-key=MANUAL_MODE)
	local MANUAL_MODE_ARR=$(echo ${MANUAL_MODE_LIST} | tr ',' ' ')
	RESULT=''

	INSERTED="false"
	for string in ${MANUAL_MODE_ARR[@]}
	
	do
		THIS_STRING_UUID=$(echo $string | awk -F '=' {'print $1'})
		THIS_STRING_MODE=$(echo $string | awk -F '=' {'print $2'})
		if [ "$THIS_STRING_UUID" = "$THIS_HOST_UUID" ]
		then
			if [ "${NEW_MODE}" != "disable" ]
			then
				#We are replacing this string
				local NEW_STRING="${THIS_HOST_UUID}=${NEW_MODE}"
				if [ ${#RESULT} -gt 32 ]
				then
					RESULT+=",$NEW_STRING"
				else
					RESULT+=$NEW_STRING
				fi
				INSERTED="true"
			fi
		else
			#This UUID is not local - re-insert into array
			if [ ${#RESULT} -gt 32 ]
			then
				RESULT+=",${string}"
			else
				RESULT+=${string}
			fi
		fi
	done

	if [ "${INSERTED}" = "false" ] && [ "${NEW_MODE}" != "disable" ]
	then
		##############################
		## This is a new insert
		##############################
		local NEW_STRING="${THIS_HOST_UUID}=${NEW_MODE}"
		if [ ${#RESULT} -gt 32 ]
		then
			RESULT+=",${NEW_STRING}"
		else
			RESULT+=${NEW_STRING}
		fi
	fi

	xe pool-param-set uuid=$THIS_POOL_UUID other-config:MANUAL_MODE="${RESULT}"

} #End function manage_db_manual_mode

case $1 in
	get)
		get $2
		;;
	
	manual-mode-enable)
		manual_mode_enable && manage_db_manual_mode enable
		;;

	manual-mode-disable)
		manual_mode_disable && manage_db_manual_mode disable
		;;

	become-primary)
		become_primary && manage_db_manual_mode primary
		;;
	
	become-secondary)
		become_secondary && manage_db_manual_mode secondary
		;;
	log)
		display_log
		;;

	backup)
		${CONFIG_EXEC} save
		;;

	restore)
		${CONFIG_EXEC} restore
		;;

	status)
		display_status	
		;;
	insert)
		insert
		;;
	-v)
		eval $(cat $ENVIRO | grep -e "^VERSION=")
		echo $VERSION
		;;
	*)
		echo "iSCSI-HA Monitoring Tool: Add-on for HA-Lizard: XenServer/XCP High Availability"
		echo  -e "Usage: iscsi-cfg <action>\n"
		echo "Available actions:"
		echo "<log>:                  Watch iSCSI-HA log file output in real time" 
		echo "<get>:                  Lists all iSCSI-HA configuration parameters"
		echo "<backup>:               Copies all required configutation files"
		echo "                        lvm, drbd, tgt, iscsi-ha, iptables"
		echo "                        to pool DB. Allows for restoring configuration into"
		echo "                        the runtime environment when dom0 upgrades are performed which"
		echo "                        would normally erase settings."
		echo "<restore>:              Restores all centrally saved static configuration"
		echo "                        files and parameters. Allows for full restoration of"
		echo "                        runtime environment when performing a dom0 upgrade."
		echo "<manual-mode-enable>:   Enter manual mode - required to manually select roles"
		echo "                        Allows for manually moving iSCSI target to desired host"
		echo "                        Used to manage rolling updates and server reboots"
		echo "                        with no VM downtime."
		echo "<manual-mode-disable>:  Exit manual mode - automatic selection of roles enabled"
		echo "                        Operation returns to normal - iSCSI-HA manages roles"
		echo "<become-primary>:       Manually promotes host to primary role regardless of"
		echo "                        role of the host in the pool master/slave. Only works"
		echo "                        when operating in manual mode."
		echo "<become-secondary>:     Manually demotes host to secondary role regardless of"
		echo "                        role of the host in the pool master/slave. Only works"
		echo "                        when operating in manual mode."
		echo "<status>:               Displays the iSCSI-HA operational status"
		echo "<insert>:               Inserts required parameters into DB. Only required to"
		echo "                        repair a broken installation or DB map."
		echo "<-v>:                   Display Version"
		echo 
		;;
esac

