#!/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/>.          #
#                                                                               #
#################################################################################

ISCSI_HA_INIT='/etc/iscsi-ha/iscsi-ha.load'
ISCSI_HA_FUNCTIONS='/etc/iscsi-ha/iscsi-ha.func'
IHA_IHA_CONF_FILE='/etc/iscsi-ha/iscsi-ha.conf'
IHA_DRBD_CONF_FILE='/etc/drbd.conf'
IHA_TGTD_CONF_FILE='/etc/tgt/conf.d/ha-targets.conf'
IHA_TGTD_ALTERNATE_CONF_FILE='/etc/tgt/targets.conf'
IHA_LVM_CONF_FILE='/etc/lvm/lvm.conf'
IHA_LVM_MASTER_CONF_FILE='/etc/lvm/master/lvm.conf'
IHA_IPTABLES_CONF_FILE='/etc/sysconfig/iptables'
ARCHIVE_FOLDER='/home'
LVM_FILTER=''
POOL_UUID=$(xe pool-list --minimal)
declare -a CONFIG_MAP
CONFIG_MAP=(IHA_IHA_CONF IHA_DRBD_CONF IHA_TGTD_CONF IHA_TGTD_ALTERNATE_CONF IHA_LVM_CONF IHA_LVM_MASTER_CONF IHA_IPTABLES_CONF)
declare -a CONF_FILE_LIST
CONF_FILE_LIST=($IHA_IHA_CONF_FILE $IHA_DRBD_CONF_FILE $IHA_TGTD_CONF_FILE $IHA_TGTD_ALTERNATE_CONF_FILE $IHA_LVM_CONF_FILE $IHA_LVM_MASTER_CONF_FILE)
THE_ACTION=$1
SUCCESS=true

if [ ! $1 ]
then
	echo "Missing madatory argument[1] save|restore"
	exit 1
fi

function exit_with_error ()
{
	echo "$1"
	exit 1
}

function warning ()
{
	echo "$1"
	SUCCESS=false
}
#############################
## function insert_into_map
##
## inserts encoded string
## of the file contents
## or string content
## into pool configuration
## for the passed filename
##
## arg1: parameter key
## arg2: configuration file
############################
function insert_into_map ()
{
	local PARAM_KEY=$1
	if [ -e "$2" ]
	then
		#we are processing a file
		local PARAM_VAL=$(cat $2 | base64 -w 0)
	else
		#we are processing a string
		local PARAM_VAL=$(echo -n $2 | base64 -w 0)
	fi
	xe pool-param-set uuid=${POOL_UUID} other-config:${PARAM_KEY}="${PARAM_VAL}"
} #End function insert_into_map

