--- a/backup.py Fri Jan 26 10:35:00 2018 +0000 +++ b/backup.py Fri Jan 26 19:04:04 2018 +0000 @@ -5,7 +5,6 @@ import config import logging import time -import keyring import borgend import repository from enum import IntEnum @@ -107,82 +106,47 @@ class Backup(TerminableThread): def __decode_config(self, cfg): - loc0='backup target %d' % self.identifier + loc0='Backup %d' % self.identifier - self._name=config.check_string(cfg, 'name', 'Name', loc0) + self.backup_name=config.check_string(cfg, 'name', 'Name', loc0) - self.logger=logger.getChild(self._name) + logger.debug("Configuring backup '%s'" % self.backup_name) - self.loc='backup target "%s"' % self._name + self.logger=logger.getChild(self.backup_name) + + loc="Backup '%s'" % self.backup_name reponame=config.check_string(cfg, 'repository', - 'Target repository', self.loc) + 'Target repository', loc) - self.repository=repository.get_controller(reponame) + self.repository=repository.find_repository(reponame) + if not self.repository: + raise Exception("Repository '%s' not configured" % reponame) self.archive_prefix=config.check_string(cfg, 'archive_prefix', - 'Archive prefix', self.loc) + 'Archive prefix', loc) self.archive_template=config.check_string(cfg, 'archive_template', - 'Archive template', self.loc) + 'Archive template', loc) self.backup_interval=config.check_nonneg_int(cfg, 'backup_interval', - 'Backup interval', self.loc, + 'Backup interval', loc, config.defaults['backup_interval']) self.retry_interval=config.check_nonneg_int(cfg, 'retry_interval', - 'Retry interval', self.loc, + 'Retry interval', loc, config.defaults['retry_interval']) - self.paths=config.check_nonempty_list_of_strings(cfg, 'paths', 'Paths', self.loc) - - self.common_parameters=config.check_list_of_dicts(cfg, 'common_parameters', - 'Borg parameters', self.loc, - default=[]) - - self.create_parameters=config.check_list_of_dicts(cfg, 'create_parameters', - 'Create parameters', self.loc, - default=[]) - - self.prune_parameters=config.check_list_of_dicts(cfg, 'prune_parameters', - 'Prune parameters', self.loc, - default=[]) - - self.__keychain_account=config.check_string(cfg, 'keychain_account', - 'Keychain account', self.loc, - default='') - - self.__passphrase=None + self.paths=config.check_nonempty_list_of_strings(cfg, 'paths', 'Paths', loc) - if config.settings['extract_passphrases_at_startup']: - try: - self.extract_passphrase() - except Exception: - pass + self.borg_parameters=config.BorgParameters.from_config(cfg, loc) - def extract_passphrase(self): - acc=self.__keychain_account - if not self.__passphrase: - if acc and acc!='': - self.logger.debug('Requesting passphrase') - try: - pw=keyring.get_password("borg-backup", acc) - except Exception as err: - self.logger.error('Failed to retrieve passphrase') - raise err - else: - self.logger.debug('Received passphrase') - self.__passphrase=pw - else: - self.__passphrase=None - return self.__passphrase def __init__(self, identifier, cfg, scheduler): self.identifier=identifier - self.config=config self.__status_update_callback=None self.scheduler=scheduler - self.logger=None # setup up __decode_config once backup name is known + self.logger=None # setup up in __decode_config once backup name is known self.borg_instance=None self.thread_log=None @@ -197,7 +161,7 @@ self.__decode_config(cfg) - super().__init__(target = self.__main_thread, name = self._name) + super().__init__(target = self.__main_thread, name = self.backup_name) self.daemon=True def is_running(self): @@ -293,17 +257,19 @@ self.logger.debug('Borg result: %s' % str(res)) with self._cond: - if res is None: + if res is None and self.errors.ok(): self.logger.error('No result from borg despite no error in log') - if errors.ok(): - self.errors=self.errors.combine(Errors.ERRORS) + self.errors=Errors.ERRORS - def __do_launch(self, op, archive_or_repository, *args): - passphrase=self.extract_passphrase() + def __do_launch(self, op, archive_or_repository, + common_params, op_params, paths=[]): - inst=BorgInstance(op.operation, archive_or_repository, *args) - inst.launch(passphrase=passphrase) + inst=BorgInstance(op.operation, archive_or_repository, + common_params, op_params, paths) + + # Only the Repository object has access to the passphrase + self.repository.launch_borg_instance(inst) self.logger.debug('Creating listener threads') @@ -328,22 +294,25 @@ t_log.start() t_res.start() + def __launch(self, op): self.logger.debug("Launching '%s'" % str(op.operation)) + params=(config.borg_parameters + +self.repository.borg_parameters + +self.borg_parameters) + if op.operation==Operation.CREATE: - archive="%s::%s%s" % (self.repository.repository_name, + archive="%s::%s%s" % (self.repository.location, self.archive_prefix, self.archive_template) - self.__do_launch(op, archive, - self.common_parameters+self.create_parameters, - self.paths) + self.__do_launch(op, archive, params.common, + params.create, self.paths) elif op.operation==Operation.PRUNE: - self.__do_launch(op, self.repository.repository_name, - ([{'prefix': self.archive_prefix}] + - self.common_parameters + - self.prune_parameters)) + self.__do_launch(op, self.repository.location, params.common, + [{'prefix': self.archive_prefix}] + params.create) + else: raise NotImplementedError("Invalid operation '%s'" % str(op.operation)) @@ -403,7 +372,7 @@ if not self._terminate: self.__main_thread_queue_and_launch() except Exception as err: - self.logger.exception("Error with backup '%s'" % self._name) + self.logger.exception("Error with backup '%s'" % self.backup_name) self.errors=Errors.ERRORS self.state=State.INACTIVE @@ -445,7 +414,7 @@ self.__update_status() # Wait under scheduled wait - self.scheduler.wait_until(now+delay, self._cond, self._name) + self.scheduler.wait_until(now+delay, self._cond, self.backup_name) else: # Nothing scheduled - just wait self.logger.info("Waiting for manual scheduling") @@ -466,7 +435,7 @@ self.__update_status() res=self.repository.queue_action(self._cond, action=self.__launch_and_wait, - name=self._name) + name=self.backup_name) if not res and not self._terminate: self.logger.debug("Queueing aborted") self.scheduled_operation=None