diff --git a/migrear b/migrear index f5d79167a48009d61ab203c645ebfe37fb4d4b75..865975c64737a41e8768e24915b5d768136fbfa1 100644 --- a/migrear +++ b/migrear @@ -3,12 +3,21 @@ import argparse import os import sys +import time +import random import platform import re import subprocess +HASH_RETRY = 5 + +class HashCollisionException(Exception): + def __init__(self, message="backup fails for hash collides, try changing your hostname."): + super().__init__(message) + + class MethodNotSupportException(Exception): def __init__(self, method, message="unsupport method '{}', available choices {}."): self.methods_supported = ["nfs", "local"] @@ -27,6 +36,10 @@ class NotRootPathException(Exception): self.message = message.format(path) super().__init__(self.message) +def p64(n: int) -> str: + table = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_" + s = bin(n)[2:][::-1] + return "".join([table[int(s[i : i + 6][::-1], 2)] for i in range(0, len(s), 6)][::-1]) def check_output(cmd): output = subprocess.check_output(cmd, shell=True) @@ -63,7 +76,22 @@ def recover_preset(): raise TypeError("Can not found backup grubby entry with title=MigReaR-xxxx") -def backup(method, initramfs_path, initramfs_url, layout_path, layout_url, excludes): +def backup(method, initramfs_path, initramfs_url, layout_path, layout_url, host_ip, excludes, hash=None): + if hash is None: + tmp_mountponit = "/tmp/mnt" + if not os.path.exists(tmp_mountponit): + os.makedirs(tmp_mountponit) + check_call("mount -v -t nfs -o rw,noatime %s:%s %s" % (host_ip, layout_path, tmp_mountponit)) + for _ in range(HASH_RETRY): + hash = p64(int(time.time() * 10 ** 7)) + p64(random.randint(0, 1<<12)).rjust(2, '0')[-12:] + if not os.path.exists("%s/%s-%s" % (tmp_mountponit, platform.node(), hash)): + os.makedirs("%s/%s-%s" % (tmp_mountponit, platform.node(), hash)) + check_call("umount " + tmp_mountponit) + break + else: + check_call("umount " + tmp_mountponit) + raise HashCollisionException() + local_cfg = """ OUTPUT=RAMDISK OUTPUT_URL=%s @@ -71,7 +99,9 @@ BACKUP=NETFS BACKUP_URL=%s BACKUP_PROG_EXCLUDE=("\$\{BACKUP_PROG_EXCLUDE[@]\}" '/media' '/var/tmp' '/var/crash' %s '/tmp/*') NETFS_KEEP_OLD_BACKUP_COPY= -""" % (initramfs_url, layout_url, " ".join(excludes)) +NETFS_PREFIX="$HOSTNAME-%s" +RAMDISK_SUFFIX="%s" +""" % (initramfs_url, layout_url, " ".join(excludes), hash, hash) with open("/etc/rear/local.conf", "w") as f: f.write(local_cfg) @@ -91,10 +121,9 @@ NETFS_KEEP_OLD_BACKUP_COPY= # grub vmlinuz = check_output("ls /boot/vmlinuz-$(uname -r)").strip() - vmlinux_ver = vmlinuz.rpartition('/')[-1] - initramfs_file_path = check_output("find %s -name 'initramfs-%s.img'" % (initramfs_path, platform.node())) - command = "grubby --add-kernel %s --initrd %s --title MigReaR-%s " % (vmlinuz, initramfs_file_path, vmlinux_ver) + initramfs_file_path = check_output("find %s -name 'initramfs-%s.img'" % (initramfs_path, hash)) + command = "grubby --add-kernel %s --initrd %s --title MigReaR-%s " % (vmlinuz, initramfs_file_path, hash) if method == "nfs": args = " --args='ro console=tty0 console=ttyS0,115200n8 console=ttyS0 unattended'" elif method == "local": @@ -112,6 +141,7 @@ def parse_args(): parser.add_argument('--path', default='/storage', help="where root layout would be kept, /storage by default.") parser.add_argument('--boot', default=None, help="where initramfs would be kept, same with --path if not given.") parser.add_argument('--recover_preset', default=None, help="set recover") + parser.add_argument('--hash', default=None, help="specific backup hash, if not given, the program will generate one to determine backup files among several machines.") args = parser.parse_args() @@ -147,4 +177,4 @@ if __name__ == "__main__": if args.recover_preset: recover_preset() else: - backup(args.method, args.boot, args.initramfs_path, args.path, args.layout_path, args.excludes) + backup(args.method, args.boot, args.initramfs_path, args.path, args.layout_path, args.url, args.excludes, args.hash)