mirror of https://github.com/jimsalterjrs/sanoid
Merge branch 'master' into path
This commit is contained in:
commit
02219b0fcf
14
README.md
14
README.md
|
|
@ -57,6 +57,10 @@ Which would be enough to tell sanoid to take and keep 36 hourly snapshots, 30 da
|
||||||
|
|
||||||
This will process your sanoid.conf file, it will NOT create snapshots, but it will purge expired ones.
|
This will process your sanoid.conf file, it will NOT create snapshots, but it will purge expired ones.
|
||||||
|
|
||||||
|
+ --force-prune
|
||||||
|
|
||||||
|
Purges expired snapshots even if a send/recv is in progress
|
||||||
|
|
||||||
+ --monitor-snapshots
|
+ --monitor-snapshots
|
||||||
|
|
||||||
This option is designed to be run by a Nagios monitoring system. It reports on the health of your snapshots.
|
This option is designed to be run by a Nagios monitoring system. It reports on the health of your snapshots.
|
||||||
|
|
@ -188,7 +192,7 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup
|
||||||
|
|
||||||
+ --no-command-checks
|
+ --no-command-checks
|
||||||
|
|
||||||
Do not check the existance of commands before attempting the transfer. It assumes all programs are available. This should never be used.
|
Does not check the existence of commands before attempting the transfer, providing administrators a way to run the tool with minimal overhead and maximum speed, at risk of potentially failed replication, or other possible edge cases. It assumes all programs are available, and should not be used in most situations. This is an not an officially supported run mode.
|
||||||
|
|
||||||
+ --no-stream
|
+ --no-stream
|
||||||
|
|
||||||
|
|
@ -198,6 +202,14 @@ As of 1.4.18, syncoid also automatically supports and enables resume of interrup
|
||||||
|
|
||||||
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.
|
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-clone-rollback
|
||||||
|
|
||||||
|
Do not rollback clones on target
|
||||||
|
|
||||||
|
+ --no-rollback
|
||||||
|
|
||||||
|
Do not rollback anything (clones or snapshots) on target host
|
||||||
|
|
||||||
+ --exclude=REGEX
|
+ --exclude=REGEX
|
||||||
|
|
||||||
The given regular expression will be matched against all datasets which would be synced by this run and excludes them. This argument can be specified multiple times.
|
The given regular expression will be matched against all datasets which would be synced by this run and excludes them. This argument can be specified multiple times.
|
||||||
|
|
|
||||||
69
sanoid
69
sanoid
|
|
@ -19,7 +19,7 @@ use Time::Local; # to parse dates in reverse
|
||||||
my %args = ("configdir" => "/etc/sanoid");
|
my %args = ("configdir" => "/etc/sanoid");
|
||||||
GetOptions(\%args, "verbose", "debug", "cron", "readonly", "quiet",
|
GetOptions(\%args, "verbose", "debug", "cron", "readonly", "quiet",
|
||||||
"monitor-health", "force-update", "configdir=s",
|
"monitor-health", "force-update", "configdir=s",
|
||||||
"monitor-snapshots", "take-snapshots", "prune-snapshots",
|
"monitor-snapshots", "take-snapshots", "prune-snapshots", "force-prune",
|
||||||
"monitor-capacity"
|
"monitor-capacity"
|
||||||
) or pod2usage(2);
|
) or pod2usage(2);
|
||||||
|
|
||||||
|
|
@ -143,8 +143,8 @@ sub monitor_snapshots {
|
||||||
|
|
||||||
my $typewarn = $type . '_warn';
|
my $typewarn = $type . '_warn';
|
||||||
my $typecrit = $type . '_crit';
|
my $typecrit = $type . '_crit';
|
||||||
my $warn = $config{$section}{$typewarn} * $smallerperiod;
|
my $warn = convertTimePeriod($config{$section}{$typewarn}, $smallerperiod);
|
||||||
my $crit = $config{$section}{$typecrit} * $smallerperiod;
|
my $crit = convertTimePeriod($config{$section}{$typecrit}, $smallerperiod);
|
||||||
my $elapsed = -1;
|
my $elapsed = -1;
|
||||||
if (defined $snapsbytype{$path}{$type}{'newest'}) {
|
if (defined $snapsbytype{$path}{$type}{'newest'}) {
|
||||||
$elapsed = $snapsbytype{$path}{$type}{'newest'};
|
$elapsed = $snapsbytype{$path}{$type}{'newest'};
|
||||||
|
|
@ -153,7 +153,7 @@ sub monitor_snapshots {
|
||||||
my $dispwarn = displaytime($warn);
|
my $dispwarn = displaytime($warn);
|
||||||
my $dispcrit = displaytime($crit);
|
my $dispcrit = displaytime($crit);
|
||||||
if ( $elapsed > $crit || $elapsed == -1) {
|
if ( $elapsed > $crit || $elapsed == -1) {
|
||||||
if ($config{$section}{$typecrit} > 0) {
|
if ($crit > 0) {
|
||||||
if (! $config{$section}{'monitor_dont_crit'}) { $errorlevel = 2; }
|
if (! $config{$section}{'monitor_dont_crit'}) { $errorlevel = 2; }
|
||||||
if ($elapsed == -1) {
|
if ($elapsed == -1) {
|
||||||
push @msgs, "CRIT: $path has no $type snapshots at all!";
|
push @msgs, "CRIT: $path has no $type snapshots at all!";
|
||||||
|
|
@ -162,7 +162,7 @@ sub monitor_snapshots {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elsif ($elapsed > $warn) {
|
} elsif ($elapsed > $warn) {
|
||||||
if ($config{$section}{$typewarn} > 0) {
|
if ($warn > 0) {
|
||||||
if (! $config{$section}{'monitor_dont_warn'} && ($errorlevel < 2) ) { $errorlevel = 1; }
|
if (! $config{$section}{'monitor_dont_warn'} && ($errorlevel < 2) ) { $errorlevel = 1; }
|
||||||
push @msgs, "WARN: $path\'s newest $type snapshot is $dispelapsed old (should be < $dispwarn)";
|
push @msgs, "WARN: $path\'s newest $type snapshot is $dispelapsed old (should be < $dispwarn)";
|
||||||
}
|
}
|
||||||
|
|
@ -305,7 +305,7 @@ sub prune_snapshots {
|
||||||
writelock('sanoid_pruning');
|
writelock('sanoid_pruning');
|
||||||
foreach my $snap( @prunesnaps ){
|
foreach my $snap( @prunesnaps ){
|
||||||
if ($args{'verbose'}) { print "INFO: pruning $snap ... \n"; }
|
if ($args{'verbose'}) { print "INFO: pruning $snap ... \n"; }
|
||||||
if (iszfsbusy($path)) {
|
if (!$args{'force-prune'} && iszfsbusy($path)) {
|
||||||
if ($args{'verbose'}) { print "INFO: deferring pruning of $snap - $path is currently in zfs send or receive.\n"; }
|
if ($args{'verbose'}) { print "INFO: deferring pruning of $snap - $path is currently in zfs send or receive.\n"; }
|
||||||
} else {
|
} else {
|
||||||
if (! $args{'readonly'}) {
|
if (! $args{'readonly'}) {
|
||||||
|
|
@ -377,9 +377,9 @@ sub take_snapshots {
|
||||||
if ($config{$section}{'process_children_only'}) { next; }
|
if ($config{$section}{'process_children_only'}) { next; }
|
||||||
|
|
||||||
my $path = $config{$section}{'path'};
|
my $path = $config{$section}{'path'};
|
||||||
|
my @types = ('yearly','monthly','weekly','daily','hourly','frequently');
|
||||||
|
|
||||||
foreach my $type (keys %{ $config{$section} }){
|
foreach my $type (@types) {
|
||||||
unless ($type =~ /ly$/) { next; }
|
|
||||||
if ($config{$section}{$type} > 0) {
|
if ($config{$section}{$type} > 0) {
|
||||||
|
|
||||||
my $newestage; # in seconds
|
my $newestage; # in seconds
|
||||||
|
|
@ -513,11 +513,15 @@ sub take_snapshots {
|
||||||
my $dataset = (split '@', $snap)[0];
|
my $dataset = (split '@', $snap)[0];
|
||||||
my $snapname = (split '@', $snap)[1];
|
my $snapname = (split '@', $snap)[1];
|
||||||
my $presnapshotfailure = 0;
|
my $presnapshotfailure = 0;
|
||||||
if ($config{$dataset}{'pre_snapshot_script'} and !$args{'readonly'}) {
|
my $ret = 0;
|
||||||
|
if ($config{$dataset}{'pre_snapshot_script'}) {
|
||||||
$ENV{'SANOID_TARGET'} = $dataset;
|
$ENV{'SANOID_TARGET'} = $dataset;
|
||||||
$ENV{'SANOID_SNAPNAME'} = $snapname;
|
$ENV{'SANOID_SNAPNAME'} = $snapname;
|
||||||
if ($args{'verbose'}) { print "executing pre_snapshot_script '".$config{$dataset}{'pre_snapshot_script'}."' on dataset '$dataset'\n"; }
|
if ($args{'verbose'}) { print "executing pre_snapshot_script '".$config{$dataset}{'pre_snapshot_script'}."' on dataset '$dataset'\n"; }
|
||||||
my $ret = runscript('pre_snapshot_script',$dataset);
|
|
||||||
|
if (!$args{'readonly'}) {
|
||||||
|
$ret = runscript('pre_snapshot_script',$dataset);
|
||||||
|
}
|
||||||
|
|
||||||
delete $ENV{'SANOID_TARGET'};
|
delete $ENV{'SANOID_TARGET'};
|
||||||
delete $ENV{'SANOID_SNAPNAME'};
|
delete $ENV{'SANOID_SNAPNAME'};
|
||||||
|
|
@ -533,12 +537,15 @@ sub take_snapshots {
|
||||||
system($zfs, "snapshot", "$snap") == 0
|
system($zfs, "snapshot", "$snap") == 0
|
||||||
or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?";
|
or warn "CRITICAL ERROR: $zfs snapshot $snap failed, $?";
|
||||||
}
|
}
|
||||||
if ($config{$dataset}{'post_snapshot_script'} and !$args{'readonly'}) {
|
if ($config{$dataset}{'post_snapshot_script'}) {
|
||||||
if (!$presnapshotfailure or $config{$dataset}{'force_post_snapshot_script'}) {
|
if (!$presnapshotfailure or $config{$dataset}{'force_post_snapshot_script'}) {
|
||||||
$ENV{'SANOID_TARGET'} = $dataset;
|
$ENV{'SANOID_TARGET'} = $dataset;
|
||||||
$ENV{'SANOID_SNAPNAME'} = $snapname;
|
$ENV{'SANOID_SNAPNAME'} = $snapname;
|
||||||
if ($args{'verbose'}) { print "executing post_snapshot_script '".$config{$dataset}{'post_snapshot_script'}."' on dataset '$dataset'\n"; }
|
if ($args{'verbose'}) { print "executing post_snapshot_script '".$config{$dataset}{'post_snapshot_script'}."' on dataset '$dataset'\n"; }
|
||||||
runscript('post_snapshot_script',$dataset);
|
|
||||||
|
if (!$args{'readonly'}) {
|
||||||
|
runscript('post_snapshot_script',$dataset);
|
||||||
|
}
|
||||||
|
|
||||||
delete $ENV{'SANOID_TARGET'};
|
delete $ENV{'SANOID_TARGET'};
|
||||||
delete $ENV{'SANOID_SNAPNAME'};
|
delete $ENV{'SANOID_SNAPNAME'};
|
||||||
|
|
@ -1511,6 +1518,43 @@ sub runscript {
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#######################################################################################################################3
|
||||||
|
#######################################################################################################################3
|
||||||
|
#######################################################################################################################3
|
||||||
|
|
||||||
|
sub convertTimePeriod {
|
||||||
|
my $value=shift;
|
||||||
|
my $period=shift;
|
||||||
|
|
||||||
|
if ($value =~ /^\d+[yY]$/) {
|
||||||
|
$period = 60*60*24*31*365;
|
||||||
|
chop $value;
|
||||||
|
} elsif ($value =~ /^\d+[wW]$/) {
|
||||||
|
$period = 60*60*24*7;
|
||||||
|
chop $value;
|
||||||
|
} elsif ($value =~ /^\d+[dD]$/) {
|
||||||
|
$period = 60*60*24;
|
||||||
|
chop $value;
|
||||||
|
} elsif ($value =~ /^\d+[hH]$/) {
|
||||||
|
$period = 60*60;
|
||||||
|
chop $value;
|
||||||
|
} elsif ($value =~ /^\d+[mM]$/) {
|
||||||
|
$period = 60;
|
||||||
|
chop $value;
|
||||||
|
} elsif ($value =~ /^\d+[sS]$/) {
|
||||||
|
$period = 1;
|
||||||
|
chop $value;
|
||||||
|
} elsif ($value =~ /^\d+$/) {
|
||||||
|
# no unit, provided fallback period is used
|
||||||
|
} else {
|
||||||
|
# invalid value, return smallest valid value as fallback
|
||||||
|
# (will trigger a warning message for monitoring for sure)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value * $period;
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
||||||
=head1 NAME
|
=head1 NAME
|
||||||
|
|
@ -1538,6 +1582,7 @@ Options:
|
||||||
--monitor-snapshots Reports on snapshot "health", in a Nagios compatible format
|
--monitor-snapshots Reports on snapshot "health", in a Nagios compatible format
|
||||||
--take-snapshots Creates snapshots as specified in sanoid.conf
|
--take-snapshots Creates snapshots as specified in sanoid.conf
|
||||||
--prune-snapshots Purges expired snapshots as specified in sanoid.conf
|
--prune-snapshots Purges expired snapshots as specified in sanoid.conf
|
||||||
|
--force-prune Purges expired snapshots even if a send/recv is in progress
|
||||||
|
|
||||||
--help Prints this helptext
|
--help Prints this helptext
|
||||||
--version Prints the version number
|
--version Prints the version number
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,9 @@ yearly_min = 0
|
||||||
# monitoring plugin - define warn / crit levels for each snapshot type by age, in units of one period down
|
# monitoring plugin - define warn / crit levels for each snapshot type by age, in units of one period down
|
||||||
# example hourly_warn = 90 means issue WARNING if most recent hourly snapshot is not less than 90 minutes old,
|
# example hourly_warn = 90 means issue WARNING if most recent hourly snapshot is not less than 90 minutes old,
|
||||||
# daily_crit = 36 means issue CRITICAL if most recent daily snapshot is not less than 36 hours old,
|
# daily_crit = 36 means issue CRITICAL if most recent daily snapshot is not less than 36 hours old,
|
||||||
# monthly_warn = 36 means issue WARNING if most recent monthly snapshot is not less than 36 days old... etc.
|
# monthly_warn = 5 means issue WARNING if most recent monthly snapshot is not less than 5 weeks old... etc.
|
||||||
|
# the following time case insensitive suffixes can also be used:
|
||||||
|
# y = years, w = weeks, d = days, h = hours, m = minutes, s = seconds
|
||||||
#
|
#
|
||||||
# monitor_dont_warn = yes will cause the monitoring service to report warnings as text, but with status OK.
|
# monitor_dont_warn = yes will cause the monitoring service to report warnings as text, but with status OK.
|
||||||
# monitor_dont_crit = yes will cause the monitoring service to report criticals as text, but with status OK.
|
# monitor_dont_crit = yes will cause the monitoring service to report criticals as text, but with status OK.
|
||||||
|
|
@ -94,14 +96,14 @@ monitor_dont_warn = no
|
||||||
monitor_dont_crit = no
|
monitor_dont_crit = no
|
||||||
frequently_warn = 0
|
frequently_warn = 0
|
||||||
frequently_crit = 0
|
frequently_crit = 0
|
||||||
hourly_warn = 90
|
hourly_warn = 90m
|
||||||
hourly_crit = 360
|
hourly_crit = 360m
|
||||||
daily_warn = 28
|
daily_warn = 28h
|
||||||
daily_crit = 32
|
daily_crit = 32h
|
||||||
weekly_warn = 0
|
weekly_warn = 0
|
||||||
weekly_crit = 0
|
weekly_crit = 0
|
||||||
monthly_warn = 5
|
monthly_warn = 32d
|
||||||
monthly_crit = 6
|
monthly_crit = 40d
|
||||||
yearly_warn = 0
|
yearly_warn = 0
|
||||||
yearly_crit = 0
|
yearly_crit = 0
|
||||||
|
|
||||||
|
|
|
||||||
80
syncoid
80
syncoid
|
|
@ -14,16 +14,30 @@ use Pod::Usage;
|
||||||
use Time::Local;
|
use Time::Local;
|
||||||
use Sys::Hostname;
|
use Sys::Hostname;
|
||||||
|
|
||||||
|
my $mbuffer_size = "16M";
|
||||||
|
|
||||||
# Blank defaults to use ssh client's default
|
# Blank defaults to use ssh client's default
|
||||||
# TODO: Merge into a single "sshflags" option?
|
# TODO: Merge into a single "sshflags" option?
|
||||||
my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [], 'target-bwlimit' => '', 'source-bwlimit' => '');
|
my %args = ('sshkey' => '', 'sshport' => '', 'sshcipher' => '', 'sshoption' => [], 'target-bwlimit' => '', 'source-bwlimit' => '');
|
||||||
GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r",
|
GetOptions(\%args, "no-command-checks", "monitor-version", "compress=s", "dumpsnaps", "recursive|r", "sendoptions=s", "recvoptions=s",
|
||||||
"source-bwlimit=s", "target-bwlimit=s", "sshkey=s", "sshport=i", "sshcipher|c=s", "sshoption|o=s@",
|
"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", "exclude=s@", "skip-parent", "identifier=s",
|
"debug", "quiet", "no-stream", "no-sync-snap", "no-resume", "exclude=s@", "skip-parent", "identifier=s",
|
||||||
"no-clone-handling", "no-privilege-elevation", "force-delete") or pod2usage(2);
|
"no-clone-handling", "no-privilege-elevation", "force-delete", "no-clone-rollback", "no-rollback",
|
||||||
|
"mbuffer-size=s" => \$mbuffer_size) or pod2usage(2);
|
||||||
|
|
||||||
my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set
|
my %compressargs = %{compressargset($args{'compress'} || 'default')}; # Can't be done with GetOptions arg, as default still needs to be set
|
||||||
|
|
||||||
|
my $sendoptions = '';
|
||||||
|
if (length $args{'sendoptions'}) {
|
||||||
|
$sendoptions = $args{'sendoptions'}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $recvoptions = '';
|
||||||
|
if (length $args{'recvoptions'}) {
|
||||||
|
$recvoptions = $args{'recvoptions'}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# TODO Expand to accept multiple sources?
|
# TODO Expand to accept multiple sources?
|
||||||
if (scalar(@ARGV) != 2) {
|
if (scalar(@ARGV) != 2) {
|
||||||
print("Source or target not found!\n");
|
print("Source or target not found!\n");
|
||||||
|
|
@ -59,7 +73,7 @@ my $pscmd = 'ps';
|
||||||
my $pvcmd = 'pv';
|
my $pvcmd = 'pv';
|
||||||
my $mbuffercmd = 'mbuffer';
|
my $mbuffercmd = 'mbuffer';
|
||||||
my $sudocmd = 'sudo';
|
my $sudocmd = 'sudo';
|
||||||
my $mbufferoptions = '-q -s 128k -m 16M 2>/dev/null';
|
my $mbufferoptions = '-q -s 128k -m $mbuffer_size 2>/dev/null';
|
||||||
# currently using POSIX compatible command to check for program existence because we aren't depending on perl
|
# currently using POSIX compatible command to check for program existence because we aren't depending on perl
|
||||||
# being present on remote machines.
|
# being present on remote machines.
|
||||||
my $checkcmd = 'command -v';
|
my $checkcmd = 'command -v';
|
||||||
|
|
@ -242,6 +256,12 @@ sub syncdataset {
|
||||||
my $sourcefsescaped = escapeshellparam($sourcefs);
|
my $sourcefsescaped = escapeshellparam($sourcefs);
|
||||||
my $targetfsescaped = escapeshellparam($targetfs);
|
my $targetfsescaped = escapeshellparam($targetfs);
|
||||||
|
|
||||||
|
# if no rollbacks are allowed, disable forced receive
|
||||||
|
my $forcedrecv = "-F";
|
||||||
|
if (defined $args{'no-rollback'}) {
|
||||||
|
$forcedrecv = "";
|
||||||
|
}
|
||||||
|
|
||||||
if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; }
|
if ($debug) { print "DEBUG: syncing source $sourcefs to target $targetfs.\n"; }
|
||||||
|
|
||||||
my $sync = getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync');
|
my $sync = getzfsvalue($sourcehost,$sourcefs,$sourceisroot,'syncoid:sync');
|
||||||
|
|
@ -362,13 +382,13 @@ sub syncdataset {
|
||||||
if (defined $args{'no-stream'}) { $oldestsnap = getnewestsnapshot(\%snaps); }
|
if (defined $args{'no-stream'}) { $oldestsnap = getnewestsnapshot(\%snaps); }
|
||||||
my $oldestsnapescaped = escapeshellparam($oldestsnap);
|
my $oldestsnapescaped = escapeshellparam($oldestsnap);
|
||||||
|
|
||||||
my $sendcmd = "$sourcesudocmd $zfscmd send $sourcefsescaped\@$oldestsnapescaped";
|
my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions $sourcefsescaped\@$oldestsnapescaped";
|
||||||
my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped";
|
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped";
|
||||||
|
|
||||||
my $pvsize;
|
my $pvsize;
|
||||||
if (defined $origin) {
|
if (defined $origin) {
|
||||||
my $originescaped = escapeshellparam($origin);
|
my $originescaped = escapeshellparam($origin);
|
||||||
$sendcmd = "$sourcesudocmd $zfscmd send -i $originescaped $sourcefsescaped\@$oldestsnapescaped";
|
$sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -i $originescaped $sourcefsescaped\@$oldestsnapescaped";
|
||||||
my $streamargBackup = $args{'streamarg'};
|
my $streamargBackup = $args{'streamarg'};
|
||||||
$args{'streamarg'} = "-i";
|
$args{'streamarg'} = "-i";
|
||||||
$pvsize = getsendsize($sourcehost,$origin,"$sourcefs\@$oldestsnap",$sourceisroot);
|
$pvsize = getsendsize($sourcehost,$origin,"$sourcefs\@$oldestsnap",$sourceisroot);
|
||||||
|
|
@ -416,7 +436,7 @@ sub syncdataset {
|
||||||
# $originaltargetreadonly = getzfsvalue($targethost,$targetfs,$targetisroot,'readonly');
|
# $originaltargetreadonly = getzfsvalue($targethost,$targetfs,$targetisroot,'readonly');
|
||||||
# setzfsvalue($targethost,$targetfs,$targetisroot,'readonly','on');
|
# setzfsvalue($targethost,$targetfs,$targetisroot,'readonly','on');
|
||||||
|
|
||||||
$sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$oldestsnapescaped $sourcefsescaped\@$newsyncsnapescaped";
|
$sendcmd = "$sourcesudocmd $zfscmd send $sendoptions $args{'streamarg'} $sourcefsescaped\@$oldestsnapescaped $sourcefsescaped\@$newsyncsnapescaped";
|
||||||
$pvsize = getsendsize($sourcehost,"$sourcefs\@$oldestsnap","$sourcefs\@$newsyncsnap",$sourceisroot);
|
$pvsize = getsendsize($sourcehost,"$sourcefs\@$oldestsnap","$sourcefs\@$newsyncsnap",$sourceisroot);
|
||||||
$disp_pvsize = readablebytes($pvsize);
|
$disp_pvsize = readablebytes($pvsize);
|
||||||
if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; }
|
if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; }
|
||||||
|
|
@ -453,8 +473,8 @@ sub syncdataset {
|
||||||
# and because this will ony resume the receive to the next
|
# and because this will ony resume the receive to the next
|
||||||
# snapshot, do a normal sync after that
|
# snapshot, do a normal sync after that
|
||||||
if (defined($receivetoken)) {
|
if (defined($receivetoken)) {
|
||||||
my $sendcmd = "$sourcesudocmd $zfscmd send -t $receivetoken";
|
my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -t $receivetoken";
|
||||||
my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped";
|
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped";
|
||||||
my $pvsize = getsendsize($sourcehost,"","",$sourceisroot,$receivetoken);
|
my $pvsize = getsendsize($sourcehost,"","",$sourceisroot,$receivetoken);
|
||||||
my $disp_pvsize = readablebytes($pvsize);
|
my $disp_pvsize = readablebytes($pvsize);
|
||||||
if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; }
|
if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; }
|
||||||
|
|
@ -566,13 +586,19 @@ sub syncdataset {
|
||||||
} else {
|
} else {
|
||||||
my $matchingsnapescaped = escapeshellparam($matchingsnap);
|
my $matchingsnapescaped = escapeshellparam($matchingsnap);
|
||||||
# rollback target to matchingsnap
|
# rollback target to matchingsnap
|
||||||
if ($debug) { print "DEBUG: rolling back target to $targetfs\@$matchingsnap...\n"; }
|
if (!defined $args{'no-rollback'}) {
|
||||||
if ($targethost ne '') {
|
my $rollbacktype = "-R";
|
||||||
if ($debug) { print "$sshcmd $targethost $targetsudocmd $zfscmd rollback -R $targetfsescaped\@$matchingsnapescaped\n"; }
|
if (defined $args{'no-clone-rollback'}) {
|
||||||
system ("$sshcmd $targethost " . escapeshellparam("$targetsudocmd $zfscmd rollback -R $targetfsescaped\@$matchingsnapescaped"));
|
$rollbacktype = "-r";
|
||||||
} else {
|
}
|
||||||
if ($debug) { print "$targetsudocmd $zfscmd rollback -R $targetfsescaped\@$matchingsnapescaped\n"; }
|
if ($debug) { print "DEBUG: rolling back target to $targetfs\@$matchingsnap...\n"; }
|
||||||
system ("$targetsudocmd $zfscmd rollback -R $targetfsescaped\@$matchingsnapescaped");
|
if ($targethost ne '') {
|
||||||
|
if ($debug) { print "$sshcmd $targethost $targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped\n"; }
|
||||||
|
system ("$sshcmd $targethost " . escapeshellparam("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped"));
|
||||||
|
} else {
|
||||||
|
if ($debug) { print "$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped\n"; }
|
||||||
|
system ("$targetsudocmd $zfscmd rollback $rollbacktype $targetfsescaped\@$matchingsnapescaped");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my $nextsnapshot = 0;
|
my $nextsnapshot = 0;
|
||||||
|
|
@ -599,8 +625,8 @@ sub syncdataset {
|
||||||
if ($nextsnapshot) {
|
if ($nextsnapshot) {
|
||||||
my $nextsnapshotescaped = escapeshellparam($nextsnapshot);
|
my $nextsnapshotescaped = escapeshellparam($nextsnapshot);
|
||||||
|
|
||||||
my $sendcmd = "$sourcesudocmd $zfscmd send -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$nextsnapshotescaped";
|
my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$nextsnapshotescaped";
|
||||||
my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped";
|
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped";
|
||||||
my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot);
|
my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot);
|
||||||
|
|
||||||
if (!$quiet) { print "Sending incremental $sourcefs#$bookmarkescaped ... $nextsnapshot (~ $disp_pvsize):\n"; }
|
if (!$quiet) { print "Sending incremental $sourcefs#$bookmarkescaped ... $nextsnapshot (~ $disp_pvsize):\n"; }
|
||||||
|
|
@ -614,8 +640,8 @@ sub syncdataset {
|
||||||
$matchingsnap = $nextsnapshot;
|
$matchingsnap = $nextsnapshot;
|
||||||
$matchingsnapescaped = escapeshellparam($matchingsnap);
|
$matchingsnapescaped = escapeshellparam($matchingsnap);
|
||||||
} else {
|
} else {
|
||||||
my $sendcmd = "$sourcesudocmd $zfscmd send -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$newsyncsnapescaped";
|
my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions -i $sourcefsescaped#$bookmarkescaped $sourcefsescaped\@$newsyncsnapescaped";
|
||||||
my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped";
|
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped";
|
||||||
my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot);
|
my $synccmd = buildsynccmd($sendcmd,$recvcmd,$pvsize,$sourceisroot,$targetisroot);
|
||||||
|
|
||||||
if (!$quiet) { print "Sending incremental $sourcefs#$bookmarkescaped ... $newsyncsnap (~ $disp_pvsize):\n"; }
|
if (!$quiet) { print "Sending incremental $sourcefs#$bookmarkescaped ... $newsyncsnap (~ $disp_pvsize):\n"; }
|
||||||
|
|
@ -631,8 +657,8 @@ sub syncdataset {
|
||||||
# do a normal replication if bookmarks aren't used or if previous
|
# do a normal replication if bookmarks aren't used or if previous
|
||||||
# bookmark replication was only done to the next oldest snapshot
|
# bookmark replication was only done to the next oldest snapshot
|
||||||
if (!$bookmark || $nextsnapshot) {
|
if (!$bookmark || $nextsnapshot) {
|
||||||
my $sendcmd = "$sourcesudocmd $zfscmd send $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped";
|
my $sendcmd = "$sourcesudocmd $zfscmd send $sendoptions $args{'streamarg'} $sourcefsescaped\@$matchingsnapescaped $sourcefsescaped\@$newsyncsnapescaped";
|
||||||
my $recvcmd = "$targetsudocmd $zfscmd receive $receiveextraargs -F $targetfsescaped";
|
my $recvcmd = "$targetsudocmd $zfscmd receive $recvoptions $receiveextraargs $forcedrecv $targetfsescaped";
|
||||||
my $pvsize = getsendsize($sourcehost,"$sourcefs\@$matchingsnap","$sourcefs\@$newsyncsnap",$sourceisroot);
|
my $pvsize = getsendsize($sourcehost,"$sourcefs\@$matchingsnap","$sourcefs\@$newsyncsnap",$sourceisroot);
|
||||||
my $disp_pvsize = readablebytes($pvsize);
|
my $disp_pvsize = readablebytes($pvsize);
|
||||||
if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; }
|
if ($pvsize == 0) { $disp_pvsize = "UNKNOWN"; }
|
||||||
|
|
@ -1307,7 +1333,7 @@ sub getbookmarks() {
|
||||||
close FH or $error = 1;
|
close FH or $error = 1;
|
||||||
|
|
||||||
if ($error == 1) {
|
if ($error == 1) {
|
||||||
if ($rawbookmarks[0] =~ /invalid type/) {
|
if ($rawbookmarks[0] =~ /invalid type/ or $rawbookmarks[0] =~ /operation not applicable to datasets of this type/) {
|
||||||
# no support for zfs bookmarks, return empty hash
|
# no support for zfs bookmarks, return empty hash
|
||||||
return %bookmarks;
|
return %bookmarks;
|
||||||
}
|
}
|
||||||
|
|
@ -1375,7 +1401,7 @@ sub getsendsize {
|
||||||
$snaps = "-t $receivetoken";
|
$snaps = "-t $receivetoken";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $getsendsizecmd = "$sourcessh $mysudocmd $zfscmd send -nP $snaps";
|
my $getsendsizecmd = "$sourcessh $mysudocmd $zfscmd send $sendoptions -nP $snaps";
|
||||||
if ($debug) { print "DEBUG: getting estimated transfer size from source $sourcehost using \"$getsendsizecmd 2>&1 |\"...\n"; }
|
if ($debug) { print "DEBUG: getting estimated transfer size from source $sourcehost using \"$getsendsizecmd 2>&1 |\"...\n"; }
|
||||||
|
|
||||||
open FH, "$getsendsizecmd 2>&1 |";
|
open FH, "$getsendsizecmd 2>&1 |";
|
||||||
|
|
@ -1480,10 +1506,14 @@ Options:
|
||||||
--skip-parent Skips syncing of the parent dataset. Does nothing without '--recursive' option.
|
--skip-parent Skips syncing of the parent dataset. Does nothing without '--recursive' option.
|
||||||
--source-bwlimit=<limit k|m|g|t> Bandwidth limit on the source transfer
|
--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
|
--target-bwlimit=<limit k|m|g|t> Bandwidth limit on the target transfer
|
||||||
|
--mbuffer-size=VALUE Specify the mbuffer size (default: 16M), please refer to mbuffer(1) manual page.
|
||||||
--no-stream Replicates using newest snapshot instead of intermediates
|
--no-stream Replicates using newest snapshot instead of intermediates
|
||||||
--no-sync-snap Does not create new snapshot, only transfers existing
|
--no-sync-snap Does not create new snapshot, only transfers existing
|
||||||
|
--no-clone-rollback Does not rollback clones on target
|
||||||
|
--no-rollback Does not rollback clones or snapshots on target (it probably requires a readonly target)
|
||||||
--exclude=REGEX Exclude specific datasets which match the given regular expression. Can be specified multiple times
|
--exclude=REGEX Exclude specific datasets which match the given regular expression. Can be specified multiple times
|
||||||
|
--sendoptions=OPTIONS DANGER: Inject OPTIONS into zfs send, e.g. syncoid --sendoptions="-Lce" sets zfs send -Lce ...
|
||||||
|
--recvoptions=OPTIONS DANGER: Inject OPTIONS into zfs received, e.g. syncoid --recvoptions="-x property" sets zfs receive -x property ...
|
||||||
--sshkey=FILE Specifies a ssh public key to use to connect
|
--sshkey=FILE Specifies a ssh public key to use to connect
|
||||||
--sshport=PORT Connects to remote on a particular port
|
--sshport=PORT Connects to remote on a particular port
|
||||||
--sshcipher|c=CIPHER Passes CIPHER to ssh to use a particular cipher set
|
--sshcipher|c=CIPHER Passes CIPHER to ssh to use a particular cipher set
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ function cleanUp {
|
||||||
trap cleanUp EXIT
|
trap cleanUp EXIT
|
||||||
|
|
||||||
while [ $timestamp -le $END ]; do
|
while [ $timestamp -le $END ]; do
|
||||||
date --utc --set @$timestamp; date; "${SANOID}" --cron --verbose
|
setdate $timestamp; date; "${SANOID}" --cron --verbose
|
||||||
timestamp=$((timestamp+3600))
|
timestamp=$((timestamp+3600))
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ function cleanUp {
|
||||||
trap cleanUp EXIT
|
trap cleanUp EXIT
|
||||||
|
|
||||||
while [ $timestamp -le $END ]; do
|
while [ $timestamp -le $END ]; do
|
||||||
date --utc --set @$timestamp; date; "${SANOID}" --cron --verbose
|
setdate $timestamp; date; "${SANOID}" --cron --verbose
|
||||||
timestamp=$((timestamp+900))
|
timestamp=$((timestamp+900))
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
unamestr="$(uname)"
|
||||||
|
|
||||||
function setup {
|
function setup {
|
||||||
export LANG=C
|
export LANG=C
|
||||||
export LANGUAGE=C
|
export LANGUAGE=C
|
||||||
|
|
@ -58,7 +60,11 @@ function saveSnapshotList {
|
||||||
zfs list -t snapshot -o name -Hr "${POOL_NAME}" | sort > "${RESULT}"
|
zfs list -t snapshot -o name -Hr "${POOL_NAME}" | sort > "${RESULT}"
|
||||||
|
|
||||||
# clear the seconds for comparing
|
# clear the seconds for comparing
|
||||||
sed -i 's/\(autosnap_[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:\)[0-9][0-9]_/\100_/g' "${RESULT}"
|
if [ "$unamestr" == 'FreeBSD' ]; then
|
||||||
|
sed -i '' 's/\(autosnap_[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:\)[0-9][0-9]_/\100_/g' "${RESULT}"
|
||||||
|
else
|
||||||
|
sed -i 's/\(autosnap_[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:\)[0-9][0-9]_/\100_/g' "${RESULT}"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifySnapshotList {
|
function verifySnapshotList {
|
||||||
|
|
@ -90,7 +96,7 @@ function verifySnapshotList {
|
||||||
message="${message}monthly snapshot count is wrong: ${monthly_count}\n"
|
message="${message}monthly snapshot count is wrong: ${monthly_count}\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
checksum=$(sha256sum "${RESULT}" | cut -d' ' -f1)
|
checksum=$(shasum -a 256 "${RESULT}" | cut -d' ' -f1)
|
||||||
if [ "${checksum}" != "${CHECKSUM}" ]; then
|
if [ "${checksum}" != "${CHECKSUM}" ]; then
|
||||||
failed=1
|
failed=1
|
||||||
message="${message}result checksum mismatch\n"
|
message="${message}result checksum mismatch\n"
|
||||||
|
|
@ -105,3 +111,13 @@ function verifySnapshotList {
|
||||||
|
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setdate {
|
||||||
|
TIMESTAMP="$1"
|
||||||
|
|
||||||
|
if [ "$unamestr" == 'FreeBSD' ]; then
|
||||||
|
date -u -f '%s' "${TIMESTAMP}"
|
||||||
|
else
|
||||||
|
date --utc --set "@${TIMESTAMP}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ for test in */; do
|
||||||
|
|
||||||
echo -n "Running test ${testName} ... "
|
echo -n "Running test ${testName} ... "
|
||||||
cd "${test}"
|
cd "${test}"
|
||||||
echo | bash run.sh > "${LOGFILE}" 2>&1
|
echo -n y | bash run.sh > "${LOGFILE}" 2>&1
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "[PASS]"
|
echo "[PASS]"
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@ zfs snapshot "${POOL_NAME}"/src@snap5
|
||||||
../../../syncoid --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1
|
../../../syncoid --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1
|
||||||
|
|
||||||
# verify
|
# verify
|
||||||
output=$(zfs list -t snapshot -r "${POOL_NAME}" -H -o name)
|
output=$(zfs list -t snapshot -r -H -o name "${POOL_NAME}")
|
||||||
checksum=$(echo "${output}" | grep -v syncoid_ | sha256sum)
|
checksum=$(echo "${output}" | grep -v syncoid_ | shasum -a 256)
|
||||||
|
|
||||||
if [ "${checksum}" != "${TARGET_CHECKSUM}" ]; then
|
if [ "${checksum}" != "${TARGET_CHECKSUM}" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@ zfs snapshot "${POOL_NAME}"/src@snap5
|
||||||
../../../syncoid --no-stream --no-sync-snap --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1
|
../../../syncoid --no-stream --no-sync-snap --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1
|
||||||
|
|
||||||
# verify
|
# verify
|
||||||
output=$(zfs list -t snapshot -r "${POOL_NAME}" -H -o name)
|
output=$(zfs list -t snapshot -r -H -o name "${POOL_NAME}")
|
||||||
checksum=$(echo "${output}" | sha256sum)
|
checksum=$(echo "${output}" | shasum -a 256)
|
||||||
|
|
||||||
if [ "${checksum}" != "${TARGET_CHECKSUM}" ]; then
|
if [ "${checksum}" != "${TARGET_CHECKSUM}" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ sleep 1
|
||||||
../../../syncoid -r --force-delete --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1
|
../../../syncoid -r --force-delete --debug --compress=none "${POOL_NAME}"/src "${POOL_NAME}"/dst || exit 1
|
||||||
|
|
||||||
# verify
|
# verify
|
||||||
output=$(zfs list -t snapshot -r "${POOL_NAME}" -H -o name | sed 's/@syncoid_.*$'/@syncoid_/)
|
output=$(zfs list -t snapshot -r -H -o name "${POOL_NAME}" | sed 's/@syncoid_.*$'/@syncoid_/)
|
||||||
checksum=$(echo "${output}" | sha256sum)
|
checksum=$(echo "${output}" | shasum -a 256)
|
||||||
|
|
||||||
if [ "${checksum}" != "${TARGET_CHECKSUM}" ]; then
|
if [ "${checksum}" != "${TARGET_CHECKSUM}" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue