|
@@ -32,16 +32,17 @@ def get_datasets_to_backup(zfs_command, source_directories):
|
|
|
|
|
|
Return the result as a sequence of (dataset name, mount point) pairs.
|
|
Return the result as a sequence of (dataset name, mount point) pairs.
|
|
'''
|
|
'''
|
|
- list_command = (
|
|
|
|
- zfs_command,
|
|
|
|
- 'list',
|
|
|
|
- '-H',
|
|
|
|
- '-t',
|
|
|
|
- 'filesystem',
|
|
|
|
- '-o',
|
|
|
|
- f'name,mountpoint,{BORGMATIC_USER_PROPERTY}',
|
|
|
|
|
|
+ list_output = borgmatic.execute.execute_command_and_capture_output(
|
|
|
|
+ (
|
|
|
|
+ zfs_command,
|
|
|
|
+ 'list',
|
|
|
|
+ '-H',
|
|
|
|
+ '-t',
|
|
|
|
+ 'filesystem',
|
|
|
|
+ '-o',
|
|
|
|
+ f'name,mountpoint,{BORGMATIC_USER_PROPERTY}',
|
|
|
|
+ )
|
|
)
|
|
)
|
|
- list_output = borgmatic.execute.execute_command_and_capture_output(list_command)
|
|
|
|
source_directories_set = set(source_directories)
|
|
source_directories_set = set(source_directories)
|
|
|
|
|
|
return tuple(
|
|
return tuple(
|
|
@@ -52,6 +53,30 @@ def get_datasets_to_backup(zfs_command, source_directories):
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
+def get_all_datasets(zfs_command):
|
|
|
|
+ '''
|
|
|
|
+ Given a ZFS command to run, return all ZFS datasets as a sequence of (dataset name, mount point)
|
|
|
|
+ pairs.
|
|
|
|
+ '''
|
|
|
|
+ list_output = borgmatic.execute.execute_command_and_capture_output(
|
|
|
|
+ (
|
|
|
|
+ zfs_command,
|
|
|
|
+ 'list',
|
|
|
|
+ '-H',
|
|
|
|
+ '-t',
|
|
|
|
+ 'filesystem',
|
|
|
|
+ '-o',
|
|
|
|
+ f'name,mountpoint',
|
|
|
|
+ )
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ return tuple(
|
|
|
|
+ (dataset_name, mount_point)
|
|
|
|
+ for line in list_output.splitlines()
|
|
|
|
+ for (dataset_name, mount_point) in (line.rstrip().split('\t'),)
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+
|
|
def snapshot_dataset(zfs_command, full_snapshot_name):
|
|
def snapshot_dataset(zfs_command, full_snapshot_name):
|
|
'''
|
|
'''
|
|
Given a ZFS command to run and a snapshot name of the form "dataset@snapshot", create a new ZFS
|
|
Given a ZFS command to run and a snapshot name of the form "dataset@snapshot", create a new ZFS
|
|
@@ -148,6 +173,57 @@ def dump_data_sources(
|
|
return []
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
+def unmount_snapshot(umount_command, snapshot_mount_path):
|
|
|
|
+ '''
|
|
|
|
+ Given a umount command to run and the mount path of a snapshot, unmount it.
|
|
|
|
+ '''
|
|
|
|
+ borgmatic.execute.execute_command(
|
|
|
|
+ (
|
|
|
|
+ umount_command,
|
|
|
|
+ snapshot_mount_path,
|
|
|
|
+ ),
|
|
|
|
+ output_log_level=logging.DEBUG,
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def destroy_snapshot(zfs_command, full_snapshot_name):
|
|
|
|
+ '''
|
|
|
|
+ Given a ZFS command to run and the name of a snapshot in the form "dataset@snapshot", destroy
|
|
|
|
+ it.
|
|
|
|
+ '''
|
|
|
|
+ borgmatic.execute.execute_command(
|
|
|
|
+ (
|
|
|
|
+ zfs_command,
|
|
|
|
+ 'destroy',
|
|
|
|
+ '-r',
|
|
|
|
+ full_snapshot_name,
|
|
|
|
+ ),
|
|
|
|
+ output_log_level=logging.DEBUG,
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+def get_all_snapshots(zfs_command):
|
|
|
|
+ '''
|
|
|
|
+ Given a ZFS command to run, return all ZFS snapshots as a sequence of full snapshot names of the
|
|
|
|
+ form "dataset@snapshot".
|
|
|
|
+ '''
|
|
|
|
+ list_output = borgmatic.execute.execute_command_and_capture_output(
|
|
|
|
+ (
|
|
|
|
+ zfs_command,
|
|
|
|
+ 'list',
|
|
|
|
+ '-H',
|
|
|
|
+ '-t',
|
|
|
|
+ 'snapshot',
|
|
|
|
+ '-o',
|
|
|
|
+ 'name',
|
|
|
|
+ )
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ return tuple(
|
|
|
|
+ line.rstrip()
|
|
|
|
+ for line in list_output.splitlines()
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+
|
|
def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_directory, dry_run):
|
|
def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_directory, dry_run):
|
|
'''
|
|
'''
|
|
Given a ZFS configuration dict, a configuration dict, a log prefix, the borgmatic runtime
|
|
Given a ZFS configuration dict, a configuration dict, a log prefix, the borgmatic runtime
|
|
@@ -159,17 +235,9 @@ def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_
|
|
|
|
|
|
# Unmount snapshots.
|
|
# Unmount snapshots.
|
|
zfs_command = hook_config.get('zfs_command', 'zfs')
|
|
zfs_command = hook_config.get('zfs_command', 'zfs')
|
|
- list_datasets_command = (
|
|
|
|
- zfs_command,
|
|
|
|
- 'list',
|
|
|
|
- '-H',
|
|
|
|
- '-o',
|
|
|
|
- 'name,mountpoint',
|
|
|
|
- )
|
|
|
|
|
|
+
|
|
try:
|
|
try:
|
|
- list_datasets_output = borgmatic.execute.execute_command_and_capture_output(
|
|
|
|
- list_datasets_command
|
|
|
|
- )
|
|
|
|
|
|
+ datasets = get_all_datasets(zfs_command)
|
|
except FileNotFoundError:
|
|
except FileNotFoundError:
|
|
logger.debug(f'{log_prefix}: Could not find "{zfs_command}" command')
|
|
logger.debug(f'{log_prefix}: Could not find "{zfs_command}" command')
|
|
return
|
|
return
|
|
@@ -177,11 +245,6 @@ def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_
|
|
logger.debug(f'{log_prefix}: {error}')
|
|
logger.debug(f'{log_prefix}: {error}')
|
|
return
|
|
return
|
|
|
|
|
|
- mount_points = tuple(
|
|
|
|
- mount_point
|
|
|
|
- for line in list_datasets_output.splitlines()
|
|
|
|
- for (dataset_name, mount_point) in (line.rstrip().split('\t'),)
|
|
|
|
- )
|
|
|
|
snapshots_glob = os.path.join(
|
|
snapshots_glob = os.path.join(
|
|
borgmatic.config.paths.replace_temporary_subdirectory_with_glob(
|
|
borgmatic.config.paths.replace_temporary_subdirectory_with_glob(
|
|
os.path.normpath(borgmatic_runtime_directory)
|
|
os.path.normpath(borgmatic_runtime_directory)
|
|
@@ -191,6 +254,7 @@ def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_
|
|
logger.debug(
|
|
logger.debug(
|
|
f'{log_prefix}: Looking for snapshots to remove in {snapshots_glob}{dry_run_label}'
|
|
f'{log_prefix}: Looking for snapshots to remove in {snapshots_glob}{dry_run_label}'
|
|
)
|
|
)
|
|
|
|
+ umount_command = hook_config.get('umount_command', 'umount')
|
|
|
|
|
|
for snapshots_directory in glob.glob(snapshots_glob):
|
|
for snapshots_directory in glob.glob(snapshots_glob):
|
|
if not os.path.isdir(snapshots_directory):
|
|
if not os.path.isdir(snapshots_directory):
|
|
@@ -202,37 +266,19 @@ def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_
|
|
# mounted is tough to do in a cross-platform way.
|
|
# mounted is tough to do in a cross-platform way.
|
|
shutil.rmtree(snapshots_directory, ignore_errors=True)
|
|
shutil.rmtree(snapshots_directory, ignore_errors=True)
|
|
|
|
|
|
- for mount_point in mount_points:
|
|
|
|
- snapshot_path = os.path.join(snapshots_directory, mount_point.lstrip(os.path.sep))
|
|
|
|
- logger.debug(f'{log_prefix}: Unmounting ZFS snapshot at {snapshot_path}{dry_run_label}')
|
|
|
|
|
|
+ for (_, mount_point) in datasets:
|
|
|
|
+ snapshot_mount_path = os.path.join(snapshots_directory, mount_point.lstrip(os.path.sep))
|
|
|
|
+ logger.debug(f'{log_prefix}: Unmounting ZFS snapshot at {snapshot_mount_path}{dry_run_label}')
|
|
|
|
|
|
if not dry_run:
|
|
if not dry_run:
|
|
- borgmatic.execute.execute_command(
|
|
|
|
- (
|
|
|
|
- hook_config.get('umount_command', 'umount'),
|
|
|
|
- snapshot_path,
|
|
|
|
- ),
|
|
|
|
- output_log_level=logging.DEBUG,
|
|
|
|
- )
|
|
|
|
|
|
+ unmount_snapshot(umount_command, snapshot_mount_path)
|
|
|
|
|
|
shutil.rmtree(snapshots_directory)
|
|
shutil.rmtree(snapshots_directory)
|
|
|
|
|
|
# Destroy snapshots.
|
|
# Destroy snapshots.
|
|
- list_snapshots_command = (
|
|
|
|
- zfs_command,
|
|
|
|
- 'list',
|
|
|
|
- '-H',
|
|
|
|
- '-t',
|
|
|
|
- 'snapshot',
|
|
|
|
- '-o',
|
|
|
|
- 'name',
|
|
|
|
- )
|
|
|
|
- list_snapshots_output = borgmatic.execute.execute_command_and_capture_output(
|
|
|
|
- list_snapshots_command
|
|
|
|
- )
|
|
|
|
|
|
+ full_snapshot_names = get_all_snapshots(zfs_command)
|
|
|
|
|
|
- for line in list_snapshots_output.splitlines():
|
|
|
|
- full_snapshot_name = line.rstrip()
|
|
|
|
|
|
+ for full_snapshot_name in full_snapshot_names:
|
|
logger.debug(f'{log_prefix}: Destroying ZFS snapshot {full_snapshot_name}{dry_run_label}')
|
|
logger.debug(f'{log_prefix}: Destroying ZFS snapshot {full_snapshot_name}{dry_run_label}')
|
|
|
|
|
|
# Only destroy snapshots that borgmatic actually created!
|
|
# Only destroy snapshots that borgmatic actually created!
|
|
@@ -240,15 +286,7 @@ def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_
|
|
continue
|
|
continue
|
|
|
|
|
|
if not dry_run:
|
|
if not dry_run:
|
|
- borgmatic.execute.execute_command(
|
|
|
|
- (
|
|
|
|
- zfs_command,
|
|
|
|
- 'destroy',
|
|
|
|
- '-r',
|
|
|
|
- full_snapshot_name,
|
|
|
|
- ),
|
|
|
|
- output_log_level=logging.DEBUG,
|
|
|
|
- )
|
|
|
|
|
|
+ destroy_snapshot(zfs_command, full_snapshot_name)
|
|
|
|
|
|
|
|
|
|
def make_data_source_dump_patterns(hook_config, config, log_prefix, name=None): # pragma: no cover
|
|
def make_data_source_dump_patterns(hook_config, config, log_prefix, name=None): # pragma: no cover
|