From 8a2a673c58ddbc1b222ec65a75c4db77b9e8b4e4 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sat, 6 Jan 2018 11:01:44 +0000 Subject: [PATCH 01/24] fixed loud 'NEWEST SNAPSHOT' message --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index 7337f5b..d039d15 100755 --- a/syncoid +++ b/syncoid @@ -597,7 +597,7 @@ sub getnewestsnapshot { my $snaps = shift; foreach my $snap ( sort { $snaps{'source'}{$b}{'creation'}<=>$snaps{'source'}{$a}{'creation'} } keys %{ $snaps{'source'} }) { # return on first snap found - it's the newest - print "NEWEST SNAPSHOT: $snap\n"; + if (!$quiet) { print "NEWEST SNAPSHOT: $snap\n"; } return $snap; } # must not have had any snapshots on source - looks like we'd better create one! From e902df1ef2347aa08aeb20478a2b988695dd5d20 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sat, 6 Jan 2018 11:03:02 +0000 Subject: [PATCH 02/24] also made warnings quiet --- syncoid | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/syncoid b/syncoid index d039d15..aec99cd 100755 --- a/syncoid +++ b/syncoid @@ -452,13 +452,13 @@ sub checkcommands { if ($avail{'sourcecompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - print "WARN: $compressargs{'rawcmd'} not available on source $s- sync will continue without compression.\n"; + if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on source $s- sync will continue without compression.\n"; } } $avail{'compress'} = 0; } if ($avail{'targetcompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - print "WARN: $compressargs{'rawcmd'} not available on target $t - sync will continue without compression.\n"; + if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on target $t - sync will continue without compression.\n"; } } $avail{'compress'} = 0; } @@ -471,7 +471,7 @@ sub checkcommands { # corner case - if source AND target are BOTH remote, we have to check for local compress too if ($sourcehost ne '' && $targethost ne '' && $avail{'localcompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - print "WARN: $compressargs{'rawcmd'} not available on local machine - sync will continue without compression.\n"; + if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on local machine - sync will continue without compression.\n"; } } $avail{'compress'} = 0; } @@ -479,7 +479,7 @@ sub checkcommands { if ($debug) { print "DEBUG: checking availability of $mbuffercmd on source...\n"; } $avail{'sourcembuffer'} = `$sourcessh $lscmd $mbuffercmd 2>/dev/null`; if ($avail{'sourcembuffer'} eq '') { - print "WARN: $mbuffercmd not available on source $s - sync will continue without source buffering.\n"; + if (!$quiet) { print "WARN: $mbuffercmd not available on source $s - sync will continue without source buffering.\n"; } $avail{'sourcembuffer'} = 0; } else { $avail{'sourcembuffer'} = 1; @@ -488,7 +488,7 @@ sub checkcommands { if ($debug) { print "DEBUG: checking availability of $mbuffercmd on target...\n"; } $avail{'targetmbuffer'} = `$targetssh $lscmd $mbuffercmd 2>/dev/null`; if ($avail{'targetmbuffer'} eq '') { - print "WARN: $mbuffercmd not available on target $t - sync will continue without target buffering.\n"; + if (!$quiet) { print "WARN: $mbuffercmd not available on target $t - sync will continue without target buffering.\n"; } $avail{'targetmbuffer'} = 0; } else { $avail{'targetmbuffer'} = 1; @@ -500,14 +500,14 @@ sub checkcommands { $avail{'localmbuffer'} = `$lscmd $mbuffercmd 2>/dev/null`; if ($avail{'localmbuffer'} eq '') { $avail{'localmbuffer'} = 0; - print "WARN: $mbuffercmd not available on local machine - sync will continue without local buffering.\n"; + if (!$quiet) { print "WARN: $mbuffercmd not available on local machine - sync will continue without local buffering.\n"; } } } if ($debug) { print "DEBUG: checking availability of $pvcmd on local machine...\n"; } $avail{'localpv'} = `$lscmd $pvcmd 2>/dev/null`; if ($avail{'localpv'} eq '') { - print "WARN: $pvcmd not available on local machine - sync will continue without progress bar.\n"; + if (!$quiet) { print "WARN: $pvcmd not available on local machine - sync will continue without progress bar.\n"; } $avail{'localpv'} = 0; } else { $avail{'localpv'} = 1; From c0c30500765395cd22f975abb198ce3d4fb1d1f3 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Tue, 19 Jun 2018 18:21:06 +0200 Subject: [PATCH 03/24] added option for skipping the parent dataset in recursive replication --- README.md | 4 ++++ syncoid | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cc75bdf..7392fa7 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,10 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup This will also transfer child datasets. ++ --skip-parent + + This will skip the syncing of the parent dataset. Does nothing without '--recursive' option. + + --compress Currently accepted options: gzip, pigz-fast, pigz-slow, lzo (default) & none. If the selected compression method is unavailable on the source and destination, no compression will be used. diff --git a/syncoid b/syncoid index 999ee79..5cbc1c4 100755 --- a/syncoid +++ b/syncoid @@ -19,7 +19,7 @@ use Sys::Hostname; my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [], 'target-bwlimit' => '', 'source-bwlimit' => ''); GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", "source-bwlimit=s", "target-bwlimit=s", "sshkey=s", "sshport=i", "sshcipher|c=s", "sshoption|o=s@", - "debug", "quiet", "no-stream", "no-sync-snap", "no-resume") or pod2usage(2); + "debug", "quiet", "no-stream", "no-sync-snap", "no-resume", "skip-parent") or pod2usage(2); my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set @@ -141,6 +141,11 @@ sub getchilddatasets { my @children = ; close FH; + if (defined $args{'skip-parent'}) { + # parent dataset is the first element + shift @children; + } + return @children; } @@ -1137,6 +1142,7 @@ Options: --compress=FORMAT Compresses data during transfer. Currently accepted options are gzip, pigz-fast, pigz-slow, lzo (default) & none --recursive|r Also transfers child datasets + --skip-parent Skipp the syncing of the parent dataset. Doesg nothing without '--recursive' option. --source-bwlimit= Bandwidth limit on the source transfer --target-bwlimit= Bandwidth limit on the target transfer --no-stream Replicates using newest snapshot instead of intermediates From 70b259ac3cb9ef9167776eac0424d0a78ea0c0c4 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Tue, 19 Jun 2018 18:24:34 +0200 Subject: [PATCH 04/24] typos --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index 5cbc1c4..8ab3a41 100755 --- a/syncoid +++ b/syncoid @@ -1142,7 +1142,7 @@ Options: --compress=FORMAT Compresses data during transfer. Currently accepted options are gzip, pigz-fast, pigz-slow, lzo (default) & none --recursive|r Also transfers child datasets - --skip-parent Skipp the syncing of the parent dataset. Doesg nothing without '--recursive' option. + --skip-parent Skips syncing of the parent dataset. Does nothing without '--recursive' option. --source-bwlimit= Bandwidth limit on the source transfer --target-bwlimit= Bandwidth limit on the target transfer --no-stream Replicates using newest snapshot instead of intermediates From 0f8fee7637ec1265bd0a72011a08b0666e6123c4 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Tue, 19 Jun 2018 19:43:36 +0200 Subject: [PATCH 05/24] added option for using extra identification in the snapshot name for replicating to multiple targets --- README.md | 4 ++++ syncoid | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cc75bdf..632a0af 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,10 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup This is the destination dataset. It can be either local or remote. ++ --identifier= + + Adds the given identifier to the snapshot name after "syncoid_" prefix and before the hostname. This enables the use case of reliable replication to multiple targets from the same host. The following chars are allowed: a-z, A-Z, 0-9 and -. + + -r --recursive This will also transfer child datasets. diff --git a/syncoid b/syncoid index 999ee79..dcb9e2f 100755 --- a/syncoid +++ b/syncoid @@ -19,7 +19,7 @@ use Sys::Hostname; my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [], 'target-bwlimit' => '', 'source-bwlimit' => ''); GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", "source-bwlimit=s", "target-bwlimit=s", "sshkey=s", "sshport=i", "sshcipher|c=s", "sshoption|o=s@", - "debug", "quiet", "no-stream", "no-sync-snap", "no-resume") or pod2usage(2); + "debug", "quiet", "no-stream", "no-sync-snap", "no-resume", "identifier=s") or pod2usage(2); my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set @@ -71,6 +71,17 @@ if (length $args{'sshkey'}) { } my $sshoptions = join " ", map { "-o " . $_ } @{$args{'sshoption'}}; # deref required +my $identifier = ""; +if (length $args{'identifier'}) { + if ($args{'identifier'} !~ /^[a-zA-Z0-9-]+$/) { + # invalid extra identifier + print("CRITICAL: extra identifier contains invalid chars!\n"); + pod2usage(2); + exit 127; + } + $identifier = "$args{'identifier'}_"; +} + # figure out if source and/or target are remote. $sshcmd = "$sshcmd $args{'sshcipher'} $sshoptions $args{'sshport'} $args{'sshkey'}"; if ($debug) { print "DEBUG: SSHCMD: $sshcmd\n"; } @@ -812,7 +823,7 @@ sub pruneoldsyncsnaps { # only prune snaps beginning with syncoid and our own hostname foreach my $snap(@snaps) { - if ($snap =~ /^syncoid_\Q$hostid\E/) { + if ($snap =~ /^syncoid_\Q$identifier$hostid\E/) { # no matter what, we categorically refuse to # prune the new sync snap we created for this run if ($snap ne $newsyncsnap) { @@ -898,7 +909,7 @@ sub newsyncsnap { if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; } my $hostid = hostname(); my %date = getdate(); - my $snapname = "syncoid\_$hostid\_$date{'stamp'}"; + my $snapname = "syncoid\_$identifier$hostid\_$date{'stamp'}"; my $snapcmd = "$rhost $mysudocmd $zfscmd snapshot $fsescaped\@$snapname\n"; system($snapcmd) == 0 or die "CRITICAL ERROR: $snapcmd failed: $?"; @@ -1136,6 +1147,7 @@ syncoid - ZFS snapshot replication tool Options: --compress=FORMAT Compresses data during transfer. Currently accepted options are gzip, pigz-fast, pigz-slow, lzo (default) & none + --identifier=EXTRA Extra identifier which is included in the snapshot name. Can be used for replicating to multiple targets. --recursive|r Also transfers child datasets --source-bwlimit= Bandwidth limit on the source transfer --target-bwlimit= Bandwidth limit on the target transfer From ba3836ec520efc30689238e294de1d1c2026fc1b Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Fri, 6 Jul 2018 15:52:54 +0200 Subject: [PATCH 06/24] fixed monitor-health command for pools containing cache and log devices --- sanoid | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sanoid b/sanoid index 9cd9d33..485ee08 100755 --- a/sanoid +++ b/sanoid @@ -976,6 +976,11 @@ sub check_zpool() { ## other cases my ($dev, $sta) = /^\s+(\S+)\s+(\S+)/; + if (!defined($sta)) { + # cache and logs are special and don't have a status + next; + } + ## pool online, not degraded thanks to dead/corrupted disk if ($state eq "OK" && $sta eq "UNAVAIL") { $state="WARNING"; @@ -1111,7 +1116,7 @@ sub checklock { # make sure lockfile contains something if ( -z $lockfile) { # zero size lockfile, something is wrong - die "ERROR: something is wrong! $lockfile is empty\n"; + die "ERROR: something is wrong! $lockfile is empty\n"; } # lockfile exists. read pid and mutex from it. see if it's our pid. if not, see if From f9c1cbb74a3c07fc9e0368721cdcedcc0b2b4a0f Mon Sep 17 00:00:00 2001 From: Jim Salter Date: Sat, 7 Jul 2018 12:06:35 -0400 Subject: [PATCH 07/24] Update INSTALL --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index 33b510d..f0de17b 100644 --- a/INSTALL +++ b/INSTALL @@ -8,7 +8,7 @@ default for SSH transport since v1.4.6. Syncoid runs will fail if one of them is not available on either end of the transport. On Ubuntu: apt install pv lzop mbuffer -On CentOS: yum install lzo pv mbuffer lzop +On CentOS: yum install lzo pv mbuffer lzop perl-Data-Dumper On FreeBSD: pkg install pv mbuffer lzop FreeBSD notes: FreeBSD may place pv and lzop in somewhere other than From dc0afebb30865faa16f42c496b2c59e5ed72b690 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Sat, 7 Jul 2018 20:06:17 +0200 Subject: [PATCH 08/24] allow extra identifier to contain all characters for snapshots names which are allowed by zfs --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index d25d9de..aaf5490 100755 --- a/syncoid +++ b/syncoid @@ -73,7 +73,7 @@ my $sshoptions = join " ", map { "-o " . $_ } @{$args{'sshoption'}}; # deref req my $identifier = ""; if (length $args{'identifier'}) { - if ($args{'identifier'} !~ /^[a-zA-Z0-9-]+$/) { + if ($args{'identifier'} !~ /^[a-zA-Z0-9-_:.]+$/) { # invalid extra identifier print("CRITICAL: extra identifier contains invalid chars!\n"); pod2usage(2); From f409b955694b9dc5f403f16593cc6540cac7fe67 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Sat, 7 Jul 2018 20:10:02 +0200 Subject: [PATCH 09/24] updated parameter documention of --identifier regarding allowed characters --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e1f651..e359886 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup + --identifier= - Adds the given identifier to the snapshot name after "syncoid_" prefix and before the hostname. This enables the use case of reliable replication to multiple targets from the same host. The following chars are allowed: a-z, A-Z, 0-9 and -. + Adds the given identifier to the snapshot name after "syncoid_" prefix and before the hostname. This enables the use case of reliable replication to multiple targets from the same host. The following chars are allowed: a-z, A-Z, 0-9, _, -, : and . . + -r --recursive From 75d8174e6997286aad57d03d1c6ad86d2fd89c79 Mon Sep 17 00:00:00 2001 From: Piotr Paczynski Date: Wed, 18 Jul 2018 01:19:37 +0200 Subject: [PATCH 10/24] Fix 'resume support' detection on FreeBSD --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index aaf5490..31ff757 100755 --- a/syncoid +++ b/syncoid @@ -614,7 +614,7 @@ sub checkcommands { # check for ZFS resume feature support if ($resume) { - my $resumechkcmd = "$zfscmd get receive_resume_token -d 0"; + my $resumechkcmd = "$zfscmd get -d 0 receive_resume_token"; if ($debug) { print "DEBUG: checking availability of zfs resume feature on source...\n"; } $avail{'sourceresume'} = system("$sourcessh $resumechkcmd >/dev/null 2>&1"); From f5508a240387bb80f3db6937c8724fa006e4a5b2 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Thu, 26 Jul 2018 21:46:50 +0200 Subject: [PATCH 11/24] fix typo to make local source bwlimit work --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index f53aee4..cf941bf 100755 --- a/syncoid +++ b/syncoid @@ -757,7 +757,7 @@ sub buildsynccmd { $synccmd = "$sendcmd |"; # avoid confusion - accept either source-bwlimit or target-bwlimit as the bandwidth limiting option here my $bwlimit = ''; - if (length $args{'bwlimit'}) { + if (length $args{'source-bwlimit'}) { $bwlimit = $args{'source-bwlimit'}; } elsif (length $args{'target-bwlimit'}) { $bwlimit = $args{'target-bwlimit'}; From e85c375bbea636aa068bb3cd4add7d621568784c Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Thu, 26 Jul 2018 21:53:56 +0200 Subject: [PATCH 12/24] fixed typo --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index f53aee4..8be70e5 100755 --- a/syncoid +++ b/syncoid @@ -863,7 +863,7 @@ sub pruneoldsyncsnaps { $prunecmd = escapeshellparam($prunecmd); } system("$rhost $prunecmd") == 0 - or warn "CRITICAL ERROR: $rhost $prunecmd failed: $?"; + or warn "WARNING: $rhost $prunecmd failed: $?"; $prunecmd = ''; $counter = 0; } From 7c68ef5e8f2f491fe7de3b23a53f8b64a026a883 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Sun, 29 Jul 2018 13:16:53 +0200 Subject: [PATCH 13/24] return a non zero exit code if there was a problem replicating datasets --- syncoid | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/syncoid b/syncoid index 6c569b2..6453d0f 100755 --- a/syncoid +++ b/syncoid @@ -97,6 +97,7 @@ my $targetsudocmd = $targetisroot ? '' : $sudocmd; my %avail = checkcommands(); my %snaps; +my $exitcode = 0; ## break here to call replication individually so that we ## ## can loop across children separately, for recursive ## @@ -127,7 +128,7 @@ if ($targethost ne '') { close FH; } -exit 0; +exit $exitcode; ############################################################################## ############################################################################## @@ -186,6 +187,7 @@ sub syncdataset { # make sure target is not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } @@ -236,6 +238,7 @@ sub syncdataset { $newsyncsnap = getnewestsnapshot($sourcehost,$sourcefs,$sourceisroot); if ($newsyncsnap eq 0) { warn "CRITICAL: no snapshots exist on source $sourcefs, and you asked for --no-sync-snap.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } } @@ -292,6 +295,7 @@ sub syncdataset { # make sure target is (still) not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } system($synccmd) == 0 @@ -318,6 +322,7 @@ sub syncdataset { # make sure target is (still) not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } @@ -325,9 +330,12 @@ sub syncdataset { if ($debug) { print "DEBUG: $synccmd\n"; } if ($oldestsnap ne $newsyncsnap) { - system($synccmd) == 0 - or warn "CRITICAL ERROR: $synccmd failed: $?"; + my $ret = system($synccmd); + if ($ret != 0) { + warn "CRITICAL ERROR: $synccmd failed: $?"; + if ($exitcode < 1) { $exitcode = 1; } return 0; + } } else { if (!$quiet) { print "INFO: no incremental sync needed; $oldestsnap is already the newest available snapshot.\n"; } } @@ -380,6 +388,7 @@ sub syncdataset { # make sure target is (still) not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; + if ($exitcode < 1) { $exitcode = 1; } return 0; } @@ -910,6 +919,7 @@ sub getmatchingsnapshot { } # if we got this far, we failed to find a matching snapshot. + if ($exitcode < 2) { $exitcode = 2; } print "\n"; print "CRITICAL ERROR: Target $targetfs exists but has no snapshots matching with $sourcefs!\n"; From 63eec4994c20d7eb6207f6c6927badf138442f22 Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Mon, 30 Jul 2018 22:21:14 +0200 Subject: [PATCH 14/24] don't die on some critical sync errors, but continue to replicate all the other datasets. after all is done exit with an error code --- syncoid | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/syncoid b/syncoid index 6453d0f..1ef1506 100755 --- a/syncoid +++ b/syncoid @@ -298,8 +298,11 @@ sub syncdataset { if ($exitcode < 1) { $exitcode = 1; } return 0; } - system($synccmd) == 0 - or die "CRITICAL ERROR: $synccmd failed: $?"; + system($synccmd) == 0 or do { + warn "CRITICAL ERROR: $synccmd failed: $?"; + if ($exitcode < 2) { $exitcode = 2; } + return 0; + }; # now do an -I to the new sync snapshot, assuming there were any snapshots # other than the new sync snapshot to begin with, of course - and that we @@ -359,8 +362,11 @@ sub syncdataset { if (!$quiet) { print "Resuming interrupted zfs send/receive from $sourcefs to $targetfs (~ $disp_pvsize remaining):\n"; } if ($debug) { print "DEBUG: $synccmd\n"; } - system("$synccmd") == 0 - or die "CRITICAL ERROR: $synccmd failed: $?"; + system("$synccmd") == 0 or do { + warn "CRITICAL ERROR: $synccmd failed: $?"; + if ($exitcode < 2) { $exitcode = 2; } + return 0; + }; # a resumed transfer will only be done to the next snapshot, # so do an normal sync cycle @@ -416,8 +422,11 @@ sub syncdataset { if (!$quiet) { print "Sending incremental $sourcefs\@$matchingsnap ... $newsyncsnap (~ $disp_pvsize):\n"; } if ($debug) { print "DEBUG: $synccmd\n"; } - system("$synccmd") == 0 - or die "CRITICAL ERROR: $synccmd failed: $?"; + system("$synccmd") == 0 or do { + warn "CRITICAL ERROR: $synccmd failed: $?"; + if ($exitcode < 2) { $exitcode = 2; } + return 0; + }; # restore original readonly value to target after sync complete # dyking this functionality out for the time being due to buggy mount/unmount behavior From 9668567a870def5418032fb922d3a27a643059fa Mon Sep 17 00:00:00 2001 From: Christoph Klaffl Date: Mon, 30 Jul 2018 22:53:48 +0200 Subject: [PATCH 15/24] continue replication on more critical errors --- syncoid | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/syncoid b/syncoid index 1ef1506..73205ce 100755 --- a/syncoid +++ b/syncoid @@ -233,6 +233,10 @@ sub syncdataset { if (!defined $args{'no-sync-snap'}) { # create a new syncoid snapshot on the source filesystem. $newsyncsnap = newsyncsnap($sourcehost,$sourcefs,$sourceisroot); + if (!$newsyncsnap) { + # we already whined about the error + return 0; + } } else { # we don't want sync snapshots created, so use the newest snapshot we can find. $newsyncsnap = getnewestsnapshot($sourcehost,$sourcefs,$sourceisroot); @@ -267,6 +271,11 @@ sub syncdataset { } my $oldestsnap = getoldestsnapshot(\%snaps); if (! $oldestsnap) { + if (defined ($args{'no-sync-snap'}) ) { + # we already whined about the missing snapshots + return 0; + } + # getoldestsnapshot() returned false, so use new sync snapshot if ($debug) { print "DEBUG: getoldestsnapshot() returned false, so using $newsyncsnap.\n"; } $oldestsnap = $newsyncsnap; @@ -752,7 +761,7 @@ sub getoldestsnapshot { # must not have had any snapshots on source - luckily, we already made one, amirite? if (defined ($args{'no-sync-snap'}) ) { # well, actually we set --no-sync-snap, so no we *didn't* already make one. Whoops. - die "CRIT: --no-sync-snap is set, and getoldestsnapshot() could not find any snapshots on source!\n"; + warn "CRIT: --no-sync-snap is set, and getoldestsnapshot() could not find any snapshots on source!\n"; } return 0; } @@ -774,6 +783,7 @@ sub getnewestsnapshot { # we also probably need an argument to mute this WARN, for people who deliberately exclude # datasets from recursive replication this way. warn "WARN: --no-sync-snap is set, and getnewestsnapshot() could not find any snapshots on source for current dataset. Continuing.\n"; + if ($exitcode < 2) { $exitcode = 2; } } return 0; } @@ -961,8 +971,12 @@ sub newsyncsnap { my %date = getdate(); my $snapname = "syncoid\_$identifier$hostid\_$date{'stamp'}"; my $snapcmd = "$rhost $mysudocmd $zfscmd snapshot $fsescaped\@$snapname\n"; - system($snapcmd) == 0 - or die "CRITICAL ERROR: $snapcmd failed: $?"; + system($snapcmd) == 0 or do { + warn "CRITICAL ERROR: $snapcmd failed: $?"; + if ($exitcode < 2) { $exitcode = 2; } + return 0; + }; + return $snapname; } From fb8edad885522040369c6ab95edfbb922fb6901d Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 3 Aug 2018 09:13:35 +0100 Subject: [PATCH 16/24] compression warnings are no longer hidden by --quiet --- syncoid | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syncoid b/syncoid index aec99cd..85d5e16 100755 --- a/syncoid +++ b/syncoid @@ -452,13 +452,13 @@ sub checkcommands { if ($avail{'sourcecompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on source $s- sync will continue without compression.\n"; } + print "WARN: $compressargs{'rawcmd'} not available on source $s- sync will continue without compression.\n"; } $avail{'compress'} = 0; } if ($avail{'targetcompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on target $t - sync will continue without compression.\n"; } + print "WARN: $compressargs{'rawcmd'} not available on target $t - sync will continue without compression.\n"; } $avail{'compress'} = 0; } @@ -471,7 +471,7 @@ sub checkcommands { # corner case - if source AND target are BOTH remote, we have to check for local compress too if ($sourcehost ne '' && $targethost ne '' && $avail{'localcompress'} eq '') { if ($compressargs{'rawcmd'} ne '') { - if (!$quiet) { print "WARN: $compressargs{'rawcmd'} not available on local machine - sync will continue without compression.\n"; } + print "WARN: $compressargs{'rawcmd'} not available on local machine - sync will continue without compression.\n"; } $avail{'compress'} = 0; } From 39d1fd38c1edd52f45b44707beabe32e3a20900d Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 5 Jan 2018 21:54:06 +0000 Subject: [PATCH 17/24] added ability to skip datasets... simply set syncoid:no-sync=true --- syncoid | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/syncoid b/syncoid index 6c569b2..8adaa87 100755 --- a/syncoid +++ b/syncoid @@ -183,6 +183,11 @@ sub syncdataset { if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; } + if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:no-sync') eq 'true') { + print "Skipping dataset (syncoid:nosync=true): $sourcefs...\n"; + return 0; + } + # make sure target is not currently in receive. if (iszfsbusy($targethost,$targetfs,$targetisroot)) { warn "Cannot sync now: $targetfs is already target of a zfs receive process.\n"; From 22c137627a9a923807565dff01794d9d3775e767 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sat, 6 Jan 2018 10:15:30 +0000 Subject: [PATCH 18/24] updated doc for syncoid:no-sync --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index e359886..ba325e6 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,14 @@ If ZFS supports resumeable send/receive streams on both the source and target th As of 1.4.18, syncoid also automatically supports and enables resume of interrupted replication when both source and target support this feature. +##### Syncoid Dataset Properties + ++ syncoid:no-sync + + Setting this to `true` will prevent the dataset from being handled by syncoid in _any_ way - it will be skipped. This can be useful for preventing certain datasets from being transferred when recursively handling a tree. + + Note that this will also prevent syncoid from handling the dataset if given explicitly on the command line. + ##### Syncoid Command Line Options + [source] From b02b9a582bd3f92c01f16c954add64a78e647896 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Sat, 6 Jan 2018 10:43:30 +0000 Subject: [PATCH 19/24] now obeying --quiet, and added 'INFO: ' prefix to skip message --- syncoid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syncoid b/syncoid index 8adaa87..2201f41 100755 --- a/syncoid +++ b/syncoid @@ -184,7 +184,7 @@ sub syncdataset { if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; } if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:no-sync') eq 'true') { - print "Skipping dataset (syncoid:nosync=true): $sourcefs...\n"; + if (!$quiet) { print "INFO: Skipping dataset (syncoid:nosync=true): $sourcefs...\n"; } return 0; } From c188e47a6da2d8ee2857a8eb1c0400bc78fc7a53 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 9 Mar 2018 15:15:06 +0000 Subject: [PATCH 20/24] inverted sync/no-sync logic --- syncoid | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syncoid b/syncoid index 2201f41..20c0853 100755 --- a/syncoid +++ b/syncoid @@ -183,8 +183,8 @@ sub syncdataset { if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; } - if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:no-sync') eq 'true') { - if (!$quiet) { print "INFO: Skipping dataset (syncoid:nosync=true): $sourcefs...\n"; } + if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync') eq 'false') { + if (!$quiet) { print "INFO: Skipping dataset (syncoid:sync=false): $sourcefs...\n"; } return 0; } From f0a37310a54b5e421264d05a02ad226a1409bb98 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 9 Mar 2018 15:18:03 +0000 Subject: [PATCH 21/24] implemented sync true/false/${HOSTS} filtering --- syncoid | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/syncoid b/syncoid index 20c0853..7877723 100755 --- a/syncoid +++ b/syncoid @@ -183,9 +183,20 @@ sub syncdataset { if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; } - if (getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync') eq 'false') { + my $sync = getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync'); + + if ($sync eq 'true' || $sync eq '-') { + # definitely sync this dataset - if a host is called 'true' or '-', then you're special + } elsif ($sync eq 'false') { if (!$quiet) { print "INFO: Skipping dataset (syncoid:sync=false): $sourcefs...\n"; } return 0; + } else { + my $hostid = hostname(); + my @hosts = split(/,/,$sync); + if (!(grep $hostid eq $_, @hosts)) { + if (!$quiet) { print "INFO: Skipping dataset (syncoid:sync doesn't include $hostid): $sourcefs...\n"; } + return 0; + } } # make sure target is not currently in receive. From a4e490f430b73454422c28254012a3328f9bbe98 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Fri, 9 Mar 2018 15:28:39 +0000 Subject: [PATCH 22/24] updated documentation for the syncoid:sync property's new behaviour --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ba325e6..bb6ec89 100644 --- a/README.md +++ b/README.md @@ -120,11 +120,27 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup ##### Syncoid Dataset Properties -+ syncoid:no-sync ++ syncoid:sync - Setting this to `true` will prevent the dataset from being handled by syncoid in _any_ way - it will be skipped. This can be useful for preventing certain datasets from being transferred when recursively handling a tree. + Available values: - Note that this will also prevent syncoid from handling the dataset if given explicitly on the command line. + + `true` (default if unset) + + This dataset will be synchronised to all hosts. + + + `false` + + This dataset will not be synchronised to any hosts - it will be skipped. This can be useful for preventing certain datasets from being transferred when recursively handling a tree. + + + `host1,host2,...` + + A comma seperated list of hosts. This dataset will only be synchronised by hosts listed in the property. + + _Note_: this check is performed by the host running `syncoid`, thus the local hostname must be present for inclusion during a push operation // the remote hostname must be present for a pull. + + _Note_: this will also prevent syncoid from handling the dataset if given explicitly on the command line. + + _Note_: syncing a child of a no-sync dataset will currently result in a critical error ##### Syncoid Command Line Options From 60b2dedc456a8464f86cf75dacfe62c0b6410dbc Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Mon, 6 Aug 2018 14:07:58 +0100 Subject: [PATCH 23/24] fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb6ec89..8c05fb3 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup + `host1,host2,...` - A comma seperated list of hosts. This dataset will only be synchronised by hosts listed in the property. + A comma separated list of hosts. This dataset will only be synchronised by hosts listed in the property. _Note_: this check is performed by the host running `syncoid`, thus the local hostname must be present for inclusion during a push operation // the remote hostname must be present for a pull. From d0ba0bc284bb92a8f0e63cfbd773e8c61e175973 Mon Sep 17 00:00:00 2001 From: Attie Grande Date: Mon, 6 Aug 2018 14:16:25 +0100 Subject: [PATCH 24/24] handled empty syncoid:sync property - behaves like unset --- README.md | 4 +++- syncoid | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8c05fb3..b833dec 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,9 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup _Note_: this will also prevent syncoid from handling the dataset if given explicitly on the command line. - _Note_: syncing a child of a no-sync dataset will currently result in a critical error + _Note_: syncing a child of a no-sync dataset will currently result in a critical error. + + _Note_: empty properties will be handled as if they were unset. ##### Syncoid Command Line Options diff --git a/syncoid b/syncoid index 7877723..fa00751 100755 --- a/syncoid +++ b/syncoid @@ -185,7 +185,8 @@ sub syncdataset { my $sync = getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync'); - if ($sync eq 'true' || $sync eq '-') { + if ($sync eq 'true' || $sync eq '-' || $sync eq '') { + # empty is handled the same as unset (aka: '-') # definitely sync this dataset - if a host is called 'true' or '-', then you're special } elsif ($sync eq 'false') { if (!$quiet) { print "INFO: Skipping dataset (syncoid:sync=false): $sourcefs...\n"; }