# HG changeset patch # User Tuomo Valkonen # Date 1516545275 0 # Node ID 06fc14211ba935a7a925ea405c28ba5fc2863f9a # Parent b4b4bb7a2ec5c3237848bf873c89a145185961a2 Error handling improvements diff -r b4b4bb7a2ec5 -r 06fc14211ba9 backup.py --- a/backup.py Sun Jan 21 13:34:12 2018 +0000 +++ b/backup.py Sun Jan 21 14:34:35 2018 +0000 @@ -78,16 +78,30 @@ 'Keychain account', self.loc, default='') + self.__passphrase=None + if config.settings['extract_passphrases_at_startup']: - self.extract_passphrase() + try: + self.extract_passphrase() + except Exception: + pass def extract_passphrase(self): acc=self.__keychain_account - if acc and acc!='': - pw=keyring.get_password("borg-backup", acc) - self.__passphrase=pw - else: - self.__passphrase=None + if not self.__passphrase: + if acc and acc!='': + logger.debug('Requesting passphrase') + try: + pw=keyring.get_password("borg-backup", acc) + except Exception as err: + logger.error('Failed to retrieve password: %s' % str(err)) + raise err + else: + logger.debug('Received passphrase') + self.__passphrase=pw + else: + self.__passphrase=None + return self.__passphrase def __init__(self, identifier, cfg): self.identifier=identifier @@ -95,7 +109,7 @@ self.__decode_config(cfg) self.config=config - self.lastrun=None + self.lastrun_when=None self.lastrun_success=None self.borg_instance=None self.current_operation=None @@ -212,7 +226,7 @@ with self.lock: if self.current_operation['operation']=='create': - self.lastrun=self.current_operation['when_monotonic'] + self.lastrun_when=self.current_operation['when_monotonic'] self.lastrun_success=success self.thread_res=None self.thread_log=None @@ -224,10 +238,10 @@ callback(self, status) def __do_launch(self, queue, op, archive_or_repository, *args): - self.extract_passphrase() + passphrase=self.extract_passphrase() inst=BorgInstance(op['operation'], archive_or_repository, *args) - inst.launch(passphrase=self.__passphrase) + inst.launch(passphrase=passphrase) logger.debug('Creating listener threads') @@ -258,24 +272,30 @@ self.timer=None self.scheduled_operation=None - logger.debug("Launching '%s' on '%s'" % (op['operation'], self.name)) + try: + logger.debug("Launching '%s' on '%s'" % (op['operation'], self.name)) - if op['operation']=='create': - archive="%s::%s%s" % (self.repository, - self.archive_prefix, - self.archive_template) + if op['operation']=='create': + archive="%s::%s%s" % (self.repository, + self.archive_prefix, + self.archive_template) - self.__do_launch(queue, op, archive, - self.common_parameters+self.create_parameters, - self.paths) - elif op['operation']=='prune': - self.__do_launch(queue, op, self.repository, - ([{'prefix': self.archive_prefix}] + - self.common_parameters + - self.prune_parameters)) - else: - logger.error("Invalid operaton '%s'" % op['operation']) + self.__do_launch(queue, op, archive, + self.common_parameters+self.create_parameters, + self.paths) + elif op['operation']=='prune': + self.__do_launch(queue, op, self.repository, + ([{'prefix': self.archive_prefix}] + + self.common_parameters + + self.prune_parameters)) + else: + raise NotImplementedError("Invalid operation '%s'" % op['operation']) + except Exception as err: + logging.debug('Rescheduling after failure') + self.lastrun_when=time.monotonic() + self.lastrun_success=False self.__schedule_unlocked() + raise err return True @@ -338,7 +358,7 @@ def __next_operation_unlocked(self): # TODO: pruning as well now=time.monotonic() - if not self.lastrun: + if not self.lastrun_when: initial_interval=self.retry_interval if initial_interval==0: initial_interval=self.backup_interval @@ -354,14 +374,14 @@ else: return {'operation': 'create', 'detail': 'retry', - 'when_monotonic': self.lastrun+self.retry_interval} + 'when_monotonic': self.lastrun_when+self.retry_interval} else: if self.backup_interval==0: return None else: return {'operation': 'create', 'detail': None, - 'when_monotonic': self.lastrun+self.backup_interval} + 'when_monotonic': self.lastrun_when+self.backup_interval} def __schedule_unlocked(self): if self.current_operation: diff -r b4b4bb7a2ec5 -r 06fc14211ba9 instance.py --- a/instance.py Sun Jan 21 13:34:12 2018 +0000 +++ b/instance.py Sun Jan 21 14:34:35 2018 +0000 @@ -43,9 +43,11 @@ logger.info('Launching ' + str(cmd)) + # Set passphrase if not, or set to empty if not known, so borg + # won't hang waiting for it, which seems to happen even if we + # close stdin. env=os.environ.copy() - if passphrase: - env['BORG_PASSPHRASE']=passphrase + env['BORG_PASSPHRASE']=passphrase or '' # Workaround: if launched is a standalone app created with py2app, # borg will fail unless Python environment is reset. @@ -71,7 +73,7 @@ try: return json.loads(line) - except: + except Exception as err: logger.warning('JSON parse failed on: "%s"' % line) return None diff -r b4b4bb7a2ec5 -r 06fc14211ba9 ui.py --- a/ui.py Sun Jan 21 13:34:12 2018 +0000 +++ b/ui.py Sun Jan 21 14:34:35 2018 +0000 @@ -30,6 +30,14 @@ def combine_state(state1, state2): return max(state1, state2) +# Workaround to rumps brokenness; +# see https://github.com/jaredks/rumps/issues/59 +def notification_workaround(title, subtitle, message): + NSDictionary = objc.lookUpClass("NSDictionary") + d=NSDictionary() + + rumps.notification(title, subtitle, message, data=d) + # Based on code snatched from # https://stackoverflow.com/questions/12523586/python-format-size-application-converting-b-to-kb-mb-gb-tb/37423778 def humanbytes(B): @@ -169,14 +177,10 @@ else: msgid=errorlog['msgid'] - logger.debug('Opening notification for error') + logger.debug("Opening notification for error %s '%s'", + msgid, errorlog['message']) - # Workaround to rumps brokenness - # See https://github.com/jaredks/rumps/issues/59 - NSDictionary = objc.lookUpClass("NSDictionary") - d=NSDictionary() - - rumps.notification('Borgend', msgid, errorlog['message'], data=d) + notification_workaround('Borgend', msgid, errorlog['message']) def my_quit(self, _): rumps.quit_application() @@ -184,12 +188,14 @@ def __menu_select_backup(self, sender, b): #sender.state=not sender.state logger.debug("Manually backup '%s'", b.name) - b.create(None) + try: + b.create(None) + except Exception as err: + logger.debug("Opening notification for exception %s '%s'", + err.__class__.__name__, str(err)) + notification_workaround('Borgend', err.__class__.__name__, str(err)) @rumps.notifications def notification_center(data): pass - - -