Add replication script
This commit is contained in:
		
							
								
								
									
										119
									
								
								bashclub-zsync/usr/bin/bashclub-zsync
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								bashclub-zsync/usr/bin/bashclub-zsync
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# bashclub zfs replication script
 | 
				
			||||||
 | 
					# Author: (C) 2023 Thorsten Spille <thorsten@spille-edv.de>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					prog="$(basename $0)"
 | 
				
			||||||
 | 
					zfs=$(which zfs)
 | 
				
			||||||
 | 
					ssh=$(which ssh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### default config file, can be changed with parameter -c
 | 
				
			||||||
 | 
					conf=/etc/bashclub/zsync.conf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### default values in config file
 | 
				
			||||||
 | 
					# replication target on local machine 
 | 
				
			||||||
 | 
					target=pool/dataset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ssh address of remote machine
 | 
				
			||||||
 | 
					source=user@host
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# zfs user parameter to identify filesystems/volumes to replicate
 | 
				
			||||||
 | 
					tag=bashclub:zsync
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# if $tag=subvols, which source to filter: "inherited" or "inherited|received"
 | 
				
			||||||
 | 
					subvol_source="inherited|received"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 
 | 
				
			||||||
 | 
					snapshot_filter="hourly|daily|weekly|monthly"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					usage() {
 | 
				
			||||||
 | 
						cat >&2 <<-EOF
 | 
				
			||||||
 | 
						usage: $prog [-h] [-c CONFIG]
 | 
				
			||||||
 | 
						  creates a mirrored replication of configured zfs filesystems / volumes
 | 
				
			||||||
 | 
					    -c CONFIG    Configuration file for this script
 | 
				
			||||||
 | 
					    -d           Debug mode
 | 
				
			||||||
 | 
					  ---------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    (C) 2023     by Spille IT Solutions for bashclub (github.com/bashclub)
 | 
				
			||||||
 | 
					                 Author: Thorsten Spille <thorsten@spille-edv.de>
 | 
				
			||||||
 | 
					  ---------------------------------------------------------------------------
 | 
				
			||||||
 | 
						EOF
 | 
				
			||||||
 | 
						exit $1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					while getopts "hc:" opt; do
 | 
				
			||||||
 | 
					  case $opt in
 | 
				
			||||||
 | 
					    h) usage 0 ;;
 | 
				
			||||||
 | 
					    c) conf=$OPTARG ;;
 | 
				
			||||||
 | 
					    *) usage 1 ;;
 | 
				
			||||||
 | 
					  esac
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					shift $((OPTIND-1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# load config file
 | 
				
			||||||
 | 
					if [ -f $conf ]; then
 | 
				
			||||||
 | 
					    source $conf
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    mkdir -p $(dirname $conf)
 | 
				
			||||||
 | 
					    cat << EOF > $conf
 | 
				
			||||||
 | 
					target=$target
 | 
				
			||||||
 | 
					source=$source
 | 
				
			||||||
 | 
					tag=$tag
 | 
				
			||||||
 | 
					subvol_source=$subvol_source
 | 
				
			||||||
 | 
					snapshot_filter=$snapshot_filter
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					    echo "Initial config file created. Please adjust and restart script. Exiting..."
 | 
				
			||||||
 | 
					    usage 0
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# query source datasets/subvols to replicate
 | 
				
			||||||
 | 
					IFS=$'\n'
 | 
				
			||||||
 | 
					for zvol in $($ssh $source "zfs get -H -o name,value,source -t filesystem,volume $tag"); do
 | 
				
			||||||
 | 
					    if [[ "$(echo $zvol | cut -f2)" == "subvols" ]] && [[ $(echo $zvol | grep -E $subvol_source) ]]; then
 | 
				
			||||||
 | 
					        name=$(echo $zvol | cut -f1)
 | 
				
			||||||
 | 
					        syncvols=("${syncvols[@]}" "$name")
 | 
				
			||||||
 | 
					    elif [[ "$(echo $zvol | cut -f2)" == "all" ]];then
 | 
				
			||||||
 | 
					        name=$(echo $zvol | cut -f1)
 | 
				
			||||||
 | 
					        syncvols=("${syncvols[@]}" "$name")
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for name in "${syncvols[@]}"; do
 | 
				
			||||||
 | 
					    IFS=$' '
 | 
				
			||||||
 | 
					    if [ $($zfs list -H -s creation -t snapshot $target/$name > /dev/null 2>&1 ; echo $?) -gt 0 ]; then
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        prefix=""
 | 
				
			||||||
 | 
					        for part in $(echo $target/$(echo $name | cut -d'/' -f1) | sed "s/\// /g"); do
 | 
				
			||||||
 | 
					            if [ $($zfs list $prefix$part > /dev/null 2>&1 ; echo $?) -gt 0 ]; then
 | 
				
			||||||
 | 
					                $zfs create $prefix$part
 | 
				
			||||||
 | 
					            fi
 | 
				
			||||||
 | 
					            prefix="$prefix$part/"
 | 
				
			||||||
 | 
					        done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        IFS=$'\n'
 | 
				
			||||||
 | 
					        for snap in $($ssh $source "zfs list -H -t snapshot -o name -S creation $name | grep -E \"$snapshot_filter\" | tail -1"); do
 | 
				
			||||||
 | 
					            $ssh $source "zfs send -pv $snap" | $zfs receive -x mountpoint -x canmount -x $tag -x com.sun:auto-snapshot -dvF $target/$(echo $name | cut -d'/' -f1)
 | 
				
			||||||
 | 
					        done
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # replicate incremental
 | 
				
			||||||
 | 
					    guid=$($zfs list -H -o guid -s creation -t snapshot $target/$name | tail -1)
 | 
				
			||||||
 | 
					    last=$($ssh $source "zfs list -H -o name,guid -t snapshot $name | grep $guid | tail -1 | cut -f1")
 | 
				
			||||||
 | 
					    IFS=$'\n'
 | 
				
			||||||
 | 
					    for snap in $($ssh $source "zfs list -H -o name,guid -s creation -t snapshot $name | grep -E \"$snapshot_filter\" | grep --after-context=200 $guid | grep -v $guid | cut -f1"); do
 | 
				
			||||||
 | 
					        $ssh $source "zfs send -v -i $last $snap" | zfs receive -x mountpoint -x canmount -x $tag -x com.sun:auto-snapshot -Fv $target/$name
 | 
				
			||||||
 | 
					        last=$snap
 | 
				
			||||||
 | 
					    done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # cleanup old snapshots
 | 
				
			||||||
 | 
					    for interval in $(echo -e $snapshot_filter) ; do
 | 
				
			||||||
 | 
					        guid=$($ssh $source "zfs list -H -o guid,name -S creation -t snapshot $name | grep $interval | cut -f1 | tail -1")
 | 
				
			||||||
 | 
					        if [[ "$(echo -e "$guid" | sed 's/\n//g')" != "" ]]; then
 | 
				
			||||||
 | 
					            for snap in $($zfs list -H -o name,guid -S creation -t snapshot $target/$name | grep $interval | grep --after-context=200 $guid | grep -v $guid | cut -f1); do
 | 
				
			||||||
 | 
					                $zfs destroy $snap
 | 
				
			||||||
 | 
					            done
 | 
				
			||||||
 | 
					        fi
 | 
				
			||||||
 | 
					    done
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
		Reference in New Issue
	
	Block a user