ZFS hook support for borgmatic's --dry-run (#261).

This commit is contained in:
Dan Helfman 2024-11-21 11:55:45 -08:00
parent d0c90389fb
commit 1e7f6d9f41
7 changed files with 63 additions and 56 deletions

View File

@ -232,7 +232,8 @@ def run_create(
borgmatic_runtime_directory,
global_arguments.dry_run,
)
source_directories.append(os.path.join(borgmatic_runtime_directory, 'bootstrap'))
if not global_arguments.dry_run:
source_directories.append(os.path.join(borgmatic_runtime_directory, 'bootstrap'))
json_output = borgmatic.borg.create.create_archive(
global_arguments.dry_run,

View File

@ -186,7 +186,8 @@ def dump_data_sources(
)
)
source_directories.append(os.path.join(borgmatic_runtime_directory, 'mariadb_databases'))
if not dry_run:
source_directories.append(os.path.join(borgmatic_runtime_directory, 'mariadb_databases'))
return [process for process in processes if process]

View File

@ -68,7 +68,8 @@ def dump_data_sources(
dump.create_named_pipe_for_dump(dump_filename)
processes.append(execute_command(command, shell=True, run_to_completion=False))
source_directories.append(os.path.join(borgmatic_runtime_directory, 'mongodb_databases'))
if not dry_run:
source_directories.append(os.path.join(borgmatic_runtime_directory, 'mongodb_databases'))
return processes

View File

@ -185,7 +185,8 @@ def dump_data_sources(
)
)
source_directories.append(os.path.join(borgmatic_runtime_directory, 'mysql_databases'))
if not dry_run:
source_directories.append(os.path.join(borgmatic_runtime_directory, 'mysql_databases'))
return [process for process in processes if process]

View File

@ -211,7 +211,8 @@ def dump_data_sources(
)
)
source_directories.append(os.path.join(borgmatic_runtime_directory, 'postgresql_databases'))
if not dry_run:
source_directories.append(os.path.join(borgmatic_runtime_directory, 'postgresql_databases'))
return processes

View File

@ -81,7 +81,8 @@ def dump_data_sources(
dump.create_named_pipe_for_dump(dump_filename)
processes.append(execute_command(command, shell=True, run_to_completion=False))
source_directories.append(os.path.join(borgmatic_runtime_directory, 'sqlite_databases'))
if not dry_run:
source_directories.append(os.path.join(borgmatic_runtime_directory, 'sqlite_databases'))
return processes

View File

@ -37,15 +37,10 @@ def dump_data_sources(
Return an empty sequence, since there are no ongoing dump processes.
If this is a dry run or ZFS isn't enabled, then don't actually snapshot anything.
'''
# TODO: Check for ZFS enabled in config and skip accordingly.
dry_run_label = ' (dry run; not actually dumping anything)' if dry_run else ''
dry_run_label = ' (dry run; not actually snapshotting anything)' if dry_run else ''
logger.info(f'{log_prefix}: Snapshotting ZFS datasets{dry_run_label}')
# TODO: Dry run.
# List ZFS datasets to get their mount points.
zfs_command = hook_config.get('zfs_command', 'zfs')
list_command = (
@ -82,17 +77,18 @@ def dump_data_sources(
for mount_point, dataset_name in requested_mount_point_to_dataset_name.items():
full_snapshot_name = f'{dataset_name}@{snapshot_name}'
logger.debug(f'{log_prefix}: Creating ZFS snapshot {full_snapshot_name}')
logger.debug(f'{log_prefix}: Creating ZFS snapshot {full_snapshot_name}{dry_run_label}')
borgmatic.execute.execute_command(
(
zfs_command,
'snapshot',
'-r',
full_snapshot_name,
),
output_log_level=logging.DEBUG,
)
if not dry_run:
borgmatic.execute.execute_command(
(
zfs_command,
'snapshot',
'-r',
full_snapshot_name,
),
output_log_level=logging.DEBUG,
)
# Mount the snapshot into a particular named temporary directory so that the snapshot ends
# up in the Borg archive at the "original" dataset mount point path.
@ -102,22 +98,24 @@ def dump_data_sources(
'.',
mount_point.lstrip(os.path.sep),
)
logger.debug(f'{log_prefix}: Mounting ZFS snapshot {full_snapshot_name} at {snapshot_path}')
logger.debug(f'{log_prefix}: Mounting ZFS snapshot {full_snapshot_name} at {snapshot_path}{dry_run_label}')
os.makedirs(snapshot_path, mode=0o700, exist_ok=True)
borgmatic.execute.execute_command(
(
hook_config.get('mount_command', 'mount'),
'-t',
'zfs',
f'{dataset_name}@{snapshot_name}',
snapshot_path,
),
output_log_level=logging.DEBUG,
)
if not dry_run:
os.makedirs(snapshot_path, mode=0o700, exist_ok=True)
borgmatic.execute.execute_command(
(
hook_config.get('mount_command', 'mount'),
'-t',
'zfs',
f'{dataset_name}@{snapshot_name}',
snapshot_path,
),
output_log_level=logging.DEBUG,
)
source_directories.remove(mount_point)
source_directories.append(snapshot_path)
if not dry_run:
source_directories.remove(mount_point)
source_directories.append(snapshot_path)
return []
@ -129,7 +127,7 @@ def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_
borgmatic. Use the log prefix in any log entries. If this is a dry run or ZFS isn't enabled,
then don't actually remove anything.
'''
# TODO: Dry run.
dry_run_label = ' (dry run; not actually removing anything)' if dry_run else ''
# Unmount snapshots.
zfs_command = hook_config.get('zfs_command', 'zfs')
@ -162,19 +160,21 @@ def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_
os.path.normpath(borgmatic_runtime_directory),
'zfs_snapshots',
)
logger.debug(f'{log_prefix}: Looking for snapshots in {snapshots_directory}')
logger.debug(f'{log_prefix}: Looking for snapshots to remove in {snapshots_directory}{dry_run_label}')
if os.path.isdir(snapshots_directory):
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}')
borgmatic.execute.execute_command(
(
hook_config.get('umount_command', 'umount'),
snapshot_path,
),
output_log_level=logging.DEBUG,
)
logger.debug(f'{log_prefix}: Unmounting ZFS snapshot at {snapshot_path}{dry_run_label}')
if not dry_run:
borgmatic.execute.execute_command(
(
hook_config.get('umount_command', 'umount'),
snapshot_path,
),
output_log_level=logging.DEBUG,
)
# Destroy snapshots.
list_snapshots_command = (
@ -192,21 +192,22 @@ def remove_data_source_dumps(hook_config, config, log_prefix, borgmatic_runtime_
for line in list_snapshots_output.splitlines():
full_snapshot_name = line.rstrip()
logger.debug(f'{log_prefix}: Destroying ZFS snapshot {full_snapshot_name}')
logger.debug(f'{log_prefix}: Destroying ZFS snapshot {full_snapshot_name}{dry_run_label}')
# Only destroy snapshots that borgmatic actually created!
if not full_snapshot_name.split('@')[-1].startswith(BORGMATIC_SNAPSHOT_PREFIX):
continue
borgmatic.execute.execute_command(
(
zfs_command,
'destroy',
'-r',
full_snapshot_name,
),
output_log_level=logging.DEBUG,
)
if not dry_run:
borgmatic.execute.execute_command(
(
zfs_command,
'destroy',
'-r',
full_snapshot_name,
),
output_log_level=logging.DEBUG,
)
def make_data_source_dump_patterns(hook_config, config, log_prefix, name=None): # pragma: no cover