#!/usr/bin/env python3 import argparse import os import subprocess def get_service_dataset_paths(data_root): return { d: os.path.join(data_root, d) for d in os.listdir(data_root) } def get_last_daily_snapshot_name(dataset_path): dataset = ''.join(["rpool", dataset_path]) snapshots = subprocess.getoutput( f"zfs list -t snapshot -H -r {dataset} -o name -s creation" ) daily_snapshots = filter(lambda s: s.endswith("_daily"), snapshots.split('\n')) return list(daily_snapshots)[-1] if __name__ == "__main__": parser = argparse.ArgumentParser(description="Backup service data using restic") parser.add_argument("--data-root", type=str, required=True, help="Service data root") parser.add_argument("--bucket-endpoint", type=str, required=True, help="S3 bucket endpoint") args = parser.parse_args() snapshots_for_backup = { service: { "dataset_path": service_dataset_path, "snapshot": get_last_daily_snapshot_name(service_dataset_path), } for service, service_dataset_path in get_service_dataset_paths(args.data_root).items() } for service, info in snapshots_for_backup.items(): backup_path = os.path.normpath( os.path.join("/", "mnt", os.path.relpath(info["dataset_path"], "/")) ) snapshot = info["snapshot"] restic_cmd_base = "restic " \ f"--repo s3:https://{args.bucket_endpoint}/the-nine-worlds---{service} " \ "--option s3.storage-class=ONEZONE_IA" print(f"Backing up {service} : {snapshot}") subprocess.run(f"zfs clone -o mountpoint={backup_path} {snapshot} rpool/restic", shell=True, check=True) try: subprocess.run(f"{restic_cmd_base} snapshots || {restic_cmd_base} init", shell=True, check=True) subprocess.run(f"{restic_cmd_base} backup .", shell=True, cwd=backup_path, check=True) subprocess.run(f"{restic_cmd_base} forget --prune --keep-daily 90 --keep-monthly 12", shell=True, check=True) subprocess.run(f"{restic_cmd_base} check", shell=True, check=True) finally: subprocess.run("zfs destroy rpool/restic", shell=True, check=True)