Browse Source

Improve Btrfs hook factoring/organization (#251).

Dan Helfman 7 months ago
parent
commit
84a0552277
1 changed files with 48 additions and 34 deletions
  1. 48 34
      borgmatic/hooks/data_source/btrfs.py

+ 48 - 34
borgmatic/hooks/data_source/btrfs.py

@@ -18,17 +18,10 @@ def use_streaming(hook_config, config, log_prefix):  # pragma: no cover
     return False
 
 
-def get_subvolumes(btrfs_command, findmnt_command, source_directories=None):
+def get_filesystem_mount_points(findmnt_command):
     '''
-    Given a Btrfs command to run and a sequence of configured source directories, find the
-    intersection between the current Btrfs filesystem and subvolume mount points and the configured
-    borgmatic source directories. The idea is that these are the requested subvolumes to snapshot.
-
-    If the source directories is None, then return all subvolumes.
-
-    Return the result as a sequence of matching subvolume mount points.
+    Given a findmnt command to run, get all top-level Btrfs filesystem mount points.
     '''
-    # Find all top-level Btrfs filesystem mount points.
     findmnt_output = borgmatic.execute.execute_command_and_capture_output(
         (
             findmnt_command,
@@ -38,43 +31,65 @@ def get_subvolumes(btrfs_command, findmnt_command, source_directories=None):
     )
 
     try:
-        filesystem_mount_points = tuple(
+        return tuple(
             line.rstrip().split(' ')[0]
             for line in findmnt_output.splitlines()
         )
     except ValueError:
         raise ValueError('Invalid {findmnt_command} output')
 
-    source_directories_set = set(source_directories or ())
+
+def get_subvolumes_for_filesystem(btrfs_command, filesystem_mount_point):
+    '''
+    Given a Btrfs command to run and a Btrfs filesystem mount point, get the subvolumes for that
+    filesystem.
+    '''
+    btrfs_output = borgmatic.execute.execute_command_and_capture_output(
+        (
+            btrfs_command,
+            'subvolume',
+            'list',
+            filesystem_mount_point,
+        )
+    )
+
+    try:
+        return tuple(
+            subvolume_path
+            for line in btrfs_output.splitlines()
+            for subvolume_subpath in (line.rstrip().split(' ')[-1],)
+            for subvolume_path in (os.path.join(filesystem_mount_point, subvolume_subpath),)
+        )
+    except ValueError:
+        raise ValueError('Invalid {btrfs_command} subvolume list output')
+
+
+def get_subvolumes(btrfs_command, findmnt_command, source_directories=None):
+    '''
+    Given a Btrfs command to run and a sequence of configured source directories, find the
+    intersection between the current Btrfs filesystem and subvolume mount points and the configured
+    borgmatic source directories. The idea is that these are the requested subvolumes to snapshot.
+
+    If the source directories is None, then return all subvolumes.
+
+    Return the result as a sequence of matching subvolume mount points.
+    '''
+    source_directories_lookup = set(source_directories or ())
     subvolumes = []
 
     # For each filesystem mount point, find its subvolumes and match them again the given source
     # directories to find the subvolumes to backup. Also try to match the filesystem mount point
-    # itself.
-    for mount_point in filesystem_mount_points:
-        if source_directories is None or mount_point in source_directories_set:
+    # itself (since it's implicitly a subvolume).
+    for mount_point in get_filesystem_mount_points(findmnt_command):
+        if source_directories is None or mount_point in source_directories_lookup:
             subvolumes.append(mount_point)
 
-        btrfs_output = borgmatic.execute.execute_command_and_capture_output(
-            (
-                btrfs_command,
-                'subvolume',
-                'list',
-                mount_point,
-            )
+        subvolumes.extend(
+            subvolume_path
+            for subvolume_path in get_subvolumes_for_filesystem(btrfs_command, mount_point)
+            if source_directories is None or subvolume_path in source_directories_lookup
         )
 
-        try:
-            subvolumes.extend(
-                subvolume_path
-                for line in btrfs_output.splitlines()
-                for subvolume_subpath in (line.rstrip().split(' ')[-1],)
-                for subvolume_path in (os.path.join(mount_point, subvolume_subpath),)
-                if source_directories is None or subvolume_path in source_directories_set
-            )
-        except ValueError:
-            raise ValueError('Invalid {btrfs_command} subvolume list output')
-
     return tuple(subvolumes)
 
 
@@ -169,10 +184,9 @@ def dump_data_sources(
     # Based on the configured source directories, determine Btrfs subvolumes to backup.
     btrfs_command = hook_config.get('btrfs_command', 'btrfs')
     findmnt_command = hook_config.get('findmnt_command', 'findmnt')
-    requested_subvolume_paths = get_subvolumes(btrfs_command, findmnt_command, source_directories)
 
     # Snapshot each subvolume, rewriting source directories to use their snapshot paths.
-    for subvolume_path in requested_subvolume_paths:
+    for subvolume_path in get_subvolumes(btrfs_command, findmnt_command, source_directories):
         logger.debug(f'{log_prefix}: Creating Btrfs snapshot for {subvolume_path} subvolume')
 
         snapshot_path = make_snapshot_path(subvolume_path)