This page contains scripts, links to other sites and "howto" docs that the staff at Open Technologies Inc. commonly use. You are free to use this material as you wish, but we take NO responsibility for your proper or improper use of this material. See Limitation of Liability below. ------------------------------------------------------------------------------------------------------------- #!/sbin/sh # # Open Technologies, Inc. # Created: 01-30-09 PJF # Updated: 04-29-09 PJF # # 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="/opt/scripts" emailalert="myemail@mycompany.com" if [ "$1" != "init" -a "$1" != "sync" -a "$1" != "start" -a "$1" != "stop" ]; then echo "Usage: zfsrep.sh | " echo " | " echo " [remotehost]}> | " echo " [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=`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 "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=`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=`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 "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=`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 | ssh $4 "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=`zfs list | 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 | ssh $4 "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=`zfs list | grep $3@rep$DATE | awk '{print $1}'` else snapchk=`ssh $4 "zfs list | 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=`zfs list | 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 | ssh $4 "zfs receive -F $3" fi #---Make sure the snapshot was replicated before removing the old snapshots if [ "$4" = "" ]; then snapchk=`zfs list | grep $3@rep$DATE` snapchk=`echo $snapchk | awk '{print $1}'` else snapchk=`ssh $4 "zfs list | 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 ------------------------------------------------------------------------------------------------------------- LIMITATION OF LIABILITY TO THE FULL EXTENT PERMITTED BY LAW, HOST IS NOT LIABLE TO YOU OR ANY OTHER INDIVIDUAL OR ENTITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, PUNITIVE, SPECIAL OR CONSEQUENTIAL DAMAGES RELATED TO OR ARISING OUT OF ANY USE OF, ACCESS TO, OR INABILITY TO ACCESS THIS WEBSITE, CONTENT, SERVICES, OR OF ANY OTHER LINKED WEBSITE OR EXTERNAL RESOURCE INCLUDING, WITHOUT LIMITATION, ANY LOST PROFITS, LOST SALES, LOST REVENUE, LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA EVEN IF OPEN TECHNOLOGIES INC. IS EXPRESSLY ADVISED OR AWARE OF THE POSSIBILITY OF SUCH DAMAGES OR LOSSES. YOU ASSUME ALL RISK FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR LOSS OF DATA THAT RESULTS FROM OBTAINING ANY CONTENT FROM THE WEBSITE, INCLUDING ANY DAMAGES RESULTING FROM COMPUTER VIRUSES, WORMS, OR OTHER ITEMS OF A DESTRUCTIVE NATURE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, SO THE ABOVE LIMITATION MAY NOT APPLY TO YOU.