57 lines
2.3 KiB
Plaintext
57 lines
2.3 KiB
Plaintext
|
#!/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"cd {backup_path} && "
|
||
|
f"{restic_cmd_base} backup .",
|
||
|
shell=True, 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)
|