Skip to content

Instantly share code, notes, and snippets.

@matthiasg
Created January 31, 2013 12:52
Show Gist options
  • Save matthiasg/4682641 to your computer and use it in GitHub Desktop.
Save matthiasg/4682641 to your computer and use it in GitHub Desktop.
zfs replication script from Ryan Kernan
#!/bin/bash
#
# Author: Ryan Kernan
# Date: September 23, 2011
# Version: 1.1
# Credits: Mike La Spina for the original concept and script http://blog.laspina.ca/
#
# Mike La Spina - 1.1 added vars to support full paths on remote ssh executions
# Mike La Spina - 1.2 added var symbol to last_snap_shost input parse.
#
# 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).
#
# The input format should conform to the following:
# source zfspath, source host, dest host
#
# eg.
#
# pool/zfsstore,host1,host2
#
# 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.
#
#
#
ZFS=/usr/sbin/zfs
DATE=/usr/gnu/bin/date
SSH=/usr/bin/ssh
PFEXEC=/usr/bin/pfexec
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment