#!/bin/bash
#
# Author: Ryan Kernan
# Date: September 23, 2011
# Version: 1.0
# Credits: Mike La Spina for the original concept and script http://blog.laspina.ca/
# 
# Function: Provides snapshot and send process which replicates ZFS file systems from a source to target server.
# Maintains a runing snapshot archive for X days (X being the value of keep_snaps).
# 
# Input parms:1
# rep_list - a file name var of which the file contains a list of comma delimed zfs paths, source and target host names.
# keep_snaps - Number of days to keep snaps for.
# snap_list - temp file to list current snapshots.

export PATH=/usr/gnu/bin:/usr/bin:/usr/sbin:/sbin	# Set Paths.

rep_list=$1
keep_snaps=7
snap_list=snaplist.lst

#######################################################################################
####################################Function###########################################
#######################################################################################
#
# Function parse the comma delimited input file and assign the field to the respective
# variables for file system, pool and host names.
#
# Global output parms
# zfspool
# zfspath
# shost
# dhost
 
parse_rep_list() {

	rep_list_line=$1
		zfspool="$(echo $rep_list_line | cut -d/ -f1)"
		zfspath="$(echo $rep_list_line | cut -d, -f1)"
		shost="$(echo $rep_list_line | cut -d, -f2)"
		dhost="$(echo $rep_list_line | cut -d, -f3)"

}

#######################################################################################
####################################Function###########################################
#######################################################################################
#
# Function issue zfs list commands and assign the variables the last snapshot names for
# both the source and destination hosts.
#
# Global input parms
# zfspath
# shost
# dhost

check_last_snap() {

	last_snap_dhost=""
	last_snap_shost=""
	last_snap_shost=$( pfexec zfs list -o name -t snapshot | grep $zfspath | tail -1 ) 
	last_snap_dhost=$( ssh -n $dhost pfexec zfs list -H -o name -r -t snapshot | grep $zfspath | tail -1 )
	true

}


#######################################################################################
####################################Function###########################################
#######################################################################################
#
# Function check if the destination zfs path exists and assign the result to the
# variable dhost_fs_name.
#
# Global input parms
# zfspath
# dhost

dhost_fs_exists() {

	dhost_fs_name=""
	dhost_fs_name=$(ssh -n $dhost pfexec zfs list -o name -H $zfspath | tail -1)

	if [ "$dhost_fs_name" = "" ] 
	then
		echo $(date) "->" $zfspath file system does not exist on target host $dhost. >> replicate.log
	fi

}

#######################################################################################
####################################Function###########################################
#######################################################################################
#
# Function create a zfs path on the destination to allow the receive command
# funtionallity then issue zfs snap and send to transfer the zfs object to the 
# destination host
#
# Global input parms
# zfspath
# dhost

dhost_create_fs() {

	ssh -n $dhost pfexec zfs create -p $zfspath 
	ssh -n $dhost pfexec zfs set mountpoint=none $zfspath
	last_snap_shost=$( pfexec zfs list -o name -t snapshot -H | grep $zfspath | tail -1 ) 
	echo $(date) "->" $last_snap_shost Initial replication start. >> replicate.log
	pfexec zfs send -v -R $last_snap_shost | ssh $dhost pfexec zfs recv -v -F -d $zfspool
	echo $(date) "->" $last_snap_shost Initial replication end. >> replicate.log

}

#######################################################################################
####################################Function###########################################
#######################################################################################
#
# Function Issue a snapshot for the source zfs path
#
# Global input parms
# zfspath

create_fs_snap() {

	snap_date="$(date +%m-%d-%y-%H:%M)"
	echo $(date) "->" $zfspath@$snap_date Snapshot creation start. >> replicate.log
	pfexec zfs snapshot $zfspath@$snap_date 
	echo $(date) "->" $zfspath@$snap_date Snapshot creation end. >> replicate.log

}

#######################################################################################
####################################Function###########################################
#######################################################################################
#
# Function create a zfs send/recv command set based on a the zfs path source 
# and target hosts for an established replication state. (aka incremental replication)
#
# Global input parms
# zfspath
# dhost

incr_repl_fs() {

	echo $(date) "->" $last_snap_dhost $last_snap_shost Incremental send start. >> replicate.log
	pfexec zfs send -I $last_snap_dhost $last_snap_shost | ssh $dhost pfexec zfs recv -d -F $zfspool >> replicate.log
	echo $(date) "->" $last_snap_dhost $last_snap_shost Incremental send end. >> replicate.log

}

#######################################################################################
####################################Function###########################################
#######################################################################################
#
# Function to clean up snapshots that are older than X days old X being the 
# value set by "keep_snaps" on both the source and destination hosts.

clean_snaps() {

	PreviousSnapDate=""
	PreviousSnap=""
	CurrentYear="$( date +%y )"
	CurrentMonth="$( date +%m )"
	CurrentDay="$( date +%d )"

	if [ "$zfspath" != "" ]
	then
		pfexec zfs list -o name -t snapshot | grep $zfspath > $snap_list

		while read snaps
		do

		stringpos=0
		let stringpos=$(expr index "$snaps" @)+1
		SnapDate=$( pfexec expr substr $snaps $stringpos 8 )
		let stringpos=$(pfexec expr index "$snaps" @)+10
       	SnapTime=$( pfexec expr substr $snaps $stringpos 5 )
		SnapDay="$(echo $SnapDate | cut -d- -f2)"
		SnapMonth="$(echo $SnapDate | cut -d- -f1)"	  
		SnapYear="$(echo $SnapDate | cut -d- -f3)"

		SnapDate="$SnapMonth-$SnapDay-$SnapYear"
				
		if [ "$(date +%m-%d-%y --date="$keep_snaps days ago")" = "$SnapDate" ]
		then
			echo "Destroying $SnapDate snapshot $snaps on $shost" >> replicate.log
			pfexec zfs destroy $snaps
			echo "Destroying $SnapDate snapshot $snaps on $dhost" >> replicate.log
			pfexec ssh -n $dhost pfexec zfs destroy $snaps
		fi

		done < $snap_list
		rm $snap_list
	fi
}

#######################################################################################
#####################################Main Entery#######################################
#######################################################################################
#
#
# Init Global Parms

zfspath="" 
shost="" 
dhost=""

# Create the snapshots all at the same time

while read line 
do

	parse_rep_list $line

	if [ "$zfspath" != "" ]
	then
		create_fs_snap >> replicate.log		#Create a new snapshot of the path spec.
	fi

done < $rep_list

# Send the snapshots to the remote and create the fs if required 

while read line 
do

	parse_rep_list $line 

	if [ "$zfspath" != "" ]
	then
		dhost_fs_exists									# Test for the existence of our listed 
														# zfs file system path on the target host.

		if [ "$dhost_fs_name" ]
		then
			check_last_snap >> replicate.log			# Get the start and stop snaps.
			incr_repl_fs >> replicate.log				# Initiate a dif replication.
			clean_snaps									# Clean up any snapshots that are X days old.
		else
			dhost_create_fs >> replicate.log			# Create a first time replication.
	    	fi
	  fi

done < $rep_list

exit 0
