#!/bin/bash
# Copyright (c) 2010-2012 Postgres-XC Development Group

#Scripts to launch DDL in PGXC cluster using a cold_backup method
#Be sure to have set a correct ssl environment in all the servers of the cluster

#This script uses pgxc.conf as a base to find the settings of all the coordinators

#Options possible to use for this script
# -D to locate the data folder, necessary to find pgxc.conf, containing the characteristics of all the coordinators
# -l to locate the folder where applications are
# -f for a DDL file
# -d for a Database name
# -n coordinator number where to launch DDl, number based on the one written in pgxc.conf
# -t base name of folder where to save configuration files, by default /tmp/pgxc_config, completed by $$

count=0

#Default options
#local folder used to save temporary the configuration files of coordinator's data folder being erased
CONFIG_FOLDER=/tmp/pgxc_config_files.$$
PGXC_BASE=
#options to launch the coordinator
#don't forget to add -i as we are in a cluster :)
COORD_OPTIONS="-C -i"

#-----------------------------------------------------------------------
# Option Management
#-----------------------------------------------------------------------
while getopts 'f:d:D:l:hn:t:' OPTION
do
	count=$((count +2))
	case $OPTION in
	d)	#for a database name
		DB_NAME="$OPTARG"
		;;

	D)	#for a data folder, to find pgxc.conf
		DATA_FOLDER="$OPTARG"
		;;

	f)	#for a DDL file
		DDL_FILE_NAME="$OPTARG"
		;;

	l)	#To define folder where applications are if necessary
		PGXC_BASE="$OPTARG"/
		;;

	n)	#for a coordinator number
		COORD_NUM_ORIGIN="$OPTARG"
		;;

	h)	printf "Usage: %s: [-d dbname] [-l bin folder] [-D data folder] [-n coord number] [-f ddl file] [-t save folder name in /tmp/]\n" $(basename $0) >&2
		exit 0
		;;
	t)	#to set the name of the folder where to save conf files. All is mandatory saved in /tmp 
		CONFIG_FOLDER=/tmp/"$OPTARG"
		;;

	?)	printf "Usage: %s: [-d dbname] [-l bin folder] [-D data folder] [-n coord number] [-f ddl file] [-t save folder name in /tmp/]\n" $(basename $0) >&2
		exit 0
		;;
	esac
done

