instance.py

changeset 80
a409242121d5
parent 79
b075b3db3044
child 81
7bcd715f19e3
equal deleted inserted replaced
79:b075b3db3044 80:a409242121d5
1 #
2 # Borgend borg launcher / processor
3 #
4
5 import json
6 import logging
7 import os
8 import loggers
9 from config import settings
10 from subprocess import Popen, PIPE
11
12 logger=loggers.get(__name__)
13
14 necessary_opts=['--log-json', '--progress']
15
16 necessary_opts_for={
17 'create': ['--json'],
18 'info': ['--json'],
19 'list': ['--json'],
20 }
21
22 # Conversion of config into command line
23 def arglistify(args):
24 flatten=lambda l: [item for sublist in l for item in sublist]
25 if args is None:
26 return []
27 else:
28 return flatten([['--' + key, str(d[key])] for d in args for key in d])
29
30 class BorgInstance:
31 def __init__(self, operation, archive_or_repository,
32 common_params, op_params, paths):
33 self.operation=operation;
34 self.archive_or_repository=archive_or_repository;
35 self.common_params=common_params
36 self.op_params=op_params
37 self.paths=paths
38
39 def construct_cmdline(self):
40 cmd=([settings['borg']['executable']]+necessary_opts+
41 arglistify(self.common_params)+
42 [self.operation])
43
44 if self.operation in necessary_opts_for:
45 cmd=cmd+necessary_opts_for[self.operation]
46
47 return (cmd+arglistify(self.op_params)
48 +[self.archive_or_repository]+self.paths)
49
50 def launch(self, passphrase=None):
51 cmd=self.construct_cmdline()
52
53 logger.info('Launching ' + str(cmd))
54
55 # Set passphrase if not, or set to empty if not known, so borg
56 # won't hang waiting for it, which seems to happen even if we
57 # close stdin.
58 env=os.environ.copy()
59 env['BORG_PASSPHRASE']=passphrase or ''
60
61 # Workaround: if launched is a standalone app created with py2app,
62 # borg will fail unless Python environment is reset.
63 # TODO: Of course, this will fail if the system needs the variables
64 # PYTHONPATH or PYTHONHOME set to certain values.
65 if '_PY2APP_LAUNCHED_' in env:
66 val=env['_PY2APP_LAUNCHED_']
67 if val=='1':
68 del env['PYTHONPATH']
69 del env['PYTHONHOME']
70
71 self.proc=Popen(cmd, env=env, stdout=PIPE, stderr=PIPE, stdin=PIPE)
72
73 # We don't do passphrase input etc.
74 self.proc.stdin.close()
75
76 def read_result(self):
77 stream=self.proc.stdout
78 line=stream.read(-1)
79 if line==b'':
80 logger.debug('Borg stdout pipe EOF?')
81 return None
82
83 try:
84 return json.loads(line)
85 except Exception as err:
86 logger.warning('JSON parse failed on: "%s"' % line)
87 return None
88
89 def read_log(self):
90 stream=self.proc.stderr
91 try:
92 line=stream.readline()
93 except err:
94 logger.debug('Pipe read failed: %s' % str(err))
95
96 return {'type': 'log_message',
97 'levelname': 'CRITICAL',
98 'name': 'borgend.instance.BorgInstance',
99 'msgid': 'Borgend.Exception',
100 'message': err}
101
102 if line==b'':
103
104 logger.debug('Borg stderr pipe EOF?')
105
106 return None
107
108 try:
109 res=json.loads(line)
110 if 'type' not in res:
111 res['type']='UNKNOWN'
112 return res
113 except:
114 logger.debug('JSON parse failed on: "%s"' % str(line))
115
116 errmsg=line
117 for line in iter(stream.readline, b''):
118 errmsg=errmsg+line
119
120 return {'type': 'log_message',
121 'levelname': 'ERROR',
122 'name': 'borgend.instance.BorgInstance',
123 'msgid': 'Borgend.JSONFail',
124 'message': str(errmsg)}
125
126 def terminate(self):
127 self.proc.terminate()
128
129 def wait(self):
130 return self.proc.wait() is not None
131
132 def has_terminated(self):
133 return self.proc.poll() is not None
134

mercurial