#!/bin/sh
#VOLDEMORT 0.6
#Last revised, S. Timm 2/20/03
#Last bug fixes S. Timm 4/14/03
# New script: rsync_clone_push
# this is to clone the full tree of voldemort files
# from one server to another, 
# wiping out whatever was there before.
# Major revisions in 0.6 to read from the database instead of relying
# on .flavors, .clusters, .nodelist files.
# also add options -e for alternate rsh command
# 
#rsync_push script
#
# Subroutines:
#
printHelp() {
            echo "rsync_push [-h]"
            echo "           [-r]"
            echo "           [-f <flavor>]"
            echo "           [-b <prefix> <start> <end> | -B <prefix> <range> | -l <filename> ]"
            echo "           [ -K ] "
            echo "           [-q | -v]"
            echo "           [ -y ]"
            echo "           [ -e <rsh_command> ] "
            echo "           [ -E <rcp_command> ] "
            echo "  Functional order of commands"
            echo "  Commands that control where to push to:"
            echo " -f  Push for this OS flavor--default is ALL flavors"
            echo " -b  push to numbered range of nodes only"
            echo " -B  push for range such as 1 3 5-7 (range in quotes)"
	    echo " -l <filename> push to list of nodes in filename"
            echo " Note: -b, -B, -l are mutually exclusive, if more than one"
	    echo " are specified the last option specified will win."
	    echo " The nodes pushed are an and of the nodes selected with"
            echo " (-f) & (-b | -B | -l )" 
            echo " -K  Don't attempt kerberos kinit"
            echo ""
            echo "  Commands that control the behavior of the push"
            echo " -e  command to pass along to rsync's -e option (default is /usr/bin/ssh)"
            echo " -E  Alternate rcp command--default is /usr/krb5/bin/rcp)"
            echo " -r  Retry the push for nodes that failed"
            echo " -q  quiet--nothing to stdout"
            echo " -v  verbose (the more v's the more verbose)"
            echo " -y  batch mode--answer yes to are you sure"
            echo ""

}
#***************************************************
# Read a file with one item per line and make it into a list that
# fits into one environment variable
#***************************************************
linetovar() {
  MEMLIST=""
  if [ $DEBUG -gt 2 ] 
  then
    echo "in linetovar"
  fi
  for line in $MEMFILE
    do
        if [ $DEBUG -gt 2 ]
        then
          echo "$line"
        fi
	if [ "$line" != "" ] 
	then
	    testchar=`echo $line | cut -c1`
	    if [ "$testchar" != "#" ]
	    then
		MEMLIST="$MEMLIST $line"
	    fi
	fi
    done
}
#**********************************************
# Load in SYSLIST by building a  linear list
#**********************************************

load_list_buildlist(){
	    i=$SUFIX_START
	    while [ $i -le $SUFIX_STOP ]
	    do
		SYSLIST="$SYSLIST $PREFIX$i"
		i=`expr $i + 1`
	    done
}

#**********************************************
# Load in SYSLIST by building a ranged list
#**********************************************

