#!/bin/sh
#
# Open Technologies, Inc.
# Created: 01-30-09 PJF
# Updated: 04-29-09 PJF
# Fixed To Work On FREEBSD ZFS: 01-14-11 admin@tediosity.com
#
# Description: Managed ZFS Replication Script
#              - Provides initial, manual and periodic sync'ing of zfs filesystems
#              - Manages snapshots on local and remote filesystem
#
# Options:     init: Create the initial snapshot and replication set
#              sync: Updates/syncs a replication set
#              start: Adds cron entry for periodic sync for a given filesystem
#              stop: Removes the cron entry for the given filesystem
#
##################################################################################

scriptdir="/root/zfsrep"
emailalert="YOUR@e-mail.com"

if [ "$1" != "init" -a "$1" != "sync" -a "$1" != "start" -a "$1" != "stop" ]; then
   echo "Usage: zfsrep.sh <start {filestem destination remotehost interval}> | "
   echo "                 <stop {filesystem}> | "
   echo "                 <init {<filesystem> <destination> [remotehost]}> | "
   echo "                 <sync {<filesystem> <destination> [remotehost]>"
   echo "                 Where: 'interval' is a number from 1 to 59."
   echo ""
   echo "Examples:        zfsrep.sh init rpool/myfs drpool/myfs host2"
   echo "Version:         0.21"
   exit 1
fi


#---Check for required zfsrep.sh snapshot storage directory
if [ ! -d $scriptdir/zfsrep.snapshots ]; then
   mkdir $scriptdir/zfsrep.snapshots
fi


#---If given, make sure we can ping and ssh into the remote system
#if [ "$4" != "" ]; then
#   alive=`ping $4 2 | grep "^no answer"`
#   if [ "$alive" != "" ]; then
#      err="Error-Unable to ping the remote host: '$4'"
#      echo "`date`  $err" >> $scriptdir/zfsrep.log
#      if [ "$emailalert" != "" ]; then
#         echo "zfsrep: $err" | mailx -s "zfsrep on `hostname`" $emailalert    
#      fi
#      exit 1
#   fi
#fi


#---Get date/time stamp for new snapshots
DATE=`date '+%Y%m%d%H%M%S'`


#---Determine previous snapshot file
#ZFS=`echo $2 | awk '{FS="/"; print $1"-"$2}'`
ZFS=`echo $2 | sed 's/\//-/g'`
ZFSsnaplog="$scriptdir/zfsrep.snapshots/$ZFS.lastsnap"


#---Check to make sure the source and destination zfs filesystems exist
zfschk=`/sbin/zfs list $2 | grep "^$2"`
if [ "$zfschk" = "" ]; then
   err="Error-Given source filesystem '$2' is not listed on local system"
   echo "`date`  $err" >> $scriptdir/zfsrep.log
   if [ "$emailalert" != "" ]; then
      echo "zfsrep: $err" | mailx -s "zfsrep on `hostname`" $emailalert
   fi
   exit 1
fi

if [ "$1" != "init" ]; then
   if [ "$4" != "" ]; then
      zfschk=`ssh $4 "/sbin/zfs list $3 | grep '^$3'`
      if [ "$zfschk" = "" ]; then
         err="Error-Given destination filesystem '$3' is not listed on the remote system."
         echo "`date`  $err" >> $scriptdir/zfsrep.log
         if [ "$emailalert" != "" ]; then
            echo "zfsrep: $err" | mailx -s "zfsrep on `hostname`" $emailalert
         fi
         exit 1
      fi
   else
      zfschk=`/sbin/zfs list $3 | grep "^$3"`
      if [ "$zfschk" = "" ]; then
         err="Error-Given destination filesystem '$3' is not listed on this system."
	 echo "`date`  $err" >> $scriptdir/zfsrep.log
         if [ "$emailalert" != "" ]; then
            echo "zfsrep: $err" | mailx -s "zfsrep on `hostname`" $emailalert
         fi 
         exit 1 
      fi 
   fi 
fi 


#---Check to make sure the given option is ready to run and last snapshots exist 
if [ "$1" = "start" -o "$1" = "sync" ]; then
   if [ -f $ZFSsnaplog ]; then
      OLDsnap=`cat $ZFSsnaplog`
      zfschk=`/sbin/zfs list $2@$OLDsnap | grep "^$2@$OLDsnap"`
      if [ "$zfschk" = "" ]; then
         echo "`date`  Error-Replication record exists, but the snapshot for the source filesystem does not exist. You may need to reinitalize." >> $scriptdir/zfsrep.log
         exit 1
      fi

      if [ "$4" != "" ]; then
         zfschk=`ssh $4 "/sbin/zfs list $3@$OLDsnap | grep '^$3@$OLDsnap'"`
         if [ "$zfschk" = "" ]; then
            echo "`date`  Error: The snapshot record exists but the snapshot itself doesn't exist on the destination." >> $scriptdir/zfsrep.log
            echo "`date`  You may need to re"init"alize the replication." >> $scriptdir/zfsrep.log
            exit 1
         fi
      else
         zfschk=`/sbin/zfs list $3@$OLDsnap | grep "^$3@$OLDsnap"`
         if [ "$zfschk" = "" ]; then
            echo "`date`  Error: The snapshot record exists but the snapshot itself doesn't exist locally" >> $scriptdir/zfsrep.log
            echo "`date`  You may need to re"init"alize the replication." >> $scriptdir/zfsrep.log
            exit 1
         fi
      fi

   else
      echo "`date`  Not able to proceed. Replication doesn't appeared to have been initialized. Cannot find last snapshot record used for syncronized baseline." >> $scriptdir/zfsrep.log
      exit 1
   fi

