revert syncoid due to compress options error in GetOpt version
This commit is contained in:
parent
1545d6a20c
commit
0be8076145
343
syncoid
343
syncoid
|
@ -4,44 +4,26 @@
|
||||||
# from http://www.gnu.org/licenses/gpl-3.0.html on 2014-11-17. A copy should also be available in this
|
# 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.
|
# project's Git repository at https://github.com/jimsalterjrs/sanoid/blob/master/LICENSE.
|
||||||
|
|
||||||
$::VERSION = '1.4.16';
|
my $version = '1.4.16';
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
use Getopt::Long qw(:config auto_version auto_help);
|
|
||||||
use Pod::Usage;
|
|
||||||
use Time::Local;
|
use Time::Local;
|
||||||
use Sys::Hostname;
|
use Sys::Hostname;
|
||||||
|
|
||||||
# Blank defaults to use ssh client's default
|
my %args = getargs(@ARGV);
|
||||||
# TODO: Merge into a single "sshflags" option?
|
|
||||||
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") or pod2usage(2);
|
|
||||||
|
|
||||||
$args{'compress'} = compressargset($args{'compress'} || 'default'); # Can't be done with GetOptions arg, as default still needs to be set
|
if ($args{'version'}) {
|
||||||
|
print "Syncoid version: $version\n";
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
||||||
# TODO Expand to accept multiple sources?
|
if (!(defined $args{'source'} && defined $args{'target'})) {
|
||||||
if (scalar(@ARGV) != 2) {
|
print 'usage: syncoid [src_user@src_host:]src_pool/src_dataset [dst_user@dst_host:]dst_pool/dst_dataset'."\n";
|
||||||
print("Source or target not found!\n");
|
|
||||||
pod2usage(2);
|
|
||||||
exit 127;
|
exit 127;
|
||||||
} else {
|
|
||||||
$args{'source'} = $ARGV[0];
|
|
||||||
$args{'target'} = $ARGV[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Could possibly merge these into an options function
|
|
||||||
if (length $args{'source-bwlimit'}) {
|
|
||||||
$args{'source-bwlimit'} = "-R $args{'source-bwlimit'}";
|
|
||||||
}
|
|
||||||
if (length $args{'target-bwlimit'}) {
|
|
||||||
$args{'target-bwlimit'} = "-r $args{'target-bwlimit'}";
|
|
||||||
}
|
|
||||||
$args{'streamarg'} = (defined $args{'no-stream'} ? '-i' : '-I');
|
|
||||||
|
|
||||||
my $rawsourcefs = $args{'source'};
|
my $rawsourcefs = $args{'source'};
|
||||||
my $rawtargetfs = $args{'target'};
|
my $rawtargetfs = $args{'target'};
|
||||||
my $debug = $args{'debug'};
|
my $debug = $args{'debug'};
|
||||||
|
@ -50,8 +32,25 @@ my $quiet = $args{'quiet'};
|
||||||
my $zfscmd = '/sbin/zfs';
|
my $zfscmd = '/sbin/zfs';
|
||||||
my $sshcmd = '/usr/bin/ssh';
|
my $sshcmd = '/usr/bin/ssh';
|
||||||
my $pscmd = '/bin/ps';
|
my $pscmd = '/bin/ps';
|
||||||
|
my $sshcipher;
|
||||||
|
if (defined $args{'c'}) {
|
||||||
|
$sshcipher = "-c $args{'c'}";
|
||||||
|
} else {
|
||||||
|
$sshcipher = '-c chacha20-poly1305@openssh.com,arcfour';
|
||||||
|
}
|
||||||
|
my $sshport = '-p 22';
|
||||||
|
my $sshoption;
|
||||||
|
if (defined $args{'o'}) {
|
||||||
|
my @options = split(',', $args{'o'});
|
||||||
|
foreach my $option (@options) {
|
||||||
|
$sshoption .= " -o $option";
|
||||||
|
if ($option eq "NoneSwitch=yes") {
|
||||||
|
$sshcipher = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$sshoption = "";
|
||||||
|
}
|
||||||
my $pvcmd = '/usr/bin/pv';
|
my $pvcmd = '/usr/bin/pv';
|
||||||
my $mbuffercmd = '/usr/bin/mbuffer';
|
my $mbuffercmd = '/usr/bin/mbuffer';
|
||||||
my $sudocmd = '/usr/bin/sudo';
|
my $sudocmd = '/usr/bin/sudo';
|
||||||
|
@ -60,25 +59,23 @@ my $mbufferoptions = '-q -s 128k -m 16M 2>/dev/null';
|
||||||
# being present on remote machines.
|
# being present on remote machines.
|
||||||
my $lscmd = '/bin/ls';
|
my $lscmd = '/bin/ls';
|
||||||
|
|
||||||
if (length $args{'sshcipher'}) {
|
if ( $args{'sshport'} ) {
|
||||||
$args{'sshcipher'} = "-c $args{'sshcipher'}";
|
$sshport = "-p $args{'sshport'}";
|
||||||
}
|
}
|
||||||
if (length $args{'sshport'}) {
|
|
||||||
$args{'sshport'} = "-p $args{'sshport'}";
|
|
||||||
}
|
|
||||||
if (length $args{'sshkey'}) {
|
|
||||||
$args{'sshkey'} = "-i $args{'sshkey'}";
|
|
||||||
}
|
|
||||||
my $sshoptions = join " ", map { "-o " . $_ } @{$args{'sshoption'}}; # deref required
|
|
||||||
|
|
||||||
# figure out if source and/or target are remote.
|
# figure out if source and/or target are remote.
|
||||||
$sshcmd = "$sshcmd $args{'sshcipher'} $sshoptions $args{'sshport'} $args{'sshkey'}";
|
if ( $args{'sshkey'} ) {
|
||||||
if ($debug) { print "DEBUG: SSHCMD: $sshcmd\n"; }
|
$sshcmd = "$sshcmd $sshoption $sshcipher $sshport -i $args{'sshkey'}";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$sshcmd = "$sshcmd $sshoption $sshcipher $sshport";
|
||||||
|
}
|
||||||
my ($sourcehost,$sourcefs,$sourceisroot) = getssh($rawsourcefs);
|
my ($sourcehost,$sourcefs,$sourceisroot) = getssh($rawsourcefs);
|
||||||
my ($targethost,$targetfs,$targetisroot) = getssh($rawtargetfs);
|
my ($targethost,$targetfs,$targetisroot) = getssh($rawtargetfs);
|
||||||
|
|
||||||
my $sourcesudocmd = $sourceisroot ? '' : $sudocmd;
|
my $sourcesudocmd;
|
||||||
my $targetsudocmd = $targetisroot ? '' : $sudocmd;
|
my $targetsudocmd;
|
||||||
|
if ($sourceisroot) { $sourcesudocmd = ''; } else { $sourcesudocmd = $sudocmd; }
|
||||||
|
if ($targetisroot) { $targetsudocmd = ''; } else { $targetsudocmd = $sudocmd; }
|
||||||
|
|
||||||
# figure out whether compression, mbuffering, pv
|
# figure out whether compression, mbuffering, pv
|
||||||
# are available on source, target, local machines.
|
# are available on source, target, local machines.
|
||||||
|
@ -91,7 +88,7 @@ my %snaps;
|
||||||
## can loop across children separately, for recursive ##
|
## can loop across children separately, for recursive ##
|
||||||
## replication ##
|
## replication ##
|
||||||
|
|
||||||
if (!defined $args{'recursive'}) {
|
if (! $args{'recursive'}) {
|
||||||
syncdataset($sourcehost, $sourcefs, $targethost, $targetfs);
|
syncdataset($sourcehost, $sourcefs, $targethost, $targetfs);
|
||||||
} else {
|
} else {
|
||||||
if ($debug) { print "DEBUG: recursive sync of $sourcefs.\n"; }
|
if ($debug) { print "DEBUG: recursive sync of $sourcefs.\n"; }
|
||||||
|
@ -164,21 +161,17 @@ sub syncdataset {
|
||||||
%snaps = (%sourcesnaps, %targetsnaps);
|
%snaps = (%sourcesnaps, %targetsnaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defined $args{'dumpsnaps'}) {
|
if ($args{'dumpsnaps'}) { print "merged snapshot list: \n"; dumphash(\%snaps); print "\n\n\n"; }
|
||||||
print "merged snapshot list of $targetfs: \n";
|
|
||||||
dumphash(\%snaps);
|
|
||||||
print "\n\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
# create a new syncoid snapshot on the source filesystem.
|
# create a new syncoid snapshot on the source filesystem.
|
||||||
my $newsyncsnap;
|
my $newsyncsnap;
|
||||||
if (!defined $args{'no-sync-snap'}) {
|
if (!defined ($args{'no-sync-snap'}) ) {
|
||||||
$newsyncsnap = newsyncsnap($sourcehost,$sourcefs,$sourceisroot);
|
$newsyncsnap = newsyncsnap($sourcehost,$sourcefs,$sourceisroot);
|
||||||
} else {
|
} else {
|
||||||
# we don't want sync snapshots created, so use the newest snapshot we can find.
|
# we don't want sync snapshots created, so use the newest snapshot we can find.
|
||||||
$newsyncsnap = getnewestsnapshot($sourcehost,$sourcefs,$sourceisroot);
|
$newsyncsnap = getnewestsnapshot($sourcehost,$sourcefs,$sourceisroot);
|
||||||
if ($newsyncsnap eq 0) {
|
if ($newsyncsnap eq 0) {
|
||||||
warn "CRITICAL: no snapshots exist on source $sourcefs, and you asked for --no-sync-snap.\n";
|
warn "CRITICAL: no snapshots exist on source, and you asked for --no-sync-snap.\n";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,7 +282,7 @@ sub syncdataset {
|
||||||
|
|
||||||
my $targetsize = getzfsvalue($targethost,$targetfs,$targetisroot,'-p used');
|
my $targetsize = getzfsvalue($targethost,$targetfs,$targetisroot,'-p used');
|
||||||
|
|
||||||
my $matchingsnap = getmatchingsnapshot($sourcefs, $targetfs, $targetsize, \%snaps);
|
my $matchingsnap = getmatchingsnapshot($targetsize, \%snaps);
|
||||||
if (! $matchingsnap) {
|
if (! $matchingsnap) {
|
||||||
# no matching snapshot; we whined piteously already, but let's go ahead and return false
|
# no matching snapshot; we whined piteously already, but let's go ahead and return false
|
||||||
# now in case more child datasets need replication.
|
# now in case more child datasets need replication.
|
||||||
|
@ -341,54 +334,111 @@ sub syncdataset {
|
||||||
|
|
||||||
} # end syncdataset()
|
} # end syncdataset()
|
||||||
|
|
||||||
sub compressargset {
|
|
||||||
my ($value) = @_;
|
|
||||||
my $DEFAULT_COMPRESSION = 'lzo';
|
|
||||||
my %COMPRESS_ARGS = (
|
|
||||||
'none' => {
|
|
||||||
rawcmd => '',
|
|
||||||
args => '',
|
|
||||||
decomrawcmd => '',
|
|
||||||
decomargs => '',
|
|
||||||
},
|
|
||||||
'gzip' => {
|
|
||||||
rawcmd => '/bin/gzip',
|
|
||||||
args => '-3',
|
|
||||||
decomrawcmd => '/bin/zcat',
|
|
||||||
decomargs => '',
|
|
||||||
},
|
|
||||||
'pigz-fast' => {
|
|
||||||
rawcmd => '/usr/bin/pigz',
|
|
||||||
args => '-3',
|
|
||||||
decomrawcmd => '/usr/bin/pigz',
|
|
||||||
decomargs => '-dc',
|
|
||||||
},
|
|
||||||
'pigz-slow' => {
|
|
||||||
rawcmd => '/usr/bin/pigz',
|
|
||||||
args => '-9',
|
|
||||||
decomrawcmd => '/usr/bin/pigz',
|
|
||||||
decomargs => '-dc',
|
|
||||||
},
|
|
||||||
'lzo' => {
|
|
||||||
rawcmd => '/usr/bin/lzop',
|
|
||||||
args => '',
|
|
||||||
decomrawcmd => '/usr/bin/lzop',
|
|
||||||
decomargs => '-dfc',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($value eq 'default') {
|
sub getargs {
|
||||||
$value = $DEFAULT_COMPRESSION;
|
my @args = @_;
|
||||||
} elsif (!(grep $value eq $_, ('gzip', 'pigz-fast', 'pigz-slow', 'lzo', 'default', 'none'))) {
|
my %args;
|
||||||
warn "Unrecognised compression value $value, defaulting to $DEFAULT_COMPRESSION";
|
|
||||||
$value = $DEFAULT_COMPRESSION;
|
my %novaluearg;
|
||||||
|
my %validarg;
|
||||||
|
push my @validargs, ('debug','nocommandchecks','version','monitor-version','compress','c','o','source-bwlimit','target-bwlimit','dumpsnaps','recursive','r','sshkey','sshport','quiet','no-stream','no-sync-snap');
|
||||||
|
foreach my $item (@validargs) { $validarg{$item} = 1; }
|
||||||
|
push my @novalueargs, ('debug','nocommandchecks','version','monitor-version','dumpsnaps','recursive','r','quiet','no-stream','no-sync-snap');
|
||||||
|
foreach my $item (@novalueargs) { $novaluearg{$item} = 1; }
|
||||||
|
|
||||||
|
while (my $rawarg = shift(@args)) {
|
||||||
|
my $arg = $rawarg;
|
||||||
|
my $argvalue = '';
|
||||||
|
if ($rawarg =~ /=/) {
|
||||||
|
# user specified the value for a CLI argument with =
|
||||||
|
# instead of with blank space. separate appropriately.
|
||||||
|
$argvalue = $arg;
|
||||||
|
$arg =~ s/=.*$//;
|
||||||
|
$argvalue =~ s/^.*=//;
|
||||||
|
}
|
||||||
|
if ($rawarg =~ /^--/) {
|
||||||
|
# doubledash arg
|
||||||
|
$arg =~ s/^--//;
|
||||||
|
if (! $validarg{$arg}) { die "ERROR: don't understand argument $rawarg.\n"; }
|
||||||
|
if ($novaluearg{$arg}) {
|
||||||
|
$args{$arg} = 1;
|
||||||
|
} else {
|
||||||
|
# if this CLI arg takes a user-specified value and
|
||||||
|
# we don't already have it, then the user must have
|
||||||
|
# specified with a space, so pull in the next value
|
||||||
|
# from the array as this value rather than as the
|
||||||
|
# next argument.
|
||||||
|
if ($argvalue eq '') { $argvalue = shift(@args); }
|
||||||
|
$args{$arg} = $argvalue;
|
||||||
|
}
|
||||||
|
} elsif ($arg =~ /^-/) {
|
||||||
|
# singledash arg
|
||||||
|
$arg =~ s/^-//;
|
||||||
|
if (! $validarg{$arg}) { die "ERROR: don't understand argument $rawarg.\n"; }
|
||||||
|
if ($novaluearg{$arg}) {
|
||||||
|
$args{$arg} = 1;
|
||||||
|
} else {
|
||||||
|
# if this CLI arg takes a user-specified value and
|
||||||
|
# we don't already have it, then the user must have
|
||||||
|
# specified with a space, so pull in the next value
|
||||||
|
# from the array as this value rather than as the
|
||||||
|
# next argument.
|
||||||
|
if ($argvalue eq '') { $argvalue = shift(@args); }
|
||||||
|
$args{$arg} = $argvalue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# bare arg
|
||||||
|
if (defined $args{'source'}) {
|
||||||
|
if (! defined $args{'target'}) {
|
||||||
|
$args{'target'} = $arg;
|
||||||
|
} else {
|
||||||
|
die "ERROR: don't know what to do with third bare argument $rawarg.\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$args{'source'} = $arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my %comargs = %{$COMPRESS_ARGS{$value}}; # copy
|
if (defined $args{'source-bwlimit'}) { $args{'source-bwlimit'} = "-R $args{'source-bwlimit'}"; } else { $args{'source-bwlimit'} = ''; }
|
||||||
$comargs{'compress'} = $value;
|
if (defined $args{'target-bwlimit'}) { $args{'target-bwlimit'} = "-r $args{'target-bwlimit'}"; } else { $args{'target-bwlimit'} = ''; }
|
||||||
$comargs{'cmd'} = "$comargs{'rawcmd'} $comargs{'args'}";
|
|
||||||
$comargs{'decomcmd'} = "$comargs{'decomrawcmd'} $comargs{'decomargs'}";
|
if (defined $args{'no-stream'}) { $args{'streamarg'} = '-i'; } else { $args{'streamarg'} = '-I'; }
|
||||||
return \%comargs;
|
|
||||||
|
if ($args{'r'}) { $args{'recursive'} = $args{'r'}; }
|
||||||
|
|
||||||
|
if (!defined $args{'compress'}) { $args{'compress'} = 'default'; }
|
||||||
|
|
||||||
|
if ($args{'compress'} eq 'gzip') {
|
||||||
|
$args{'rawcompresscmd'} = '/bin/gzip';
|
||||||
|
$args{'compressargs'} = '-3';
|
||||||
|
$args{'rawdecompresscmd'} = '/bin/zcat';
|
||||||
|
$args{'decompressargs'} = '';
|
||||||
|
} elsif ( ($args{'compress'} eq 'pigz-fast')) {
|
||||||
|
$args{'rawcompresscmd'} = '/usr/bin/pigz';
|
||||||
|
$args{'compressargs'} = '-3';
|
||||||
|
$args{'rawdecompresscmd'} = '/usr/bin/pigz';
|
||||||
|
$args{'decompressargs'} = '-dc';
|
||||||
|
} elsif ( ($args{'compress'} eq 'pigz-slow')) {
|
||||||
|
$args{'rawcompresscmd'} = '/usr/bin/pigz';
|
||||||
|
$args{'compressargs'} = '-9';
|
||||||
|
$args{'rawdecompresscmd'} = '/usr/bin/pigz';
|
||||||
|
$args{'decompressargs'} = '-dc';
|
||||||
|
} elsif ( ($args{'compress'} eq 'lzo') || ($args{'compress'} eq 'default') ) {
|
||||||
|
$args{'rawcompresscmd'} = '/usr/bin/lzop';
|
||||||
|
$args{'compressargs'} = '';
|
||||||
|
$args{'rawdecompresscmd'} = '/usr/bin/lzop';
|
||||||
|
$args{'decompressargs'} = '-dfc';
|
||||||
|
} else {
|
||||||
|
$args{'rawcompresscmd'} = '';
|
||||||
|
$args{'compressargs'} = '';
|
||||||
|
$args{'rawdecompresscmd'} = '';
|
||||||
|
$args{'decompressargs'} = '';
|
||||||
|
}
|
||||||
|
$args{'compresscmd'} = "$args{'rawcompresscmd'} $args{'compressargs'}";
|
||||||
|
$args{'decompresscmd'} = "$args{'rawdecompresscmd'} $args{'decompressargs'}";
|
||||||
|
|
||||||
|
return %args;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub checkcommands {
|
sub checkcommands {
|
||||||
|
@ -418,15 +468,24 @@ sub checkcommands {
|
||||||
|
|
||||||
# if raw compress command is null, we must have specified no compression. otherwise,
|
# if raw compress command is null, we must have specified no compression. otherwise,
|
||||||
# make sure that compression is available everywhere we need it
|
# make sure that compression is available everywhere we need it
|
||||||
if ($args{'compress'}{'compress'} eq 'none') {
|
if ($args{'rawcompresscmd'} eq '') {
|
||||||
|
$avail{'sourcecompress'} = 0;
|
||||||
|
$avail{'sourcecompress'} = 0;
|
||||||
|
$avail{'localcompress'} = 0;
|
||||||
|
if ($args{'compress'} eq 'none' ||
|
||||||
|
$args{'compress'} eq 'no' ||
|
||||||
|
$args{'compress'} eq '0') {
|
||||||
if ($debug) { print "DEBUG: compression forced off from command line arguments.\n"; }
|
if ($debug) { print "DEBUG: compression forced off from command line arguments.\n"; }
|
||||||
} else {
|
} else {
|
||||||
if ($debug) { print "DEBUG: checking availability of $args{'compress'}{'rawcmd'} on source...\n"; }
|
print "WARN: value $args{'compress'} for argument --compress not understood, proceeding without compression.\n";
|
||||||
$avail{'sourcecompress'} = `$sourcessh $lscmd $args{'compress'}{'rawcmd'} 2>/dev/null`;
|
}
|
||||||
if ($debug) { print "DEBUG: checking availability of $args{'compress'}{'rawcmd'} on target...\n"; }
|
} else {
|
||||||
$avail{'targetcompress'} = `$targetssh $lscmd $args{'compress'}{'rawcmd'} 2>/dev/null`;
|
if ($debug) { print "DEBUG: checking availability of $args{'rawcompresscmd'} on source...\n"; }
|
||||||
if ($debug) { print "DEBUG: checking availability of $args{'compress'}{'rawcmd'} on local machine...\n"; }
|
$avail{'sourcecompress'} = `$sourcessh $lscmd $args{'rawcompresscmd'} 2>/dev/null`;
|
||||||
$avail{'localcompress'} = `$lscmd $args{'compress'}{'rawcmd'} 2>/dev/null`;
|
if ($debug) { print "DEBUG: checking availability of $args{'rawcompresscmd'} on target...\n"; }
|
||||||
|
$avail{'targetcompress'} = `$targetssh $lscmd $args{'rawcompresscmd'} 2>/dev/null`;
|
||||||
|
if ($debug) { print "DEBUG: checking availability of $args{'rawcompresscmd'} on local machine...\n"; }
|
||||||
|
$avail{'localcompress'} = `$lscmd $args{'rawcompresscmd'} 2>/dev/null`;
|
||||||
}
|
}
|
||||||
|
|
||||||
my ($s,$t);
|
my ($s,$t);
|
||||||
|
@ -452,14 +511,14 @@ sub checkcommands {
|
||||||
|
|
||||||
|
|
||||||
if ($avail{'sourcecompress'} eq '') {
|
if ($avail{'sourcecompress'} eq '') {
|
||||||
if ($args{'compress'}{'rawcmd'} ne '') {
|
if ($args{'rawcompresscmd'} ne '') {
|
||||||
print "WARN: $args{'compress'}{'rawcmd'} not available on source $s- sync will continue without compression.\n";
|
print "WARN: $args{'compresscmd'} not available on source $s- sync will continue without compression.\n";
|
||||||
}
|
}
|
||||||
$avail{'compress'} = 0;
|
$avail{'compress'} = 0;
|
||||||
}
|
}
|
||||||
if ($avail{'targetcompress'} eq '') {
|
if ($avail{'targetcompress'} eq '') {
|
||||||
if ($args{'compress'}{'rawcmd'} ne '') {
|
if ($args{'rawcompresscmd'} ne '') {
|
||||||
print "WARN: $args{'compress'}{'rawcmd'} not available on target $t - sync will continue without compression.\n";
|
print "WARN: $args{'compresscmd'} not available on target $t - sync will continue without compression.\n";
|
||||||
}
|
}
|
||||||
$avail{'compress'} = 0;
|
$avail{'compress'} = 0;
|
||||||
}
|
}
|
||||||
|
@ -471,8 +530,8 @@ sub checkcommands {
|
||||||
|
|
||||||
# corner case - if source AND target are BOTH remote, we have to check for local compress too
|
# 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 ($sourcehost ne '' && $targethost ne '' && $avail{'localcompress'} eq '') {
|
||||||
if ($args{'compress'}{'rawcmd'} ne '') {
|
if ($args{'rawcompresscmd'} ne '') {
|
||||||
print "WARN: $args{'compress'}{'rawcmd'} not available on local machine - sync will continue without compression.\n";
|
print "WARN: $args{'compresscmd'} not available on local machine - sync will continue without compression.\n";
|
||||||
}
|
}
|
||||||
$avail{'compress'} = 0;
|
$avail{'compress'} = 0;
|
||||||
}
|
}
|
||||||
|
@ -628,9 +687,9 @@ sub buildsynccmd {
|
||||||
$synccmd = "$sendcmd |";
|
$synccmd = "$sendcmd |";
|
||||||
# avoid confusion - accept either source-bwlimit or target-bwlimit as the bandwidth limiting option here
|
# avoid confusion - accept either source-bwlimit or target-bwlimit as the bandwidth limiting option here
|
||||||
my $bwlimit = '';
|
my $bwlimit = '';
|
||||||
if (length $args{'bwlimit'}) {
|
if (defined $args{'source-bwlimit'}) {
|
||||||
$bwlimit = $args{'source-bwlimit'};
|
$bwlimit = $args{'source-bwlimit'};
|
||||||
} elsif (length $args{'target-bwlimit'}) {
|
} elsif (defined $args{'target-bwlimit'}) {
|
||||||
$bwlimit = $args{'target-bwlimit'};
|
$bwlimit = $args{'target-bwlimit'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,18 +698,18 @@ sub buildsynccmd {
|
||||||
$synccmd .= " $recvcmd";
|
$synccmd .= " $recvcmd";
|
||||||
} elsif ($sourcehost eq '') {
|
} elsif ($sourcehost eq '') {
|
||||||
# local source, remote target.
|
# local source, remote target.
|
||||||
#$synccmd = "$sendcmd | $pvcmd | $args{'compress'}{'cmd'} | $mbuffercmd | $sshcmd $targethost '$args{'compress'}{'decomcmd'} | $mbuffercmd | $recvcmd'";
|
#$synccmd = "$sendcmd | $pvcmd | $args{'compresscmd'} | $mbuffercmd | $sshcmd $targethost '$args{'decompresscmd'} | $mbuffercmd | $recvcmd'";
|
||||||
$synccmd = "$sendcmd |";
|
$synccmd = "$sendcmd |";
|
||||||
if ($avail{'localpv'} && !$quiet) { $synccmd .= " $pvcmd -s $pvsize |"; }
|
if ($avail{'localpv'} && !$quiet) { $synccmd .= " $pvcmd -s $pvsize |"; }
|
||||||
if ($avail{'compress'}) { $synccmd .= " $args{'compress'}{'cmd'} |"; }
|
if ($avail{'compress'}) { $synccmd .= " $args{'compresscmd'} |"; }
|
||||||
if ($avail{'sourcembuffer'}) { $synccmd .= " $mbuffercmd $args{'source-bwlimit'} $mbufferoptions |"; }
|
if ($avail{'sourcembuffer'}) { $synccmd .= " $mbuffercmd $args{'source-bwlimit'} $mbufferoptions |"; }
|
||||||
$synccmd .= " $sshcmd $targethost '";
|
$synccmd .= " $sshcmd $targethost '";
|
||||||
if ($avail{'targetmbuffer'}) { $synccmd .= " $mbuffercmd $args{'target-bwlimit'} $mbufferoptions |"; }
|
if ($avail{'targetmbuffer'}) { $synccmd .= " $mbuffercmd $args{'target-bwlimit'} $mbufferoptions |"; }
|
||||||
if ($avail{'compress'}) { $synccmd .= " $args{'compress'}{'decomcmd'} |"; }
|
if ($avail{'compress'}) { $synccmd .= " $args{'decompresscmd'} |"; }
|
||||||
$synccmd .= " $recvcmd'";
|
$synccmd .= " $recvcmd'";
|
||||||
} elsif ($targethost eq '') {
|
} elsif ($targethost eq '') {
|
||||||
# remote source, local target.
|
# remote source, local target.
|
||||||
#$synccmd = "$sshcmd $sourcehost '$sendcmd | $args{'compress'}{'cmd'} | $mbuffercmd' | $args{'decompress'}{'cmd'} | $mbuffercmd | $pvcmd | $recvcmd";
|
#$synccmd = "$sshcmd $sourcehost '$sendcmd | $args{'compresscmd'} | $mbuffercmd' | $args{'decompresscmd'} | $mbuffercmd | $pvcmd | $recvcmd";
|
||||||
$synccmd = "$sshcmd $sourcehost '$sendcmd";
|
$synccmd = "$sshcmd $sourcehost '$sendcmd";
|
||||||
if ($avail{'compress'}) { $synccmd .= " | $args{'compresscmd'}"; }
|
if ($avail{'compress'}) { $synccmd .= " | $args{'compresscmd'}"; }
|
||||||
if ($avail{'sourcembuffer'}) { $synccmd .= " | $mbuffercmd $args{'source-bwlimit'} $mbufferoptions"; }
|
if ($avail{'sourcembuffer'}) { $synccmd .= " | $mbuffercmd $args{'source-bwlimit'} $mbufferoptions"; }
|
||||||
|
@ -661,18 +720,18 @@ sub buildsynccmd {
|
||||||
$synccmd .= "$recvcmd";
|
$synccmd .= "$recvcmd";
|
||||||
} else {
|
} else {
|
||||||
#remote source, remote target... weird, but whatever, I'm not here to judge you.
|
#remote source, remote target... weird, but whatever, I'm not here to judge you.
|
||||||
#$synccmd = "$sshcmd $sourcehost '$sendcmd | $args{'compress'}{'cmd'} | $mbuffercmd' | $args{'compress'}{'decomcmd'} | $pvcmd | $args{'compress'}{'cmd'} | $mbuffercmd | $sshcmd $targethost '$args{'compress'}{'decomcmd'} | $mbuffercmd | $recvcmd'";
|
#$synccmd = "$sshcmd $sourcehost '$sendcmd | $args{'compresscmd'} | $mbuffercmd' | $args{'decompresscmd'} | $pvcmd | $args{'compresscmd'} | $mbuffercmd | $sshcmd $targethost '$args{'decompresscmd'} | $mbuffercmd | $recvcmd'";
|
||||||
$synccmd = "$sshcmd $sourcehost '$sendcmd";
|
$synccmd = "$sshcmd $sourcehost '$sendcmd";
|
||||||
if ($avail{'compress'}) { $synccmd .= " | $args{'compress'}{'cmd'}"; }
|
if ($avail{'compress'}) { $synccmd .= " | $args{'compresscmd'}"; }
|
||||||
if ($avail{'sourcembuffer'}) { $synccmd .= " | $mbuffercmd $args{'source-bwlimit'} $mbufferoptions"; }
|
if ($avail{'sourcembuffer'}) { $synccmd .= " | $mbuffercmd $args{'source-bwlimit'} $mbufferoptions"; }
|
||||||
$synccmd .= "' | ";
|
$synccmd .= "' | ";
|
||||||
if ($avail{'compress'}) { $synccmd .= "$args{'compress'}{'decomcmd'} | "; }
|
if ($avail{'compress'}) { $synccmd .= "$args{'decompresscmd'} | "; }
|
||||||
if ($avail{'localpv'} && !$quiet) { $synccmd .= "$pvcmd -s $pvsize | "; }
|
if ($avail{'localpv'} && !$quiet) { $synccmd .= "$pvcmd -s $pvsize | "; }
|
||||||
if ($avail{'compress'}) { $synccmd .= "$args{'compress'}{'cmd'} | "; }
|
if ($avail{'compress'}) { $synccmd .= "$args{'compresscmd'} | "; }
|
||||||
if ($avail{'localmbuffer'}) { $synccmd .= "$mbuffercmd $mbufferoptions | "; }
|
if ($avail{'localmbuffer'}) { $synccmd .= "$mbuffercmd $mbufferoptions | "; }
|
||||||
$synccmd .= "$sshcmd $targethost '";
|
$synccmd .= "$sshcmd $targethost '";
|
||||||
if ($avail{'targetmbuffer'}) { $synccmd .= "$mbuffercmd $args{'target-bwlimit'} $mbufferoptions | "; }
|
if ($avail{'targetmbuffer'}) { $synccmd .= "$mbuffercmd $args{'target-bwlimit'} $mbufferoptions | "; }
|
||||||
if ($avail{'compress'}) { $synccmd .= "$args{'compress'}{'decomcmd'} | "; }
|
if ($avail{'compress'}) { $synccmd .= "$args{'decompresscmd'} | "; }
|
||||||
$synccmd .= "$recvcmd'";
|
$synccmd .= "$recvcmd'";
|
||||||
}
|
}
|
||||||
return $synccmd;
|
return $synccmd;
|
||||||
|
@ -732,7 +791,7 @@ sub pruneoldsyncsnaps {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub getmatchingsnapshot {
|
sub getmatchingsnapshot {
|
||||||
my ($sourcefs, $targetfs, $targetsize, $snaps) = @_;
|
my ($targetsize, $snaps) = shift;
|
||||||
foreach my $snap ( sort { $snaps{'source'}{$b}{'creation'}<=>$snaps{'source'}{$a}{'creation'} } keys %{ $snaps{'source'} }) {
|
foreach my $snap ( sort { $snaps{'source'}{$b}{'creation'}<=>$snaps{'source'}{$a}{'creation'} } keys %{ $snaps{'source'} }) {
|
||||||
if (defined $snaps{'target'}{$snap}{'guid'}) {
|
if (defined $snaps{'target'}{$snap}{'guid'}) {
|
||||||
if ($snaps{'source'}{$snap}{'guid'} == $snaps{'target'}{$snap}{'guid'}) {
|
if ($snaps{'source'}{$snap}{'guid'} == $snaps{'target'}{$snap}{'guid'}) {
|
||||||
|
@ -744,7 +803,7 @@ sub getmatchingsnapshot {
|
||||||
# if we got this far, we failed to find a matching snapshot.
|
# if we got this far, we failed to find a matching snapshot.
|
||||||
|
|
||||||
print "\n";
|
print "\n";
|
||||||
print "CRITICAL ERROR: Target $targetfs exists but has no snapshots matching with $sourcefs!\n";
|
print "CRITICAL ERROR: Target exists but has no matching snapshots!\n";
|
||||||
print " Replication to target would require destroying existing\n";
|
print " Replication to target would require destroying existing\n";
|
||||||
print " target. Cowardly refusing to destroy your existing target.\n\n";
|
print " target. Cowardly refusing to destroy your existing target.\n\n";
|
||||||
|
|
||||||
|
@ -752,7 +811,7 @@ sub getmatchingsnapshot {
|
||||||
# zfs create targetpool/targetsnap ; syncoid sourcepool/sourcesnap targetpool/targetsnap ...
|
# zfs create targetpool/targetsnap ; syncoid sourcepool/sourcesnap targetpool/targetsnap ...
|
||||||
|
|
||||||
if ( $targetsize < (64*1024*1024) ) {
|
if ( $targetsize < (64*1024*1024) ) {
|
||||||
print " NOTE: Target $targetfs dataset is < 64MB used - did you mistakenly run\n";
|
print " NOTE: Target dataset is < 64MB used - did you mistakenly run\n";
|
||||||
print " \`zfs create $args{'target'}\` on the target? ZFS initial\n";
|
print " \`zfs create $args{'target'}\` on the target? ZFS initial\n";
|
||||||
print " replication must be to a NON EXISTENT DATASET, which will\n";
|
print " replication must be to a NON EXISTENT DATASET, which will\n";
|
||||||
print " then be CREATED BY the initial replication process.\n\n";
|
print " then be CREATED BY the initial replication process.\n\n";
|
||||||
|
@ -806,7 +865,7 @@ sub getssh {
|
||||||
if ($remoteuser eq 'root') { $isroot = 1; } else { $isroot = 0; }
|
if ($remoteuser eq 'root') { $isroot = 1; } else { $isroot = 0; }
|
||||||
# now we need to establish a persistent master SSH connection
|
# now we need to establish a persistent master SSH connection
|
||||||
$socket = "/tmp/syncoid-$remoteuser-$rhost-" . time();
|
$socket = "/tmp/syncoid-$remoteuser-$rhost-" . time();
|
||||||
open FH, "$sshcmd -M -S $socket -o ControlPersist=1m $args{'sshport'} $rhost exit |";
|
open FH, "$sshcmd -M -S $socket -o ControlPersist=1m $sshport $rhost exit |";
|
||||||
close FH;
|
close FH;
|
||||||
$rhost = "-S $socket $rhost";
|
$rhost = "-S $socket $rhost";
|
||||||
} else {
|
} else {
|
||||||
|
@ -926,40 +985,4 @@ sub getdate {
|
||||||
return %date;
|
return %date;
|
||||||
}
|
}
|
||||||
|
|
||||||
__END__
|
|
||||||
|
|
||||||
=head1 NAME
|
|
||||||
|
|
||||||
syncoid - ZFS snapshot replication tool
|
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
|
||||||
|
|
||||||
syncoid [options]... SOURCE TARGET
|
|
||||||
or syncoid [options]... SOURCE [USER@]HOST:TARGET
|
|
||||||
or syncoid [options]... [USER@]HOST:SOURCE [TARGET]
|
|
||||||
or syncoid [options]... [USER@]HOST:SOURCE [USER@]HOST:TARGET
|
|
||||||
|
|
||||||
SOURCE Source ZFS dataset. Can be either local or remote
|
|
||||||
TARGET Target ZFS dataset. Can be either local or remote
|
|
||||||
|
|
||||||
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
|
|
||||||
--source-bwlimit=<limit k|m|g|t> Bandwidth limit on the source transfer
|
|
||||||
--target-bwlimit=<limit k|m|g|t> Bandwidth limit on the target transfer
|
|
||||||
--no-stream Replicates using newest snapshot instead of intermediates
|
|
||||||
--no-sync-snap Does not create new snapshot, only transfers existing
|
|
||||||
|
|
||||||
--sshkey=FILE Specifies a ssh public key to use to connect
|
|
||||||
--sshport=PORT Connects to remote on a particular port
|
|
||||||
--sshcipher|c=CIPHER Passes CIPHER to ssh to use a particular cipher set
|
|
||||||
--sshoption|o=OPTION Passes OPTION to ssh for remote usage. Can be specified multiple times
|
|
||||||
|
|
||||||
--help Prints this helptext
|
|
||||||
--verbose Prints the version number
|
|
||||||
--debug Prints out a lot of additional information during a syncoid run
|
|
||||||
--monitor-version Currently does nothing
|
|
||||||
--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
|
|
||||||
|
|
Loading…
Reference in New Issue