Browse Source

Warn if Btrfs is configured but there are no Btrfs subvolumes detected (#251).

Dan Helfman 7 months ago
parent
commit
37efaeae88

+ 6 - 2
borgmatic/hooks/data_source/btrfs.py

@@ -171,14 +171,18 @@ def dump_data_sources(
     If this is a dry run, then don't actually snapshot anything.
     If this is a dry run, then don't actually snapshot anything.
     '''
     '''
     dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
     dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
-    logger.info(f'{log_prefix}: Snapshotting Btrfs datasets{dry_run_label}')
+    logger.info(f'{log_prefix}: Snapshotting Btrfs subvolumes{dry_run_label}')
 
 
     # Based on the configured source directories, determine Btrfs subvolumes to backup.
     # Based on the configured source directories, determine Btrfs subvolumes to backup.
     btrfs_command = hook_config.get('btrfs_command', 'btrfs')
     btrfs_command = hook_config.get('btrfs_command', 'btrfs')
     findmnt_command = hook_config.get('findmnt_command', 'findmnt')
     findmnt_command = hook_config.get('findmnt_command', 'findmnt')
+    subvolumes = get_subvolumes(btrfs_command, findmnt_command, source_directories)
+
+    if not subvolumes:
+        logger.warning(f'{log_prefix}: No Btrfs subvolumes found to snapshot{dry_run_label}')
 
 
     # Snapshot each subvolume, rewriting source directories to use their snapshot paths.
     # Snapshot each subvolume, rewriting source directories to use their snapshot paths.
-    for subvolume_path in get_subvolumes(btrfs_command, findmnt_command, source_directories):
+    for subvolume_path in subvolumes:
         logger.debug(f'{log_prefix}: Creating Btrfs snapshot for {subvolume_path} subvolume')
         logger.debug(f'{log_prefix}: Creating Btrfs snapshot for {subvolume_path} subvolume')
 
 
         snapshot_path = make_snapshot_path(subvolume_path)
         snapshot_path = make_snapshot_path(subvolume_path)

+ 3 - 0
borgmatic/hooks/data_source/zfs.py

@@ -148,6 +148,9 @@ def dump_data_sources(
     # Snapshot each dataset, rewriting source directories to use the snapshot paths.
     # Snapshot each dataset, rewriting source directories to use the snapshot paths.
     snapshot_name = f'{BORGMATIC_SNAPSHOT_PREFIX}{os.getpid()}'
     snapshot_name = f'{BORGMATIC_SNAPSHOT_PREFIX}{os.getpid()}'
 
 
+    if not requested_datasets:
+        logger.warning(f'{log_prefix}: No ZFS datasets found to snapshot{dry_run_label}')
+
     for dataset_name, mount_point in requested_datasets:
     for dataset_name, mount_point in requested_datasets:
         full_snapshot_name = f'{dataset_name}@{snapshot_name}'
         full_snapshot_name = f'{dataset_name}@{snapshot_name}'
         logger.debug(f'{log_prefix}: Creating ZFS snapshot {full_snapshot_name}{dry_run_label}')
         logger.debug(f'{log_prefix}: Creating ZFS snapshot {full_snapshot_name}{dry_run_label}')

+ 23 - 0
tests/unit/hooks/data_source/test_zfs.py

@@ -97,6 +97,29 @@ def test_dump_data_sources_snapshots_and_mounts_and_updates_source_directories()
     assert source_directories == [snapshot_mount_path]
     assert source_directories == [snapshot_mount_path]
 
 
 
 
+def test_dump_data_sources_snapshots_with_no_datasets_skips_snapshots():
+    flexmock(module).should_receive('get_datasets_to_backup').and_return(())
+    flexmock(module.os).should_receive('getpid').and_return(1234)
+    flexmock(module).should_receive('snapshot_dataset').never()
+    flexmock(module).should_receive('mount_snapshot').never()
+    source_directories = ['/mnt/dataset']
+
+    assert (
+        module.dump_data_sources(
+            hook_config={},
+            config={'source_directories': '/mnt/dataset', 'zfs': {}},
+            log_prefix='test',
+            config_paths=('test.yaml',),
+            borgmatic_runtime_directory='/run/borgmatic',
+            source_directories=source_directories,
+            dry_run=False,
+        )
+        == []
+    )
+
+    assert source_directories == ['/mnt/dataset']
+
+
 def test_dump_data_sources_uses_custom_commands():
 def test_dump_data_sources_uses_custom_commands():
     flexmock(module).should_receive('get_datasets_to_backup').and_return(
     flexmock(module).should_receive('get_datasets_to_backup').and_return(
         (('dataset', '/mnt/dataset'),)
         (('dataset', '/mnt/dataset'),)