#############################
## function restore_from_map
##
## restores configuration
## data from pool database
## into target configuration
## file.
##
## arg1: parameter key
## arg2: configuration file
############################
function restore_from_map ()
{
	local PARAM_KEY=$1
	local PARAM_VAL=$(xe pool-param-get uuid=${POOL_UUID} param-name=other-config param-key=${PARAM_KEY})
	local PARAM_FILE=$2
	if [[ "${PARAM_KEY}" == *IPTABLES* ]]
	then
		#############################################
		## we are processing a FW sctipt - do not
		## restore since future releses of
		## XCP/XenServer could change the default
		## script and we don't want to get in the way.
		## Replication network FW rules are now managed by
		## iscsi-ha init script at runtime instead
		#############################################
		echo "Not Restoring file [${PARAM_FILE}]. Use fw_init instead"
		return 0

	elif [[ "${PARAM_KEY}" == *LVM* ]]
	then
		local THIS_STRING_CONTENT=$(echo ${PARAM_VAL} | base64 -d)
		local THIS_PARAM_CONTENT=$(echo ${PARAM_VAL} | base64 -d)
		if [ ${#THIS_PARAM_CONTENT} -eq 0 ]
		then
			echo "Parameter [${PARAM_KEY}] is empty. Not restoring an empty value."
			return 1
		fi
		#############################################
		# we are processing a string in lvm.conf
		# if filter exists, replace it
		# else, find position in file and insert new
		#############################################
		if [ $(cat ${PARAM_FILE} | grep -c -E ^[[:space:]]*filter) -eq 0 ]
		then
			#insert new filter
			local LVM_DEVICES_LINE_BEGIN=$(cat /etc/lvm/lvm.conf | grep -n -E ^[[:space:]]*devices | awk -F ':' {'print $1'})
			local LVM_INSERT_ROW_NUMBER=$(( $LVM_DEVICES_LINE_BEGIN + 1 ))
			echo "Inserting filter into [${PARAM_FILE}] row [${LVM_INSERT_ROW_NUMBER}]"
			sed -i "${LVM_INSERT_ROW_NUMBER}i ${THIS_STRING_CONTENT}" ${PARAM_FILE}
		else	
			#replace existing filter
			local FILTER_LINE_NUMBER=$(cat ${PARAM_FILE} | grep -n -E ^[[:space:]]*filter | awk -F ':' {'print $1'})
			echo "Replacing filter in [${PARAM_FILE}] row [${FILTER_LINE_NUMBER}]"
			sed -i "${FILTER_LINE_NUMBER}s!.*!${THIS_STRING_CONTENT}!" ${PARAM_FILE}
		fi
	else
		#we are processing a file
		local THIS_FILE_CONTENT=$(echo ${PARAM_VAL} | base64 -d)
		echo "Restoring file [${PARAM_FILE}] from pool configuration"
		if [ ${#THIS_FILE_CONTENT} -gt 0 ]
		then
			echo "${THIS_FILE_CONTENT}" > ${PARAM_FILE} 
		else
			echo "Parameter [${PARAM_KEY}] is empty. Not restoring an empty file."
		fi
	fi

} #End function restore_from_map

###############################
## function archive_config
##
## creates a tarball including
## all relevent configuration
## files in a 2-node
## hyperconverged HA-Lizard
## cluster/pool
##############################
function archive_config ()
{
	local NOW=$(date +%s)
	local ARCHIVE_FILE="${ARCHIVE_FOLDER}/ha-lizard-nosan-conf-files-all-${NOW}.tgz"
	local TAR_EXEC="tar -czf ${ARCHIVE_FILE} "
	for filename in ${CONF_FILE_LIST[@]}
	do
		if [ -e $filename ]
		then
			echo "Adding [$filename] to archive"
			TAR_EXEC+="$filename "
		fi
	done
	echo "Creating archive [${ARCHIVE_FILE}]"
	${TAR_EXEC}
	return $?
} #End function archive_config

#############################
## Load shared functions
## and environment
#############################
if [ -e ${ISCSI_HA_INIT} ]
then
	source ${ISCSI_HA_INIT}
else
	echo "Missing required environment [${ISCSI_HA_INIT}]"
	exit 1
fi

if [ -e ${ISCSI_HA_FUNCTIONS} ]
then
	source ${ISCSI_HA_FUNCTIONS}
else
	echo "Missing shared functions [${ISCSI_HA_FUNCTIONS}]"
	exit 1
fi



#############################
## Insert config param if
## ! exist already
#############################
for param in ${CONFIG_MAP[@]}
do
	xe pool-param-get uuid=${POOL_UUID} param-name=other-config param-key=$param &>/dev/null
	RETVAL=$?
	if [ $RETVAL -ne 0 ]
	then
		echo "Adding missing configuration parameter [$param]"
		xe pool-param-add uuid=${POOL_UUID} param-name=other-config $param
		RETVAL=$?
		if [ $RETVAL -ne 0 ]
		then
			echo "Error adding parameter [$param}] to database"
			exit 1
		fi
	fi
done

case ${THE_ACTION} in
	save)
		for param in ${CONFIG_MAP[@]}
		do
			FILE_NAME=$param
			FILE_NAME+=_FILE
			if [ -z ${!FILE_NAME} ]
			then
				continue
			fi
			echo "Processing [${!FILE_NAME}]"
			if [[ "${FILE_NAME}" != *LVM* ]] #########&& [[ "${FILE_NAME}" != *IPTABLES* ]]
			then
				insert_into_map "${param}" "${!FILE_NAME}" || warning "Error processig [${!FILE_NAME}]"
			else
				###########################
				## Special case for LVM
				## we only store the filter
				## for LVM
				###########################
				if [[ "${FILE_NAME}" == *LVM* ]]
				then
					THIS_FILTER=$(cat ${!FILE_NAME} | grep -E ^[[:space:]]*filter)
					insert_into_map "${param}" "${THIS_FILTER}" || warning "Error processig [${!FILE_NAME}]"
				fi
			fi
		done
	;;

	restore)
		############################
		## Archive the current
		## config before restoring
		############################
		archive_config

		for param in ${CONFIG_MAP[@]}
		do
			FILE_NAME=$param
			FILE_NAME+=_FILE
			#echo "Processing [${!FILE_NAME}]"
			restore_from_map "${param}" "${!FILE_NAME}" || warning "Error processig [${!FILE_NAME}]"
		done
	;;		

	delete)
		###########################
		## WARNING - this deletes
		## all cofig data and
		## keys from DB
		###########################
		echo "W A R N I N G"
		echo "Are you sure you want to delete all database paameters and stored configuration data [y|n]?"
		read N1
		if [ "$N1" = "y" ]
		then
			for key in ${CONFIG_MAP[@]}
			do
				echo "Removing [${key}]"
				xe pool-param-remove uuid=${POOL_UUID} param-name=other-config param-key=${key}
			done
		else
			echo "Exiting..."
			exit 0
		fi
	;;

	*)
		echo "[$1] is not a valid input paramter"
	;;
esac

if [ "${SUCCESS}" = "false" ]
then
	echo "Job completed with errors"
	exit 1
else
	echo "job completed successfully"
	exit 0
fi
