Compare commits

...

62 Commits

Author SHA1 Message Date
1ed851a388 Merge pull request 'dev' (#1) from dev into main
Reviewed-on: #1
2023-11-29 19:55:08 +01:00
4b09edb962 Add log message, if repl fails 2023-11-21 23:55:14 +01:00
e90697ae59 Fis snapshot filtering 2023-11-21 23:23:30 +01:00
da27f61fdb Additional error handling and return code 2023-11-21 22:40:27 +01:00
4b23888a13 Add returncode 2023-11-21 22:20:54 +01:00
2d18b32051 Detect incompatible target, fix logmessages 2023-11-21 21:54:10 +01:00
4cd065ab77 Revert dataset creation & initial replication fix 2023-11-21 19:56:07 +01:00
62e31fdfba Fix dataset creation 2023-11-21 19:20:14 +01:00
2ab3bf7d29 Chenge path in Readme to dev 2023-11-21 18:58:28 +01:00
1aad3d96fa Bugfixes: canmount, config log, init repl 2023-11-21 18:57:05 +01:00
a5c00566ce Update README.md 2023-11-06 09:53:50 +01:00
0084293ecb Update documentation 2023-11-02 23:55:12 +01:00
3ca035e59a Fix line 218, quotation 2023-11-02 23:46:20 +01:00
b60f4011e2 Fix deletion 2023-11-02 23:31:32 +01:00
a639bb05c2 Fix snapshot deletion 2023-11-02 22:17:41 +01:00
98769cc028 Bugfixes 2023-11-02 20:36:24 +01:00
b7e480db73 Fix paths of executables 2023-11-02 20:11:50 +01:00
dd3a6a2fdc Add snapshot function based on zfs-auto-snapshot 2023-11-02 19:29:18 +01:00
7c6728148c Fix canmount 2023-10-19 22:14:58 +02:00
f5a852e48a Remove refquota; Fix canmount 2023-10-19 20:36:54 +02:00
15a0658903 Update refquota on target, if changed on source 2023-10-15 21:14:35 +02:00
7c5fd18c4e Remove dead code 2023-10-12 20:08:30 +02:00
31d1843e88 Fix backup storage creation 2023-10-12 20:05:50 +02:00
4c49199a35 „README.md“ ändern 2023-05-23 21:56:53 +02:00
a4d8881971 „DOCUMENTATION_DE.md“ ändern 2023-05-15 18:34:11 +02:00
14906d0645 Add documentation in german 2023-05-15 18:30:49 +02:00
75549baba6 Fix mp and cm variables 2023-05-15 17:45:28 +02:00
b84b4ddfec „README.md“ ändern 2023-05-15 15:51:39 +02:00
6fde230d4d „bashclub-zsync/etc/logrotate.d/bashclub_zsync“ ändern 2023-05-15 15:50:58 +02:00
65c23b744a Add logrotate config 2023-05-15 15:48:39 +02:00
fec08d6873 Fix remove guid 2023-05-10 18:02:54 +02:00
2fcb72de5c Fix it again 2023-05-10 17:57:53 +02:00
2e9ae9c965 Fix mountpoint/canmount properties 2023-05-10 17:52:01 +02:00
79aaa3a146 Fix mountpoint,canmount error on volume replication 2023-05-08 20:00:40 +02:00
6f1f387e22 „README.md“ ändern 2023-03-15 15:43:18 +01:00
923aa36bec „README.md“ ändern 2023-03-05 23:30:21 +01:00
69dc859a97 fix wc -l on truenas 2023-03-05 23:24:11 +01:00
fa6c2a0b12 Testing truenas support 2023-03-05 23:00:37 +01:00
dffd1037a2 Add min_keep parameter 2023-03-05 00:28:46 +01:00
d7eccfd4fc Change logging 2023-03-04 23:40:58 +01:00
da25107a83 Improve logging 2023-03-04 23:39:55 +01:00
034431a935 Skip datasets without snapshots 2023-03-04 23:13:36 +01:00
120e057ea2 Improve log messages 2023-03-04 22:49:08 +01:00
23247bcb9b Fix log message 2023-03-04 22:46:51 +01:00
221fd22ba5 Improve logging 2023-03-04 22:40:47 +01:00
6e1d973f61 Add log messages 2023-03-04 22:05:53 +01:00
a817abbe2d Fix snaps to delete 2023-03-04 21:52:18 +01:00
1e2343f908 Add min_keep parameter 2023-03-04 21:39:52 +01:00
ef67fc87b1 Rework aes detection 2023-03-04 20:39:59 +01:00
b645d1f106 Fix sshport 2023-03-04 20:28:32 +01:00
af290e0fe9 Redesign auto-snapshot config 2023-03-04 20:25:47 +01:00
aecb871de1 Fix aes check 2023-03-04 17:20:58 +01:00
24483b08bb fix ssh parameter 2023-03-04 17:18:59 +01:00
754243de52 Set 2023-03-04 17:18:19 +01:00
4326841b46 Activate raw parameter in send command 2023-03-04 17:02:27 +01:00
682b3f107e Disable auto-snapshot on target creation 2023-03-04 16:52:42 +01:00
5930637ded „README.md“ ändern 2023-02-23 20:19:11 +01:00
d3f5709b72 Fix logging 2023-02-19 17:30:06 +01:00
68ac6b7bca Improve logging 2023-02-18 23:17:11 +01:00
02e330de61 Fix deletion 2023-02-18 17:35:25 +01:00
c08ac04879 fix config print 2023-02-18 14:43:18 +01:00
a16db99f01 Add ssh port config parameter 2023-02-18 14:27:52 +01:00
4 changed files with 319 additions and 61 deletions

72
DOCUMENTATION_DE.md Normal file
View File

@ -0,0 +1,72 @@
# Konfiguration auf dem Zielserver
1. Öffnen Sie eine SSH-Verbindung zum Zielserver, entweder über die Befehlszeile oder ein SSH-Tool wie PuTTY.
2. Stellen Sie sicher, dass der Zielserver die erforderlichen Voraussetzungen erfüllt. Dazu gehören das Vorhandensein der benötigten Befehle (`zfs`, `ssh`, `grep`) und die korrekte Konfiguration der Pfade (`PATH`-Variable im Script).
3. Erstellen Sie eine Konfigurationsdatei für das Script. Standardmäßig wird die Datei `/etc/bashclub/zsync.conf` verwendet. Sie können jedoch einen anderen Pfad angeben, indem Sie den Parameter `-c` beim Aufruf des Scripts verwenden. Zusätzlich können Sie weitere Konfigurationsdateien erstellen, um unterschiedliche Replikationen einzurichten. Zum Beispiel: `/usr/bin/bashclub-zsync -c /pfad/zur/konfiguration1.conf`, `/usr/bin/bashclub-zsync -c /pfad/zur/konfiguration2.conf`
4. Öffnen Sie die Konfigurationsdatei mit einem Texteditor und passen Sie die folgenden Einstellungen an:
- `source`: Geben Sie den SSH-Adresse des Quellservers an, von dem die Daten repliziert werden sollen. Beispiel: `source=user@host`
- `target`: Geben Sie die Pfad des Ziel-ZFS-Dateisystems auf dem Zielserver an, zu dem die Daten repliziert werden sollen. Beispiel: `target=pool/dataset`
- `sshport`: Geben Sie den SSH-Port des Quellservers an. Standardmäßig ist dies `22`, aber Sie können ihn entsprechend anpassen.
- `tag`: Geben Sie den ZFS-Tag an, der verwendet werden soll, um die zu replizierenden Dateisysteme oder Volumes zu identifizieren. Beispiel: `tag=bashclub:zsync`
- `snapshot_filter`: Geben Sie eine Pipe-separierte Liste von Snapshot-Namenfiltern an, die bestimmen, welche Snapshots repliziert werden sollen. Beispiel: `snapshot_filter="hourly|daily|weekly|monthly"`
- `min_keep`: Geben Sie die Mindestanzahl von Snapshots pro Filter an, die beibehalten werden sollen. Beispiel: `min_keep=3`
- `zfs_auto_snapshot_keep`: Aktiviert das erzeugen eines Snapshots auf der Quelle vor der Replikation, gibt die Anzahl der Snapshots an, die behalten werden sollen. Beispiel: `zfs_auto_snapshot_keep=0` => Deaktiviert die Funktion, `zfs_auto_snapshot_keep=7` behält 7 Snapshots.
- `zfs_auto_snapshot_label`: Definniert das Label des Snapshots, Default-Wert ist `backup` (ergibt: zfs-auto-snap_backup-YYYY-MM-dd-hhmm). Beispiel: `zfs_auto_snapshot_label=backup`
Wiederholen Sie diese Schritte für jede zusätzliche Konfigurationsdatei, um unterschiedliche Replikationen einzurichten.
5. Speichern Sie die Konfigurationsdateien.
6. Überprüfen Sie, ob der Zielserver die erforderlichen ZFS-Datasets oder Volumes enthält, auf die die Daten repliziert werden sollen. Stellen Sie sicher, dass die Namen der Datasets/Volumes dem ZFS-Tag entsprechen, den Sie in den Konfigurationsdateien festgelegt haben.
7. Um das Script automatisch auszuführen, erstellen Sie eine Cronjob-Konfigurationsdatei im Verzeichnis `/etc/cron.d/`. Öffnen Sie eine neue Datei mit einem Texteditor und geben Sie die gewünschte Ausführungszeit und den Befehl ein. Beispiel:
a) Erstellen Sie das Verzeichnis `/var/log/bashclub-zsync`, falls es noch nicht existiert. Verwenden Sie den Befehl:
```plaintext
sudo mkdir -p /var/log/bashclub-zsync
```
b) Öffnen Sie die Cronjob-Konfigurationsdatei mit dem Befehl:
```plaintext
sudo nano /etc/cron.d/bashclub-zsync-cronjob
```
c) Fügen Sie den folgenden Inhalt in die Datei ein:
```plaintext
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 * * * * root /usr/bin/bashclub-zsync -c /etc/bashclub/zsync.conf >> /var/log/bashclub-zsync/zsync.log 2>&1
```
In diesem Beispiel wird das Script einmal pro Stunde ausgeführt. Der Befehl `/usr/bin/bashclub-zsync -c /etc/bashclub/zsync.conf` wird als root-Benutzer ausgeführt, und die Ausgabe wird in die Datei `/var/log/bashclub-zsync/zsync.log` umgeleitet.
d) Speichern Sie die Datei und schließen Sie den Texteditor.
Das Script wird nun automatisch gemäß dem angegebenen Intervall ausgeführt, und die Ausgabe wird im angegebenen Logfile gespeichert. Stellen Sie sicher, dass die Dateiberechtigungen für die Cronjob-Konfigurationsdatei korrekt sind, damit sie von Cron erkannt wird.
# Konfiguration auf dem Quellserver
Um ZFS-Dateisysteme und Volumes für die Replikation mit dem bashclub-zsync-Script zu markieren, verwenden Sie das ZFS-Attribut "bashclub:zsync" auf dem Quellserver. Dieses Attribut kann mit dem Parameter "value" konfiguriert werden, der die Werte "subvols", "all" oder "exclude" haben kann.
Hier ist eine Anleitung zur Konfiguration auf dem Quellserver:
1. Identifizieren Sie das ZFS-Dateisystem oder Volume, das Sie für die Replikation markieren möchten.
2. Setzen Sie das ZFS-Attribut "bashclub:zsync" mit dem gewünschten Value-Parameter-Wert. Verwenden Sie den Befehl:
```plaintext
zfs set bashclub:zsync=<value> <datensatz>
```
Ersetzen Sie `<value>` durch einen der folgenden Werte: "subvols", "all" oder "exclude". Ersetzen Sie `<datensatz>` durch den Namen des ZFS-Datensatzes.
- "subvols": Markiert nur Volumes und Dateisysteme in der Hierarchie unterhalb des Datensatzes, für den das Attribut gesetzt ist (schließt den Wurzel-Datensatz aus).
- "all": Markiert alle Volumes und Dateisysteme im ZFS-Datensatz einschließlich des Wurzel-Datensatzes.
- "exclude": Schließt Volumes und Dateisysteme im ZFS-Datensatz aus, für den das Attribut gesetzt ist.
Durch die Markierung mit dem Attribut "bashclub:zsync" wird das betreffende ZFS-Dateisystem oder -Volume für die Replikation mit dem bashclub-zsync-Script berücksichtigt.
Bitte beachten Sie, dass die genaue Syntax und Verwendung des Befehls je nach dem von Ihnen verwendeten Betriebssystem oder der ZFS-Version variieren kann. Stellen Sie sicher, dass Sie über ausreichende Berechtigungen verfügen, um die Konfiguration auf dem Quellserver vorzunehmen.
Um mehrere Replikationen zu unterschiedlichen Hosts mit dem bashclub-zsync-Script auf dem Zielserver zu ermöglichen, können Sie den Namen des ZFS-Attributs "bashclub:zsync" in der Scriptkonfiguration mit dem Parameter "tag" anpassen. Dadurch können Sie verschiedene Replikationen zu unterschiedlichen Hosts einrichten und steuern. Öffnen Sie das bashclub-zsync-Script auf dem Zielserver, suchen Sie nach dem "tag"-Parameter und ändern Sie den Wert in den gewünschten Namen für das ZFS-Attribut. Speichern Sie das Script nach der Änderung. Stellen Sie sicher, dass Sie über ausreichende Berechtigungen verfügen, um das Script zu bearbeiten und die Konfiguration auf dem Zielserver vorzunehmen.

