| 76 |
76 |
| 77 self.__keychain_account=config.check_string(cfg, 'keychain_account', |
77 self.__keychain_account=config.check_string(cfg, 'keychain_account', |
| 78 'Keychain account', self.loc, |
78 'Keychain account', self.loc, |
| 79 default='') |
79 default='') |
| 80 |
80 |
| |
81 self.__passphrase=None |
| |
82 |
| 81 if config.settings['extract_passphrases_at_startup']: |
83 if config.settings['extract_passphrases_at_startup']: |
| 82 self.extract_passphrase() |
84 try: |
| |
85 self.extract_passphrase() |
| |
86 except Exception: |
| |
87 pass |
| 83 |
88 |
| 84 def extract_passphrase(self): |
89 def extract_passphrase(self): |
| 85 acc=self.__keychain_account |
90 acc=self.__keychain_account |
| 86 if acc and acc!='': |
91 if not self.__passphrase: |
| 87 pw=keyring.get_password("borg-backup", acc) |
92 if acc and acc!='': |
| 88 self.__passphrase=pw |
93 logger.debug('Requesting passphrase') |
| 89 else: |
94 try: |
| 90 self.__passphrase=None |
95 pw=keyring.get_password("borg-backup", acc) |
| |
96 except Exception as err: |
| |
97 logger.error('Failed to retrieve password: %s' % str(err)) |
| |
98 raise err |
| |
99 else: |
| |
100 logger.debug('Received passphrase') |
| |
101 self.__passphrase=pw |
| |
102 else: |
| |
103 self.__passphrase=None |
| |
104 return self.__passphrase |
| 91 |
105 |
| 92 def __init__(self, identifier, cfg): |
106 def __init__(self, identifier, cfg): |
| 93 self.identifier=identifier |
107 self.identifier=identifier |
| 94 |
108 |
| 95 self.__decode_config(cfg) |
109 self.__decode_config(cfg) |
| 96 |
110 |
| 97 self.config=config |
111 self.config=config |
| 98 self.lastrun=None |
112 self.lastrun_when=None |
| 99 self.lastrun_success=None |
113 self.lastrun_success=None |
| 100 self.borg_instance=None |
114 self.borg_instance=None |
| 101 self.current_operation=None |
115 self.current_operation=None |
| 102 self.thread_log=None |
116 self.thread_log=None |
| 103 self.thread_res=None |
117 self.thread_res=None |
| 210 |
224 |
| 211 self.thread_log.join() |
225 self.thread_log.join() |
| 212 |
226 |
| 213 with self.lock: |
227 with self.lock: |
| 214 if self.current_operation['operation']=='create': |
228 if self.current_operation['operation']=='create': |
| 215 self.lastrun=self.current_operation['when_monotonic'] |
229 self.lastrun_when=self.current_operation['when_monotonic'] |
| 216 self.lastrun_success=success |
230 self.lastrun_success=success |
| 217 self.thread_res=None |
231 self.thread_res=None |
| 218 self.thread_log=None |
232 self.thread_log=None |
| 219 self.borg_instance=None |
233 self.borg_instance=None |
| 220 self.current_operation=None |
234 self.current_operation=None |
| 222 status, callback=self.__status_unlocked() |
236 status, callback=self.__status_unlocked() |
| 223 if callback: |
237 if callback: |
| 224 callback(self, status) |
238 callback(self, status) |
| 225 |
239 |
| 226 def __do_launch(self, queue, op, archive_or_repository, *args): |
240 def __do_launch(self, queue, op, archive_or_repository, *args): |
| 227 self.extract_passphrase() |
241 passphrase=self.extract_passphrase() |
| 228 |
242 |
| 229 inst=BorgInstance(op['operation'], archive_or_repository, *args) |
243 inst=BorgInstance(op['operation'], archive_or_repository, *args) |
| 230 inst.launch(passphrase=self.__passphrase) |
244 inst.launch(passphrase=passphrase) |
| 231 |
245 |
| 232 logger.debug('Creating listener threads') |
246 logger.debug('Creating listener threads') |
| 233 |
247 |
| 234 t_log=Thread(target=self.__log_listener) |
248 t_log=Thread(target=self.__log_listener) |
| 235 t_log.daemon=True |
249 t_log.daemon=True |
| 256 if self.timer: |
270 if self.timer: |
| 257 logger.debug('Unscheduling timed operation due to launch of operation') |
271 logger.debug('Unscheduling timed operation due to launch of operation') |
| 258 self.timer=None |
272 self.timer=None |
| 259 self.scheduled_operation=None |
273 self.scheduled_operation=None |
| 260 |
274 |
| 261 logger.debug("Launching '%s' on '%s'" % (op['operation'], self.name)) |
275 try: |
| 262 |
276 logger.debug("Launching '%s' on '%s'" % (op['operation'], self.name)) |
| 263 if op['operation']=='create': |
277 |
| 264 archive="%s::%s%s" % (self.repository, |
278 if op['operation']=='create': |
| 265 self.archive_prefix, |
279 archive="%s::%s%s" % (self.repository, |
| 266 self.archive_template) |
280 self.archive_prefix, |
| 267 |
281 self.archive_template) |
| 268 self.__do_launch(queue, op, archive, |
282 |
| 269 self.common_parameters+self.create_parameters, |
283 self.__do_launch(queue, op, archive, |
| 270 self.paths) |
284 self.common_parameters+self.create_parameters, |
| 271 elif op['operation']=='prune': |
285 self.paths) |
| 272 self.__do_launch(queue, op, self.repository, |
286 elif op['operation']=='prune': |
| 273 ([{'prefix': self.archive_prefix}] + |
287 self.__do_launch(queue, op, self.repository, |
| 274 self.common_parameters + |
288 ([{'prefix': self.archive_prefix}] + |
| 275 self.prune_parameters)) |
289 self.common_parameters + |
| 276 else: |
290 self.prune_parameters)) |
| 277 logger.error("Invalid operaton '%s'" % op['operation']) |
291 else: |
| |
292 raise NotImplementedError("Invalid operation '%s'" % op['operation']) |
| |
293 except Exception as err: |
| |
294 logging.debug('Rescheduling after failure') |
| |
295 self.lastrun_when=time.monotonic() |
| |
296 self.lastrun_success=False |
| 278 self.__schedule_unlocked() |
297 self.__schedule_unlocked() |
| |
298 raise err |
| 279 |
299 |
| 280 return True |
300 return True |
| 281 |
301 |
| 282 def create(self, queue): |
302 def create(self, queue): |
| 283 op={'operation': 'create', 'detail': 'manual'} |
303 op={'operation': 'create', 'detail': 'manual'} |
| 336 self.__launch(op, None) |
356 self.__launch(op, None) |
| 337 |
357 |
| 338 def __next_operation_unlocked(self): |
358 def __next_operation_unlocked(self): |
| 339 # TODO: pruning as well |
359 # TODO: pruning as well |
| 340 now=time.monotonic() |
360 now=time.monotonic() |
| 341 if not self.lastrun: |
361 if not self.lastrun_when: |
| 342 initial_interval=self.retry_interval |
362 initial_interval=self.retry_interval |
| 343 if initial_interval==0: |
363 if initial_interval==0: |
| 344 initial_interval=self.backup_interval |
364 initial_interval=self.backup_interval |
| 345 if initial_interval==0: |
365 if initial_interval==0: |
| 346 return None |
366 return None |
| 352 if self.retry_interval==0: |
372 if self.retry_interval==0: |
| 353 return None |
373 return None |
| 354 else: |
374 else: |
| 355 return {'operation': 'create', |
375 return {'operation': 'create', |
| 356 'detail': 'retry', |
376 'detail': 'retry', |
| 357 'when_monotonic': self.lastrun+self.retry_interval} |
377 'when_monotonic': self.lastrun_when+self.retry_interval} |
| 358 else: |
378 else: |
| 359 if self.backup_interval==0: |
379 if self.backup_interval==0: |
| 360 return None |
380 return None |
| 361 else: |
381 else: |
| 362 return {'operation': 'create', |
382 return {'operation': 'create', |
| 363 'detail': None, |
383 'detail': None, |
| 364 'when_monotonic': self.lastrun+self.backup_interval} |
384 'when_monotonic': self.lastrun_when+self.backup_interval} |
| 365 |
385 |
| 366 def __schedule_unlocked(self): |
386 def __schedule_unlocked(self): |
| 367 if self.current_operation: |
387 if self.current_operation: |
| 368 return self.current_operation |
388 return self.current_operation |
| 369 else: |
389 else: |