btrbk: virtual machine setups; use MACHINE_ID for caches; use port in URL

Common virtual machine setups have multiple volume sections with same
host, but distinct port numbers for each machine.

- make caches dependent on MACHINE_ID instead of HOST
- append port number to URL
- add MACHINE_ID to vinfo
- use MACHINE_ID where applicable

This even works if virtual machines share the same btrfs filesystems:
If a equal UUID is found on distinct machines, btr_tree() will return
the already present tree, in order to be consistent after node
injections.
pull/274/head
Axel Burri 2019-04-01 00:32:24 +02:00
parent d8b7988ffa
commit 2f09a9a723
2 changed files with 40 additions and 26 deletions

59
btrbk
View File

@ -257,8 +257,8 @@ my %raw_info_sort = (
); );
my %raw_url_cache; # map URL to (fake) btr_tree node my %raw_url_cache; # map URL to (fake) btr_tree node
my %mountinfo_cache; # map HOST to mount points (sorted descending by file length) my %mountinfo_cache; # map MACHINE_ID to mount points (sorted descending by file length)
my %mount_source_cache; # map HOST:mount_source (aka device) to btr_tree node my %mount_source_cache; # map URL_PREFIX:mount_source (aka device) to btr_tree node
my %uuid_cache; # map UUID to btr_tree node my %uuid_cache; # map UUID to btr_tree node
my %realpath_cache; # map URL to realpath (symlink target) my %realpath_cache; # map URL to realpath (symlink target)
@ -1219,11 +1219,11 @@ sub btrfs_subvolume_delete($@)
return () unless(scalar(@$targets)); return () unless(scalar(@$targets));
# NOTE: rsh and backend command is taken from first target # NOTE: rsh and backend command is taken from first target
my $rsh_host_check = $targets->[0]->{HOST} || ""; my $rsh_machine_check = $targets->[0]->{MACHINE_ID};
my $target_type = $targets->[0]->{node}{TARGET_TYPE} || ""; my $target_type = $targets->[0]->{node}{TARGET_TYPE} || "";
foreach (@$targets) { foreach (@$targets) {
# assert all targets share same HOST # assert all targets share same MACHINE_ID
die if($rsh_host_check ne ($_->{HOST} || "")); die if($rsh_machine_check ne $_->{MACHINE_ID});
# assert all targets share same target type # assert all targets share same target type
die if($target_type && ($_->{node}{TARGET_TYPE} ne $target_type)); die if($target_type && ($_->{node}{TARGET_TYPE} ne $target_type));
} }
@ -1793,7 +1793,7 @@ sub system_list_mountinfo($)
\s(?<super_options>\S+)$ # super options: per super block options \s(?<super_options>\S+)$ # super options: per super block options
/x) /x)
{ {
ERROR "Failed to parse \"$file\" on " . ($vol->{HOST} || "localhost"); ERROR "Failed to parse \"$vol->{URL_PREFIX}$file\"";
DEBUG "Offending line: $_"; DEBUG "Offending line: $_";
return undef; return undef;
} }
@ -1867,13 +1867,12 @@ sub btrfs_mountpoint
return undef unless($realpath); return undef unless($realpath);
# get all mountpoints # get all mountpoints
my $host = $vol->{HOST} || "localhost"; my $mountinfo = $mountinfo_cache{$vol->{MACHINE_ID}};
my $mountinfo = $mountinfo_cache{$host}; TRACE "mountinfo_cache " . ($mountinfo ? "HIT" : "MISS") . ": $vol->{MACHINE_ID}";
TRACE "mountinfo_cache " . ($mountinfo ? "HIT" : "MISS") . ": $host";
unless($mountinfo) { unless($mountinfo) {
$mountinfo = system_list_mountinfo($vol); $mountinfo = system_list_mountinfo($vol);
return undef unless($mountinfo); return undef unless($mountinfo);
$mountinfo_cache{$host} = $mountinfo; $mountinfo_cache{$vol->{MACHINE_ID}} = $mountinfo;
} }
# find longest match # find longest match
@ -1905,7 +1904,7 @@ sub btrfs_mountpoint
} }
DEBUG "autofs mount point found, triggering automount on $mountpoint->{mount_point} for: $vol->{PRINT}"; DEBUG "autofs mount point found, triggering automount on $mountpoint->{mount_point} for: $vol->{PRINT}";
btrfs_subvolume_show(vinfo($vol->{URL_PREFIX} . $mountpoint->{mount_point}, $vol->{CONFIG})); btrfs_subvolume_show(vinfo($vol->{URL_PREFIX} . $mountpoint->{mount_point}, $vol->{CONFIG}));
$mountinfo_cache{$host} = undef; $mountinfo_cache{$vol->{MACHINE_ID}} = undef;
return btrfs_mountpoint($vol, 1); return btrfs_mountpoint($vol, 1);
} }
elsif($mountpoint->{fs_type} ne 'btrfs') { elsif($mountpoint->{fs_type} ne 'btrfs') {
@ -1924,7 +1923,7 @@ sub btrfs_mountpoint
next; next;
} }
unless($mnt->{mount_point} =~ /^$file_match$/) { unless($mnt->{mount_point} =~ /^$file_match$/) {
INFO "Ignoring non-parseable btrfs mountpoint on $host: \"$mnt->{mount_point}\""; INFO "Ignoring non-parseable btrfs mountpoint on $vol->{MACHINE_ID}: \"$mnt->{mount_point}\"";
next; next;
} }
unless($mnt->{MNTOPS}->{subvolid}) { unless($mnt->{MNTOPS}->{subvolid}) {
@ -2136,7 +2135,7 @@ sub btr_tree($$$$)
die unless($vol_root_id >= 5); die unless($vol_root_id >= 5);
# return parsed tree from %mount_source_cache if present # return parsed tree from %mount_source_cache if present
my $host_mount_source = ($vol->{HOST} // "localhost") . ':' . $mount_source; my $host_mount_source = $vol->{URL_PREFIX} . $mount_source; # printed in _fs_path()
my $cached_tree = $mount_source_cache{$host_mount_source}; my $cached_tree = $mount_source_cache{$host_mount_source};
TRACE "mount_source_cache " . ($cached_tree ? "HIT" : "MISS") . ": $host_mount_source"; TRACE "mount_source_cache " . ($cached_tree ? "HIT" : "MISS") . ": $host_mount_source";
if($cached_tree) { if($cached_tree) {
@ -2152,12 +2151,17 @@ sub btr_tree($$$$)
TRACE "btr_tree: processing subvolume list of: $vol->{PRINT}"; TRACE "btr_tree: processing subvolume list of: $vol->{PRINT}";
# return a reference to the cached root if we already know the tree # return a reference to the cached root if we already know the tree,
# (making sure every tree is only stored once). # making sure every tree is only stored once, which is essential
# die if duplicate UUID exist on different file systems (no matter # e.g. when injecting nodes. die if duplicate UUID exist on
# if local or remote). # different file systems (no matter if local or remote).
#
# note: this relies on subvolume UUID's to be "universally unique" # note: this relies on subvolume UUID's to be "universally unique"
# (which is why cloning btrfs filesystems using "dd" is a bad idea) # (which is why cloning btrfs filesystems using "dd" is a bad idea)
#
# note: a better way would be to always compare the UUID of
# subvolid=5. unfortunately this is not possible for filesystems
# created with btrfs-progs < 4.16 (no UUID for subvolid=5).
foreach(@$node_list) { foreach(@$node_list) {
my $node_uuid = $_->{uuid}; my $node_uuid = $_->{uuid};
next unless($node_uuid); next unless($node_uuid);
@ -2166,7 +2170,9 @@ sub btr_tree($$$$)
TRACE "uuid_cache HIT: $node_uuid"; TRACE "uuid_cache HIT: $node_uuid";
$vol_root = $uuid_cache{$node_uuid}->{TREE_ROOT}->{ID_HASH}->{$vol_root_id}; $vol_root = $uuid_cache{$node_uuid}->{TREE_ROOT}->{ID_HASH}->{$vol_root_id};
die "Duplicate UUID on different file systems" unless($vol_root); die "Duplicate UUID on different file systems" unless($vol_root);
INFO "Assuming same filesystem: \"$vol_root->{TREE_ROOT}->{host_mount_source}\", \"$host_mount_source\"";
TRACE "btr_tree: returning already parsed tree at id=$vol_root->{id}"; TRACE "btr_tree: returning already parsed tree at id=$vol_root->{id}";
$mount_source_cache{$host_mount_source} = $vol_root->{TREE_ROOT};
return $vol_root; return $vol_root;
} }
last; # check only first UUID (for performance) last; # check only first UUID (for performance)
@ -2202,7 +2208,12 @@ sub btr_tree($$$$)
$tree_root->{UUID_HASH} = \%uuid_hash; $tree_root->{UUID_HASH} = \%uuid_hash;
$tree_root->{RECEIVED_UUID_HASH} = \%received_uuid_hash; $tree_root->{RECEIVED_UUID_HASH} = \%received_uuid_hash;
$tree_root->{GEN_MAX} = $gen_max; $tree_root->{GEN_MAX} = $gen_max;
$tree_root->{host_mount_source} = $host_mount_source; # unique identifier, e.g. "localhost:/dev/sda1"
# NOTE: host_mount_source is NOT dependent on MACHINE_ID:
# if we return already present tree (see above), the value of
# host_mount_source will still point to the mount_source of the
# first machine.
$tree_root->{host_mount_source} = $host_mount_source; # unique identifier, e.g. "LOCAL:/dev/sda1" or "ssh://hostname[:port]/dev/sda1"
$vol_root = $id{$vol_root_id}; $vol_root = $id{$vol_root_id};
unless($vol_root) { unless($vol_root) {
@ -2358,9 +2369,11 @@ sub vinfo($;$)
if($url_prefix) { if($url_prefix) {
$host = $url_prefix; $host = $url_prefix;
die unless($host =~ s/^ssh:\/\///); die unless($host =~ s/^ssh:\/\///);
$print = "$host:$path";
if($host =~ s/:([1-9][0-9]*)$//) { if($host =~ s/:([1-9][0-9]*)$//) {
$port = $1; $port = $1;
$print = "$host\[$port\]:$path"; # hostname[port]:/path
} else {
$print = "$host:$path"; # hostname:/path
} }
} }
@ -2370,8 +2383,9 @@ sub vinfo($;$)
NAME => $name, NAME => $name,
PATH => $path, PATH => $path,
PRINT => $print, PRINT => $print,
URL => $url_prefix . $path, # ssh://hostname[:port]/path URL => $url_prefix . $path, # ssh://hostname[:port]/path
URL_PREFIX => $url_prefix, # ssh://hostname[:port] (or "" if local) URL_PREFIX => $url_prefix, # ssh://hostname[:port] (or "" if local)
MACHINE_ID => $url_prefix || "LOCAL:", # unique: "LOCAL:" or hostname and port
CONFIG => $config, CONFIG => $config,
} }
} }
@ -2396,6 +2410,7 @@ sub vinfo_child($$;$)
PRINT => "$parent->{PRINT}/$rel_path", PRINT => "$parent->{PRINT}/$rel_path",
URL => "$parent->{URL}/$rel_path", URL => "$parent->{URL}/$rel_path",
URL_PREFIX => $parent->{URL_PREFIX}, URL_PREFIX => $parent->{URL_PREFIX},
MACHINE_ID => $parent->{MACHINE_ID},
SUBVOL_PATH => $rel_path, SUBVOL_PATH => $rel_path,
SUBVOL_DIR => $subvol_dir, # SUBVOL_PATH=SUBVOL_DIR/NAME SUBVOL_DIR => $subvol_dir, # SUBVOL_PATH=SUBVOL_DIR/NAME
CONFIG => $config // $parent->{CONFIG}, CONFIG => $config // $parent->{CONFIG},
@ -2580,7 +2595,7 @@ sub vinfo_init_raw_root($;@)
# create fake btr_tree # create fake btr_tree
$tree_root = { id => 5, $tree_root = { id => 5,
is_root => 1, is_root => 1,
host_mount_source => 'raw_tree@' . $droot->{URL}, # for completeness (this is never used) host_mount_source => $droot->{URL} . '@raw_tree', # for completeness (this is never used)
GEN_MAX => 1, GEN_MAX => 1,
SUBTREE => [], SUBTREE => [],
UUID_HASH => {}, UUID_HASH => {},

View File

@ -87,10 +87,9 @@ below. Accepted formats are:
ssh://<hostname>[:<port>]/<directory> ssh://<hostname>[:<port>]/<directory>
<hostname>:<directory> <hostname>:<directory>
Note that btrbk keeps mountpoint and btrfs-tree information per If you are connecting to virtual machines, consider configuring
'hostname': specifying different 'ssh_port' for the same host, several 'volume' sections for a '<hostname>', with distinct '<port>'
e.g. for several virtual machines listening on same address, will NOT numbers for each machine.
work. If you need this, define alias host names for each vm.
OPTIONS OPTIONS