fi


#---Make sure only one replication script is running to replicte to a single destination filesystem
tmp=`echo $3 | sed 's/\//-/g'`
destrun="$scriptdir/zfsrep.snapshots/$tmp.running"
if [ -f $destrun ]; then
   echo "Error:  zfsrep FLAG indicates that you are currently replicating to the given destination file system."
   echo "        If you are sure this is not the case, you can delete the flag file '$destrun' and try again."
   exit 1
else
   touch $destrun
fi


#---Case Statement
case "$1" in
'start')
	
	;;

'stop')
	;;

'init')
	#---Create the "initial" baseline replication set incase we need to fall back to a safe point
        zfs snapshot $2@rep-init-$DATE
        echo "rep-init-$DATE" > $ZFSsnaplog

	if [ "$4" = "" ]; then
	   zfs send $2@rep-init-$DATE | zfs receive -F $3
	else
	   zfs send $2@rep-init-$DATE | bzip2 -c | ssh -c blowfish $4 "bzcat | zfs receive -F $3"
	fi
	echo "`date`  init-baseline snapshot: $2@rep-init-$DATE > $4:$3@rep$DATE" >> $scriptdir/zfsrep.log

	#---Create first "difference" replication set
        zfs snapshot $2@rep$DATE

        #---Make sure the snapshot was taken before attempting to replicate it
        snapchk=`/sbin/zfs list -t snapshot | grep $2@rep$DATE | awk '{print $1}'`

        if [ "$snapchk" = "$2@rep$DATE" ]; then

           if [ "$4" = "" ]; then
              zfs send -i $2@rep-init-$DATE $2@rep$DATE | zfs receive -F $3
           else
              zfs send -i $2@rep-init-$DATE $2@rep$DATE | bzip2 -c | ssh -c blowfish $4 "bzcat | zfs receive -F $3"
           fi
	   echo "`date`  snapshot: $2@rep-init-$DATE > $4:$3@rep$DATE" >> $scriptdir/zfsrep.log

           #---Make sure the snapshot was replicated before removing the old snapshots
           if [ "$4" = "" ]; then
              snapchk=`/sbin/zfs list -t snapshot | grep $3@rep$DATE | awk '{print $1}'`
           else
              snapchk=`ssh $4 "/sbin/zfs list -t snapshot | grep $3@rep$DATE"`
	      snapchk=`echo $snapchk | awk '{print $1}'`
           fi

           #---If snapshot replication was successful, then remove old and record log else remove new snaps
           if [ "$snapchk" = "$3@rep$DATE" ]; then
              echo "rep$DATE" > $ZFSsnaplog
           else
              zfs destroy $2@rep$DATE
              echo "`date`  Error: Not able to complete the initialization by creating first destination snapshot: $2@rep-init-$DATE > $4:$3@rep$DATE" >> $scriptdir/zfsrep.log
           fi

        else
           echo "`date`  Error: Not able to complete the initialization by creating first source snapshot: $2@rep-init-$DATE > $4:$3@rep$DATE" >> $scriptdir/zfsrep.log
        fi
	;;

'sync')
        zfs snapshot $2@rep$DATE

        #---Make sure the snapshot was taken before attempting to replicate it
        snapchk=`/sbin/zfs list -t snapshot | grep $2@rep$DATE | awk '{print $1}'`

        if [ "$snapchk" = "$2@rep$DATE" ]; then

           if [ "$4" = "" ]; then
              zfs send -i $2@$OLDsnap $2@rep$DATE | zfs receive -F $3
           else
              zfs send -i $2@$OLDsnap $2@rep$DATE | bzip2 -c | ssh -c blowfish $4 "bzcat | zfs receive -F $3"
           fi

           #---Make sure the snapshot was replicated before removing the old snapshots
           if [ "$4" = "" ]; then
              snapchk=`/sbin/zfs list -t snapshot | grep $3@rep$DATE`
	      snapchk=`echo $snapchk | awk '{print $1}'`
           else
              snapchk=`ssh $4 "/sbin/zfs list -t snapshot | grep $3@rep$DATE"`
	      snapchk=`echo $snapchk | awk '{print $1}'`
           fi

           #---If snapshot replication was successful, then remove old snapshot and record log else remove new snaps
           if [ "$snapchk" = "$3@rep$DATE" ]; then

              if [ "$4" = "" ]; then
                 zfs destroy $3@$OLDsnap
              else
                 ssh $4 "zfs destroy $3@$OLDsnap"
              fi

              zfs destroy $2@$OLDsnap
              echo "rep$DATE" > $ZFSsnaplog
	      echo "`date`  Successfully replicated $2 to $4:$3 at $DATE" >> $scriptdir/zfsrep.log
           else
              zfs destroy $2@rep$DATE
              echo "`date`  Error:  Source snapshot ($2@rep$DATE) was sucessful, but failed to replicate it." >> $scriptdir/zfsrep.log
	      echo "`date`  May need to manually fall back to baseline snaphot. This will be done automatically in future releases" >> $scriptdir/zfsrep.log
           fi

        else
           echo "`date`  Error:  Source snapshot failed, unable to continue with replication." >> $scriptdir/zfsrep.log
	   echo "`date`  May need to manually fall back to baseline snaphot. This will be done automatically in future releases" >> $scriptdir/zfsrep.log 
        fi
        ;;

esac

#---Remove "running" log file for the given replication set
if [ -f $destrun ]; then
   rm $destrun
fi

exit 0