load_list_buildrange(){
	    for i in $SUFIX_RANGE
	    do
		case "$i" in
		    *-*)
			start=`echo $i | cut -d\- -f1`
			end=`echo $i | cut -d\- -f2`
			if [ $start -gt $end ]
			then
			    echo "Range: $d, is not in ascending order."
	        	    echo "Reversing order."
			    tmp=$start
			    start=$end
			    end=$tmp
			fi
        		i=$start
        		while [ $i -le $end ]
        		do
			    SYSLIST="$SYSLIST $PREFIX$i"
			    i=`expr $i + 1`
        		done
			;;
		    [1-9]*)
                	SYSLIST="$SYSLIST $PREFIX$i"
			;;
		    *)
			echo "Please make sure that args are space separated."
			exit 1
			;;
		esac
	    done
}
load_list_file(){
if [ -e "$LISTFILE" ] 
then
    MEMFILE="`cat $LISTFILE`"
    linetovar
    SYSLIST="$MEMLIST"
fi
}
load_list_file(){
if [ -e ${RSYNC_HOME}/clusters/common/db/failed ] 
then
    mv  ${RSYNC_HOME}/clusters/common/db/failed  ${RSYNC_HOME}/clusters/common/db/failed.1
    MEMFILE="`cat ${RSYNC_HOME}/clusters/common/db/failed.1`"
    linetovar
    FAILLIST="$MEMLIST"
fi
}
hostcheck(){
    FINALHOST=""
    for checkhost in $HOSTCHECK
    do
	for refhost in $SYSLIST
	do
	    if [ ${checkhost} == ${refhost} ]
	    then
		FINALHOST="$FINALHOST $checkhost"
	     fi
	done
    done
}
failcheck(){
    FINALHOST=""
    for checkhost in $HOSTCHECK
    do
	for refhost in $FAILLIST
	do
	    if [ ${checkhost} == ${refhost} ]
	    then
		FINALHOST="$FINALHOST $checkhost"
	     fi
	done
    done
}
#this subroutine adds a node to the failed file
#if it isn't already there
failadd() {
    found="no"
    for infail in $INFAILFILE
    do
	if [ "${host}" == "${infail}" ]
	then
	    found="yes"
	fi
    done
    if [ "${found}" == "no" ]
    then
	INFAILFILE="${INFAILFILE} ${host}"
        echo ${host} >> ${RSYNC_HOME}/clusters/common/db/failed
    fi
}
#
#Push logic --assumes you are in the directory from which you want to push 
# the files, $host is set to the host you want to push to.
# $DB_DIRECTORY is the destination directory on the remote machine
# that we are pushing to.
#
hostpush(){
		if [ $DEBUG -gt 2 ]
		then
		    echo "in hostpush, category ${category}"
		fi
#  In rsync_clone_push, we push the entire top-level 
#  directory.
#  First clear the remote directory
		${RSHCMD} -n $host "cd $DB_DIRECTORY && rm -Rf *"
#  Then rsync out the whole tree.
			if [ $DEBUG -le 0 ]
                        then 
			    if [ "${flavor:0:5}" != "Linux" ] 
			    then 
				$RSYNC_PATH -LrptgoDz --exclude '*Makefile' -e ${RSHCMD}  --rsync-path=/usr/local/bin/rsync *  ${host}:${DB_DIRECTORY}
FLAVOR  = $(shell curdir)
D
			    else
				$RSYNC_PATH -LrptgoDz --exclude '*Makefile' -e ${RSHCMD}  *  ${host}:${DB_DIRECTORY}
			    fi
			else
			    if [ "${flavor:0:5}" != "Linux" ] 
			    then 
				$RSYNC_PATH -LrptgoDvz --exclude '*Makefile' -e ${RSHCMD} --rsync-path=/usr/local/bin/rsync * ${host}:${DB_DIRECTORY}
			    else
				$RSYNC_PATH -LrptgoDvz --exclude '*Makefile' -e ${RSHCMD}  * ${host}:${DB_DIRECTORY}
			    fi
                        fi

}
dbsplit() {
if [ $DEBUG -gt 2 ]
then
    echo $DBLINE
fi
DB_HOST="$(echo $DBLINE | cut -d ':' -f1)"
DB_FLAVOR="$(echo $DBLINE | cut -d ':' -f2)"
DB_DIRECTORY="$(echo $DBLINE | cut -d ':' -f3)"
}
#
#  BEGIN MAIN PROGRAM EXECUTION
#
#Source the global config file
if [ -x "$VOLDEMORT_CONF" ] 
then
    . $VOLDEMORT_CONF
else
    . /etc/voldemort_push.conf
fi
RETRY="no"; export RETRY
AREYOUSURE="no"; export AREYOUSURE
KERBEROS="yes"; export KERBEROS
DHCP="no"; export DHCP
DEBUG=1; export DEBUG
#Note--/usr/bin/ssh transport is needed to push out a tree with 
#as many files as the Fermilab tree.
RSHCMD="/usr/bin/ssh" ; export RSHCMD
RCPCMD="/usr/krb5/bin/rcp" ; export RCPCMD
RSYNC_PATH="/usr/bin/rsync" ; export RSYNC_PATH
RSYNC_ALTPATH="/usr/local/bin/rsync" ; export RSYNC_ALTPATH
#
# Make sure local rsync executable is here.
#
if [ ! -x "$RSYNC_PATH" ]
then
    if [ ! -x "$RSYNC_ALTPATH" ]
    then
	echo "neither /usr/bin/rsync nor /usr/local/bin/rsync found"
	exit
    else
	RSYNC_PATH="$RSYNC_ALTPATH"
    fi
fi
	
INFAILFILE=""; export INFAILFILE
#Parse the options passed to the program
while [ $# != 0 ]
do 
	case $1 in 
            -h | -help | --help) printHelp ; exit 0 ;;
            -r | --retry) RETRY="yes"; export RETRY ; load_list_failed ; shift 1 ;;
            -f) test $# -lt 2 && { printHelp ; exit 1 ; }
                RSYNC_FLAVOR="$2" ; export RSYNC_FLAVOR; shift 2 ;;
	    -b)  test $# -lt 4 && { printHelp ; exit 1 ; }
	        PREFIX="$2" ; SUFIX_START="$3" ; SUFIX_STOP="$4" ; load_list_buildlist ; LISTMADE="yes" ; shift 4 ;;
	    -B)  test $# -lt 3 && { printHelp ; exit 1 ; }
	        PREFIX="$2" ; SUFIX_RANGE="$3" ; load_list_buildrange ; LISTMADE="yes" ; shift 3 ;;
            -K) KERBEROS="no" ; export KERBEROS ; shift 1 ;;
            -d) DHCP="yes" ; export DHCP ; shift 1 ;;
            -q) DEBUG=0 ; shift 1 ;;
            -v*) PAR=$1 ; DEBUG=`echo ${PAR:1} | wc -c` ; echo "Debug level $DEBUG" ; shift 1 ;;
            -l) LISTFILE="$2" ; export LISTFILE ; load_list_file ; LISTMADE="yes" ;  shift 2 ;;
            -e) RSHCMD="$2"; export RSHCMD ; shift 2 ;;
            -E) RCPCMD="$2"; export RCPCMD ; shift 2 ;;
            -y) AREYOUSURE="yes" ; export AREYOUSURE ; shift 1;;
             *) printHelp; break
	esac
done
if [ "$KERBEROS" == "yes" ] 
then
#
#
# Get a kerberos ticket, make sure we don't clobber anyone
# else's credential cache--especially important if running in cron.
    KRB5CCDIR=/tmp/voldemort_$$; export KRB5CCDIR
    KRB5CCNAME=${KRB5CCDIR}/krb5cc_voldemort_$$; export KRB5CCDIR
    if [ -e $KRB5CCDIR ]
    then
	rm -Rf $KRB5CCDIR
    fi
    mkdir $KRB5CCDIR
    /usr/krb5/bin/kinit -k
fi
#
#     If not in retry mode, reset the failed list
#
    if [ "$RETRY" == "no" ]
    then
	if [ -e ${RSYNC_HOME}/clusters/common/db/failed ]
	then
	    mv ${RSYNC_HOME}/clusters/common/db/failed ${RSYNC_HOME}/clusters/common/db/failed.1
	fi
    fi
###
###   Write confirmation of what we are about to do:

if [ $DEBUG -gt 0 ] 
then
    if [ "RSYNC_FLAVOR" == "" ]
    then 
	echo "Pushing for all flavors"
    else 
	echo "Pushing for flavor $RSYNC_FLAVOR"
    fi
    if [ "$LISTMADE" == "yes" ] 
    then
	 echo "Pushing only for hosts: $SYSLIST"
    fi
fi


###
### Print the are-you-sure message
if [ "$AREYOUSURE" == "no" ] 
then
    echo "Are you sure? (yes/no)"
    read AREYOUSURE
    if [ "$AREYOUSURE" != "yes" ]
    then
	exit
    fi
fi

### New main loop---Loop through database line by line, push for 
### all that match
#
#   Change stdin to read from the database
exec 6<&0
exec < $RSYNC_HOME/clusters/common/db/slave.conf
#
#   Begin database while loop 1
#
while read DBLINE 
do
    dbsplit
#
#   Nested If structure 1B  (flavor)
#
	if [ $DEBUG -gt 2 ] 
	then
	    echo "$RSYNC_FLAVOR $DB_FLAVOR"
        fi
	if [ "$RSYNC_FLAVOR" == "" ] || [ "$RSYNC_FLAVOR" == "$DB_FLAVOR" ]
	then
	    cluster=$DB_CLUSTER
	    flavor=$DB_FLAVOR
	    host=$DB_HOST
	    if [ "$flavor" == "Linux+2.4.18" ]
	    then
		linuxrelease="731"
	    elif [ "$flavor" == "Linux+2.4" ]
	    then 
		linuxrelease="711"
	    elif [ "$flavor" == "Linux+2.2" ]
	    then
		linuxrelease="612"
            else
		linuxrelease="$flavor"
	    fi
	    if [ $DEBUG -gt 1 ] 
	    then
		echo "$cluster $flavor $host"
            fi
