Sun, 28 Jan 2018 11:01:45 +0000
DreamTime.monotonic() no longer needs to call time.monotonic()
so is more "static" within wakeup periods, and so comparable to
time.monotonic() realisations. Also some PyObjC weirdness handling.
# # Borgend borg launcher / processor # import json import logging import os import borgend from config import settings from subprocess import Popen, PIPE logger=borgend.logger.getChild(__name__) necessary_opts=['--log-json', '--progress'] necessary_opts_for={ 'create': ['--json'], 'info': ['--json'], 'list': ['--json'], } # Conversion of config into command line def arglistify(args): flatten=lambda l: [item for sublist in l for item in sublist] if args is None: return [] else: return flatten([['--' + key, str(d[key])] for d in args for key in d]) class BorgInstance: def __init__(self, operation, archive_or_repository, common_params, op_params, paths): self.operation=operation; self.archive_or_repository=archive_or_repository; self.common_params=common_params self.op_params=op_params self.paths=paths def construct_cmdline(self): cmd=([settings['borg']['executable']]+necessary_opts+ arglistify(self.common_params)+ [self.operation]) if self.operation in necessary_opts_for: cmd=cmd+necessary_opts_for[self.operation] return (cmd+arglistify(self.op_params) +[self.archive_or_repository]+self.paths) def launch(self, passphrase=None): cmd=self.construct_cmdline() 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() env['BORG_PASSPHRASE']=passphrase or '' # Workaround: if launched is a standalone app created with py2app, # borg will fail unless Python environment is reset. # TODO: Of course, this will fail if the system needs the variables # PYTHONPATH or PYTHONHOME set to certain values. if '_PY2APP_LAUNCHED_' in env: val=env['_PY2APP_LAUNCHED_'] if val=='1': del env['PYTHONPATH'] del env['PYTHONHOME'] self.proc=Popen(cmd, env=env, stdout=PIPE, stderr=PIPE, stdin=PIPE) # We don't do passphrase input etc. self.proc.stdin.close() def read_result(self): stream=self.proc.stdout line=stream.read(-1) if line==b'': logger.debug('Borg stdout pipe EOF?') return None try: return json.loads(line) except Exception as err: logger.warning('JSON parse failed on: "%s"' % line) return None def read_log(self): stream=self.proc.stderr try: line=stream.readline() except err: logger.debug('Pipe read failed: %s' % str(err)) return {'type': 'log_message', 'levelname': 'CRITICAL', 'name': 'borgend.instance.BorgInstance', 'msgid': 'Borgend.Exception', 'message': err} if line==b'': logger.debug('Borg stderr pipe EOF?') return None try: res=json.loads(line) if 'type' not in res: res['type']='UNKNOWN' return res except: logger.debug('JSON parse failed on: "%s"' % str(line)) errmsg=line for line in iter(stream.readline, b''): errmsg=errmsg+line return {'type': 'log_message', 'levelname': 'ERROR', 'name': 'borgend.instance.BorgInstance', 'msgid': 'Borgend.JSONFail', 'message': str(errmsg)} def terminate(self): self.proc.terminate() def wait(self): return self.proc.wait() is not None def has_terminated(self): return self.proc.poll() is not None