6 import logging |
6 import logging |
7 import time |
7 import time |
8 from instance import BorgInstance |
8 from instance import BorgInstance |
9 from queue import Queue |
9 from queue import Queue |
10 from threading import Thread, Lock |
10 from threading import Thread, Lock |
|
11 |
|
12 loglevel_translation={ |
|
13 'CRITICAL': logging.CRITICAL, |
|
14 'ERROR': logging.ERROR, |
|
15 'WARNING': logging.WARNING, |
|
16 'DEBUG': logging.DEBUG, |
|
17 'INFO': logging.INFO |
|
18 } |
|
19 |
|
20 def translate_loglevel(x): |
|
21 if x in loglevel_translation: |
|
22 return loglevel_translation[x] |
|
23 else: |
|
24 return logging.ERROR |
11 |
25 |
12 class Backup: |
26 class Backup: |
13 |
27 |
14 def __decode_config(self, cfg): |
28 def __decode_config(self, cfg): |
15 loc0='backup target %d' % self.identifier |
29 loc0='backup target %d' % self.identifier |
35 'Retry interval', self.loc, |
49 'Retry interval', self.loc, |
36 config.defaults['retry_interval']) |
50 config.defaults['retry_interval']) |
37 |
51 |
38 self.paths=config.check_nonempty_list_of_strings(cfg, 'paths', 'Paths', self.loc) |
52 self.paths=config.check_nonempty_list_of_strings(cfg, 'paths', 'Paths', self.loc) |
39 |
53 |
40 self.create_parameters=config.check_list_of_dicts(cfg, 'create_parameters', |
54 self.common_parameters=config.check_list_of_dicts(cfg, 'common_parameters', |
41 'Borg parameters', self.loc, |
55 'Borg parameters', self.loc, |
42 default=[]) |
56 default=[]) |
43 |
57 |
|
58 self.create_parameters=config.check_list_of_dicts(cfg, 'create_parameters', |
|
59 'Create parameters', self.loc, |
|
60 default=[]) |
|
61 |
44 self.prune_parameters=config.check_list_of_dicts(cfg, 'prune_parameters', |
62 self.prune_parameters=config.check_list_of_dicts(cfg, 'prune_parameters', |
45 'Borg parameters', self.loc, |
63 'Prune parameters', self.loc, |
46 default=[]) |
64 default=[]) |
47 |
65 |
48 |
66 |
49 def __init__(self, identifier, cfg): |
67 def __init__(self, identifier, cfg): |
50 self.identifier=identifier |
68 self.identifier=identifier |
63 with self.lock: |
81 with self.lock: |
64 not_running=self.borg_instance is None and self.thread is None |
82 not_running=self.borg_instance is None and self.thread is None |
65 assert(not_running) |
83 assert(not_running) |
66 |
84 |
67 def __listener(self): |
85 def __listener(self): |
68 success=True |
86 success=False |
69 for status in iter(self.borg_instance.read, None): |
87 for status in iter(self.borg_instance.read, None): |
|
88 logging.info(str(status)) |
70 t=status['type'] |
89 t=status['type'] |
71 if t=='progress_percent': |
90 if t=='progress_percent': |
72 pass |
91 pass |
73 elif t=='archive_progress': |
92 elif t=='archive_progress': |
74 pass |
93 pass |
75 elif t=='progress_message': |
94 elif t=='progress_message': |
76 # handle errors |
95 if 'finished' in status: |
77 pass |
96 logging.info('Borg subprocess finished succesfully') |
|
97 success=status['finished'] |
78 elif t=='progress_percent': |
98 elif t=='progress_percent': |
|
99 # Temporary output |
|
100 print('%d / %d', status['current'], status['total']) |
79 pass |
101 pass |
80 elif t=='file_status': |
102 elif t=='file_status': |
81 pass |
103 pass |
82 elif t=='log_message': |
104 elif t=='log_message': |
83 pass |
105 if 'levelname' not in status: |
|
106 status['levelname']='ERROR' |
|
107 if 'message' not in status: |
|
108 status['message']='UNKNOWN' |
|
109 if 'name' not in status: |
|
110 status['name']='borg' |
|
111 logging.log(translate_loglevel(status['levelname']), |
|
112 status['name'] + ': ' + status['message']) |
84 elif t=='exception': |
113 elif t=='exception': |
85 success=False |
|
86 pass |
114 pass |
87 elif t=='unparsed_error': |
115 elif t=='unparsed_error': |
88 success=False |
|
89 pass |
116 pass |
90 # What to do? |
|
91 print(status) |
|
92 |
117 |
93 logging.info('Borg subprocess finished; terminating listener thread') |
118 logging.info('Waiting for borg subprocess to terminate') |
|
119 |
|
120 self.borg_instance.wait() |
|
121 |
|
122 logging.info('Borg subprocess terminated; terminating listener thread') |
94 |
123 |
95 with self.lock: |
124 with self.lock: |
96 if self.current_operation=='create': |
125 if self.current_operation=='create': |
97 self.lastrun=self.time_started |
126 self.lastrun=self.time_started |
98 self.lastrun_success=success |
127 self.lastrun_success=success |
123 archive="%s::%s%s" % (self.repository, |
152 archive="%s::%s%s" % (self.repository, |
124 self.archive_prefix, |
153 self.archive_prefix, |
125 self.archive_template) |
154 self.archive_template) |
126 |
155 |
127 self.__launch(queue, 'create', archive, |
156 self.__launch(queue, 'create', archive, |
128 self.create_parameters, self.paths) |
157 self.common_parameters+self.create_parameters, |
|
158 self.paths) |
129 |
159 |
130 def prune(self, queue): |
160 def prune(self, queue): |
131 self.__block_when_running() |
161 self.__block_when_running() |
132 self.__launch(queue, 'prune', self.repository, |
162 self.__launch(queue, 'prune', self.repository, |
133 [{'prefix': self.archive_prefix}] + self.prune_parameters) |
163 ([{'prefix': self.archive_prefix}] + |
|
164 self.common_parameters + |
|
165 self.prune_parameters)) |
134 |
166 |
135 # TODO: Decide exact (manual) abort mechanism. Perhaps two stages |
167 # TODO: Decide exact (manual) abort mechanism. Perhaps two stages |
136 def abort(self): |
168 def abort(self): |
137 with self.lock: |
169 with self.lock: |
138 if self.borg_instance: |
170 if self.borg_instance: |