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 %mountinfo_cache; # map HOST to mount points (sorted descending by file length)
my %mount_source_cache; # map HOST:mount_source (aka device) to btr_tree node
my %mountinfo_cache; # map MACHINE_ID to mount points (sorted descending by file length)
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 %realpath_cache; # map URL to realpath (symlink target)
@ -1219,11 +1219,11 @@ sub btrfs_subvolume_delete($@)
return () unless(scalar(@$targets));
# 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} || "";
foreach (@$targets) {
# assert all targets share same HOST
die if($rsh_host_check ne ($_->{HOST} || ""));
# assert all targets share same MACHINE_ID
die if($rsh_machine_check ne $_->{MACHINE_ID});
# assert all targets share same 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
/x)
{
ERROR "Failed to parse \"$file\" on " . ($vol->{HOST} || "localhost");
ERROR "Failed to parse \"$vol->{URL_PREFIX}$file\"";
DEBUG "Offending line: $_";
return undef;
}
@ -1867,13 +1867,12 @@ sub btrfs_mountpoint
return undef unless($realpath);
# get all mountpoints
my $host = $vol->{HOST} || "localhost";
my $mountinfo = $mountinfo_cache{$host};
TRACE "mountinfo_cache " . ($mountinfo ? "HIT" : "MISS") . ": $host";
my $mountinfo = $mountinfo_cache{$vol->{MACHINE_ID}};
TRACE "mountinfo_cache " . ($mountinfo ? "HIT" : "MISS") . ": $vol->{MACHINE_ID}";
unless($mountinfo) {
$mountinfo = system_list_mountinfo($vol);
return undef unless($mountinfo);
$mountinfo_cache{$host} = $mountinfo;
$mountinfo_cache{$vol->{MACHINE_ID}} = $mountinfo;
}
# find longest match
@ -1905,7 +1904,7 @@ sub btrfs_mountpoint
}
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}));
$mountinfo_cache{$host} = undef;
$mountinfo_cache{$vol->{MACHINE_ID}} = undef;
return btrfs_mountpoint($vol, 1);
}
elsif($mountpoint->{fs_type} ne 'btrfs') {
@ -1924,7 +1923,7 @@ sub btrfs_mountpoint
next;
}
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;
}
unless($mnt->{MNTOPS}->{subvolid}) {
@ -2136,7 +2135,7 @@ sub btr_tree($$$$)
die unless($vol_root_id >= 5);
# 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};
TRACE "mount_source_cache " . ($cached_tree ? "HIT" : "MISS") . ": $host_mount_source";
if($cached_tree) {
@ -2152,12 +2151,17 @@ sub btr_tree($$$$)
TRACE "btr_tree: processing subvolume list of: $vol->{PRINT}";
# return a reference to the cached root if we already know the tree
# (making sure every tree is only stored once).
# die if duplicate UUID exist on different file systems (no matter
# if local or remote).
# return a reference to the cached root if we already know the tree,
# making sure every tree is only stored once, which is essential
# e.g. when injecting nodes. die if duplicate UUID exist on
# different file systems (no matter if local or remote).
#
# note: this relies on subvolume UUID's to be "universally unique"
# (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) {
my $node_uuid = $_->{uuid};
next unless($node_uuid);
@ -2166,7 +2170,9 @@ sub btr_tree($$$$)
TRACE "uuid_cache HIT: $node_uuid";
$vol_root = $uuid_cache{$node_uuid}->{TREE_ROOT}->{ID_HASH}->{$vol_root_id};
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}";
$mount_source_cache{$host_mount_source} = $vol_root->{TREE_ROOT};
return $vol_root;
}
last; # check only first UUID (for performance)
@ -2202,7 +2208,12 @@ sub btr_tree($$$$)
$tree_root->{UUID_HASH} = \%uuid_hash;
$tree_root->{RECEIVED_UUID_HASH} = \%received_uuid_hash;
$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};
unless($vol_root) {
@ -2358,9 +2369,11 @@ sub vinfo($;$)
if($url_prefix) {
$host = $url_prefix;
die unless($host =~ s/^ssh:\/\///);
$print = "$host:$path";
if($host =~ s/:([1-9][0-9]*)$//) {
$port = $1;
$print = "$host\[$port\]:$path"; # hostname[port]:/path
} else {
$print = "$host:$path"; # hostname:/path
}
}
@ -2370,8 +2383,9 @@ sub vinfo($;$)
NAME => $name,
PATH => $path,
PRINT => $print,
URL => $url_prefix . $path, # ssh://hostname[:port]/path
URL_PREFIX => $url_prefix, # ssh://hostname[:port] (or "" if local)
URL => $url_prefix . $path, # ssh://hostname[:port]/path
URL_PREFIX => $url_prefix, # ssh://hostname[:port] (or "" if local)
MACHINE_ID => $url_prefix || "LOCAL:", # unique: "LOCAL:" or hostname and port
CONFIG => $config,
}
}
@ -2396,6 +2410,7 @@ sub vinfo_child($$;$)
PRINT => "$parent->{PRINT}/$rel_path",
URL => "$parent->{URL}/$rel_path",
URL_PREFIX => $parent->{URL_PREFIX},
MACHINE_ID => $parent->{MACHINE_ID},
SUBVOL_PATH => $rel_path,
SUBVOL_DIR => $subvol_dir, # SUBVOL_PATH=SUBVOL_DIR/NAME
CONFIG => $config // $parent->{CONFIG},
@ -2580,7 +2595,7 @@ sub vinfo_init_raw_root($;@)
# create fake btr_tree
$tree_root = { id => 5,
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,
SUBTREE => [],
UUID_HASH => {},

View File

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