|
|
|
@ -498,7 +498,6 @@ sub syncdataset {
|
|
|
|
|
|
|
|
|
|
my $ret;
|
|
|
|
|
if (defined $origin) {
|
|
|
|
|
writelog('INFO', "Clone is recreated on target $targetfs based on $origin");
|
|
|
|
|
($ret, $stdout) = syncclone($sourcehost, $sourcefs, $origin, $targethost, $targetfs, $oldestsnap);
|
|
|
|
|
if ($ret) {
|
|
|
|
|
writelog('INFO', "clone creation failed, trying ordinary replication as fallback");
|
|
|
|
@ -506,12 +505,6 @@ sub syncdataset {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!defined ($args{'no-stream'}) ) {
|
|
|
|
|
writelog('INFO', "Sending oldest full snapshot $sourcefs\@$oldestsnap to new target filesystem:");
|
|
|
|
|
} else {
|
|
|
|
|
writelog('INFO', "--no-stream selected; sending newest full snapshot $sourcefs\@$oldestsnap to new target filesystem:");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
($ret, $stdout) = syncfull($sourcehost, $sourcefs, $targethost, $targetfs, $oldestsnap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -532,8 +525,6 @@ sub syncdataset {
|
|
|
|
|
# $originaltargetreadonly = getzfsvalue($targethost,$targetfs,$targetisroot,'readonly');
|
|
|
|
|
# setzfsvalue($targethost,$targetfs,$targetisroot,'readonly','on');
|
|
|
|
|
|
|
|
|
|
writelog('INFO', "Updating new target filesystem with incremental $sourcefs\@$oldestsnap ... $newsyncsnap:");
|
|
|
|
|
|
|
|
|
|
(my $ret, $stdout) = syncincremental($sourcehost, $sourcefs, $targethost, $targetfs, $oldestsnap, $newsyncsnap, 0);
|
|
|
|
|
|
|
|
|
|
if ($ret != 0) {
|
|
|
|
@ -865,6 +856,16 @@ sub syncdataset {
|
|
|
|
|
# those that exist on the source. Remaining are the snapshots
|
|
|
|
|
# that are only on the target. Then sort to remove the oldest
|
|
|
|
|
# snapshots first.
|
|
|
|
|
|
|
|
|
|
# regather snapshots on source and target
|
|
|
|
|
%snaps = getsnaps('source',$sourcehost,$sourcefs,$sourceisroot,0);
|
|
|
|
|
|
|
|
|
|
if ($targetexists) {
|
|
|
|
|
my %targetsnaps = getsnaps('target',$targethost,$targetfs,$targetisroot,0);
|
|
|
|
|
my %sourcesnaps = %snaps;
|
|
|
|
|
%snaps = (%sourcesnaps, %targetsnaps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my @to_delete = sort { sortsnapshots(\%snaps, $a, $b) } grep {!exists $snaps{'source'}{$_}} keys %{ $snaps{'target'} };
|
|
|
|
|
while (@to_delete) {
|
|
|
|
|
# Create batch of snapshots to remove
|
|
|
|
@ -898,7 +899,6 @@ sub runsynccmd {
|
|
|
|
|
my $disp_pvsize = $pvsize == 0 ? 'UNKNOWN' : readablebytes($pvsize);
|
|
|
|
|
my $sendoptions;
|
|
|
|
|
if ($sendsource =~ / -t /) {
|
|
|
|
|
writelog('INFO', "Resuming interrupted zfs send/receive from $sourcefs to $targetfs (~ $disp_pvsize remaining):");
|
|
|
|
|
$sendoptions = getoptionsline(\@sendoptions, ('P','V','e','v'));
|
|
|
|
|
} elsif ($sendsource =~ /#/) {
|
|
|
|
|
$sendoptions = getoptionsline(\@sendoptions, ('L','V','c','e','w'));
|
|
|
|
@ -934,12 +934,13 @@ sub runsynccmd {
|
|
|
|
|
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $targetfsescaped 2>&1";
|
|
|
|
|
|
|
|
|
|
my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot);
|
|
|
|
|
writelog('INFO', "Sync size: ~$disp_pvsize");
|
|
|
|
|
writelog('DEBUG', "sync size: ~$disp_pvsize");
|
|
|
|
|
writelog('DEBUG', "$synccmd");
|
|
|
|
|
|
|
|
|
|
# make sure target is (still) not currently in receive.
|
|
|
|
|
if (iszfsbusy($targethost,$targetfs,$targetisroot)) {
|
|
|
|
|
writelog('WARN', "Cannot sync now: $targetfs is already target of a zfs receive process.");
|
|
|
|
|
my $targetname = buildnicename($targethost, $targetfs);
|
|
|
|
|
writelog('WARN', "Cannot sync now: $targetname is already target of a zfs receive process.");
|
|
|
|
|
return (1, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -971,6 +972,16 @@ sub syncfull {
|
|
|
|
|
my $sendsource = "$sourcefsescaped\@$snapescaped";
|
|
|
|
|
my $pvsize = getsendsize($sourcehost,"$sourcefs\@$snapname",0,$sourceisroot);
|
|
|
|
|
|
|
|
|
|
my $srcname = buildnicename($sourcehost, $sourcefs, $snapname);
|
|
|
|
|
my $targetname = buildnicename($targethost, $targetfs);
|
|
|
|
|
my $disp_pvsize = $pvsize == 0 ? 'UNKNOWN' : readablebytes($pvsize);
|
|
|
|
|
|
|
|
|
|
if (!defined ($args{'no-stream'}) ) {
|
|
|
|
|
writelog('INFO', "Sending oldest full snapshot $srcname to new target filesystem $targetname (~ $disp_pvsize):");
|
|
|
|
|
} else {
|
|
|
|
|
writelog('INFO', "--no-stream selected; sending newest full snapshot $srcname to new target filesystem $targetname: (~ $disp_pvsize)");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return runsynccmd($sourcehost, $sourcefs, $sendsource, $targethost, $targetfs, $pvsize);
|
|
|
|
|
} # end syncfull()
|
|
|
|
|
|
|
|
|
@ -1011,8 +1022,11 @@ sub syncincremental {
|
|
|
|
|
foreach my $i (0..(scalar(@intsnaps) - 2)) {
|
|
|
|
|
my $snapa = $intsnaps[$i];
|
|
|
|
|
my $snapb = $intsnaps[$i + 1];
|
|
|
|
|
writelog('INFO', "Performing an incremental sync between '$snapa' and '$snapb'");
|
|
|
|
|
syncincremental($sourcehost, $sourcefs, $targethost, $targetfs, $snapa, $snapb, 1) == 0 or return $?;
|
|
|
|
|
(my $ret, my $stdout) = syncincremental($sourcehost, $sourcefs, $targethost, $targetfs, $snapa, $snapb, 1);
|
|
|
|
|
|
|
|
|
|
if ($ret != 0) {
|
|
|
|
|
return ($ret, $stdout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Return after finishing the -i syncs so that we don't try to do another -I
|
|
|
|
@ -1026,6 +1040,12 @@ sub syncincremental {
|
|
|
|
|
my $sendsource = "$streamarg $sourcefsescaped\@$fromsnapescaped $sourcefsescaped\@$tosnapescaped";
|
|
|
|
|
my $pvsize = getsendsize($sourcehost,"$sourcefs\@$fromsnap","$sourcefs\@$tosnap",$sourceisroot);
|
|
|
|
|
|
|
|
|
|
my $srcname = buildnicename($sourcehost, $sourcefs, $fromsnap);
|
|
|
|
|
my $targetname = buildnicename($targethost, $targetfs);
|
|
|
|
|
my $disp_pvsize = $pvsize == 0 ? 'UNKNOWN' : readablebytes($pvsize);
|
|
|
|
|
|
|
|
|
|
writelog('INFO', "Sending incremental $srcname ... $tosnap to $targetname (~ $disp_pvsize):");
|
|
|
|
|
|
|
|
|
|
return runsynccmd($sourcehost, $sourcefs, $sendsource, $targethost, $targetfs, $pvsize);
|
|
|
|
|
} # end syncincremental()
|
|
|
|
|
|
|
|
|
@ -1038,6 +1058,12 @@ sub syncclone {
|
|
|
|
|
my $sendsource = "-i $originescaped $sourcefsescaped\@$tosnapescaped";
|
|
|
|
|
my $pvsize = getsendsize($sourcehost,$origin,"$sourcefs\@$tosnap",$sourceisroot);
|
|
|
|
|
|
|
|
|
|
my $srcname = buildnicename($sourcehost, $origin);
|
|
|
|
|
my $targetname = buildnicename($targethost, $targetfs);
|
|
|
|
|
my $disp_pvsize = $pvsize == 0 ? 'UNKNOWN' : readablebytes($pvsize);
|
|
|
|
|
|
|
|
|
|
writelog('INFO', "Clone is recreated on target $targetname based on $srcname (~ $disp_pvsize):");
|
|
|
|
|
|
|
|
|
|
return runsynccmd($sourcehost, $sourcefs, $sendsource, $targethost, $targetfs, $pvsize);
|
|
|
|
|
} # end syncclone()
|
|
|
|
|
|
|
|
|
@ -1047,6 +1073,12 @@ sub syncresume {
|
|
|
|
|
my $sendsource = "-t $receivetoken";
|
|
|
|
|
my $pvsize = getsendsize($sourcehost,"","",$sourceisroot,$receivetoken);
|
|
|
|
|
|
|
|
|
|
my $srcname = buildnicename($sourcehost, $sourcefs);
|
|
|
|
|
my $targetname = buildnicename($targethost, $targetfs);
|
|
|
|
|
my $disp_pvsize = $pvsize == 0 ? 'UNKNOWN' : readablebytes($pvsize);
|
|
|
|
|
|
|
|
|
|
writelog('INFO', "Resuming interrupted zfs send/receive from $srcname to $targetname (~ $disp_pvsize remaining):");
|
|
|
|
|
|
|
|
|
|
return runsynccmd($sourcehost, $sourcefs, $sendsource, $targethost, $targetfs, $pvsize);
|
|
|
|
|
} # end syncresume()
|
|
|
|
|
|
|
|
|
@ -1058,6 +1090,11 @@ sub syncbookmark {
|
|
|
|
|
my $tosnapescaped = escapeshellparam($tosnap);
|
|
|
|
|
my $sendsource = "-i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$tosnapescaped";
|
|
|
|
|
|
|
|
|
|
my $srcname = buildnicename($sourcehost, $sourcefs, '', $bookmark);
|
|
|
|
|
my $targetname = buildnicename($targethost, $targetfs);
|
|
|
|
|
|
|
|
|
|
writelog('INFO', "Sending incremental $srcname ... $tosnap to $targetname:");
|
|
|
|
|
|
|
|
|
|
return runsynccmd($sourcehost, $sourcefs, $sendsource, $targethost, $targetfs, 0);
|
|
|
|
|
} # end syncbookmark
|
|
|
|
|
|
|
|
|
@ -1110,12 +1147,24 @@ sub compressargset {
|
|
|
|
|
decomrawcmd => 'zstd',
|
|
|
|
|
decomargs => '-dc',
|
|
|
|
|
},
|
|
|
|
|
'zstdmt-fast' => {
|
|
|
|
|
rawcmd => 'zstdmt',
|
|
|
|
|
args => '-3',
|
|
|
|
|
decomrawcmd => 'zstdmt',
|
|
|
|
|
decomargs => '-dc',
|
|
|
|
|
},
|
|
|
|
|
'zstd-slow' => {
|
|
|
|
|
rawcmd => 'zstd',
|
|
|
|
|
args => '-19',
|
|
|
|
|
decomrawcmd => 'zstd',
|
|
|
|
|
decomargs => '-dc',
|
|
|
|
|
},
|
|
|
|
|
'zstdmt-slow' => {
|
|
|
|
|
rawcmd => 'zstdmt',
|
|
|
|
|
args => '-19',
|
|
|
|
|
decomrawcmd => 'zstdmt',
|
|
|
|
|
decomargs => '-dc',
|
|
|
|
|
},
|
|
|
|
|
'xz' => {
|
|
|
|
|
rawcmd => 'xz',
|
|
|
|
|
args => '',
|
|
|
|
@ -1138,7 +1187,7 @@ sub compressargset {
|
|
|
|
|
|
|
|
|
|
if ($value eq 'default') {
|
|
|
|
|
$value = $DEFAULT_COMPRESSION;
|
|
|
|
|
} elsif (!(grep $value eq $_, ('gzip', 'pigz-fast', 'pigz-slow', 'zstd-fast', 'zstd-slow', 'lz4', 'xz', 'lzo', 'default', 'none'))) {
|
|
|
|
|
} elsif (!(grep $value eq $_, ('gzip', 'pigz-fast', 'pigz-slow', 'zstd-fast', 'zstdmt-fast', 'zstd-slow', 'zstdmt-slow', 'lz4', 'xz', 'lzo', 'default', 'none'))) {
|
|
|
|
|
writelog('WARN', "Unrecognised compression value $value, defaulting to $DEFAULT_COMPRESSION");
|
|
|
|
|
$value = $DEFAULT_COMPRESSION;
|
|
|
|
|
}
|
|
|
|
@ -1507,7 +1556,7 @@ sub getnewestsnapshot {
|
|
|
|
|
my $snaps = shift;
|
|
|
|
|
foreach my $snap (sort { sortsnapshots($snaps, $b, $a) } keys %{ $snaps{'source'} }) {
|
|
|
|
|
# return on first snap found - it's the newest
|
|
|
|
|
writelog('INFO', "NEWEST SNAPSHOT: $snap");
|
|
|
|
|
writelog('DEBUG', "NEWEST SNAPSHOT: $snap");
|
|
|
|
|
return $snap;
|
|
|
|
|
}
|
|
|
|
|
# must not have had any snapshots on source - looks like we'd better create one!
|
|
|
|
@ -2233,6 +2282,26 @@ sub snapisincluded {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub buildnicename {
|
|
|
|
|
my ($host,$fs,$snapname,$bookmarkname) = @_;
|
|
|
|
|
|
|
|
|
|
my $name;
|
|
|
|
|
if ($host) {
|
|
|
|
|
$host =~ s/-S \/tmp\/syncoid[a-zA-Z0-9-@]+ //g;
|
|
|
|
|
$name = "$host:$fs";
|
|
|
|
|
} else {
|
|
|
|
|
$name = "$fs";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($snapname) {
|
|
|
|
|
$name = "$name\@$snapname";
|
|
|
|
|
} elsif ($bookmarkname) {
|
|
|
|
|
$name = "$name#$bookmarkname";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__END__
|
|
|
|
|
|
|
|
|
|
=head1 NAME
|
|
|
|
@ -2251,7 +2320,7 @@ syncoid - ZFS snapshot replication tool
|
|
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
|
|
|
|
|
--compress=FORMAT Compresses data during transfer. Currently accepted options are gzip, pigz-fast, pigz-slow, zstd-fast, zstd-slow, lz4, xz, lzo (default) & none
|
|
|
|
|
--compress=FORMAT Compresses data during transfer. Currently accepted options are gzip, pigz-fast, pigz-slow, zstd-fast, zstdmt-fast, zstd-slow, zstdmt-slow, lz4, xz, 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
|
|
|
|
|
--skip-parent Skips syncing of the parent dataset. Does nothing without '--recursive' option.
|
|
|
|
|