View File

@ -1,7 +1,71 @@
---- THIS IS A CLONE OF THE ORIGINAL REPOSITORY ON git.bashclub.org ----
# zsync
ZFS replication script by Thorsten Spille <thorsten@spille-edv.de>
- replicates ZFS filesystems/volumes with user parameter bashclub:zsync configured
- mirrored replication with existing snapshots
- pull replication only
- creates full path on target pool
ZFS replication script by Thorsten Spille <thorsten@spille-edv.de>
- replicates ZFS filesystems/volumes with user parameter bashclub:zsync (or custom name) configured
- creates optional snapshot before replication (required zfs-auto-znapshot)
- parameter setting uses zfs hierarchy on source
- mirrored replication with existing snapshots (filtered by snapshot_filter)
- pull/local replication only
- auto creates full path on target pool, enforce com.sun:auto-snapshot=false, inherits mountpoint and sets canmount=noauto
- raw replication
- tested on Proxmox VE 7.x/8.x
- ssh cipher auto selection
## Installation
#### Download and make executable
~~~
wget -q --no-cache -O /usr/bin/bashclub-zsync https://git.bashclub.org/bashclub/zsync/raw/branch/dev/bashclub-zsync/usr/bin/bashclub-zsync
chmod +x /usr/bin/bashclub-zsync
bashclub-zsync
~~~
## Configuration
After first execution adjust the default config file `/etc/bashclub/zsync.conf`:
~~~
# target path on local machine
target=backup/px1
# source host
source=user@host
# source host ssh port
sshport=22
# tag to mark source filesystem
tag=bashclub:zsync
# snapshot name filter
snapshot_filter="hourly|daily|weekly|monthly"
# number of minimum snapshots to keep (per snapshot filter)
min_keep=3
# number of zfs snapshots to keep on source (0 = snapshot function disabled)
zfs_auto_snapshot_keep=0
# make snapshot via zfs-auto-snapshot before replication
zfs_auto_snapshot_label="backup"
~~~
### Define a cronjob
#### cron.d example
File: /etc/cron.d/bashclub-zsync
~~~
00 23 * * * root /usr/bin/bashclub-zsync -c /etc/bashclub/zsync.conf > /var/log/bashclub-zsync/zsync.log
~~~
#### cron.{hourly|daily|weekly|monthly}
File: /etc/cron.hourly/bashclub-zsync
~~~
/usr/bin/bashclub-zsync -c /etc/bashclub/zsync.conf > /var/log/bashclub-zsync/zsync.log
~~~
# Author
### Thorsten Spille
[<img src="https://storage.ko-fi.com/cdn/brandasset/kofi_s_tag_dark.png" rel="Support me on Ko-Fi">](https://ko-fi.com/thorakel)

View File

@ -0,0 +1,9 @@
/var/log/bashclub-zsync/*.log {
weekly
rotate 12
compress
delaycompress
missingok
notifempty
create 644 root root
}

View File

@ -8,6 +8,15 @@ PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
prog="$(basename $0)"
zfs=$(which zfs)
ssh=$(which ssh)
grep=$(which grep)
uniq=$(which uniq)
cut=$(which cut)
tail=$(which tail)
wc=$(which wc)
tr=$(which tr)
sed=$(which sed)
zfs_auto_snapshot=$(which zfs-auto-snapshot)
rc=0
debug=
#### default config file, can be changed with parameter -c
@ -20,15 +29,24 @@ target=pool/dataset
# ssh address of remote machine
source=user@host
# ssh port of remote machine
sshport=22
# 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"
#
# pipe separated list of snapshot name filters
snapshot_filter="hourly|daily|weekly|monthly"
# minimum count of snapshots per filter to keep
min_keep=3
# number of zfs snapshots to keep on source (0 or 1 = snapshot function disabled)
zfs_auto_snapshot_keep=0
# make snapshot via zfs-auto-snapshot before replication
zfs_auto_snapshot_label="backup"
usage() {
cat >&2 <<-EOF
usage: $prog [-h] [-d] [-c CONFIG]
@ -53,88 +71,183 @@ while getopts "hdc:" opt; do
done
shift $((OPTIND-1))
function log() {
echo -e "$(date +'%b %d %T') $1"
}
# load config file
if [ -f $conf ]; then
echo "Reading configuration $conf"
log "[INFO] Reading configuration $conf"
source $conf
else
mkdir -p $(dirname $conf)
cat << EOF > $conf
target=$target
source=$source
sshport=$sshport
tag=$tag
subvol_source="$subvol_source"
snapshot_filter="$snapshot_filter"
min_keep=$min_keep
zfs_auto_snapshot_keep=$zfs_auto_snapshot_keep
zfs_auto_snapshot_label=$zfs_auto_snapshot_label
EOF
echo "Initial config file created. Please adjust and restart script. Exiting..."
log "[INFO] Initial config file created. Please adjust and restart script. Exiting..."
usage 0
fi
if [[ $source == "" ]]; then
echo "source is empty, switching to local mode."
log "[INFO] source is empty, switching to local mode."
ssh=
echo -e "Configuration:\n\ttarget=$target\n\ttag=$tag\n\tsubvol_source=$subvol_source\n\tsnapshot_filter=$snapshot_filter\n"
sshport=
log "[INFO] Configuration:\n\ttarget=$target\n\ttag=$tag\n\tsnapshot_filter=$snapshot_filter\n\tmin_keep=$min_keep\nzfs_auto_snapshot_keep=$zfs_auto_snapshot_keep\nzfs_auto_snapshot_label=$zfs_auto_snapshot_label\n"
else
echo -e "Configuration:\n\ttarget=$target\n\tsource=$source\n\ttag=$tag\n\tsubvol_source=$subvol_source\n\tsnapshot_filter=$snapshot_filter\n"
sshport=-p$sshport
log "[INFO] Configuration:\n\ttarget=$target\n\tsource=$source\n\tsshport=$sshport\n\ttag=$tag\n\tsnapshot_filter=$snapshot_filter\n\tmin_keep=$min_keep\nzfs_auto_snapshot_keep=$zfs_auto_snapshot_keep\nzfs_auto_snapshot_label=$zfs_auto_snapshot_label\n"
fi
# query source datasets/subvols to replicate
local_os_id=$($grep -E "^ID=" /etc/os-release | $cut -d'=' -f2)
remote_os_id=$($ssh $source $sshport "grep -E \"^ID=\" /etc/os-release | cut -d'=' -f2")
if [[ $local_os_id == "freebsd" ]]; then
local_aes=$($grep -o AES /var/run/dmesg.boot | $uniq)
else
local_aes=$($grep -m1 -o aes /proc/cpuinfo | $uniq)
fi
if [[ $remote_os_id == "freebsd" ]]; then
remote_aes=$($ssh $source $sshport "grep -o AES /var/run/dmesg.boot | uniq")
else
remote_aes=$($ssh $source $sshport "grep -m1 -o aes /proc/cpuinfo | uniq")
fi
if [[ $local_aes == "aes" ]] && [[ $remote_aes == "aes" ]]; then
if [[ $debug == "-v" ]]; then log "[DEBUG] Switching to cipher aes256-gcm@openssh.com"; fi
sshcipher=-caes256-gcm@openssh.com
else
if [[ $debug == "-v" ]]; then log "[DEBUG] Using default cipher chacha20-poly1305@openssh.com"; fi
sshcipher=-cchacha20-poly1305@openssh.com
fi
include_list="[INFO] Included datasets:\n"
exclude_list="[INFO] Excluded datasets:\n"
IFS=$'\n'
for zvol in $($ssh $source "zfs get -H -o name,value,source -t filesystem,volume $tag"); do
name=$(echo $zvol | cut -f1)
if [[ "$(echo $zvol | cut -f2)" == "subvols" ]] && [[ $(echo $zvol | grep -E $subvol_source) ]]; then
echo "Including $name"
for zvol in $($ssh $sshcipher $sshport $source "zfs get -H -o name,value,source -t filesystem,volume $tag"); do
name=$(echo $zvol | $cut -f1)
if [[ "$(echo $zvol | $cut -f2)" == "subvols" ]] && [[ $(echo $zvol | $grep -vE "local|received") ]]; then
include_list="${include_list}\t${name}\n"
syncvols=("${syncvols[@]}" "$name")
elif [[ "$(echo $zvol | cut -f2)" == "all" ]];then
echo "Including $name"
elif [[ "$(echo $zvol | $cut -f2)" == "all" ]] && [[ $(echo $zvol | $grep -v received) ]];then
include_list="${include_list}\t${name}\n"
syncvols=("${syncvols[@]}" "$name")
else
echo "Excluding $name"
exclude_list="${exclude_list}\t${name}\n"
fi
done
log "$include_list"
log "$exclude_list"
if ! $zfs list $target > /dev/null 2>&1 ; then
log "[DEBUG] $target does not exist. Creating..."
$zfs create -o canmount=noauto -o com.sun:auto-snapshot=false $target
else
log "[DEBUG] $target exists, check auto-snapshot..."
if [[ $($zfs get -H -o value,source com.sun:auto-snapshot $target) != "false local" ]]; then
$zfs set com.sun:auto-snapshot=false $target
fi
fi
if [ $zfs_auto_snapshot_keep -gt 1 ]; then
log "[DEBUG] Running zfs-auto-snapshot"
$ssh $sshcipher $sshport $source "which zfs-auto-snapshot > /dev/null || exit 0 ; zfs-auto-snapshot --quiet --syslog --label=$zfs_auto_snapshot_label --keep=$zfs_auto_snapshot_keep //"
if [[ $snapshot_filter == "" ]]; then
snapshot_filter="$zfs_auto_snapshot_label"
else
snapshot_filter="$snapshot_filter|$zfs_auto_snapshot_label"
fi
fi
for name in "${syncvols[@]}"; do
IFS=$' '
if [ $($zfs list -H -s creation -t snapshot $target/$name > /dev/null 2>&1 ; echo $?) -gt 0 ]; then
# create parent datasets
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
echo "Creating $prefix$part"
$zfs create -p $prefix$part
fi
prefix="$prefix$part/"
done
# start initial replication
IFS=$'\n'
for snap in $($ssh $source "zfs list -H -t snapshot -o name -S creation $name | grep -E \"$snapshot_filter\" | tail -1"); do
echo "Start initial replication: $snap => $target/$(echo $name | cut -d'/' -f1)"
$ssh $source "zfs send -p $debug $snap" | $zfs receive -x mountpoint -x canmount -x $tag -x com.sun:auto-snapshot $debug -dF $target/$(echo $name | cut -d'/' -f1)
done
log "[INFO] Replicate $name"
fstype=$($ssh $sshcipher $sshport $source zfs get -H -o value type $name)
if [[ $fstype == "filesystem" ]]; then
mp=-xmountpoint
cm=-ocanmount=noauto
else
mp=
cm=
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
echo "Replicating delta of $last <=> $snap to $target/$name"
$ssh $source "zfs send $debug -i $last $snap" | zfs receive -x mountpoint -x canmount -x $tag -x com.sun:auto-snapshot -F $debug $target/$name
last=$snap
done
if [[ $($ssh $sshcipher $sshport $source "zfs list -H -t snapshot -o name -S creation $name 2>/dev/null | grep -E \"@.*($snapshot_filter)\" | wc -l | tr -d ' '") -gt 0 ]]; then
IFS=$' '
if ! $zfs list -H $target/$name > /dev/null 2>&1 ; then
if [[ $debug == "-v" ]]; then log "[DEBUG] $target/$name does not exist"; fi
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
if [[ $debug == "-v" ]]; then log "[DEBUG] $prefix$part does not exist"; fi
log "[INFO] Creating $prefix$part"
$zfs create -o canmount=noauto $autosnap -p $prefix$part
fi
prefix="$prefix$part/"
done
# cleanup old snapshots
filter=$(echo -e $snapshot_filter | sed "s/|/ /g")
IFS=$' '
for interval in $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
echo "Deleting $snap"
$zfs destroy $debug $snap
if [[ $debug == "-v" ]]; then log "[DEBUG] $name - Start initial replication"; fi
IFS=$'\n'
for snap in $($ssh $sshcipher $sshport $source "zfs list -H -t snapshot -o name -S creation $name | grep -E \"@.*($snapshot_filter)\" | tail -1"); do
log "[INFO] Start initial replication: $snap => $target/$(echo $name | $cut -d'/' -f1)"
$ssh $sshcipher $sshport $source "zfs send -w -p $debug $snap" | $zfs receive $mp $cm -x $tag -x com.sun:auto-snapshot $debug -dF $target/$(echo $name | $cut -d'/' -f1)
if [ $? -gt $rc ]; then rc=1; log "[ERROR] initial replication to $target/$name failed."; fi
done
fi
done
done
if [[ $debug == "-v" ]]; then log "[DEBUG] $name - Start incremental replication"; fi
guid=$($zfs list -H -o guid -s creation -t snapshot $target/$name | $tail -1)
if [[ $guid != "" ]]; then
last=$($ssh $sshcipher $sshport $source "zfs list -H -o name,guid -t snapshot $name | grep $guid | tail -1 | cut -f1")
if [[ $last != "" ]]; then
IFS=$'\n'
if [[ $fstype == "filesystem" ]] && [[ $($zfs get -H -o value canmount $target/$name) != "noauto" ]]; then
$zfs set canmount=noauto $target/$name
fi
for snap in $($ssh $sshcipher $sshport $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
log "[INFO] Replicating delta of $last => $snap to $target/$name"
$ssh $sshcipher $sshport $source "zfs send -w $debug -i $last $snap" | zfs receive -x $tag -x com.sun:auto-snapshot -F $debug $target/$name
if [ $? -gt $rc ]; then rc=1; log "[ERROR] incremental replication to $target/$name failed."; fi
last=$snap
done
else
log "[ERROR] No matching snapshot (guid: $guid) found on source filesystem. This can be outdated snapshots on target or a previously deleted and new created dataset $name on the source filesystem."
if [ $rc -eq 0 ]; then rc=1; fi
fi
else
log "[ERROR] No snapshot found on $target/$name to add incremental snapshots to. The target dataset (with all children) needs to be deleted and recreated via replication."
if [ $rc -eq 0 ]; then rc=1; fi
fi
if [[ $debug == "-v" ]]; then log "[DEBUG] $name - Start deletion of old snapshots"; fi
filter=$(echo -e $snapshot_filter | sed "s/|/\n/g")
IFS=$'\n'
for interval in $filter ; do
if [[ $debug == "-v" ]]; then log "[DEBUG] $name - Checking interval $interval"; fi
guid=$($ssh $sshcipher $sshport $source "zfs list -H -o guid,name -S creation -t snapshot $name | grep -E \"@.*$interval\" | cut -f1 | tail -1")
if [[ "$(echo -e "$guid" | sed 's/\n//g')" != "" ]]; then
snaps_to_delete=$($zfs list -H -o name,guid -S creation -t snapshot $target/$name | $grep -E "@.*$interval" | $grep --after-context=200 $guid | $grep -v $guid | $cut -f1)
snap_count=$($zfs list -H -o name,guid -S creation -t snapshot $target/$name | $grep -E "@.*$interval" | $wc -l | $tr -d ' ')
for snap in $snaps_to_delete; do
if [[ $snap_count -gt $min_keep ]]; then
log "[INFO] Deleting $snap"
if [[ $debug == "-v" ]]; then log "[DEBUG] $name - snap_count=$snap_count, min_keep=$min_keep"; fi
$zfs destroy $debug $snap
snap_count=$(expr $snap_count - 1)
else
if [[ $debug == "-v" ]]; then log "[DEBUG] $name - Skipping deletion of $snap. snap_count=$snap_count, min_keep=$min_keep"; fi
fi
done
fi
done
else
if [[ $debug == "-v" ]]; then log "[DEBUG] $name - No snapshots found with filter $snapshot_filter"; fi
fi
done
exit $rc