import fcntl import logging import os import os.path import subprocess class StepResult: retcode = None output = None def __init__(self, retcode = 0, output = None): self.retcode = retcode self.output = output class BackupResult: target_results = None par2_result = None def __init__(self): self.target_results = [] self.par2_result = StepResult() @property def all_ok(self): return (all(map(lambda tgtres: tgtres.retcode == 0, self.target_results)) and self.par2_result.retcode == 0) class Repo: """ A single Bup repository into which the data will be backed up, plus a separate directory for extra runtime data. :param str bup_dir: path to the bup repository, defaults to BUP_DIR or ~/.bup :param str data_dir: path to the directory for storing the runtime data, defaults to ~/.local/var/lbup """ bup_dir = None data_dir = None lock_name = 'lock' _logger = None def __init__(self, bup_dir = None, data_dir = None, logger = None): if bup_dir is None: if 'BUP_DIR' in os.environ: bup_dir = os.environ['BUP_DIR'] else: bup_dir = os.path.expanduser('~/.bup') if data_dir is None: data_dir = os.path.expanduser('~/.local/var/lbup/') if logger is None: self._logger = logging.getLogger(self.name) else: self._logger = logger # create the data dir, if it does not already exist os.makedirs(data_dir, 0o700, exist_ok = True) self.bup_dir = bup_dir self.data_dir = data_dir def backup(self, tgts, gen_par2 = True): """ Backup the supplied targets. :param list of Target tgts: List of targets to back up. :param bool gen_par2: Whether to generate par2 recovery information after the backup concludes' """ with open(os.path.join(self.data_dir, self.lock_name), 'w') as lockfile: result = BackupResult() self._logger.debug('Acquiring repository lock') fcntl.lockf(lockfile, fcntl.LOCK_EX) try: for tgt in tgts: self._logger.info('Backing up %s...' % tgt.name) res = tgt.save(self.data_dir) self._logger.info('Backing up %s done' % tgt.name) result.target_results.append(res) if gen_par2: self._logger.info('Generating par2 files...') res = subprocess.run(['bup', 'fsck', '-g'], capture_output = True) self._logger.info('Generating par2 files done') result.par2_result = StepResult(res.returncode, res.stderr + res.stdout) finally: self._logger.debug('Releasing repository lock') fcntl.lockf(lockfile, fcntl.LOCK_UN) self._logger.debug('Backup finished') return result