Update to current sanoid master and solved a (fake ?) confict in syncoid
This commit is contained in:
commit
2f518d3209
|
@ -1,3 +1,6 @@
|
|||
1.4.18 implemented special character handling and support of ZFS resume/receive tokens by default in syncoid,
|
||||
thank you @phreaker0!
|
||||
|
||||
1.4.17 changed die to warn when unexpectedly unable to remove a snapshot - this
|
||||
allows sanoid to continue taking/removing other snapshots not affected by
|
||||
whatever lock prevented the first from being taken or removed
|
||||
|
|
|
@ -11,3 +11,14 @@ If you don't want to have to change the shebangs, your other option is to drop a
|
|||
root@bsd:~# ln -s /usr/local/bin/perl /usr/bin/perl
|
||||
|
||||
After putting this symlink in place, ANY perl script shebanged for Linux will work on your system too.
|
||||
|
||||
Syncoid assumes a bourne style shell on remote hosts. Using (t)csh (the default for root under FreeBSD)
|
||||
has some known issues:
|
||||
|
||||
* If mbuffer is present, syncoid will fail with an "Ambiguous output redirect." error. So if you:
|
||||
root@bsd:~# ln -s /usr/local/bin/mbuffer /usr/bin/mbuffer
|
||||
make sure the remote user is using an sh compatible shell.
|
||||
|
||||
To change to a compatible shell, use the chsh command:
|
||||
|
||||
root@bsd:~# chsh -s /bin/sh
|
||||
|
|
4
INSTALL
4
INSTALL
|
@ -9,7 +9,7 @@ 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 FreeBSD: pkg install pv lzop
|
||||
On FreeBSD: pkg install pv mbuffer lzop
|
||||
|
||||
FreeBSD notes: FreeBSD may place pv and lzop in somewhere other than
|
||||
/usr/bin ; syncoid currently does not check path.
|
||||
|
@ -19,6 +19,8 @@ FreeBSD notes: FreeBSD may place pv and lzop in somewhere other than
|
|||
or similar, as appropriate, to create links in /usr/bin
|
||||
to wherever the utilities actually are on your system.
|
||||
|
||||
See note about mbuffer in FREEBSD.readme
|
||||
|
||||
|
||||
SANOID
|
||||
------
|
||||
|
|
|
@ -108,6 +108,9 @@ syncoid root@remotehost:data/images/vm backup/images/vm
|
|||
Which would pull-replicate the filesystem from the remote host to the local system over an SSH tunnel.
|
||||
|
||||
Syncoid supports recursive replication (replication of a dataset and all its child datasets) and uses mbuffer buffering, lzop compression, and pv progress bars if the utilities are available on the systems used.
|
||||
If ZFS supports resumeable send/receive streams on both the source and target those will be enabled as default.
|
||||
|
||||
As of 1.4.18, syncoid also automatically supports and enables resume of interrupted replication when both source and target support this feature.
|
||||
|
||||
##### Syncoid Command Line Options
|
||||
|
||||
|
@ -147,6 +150,10 @@ Syncoid supports recursive replication (replication of a dataset and all its chi
|
|||
|
||||
This argument tells syncoid to restrict itself to existing snapshots, instead of creating a semi-ephemeral syncoid snapshot at execution time. Especially useful in multi-target (A->B, A->C) replication schemes, where you might otherwise accumulate a large number of foreign syncoid snapshots.
|
||||
|
||||
+ --no-resume
|
||||
|
||||
This argument tells syncoid to not use resumeable zfs send/receive streams.
|
||||
|
||||
+ --dumpsnaps
|
||||
|
||||
This prints a list of snapshots during the run.
|
||||
|
|
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
%global version 1.4.14
|
||||
%global version 1.4.18
|
||||
%global git_tag v%{version}
|
||||
|
||||
# Enable with systemctl "enable sanoid.timer"
|
||||
|
@ -6,7 +6,7 @@
|
|||
|
||||
Name: sanoid
|
||||
Version: %{version}
|
||||
Release: 2%{?dist}
|
||||
Release: 1%{?dist}
|
||||
BuildArch: noarch
|
||||
Summary: A policy-driven snapshot management tool for ZFS file systems
|
||||
Group: Applications/System
|
||||
|
@ -110,6 +110,8 @@ echo "* * * * * root %{_sbindir}/sanoid --cron" > %{buildroot}%{_docdir}/%{name}
|
|||
%endif
|
||||
|
||||
%changelog
|
||||
* Sat Apr 28 2018 Dominic Robinson <github@dcrdev.com> - 1.4.18-1
|
||||
- Bump to 1.4.18
|
||||
* Thu Aug 31 2017 Dominic Robinson <github@dcrdev.com> - 1.4.14-2
|
||||
- Add systemd timers
|
||||
* Wed Aug 30 2017 Dominic Robinson <github@dcrdev.com> - 1.4.14-1
|
||||
|
@ -121,6 +123,5 @@ echo "* * * * * root %{_sbindir}/sanoid --cron" > %{buildroot}%{_docdir}/%{name}
|
|||
- Version bump
|
||||
- Clean up variables and macros
|
||||
- Compatible with both Fedora and Red Hat
|
||||
|
||||
* Sat Feb 13 2016 Thomas M. Lapp <tmlapp@gmail.com> - 1.4.4-1
|
||||
- Initial RPM Package
|
|
@ -0,0 +1 @@
|
|||
cf0ec23c310d2f9416ebabe48f5edb73 sanoid-1.4.18.tar.gz
|
2
sanoid
2
sanoid
|
@ -4,7 +4,7 @@
|
|||
# from http://www.gnu.org/licenses/gpl-3.0.html on 2014-11-17. A copy should also be available in this
|
||||
# project's Git repository at https://github.com/jimsalterjrs/sanoid/blob/master/LICENSE.
|
||||
|
||||
$::VERSION = '1.4.17';
|
||||
$::VERSION = '1.4.18';
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
|
305
syncoid
305
syncoid
|
@ -4,7 +4,7 @@
|
|||
# from http://www.gnu.org/licenses/gpl-3.0.html on 2014-11-17. A copy should also be available in this
|
||||
# project's Git repository at https://github.com/jimsalterjrs/sanoid/blob/master/LICENSE.
|
||||
|
||||
$::VERSION = '1.4.16';
|
||||
$::VERSION = '1.4.18';
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
@ -46,6 +46,7 @@ my $rawsourcefs = $args{'source'};
|
|||
my $rawtargetfs = $args{'target'};
|
||||
my $debug = $args{'debug'};
|
||||
my $quiet = $args{'quiet'};
|
||||
my $resume = !$args{'no-resume'};
|
||||
|
||||
my $zfscmd = '/sbin/zfs';
|
||||
my $sshcmd = '/usr/bin/ssh';
|
||||
|
@ -96,7 +97,7 @@ if (!defined $args{'recursive'}) {
|
|||
if ($debug) { print "DEBUG: recursive sync of $sourcefs.\n"; }
|
||||
my @datasets = getchilddatasets($sourcehost, $sourcefs, $sourceisroot);
|
||||
foreach my $dataset(@datasets) {
|
||||
$dataset =~ s/$sourcefs//;
|
||||
$dataset =~ s/\Q$sourcefs\E//;
|
||||
chomp $dataset;
|
||||
my $childsourcefs = $sourcefs . $dataset;
|
||||
my $childtargetfs = $targetfs . $dataset;
|
||||
|
@ -125,11 +126,16 @@ exit 0;
|
|||
sub getchilddatasets {
|
||||
my ($rhost,$fs,$isroot,%snaps) = @_;
|
||||
my $mysudocmd;
|
||||
my $fsescaped = escapeshellparam($fs);
|
||||
|
||||
if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; }
|
||||
if ($rhost ne '') { $rhost = "$sshcmd $rhost"; }
|
||||
if ($rhost ne '') {
|
||||
$rhost = "$sshcmd $rhost";
|
||||
# double escaping needed
|
||||
$fsescaped = escapeshellparam($fsescaped);
|
||||
}
|
||||
|
||||
my $getchildrencmd = "$rhost $mysudocmd $zfscmd list -o name -t filesystem,volume -Hr $fs |";
|
||||
my $getchildrencmd = "$rhost $mysudocmd $zfscmd list -o name -t filesystem,volume -Hr $fsescaped |";
|
||||
if ($debug) { print "DEBUG: getting list of child datasets on $fs using $getchildrencmd...\n"; }
|
||||
open FH, $getchildrencmd;
|
||||
my @children = <FH>;
|
||||
|
@ -142,6 +148,9 @@ sub syncdataset {
|
|||
|
||||
my ($sourcehost, $sourcefs, $targethost, $targetfs) = @_;
|
||||
|
||||
my $sourcefsescaped = escapeshellparam($sourcefs);
|
||||
my $targetfsescaped = escapeshellparam($targetfs);
|
||||
|
||||
if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; }
|
||||
|
||||
# make sure target is not currently in receive.
|
||||
|
@ -153,6 +162,26 @@ sub syncdataset {
|
|||
# does the target filesystem exist yet?
|
||||
my $targetexists = targetexists($targethost,$targetfs,$targetisroot);
|
||||
|
||||
my $receiveextraargs = "";
|
||||
my $receivetoken;
|
||||
if ($resume) {
|
||||
# save state of interrupted receive stream
|
||||
$receiveextraargs = "-s";
|
||||
|
||||
if ($targetexists) {
|
||||
# check remote dataset for receive resume token (interrupted receive)
|
||||
$receivetoken = getreceivetoken($targethost,$targetfs,$targetisroot);
|
||||
|
||||
if ($debug && defined($receivetoken)) {
|
||||
print "DEBUG: got receive resume token: $receivetoken: \n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $newsyncsnap;
|
||||
|
||||
# skip snapshot checking/creation in case of resumed receive
|
||||
if (!defined($receivetoken)) {
|
||||
# build hashes of the snaps on the source and target filesystems.
|
||||
|
||||
%snaps = getsnaps('source',$sourcehost,$sourcefs,$sourceisroot);
|
||||
|
@ -169,9 +198,8 @@ sub syncdataset {
|
|||
print "\n\n\n";
|
||||
}
|
||||
|
||||
# create a new syncoid snapshot on the source filesystem.
|
||||
my $newsyncsnap;
|
||||
if (!defined $args{'no-sync-snap'}) {
|
||||
# create a new syncoid snapshot on the source filesystem.
|
||||
$newsyncsnap = newsyncsnap($sourcehost,$sourcefs,$sourceisroot);
|
||||
} else {
|
||||
# we don't want sync snapshots created, so use the newest snapshot we can find.
|
||||
|
@ -181,6 +209,8 @@ sub syncdataset {
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
my $newsyncsnapescaped = escapeshellparam($newsyncsnap);
|
||||
|
||||
# there is currently (2014-09-01) a bug in ZFS on Linux
|
||||
# that causes readonly to always show on if it's EVER
|
||||
|
@ -211,9 +241,10 @@ sub syncdataset {
|
|||
|
||||
# if --no-stream is specified, our full needs to be the newest snapshot, not the oldest.
|
||||
if (defined $args{'no-stream'}) { $oldestsnap = getnewestsnapshot(\%snaps); }
|
||||
my $oldestsnapescaped = escapeshellparam($oldestsnap);
|
||||
|
||||
my $sendcmd = "$sourcesudocmd $zfscmd send $sourcefs\@$oldestsnap";
|
||||
my $recvcmd = "$targetsudocmd $zfscmd receive -F $targetfs";
|
||||
my $sendcmd = "$sourcesudocmd $zfscmd send $sourcefsescaped\@$oldestsnapescaped";
|
||||
my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped";
|
||||
|
||||
my $pvsize = getsendsize($sourcehost,"$sourcefs\@$oldestsnap",0,$sourceisroot);
|
||||
my $disp_pvsize = readablebytes($pvsize);
|
||||
|
@ -248,7 +279,7 @@ sub syncdataset {
|
|||
# $originaltargetreadonly = getzfsvalue($targethost,$targetfs,$targetisroot,'readonly');
|
||||
# setzfsvalue($targethost,$targetfs,$targetisroot,'readonly','on');
|
||||
|
||||
$sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefs\@$oldestsnap $sourcefs\@$newsyncsnap";
|
||||
$sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$oldestsnapescaped $sourcefsescaped\@$newsyncsnapescaped";
|
||||
$pvsize = getsendsize($sourcehost,"$sourcefs\@$oldestsnap","$sourcefs\@$newsyncsnap",$sourceisroot);
|
||||
$disp_pvsize = readablebytes($pvsize);
|
||||
if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; }
|
||||
|
@ -277,6 +308,27 @@ sub syncdataset {
|
|||
# setzfsvalue($targethost,$targetfs,$targetisroot,'readonly',$originaltargetreadonly);
|
||||
}
|
||||
} else {
|
||||
# resume interrupted receive if there is a valid resume $token
|
||||
# and because this will ony resume the receive to the next
|
||||
# snapshot, do a normal sync after that
|
||||
if (defined($receivetoken)) {
|
||||
my $sendcmd = "$sourcesudocmd $zfscmd send -t $receivetoken";
|
||||
my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped";
|
||||
my $pvsize = getsendsize($sourcehost,"","",$sourceisroot,$receivetoken);
|
||||
my $disp_pvsize = readablebytes($pvsize);
|
||||
if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; }
|
||||
my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot);
|
||||
|
||||
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: $?";
|
||||
|
||||
# a resumed transfer will only be done to the next snapshot,
|
||||
# so do an normal sync cycle
|
||||
return syncdataset($sourcehost, $sourcefs, $targethost, $targetfs);
|
||||
}
|
||||
|
||||
# find most recent matching snapshot and do an -I
|
||||
# to the new snapshot
|
||||
|
||||
|
@ -305,6 +357,7 @@ sub syncdataset {
|
|||
# barf some text but don't touch the filesystem
|
||||
if (!$quiet) { print "INFO: no snapshots on source newer than $newsyncsnap on target. Nothing to do, not syncing.\n"; }
|
||||
} else {
|
||||
my $matchingsnapescaped = escapeshellparam($matchingsnap);
|
||||
# rollback target to matchingsnap
|
||||
my $rollbacktype="-R";
|
||||
if (defined $args{'no-clone-rollback'}) {
|
||||
|
@ -319,8 +372,8 @@ sub syncdataset {
|
|||
system ("$targetsudocmd $zfscmd rollback $rollbacktype $targetfs\@$matchingsnap");
|
||||
}
|
||||
|
||||
my $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefs\@$matchingsnap $sourcefs\@$newsyncsnap";
|
||||
my $recvcmd = "$targetsudocmd $zfscmd receive -F $targetfs";
|
||||
my $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped";
|
||||
my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped";
|
||||
my $pvsize = getsendsize($sourcehost,"$sourcefs\@$matchingsnap","$sourcefs\@$newsyncsnap",$sourceisroot);
|
||||
my $disp_pvsize = readablebytes($pvsize);
|
||||
if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; }
|
||||
|
@ -410,6 +463,8 @@ sub checkcommands {
|
|||
$avail{'localmbuffer'} = 1;
|
||||
$avail{'sourcembuffer'} = 1;
|
||||
$avail{'targetmbuffer'} = 1;
|
||||
$avail{'sourceresume'} = 1;
|
||||
$avail{'targetresume'} = 1;
|
||||
return %avail;
|
||||
}
|
||||
|
||||
|
@ -517,6 +572,37 @@ sub checkcommands {
|
|||
$avail{'localpv'} = 1;
|
||||
}
|
||||
|
||||
# check for ZFS resume feature support
|
||||
if ($resume) {
|
||||
my $resumechkcmd = "$zfscmd get receive_resume_token -d 0";
|
||||
|
||||
if ($debug) { print "DEBUG: checking availability of zfs resume feature on source...\n"; }
|
||||
$avail{'sourceresume'} = system("$sourcessh $resumechkcmd >/dev/null 2>&1");
|
||||
$avail{'sourceresume'} = $avail{'sourceresume'} == 0 ? 1 : 0;
|
||||
|
||||
if ($debug) { print "DEBUG: checking availability of zfs resume feature on target...\n"; }
|
||||
$avail{'targetresume'} = system("$targetssh $resumechkcmd >/dev/null 2>&1");
|
||||
$avail{'targetresume'} = $avail{'targetresume'} == 0 ? 1 : 0;
|
||||
|
||||
if ($avail{'sourceresume'} == 0 || $avail{'targetresume'} == 0) {
|
||||
# disable resume
|
||||
$resume = '';
|
||||
|
||||
my @hosts = ();
|
||||
if ($avail{'sourceresume'} == 0) {
|
||||
push @hosts, 'source';
|
||||
}
|
||||
if ($avail{'targetresume'} == 0) {
|
||||
push @hosts, 'target';
|
||||
}
|
||||
my $affected = join(" and ", @hosts);
|
||||
print "WARN: ZFS resume feature not available on $affected machine - sync will continue without resume support.\n";
|
||||
}
|
||||
} else {
|
||||
$avail{'sourceresume'} = 0;
|
||||
$avail{'targetresume'} = 0;
|
||||
}
|
||||
|
||||
return %avail;
|
||||
}
|
||||
|
||||
|
@ -531,7 +617,7 @@ sub iszfsbusy {
|
|||
|
||||
foreach my $process (@processes) {
|
||||
# if ($debug) { print "DEBUG: checking process $process...\n"; }
|
||||
if ($process =~ /zfs *(receive|recv).*$fs/) {
|
||||
if ($process =~ /zfs *(receive|recv).*\Q$fs\E/) {
|
||||
# there's already a zfs receive process for our target filesystem - return true
|
||||
if ($debug) { print "DEBUG: process $process matches target $fs!\n"; }
|
||||
return 1;
|
||||
|
@ -544,24 +630,40 @@ sub iszfsbusy {
|
|||
|
||||
sub setzfsvalue {
|
||||
my ($rhost,$fs,$isroot,$property,$value) = @_;
|
||||
if ($rhost ne '') { $rhost = "$sshcmd $rhost"; }
|
||||
|
||||
my $fsescaped = escapeshellparam($fs);
|
||||
|
||||
if ($rhost ne '') {
|
||||
$rhost = "$sshcmd $rhost";
|
||||
# double escaping needed
|
||||
$fsescaped = escapeshellparam($fsescaped);
|
||||
}
|
||||
|
||||
if ($debug) { print "DEBUG: setting $property to $value on $fs...\n"; }
|
||||
my $mysudocmd;
|
||||
if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; }
|
||||
if ($debug) { print "$rhost $mysudocmd $zfscmd set $property=$value $fs\n"; }
|
||||
system("$rhost $mysudocmd $zfscmd set $property=$value $fs") == 0
|
||||
or warn "WARNING: $rhost $mysudocmd $zfscmd set $property=$value $fs died: $?, proceeding anyway.\n";
|
||||
if ($debug) { print "$rhost $mysudocmd $zfscmd set $property=$value $fsescaped\n"; }
|
||||
system("$rhost $mysudocmd $zfscmd set $property=$value $fsescaped") == 0
|
||||
or warn "WARNING: $rhost $mysudocmd $zfscmd set $property=$value $fsescaped died: $?, proceeding anyway.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
sub getzfsvalue {
|
||||
my ($rhost,$fs,$isroot,$property) = @_;
|
||||
if ($rhost ne '') { $rhost = "$sshcmd $rhost"; }
|
||||
|
||||
my $fsescaped = escapeshellparam($fs);
|
||||
|
||||
if ($rhost ne '') {
|
||||
$rhost = "$sshcmd $rhost";
|
||||
# double escaping needed
|
||||
$fsescaped = escapeshellparam($fsescaped);
|
||||
}
|
||||
|
||||
if ($debug) { print "DEBUG: getting current value of $property on $fs...\n"; }
|
||||
my $mysudocmd;
|
||||
if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; }
|
||||
if ($debug) { print "$rhost $mysudocmd $zfscmd get -H $property $fs\n"; }
|
||||
open FH, "$rhost $mysudocmd $zfscmd get -H $property $fs |";
|
||||
if ($debug) { print "$rhost $mysudocmd $zfscmd get -H $property $fsescaped\n"; }
|
||||
open FH, "$rhost $mysudocmd $zfscmd get -H $property $fsescaped |";
|
||||
my $value = <FH>;
|
||||
close FH;
|
||||
my @values = split(/\s/,$value);
|
||||
|
@ -647,17 +749,24 @@ sub buildsynccmd {
|
|||
if ($avail{'localpv'} && !$quiet) { $synccmd .= " $pvcmd -s $pvsize |"; }
|
||||
if ($avail{'compress'}) { $synccmd .= " $compressargs{'cmd'} |"; }
|
||||
if ($avail{'sourcembuffer'}) { $synccmd .= " $mbuffercmd $args{'source-bwlimit'} $mbufferoptions |"; }
|
||||
$synccmd .= " $sshcmd $targethost '";
|
||||
if ($avail{'targetmbuffer'}) { $synccmd .= " $mbuffercmd $args{'target-bwlimit'} $mbufferoptions |"; }
|
||||
if ($avail{'compress'}) { $synccmd .= " $compressargs{'decomcmd'} |"; }
|
||||
$synccmd .= " $recvcmd'";
|
||||
$synccmd .= " $sshcmd $targethost ";
|
||||
|
||||
my $remotecmd = "";
|
||||
if ($avail{'targetmbuffer'}) { $remotecmd .= " $mbuffercmd $args{'target-bwlimit'} $mbufferoptions |"; }
|
||||
if ($avail{'compress'}) { $remotecmd .= " $compressargs{'decomcmd'} |"; }
|
||||
$remotecmd .= " $recvcmd";
|
||||
|
||||
$synccmd .= escapeshellparam($remotecmd);
|
||||
} elsif ($targethost eq '') {
|
||||
# remote source, local target.
|
||||
#$synccmd = "$sshcmd $sourcehost '$sendcmd | $compressargs{'cmd'} | $mbuffercmd' | $args{'decompress'}{'cmd'} | $mbuffercmd | $pvcmd | $recvcmd";
|
||||
$synccmd = "$sshcmd $sourcehost '$sendcmd";
|
||||
if ($avail{'compress'}) { $synccmd .= " | $compressargs{'cmd'}"; }
|
||||
if ($avail{'sourcembuffer'}) { $synccmd .= " | $mbuffercmd $args{'source-bwlimit'} $mbufferoptions"; }
|
||||
$synccmd .= "' | ";
|
||||
#$synccmd = "$sshcmd $sourcehost '$sendcmd | $compressargs{'cmd'} | $mbuffercmd' | $compressargs{'decomcmd'} | $mbuffercmd | $pvcmd | $recvcmd";
|
||||
|
||||
my $remotecmd = $sendcmd;
|
||||
if ($avail{'compress'}) { $remotecmd .= " | $compressargs{'cmd'}"; }
|
||||
if ($avail{'sourcembuffer'}) { $remotecmd .= " | $mbuffercmd $args{'source-bwlimit'} $mbufferoptions"; }
|
||||
|
||||
$synccmd = "$sshcmd $sourcehost " . escapeshellparam($remotecmd);
|
||||
$synccmd .= " | ";
|
||||
if ($avail{'targetmbuffer'}) { $synccmd .= "$mbuffercmd $args{'target-bwlimit'} $mbufferoptions | "; }
|
||||
if ($avail{'compress'}) { $synccmd .= "$compressargs{'decomcmd'} | "; }
|
||||
if ($avail{'localpv'} && !$quiet) { $synccmd .= "$pvcmd -s $pvsize | "; }
|
||||
|
@ -665,25 +774,37 @@ sub buildsynccmd {
|
|||
} else {
|
||||
#remote source, remote target... weird, but whatever, I'm not here to judge you.
|
||||
#$synccmd = "$sshcmd $sourcehost '$sendcmd | $compressargs{'cmd'} | $mbuffercmd' | $compressargs{'decomcmd'} | $pvcmd | $compressargs{'cmd'} | $mbuffercmd | $sshcmd $targethost '$compressargs{'decomcmd'} | $mbuffercmd | $recvcmd'";
|
||||
$synccmd = "$sshcmd $sourcehost '$sendcmd";
|
||||
if ($avail{'compress'}) { $synccmd .= " | $compressargs{'cmd'}"; }
|
||||
if ($avail{'sourcembuffer'}) { $synccmd .= " | $mbuffercmd $args{'source-bwlimit'} $mbufferoptions"; }
|
||||
$synccmd .= "' | ";
|
||||
|
||||
my $remotecmd = $sendcmd;
|
||||
if ($avail{'compress'}) { $remotecmd .= " | $compressargs{'cmd'}"; }
|
||||
if ($avail{'sourcembuffer'}) { $remotecmd .= " | $mbuffercmd $args{'source-bwlimit'} $mbufferoptions"; }
|
||||
|
||||
$synccmd = "$sshcmd $sourcehost " . escapeshellparam($remotecmd);
|
||||
$synccmd .= " | ";
|
||||
|
||||
if ($avail{'compress'}) { $synccmd .= "$compressargs{'decomcmd'} | "; }
|
||||
if ($avail{'localpv'} && !$quiet) { $synccmd .= "$pvcmd -s $pvsize | "; }
|
||||
if ($avail{'compress'}) { $synccmd .= "$compressargs{'cmd'} | "; }
|
||||
if ($avail{'localmbuffer'}) { $synccmd .= "$mbuffercmd $mbufferoptions | "; }
|
||||
$synccmd .= "$sshcmd $targethost '";
|
||||
if ($avail{'targetmbuffer'}) { $synccmd .= "$mbuffercmd $args{'target-bwlimit'} $mbufferoptions | "; }
|
||||
if ($avail{'compress'}) { $synccmd .= "$compressargs{'decomcmd'} | "; }
|
||||
$synccmd .= "$recvcmd'";
|
||||
$synccmd .= "$sshcmd $targethost ";
|
||||
|
||||
$remotecmd = "";
|
||||
if ($avail{'targetmbuffer'}) { $remotecmd .= " $mbuffercmd $args{'target-bwlimit'} $mbufferoptions |"; }
|
||||
if ($avail{'compress'}) { $remotecmd .= " $compressargs{'decomcmd'} |"; }
|
||||
$remotecmd .= " $recvcmd";
|
||||
|
||||
$synccmd .= escapeshellparam($remotecmd);
|
||||
}
|
||||
return $synccmd;
|
||||
}
|
||||
|
||||
sub pruneoldsyncsnaps {
|
||||
my ($rhost,$fs,$newsyncsnap,$isroot,@snaps) = @_;
|
||||
|
||||
my $fsescaped = escapeshellparam($fs);
|
||||
|
||||
if ($rhost ne '') { $rhost = "$sshcmd $rhost"; }
|
||||
|
||||
my $hostid = hostname();
|
||||
|
||||
my $mysudocmd;
|
||||
|
@ -693,7 +814,7 @@ sub pruneoldsyncsnaps {
|
|||
|
||||
# only prune snaps beginning with syncoid and our own hostname
|
||||
foreach my $snap(@snaps) {
|
||||
if ($snap =~ /^syncoid_$hostid/) {
|
||||
if ($snap =~ /^syncoid_\Q$hostid\E/) {
|
||||
# no matter what, we categorically refuse to
|
||||
# prune the new sync snap we created for this run
|
||||
if ($snap ne $newsyncsnap) {
|
||||
|
@ -709,12 +830,14 @@ sub pruneoldsyncsnaps {
|
|||
my $prunecmd;
|
||||
foreach my $snap(@prunesnaps) {
|
||||
$counter ++;
|
||||
$prunecmd .= "$mysudocmd $zfscmd destroy $fs\@$snap; ";
|
||||
$prunecmd .= "$mysudocmd $zfscmd destroy $fsescaped\@$snap; ";
|
||||
if ($counter > $maxsnapspercmd) {
|
||||
$prunecmd =~ s/\; $//;
|
||||
if ($rhost ne '') { $prunecmd = '"' . $prunecmd . '"'; }
|
||||
if ($debug) { print "DEBUG: pruning up to $maxsnapspercmd obsolete sync snapshots...\n"; }
|
||||
if ($debug) { print "DEBUG: $rhost $prunecmd\n"; }
|
||||
if ($rhost ne '') {
|
||||
$prunecmd = escapeshellparam($prunecmd);
|
||||
}
|
||||
system("$rhost $prunecmd") == 0
|
||||
or warn "CRITICAL ERROR: $rhost $prunecmd failed: $?";
|
||||
$prunecmd = '';
|
||||
|
@ -725,9 +848,11 @@ sub pruneoldsyncsnaps {
|
|||
# the loop, commit 'em now
|
||||
if ($counter) {
|
||||
$prunecmd =~ s/\; $//;
|
||||
if ($rhost ne '') { $prunecmd = '"' . $prunecmd . '"'; }
|
||||
if ($debug) { print "DEBUG: pruning up to $maxsnapspercmd obsolete sync snapshots...\n"; }
|
||||
if ($debug) { print "DEBUG: $rhost $prunecmd\n"; }
|
||||
if ($rhost ne '') {
|
||||
$prunecmd = escapeshellparam($prunecmd);
|
||||
}
|
||||
system("$rhost $prunecmd") == 0
|
||||
or warn "WARNING: $rhost $prunecmd failed: $?";
|
||||
}
|
||||
|
@ -765,13 +890,18 @@ sub getmatchingsnapshot {
|
|||
|
||||
sub newsyncsnap {
|
||||
my ($rhost,$fs,$isroot) = @_;
|
||||
if ($rhost ne '') { $rhost = "$sshcmd $rhost"; }
|
||||
my $fsescaped = escapeshellparam($fs);
|
||||
if ($rhost ne '') {
|
||||
$rhost = "$sshcmd $rhost";
|
||||
# double escaping needed
|
||||
$fsescaped = escapeshellparam($fsescaped);
|
||||
}
|
||||
my $mysudocmd;
|
||||
if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; }
|
||||
my $hostid = hostname();
|
||||
my %date = getdate();
|
||||
my $snapname = "syncoid\_$hostid\_$date{'stamp'}";
|
||||
my $snapcmd = "$rhost $mysudocmd $zfscmd snapshot $fs\@$snapname\n";
|
||||
my $snapcmd = "$rhost $mysudocmd $zfscmd snapshot $fsescaped\@$snapname\n";
|
||||
system($snapcmd) == 0
|
||||
or die "CRITICAL ERROR: $snapcmd failed: $?";
|
||||
return $snapname;
|
||||
|
@ -779,16 +909,21 @@ sub newsyncsnap {
|
|||
|
||||
sub targetexists {
|
||||
my ($rhost,$fs,$isroot) = @_;
|
||||
if ($rhost ne '') { $rhost = "$sshcmd $rhost"; }
|
||||
my $fsescaped = escapeshellparam($fs);
|
||||
if ($rhost ne '') {
|
||||
$rhost = "$sshcmd $rhost";
|
||||
# double escaping needed
|
||||
$fsescaped = escapeshellparam($fsescaped);
|
||||
}
|
||||
my $mysudocmd;
|
||||
if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; }
|
||||
my $checktargetcmd = "$rhost $mysudocmd $zfscmd get -H name $fs";
|
||||
my $checktargetcmd = "$rhost $mysudocmd $zfscmd get -H name $fsescaped";
|
||||
if ($debug) { print "DEBUG: checking to see if target filesystem exists using \"$checktargetcmd 2>&1 |\"...\n"; }
|
||||
open FH, "$checktargetcmd 2>&1 |";
|
||||
my $targetexists = <FH>;
|
||||
close FH;
|
||||
my $exit = $?;
|
||||
$targetexists = ( $targetexists =~ /^$fs/ && $exit == 0 );
|
||||
$targetexists = ( $targetexists =~ /^\Q$fs\E/ && $exit == 0 );
|
||||
return $targetexists;
|
||||
}
|
||||
|
||||
|
@ -803,7 +938,7 @@ sub getssh {
|
|||
if ($fs =~ /\@/) {
|
||||
$rhost = $fs;
|
||||
$fs =~ s/^\S*\@\S*://;
|
||||
$rhost =~ s/:$fs$//;
|
||||
$rhost =~ s/:\Q$fs\E$//;
|
||||
my $remoteuser = $rhost;
|
||||
$remoteuser =~ s/\@.*$//;
|
||||
if ($remoteuser eq 'root') { $isroot = 1; } else { $isroot = 0; }
|
||||
|
@ -829,11 +964,16 @@ sub dumphash() {
|
|||
sub getsnaps() {
|
||||
my ($type,$rhost,$fs,$isroot,%snaps) = @_;
|
||||
my $mysudocmd;
|
||||
my $fsescaped = escapeshellparam($fs);
|
||||
if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; }
|
||||
|
||||
if ($rhost ne '') { $rhost = "$sshcmd $rhost"; }
|
||||
if ($rhost ne '') {
|
||||
$rhost = "$sshcmd $rhost";
|
||||
# double escaping needed
|
||||
$fsescaped = escapeshellparam($fsescaped);
|
||||
}
|
||||
|
||||
my $getsnapcmd = "$rhost $mysudocmd $zfscmd get -Hpd 1 -t snapshot guid,creation $fs |";
|
||||
my $getsnapcmd = "$rhost $mysudocmd $zfscmd get -Hpd 1 -t snapshot guid,creation $fsescaped |";
|
||||
if ($debug) { print "DEBUG: getting list of snapshots on $fs using $getsnapcmd...\n"; }
|
||||
open FH, $getsnapcmd;
|
||||
my @rawsnaps = <FH>;
|
||||
|
@ -844,24 +984,24 @@ sub getsnaps() {
|
|||
|
||||
foreach my $line (@rawsnaps) {
|
||||
# only import snap guids from the specified filesystem
|
||||
if ($line =~ /$fs\@.*guid/) {
|
||||
if ($line =~ /\Q$fs\E\@.*guid/) {
|
||||
chomp $line;
|
||||
my $guid = $line;
|
||||
$guid =~ s/^.*\sguid\s*(\d*).*/$1/;
|
||||
my $snap = $line;
|
||||
$snap =~ s/^\S*\@(\S*)\s*guid.*$/$1/;
|
||||
$snap =~ s/^.*\@(.*)\tguid.*$/$1/;
|
||||
$snaps{$type}{$snap}{'guid'}=$guid;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $line (@rawsnaps) {
|
||||
# only import snap creations from the specified filesystem
|
||||
if ($line =~ /$fs\@.*creation/) {
|
||||
if ($line =~ /\Q$fs\E\@.*creation/) {
|
||||
chomp $line;
|
||||
my $creation = $line;
|
||||
$creation =~ s/^.*\screation\s*(\d*).*/$1/;
|
||||
my $snap = $line;
|
||||
$snap =~ s/^\S*\@(\S*)\s*creation.*$/$1/;
|
||||
$snap =~ s/^.*\@(.*)\tcreation.*$/$1/;
|
||||
$snaps{$type}{$snap}{'creation'}=$creation;
|
||||
}
|
||||
}
|
||||
|
@ -871,22 +1011,37 @@ sub getsnaps() {
|
|||
|
||||
|
||||
sub getsendsize {
|
||||
my ($sourcehost,$snap1,$snap2,$isroot) = @_;
|
||||
my ($sourcehost,$snap1,$snap2,$isroot,$receivetoken) = @_;
|
||||
|
||||
my $snap1escaped = escapeshellparam($snap1);
|
||||
my $snap2escaped = escapeshellparam($snap2);
|
||||
|
||||
my $mysudocmd;
|
||||
if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; }
|
||||
|
||||
my $sourcessh;
|
||||
if ($sourcehost ne '') {
|
||||
$sourcessh = "$sshcmd $sourcehost";
|
||||
$snap1escaped = escapeshellparam($snap1escaped);
|
||||
$snap2escaped = escapeshellparam($snap2escaped);
|
||||
} else {
|
||||
$sourcessh = '';
|
||||
}
|
||||
|
||||
my $snaps;
|
||||
if ($snap2) {
|
||||
# if we got a $snap2 argument, we want an incremental send estimate from $snap1 to $snap2.
|
||||
$snaps = "$args{'streamarg'} $snap1 $snap2";
|
||||
$snaps = "$args{'streamarg'} $snap1escaped $snap2escaped";
|
||||
} else {
|
||||
# if we didn't get a $snap2 arg, we want a full send estimate for $snap1.
|
||||
$snaps = "$snap1";
|
||||
$snaps = "$snap1escaped";
|
||||
}
|
||||
|
||||
my $sourcessh;
|
||||
if ($sourcehost ne '') { $sourcessh = "$sshcmd $sourcehost"; } else { $sourcessh = ''; }
|
||||
# in case of a resumed receive, get the remaining
|
||||
# size based on the resume token
|
||||
if (defined($receivetoken)) {
|
||||
$snaps = "-t $receivetoken";
|
||||
}
|
||||
|
||||
my $getsendsizecmd = "$sourcessh $mysudocmd $zfscmd send -nP $snaps";
|
||||
if ($debug) { print "DEBUG: getting estimated transfer size from source $sourcehost using \"$getsendsizecmd 2>&1 |\"...\n"; }
|
||||
|
@ -900,7 +1055,13 @@ sub getsendsize {
|
|||
# size of proposed xfer in bytes, but we need to remove
|
||||
# human-readable crap from it
|
||||
my $sendsize = pop(@rawsize);
|
||||
# the output format is different in case of
|
||||
# a resumed receive
|
||||
if (defined($receivetoken)) {
|
||||
$sendsize =~ s/.*\s([0-9]+)$/$1/;
|
||||
} else {
|
||||
$sendsize =~ s/^size\s*//;
|
||||
}
|
||||
chomp $sendsize;
|
||||
|
||||
# to avoid confusion with a zero size pv, give sendsize
|
||||
|
@ -929,6 +1090,35 @@ sub getdate {
|
|||
return %date;
|
||||
}
|
||||
|
||||
sub escapeshellparam {
|
||||
my ($par) = @_;
|
||||
# avoid use of uninitialized string in regex
|
||||
if (length($par)) {
|
||||
# "escape" all single quotes
|
||||
$par =~ s/'/'"'"'/g;
|
||||
} else {
|
||||
# avoid use of uninitialized string in concatenation below
|
||||
$par = '';
|
||||
}
|
||||
# single-quote entire string
|
||||
return "'$par'";
|
||||
}
|
||||
|
||||
sub getreceivetoken() {
|
||||
my ($rhost,$fs,$isroot) = @_;
|
||||
my $token = getzfsvalue($rhost,$fs,$isroot,"receive_resume_token");
|
||||
|
||||
if ($token ne '-' && $token ne '') {
|
||||
return $token;
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
print "DEBUG: no receive token found \n";
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
@ -967,3 +1157,4 @@ Options:
|
|||
--quiet Suppresses non-error output
|
||||
--dumpsnaps Dumps a list of snapshots during the run
|
||||
--no-command-checks Do not check command existence before attempting transfer. Not recommended
|
||||
--no-resume Don't use the ZFS resume feature if available
|
||||
|
|
Loading…
Reference in New Issue