| 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: |