import fcntl import logging import os import os.path import subprocess class StepResult: success = None output = None def __init__(self, success = True, output = ''): self.success = success self.output = output class BackupResult: target_results = None par2_result = None def __init__(self): self.target_results = {} self.par2_result = StepResult() 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('%s.%s' % (self.__class__.__name__, bup_dir)) 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, dry_run = False): """ 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) try: res = tgt.save(self.data_dir, dry_run) except Exception as e: self._logger.exception('Exception backing up %s: %s' % (tgt.name, str(e))) res = StepResult(False, str(e).encode('utf-8')) else: self._logger.info('Backing up %s done' % tgt.name) result.target_results[tgt.name] = 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 == 0, res.stderr + res.stdout) finally: self._logger.debug('Releasing repository lock') fcntl.lockf(lockfile, fcntl.LOCK_UN) self._logger.debug('Backup finished') return result