#!/usr/bin/python3 # vim: shiftwidth=4 softtabstop=4 tabstop=4 expandtab # pylint: disable=too-few-public-methods """Backup Functionality of xbackup""" import os import sys import subprocess POSITIVE_ANSWERS = ["Y", "y", "Yes", "yes", "YES"] def backup(config, scripted): """perform a file-based full system backup""" # generate all necessary paths based on config paths = Filepaths(config) print(paths) # get confirmation from user if not in scripted execution mode if not scripted: answer = input("\nDo you want to continue? [y/N] ") if answer not in POSITIVE_ANSWERS: print("\nTerminated.") sys.exit(0) # init borg repository (accepting failure on already existing repo) shell(f"borg init -e none {paths.borg_repo}", panic=False) # run backup run_backup(paths) class Filepaths(): """A Container Class to hold Filepaths for the Borg Backup Tool""" def __init__(self, config): """The Constructor""" # parse backup source (usually / for system backups) self.backup_source = config["backup_source"] # parse folder for borg repositories self.borg_repos_folder = config["borg_repos_folder"] # path to the borg repository hostname = os.uname()[1] self.borg_repo = os.path.join(self.borg_repos_folder, hostname) # create blacklist of folders to be excluded from the backup self.blacklist = [] for entry in config["blacklist"]: self.blacklist.append(os.path.join(self.backup_source, entry)) # avoid matroska-style backups of backups (yes, it is important) self.blacklist.append(self.borg_repos_folder) def __str__(self): """Converts an Object based on this Class to a String""" retval = "" retval += "\nbackup_source:\n" + self.backup_source + "\n" retval += "\nblacklist:\n" for item in self.blacklist: retval += item + "\n" retval += "\nborg_repo:\n" + self.borg_repo return retval def shell(command, panic=True): """Savely execute a Shell Command - set panic=False to continue with execution on non-zero return code """ # print command print("\nExecuting '" + command + "' ...") # command execution return_code = subprocess.call(command, shell=True) # handle non-zero return code if return_code != 0 and panic: print(f"Command '{command}'\nfailed with return code {return_code}") sys.exit(return_code) # final message and return print("... done!") def run_backup(paths): """Execute the Borg Backup""" # base command command = "borg create -v --stats --compression zstd \\" # set repository path command += \ f"{paths.borg_repo}::'{{hostname}}-{{user}}-{{now:%Y-%m-%d_%H:%M:%S}}' \\" # set backup source command += f"\n {paths.backup_source} \\" # append blacklist elements for key,value in enumerate(paths.blacklist): command += f"\n --exclude '{value}'" if key != len(paths.blacklist) -1: command += " \\" # run command shell(command)