Fri, 19 Jan 2018 15:41:45 +0000
Better borg output processing and some logging
# # Borgend Backup instance # import config import logging from instance import BorgInstance from queue import Queue from threading import Thread, Lock class Backup: def __decode_config(self, cfg): loc0='backup target %d' % self.identifier self.name=config.check_string(cfg, 'name', 'Name', loc0) self.loc='backup target "%s"' % self.name self.repository=config.check_string(cfg, 'repository', 'Target repository', self.loc) self.archive_prefix=config.check_string(cfg, 'archive_prefix', 'Archive prefix', self.loc) self.archive_template=config.check_string(cfg, 'archive_template', 'Archive template', self.loc) self.backup_interval=config.check_nonneg_int(cfg, 'backup_interval', 'Backup interval', self.loc, config.defaults['backup_interval']) self.retry_interval=config.check_nonneg_int(cfg, 'retry_interval', 'Retry interval', self.loc, config.defaults['retry_interval']) self.paths=config.check_nonempty_list_of_strings(cfg, 'paths', 'Paths', self.loc) self.create_parameters=config.check_list_of_dicts(cfg, 'create_parameters', 'Borg parameters', self.loc, default=[]) self.prune_parameters=config.check_list_of_dicts(cfg, 'prune_parameters', 'Borg parameters', self.loc, default=[]) def __init__(self, identifier, cfg): self.identifier=identifier self.__decode_config(cfg) self.config=config self.lastrun=None self.borg_instance=None self.thread=None self.lock=Lock() def __block_when_running(self): self.lock.acquire() not_running=self.borg_instance is None and self.thread is None self.lock.release() assert(not_running) def __listener(self): for status in iter(self.borg_instance.read, None): t=status['type'] if t=='progress_percent': pass elif t=='archive_progress': pass elif t=='progress_message': pass elif t=='progress_percent': pass elif t=='file_status': pass elif t=='log_message': pass elif t=='exception': pass elif t=='unparsed_error': pass # What to do? print(status) logging.info('Borg subprocess finished; terminating listener thread') self.lock.acquire() self.borg_instance=None self.thread=None self.lock.release() def __launch(self, queue, operation, archive_or_repository, *args): inst=BorgInstance(operation, archive_or_repository, *args) inst.launch() t=Thread(target=self.__listener) t.daemon=True self.thread=t self.borg_instance=inst self.queue=queue t.start() def create(self, queue): self.__block_when_running() archive="%s::%s%s" % (self.repository, self.archive_prefix, self.archive_template) self.__launch(queue, 'create', archive, self.create_parameters, self.paths) def prune(self, queue): self.__block_when_running() self.__launch(queue, 'prune', self.repository, [{'prefix': self.archive_prefix}] + self.prune_parameters) # TODO: Decide exact (manual) abort mechanism. Perhaps two stages def abort(self): self.lock.acquire() if self.borg_instance: self.borg_instance.terminate() if self.thread: self.thread.terminate() self.lock.release() def join(self): if self.thread: self.thread.join()