mirror of https://github.com/digint/btrbk
btrbk: rename action "clone" to "archive" (should have been like this from the beginning)
parent
4bd68a2e35
commit
de05b99757
100
btrbk
100
btrbk
|
@ -245,22 +245,22 @@ sub HELP_MESSAGE
|
|||
print STDERR " --progress show progress bar on send-receive operation\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "commands:\n";
|
||||
print STDERR " run perform backup operations as defined in the config file\n";
|
||||
print STDERR " dryrun don't run btrfs commands; show what would be executed\n";
|
||||
print STDERR " stats print snapshot/backup statistics\n";
|
||||
print STDERR " list <subcommand> available subcommands are:\n";
|
||||
print STDERR " backups all backups and corresponding snapshots\n";
|
||||
print STDERR " snapshots all snapshots and corresponding backups\n";
|
||||
print STDERR " latest most recent snapshots and backups\n";
|
||||
print STDERR " config configured source/snapshot/target relations\n";
|
||||
print STDERR " source configured source/snapshot relations\n";
|
||||
print STDERR " volume configured volume sections\n";
|
||||
print STDERR " target configured targets\n";
|
||||
print STDERR " clean delete incomplete (garbled) backups\n";
|
||||
print STDERR " clone <src> <dst> recursively copy all subvolumes (experimental)\n";
|
||||
print STDERR " usage print filesystem usage\n";
|
||||
print STDERR " origin <subvol> print origin information for subvolume\n";
|
||||
print STDERR " diff <from> <to> shows new files since subvolume <from> for subvolume <to>\n";
|
||||
print STDERR " run perform backup operations as defined in the config\n";
|
||||
print STDERR " dryrun don't run btrfs commands; show what would be executed\n";
|
||||
print STDERR " stats print snapshot/backup statistics\n";
|
||||
print STDERR " list <subcommand> available subcommands are:\n";
|
||||
print STDERR " backups all backups and corresponding snapshots\n";
|
||||
print STDERR " snapshots all snapshots and corresponding backups\n";
|
||||
print STDERR " latest most recent snapshots and backups\n";
|
||||
print STDERR " config configured source/snapshot/target relations\n";
|
||||
print STDERR " source configured source/snapshot relations\n";
|
||||
print STDERR " volume configured volume sections\n";
|
||||
print STDERR " target configured targets\n";
|
||||
print STDERR " clean delete incomplete (garbled) backups\n";
|
||||
print STDERR " archive <src> <dst> recursively copy all subvolumes (experimental)\n";
|
||||
print STDERR " usage print filesystem usage\n";
|
||||
print STDERR " origin <subvol> print origin information for subvolume\n";
|
||||
print STDERR " diff <from> <to> shows new files between related subvolumes\n";
|
||||
print STDERR "\n";
|
||||
print STDERR "For additional information, see $PROJECT_HOME\n";
|
||||
}
|
||||
|
@ -2634,7 +2634,7 @@ sub macro_delete($$$$$;@)
|
|||
}
|
||||
|
||||
|
||||
sub macro_clone_target($$$;$)
|
||||
sub macro_archive_target($$$;$)
|
||||
{
|
||||
my $sroot = shift || die;
|
||||
my $droot = shift || die;
|
||||
|
@ -3181,7 +3181,7 @@ MAIN:
|
|||
WARN 'Found option "--progress", but "pv" is not present: (please install "pv")';
|
||||
$show_progress = 0;
|
||||
}
|
||||
my ($action_run, $action_usage, $action_resolve, $action_diff, $action_origin, $action_config_print, $action_list, $action_clean, $action_clone);
|
||||
my ($action_run, $action_usage, $action_resolve, $action_diff, $action_origin, $action_config_print, $action_list, $action_clean, $action_archive);
|
||||
my @filter_args;
|
||||
my $args_allow_group = 1;
|
||||
my $args_expected_min = 0;
|
||||
|
@ -3196,8 +3196,8 @@ MAIN:
|
|||
$action_clean = 1;
|
||||
@filter_args = @ARGV;
|
||||
}
|
||||
elsif ($command eq "clone") {
|
||||
$action_clone = 1;
|
||||
elsif ($command eq "archive") {
|
||||
$action_archive = 1;
|
||||
$args_expected_min = $args_expected_max = 2;
|
||||
$args_allow_group = 0;
|
||||
@filter_args = @ARGV;
|
||||
|
@ -3412,10 +3412,10 @@ MAIN:
|
|||
}
|
||||
|
||||
|
||||
if($action_clone)
|
||||
if($action_archive)
|
||||
{
|
||||
#
|
||||
# clone (archive) tree
|
||||
# archive (clone) tree
|
||||
#
|
||||
# NOTE: This is intended to work without a config file! The only
|
||||
# thing used from the configuration is the SSH and transaction log
|
||||
|
@ -3429,9 +3429,9 @@ MAIN:
|
|||
# FIXME: add command line options for preserve logic
|
||||
$config->{SUBSECTION} = []; # clear configured subsections, we build them dynamically
|
||||
|
||||
my $clone_src_root = vinfo($src_url, $config);
|
||||
unless(vinfo_init_root($clone_src_root, resolve_subdir => 1)) {
|
||||
ERROR "Failed to fetch subvolume detail for '$clone_src_root->{PRINT}'" . ($err ? ": $err" : "");
|
||||
my $src_root = vinfo($src_url, $config);
|
||||
unless(vinfo_init_root($src_root, resolve_subdir => 1)) {
|
||||
ERROR "Failed to fetch subvolume detail for '$src_root->{PRINT}'" . ($err ? ": $err" : "");
|
||||
exit 1;
|
||||
}
|
||||
my $target_root = vinfo($target_url, $config);
|
||||
|
@ -3441,7 +3441,7 @@ MAIN:
|
|||
}
|
||||
|
||||
my %name_uniq;
|
||||
my @subvol_list = @{vinfo_subvol_list($clone_src_root)};
|
||||
my @subvol_list = @{vinfo_subvol_list($src_root)};
|
||||
my @sorted = sort { ($a->{subtree_depth} <=> $b->{subtree_depth}) || ($a->{SUBVOL_DIR} cmp $b->{SUBVOL_DIR}) } @subvol_list;
|
||||
foreach my $vol (@sorted) {
|
||||
next unless($vol->{node}{readonly});
|
||||
|
@ -3455,34 +3455,34 @@ MAIN:
|
|||
$name_uniq{"$subvol_dir/$snapshot_name"} = 1;
|
||||
my $droot_url = $target_url . ($subvol_dir eq "" ? "" : "/$subvol_dir");
|
||||
my $sroot_url = $src_url . ($subvol_dir eq "" ? "" : "/$subvol_dir");
|
||||
my $config_clone_src = { CONTEXT => "clone_source",
|
||||
PARENT => $config,
|
||||
url => $sroot_url, # ABORTED() needs this
|
||||
snapshot_name => $snapshot_name,
|
||||
};
|
||||
my $config_target = { CONTEXT => "target",
|
||||
PARENT => $config_clone_src,
|
||||
target_type => "send-receive", # macro_send_receive checks this
|
||||
url => $droot_url, # ABORTED() needs this
|
||||
};
|
||||
$config_clone_src->{SUBSECTION} = [ $config_target ];
|
||||
push(@{$config->{SUBSECTION}}, $config_clone_src);
|
||||
my $config_sroot = { CONTEXT => "archive_source",
|
||||
PARENT => $config,
|
||||
url => $sroot_url, # ABORTED() needs this
|
||||
snapshot_name => $snapshot_name,
|
||||
};
|
||||
my $config_droot = { CONTEXT => "target",
|
||||
PARENT => $config_sroot,
|
||||
target_type => "send-receive", # macro_send_receive checks this
|
||||
url => $droot_url, # ABORTED() needs this
|
||||
};
|
||||
$config_sroot->{SUBSECTION} = [ $config_droot ];
|
||||
push(@{$config->{SUBSECTION}}, $config_sroot);
|
||||
|
||||
my $sroot = vinfo($sroot_url, $config_clone_src);
|
||||
vinfo_assign_config($sroot, $config_clone_src);
|
||||
my $sroot = vinfo($sroot_url, $config_sroot);
|
||||
vinfo_assign_config($sroot, $config_sroot);
|
||||
unless(vinfo_init_root($sroot, resolve_subdir => 1)) {
|
||||
ABORTED($sroot, "Failed to fetch subvolume detail" . ($err ? ": $err" : ""));
|
||||
WARN "Skipping clone source \"$sroot->{PRINT}\": $abrt";
|
||||
WARN "Skipping archive source \"$sroot->{PRINT}\": $abrt";
|
||||
next;
|
||||
}
|
||||
|
||||
my $droot = vinfo($droot_url, $config_target);
|
||||
vinfo_assign_config($droot, $config_target);
|
||||
my $droot = vinfo($droot_url, $config_droot);
|
||||
vinfo_assign_config($droot, $config_droot);
|
||||
unless(vinfo_init_root($droot, resolve_subdir => 1)) {
|
||||
DEBUG("Failed to fetch subvolume detail" . ($err ? ": $err" : ""));
|
||||
unless(system_mkdir($droot)) {
|
||||
ABORTED($droot, "Failed to create directory: $droot->{PRINT}/");
|
||||
WARN "Skipping clone target \"$droot->{PRINT}\": $abrt";
|
||||
WARN "Skipping archive target \"$droot->{PRINT}\": $abrt";
|
||||
next;
|
||||
}
|
||||
if($dryrun) {
|
||||
|
@ -3494,7 +3494,7 @@ MAIN:
|
|||
# after directory is created, try to init again
|
||||
unless(vinfo_init_root($droot, resolve_subdir => 1)) {
|
||||
ABORTED($droot, "Failed to fetch subvolume detail" . ($err ? ": $err" : ""));
|
||||
WARN "Skipping clone target \"$droot->{PRINT}\": $abrt";
|
||||
WARN "Skipping archive target \"$droot->{PRINT}\": $abrt";
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
@ -3506,11 +3506,11 @@ MAIN:
|
|||
}
|
||||
|
||||
my $schedule_results = [];
|
||||
foreach my $sroot (vinfo_subsection($config, 'clone_source')) {
|
||||
foreach my $sroot (vinfo_subsection($config, 'archive_source')) {
|
||||
foreach my $droot (vinfo_subsection($sroot, 'target')) {
|
||||
my $snapshot_name = config_key($droot, "snapshot_name") // die;
|
||||
INFO "Archiving subvolumes: $sroot->{PRINT}/${snapshot_name}.*";
|
||||
macro_clone_target($sroot, $droot, $snapshot_name, { results => $schedule_results });
|
||||
macro_archive_target($sroot, $droot, $snapshot_name, { results => $schedule_results });
|
||||
if(ABORTED($droot)) {
|
||||
# also abort $sroot
|
||||
ABORTED($sroot, "At least one target aborted");
|
||||
|
@ -3536,7 +3536,7 @@ MAIN:
|
|||
# print scheduling results
|
||||
if($print_schedule) {
|
||||
my @data = map { { %$_, vinfo_prefixed_keys("", $_->{value}) }; } @$schedule_results;
|
||||
print_formatted("schedule", \@data, title => "CLONE SCHEDULE");
|
||||
print_formatted("schedule", \@data, title => "ARCHIVE SCHEDULE");
|
||||
print "\n";
|
||||
}
|
||||
|
||||
|
@ -3546,7 +3546,7 @@ MAIN:
|
|||
{
|
||||
my @unrecoverable;
|
||||
my @out;
|
||||
foreach my $sroot (vinfo_subsection($config, 'clone_source')) {
|
||||
foreach my $sroot (vinfo_subsection($config, 'archive_source')) {
|
||||
foreach my $droot (vinfo_subsection($sroot, 'target', 1)) {
|
||||
my @subvol_out;
|
||||
foreach(@{$droot->{SUBVOL_RECEIVED} // []}) {
|
||||
|
@ -3567,7 +3567,7 @@ MAIN:
|
|||
}
|
||||
}
|
||||
|
||||
print_header(title => "Clone Summary",
|
||||
print_header(title => "Archive Summary",
|
||||
time => $start_time,
|
||||
legend => [
|
||||
"--- deleted subvolume",
|
||||
|
|
47
doc/FAQ.md
47
doc/FAQ.md
|
@ -199,7 +199,23 @@ data physically, either to the datacenter or to your safe in the
|
|||
basement.
|
||||
|
||||
|
||||
### Answer 1: Use external storage as "stream-fifo"
|
||||
### Answer 1: Use "btrbk archive"
|
||||
|
||||
A robust approach is to use external disks as archives (secondary
|
||||
backups), and regularly run "btrbk archive" on them. As a nice side
|
||||
effect, this also detects possible read-errors on your backup targets
|
||||
(Note that a "btrfs scrub" is still more effective for that purpose).
|
||||
|
||||
See **btrbk archive** command in [btrbk(1)] for more details.
|
||||
|
||||
**Note that kernels >=4.1 and <4.4 have a bug when re-sending
|
||||
subvolumes**, make sure you run a recent/patched kernel or step 3 will
|
||||
fail. Read
|
||||
[this thread on gmane](http://thread.gmane.org/gmane.comp.file-systems.btrfs/48798)
|
||||
(the patch provided is confirmed working on kernels 4.2.x and 4.3.x).
|
||||
|
||||
|
||||
### Answer 2: Use external storage as "stream-fifo"
|
||||
|
||||
This example uses a USB disk as "stream-fifo" for transferring
|
||||
(cloning) of btrfs subvolumes:
|
||||
|
@ -217,32 +233,3 @@ This approach has the advantage that you don't need to reformat your
|
|||
USB disk. This works fine, but be aware that you may run into trouble
|
||||
if a single stream gets corrupted, making all subsequent streams
|
||||
unusable.
|
||||
|
||||
|
||||
### Answer 2: Clone btrfs subvolumes
|
||||
|
||||
A more robust approach is to use the USB disk as secondary backup.
|
||||
This has the advantage that possible errors can already be detected by
|
||||
btrfs on the source side:
|
||||
|
||||
1. Initialize USB disk:
|
||||
|
||||
`mkfs.btrfs /dev/usbX`
|
||||
|
||||
2. For all source subvolumes (in order of generation):
|
||||
|
||||
`btrfs send /source/subvolX -p PARENT | btrfs receive /usbdisk/`
|
||||
|
||||
3. At the target location (in order of generation):
|
||||
|
||||
`btrfs send /usbdisk/subvolX -p PARENT | btrfs receive /target`
|
||||
|
||||
If you simply want to have a clone of the source disk, skip step 3 and
|
||||
store your USB disk in a safe. You will be able to use it for
|
||||
restoring backups later, or *as a replacement for your backup disks*.
|
||||
|
||||
**Note that kernels >=4.1 and <4.4 have a bug when re-sending
|
||||
subvolumes**, make sure you run a recent/patched kernel or step 3 will
|
||||
fail. Read
|
||||
[this thread on gmane](http://thread.gmane.org/gmane.comp.file-systems.btrfs/48798)
|
||||
(the patch provided is confirmed working on kernels 4.2.x and 4.3.x).
|
||||
|
|
19
doc/btrbk.1
19
doc/btrbk.1
|
@ -167,26 +167,27 @@ by the \fBrun\fR command. Use in conjunction with \fI\-l debug\fR to
|
|||
see the btrfs commands that would be executed.
|
||||
.RE
|
||||
.PP
|
||||
.B clone
|
||||
.B archive
|
||||
<source> <target>
|
||||
.I *experimental*
|
||||
.RS 4
|
||||
Recursively copy all subvolumes created by btrbk from <source> to
|
||||
<target> directory, optionally rescheduled using
|
||||
\fIarchive_preserve_*\fR configuration options. Useful for creating
|
||||
extra archive copies (clones) from your backup disks. Note that you
|
||||
can continue using btrbk after swapping your backup disk with the
|
||||
cloned disk.
|
||||
\fIarchive_preserve_*\fR configuration options. Also creates directory
|
||||
tree on <target> (see bugs below). Useful for creating extra archive
|
||||
copies (clones) from your backup disks. Note that you can continue
|
||||
using btrbk after swapping your backup disk with the archive disk.
|
||||
.PP
|
||||
Note that this feature needs a \fBlinux kernel >=4.4\fR to work
|
||||
correctly! Kernels >=4.1 and <4.4 have a bug when re-sending
|
||||
subvolumes (the cloned subvolumes will have incorrect received_uuid,
|
||||
subvolumes (the archived subvolumes will have incorrect received_uuid,
|
||||
see <http://thread.gmane.org/gmane.comp.file-systems.btrfs/48798>), so
|
||||
make sure you run a recent kernel.
|
||||
.PP
|
||||
Known bugs: On the target filesystem, you have to create all
|
||||
directories by hand, or "btrbk clone" will complain: "WARNING:
|
||||
Skipping clone target <...>: Failed to fetch subvolume detail".
|
||||
Known bugs: If you want to use nested subvolumes on the target
|
||||
filesystem, you need to create them by hand (e.g. by running "btrfs
|
||||
subvolume create <target>/dir"). Check the output of --dry-run if
|
||||
unsure.
|
||||
.RE
|
||||
.PP
|
||||
.B stats
|
||||
|
|
Loading…
Reference in New Issue