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: |