Wed, 07 Feb 2018 20:39:01 +0000
Time snapshot fixes.
Python's default arguments are purely idiotic (aka. pythonic): generated
only once. This makes sense in a purely functional language, which Python
lightyears away from, but severely limits their usefulness in an imperative
language. Decorators also seem clumsy for this, as one would have to tell
the number of positional arguments for things to work nice, being able to
pass the snapshot both positionally and as keyword. No luck.
So have to do things the old-fashioned hard way.
0 | 1 | # |
89
51cc2e25af38
Added author information headers and content information to source files
Tuomo Valkonen <tuomov@iki.fi>
parents:
87
diff
changeset
|
2 | # Borgend by Tuomo Valkonen, 2018 |
51cc2e25af38
Added author information headers and content information to source files
Tuomo Valkonen <tuomov@iki.fi>
parents:
87
diff
changeset
|
3 | # |
51cc2e25af38
Added author information headers and content information to source files
Tuomo Valkonen <tuomov@iki.fi>
parents:
87
diff
changeset
|
4 | # This file implements a Borg launching interface. |
0 | 5 | # |
6 | ||
80
a409242121d5
Better package-like organisation
Tuomo Valkonen <tuomov@iki.fi>
parents:
79
diff
changeset
|
7 | import os |
0 | 8 | import json |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
9 | import logging |
2 | 10 | from subprocess import Popen, PIPE |
0 | 11 | |
80
a409242121d5
Better package-like organisation
Tuomo Valkonen <tuomov@iki.fi>
parents:
79
diff
changeset
|
12 | from .config import settings |
a409242121d5
Better package-like organisation
Tuomo Valkonen <tuomov@iki.fi>
parents:
79
diff
changeset
|
13 | |
86
2fe66644c50d
Can use logging.getLogger directly now after proper packageisation
Tuomo Valkonen <tuomov@iki.fi>
parents:
80
diff
changeset
|
14 | logger=logging.getLogger(__name__) |
31 | 15 | |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
16 | necessary_opts=['--log-json', '--progress'] |
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
17 | |
7 | 18 | necessary_opts_for={ |
19 | 'create': ['--json'], | |
20 | 'info': ['--json'], | |
21 | 'list': ['--json'], | |
97 | 22 | 'prune': ['--list'], |
7 | 23 | } |
24 | ||
74
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
25 | # Conversion of config into command line |
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
26 | def arglistify(args): |
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
27 | flatten=lambda l: [item for sublist in l for item in sublist] |
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
28 | if args is None: |
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
29 | return [] |
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
30 | else: |
97 | 31 | # Insert --key=str(value) for 'key: value' in the config. |
32 | # Boolean values are handled different, since borg does not take | |
33 | # --key=true type of arguments. If the value is true --key is inserted, | |
34 | # otherwise not. | |
35 | def mkarg(key, value): | |
36 | if isinstance(value, bool): | |
37 | if value: | |
38 | return ['--' + key] | |
39 | else: | |
40 | return [] | |
41 | else: | |
42 | return ['--' + key, str(value)] | |
43 | return flatten([mkarg(key, d[key]) for d in args for key in d]) | |
74
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
44 | |
0 | 45 | class BorgInstance: |
74
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
46 | def __init__(self, operation, archive_or_repository, |
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
47 | common_params, op_params, paths): |
0 | 48 | self.operation=operation; |
3
4cad934aa9ce
Can launch borg now; output not yet processed
Tuomo Valkonen <tuomov@iki.fi>
parents:
2
diff
changeset
|
49 | self.archive_or_repository=archive_or_repository; |
74
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
50 | self.common_params=common_params |
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
51 | self.op_params=op_params |
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
52 | self.paths=paths |
87
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
53 | self.proc=None |
0 | 54 | |
55 | def construct_cmdline(self): | |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
56 | cmd=([settings['borg']['executable']]+necessary_opts+ |
74
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
57 | arglistify(self.common_params)+ |
2 | 58 | [self.operation]) |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
59 | |
7 | 60 | if self.operation in necessary_opts_for: |
61 | cmd=cmd+necessary_opts_for[self.operation] | |
62 | ||
74
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
63 | return (cmd+arglistify(self.op_params) |
4f56142e7497
Separated repository configuration form backup configuration;
Tuomo Valkonen <tuomov@iki.fi>
parents:
34
diff
changeset
|
64 | +[self.archive_or_repository]+self.paths) |
0 | 65 | |
21
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
66 | def launch(self, passphrase=None): |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
67 | cmd=self.construct_cmdline() |
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
68 | |
31 | 69 | logger.info('Launching ' + str(cmd)) |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
70 | |
32 | 71 | # Set passphrase if not, or set to empty if not known, so borg |
72 | # won't hang waiting for it, which seems to happen even if we | |
73 | # close stdin. | |
29 | 74 | env=os.environ.copy() |
32 | 75 | env['BORG_PASSPHRASE']=passphrase or '' |
20
fdfbe5d7b677
Keychain support and random fixes
Tuomo Valkonen <tuomov@iki.fi>
parents:
12
diff
changeset
|
76 | |
24
94d58d514d69
Workaround to PYTHONPATH and PYTHONHOME being messed up by py2app.
Tuomo Valkonen <tuomov@iki.fi>
parents:
23
diff
changeset
|
77 | # Workaround: if launched is a standalone app created with py2app, |
94d58d514d69
Workaround to PYTHONPATH and PYTHONHOME being messed up by py2app.
Tuomo Valkonen <tuomov@iki.fi>
parents:
23
diff
changeset
|
78 | # borg will fail unless Python environment is reset. |
94d58d514d69
Workaround to PYTHONPATH and PYTHONHOME being messed up by py2app.
Tuomo Valkonen <tuomov@iki.fi>
parents:
23
diff
changeset
|
79 | # TODO: Of course, this will fail if the system needs the variables |
94d58d514d69
Workaround to PYTHONPATH and PYTHONHOME being messed up by py2app.
Tuomo Valkonen <tuomov@iki.fi>
parents:
23
diff
changeset
|
80 | # PYTHONPATH or PYTHONHOME set to certain values. |
29 | 81 | if '_PY2APP_LAUNCHED_' in env: |
82 | val=env['_PY2APP_LAUNCHED_'] | |
83 | if val=='1': | |
84 | del env['PYTHONPATH'] | |
85 | del env['PYTHONHOME'] | |
24
94d58d514d69
Workaround to PYTHONPATH and PYTHONHOME being messed up by py2app.
Tuomo Valkonen <tuomov@iki.fi>
parents:
23
diff
changeset
|
86 | |
20
fdfbe5d7b677
Keychain support and random fixes
Tuomo Valkonen <tuomov@iki.fi>
parents:
12
diff
changeset
|
87 | self.proc=Popen(cmd, env=env, stdout=PIPE, stderr=PIPE, stdin=PIPE) |
12 | 88 | |
21
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
89 | # We don't do passphrase input etc. |
12 | 90 | self.proc.stdin.close() |
0 | 91 | |
7 | 92 | def read_result(self): |
93 | stream=self.proc.stdout | |
95
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
94 | try: |
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
95 | line=stream.read(-1) |
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
96 | except Exception: |
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
97 | logger.exception('Borg stdout pipe read failed') |
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
98 | |
7 | 99 | if line==b'': |
95
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
100 | #logger.debug('Borg stdout pipe EOF?') |
7 | 101 | return None |
102 | ||
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
103 | try: |
95
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
104 | return json.loads(line.decode()) |
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
105 | except Exception: |
97 | 106 | logger.exception('JSON parse failed on stdout: %s' % str(line)) |
7 | 107 | return None |
108 | ||
109 | def read_log(self): | |
110 | stream=self.proc.stderr | |
111 | try: | |
112 | line=stream.readline() | |
95
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
113 | except Exception: |
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
114 | logger.exception('Pipe stderr pipe read failed') |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
115 | |
21
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
116 | return {'type': 'log_message', |
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
117 | 'levelname': 'CRITICAL', |
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
118 | 'name': 'borgend.instance.BorgInstance', |
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
119 | 'msgid': 'Borgend.Exception', |
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
120 | 'message': err} |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
121 | |
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
122 | if line==b'': |
95
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
123 | #logger.debug('Borg stderr pipe EOF?') |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
124 | return None |
3
4cad934aa9ce
Can launch borg now; output not yet processed
Tuomo Valkonen <tuomov@iki.fi>
parents:
2
diff
changeset
|
125 | |
95
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
126 | |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
127 | try: |
95
41bd7025532f
Fixes to borg output processing after testing on a Linux system
Tuomo Valkonen <tuomov@iki.fi>
parents:
89
diff
changeset
|
128 | res=json.loads(line.decode()) |
6 | 129 | if 'type' not in res: |
130 | res['type']='UNKNOWN' | |
131 | return res | |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
132 | except: |
97 | 133 | logger.exception('JSON parse failed on stderr: %s' % str(line)) |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
134 | |
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
135 | errmsg=line |
7 | 136 | for line in iter(stream.readline, b''): |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
137 | errmsg=errmsg+line |
3
4cad934aa9ce
Can launch borg now; output not yet processed
Tuomo Valkonen <tuomov@iki.fi>
parents:
2
diff
changeset
|
138 | |
21
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
139 | return {'type': 'log_message', |
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
140 | 'levelname': 'ERROR', |
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
141 | 'name': 'borgend.instance.BorgInstance', |
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
142 | 'msgid': 'Borgend.JSONFail', |
c36e549a7f12
Errors as rumps notifications
Tuomo Valkonen <tuomov@iki.fi>
parents:
20
diff
changeset
|
143 | 'message': str(errmsg)} |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
144 | |
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
145 | def terminate(self): |
87
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
146 | if self.proc: |
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
147 | self.proc.terminate() |
0 | 148 | |
87
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
149 | # Returns True if has terminated |
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
150 | def wait(self, timeout=None): |
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
151 | if self.proc: |
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
152 | return self.proc.wait(timeout=timeout) is not None |
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
153 | else: |
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
154 | return True |
0 | 155 | |
4
d72c4844e791
Better borg output processing and some logging
Tuomo Valkonen <tuomov@iki.fi>
parents:
3
diff
changeset
|
156 | def has_terminated(self): |
87
a214d475aa28
Better recovery from errors; fixes to potential race conditions in scheduler and repository queue
Tuomo Valkonen <tuomov@iki.fi>
parents:
86
diff
changeset
|
157 | return not self.proc or (self.proc.poll() is not None) |
0 | 158 |