#!/usr/bin/python3 # vim: shiftwidth=4 softtabstop=4 tabstop=4 expandtab """Backup Functionality of xbackup""" import os import sys import subprocess def backup(config): """perform a file-based full system backup""" # generate all necessary paths based on config paths = Filepaths(config) print(paths) # init borg repository (accepting failure on already existing repo) shell("borg init -e none {}".format(paths.borg_repo), panic=False) # run backup command = "borg create -v --stats --compression zstd \\" command += """ {repo}::'{{hostname}}-{{user}}-{{now:%Y-%m-%d_%H:%M:%S}}' \\""".format( repo=paths.borg_repo ) command += "\n {} \\".format(paths.backup_source) for key,value in enumerate(paths.blacklist): command += "\n --exclude '{}'".format(value) if key != len(paths.blacklist) -1: command += " \\" shell(command) class Filepaths(object): """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"] # 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 + "\n" 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("Command '{}'\nfailed with return code {}".format( command, str(return_code) )) sys.exit(return_code) # final message and return print("... done!") return