#
#Check to see if this host is in the host list
#
	    if [ "$LISTMADE" == "yes" ]
	    then
		HOSTCHECK="$host"
		hostcheck
		host="$FINALHOST"
	    fi
#
#If we are in retry mode, check to see if this host is in failed list.
#
	    if [ "$RETRY" == "yes" ] && [ "$host" != "" ]
	    then
		HOSTCHECK="$host"
		failcheck
		host="$FINALHOST"
	    fi
#
#        Nested IF structure 1C   (host)
#
	    if [ "$host" != "" ]
	    then
#
#   Ping host to be sure it is up
#
		hostup="no"
		if [ -e /tmp/ping.err ]
		then
		    rm /tmp/ping.err
		fi
		if [ -e /tmp/ping.out ]
		then
		    rm /tmp/ping.out
		fi
		ping -c1 -w2 $host > /tmp/ping.out 2> /tmp/ping.err
		pingcount="`grep received /tmp/ping.out | cut -d' ' -f4`"
		if [ -s /tmp/ping.err ] || [ "$pingcount" != "1" ]
		then 
		    if [ $DEBUG -gt 2 ] 
		    then
			echo "$host not pingable"
		    fi
		    if [ "$DHCP" = "yes" ] 
		    then
			hostdhcp="$host.dhcp"
			rm /tmp/ping.err
			rm /tmp/ping.out
			ping -c1 -w2 $hostdhcp > /tmp/ping.out 2> /tmp/ping.err
			pingcount="`grep received /tmp/ping.out | cut -d' ' -f4`"
			if [ -s /tmp/ping.err ] || [ "$pingcount" != "1" ]
			then
			    if [ $DEBUG -gt 2 ] 
			    then
				echo "$hostdhcp not pingable"
			    fi
			    failadd
			else
			    host=$hostdhcp
			    hostup="yes"
			fi
		    else
		    failadd
		    fi
		else
		hostup="yes"
		fi
#
#  Fetch the location of the helper applications of this node
#  should be in database in future, right now we have to 
#  pull it off of /etc/voldemort.conf on each node.
#  rsh/rcp has to be enabled for this to work.
#  Also note that sometimes ping -c1 -w2 won't put anything
#  in stderr even if the node is down.  Have to check 
#  for that as well, also if the kerberos on the node is 
#  misconfigured.  If so, we don't continue with the node.
                if [ "$hostup" == "yes" ]
		then
   		if [ -x $RCPCMD ] 
		then
                    if [ -e /tmp/rcp.err ]
		    then
			rm /tmp/rcp.err
		    fi
                    if [ -e /tmp/voldemort.conf ]
		    then
			rm /tmp/voldemort.conf
	            fi
		    KERBFAULT=""
		    NOROUTE=""
		    $RCPCMD -x root@${host}:/etc/voldemort.conf /tmp/voldemort.conf 2>/tmp/rcp.err
		    if [ -s /tmp/rcp.err ]
		    then
			KERBFAULT="`grep 'Server rejected authentication' /tmp/rcp.err`"
			NOROUTE="`grep 'No route to host' /tmp/rcp.err`"
			if [ "$KERBFAULT" != "" ] || [ "$NOROUTE" != "" ] 
			then
			    hostup="no"
			    failadd
			fi
                    fi
                    HELPLINE="`grep RSYNC_HELPERS /tmp/voldemort.conf`"
		    if [ "$HELPLINE" != "" ]
		    then
			echo $HELPLINE > /tmp/helpline.sh
			chmod 755 /tmp/helpline.sh
			. /tmp/helpline.sh
		    else
			RSYNC_HELPERS=/sbin
		    fi
		else
		    RSYNC_HELPERS=/sbin
		fi
		fi
#
#
		if [ "$hostup" == "yes" ] 
		then

		    
#   New for rsync_clone_push
                            
                            cd ${RSYNC_HOME}
			    hostpush
                 fi
#    End nested IF 1C (host)
	    fi
#    End nested IF 1B  (flavor)
	fi
#    End nested IF 1A  (cluster)
#     fi
#    End of database while loop 1
done
if [ "$KERBEROS" == "yes" ]
then
    /usr/krb5/bin/kdestroy
    rm -Rf $KRB5CCDIR
fi