if [ $# -lt "1" ]
then
	echo "No arguments defined, you should try help -h"
	exit 2
fi

#A couple of option checks
if [ "$count" -ne "$#" ]
then
	echo "Arguments not correctly set, try -h for help"
	exit 2
fi

if [ -z $COORD_NUM_ORIGIN ]
then
	echo "Coordinator number not defined, mandatory -n argument missing"
	exit 2
fi
if [ -z $DATA_FOLDER ]
then
	echo "Data folder not defined, mandatory -D argument missing"
	exit 2
fi

#Check if Argument of -n is an integer
if [ ! $(echo "$COORD_NUM_ORIGIN" | grep -E "^[0-9]+$") ]
	then
	echo "Argument -n is not a valid integer"
	exit 2
fi

#Check if DDL file exists
if [ "$DDL_FILE_NAME" != "" ]
then
	if [ ! -e $DDL_FILE_NAME ]
	then
		echo "DDL file not defined"
		exit 2
	fi
	if [ -z $DB_NAME ]
	then
		echo "Dbname not defined, mandatory -d argument missing when using a ddl file"
		exit 2
	fi
fi

#-----------------------------------------------------------------------
# Begin to read the pgxc.conf to get coordinator characteristics
#-----------------------------------------------------------------------
PGXC_CONF=$DATA_FOLDER/pgxc.conf

if [ ! -e $PGXC_CONF ]
then
	echo "pgxc.conf not defined in the directory defined by -D"
	exit 2
fi

#Find parameters
hosts=`cat $PGXC_CONF | grep coordinator_hosts | cut -d "'" -f 2`
ports=`cat $PGXC_CONF | grep coordinator_ports | cut -d "'" -f 2`
folders=`cat $PGXC_CONF | grep coordinator_folders | cut -d "'" -f 2`
if [ "$hosts" = "" ]
then
	echo "coordinator_hosts not defined in pgxc.conf"
	exit 2
fi
if [ "$ports" = "" ]
then
	echo "coordinator_ports not defined in pgxc.conf"
	exit 2
fi
if [ "$folders" = "" ]
then
	echo "coordinator_folders not defined in pgxc.conf"
	exit 2
fi

#Check if the strings are using commas as separators
hosts_sep="${hosts//[^,]/}"
ports_sep="${ports//[^,]/}"
folders_sep="${folders//[^,]/}"
if [ "$hosts_sep" = "" ]
then
	echo "coordinator_hosts should use commas as a separator"
	exit 2
fi
if [ "$ports_sep" = "" ]
then
	echo "coordinator_ports should use commas as a separator"
	exit 2
fi
if [ "$folders_sep" = "" ]
then
	echo "coordinator_folders should use commas as a separator"
	exit 2
fi


#-----------------------------------------------------------------------
# Fill in Arrays that are used for the process from pgxc configuration file
#-----------------------------------------------------------------------

count=1
#Coordinator list
host_local=`echo $hosts |  cut -d "," -f $count`
while [ "$host_local" != "" ]
do
	COORD_HOSTNAMES[$((count -1))]=`echo $host_local`
	count=$((count +1))
	host_local=`echo $hosts | cut -d "," -f $count`
done
COORD_COUNT=${#COORD_HOSTNAMES[*]}

#Port list corresponding to the coordinators
#If all the coordinators use the same port on different servers,
#it is possible to define that with a unique element array.
count=1
port_local=`echo $ports |  cut -d "," -f $count`
while [ "$port_local" != "" ]
do
	COORD_PORTS[$((count -1))]=$port_local
	count=$((count +1))
	port_local=`echo $ports |  cut -d "," -f $count`
done
COORD_PORTS_COUNT=${#COORD_PORTS[*]}

#Data folder list corresponding to the coordinators
#If all the coordinators use the same data folder name on different servers,
#it is possible to define that with a unique element array.
count=1
folder_local=`echo $folders | cut -d "," -f $count`

while [ "$folder_local" != "" ]
do
	COORD_PGDATA[$((count -1))]=$folder_local
	count=$((count +1))
	folder_local=`echo $folders | cut -d "," -f $count`
done
COORD_PGDATA_COUNT=${#COORD_PGDATA[*]}


#-----------------------------------------------------------------------
# Start DDL process
#-----------------------------------------------------------------------

#It is supposed that the same bin folders are used among the servers
#to call postgres processes
#This can be customized by the user with option -l
COORD_SERVER_PROCESS=postgres
PGCTL_SERVER_PROCESS=pg_ctl
PSQL_CLIENT_PROCESS=psql

COORD_SERVER=$PGXC_BASE$COORD_SERVER_PROCESS
PGCTL_SERVER=$PGXC_BASE$PGCTL_SERVER_PROCESS
PSQL_CLIENT=$PGXC_BASE$PSQL_CLIENT_PROCESS

#reajust coord number with index number
COORD_NUM_ORIGIN=$((COORD_NUM_ORIGIN -1))

#check data validity
#Note: Add other checks here

if [ $COORD_COUNT -eq "1" ]
then
	echo "Are you sure you want to use this utility with one only coordinator??"
	exit 2
fi

if [ $COORD_PGDATA_COUNT -ne $COORD_COUNT ]
then
	echo "Number of pgdata folders must be the same as coordinator server number"
	exit 2
fi

if [ $COORD_PORTS_COUNT -ne $COORD_COUNT ]
then
	echo "Number of coordinator ports defined must be the same as coordinator server number"
	exit 2
fi

#Check if coordinator number is not outbounds
if [ $COORD_NUM_ORIGIN -gt $((COORD_COUNT -1)) ]
then
	echo "coordinator number is out of bounds"
	exit 2
fi
COORD_ORIG_INDEX=$COORD_NUM_ORIGIN

#Check if the data folders are defined
for index in ${!COORD_HOSTNAMES[*]}
do
	targethost=${COORD_HOSTNAMES[$index]}
	targetdata=${COORD_PGDATA[$index]}
	if [[ `ssh $targethost test -d $targetdata && echo exists` ]]
	then
		echo "defined directory exists for "$targethost
	else
		echo "defined directory does not exist for "$targethost
		exit 2
	fi
done

#Origin Coordinator Index has been found?
if [ -z $COORD_ORIG_INDEX ]
then
	echo "origin coordinator is not in the coordinator list"
	exit 2
fi

#Main process begins

#Check if the database is defined, This could lead to coordinator being stopped uselessly
if [ "$DB_NAME" != "" ]
then
	#Simply launch a fake SQL on the Database wanted
	$PSQL_CLIENT -h ${COORD_HOSTNAMES[$COORD_ORIG_INDEX]} -p ${COORD_PORTS[$COORD_ORIG_INDEX]} -c 'select now()' -d $DB_NAME; err=$?
	if [ $err -gt "0" ]
	then
		echo "Database not defined"
		exit 2
	fi
fi

#1) stop all the coordinators
echo "Stopping all the coordinators"
for index in ${!COORD_HOSTNAMES[*]}
do
	targethost=${COORD_HOSTNAMES[$index]}
	targetdata=${COORD_PGDATA[$index]}
	echo ssh $targethost $PGCTL_SERVER stop -D $targetdata
	ssh $targethost $PGCTL_SERVER stop -D $targetdata; err=$?
	if [ $err -gt "0" ]
	then
		"pg_ctl couldn't stop server"
		exit 2
	fi
done

#If a DDL file is not set by the user, just synchronize the catalogs with the catalog of the chosen coordinator
if [ "$DDL_FILE_NAME" != "" ]
then
	echo "-f activated, DDL being launched"

	#2) restart the one we want to launch DDL to...
	echo ssh ${COORD_HOSTNAMES[$COORD_ORIG_INDEX]} $COORD_SERVER $COORD_OPTIONS -p ${COORD_PORTS[$COORD_ORIG_INDEX]} -D ${COORD_PGDATA[$COORD_ORIG_INDEX]}
	ssh ${COORD_HOSTNAMES[$COORD_ORIG_INDEX]} $COORD_SERVER $COORD_OPTIONS -p ${COORD_PORTS[$COORD_ORIG_INDEX]} -D ${COORD_PGDATA[$COORD_ORIG_INDEX]} &

	#wait a little bit to be sure it switched on
	sleep 3

	#3) launch the DDL
	#This has to be done depending on if the user has defined a file or a command
	echo $PSQL_CLIENT -h ${COORD_HOSTNAMES[$COORD_ORIG_INDEX]} -p ${COORD_PORTS[$COORD_ORIG_INDEX]} -f $DDL_FILE_NAME -d $DB_NAME
	$PSQL_CLIENT -h ${COORD_HOSTNAMES[$COORD_ORIG_INDEX]} -p ${COORD_PORTS[$COORD_ORIG_INDEX]} -f $DDL_FILE_NAME -d $DB_NAME; err=$?
	if [ $err -gt "0" ]
	then
		echo "psql error, is Database defined?"
		exit 2
	fi

	#4) Stop again the origin coordinator as we cannot copy the lock files to other coordinators
	echo ssh ${COORD_HOSTNAMES[$COORD_ORIG_INDEX]} $PGCTL_SERVER stop -D ${COORD_PGDATA[$COORD_ORIG_INDEX]}
	ssh ${COORD_HOSTNAMES[$COORD_ORIG_INDEX]} $PGCTL_SERVER stop -D ${COORD_PGDATA[$COORD_ORIG_INDEX]}; err=$?
	if [ $err -gt "0" ]
	then
		"pg_ctl couldn't stop server"
		exit 2
	fi
fi

#5) before copying the catalogs, save the configuration files or they are erased by the catalog copy
#make a copy of them in a folder in /tmp/pgxc_conf (default folder)
if [ -d $CONFIG_FOLDER ]
then
	rm -rf $CONFIG_FOLDER
fi
mkdir $CONFIG_FOLDER

for index in ${!COORD_HOSTNAMES[*]}
do
	if [ $index -ne $COORD_ORIG_INDEX ]
	then
		targethost=${COORD_HOSTNAMES[$index]}
		targetdata=${COORD_PGDATA[$index]}
		echo scp -pr $targethost:$targetdata/postgresql.conf $CONFIG_FOLDER/postgresql.conf.$index
		echo scp -pr $targethost:$targetdata/pg_hba.conf $CONFIG_FOLDER/pg_hba.conf.$index
		scp -pr $targethost:$targetdata/postgresql.conf $CONFIG_FOLDER/postgresql.conf.$index; err=$?
		if [ $err -gt "0" ]
		then
			echo "deleting saved configuration files"
			rm -rf $CONFIG_FOLDER
			echo "scp failed with "$targethost
			exit 2
		fi
		scp -pr $targethost:$targetdata/pg_hba.conf $CONFIG_FOLDER/pg_hba.conf.$index; err=$?
		if [ $err -gt "0" ]
		then
			echo "deleting saved configuration files"
			rm -rf $CONFIG_FOLDER
			echo "scp failed with "$targethost
			exit 2
		fi
	fi
done

#6) copy catalog files to all coordinators but not to the origin one
for index in ${!COORD_HOSTNAMES[*]}
do
	if [ $index -ne $COORD_ORIG_INDEX ]
	then
		srchost=${COORD_HOSTNAMES[$COORD_ORIG_INDEX]}
		srcdata=${COORD_PGDATA[$COORD_ORIG_INDEX]}
		targethost=${COORD_HOSTNAMES[$index]}
		targetdata=${COORD_PGDATA[$index]}
		#First erase the data to have a nice cleanup
		echo ssh $targethost rm -rf $targetdata
		ssh $targethost rm -rf $targetdata

		#Just to be sure that catalog files of origin coordinator are copied well
		echo scp -pr $srchost:$srcdata $targethost:$targetdata
		scp -pr $srchost:$srcdata $targethost:$targetdata; err=$?
		if [ $err -gt "0" ]
		then
			echo "deleting saved configuration files"
			rm -rf $CONFIG_FOLDER
			echo "scp failed with "$targethost
			exit 2
		fi
	fi
done

#7) copy back the configuration files to the corresponding fresh folders
#but not the configuration files of the origin coordinator
for index in ${!COORD_HOSTNAMES[*]}
do
	if [ $index -ne $COORD_ORIG_INDEX ]
	then
		targethost=${COORD_HOSTNAMES[$index]}
		targetdata=${COORD_PGDATA[$index]}
		echo scp -pr $CONFIG_FOLDER/postgresql.conf.$index $targethost:$targetdata/postgresql.conf
		echo scp -pr $CONFIG_FOLDER/pg_hba.conf.$index $targethost:$targetdata/pg_hba.conf
		scp -pr $CONFIG_FOLDER/postgresql.conf.$index $targethost:$targetdata/postgresql.conf; err=$?
		if [ $err -gt "0" ]
		then
			echo "deleting saved configuration files"
			rm -rf $CONFIG_FOLDER
			echo "scp failed with "$targethost
			exit 2
		fi
		scp -pr $CONFIG_FOLDER/pg_hba.conf.$index $targethost:$targetdata/pg_hba.conf; err=$?
		if [ $err -gt "0" ]
		then
			echo "deleting saved configuration files"
			rm -rf $CONFIG_FOLDER
			echo "scp failed with "$targethost
			exit 2
		fi
	fi
done

#8) wait a little bit...
sleep 1

#9) restart all the other coordinators, origin coordinator has been stopped after DDL run
for index in ${!COORD_HOSTNAMES[*]}
do
	echo ssh ${COORD_HOSTNAMES[$index]} $COORD_SERVER $COORD_OPTIONS -p ${COORD_PORTS[$index]} -D ${COORD_PGDATA[$index]} &
	ssh ${COORD_HOSTNAMES[$index]} $COORD_SERVER $COORD_OPTIONS -p ${COORD_PORTS[$index]} -D ${COORD_PGDATA[$index]} &
done

sleep 2

#Clean also the folder in tmp keeping the configuration files
rm -rf $CONFIG_FOLDER

#10) finished :p
exit