Merge lp:~germar/backintime/ssh into lp:backintime/1.0
- ssh
- Merge into release-1.0
Proposed by
Germar
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 808 | ||||||||
Proposed branch: | lp:~germar/backintime/ssh | ||||||||
Merge into: | lp:backintime/1.0 | ||||||||
Diff against target: |
3394 lines (+2339/-271) 16 files modified
AUTHORS (+1/-0) CHANGES (+5/-0) common/backintime.py (+61/-0) common/config.py (+153/-6) common/configfile.py (+0/-1) common/debian_specific/control (+1/-1) common/dummytools.py (+105/-0) common/mount.py (+421/-0) common/snapshots.py (+154/-26) common/sshtools.py (+309/-0) common/tools.py (+21/-1) gnome/app.py (+51/-10) gnome/settingsdialog.glade (+608/-211) gnome/settingsdialog.py (+202/-7) kde4/app.py (+31/-0) kde4/settingsdialog.py (+216/-8) |
||||||||
To merge this branch: | bzr merge lp:~germar/backintime/ssh | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Back In Time Team | Pending | ||
Review via email: mp+131080@code.launchpad.net |
Commit message
Description of the change
* Add generic mount-framework which will make it easy to add new mount services to BIT
* Add mode 'SSH' for backups on remote host using ssh protocol
* Fix bug: wrong path if restore system root
* Add option to choose multiple custom hours for backup
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'AUTHORS' | |||
2 | --- AUTHORS 2009-11-06 13:00:42 +0000 | |||
3 | +++ AUTHORS 2012-10-23 19:40:24 +0000 | |||
4 | @@ -1,3 +1,4 @@ | |||
5 | 1 | Oprea Dan (<dan@le-web.org>) | 1 | Oprea Dan (<dan@le-web.org>) |
6 | 2 | Bart de Koning (<bratdaking@gmail.com>) | 2 | Bart de Koning (<bratdaking@gmail.com>) |
7 | 3 | Richard Bailey (<rmjb@mail.com>) | 3 | Richard Bailey (<rmjb@mail.com>) |
8 | 4 | Germar Reitze (<germar.reitze@gmx.de>) | ||
9 | 4 | 5 | ||
10 | === modified file 'CHANGES' | |||
11 | --- CHANGES 2012-02-22 09:55:08 +0000 | |||
12 | +++ CHANGES 2012-10-23 19:40:24 +0000 | |||
13 | @@ -1,5 +1,10 @@ | |||
14 | 1 | Back In Time | 1 | Back In Time |
15 | 2 | 2 | ||
16 | 3 | Version 1.1.0 | ||
17 | 4 | * Add generic mount-framework | ||
18 | 5 | * Add mode 'SSH' for backups on remote host using ssh protocol. | ||
19 | 6 | * Fix bug: wrong path if restore system root | ||
20 | 7 | |||
21 | 3 | Version 1.0.10 | 8 | Version 1.0.10 |
22 | 4 | * Add "Restore to ..." in replacement of copy (with or without drag & drop) because copy don't restore user/group/rights | 9 | * Add "Restore to ..." in replacement of copy (with or without drag & drop) because copy don't restore user/group/rights |
23 | 5 | 10 | ||
24 | 6 | 11 | ||
25 | === modified file 'common/backintime.py' | |||
26 | --- common/backintime.py 2010-11-08 21:11:52 +0000 | |||
27 | +++ common/backintime.py 2012-10-23 19:40:24 +0000 | |||
28 | @@ -26,6 +26,8 @@ | |||
29 | 26 | import logger | 26 | import logger |
30 | 27 | import snapshots | 27 | import snapshots |
31 | 28 | import tools | 28 | import tools |
32 | 29 | import sshtools | ||
33 | 30 | import mount | ||
34 | 29 | 31 | ||
35 | 30 | _=gettext.gettext | 32 | _=gettext.gettext |
36 | 31 | 33 | ||
37 | @@ -44,9 +46,26 @@ | |||
38 | 44 | 46 | ||
39 | 45 | def take_snapshot( cfg, force = True ): | 47 | def take_snapshot( cfg, force = True ): |
40 | 46 | logger.openlog() | 48 | logger.openlog() |
41 | 49 | _mount(cfg) | ||
42 | 47 | snapshots.Snapshots( cfg ).take_snapshot( force ) | 50 | snapshots.Snapshots( cfg ).take_snapshot( force ) |
43 | 51 | _umount(cfg) | ||
44 | 48 | logger.closelog() | 52 | logger.closelog() |
45 | 49 | 53 | ||
46 | 54 | def _mount(cfg): | ||
47 | 55 | try: | ||
48 | 56 | hash_id = mount.Mount(cfg = cfg).mount() | ||
49 | 57 | except mount.MountException as ex: | ||
50 | 58 | logger.error(str(ex)) | ||
51 | 59 | sys.exit(1) | ||
52 | 60 | else: | ||
53 | 61 | cfg.set_current_hash_id(hash_id) | ||
54 | 62 | |||
55 | 63 | def _umount(cfg): | ||
56 | 64 | try: | ||
57 | 65 | mount.Mount(cfg = cfg).umount(cfg.current_hash_id) | ||
58 | 66 | except mount.MountException as ex: | ||
59 | 67 | logger.error(str(ex)) | ||
60 | 68 | |||
61 | 50 | 69 | ||
62 | 51 | def print_version( cfg, app_name ): | 70 | def print_version( cfg, app_name ): |
63 | 52 | print '' | 71 | print '' |
64 | @@ -79,6 +98,13 @@ | |||
65 | 79 | print '\tShow the ID of the last snapshot (and exit)' | 98 | print '\tShow the ID of the last snapshot (and exit)' |
66 | 80 | print '--last-snapshot-path' | 99 | print '--last-snapshot-path' |
67 | 81 | print '\tShow the path to the last snapshot (and exit)' | 100 | print '\tShow the path to the last snapshot (and exit)' |
68 | 101 | print '--keep-mount' | ||
69 | 102 | print '\tDon\'t unmount on exit. Only valid with' | ||
70 | 103 | print '\t--snapshots-list-path and --last-snapshot-path.' | ||
71 | 104 | print '--unmount' | ||
72 | 105 | print '\tUnmount the profile.' | ||
73 | 106 | print '--benchmark-cipher [file-size]' | ||
74 | 107 | print '\tShow a benchmark of all ciphers for ssh transfer (and exit)' | ||
75 | 82 | print '-v | --version' | 108 | print '-v | --version' |
76 | 83 | print '\tShow version (and exit)' | 109 | print '\tShow version (and exit)' |
77 | 84 | print '--license' | 110 | print '--license' |
78 | @@ -94,6 +120,7 @@ | |||
79 | 94 | 120 | ||
80 | 95 | skip = False | 121 | skip = False |
81 | 96 | index = 0 | 122 | index = 0 |
82 | 123 | keep_mount = False | ||
83 | 97 | 124 | ||
84 | 98 | for arg in sys.argv[ 1 : ]: | 125 | for arg in sys.argv[ 1 : ]: |
85 | 99 | index = index + 1 | 126 | index = index + 1 |
86 | @@ -146,18 +173,21 @@ | |||
87 | 146 | if not cfg.is_configured(): | 173 | if not cfg.is_configured(): |
88 | 147 | print "The application is not configured !" | 174 | print "The application is not configured !" |
89 | 148 | else: | 175 | else: |
90 | 176 | _mount(cfg) | ||
91 | 149 | list = snapshots.Snapshots( cfg ).get_snapshots_list() | 177 | list = snapshots.Snapshots( cfg ).get_snapshots_list() |
92 | 150 | if len( list ) <= 0: | 178 | if len( list ) <= 0: |
93 | 151 | print "There are no snapshots" | 179 | print "There are no snapshots" |
94 | 152 | else: | 180 | else: |
95 | 153 | for snapshot_id in list: | 181 | for snapshot_id in list: |
96 | 154 | print "SnapshotID: %s" % snapshot_id | 182 | print "SnapshotID: %s" % snapshot_id |
97 | 183 | _umount(cfg) | ||
98 | 155 | sys.exit(0) | 184 | sys.exit(0) |
99 | 156 | 185 | ||
100 | 157 | if arg == '--snapshots-list-path': | 186 | if arg == '--snapshots-list-path': |
101 | 158 | if not cfg.is_configured(): | 187 | if not cfg.is_configured(): |
102 | 159 | print "The application is not configured !" | 188 | print "The application is not configured !" |
103 | 160 | else: | 189 | else: |
104 | 190 | _mount(cfg) | ||
105 | 161 | s = snapshots.Snapshots( cfg ) | 191 | s = snapshots.Snapshots( cfg ) |
106 | 162 | list = s.get_snapshots_list() | 192 | list = s.get_snapshots_list() |
107 | 163 | if len( list ) <= 0: | 193 | if len( list ) <= 0: |
108 | @@ -165,29 +195,60 @@ | |||
109 | 165 | else: | 195 | else: |
110 | 166 | for snapshot_id in list: | 196 | for snapshot_id in list: |
111 | 167 | print "SnapshotPath: %s" % s.get_snapshot_path( snapshot_id ) | 197 | print "SnapshotPath: %s" % s.get_snapshot_path( snapshot_id ) |
112 | 198 | if not keep_mount: | ||
113 | 199 | _umount(cfg) | ||
114 | 168 | sys.exit(0) | 200 | sys.exit(0) |
115 | 169 | 201 | ||
116 | 170 | if arg == '--last-snapshot': | 202 | if arg == '--last-snapshot': |
117 | 171 | if not cfg.is_configured(): | 203 | if not cfg.is_configured(): |
118 | 172 | print "The application is not configured !" | 204 | print "The application is not configured !" |
119 | 173 | else: | 205 | else: |
120 | 206 | _mount(cfg) | ||
121 | 174 | list = snapshots.Snapshots( cfg ).get_snapshots_list() | 207 | list = snapshots.Snapshots( cfg ).get_snapshots_list() |
122 | 175 | if len( list ) <= 0: | 208 | if len( list ) <= 0: |
123 | 176 | print "There are no snapshots" | 209 | print "There are no snapshots" |
124 | 177 | else: | 210 | else: |
125 | 178 | print "SnapshotID: %s" % list[0] | 211 | print "SnapshotID: %s" % list[0] |
126 | 212 | _umount(cfg) | ||
127 | 179 | sys.exit(0) | 213 | sys.exit(0) |
128 | 180 | 214 | ||
129 | 181 | if arg == '--last-snapshot-path': | 215 | if arg == '--last-snapshot-path': |
130 | 182 | if not cfg.is_configured(): | 216 | if not cfg.is_configured(): |
131 | 183 | print "The application is not configured !" | 217 | print "The application is not configured !" |
132 | 184 | else: | 218 | else: |
133 | 219 | _mount(cfg) | ||
134 | 185 | s = snapshots.Snapshots( cfg ) | 220 | s = snapshots.Snapshots( cfg ) |
135 | 186 | list = s.get_snapshots_list() | 221 | list = s.get_snapshots_list() |
136 | 187 | if len( list ) <= 0: | 222 | if len( list ) <= 0: |
137 | 188 | print "There are no snapshots" | 223 | print "There are no snapshots" |
138 | 189 | else: | 224 | else: |
139 | 190 | print "SnapshotPath: %s" % s.get_snapshot_path( list[0] ) | 225 | print "SnapshotPath: %s" % s.get_snapshot_path( list[0] ) |
140 | 226 | if not keep_mount: | ||
141 | 227 | _umount(cfg) | ||
142 | 228 | sys.exit(0) | ||
143 | 229 | |||
144 | 230 | if arg == '--keep-mount': | ||
145 | 231 | keep_mount = True | ||
146 | 232 | continue | ||
147 | 233 | |||
148 | 234 | if arg == '--unmount': | ||
149 | 235 | _mount(cfg) | ||
150 | 236 | _umount(cfg) | ||
151 | 237 | sys.exit(0) | ||
152 | 238 | |||
153 | 239 | if arg == '--benchmark-cipher': | ||
154 | 240 | if not cfg.is_configured(): | ||
155 | 241 | print "The application is not configured !" | ||
156 | 242 | else: | ||
157 | 243 | try: | ||
158 | 244 | size = sys.argv[index + 1] | ||
159 | 245 | except IndexError: | ||
160 | 246 | size = '40' | ||
161 | 247 | if cfg.get_snapshots_mode() == 'ssh': | ||
162 | 248 | ssh = sshtools.SSH(cfg=cfg) | ||
163 | 249 | ssh.benchmark_cipher(size) | ||
164 | 250 | else: | ||
165 | 251 | print('ssh is not configured !') | ||
166 | 191 | sys.exit(0) | 252 | sys.exit(0) |
167 | 192 | 253 | ||
168 | 193 | if arg == '--snapshots' or arg == '-s': | 254 | if arg == '--snapshots' or arg == '-s': |
169 | 194 | 255 | ||
170 | === modified file 'common/config.py' | |||
171 | --- common/config.py 2012-03-06 21:23:31 +0000 | |||
172 | +++ common/config.py 2012-10-23 19:40:24 +0000 | |||
173 | @@ -26,6 +26,9 @@ | |||
174 | 26 | import configfile | 26 | import configfile |
175 | 27 | import tools | 27 | import tools |
176 | 28 | import logger | 28 | import logger |
177 | 29 | import mount | ||
178 | 30 | import sshtools | ||
179 | 31 | ##import dummytools | ||
180 | 29 | 32 | ||
181 | 30 | _=gettext.gettext | 33 | _=gettext.gettext |
182 | 31 | 34 | ||
183 | @@ -44,6 +47,7 @@ | |||
184 | 44 | _5_MIN = 2 | 47 | _5_MIN = 2 |
185 | 45 | _10_MIN = 4 | 48 | _10_MIN = 4 |
186 | 46 | HOUR = 10 | 49 | HOUR = 10 |
187 | 50 | CUSTOM_HOUR = 15 | ||
188 | 47 | DAY = 20 | 51 | DAY = 20 |
189 | 48 | WEEK = 30 | 52 | WEEK = 30 |
190 | 49 | MONTH = 40 | 53 | MONTH = 40 |
191 | @@ -58,6 +62,7 @@ | |||
192 | 58 | _5_MIN: _('Every 5 minutes'), | 62 | _5_MIN: _('Every 5 minutes'), |
193 | 59 | _10_MIN: _('Every 10 minutes'), | 63 | _10_MIN: _('Every 10 minutes'), |
194 | 60 | HOUR : _('Every Hour'), | 64 | HOUR : _('Every Hour'), |
195 | 65 | CUSTOM_HOUR : _('Custom Hours'), | ||
196 | 61 | DAY : _('Every Day'), | 66 | DAY : _('Every Day'), |
197 | 62 | WEEK : _('Every Week'), | 67 | WEEK : _('Every Week'), |
198 | 63 | MONTH : _('Every Month') | 68 | MONTH : _('Every Month') |
199 | @@ -71,7 +76,30 @@ | |||
200 | 71 | 76 | ||
201 | 72 | MIN_FREE_SPACE_UNITS = { DISK_UNIT_MB : 'Mb', DISK_UNIT_GB : 'Gb' } | 77 | MIN_FREE_SPACE_UNITS = { DISK_UNIT_MB : 'Mb', DISK_UNIT_GB : 'Gb' } |
202 | 73 | 78 | ||
204 | 74 | DEFAULT_EXCLUDE = [ '.gvfs', '.cache*', '[Cc]ache*', '.thumbnails*', '[Tt]rash*', '*.backup*', '*~', os.path.expanduser( '~/Ubuntu One' ), '.dropbox*', '/proc', '/sys', '/dev' ] | 79 | DEFAULT_EXCLUDE = [ '.gvfs', '.cache*', '[Cc]ache*', '.thumbnails*', '[Tt]rash*', '*.backup*', '*~', os.path.expanduser( '~/Ubuntu One' ), '.dropbox*', '/proc', '/sys', '/dev' , '/tmp/backintime'] |
205 | 80 | |||
206 | 81 | SNAPSHOT_MODES = { | ||
207 | 82 | #mode : (<mounttools>, _('ComboBox Text') ), | ||
208 | 83 | 'local' : (None, _('Local') ), | ||
209 | 84 | 'ssh' : (sshtools.SSH, _('SSH (without password)') ) | ||
210 | 85 | ## 'dummy' : (dummytools.Dummy, _('Dummy') ) | ||
211 | 86 | } | ||
212 | 87 | |||
213 | 88 | MOUNT_ROOT = '/tmp/backintime' | ||
214 | 89 | |||
215 | 90 | SSH_CIPHERS = {'default': _('Default'), | ||
216 | 91 | 'aes128-ctr': _('AES128-CTR'), | ||
217 | 92 | 'aes192-ctr': _('AES192-CTR'), | ||
218 | 93 | 'aes256-ctr': _('AES256-CTR'), | ||
219 | 94 | 'arcfour256': _('ARCFOUR256'), | ||
220 | 95 | 'arcfour128': _('ARCFOUR128'), | ||
221 | 96 | 'aes128-cbc': _('AES128-CBC'), | ||
222 | 97 | '3des-cbc': _('3DES-CBC'), | ||
223 | 98 | 'blowfish-cbc': _('Blowfish-CBC'), | ||
224 | 99 | 'cast128-cbc': _('Cast128-CBC'), | ||
225 | 100 | 'aes192-cbc': _('AES192-CBC'), | ||
226 | 101 | 'aes256-cbc': _('AES256-CBC'), | ||
227 | 102 | 'arcfour': _('ARCFOUR') } | ||
228 | 75 | 103 | ||
229 | 76 | def __init__( self ): | 104 | def __init__( self ): |
230 | 77 | configfile.ConfigFileWithProfiles.__init__( self, _('Main profile') ) | 105 | configfile.ConfigFileWithProfiles.__init__( self, _('Main profile') ) |
231 | @@ -179,6 +207,8 @@ | |||
232 | 179 | 207 | ||
233 | 180 | self.set_int_value( 'config.version', self.CONFIG_VERSION ) | 208 | self.set_int_value( 'config.version', self.CONFIG_VERSION ) |
234 | 181 | self.save() | 209 | self.save() |
235 | 210 | |||
236 | 211 | self.current_hash_id = 'local' | ||
237 | 182 | 212 | ||
238 | 183 | def save( self ): | 213 | def save( self ): |
239 | 184 | configfile.ConfigFile.save( self, self._LOCAL_CONFIG_PATH ) | 214 | configfile.ConfigFile.save( self, self._LOCAL_CONFIG_PATH ) |
240 | @@ -254,8 +284,22 @@ | |||
241 | 254 | 284 | ||
242 | 255 | return user | 285 | return user |
243 | 256 | 286 | ||
246 | 257 | def get_snapshots_path( self, profile_id = None ): | 287 | def get_pid(self): |
247 | 258 | return self.get_profile_str_value( 'snapshots.path', '', profile_id ) | 288 | return str(os.getpid()) |
248 | 289 | |||
249 | 290 | def get_host(self): | ||
250 | 291 | return socket.gethostname() | ||
251 | 292 | |||
252 | 293 | def get_snapshots_path( self, profile_id = None, mode = None, tmp_mount = False ): | ||
253 | 294 | if mode is None: | ||
254 | 295 | mode = self.get_snapshots_mode(profile_id) | ||
255 | 296 | if self.SNAPSHOT_MODES[mode][0] == None: | ||
256 | 297 | #no mount needed | ||
257 | 298 | return self.get_profile_str_value( 'snapshots.path', '', profile_id ) | ||
258 | 299 | else: | ||
259 | 300 | #mode need to be mounted; return mountpoint | ||
260 | 301 | symlink = self.get_snapshots_symlink(profile_id = profile_id, tmp_mount = tmp_mount) | ||
261 | 302 | return os.path.join(self.MOUNT_ROOT, self.get_user(), symlink) | ||
262 | 259 | 303 | ||
263 | 260 | def get_snapshots_full_path( self, profile_id = None, version = None ): | 304 | def get_snapshots_full_path( self, profile_id = None, version = None ): |
264 | 261 | '''Returns the full path for the snapshots: .../backintime/machine/user/profile_id/''' | 305 | '''Returns the full path for the snapshots: .../backintime/machine/user/profile_id/''' |
265 | @@ -268,7 +312,7 @@ | |||
266 | 268 | host, user, profile = self.get_host_user_profile( profile_id ) | 312 | host, user, profile = self.get_host_user_profile( profile_id ) |
267 | 269 | return os.path.join( self.get_snapshots_path( profile_id ), 'backintime', host, user, profile ) | 313 | return os.path.join( self.get_snapshots_path( profile_id ), 'backintime', host, user, profile ) |
268 | 270 | 314 | ||
270 | 271 | def set_snapshots_path( self, value, profile_id = None ): | 315 | def set_snapshots_path( self, value, profile_id = None, mode = None ): |
271 | 272 | """Sets the snapshot path to value, initializes, and checks it""" | 316 | """Sets the snapshot path to value, initializes, and checks it""" |
272 | 273 | if len( value ) <= 0: | 317 | if len( value ) <= 0: |
273 | 274 | return False | 318 | return False |
274 | @@ -278,6 +322,9 @@ | |||
275 | 278 | # tjoep | 322 | # tjoep |
276 | 279 | # return False | 323 | # return False |
277 | 280 | profile_id = self.get_current_profile() | 324 | profile_id = self.get_current_profile() |
278 | 325 | |||
279 | 326 | if mode is None: | ||
280 | 327 | mode = self.get_snapshots_mode( profile_id ) | ||
281 | 281 | 328 | ||
282 | 282 | if not os.path.isdir( value ): | 329 | if not os.path.isdir( value ): |
283 | 283 | self.notify_error( _( '%s is not a folder !' ) % value ) | 330 | self.notify_error( _( '%s is not a folder !' ) % value ) |
284 | @@ -312,8 +359,100 @@ | |||
285 | 312 | return False | 359 | return False |
286 | 313 | 360 | ||
287 | 314 | os.rmdir( check_path ) | 361 | os.rmdir( check_path ) |
290 | 315 | self.set_profile_str_value( 'snapshots.path', value, profile_id ) | 362 | if self.SNAPSHOT_MODES[mode][0] is None: |
291 | 316 | return True | 363 | self.set_profile_str_value( 'snapshots.path', value, profile_id ) |
292 | 364 | return True | ||
293 | 365 | |||
294 | 366 | def get_snapshots_mode( self, profile_id = None ): | ||
295 | 367 | return self.get_profile_str_value( 'snapshots.mode', 'local', profile_id ) | ||
296 | 368 | |||
297 | 369 | def set_snapshots_mode( self, value, profile_id = None ): | ||
298 | 370 | self.set_profile_str_value( 'snapshots.mode', value, profile_id ) | ||
299 | 371 | |||
300 | 372 | def get_snapshots_symlink(self, profile_id = None, tmp_mount = False): | ||
301 | 373 | if profile_id is None: | ||
302 | 374 | profile_id = self.current_profile_id | ||
303 | 375 | symlink = '%s_%s' % (profile_id, self.get_pid()) | ||
304 | 376 | if tmp_mount: | ||
305 | 377 | symlink = 'tmp_%s' % symlink | ||
306 | 378 | return symlink | ||
307 | 379 | |||
308 | 380 | def set_current_hash_id(self, hash_id): | ||
309 | 381 | self.current_hash_id = hash_id | ||
310 | 382 | |||
311 | 383 | def get_hash_collision(self): | ||
312 | 384 | return self.get_int_value( 'global.hash_collision', 0 ) | ||
313 | 385 | |||
314 | 386 | def increment_hash_collision(self): | ||
315 | 387 | value = self.get_hash_collision() + 1 | ||
316 | 388 | self.set_int_value( 'global.hash_collision', value ) | ||
317 | 389 | |||
318 | 390 | def get_snapshots_path_ssh( self, profile_id = None ): | ||
319 | 391 | return self.get_profile_str_value( 'snapshots.ssh.path', './', profile_id ) | ||
320 | 392 | |||
321 | 393 | def get_snapshots_full_path_ssh( self, profile_id = None, version = None ): | ||
322 | 394 | '''Returns the full path for the snapshots: .../backintime/machine/user/profile_id/''' | ||
323 | 395 | if version is None: | ||
324 | 396 | version = self.get_int_value( 'config.version', self.CONFIG_VERSION ) | ||
325 | 397 | |||
326 | 398 | if version < 4: | ||
327 | 399 | return os.path.join( self.get_snapshots_path_ssh( profile_id ), 'backintime' ) | ||
328 | 400 | else: | ||
329 | 401 | host, user, profile = self.get_host_user_profile( profile_id ) | ||
330 | 402 | return os.path.join( self.get_snapshots_path_ssh( profile_id ), 'backintime', host, user, profile ) | ||
331 | 403 | |||
332 | 404 | def set_snapshots_path_ssh( self, value, profile_id = None ): | ||
333 | 405 | self.set_profile_str_value( 'snapshots.ssh.path', value, profile_id ) | ||
334 | 406 | return True | ||
335 | 407 | |||
336 | 408 | def get_ssh_host( self, profile_id = None ): | ||
337 | 409 | return self.get_profile_str_value( 'snapshots.ssh.host', '', profile_id ) | ||
338 | 410 | |||
339 | 411 | def set_ssh_host( self, value, profile_id = None ): | ||
340 | 412 | self.set_profile_str_value( 'snapshots.ssh.host', value, profile_id ) | ||
341 | 413 | |||
342 | 414 | def get_ssh_port( self, profile_id = None ): | ||
343 | 415 | return self.get_profile_int_value( 'snapshots.ssh.port', '22', profile_id ) | ||
344 | 416 | |||
345 | 417 | def set_ssh_port( self, value, profile_id = None ): | ||
346 | 418 | self.set_profile_int_value( 'snapshots.ssh.port', value, profile_id ) | ||
347 | 419 | |||
348 | 420 | def get_ssh_cipher( self, profile_id = None ): | ||
349 | 421 | return self.get_profile_str_value( 'snapshots.ssh.cipher', 'default', profile_id ) | ||
350 | 422 | |||
351 | 423 | def set_ssh_cipher( self, value, profile_id = None ): | ||
352 | 424 | self.set_profile_str_value( 'snapshots.ssh.cipher', value, profile_id ) | ||
353 | 425 | |||
354 | 426 | def get_ssh_user( self, profile_id = None ): | ||
355 | 427 | return self.get_profile_str_value( 'snapshots.ssh.user', self.get_user(), profile_id ) | ||
356 | 428 | |||
357 | 429 | def set_ssh_user( self, value, profile_id = None ): | ||
358 | 430 | self.set_profile_str_value( 'snapshots.ssh.user', value, profile_id ) | ||
359 | 431 | |||
360 | 432 | def get_ssh_host_port_user_path(self, profile_id = None ): | ||
361 | 433 | host = self.get_ssh_host(profile_id) | ||
362 | 434 | port = self.get_ssh_port(profile_id) | ||
363 | 435 | user = self.get_ssh_user(profile_id) | ||
364 | 436 | path = self.get_snapshots_path_ssh(profile_id) | ||
365 | 437 | return (host, port, user, path) | ||
366 | 438 | |||
367 | 439 | ## def get_dummy_host( self, profile_id = None ): | ||
368 | 440 | ## return self.get_profile_str_value( 'snapshots.dummy.host', '', profile_id ) | ||
369 | 441 | ## | ||
370 | 442 | ## def set_dummy_host( self, value, profile_id = None ): | ||
371 | 443 | ## self.set_profile_str_value( 'snapshots.dummy.host', value, profile_id ) | ||
372 | 444 | ## | ||
373 | 445 | ## def get_dummy_port( self, profile_id = None ): | ||
374 | 446 | ## return self.get_profile_int_value( 'snapshots.dummy.port', '22', profile_id ) | ||
375 | 447 | ## | ||
376 | 448 | ## def set_dummy_port( self, value, profile_id = None ): | ||
377 | 449 | ## self.set_profile_int_value( 'snapshots.dummy.port', value, profile_id ) | ||
378 | 450 | ## | ||
379 | 451 | ## def get_dummy_user( self, profile_id = None ): | ||
380 | 452 | ## return self.get_profile_str_value( 'snapshots.dummy.user', self.get_user(), profile_id ) | ||
381 | 453 | ## | ||
382 | 454 | ## def set_dummy_user( self, value, profile_id = None ): | ||
383 | 455 | ## self.set_profile_str_value( 'snapshots.dummy.user', value, profile_id ) | ||
384 | 317 | 456 | ||
385 | 318 | def get_auto_host_user_profile( self, profile_id = None ): | 457 | def get_auto_host_user_profile( self, profile_id = None ): |
386 | 319 | return self.get_profile_bool_value( 'snapshots.path.auto', True, profile_id ) | 458 | return self.get_profile_bool_value( 'snapshots.path.auto', True, profile_id ) |
387 | @@ -494,6 +633,12 @@ | |||
388 | 494 | def set_automatic_backup_weekday( self, value, profile_id = None ): | 633 | def set_automatic_backup_weekday( self, value, profile_id = None ): |
389 | 495 | self.set_profile_int_value( 'snapshots.automatic_backup_weekday', value, profile_id ) | 634 | self.set_profile_int_value( 'snapshots.automatic_backup_weekday', value, profile_id ) |
390 | 496 | 635 | ||
391 | 636 | def get_custom_backup_time( self, profile_id = None ): | ||
392 | 637 | return self.get_profile_str_value( 'snapshots.custom_backup_time', '8,12,18,23', profile_id ) | ||
393 | 638 | |||
394 | 639 | def set_custom_backup_time( self, value, profile_id = None ): | ||
395 | 640 | self.set_profile_str_value( 'snapshots.custom_backup_time', value, profile_id ) | ||
396 | 641 | |||
397 | 497 | #def get_per_directory_schedule( self, profile_id = None ): | 642 | #def get_per_directory_schedule( self, profile_id = None ): |
398 | 498 | # return self.get_profile_bool_value( 'snapshots.expert.per_directory_schedule', False, profile_id ) | 643 | # return self.get_profile_bool_value( 'snapshots.expert.per_directory_schedule', False, profile_id ) |
399 | 499 | 644 | ||
400 | @@ -816,6 +961,8 @@ | |||
401 | 816 | cron_line = 'echo "{msg}\n*/10 * * * * {cmd}"' | 961 | cron_line = 'echo "{msg}\n*/10 * * * * {cmd}"' |
402 | 817 | if self.HOUR == backup_mode: | 962 | if self.HOUR == backup_mode: |
403 | 818 | cron_line = 'echo "{msg}\n0 * * * * {cmd}"' | 963 | cron_line = 'echo "{msg}\n0 * * * * {cmd}"' |
404 | 964 | if self.CUSTOM_HOUR == backup_mode: | ||
405 | 965 | cron_line = 'echo "{msg}\n0 ' + self.get_custom_backup_time( profile_id ) + ' * * * {cmd}"' | ||
406 | 819 | elif self.DAY == backup_mode: | 966 | elif self.DAY == backup_mode: |
407 | 820 | cron_line = "echo \"{msg}\n%s %s * * * {cmd}\"" % (minute, hour) | 967 | cron_line = "echo \"{msg}\n%s %s * * * {cmd}\"" % (minute, hour) |
408 | 821 | elif self.WEEK == backup_mode: | 968 | elif self.WEEK == backup_mode: |
409 | 822 | 969 | ||
410 | === modified file 'common/configfile.py' | |||
411 | --- common/configfile.py 2010-03-05 20:23:32 +0000 | |||
412 | +++ common/configfile.py 2012-10-23 19:40:24 +0000 | |||
413 | @@ -347,4 +347,3 @@ | |||
414 | 347 | 347 | ||
415 | 348 | def set_profile_bool_value( self, key, value, profile_id = None ): | 348 | def set_profile_bool_value( self, key, value, profile_id = None ): |
416 | 349 | self.set_bool_value( self._get_profile_key_( key, profile_id ), value ) | 349 | self.set_bool_value( self._get_profile_key_( key, profile_id ), value ) |
417 | 350 | |||
418 | 351 | 350 | ||
419 | === modified file 'common/debian_specific/control' | |||
420 | --- common/debian_specific/control 2012-03-06 21:23:31 +0000 | |||
421 | +++ common/debian_specific/control 2012-10-23 19:40:24 +0000 | |||
422 | @@ -6,7 +6,7 @@ | |||
423 | 6 | Maintainer: BIT Team <bit-team@lists.launchpad.net> | 6 | Maintainer: BIT Team <bit-team@lists.launchpad.net> |
424 | 7 | Homepage: http://backintime.le-web.org | 7 | Homepage: http://backintime.le-web.org |
425 | 8 | Architecture: all | 8 | Architecture: all |
427 | 9 | Depends: python, rsync, cron | 9 | Depends: python, rsync, cron, openssh-client, sshfs |
428 | 10 | Recommends: powermgmt-base | 10 | Recommends: powermgmt-base |
429 | 11 | Conflicts: backintime | 11 | Conflicts: backintime |
430 | 12 | Replaces: backintime | 12 | Replaces: backintime |
431 | 13 | 13 | ||
432 | === added file 'common/dummytools.py' | |||
433 | --- common/dummytools.py 1970-01-01 00:00:00 +0000 | |||
434 | +++ common/dummytools.py 2012-10-23 19:40:24 +0000 | |||
435 | @@ -0,0 +1,105 @@ | |||
436 | 1 | # Copyright (c) 2012 Germar Reitze | ||
437 | 2 | # | ||
438 | 3 | # This program is free software; you can redistribute it and/or modify | ||
439 | 4 | # it under the terms of the GNU General Public License as published by | ||
440 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
441 | 6 | # (at your option) any later version. | ||
442 | 7 | # | ||
443 | 8 | # This program is distributed in the hope that it will be useful, | ||
444 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
445 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
446 | 11 | # GNU General Public License for more details. | ||
447 | 12 | # | ||
448 | 13 | # You should have received a copy of the GNU General Public License along | ||
449 | 14 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
450 | 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
451 | 16 | |||
452 | 17 | import gettext | ||
453 | 18 | |||
454 | 19 | import config | ||
455 | 20 | import mount | ||
456 | 21 | |||
457 | 22 | _=gettext.gettext | ||
458 | 23 | |||
459 | 24 | class Dummy(mount.MountControl): | ||
460 | 25 | """ | ||
461 | 26 | This is a template for mounting services. For simple mount services | ||
462 | 27 | all you need to do is: | ||
463 | 28 | - add your settings in gnome|kde/settingsdialog.py (search for the dummy examples) | ||
464 | 29 | - add settings in gnome/settingsdialog.glade (copy GtkFrame 'mode_dummy') | ||
465 | 30 | - add settings in common/config.py (search for the dummy examples) | ||
466 | 31 | - modify a copy of this file | ||
467 | 32 | |||
468 | 33 | Please use self.mountpoint as your local mountpoint. | ||
469 | 34 | This class inherit from mount.MountControl. All methodes from MountControl can | ||
470 | 35 | be used exactly like they were in this class. | ||
471 | 36 | Methodes from MountControl also can be overriden in here if you need | ||
472 | 37 | something different.""" | ||
473 | 38 | def __init__(self, cfg = None, profile_id = None, hash_id = None, tmp_mount = False, **kwargs): | ||
474 | 39 | self.config = cfg | ||
475 | 40 | if self.config is None: | ||
476 | 41 | self.config = config.Config() | ||
477 | 42 | |||
478 | 43 | self.profile_id = profile_id | ||
479 | 44 | if not self.profile_id: | ||
480 | 45 | self.profile_id = self.config.get_current_profile() | ||
481 | 46 | |||
482 | 47 | self.tmp_mount = tmp_mount | ||
483 | 48 | self.hash_id = hash_id | ||
484 | 49 | |||
485 | 50 | #init MountControl | ||
486 | 51 | mount.MountControl.__init__(self) | ||
487 | 52 | |||
488 | 53 | self.all_kwargs = {} | ||
489 | 54 | |||
490 | 55 | #First we need to map the settings. | ||
491 | 56 | #If <arg> is in kwargs (e.g. if this class is called with dummytools.Dummy(<arg> = <value>) | ||
492 | 57 | #this will map self.<arg> to kwargs[<arg>]; else self.<arg> = <default> from config | ||
493 | 58 | #e.g. self.setattr_kwargs(<arg>, <default>, **kwargs) | ||
494 | 59 | self.setattr_kwargs('mode', self.config.get_snapshots_mode(self.profile_id), **kwargs) | ||
495 | 60 | self.setattr_kwargs('hash_collision', self.config.get_hash_collision(), **kwargs) | ||
496 | 61 | #start editing from here--------------------------------------------------------- | ||
497 | 62 | self.setattr_kwargs('user', self.config.get_dummy_user(self.profile_id), **kwargs) | ||
498 | 63 | self.setattr_kwargs('host', self.config.get_dummy_host(self.profile_id), **kwargs) | ||
499 | 64 | self.setattr_kwargs('port', self.config.get_dummy_port(self.profile_id), **kwargs) | ||
500 | 65 | |||
501 | 66 | self.set_default_args() | ||
502 | 67 | |||
503 | 68 | #if self.mountpoint is not the remote snapshot path you can specify | ||
504 | 69 | #a subfolder of self.mountpoint for the symlink | ||
505 | 70 | self.symlink_subfolder = None | ||
506 | 71 | |||
507 | 72 | self.log_command = '%s: %s@%s' % (self.mode, self.user, self.host) | ||
508 | 73 | |||
509 | 74 | def _mount(self): | ||
510 | 75 | """mount the service""" | ||
511 | 76 | #implement your mountprocess here | ||
512 | 77 | pass | ||
513 | 78 | |||
514 | 79 | def _umount(self): | ||
515 | 80 | """umount the service""" | ||
516 | 81 | #implement your unmountprocess here | ||
517 | 82 | pass | ||
518 | 83 | |||
519 | 84 | def pre_mount_check(self, first_run = False): | ||
520 | 85 | """check what ever conditions must be given for the mount to be done successful | ||
521 | 86 | raise MountException( _('Error discription') ) if service can not mount | ||
522 | 87 | return True if everything is okay | ||
523 | 88 | all pre|post_[u]mount_check can also be used to prepare things or clean up""" | ||
524 | 89 | return True | ||
525 | 90 | |||
526 | 91 | def post_mount_check(self): | ||
527 | 92 | """check if mount was successful | ||
528 | 93 | raise MountException( _('Error discription') ) if not""" | ||
529 | 94 | return True | ||
530 | 95 | |||
531 | 96 | def pre_umount_check(self): | ||
532 | 97 | """check if service is safe to umount | ||
533 | 98 | raise MountException( _('Error discription') ) if not""" | ||
534 | 99 | return True | ||
535 | 100 | |||
536 | 101 | def post_umount_check(self): | ||
537 | 102 | """check if umount successful | ||
538 | 103 | raise MountException( _('Error discription') ) if not""" | ||
539 | 104 | return True | ||
540 | 105 | |||
541 | 0 | \ No newline at end of file | 106 | \ No newline at end of file |
542 | 1 | 107 | ||
543 | === modified file 'common/man/C/backintime.1.gz' | |||
544 | 2 | Binary files common/man/C/backintime.1.gz 2012-03-06 21:23:31 +0000 and common/man/C/backintime.1.gz 2012-10-23 19:40:24 +0000 differ | 108 | Binary files common/man/C/backintime.1.gz 2012-03-06 21:23:31 +0000 and common/man/C/backintime.1.gz 2012-10-23 19:40:24 +0000 differ |
545 | === added file 'common/mount.py' | |||
546 | --- common/mount.py 1970-01-01 00:00:00 +0000 | |||
547 | +++ common/mount.py 2012-10-23 19:40:24 +0000 | |||
548 | @@ -0,0 +1,421 @@ | |||
549 | 1 | # Copyright (c) 2012 Germar Reitze | ||
550 | 2 | # | ||
551 | 3 | # This program is free software; you can redistribute it and/or modify | ||
552 | 4 | # it under the terms of the GNU General Public License as published by | ||
553 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
554 | 6 | # (at your option) any later version. | ||
555 | 7 | # | ||
556 | 8 | # This program is distributed in the hope that it will be useful, | ||
557 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
558 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
559 | 11 | # GNU General Public License for more details. | ||
560 | 12 | # | ||
561 | 13 | # You should have received a copy of the GNU General Public License along | ||
562 | 14 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
563 | 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
564 | 16 | |||
565 | 17 | import os | ||
566 | 18 | import subprocess | ||
567 | 19 | import socket | ||
568 | 20 | import json | ||
569 | 21 | import gettext | ||
570 | 22 | from zlib import crc32 | ||
571 | 23 | from time import sleep | ||
572 | 24 | |||
573 | 25 | import config | ||
574 | 26 | import logger | ||
575 | 27 | |||
576 | 28 | _=gettext.gettext | ||
577 | 29 | |||
578 | 30 | class MountException(Exception): | ||
579 | 31 | pass | ||
580 | 32 | |||
581 | 33 | class HashCollision(Exception): | ||
582 | 34 | pass | ||
583 | 35 | |||
584 | 36 | class Mount(object): | ||
585 | 37 | def __init__(self, cfg = None, profile_id = None, tmp_mount = False): | ||
586 | 38 | self.config = cfg | ||
587 | 39 | if self.config is None: | ||
588 | 40 | self.config = config.Config() | ||
589 | 41 | |||
590 | 42 | self.profile_id = profile_id | ||
591 | 43 | if self.profile_id is None: | ||
592 | 44 | self.profile_id = self.config.get_current_profile() | ||
593 | 45 | |||
594 | 46 | self.tmp_mount = tmp_mount | ||
595 | 47 | |||
596 | 48 | def mount(self, mode = None, check = True, **kwargs): | ||
597 | 49 | if mode is None: | ||
598 | 50 | mode = self.config.get_snapshots_mode(self.profile_id) | ||
599 | 51 | |||
600 | 52 | if self.config.SNAPSHOT_MODES[mode][0] is None: | ||
601 | 53 | #mode doesn't need to mount | ||
602 | 54 | return 'local' | ||
603 | 55 | else: | ||
604 | 56 | while True: | ||
605 | 57 | try: | ||
606 | 58 | mounttools = self.config.SNAPSHOT_MODES[mode][0] | ||
607 | 59 | tools = mounttools(cfg = self.config, profile_id = self.profile_id, tmp_mount = self.tmp_mount, mode = mode, **kwargs) | ||
608 | 60 | return tools.mount(check = check) | ||
609 | 61 | except HashCollision as ex: | ||
610 | 62 | logger.warning(str(ex)) | ||
611 | 63 | del tools | ||
612 | 64 | check = False | ||
613 | 65 | continue | ||
614 | 66 | break | ||
615 | 67 | |||
616 | 68 | def umount(self, hash_id = None): | ||
617 | 69 | if hash_id is None: | ||
618 | 70 | hash_id = self.config.current_hash_id | ||
619 | 71 | if hash_id == 'local': | ||
620 | 72 | #mode doesn't need to umount | ||
621 | 73 | return | ||
622 | 74 | else: | ||
623 | 75 | umount_info = os.path.join(self.config.MOUNT_ROOT, self.config.get_user(), 'mnt', hash_id, 'umount') | ||
624 | 76 | with open(umount_info, 'r') as f: | ||
625 | 77 | data_string = f.read() | ||
626 | 78 | f.close() | ||
627 | 79 | kwargs = json.loads(data_string) | ||
628 | 80 | mode = kwargs.pop('mode') | ||
629 | 81 | mounttools = self.config.SNAPSHOT_MODES[mode][0] | ||
630 | 82 | tools = mounttools(cfg = self.config, profile_id = self.profile_id, tmp_mount = self.tmp_mount, mode = mode, hash_id = hash_id, **kwargs) | ||
631 | 83 | tools.umount() | ||
632 | 84 | |||
633 | 85 | def pre_mount_check(self, mode = None, first_run = False, **kwargs): | ||
634 | 86 | """called by SettingsDialog.save_profile() to check | ||
635 | 87 | if settings are correct before saving""" | ||
636 | 88 | if mode is None: | ||
637 | 89 | mode = self.config.get_snapshots_mode(self.profile_id) | ||
638 | 90 | |||
639 | 91 | if self.config.SNAPSHOT_MODES[mode][0] is None: | ||
640 | 92 | #mode doesn't need to mount | ||
641 | 93 | return True | ||
642 | 94 | else: | ||
643 | 95 | mounttools = self.config.SNAPSHOT_MODES[mode][0] | ||
644 | 96 | tools = mounttools(cfg = self.config, profile_id = self.profile_id, tmp_mount = self.tmp_mount, mode = mode, **kwargs) | ||
645 | 97 | return tools.pre_mount_check(first_run) | ||
646 | 98 | |||
647 | 99 | def remount(self, new_profile_id, mode = None, hash_id = None, **kwargs): | ||
648 | 100 | """mode <= new profile | ||
649 | 101 | kwargs <= new profile | ||
650 | 102 | hash_id <= old profile | ||
651 | 103 | self.profile_id <= old profile""" | ||
652 | 104 | if mode is None: | ||
653 | 105 | mode = self.config.get_snapshots_mode(new_profile_id) | ||
654 | 106 | if hash_id is None: | ||
655 | 107 | hash_id = self.config.current_hash_id | ||
656 | 108 | |||
657 | 109 | if self.config.SNAPSHOT_MODES[mode][0] is None: | ||
658 | 110 | #new profile don't need to mount. | ||
659 | 111 | self.umount(hash_id = hash_id) | ||
660 | 112 | return 'local' | ||
661 | 113 | |||
662 | 114 | if hash_id == 'local': | ||
663 | 115 | #old profile don't need to umount. | ||
664 | 116 | self.profile_id = new_profile_id | ||
665 | 117 | return self.mount(mode = mode, **kwargs) | ||
666 | 118 | |||
667 | 119 | mounttools = self.config.SNAPSHOT_MODES[mode][0] | ||
668 | 120 | tools = mounttools(cfg = self.config, profile_id = new_profile_id, tmp_mount = self.tmp_mount, mode = mode, **kwargs) | ||
669 | 121 | if tools.compare_remount(hash_id): | ||
670 | 122 | #profiles uses the same settings. just swap the symlinks | ||
671 | 123 | tools.remove_symlink(profile_id = self.profile_id) | ||
672 | 124 | tools.set_symlink(profile_id = new_profile_id, hash_id = hash_id) | ||
673 | 125 | return hash_id | ||
674 | 126 | else: | ||
675 | 127 | #profiles are different. we need to umount and mount again | ||
676 | 128 | self.umount(hash_id = hash_id) | ||
677 | 129 | self.profile_id = new_profile_id | ||
678 | 130 | return self.mount(mode = mode, **kwargs) | ||
679 | 131 | |||
680 | 132 | class MountControl(object): | ||
681 | 133 | def __init__(self): | ||
682 | 134 | self.local_host = self.config.get_host() | ||
683 | 135 | self.local_user = self.config.get_user() | ||
684 | 136 | self.pid = self.config.get_pid() | ||
685 | 137 | |||
686 | 138 | def set_default_args(self): | ||
687 | 139 | #self.destination should contain all arguments that are nessesary for mount. | ||
688 | 140 | args = self.all_kwargs.keys() | ||
689 | 141 | self.destination = '%s:' % self.all_kwargs['mode'] | ||
690 | 142 | args.remove('mode') | ||
691 | 143 | args.sort() | ||
692 | 144 | for arg in args: | ||
693 | 145 | self.destination += ' %s' % self.all_kwargs[arg] | ||
694 | 146 | |||
695 | 147 | #unique id for every different mount settings. Similar settings even in | ||
696 | 148 | #different profiles will generate the same hash_id and so share the same | ||
697 | 149 | #mountpoint | ||
698 | 150 | if self.hash_id is None: | ||
699 | 151 | self.hash_id = self.hash(self.destination) | ||
700 | 152 | |||
701 | 153 | self.mount_root = self.config.MOUNT_ROOT | ||
702 | 154 | self.mount_user_path = os.path.join(self.mount_root, self.local_user) | ||
703 | 155 | self.snapshots_path = self.config.get_snapshots_path(profile_id = self.profile_id, mode = self.mode, tmp_mount = self.tmp_mount) | ||
704 | 156 | |||
705 | 157 | self.hash_id_path = self.get_hash_id_path() | ||
706 | 158 | self.mountpoint = self.get_mountpoint() | ||
707 | 159 | self.lock_path = self.get_lock_path() | ||
708 | 160 | self.umount_info = self.get_umount_info() | ||
709 | 161 | |||
710 | 162 | def mount(self, check = True): | ||
711 | 163 | self.create_mountstructure() | ||
712 | 164 | self.mountprocess_lock_acquire() | ||
713 | 165 | try: | ||
714 | 166 | if self.is_mounted(): | ||
715 | 167 | if not self.compare_umount_info(): | ||
716 | 168 | #We probably have a hash collision | ||
717 | 169 | self.config.increment_hash_collision() | ||
718 | 170 | raise HashCollision( _('Hash collision occurred in hash_id %s. Incrementing global value hash_collision and try again.') % self.hash_id) | ||
719 | 171 | logger.info('Mountpoint %s is already mounted' % self.mountpoint) | ||
720 | 172 | else: | ||
721 | 173 | if check: | ||
722 | 174 | self.pre_mount_check() | ||
723 | 175 | self._mount() | ||
724 | 176 | self.post_mount_check() | ||
725 | 177 | logger.info('mount %s on %s' % (self.log_command, self.mountpoint)) | ||
726 | 178 | self.write_umount_info() | ||
727 | 179 | except Exception: | ||
728 | 180 | raise | ||
729 | 181 | else: | ||
730 | 182 | self.set_mount_lock() | ||
731 | 183 | self.set_symlink() | ||
732 | 184 | finally: | ||
733 | 185 | self.mountprocess_lock_release() | ||
734 | 186 | return self.hash_id | ||
735 | 187 | |||
736 | 188 | def umount(self): | ||
737 | 189 | self.mountprocess_lock_acquire() | ||
738 | 190 | try: | ||
739 | 191 | if not os.path.isdir(self.hash_id_path): | ||
740 | 192 | logger.info('Mountpoint %s does not exist.' % self.hash_id_path) | ||
741 | 193 | else: | ||
742 | 194 | if not self.is_mounted(): | ||
743 | 195 | logger.info('Mountpoint %s is not mounted' % self.hash_id_path) | ||
744 | 196 | else: | ||
745 | 197 | if self.check_mount_lock(): | ||
746 | 198 | logger.info('Mountpoint %s still in use. Keep mounted' % self.mountpoint) | ||
747 | 199 | else: | ||
748 | 200 | self.pre_umount_check() | ||
749 | 201 | self._umount() | ||
750 | 202 | self.post_umount_check() | ||
751 | 203 | if len(os.listdir(self.mountpoint)) > 0: | ||
752 | 204 | logger.warning('Mountpoint %s not empty after unmount' % self.mountpoint) | ||
753 | 205 | else: | ||
754 | 206 | logger.info('unmount %s from %s' % (self.log_command, self.mountpoint)) | ||
755 | 207 | except Exception: | ||
756 | 208 | raise | ||
757 | 209 | else: | ||
758 | 210 | self.del_mount_lock() | ||
759 | 211 | self.remove_symlink() | ||
760 | 212 | finally: | ||
761 | 213 | self.mountprocess_lock_release() | ||
762 | 214 | |||
763 | 215 | def is_mounted(self): | ||
764 | 216 | """return True if path is is already mounted""" | ||
765 | 217 | try: | ||
766 | 218 | subprocess.check_call(['mountpoint', self.mountpoint], stdout=open(os.devnull, 'w')) | ||
767 | 219 | except subprocess.CalledProcessError: | ||
768 | 220 | if len(os.listdir(self.mountpoint)) > 0: | ||
769 | 221 | raise MountException( _('mountpoint %s not empty.') % self.mountpoint) | ||
770 | 222 | return False | ||
771 | 223 | else: | ||
772 | 224 | return True | ||
773 | 225 | |||
774 | 226 | def create_mountstructure(self): | ||
775 | 227 | """ folder structure in /tmp/backintime/<user>/: | ||
776 | 228 | mnt/ <= used for mount points | ||
777 | 229 | <pid>.lock <= mountprocess lock that will prevent different | ||
778 | 230 | processes modifying mountpoints at one time | ||
779 | 231 | <hash_id>/ <= will be shared by all profiles with the same mount settings | ||
780 | 232 | mountpoint/ <= real mountpoint | ||
781 | 233 | umount <= json file with all nessesary args for unmount | ||
782 | 234 | locks/ <= for each process you have a <pid>.lock file | ||
783 | 235 | <profile id>_<pid>/ <= sym-link to the right path. return by config.get_snapshots_path | ||
784 | 236 | (can be ../mnt/<hash_id>/mount_point for ssh or | ||
785 | 237 | ../mnt/<hash_id>/<HOST>/<SHARE> for fusesmb ...) | ||
786 | 238 | tmp_<profile id>_<pid>/ <= sym-link for testing mountpoints in settingsdialog | ||
787 | 239 | """ | ||
788 | 240 | self.mkdir(self.mount_root, 0777, force_chmod = True) | ||
789 | 241 | self.mkdir(self.mount_user_path, 0700) | ||
790 | 242 | self.mkdir(os.path.join(self.mount_user_path, 'mnt'), 0700) | ||
791 | 243 | self.mkdir(self.hash_id_path, 0700) | ||
792 | 244 | self.mkdir(self.mountpoint, 0700) | ||
793 | 245 | self.mkdir(self.lock_path, 0700) | ||
794 | 246 | |||
795 | 247 | def mkdir(self, path, mode = 0777, force_chmod = False): | ||
796 | 248 | if not os.path.isdir(path): | ||
797 | 249 | os.mkdir(path, mode) | ||
798 | 250 | if force_chmod: | ||
799 | 251 | #hack: debian and ubuntu won't set go+w on mkdir in tmp | ||
800 | 252 | os.chmod(path, mode) | ||
801 | 253 | |||
802 | 254 | def mountprocess_lock_acquire(self, timeout = 60): | ||
803 | 255 | """block while an other process is mounting or unmounting""" | ||
804 | 256 | lock_path = os.path.join(self.mount_user_path, 'mnt') | ||
805 | 257 | lock_suffix = '.lock' | ||
806 | 258 | lock = self.pid + lock_suffix | ||
807 | 259 | count = 0 | ||
808 | 260 | while self.check_locks(lock_path, lock_suffix): | ||
809 | 261 | count += 1 | ||
810 | 262 | if count == timeout: | ||
811 | 263 | raise MountException( _('Mountprocess lock timeout') ) | ||
812 | 264 | sleep(1) | ||
813 | 265 | |||
814 | 266 | with open(os.path.join(lock_path, lock), 'w') as f: | ||
815 | 267 | f.write(self.pid) | ||
816 | 268 | f.close() | ||
817 | 269 | |||
818 | 270 | def mountprocess_lock_release(self): | ||
819 | 271 | lock_path = os.path.join(self.mount_user_path, 'mnt') | ||
820 | 272 | lock_suffix = '.lock' | ||
821 | 273 | lock = os.path.join(lock_path, self.pid + lock_suffix) | ||
822 | 274 | if os.path.exists(lock): | ||
823 | 275 | os.remove(lock) | ||
824 | 276 | |||
825 | 277 | def set_mount_lock(self): | ||
826 | 278 | """lock mount for this process""" | ||
827 | 279 | if self.tmp_mount: | ||
828 | 280 | lock_suffix = '.tmp.lock' | ||
829 | 281 | else: | ||
830 | 282 | lock_suffix = '.lock' | ||
831 | 283 | lock = self.pid + lock_suffix | ||
832 | 284 | with open(os.path.join(self.lock_path, lock), 'w') as f: | ||
833 | 285 | f.write(self.pid) | ||
834 | 286 | f.close() | ||
835 | 287 | |||
836 | 288 | def check_mount_lock(self): | ||
837 | 289 | """return True if mount is locked by other processes""" | ||
838 | 290 | lock_suffix = '.lock' | ||
839 | 291 | return self.check_locks(self.lock_path, lock_suffix) | ||
840 | 292 | |||
841 | 293 | def del_mount_lock(self): | ||
842 | 294 | """remove mount lock for this process""" | ||
843 | 295 | if self.tmp_mount: | ||
844 | 296 | lock_suffix = '.tmp.lock' | ||
845 | 297 | else: | ||
846 | 298 | lock_suffix = '.lock' | ||
847 | 299 | lock = os.path.join(self.lock_path, self.pid + lock_suffix) | ||
848 | 300 | if os.path.exists(lock): | ||
849 | 301 | os.remove(lock) | ||
850 | 302 | |||
851 | 303 | def check_process_alive(self, pid): | ||
852 | 304 | """check if process is still alive""" | ||
853 | 305 | if os.path.exists(os.path.join('/proc', pid)): | ||
854 | 306 | return True | ||
855 | 307 | return False | ||
856 | 308 | |||
857 | 309 | def check_locks(self, path, lock_suffix): | ||
858 | 310 | """return True if there are active locks""" | ||
859 | 311 | for file in os.listdir(path): | ||
860 | 312 | if not file[-len(lock_suffix):] == lock_suffix: | ||
861 | 313 | continue | ||
862 | 314 | is_tmp = False | ||
863 | 315 | if os.path.basename(file)[-len(lock_suffix)-len('.tmp'):-len(lock_suffix)] == '.tmp': | ||
864 | 316 | is_tmp = True | ||
865 | 317 | if is_tmp: | ||
866 | 318 | lock_pid = os.path.basename(file)[:-len('.tmp')-len(lock_suffix)] | ||
867 | 319 | else: | ||
868 | 320 | lock_pid = os.path.basename(file)[:-len(lock_suffix)] | ||
869 | 321 | if lock_pid == self.pid: | ||
870 | 322 | if is_tmp == self.tmp_mount: | ||
871 | 323 | continue | ||
872 | 324 | if self.check_process_alive(lock_pid): | ||
873 | 325 | return True | ||
874 | 326 | else: | ||
875 | 327 | #clean up | ||
876 | 328 | os.remove(os.path.join(path, file)) | ||
877 | 329 | for symlink in os.listdir(self.mount_user_path): | ||
878 | 330 | if symlink.endswith('_%s' % lock_pid): | ||
879 | 331 | os.remove(os.path.join(self.mount_user_path, symlink)) | ||
880 | 332 | return False | ||
881 | 333 | |||
882 | 334 | def setattr_kwargs(self, arg, default, **kwargs): | ||
883 | 335 | """if kwargs[arg] exist set self.<arg> to kwargs[arg] | ||
884 | 336 | else set self.<arg> to default which should be the value from config""" | ||
885 | 337 | if arg in kwargs: | ||
886 | 338 | value = kwargs[arg] | ||
887 | 339 | else: | ||
888 | 340 | value = default | ||
889 | 341 | setattr(self, arg, value) | ||
890 | 342 | #make dictionary with all used args for umount | ||
891 | 343 | self.all_kwargs[arg] = value | ||
892 | 344 | |||
893 | 345 | def write_umount_info(self): | ||
894 | 346 | """dump dictionary self.all_kwargs to umount_info file""" | ||
895 | 347 | data_string = json.dumps(self.all_kwargs) | ||
896 | 348 | with open(self.umount_info, 'w') as f: | ||
897 | 349 | f.write(data_string) | ||
898 | 350 | f.close | ||
899 | 351 | |||
900 | 352 | def read_umount_info(self, umount_info = None): | ||
901 | 353 | """load dictionary kwargs from umount_info file""" | ||
902 | 354 | if umount_info is None: | ||
903 | 355 | umount_info = self.umount_info | ||
904 | 356 | with open(umount_info, 'r') as f: | ||
905 | 357 | data_string = f.read() | ||
906 | 358 | f.close() | ||
907 | 359 | return json.loads(data_string) | ||
908 | 360 | |||
909 | 361 | def compare_umount_info(self, umount_info = None): | ||
910 | 362 | """just in case of hash collisions in <hash_id> we compare self.all_kwargs | ||
911 | 363 | with the old saved in umount_info file. | ||
912 | 364 | return True if both are identical""" | ||
913 | 365 | #run self.all_kwargs through json first | ||
914 | 366 | current_kwargs = json.loads(json.dumps(self.all_kwargs)) | ||
915 | 367 | saved_kwargs = self.read_umount_info(umount_info) | ||
916 | 368 | if not len(current_kwargs) == len(saved_kwargs): | ||
917 | 369 | return False | ||
918 | 370 | for arg in current_kwargs.keys(): | ||
919 | 371 | if not arg in saved_kwargs.keys(): | ||
920 | 372 | return False | ||
921 | 373 | if not current_kwargs[arg] == saved_kwargs[arg]: | ||
922 | 374 | return False | ||
923 | 375 | return True | ||
924 | 376 | |||
925 | 377 | def compare_remount(self, old_hash_id): | ||
926 | 378 | """return True if profiles are identiacal and we don't need to remount""" | ||
927 | 379 | if old_hash_id == self.hash_id: | ||
928 | 380 | return self.compare_umount_info(self.get_umount_info(old_hash_id)) | ||
929 | 381 | return False | ||
930 | 382 | |||
931 | 383 | def set_symlink(self, profile_id = None, hash_id = None, tmp_mount = None): | ||
932 | 384 | if profile_id is None: | ||
933 | 385 | profile_id = self.profile_id | ||
934 | 386 | if hash_id is None: | ||
935 | 387 | hash_id = self.hash_id | ||
936 | 388 | if tmp_mount is None: | ||
937 | 389 | tmp_mount = self.tmp_mount | ||
938 | 390 | dst = self.config.get_snapshots_path(profile_id = profile_id, mode = self.mode, tmp_mount = tmp_mount) | ||
939 | 391 | mountpoint = self.get_mountpoint(hash_id) | ||
940 | 392 | if self.symlink_subfolder is None: | ||
941 | 393 | src = mountpoint | ||
942 | 394 | else: | ||
943 | 395 | src = os.path.join(mountpoint, self.symlink_subfolder) | ||
944 | 396 | os.symlink(src, dst) | ||
945 | 397 | |||
946 | 398 | def remove_symlink(self, profile_id = None, tmp_mount = None): | ||
947 | 399 | if profile_id is None: | ||
948 | 400 | profile_id = self.profile_id | ||
949 | 401 | if tmp_mount is None: | ||
950 | 402 | tmp_mount = self.tmp_mount | ||
951 | 403 | os.remove(self.config.get_snapshots_path(profile_id = profile_id, mode = self.mode, tmp_mount = tmp_mount)) | ||
952 | 404 | |||
953 | 405 | def hash(self, str): | ||
954 | 406 | """return a hex crc32 hash of str""" | ||
955 | 407 | return('%X' % (crc32(str) & 0xFFFFFFFF)) | ||
956 | 408 | |||
957 | 409 | def get_hash_id_path(self, hash_id = None): | ||
958 | 410 | if hash_id is None: | ||
959 | 411 | hash_id = self.hash_id | ||
960 | 412 | return os.path.join(self.mount_user_path, 'mnt', self.hash_id) | ||
961 | 413 | |||
962 | 414 | def get_mountpoint(self, hash_id = None): | ||
963 | 415 | return os.path.join(self.get_hash_id_path(hash_id), 'mountpoint') | ||
964 | 416 | |||
965 | 417 | def get_lock_path(self, hash_id = None): | ||
966 | 418 | return os.path.join(self.get_hash_id_path(hash_id), 'locks') | ||
967 | 419 | |||
968 | 420 | def get_umount_info(self, hash_id = None): | ||
969 | 421 | return os.path.join(self.get_hash_id_path(hash_id), 'umount') | ||
970 | 0 | 422 | ||
971 | === modified file 'common/snapshots.py' | |||
972 | --- common/snapshots.py 2012-03-05 10:06:01 +0000 | |||
973 | +++ common/snapshots.py 2012-10-23 19:40:24 +0000 | |||
974 | @@ -27,6 +27,7 @@ | |||
975 | 27 | import pwd | 27 | import pwd |
976 | 28 | import grp | 28 | import grp |
977 | 29 | import socket | 29 | import socket |
978 | 30 | import subprocess | ||
979 | 30 | 31 | ||
980 | 31 | import config | 32 | import config |
981 | 32 | import configfile | 33 | import configfile |
982 | @@ -112,6 +113,11 @@ | |||
983 | 112 | #print path | 113 | #print path |
984 | 113 | return path | 114 | return path |
985 | 114 | 115 | ||
986 | 116 | def get_snapshot_path_ssh( self, date ): | ||
987 | 117 | profile_id = self.config.get_current_profile() | ||
988 | 118 | path = os.path.join( self.config.get_snapshots_full_path_ssh( profile_id ), self.get_snapshot_id( date ) ) | ||
989 | 119 | return path | ||
990 | 120 | |||
991 | 115 | def get_snapshot_info_path( self, date ): | 121 | def get_snapshot_info_path( self, date ): |
992 | 116 | return os.path.join( self.get_snapshot_path( date ), 'info' ) | 122 | return os.path.join( self.get_snapshot_path( date ), 'info' ) |
993 | 117 | 123 | ||
994 | @@ -132,6 +138,14 @@ | |||
995 | 132 | def get_snapshot_path_to( self, snapshot_id, toPath = '/' ): | 138 | def get_snapshot_path_to( self, snapshot_id, toPath = '/' ): |
996 | 133 | return os.path.join( self._get_snapshot_data_path( snapshot_id ), toPath[ 1 : ] ) | 139 | return os.path.join( self._get_snapshot_data_path( snapshot_id ), toPath[ 1 : ] ) |
997 | 134 | 140 | ||
998 | 141 | def _get_snapshot_data_path_ssh( self, snapshot_id ): | ||
999 | 142 | if len( snapshot_id ) <= 1: | ||
1000 | 143 | return '/'; | ||
1001 | 144 | return os.path.join( self.get_snapshot_path_ssh( snapshot_id ), 'backup' ) | ||
1002 | 145 | |||
1003 | 146 | def get_snapshot_path_to_ssh( self, snapshot_id, toPath = '/' ): | ||
1004 | 147 | return os.path.join( self._get_snapshot_data_path_ssh( snapshot_id ), toPath[ 1 : ] ) | ||
1005 | 148 | |||
1006 | 135 | def can_open_path( self, snapshot_id, full_path ): | 149 | def can_open_path( self, snapshot_id, full_path ): |
1007 | 136 | #full_path = self.get_snapshot_path_to( snapshot_id, path ) | 150 | #full_path = self.get_snapshot_path_to( snapshot_id, path ) |
1008 | 137 | if not os.path.exists( full_path ): | 151 | if not os.path.exists( full_path ): |
1009 | @@ -452,6 +466,18 @@ | |||
1010 | 452 | def restore( self, snapshot_id, path, callback = None, restore_to = '' ): | 466 | def restore( self, snapshot_id, path, callback = None, restore_to = '' ): |
1011 | 453 | if restore_to.endswith('/'): | 467 | if restore_to.endswith('/'): |
1012 | 454 | restore_to = restore_to[ : -1 ] | 468 | restore_to = restore_to[ : -1 ] |
1013 | 469 | |||
1014 | 470 | #ssh | ||
1015 | 471 | ssh = False | ||
1016 | 472 | if self.config.get_snapshots_mode() == 'ssh': | ||
1017 | 473 | ssh = True | ||
1018 | 474 | (ssh_host, ssh_port, ssh_user, ssh_path) = self.config.get_ssh_host_port_user_path() | ||
1019 | 475 | ssh_cipher = self.config.get_ssh_cipher() | ||
1020 | 476 | if ssh_cipher == 'default': | ||
1021 | 477 | ssh_cipher_suffix = '' | ||
1022 | 478 | else: | ||
1023 | 479 | ssh_cipher_suffix = '-c %s' % ssh_cipher | ||
1024 | 480 | rsync_ssh_suffix = '--rsh="ssh -p %s %s" "%s@%s:' % ( str(ssh_port), ssh_cipher_suffix, ssh_user, ssh_host ) | ||
1025 | 455 | 481 | ||
1026 | 456 | logger.info( "Restore: %s to: %s" % (path, restore_to) ) | 482 | logger.info( "Restore: %s to: %s" % (path, restore_to) ) |
1027 | 457 | 483 | ||
1028 | @@ -466,6 +492,8 @@ | |||
1029 | 466 | cmd = cmd + "--backup --suffix=%s " % backup_suffix | 492 | cmd = cmd + "--backup --suffix=%s " % backup_suffix |
1030 | 467 | #cmd = cmd + '--chmod=+w ' | 493 | #cmd = cmd + '--chmod=+w ' |
1031 | 468 | src_base = self.get_snapshot_path_to( snapshot_id ) | 494 | src_base = self.get_snapshot_path_to( snapshot_id ) |
1032 | 495 | if ssh: | ||
1033 | 496 | src_base = self.get_snapshot_path_to_ssh( snapshot_id ) | ||
1034 | 469 | src_path = path | 497 | src_path = path |
1035 | 470 | src_delta = 0 | 498 | src_delta = 0 |
1036 | 471 | if len(restore_to) > 0: | 499 | if len(restore_to) > 0: |
1037 | @@ -475,17 +503,24 @@ | |||
1038 | 475 | items = os.path.split(src_path) | 503 | items = os.path.split(src_path) |
1039 | 476 | aux = items[0] | 504 | aux = items[0] |
1040 | 477 | if aux.startswith('/'): | 505 | if aux.startswith('/'): |
1043 | 478 | aux = aux[1:] | 506 | aux = aux[1:] |
1044 | 479 | src_base = os.path.join(src_base, aux) + '/' | 507 | if len(aux) > 0: #bugfix: restore system root ended in <src_base>//.<src_path> |
1045 | 508 | src_base = os.path.join(src_base, aux) + '/' | ||
1046 | 480 | src_path = '/' + items[1] | 509 | src_path = '/' + items[1] |
1048 | 481 | src_delta = len(items[0]) | 510 | if items[0] == '/': |
1049 | 511 | src_delta = 0 | ||
1050 | 512 | else: | ||
1051 | 513 | src_delta = len(items[0]) | ||
1052 | 482 | 514 | ||
1053 | 483 | #print "src_base: %s" % src_base | 515 | #print "src_base: %s" % src_base |
1054 | 484 | #print "src_path: %s" % src_path | 516 | #print "src_path: %s" % src_path |
1055 | 485 | #print "src_delta: %s" % src_delta | 517 | #print "src_delta: %s" % src_delta |
1056 | 486 | #print "snapshot_id: %s" % snapshot_id | 518 | #print "snapshot_id: %s" % snapshot_id |
1057 | 487 | 519 | ||
1059 | 488 | cmd = cmd + "\"%s.%s\" %s" % ( src_base, src_path, restore_to + '/' ) | 520 | if ssh: |
1060 | 521 | cmd = cmd + rsync_ssh_suffix + "%s.%s\" %s" % ( src_base, src_path, restore_to + '/' ) | ||
1061 | 522 | else: | ||
1062 | 523 | cmd = cmd + "\"%s.%s\" %s" % ( src_base, src_path, restore_to + '/' ) | ||
1063 | 489 | self.restore_callback( callback, True, cmd ) | 524 | self.restore_callback( callback, True, cmd ) |
1064 | 490 | self._execute( cmd, callback ) | 525 | self._execute( cmd, callback ) |
1065 | 491 | 526 | ||
1066 | @@ -600,13 +635,33 @@ | |||
1067 | 600 | def remove_snapshot( self, snapshot_id ): | 635 | def remove_snapshot( self, snapshot_id ): |
1068 | 601 | if len( snapshot_id ) <= 1: | 636 | if len( snapshot_id ) <= 1: |
1069 | 602 | return | 637 | return |
1077 | 603 | 638 | #ssh | |
1078 | 604 | path = self.get_snapshot_path( snapshot_id ) | 639 | profile_id = self.config.get_current_profile() |
1079 | 605 | #cmd = "chmod -R u+rwx \"%s\"" % path | 640 | ssh = False |
1080 | 606 | cmd = "find \"%s\" -type d -exec chmod u+wx {} \\;" % path #Debian patch | 641 | if self.config.get_snapshots_mode() == 'ssh': |
1081 | 607 | self._execute( cmd ) | 642 | ssh = True |
1082 | 608 | cmd = "rm -rfv \"%s\"" % path | 643 | (ssh_host, ssh_port, ssh_user, ssh_path) = self.config.get_ssh_host_port_user_path(profile_id) |
1083 | 609 | self._execute( cmd ) | 644 | ssh_cipher = self.config.get_ssh_cipher(profile_id) |
1084 | 645 | if ssh_cipher == 'default': | ||
1085 | 646 | ssh_cipher_suffix = '' | ||
1086 | 647 | else: | ||
1087 | 648 | ssh_cipher_suffix = '-c %s' % ssh_cipher | ||
1088 | 649 | cmd_ssh = 'ssh -p %s %s %s@%s ' % ( ssh_port, ssh_cipher_suffix, ssh_user, ssh_host ) | ||
1089 | 650 | |||
1090 | 651 | if not ssh: | ||
1091 | 652 | path = self.get_snapshot_path( snapshot_id ) | ||
1092 | 653 | #cmd = "chmod -R u+rwx \"%s\"" % path | ||
1093 | 654 | cmd = "find \"%s\" -type d -exec chmod u+wx {} \\;" % path #Debian patch | ||
1094 | 655 | self._execute( cmd ) | ||
1095 | 656 | cmd = "rm -rf \"%s\"" % path | ||
1096 | 657 | self._execute( cmd ) | ||
1097 | 658 | else: | ||
1098 | 659 | path = self.get_snapshot_path_ssh( snapshot_id ) | ||
1099 | 660 | #cmd = "chmod -R u+rwx \"%s\"" % path | ||
1100 | 661 | cmd = cmd_ssh + '\'find \"%s\" -type d -exec chmod u+wx \"{}\" \\;\'' % path #Debian patch | ||
1101 | 662 | self._execute( cmd ) | ||
1102 | 663 | cmd = cmd_ssh + "rm -rf \"%s\"" % path | ||
1103 | 664 | self._execute( cmd ) | ||
1104 | 610 | 665 | ||
1105 | 611 | def copy_snapshot( self, snapshot_id, new_folder ): | 666 | def copy_snapshot( self, snapshot_id, new_folder ): |
1106 | 612 | '''Copies a known snapshot to a new location''' | 667 | '''Copies a known snapshot to a new location''' |
1107 | @@ -1016,6 +1071,22 @@ | |||
1108 | 1016 | new_snapshot_id = 'new_snapshot' | 1071 | new_snapshot_id = 'new_snapshot' |
1109 | 1017 | new_snapshot_path = self.get_snapshot_path( new_snapshot_id ) | 1072 | new_snapshot_path = self.get_snapshot_path( new_snapshot_id ) |
1110 | 1018 | 1073 | ||
1111 | 1074 | #ssh | ||
1112 | 1075 | profile_id = self.config.get_current_profile() | ||
1113 | 1076 | ssh = False | ||
1114 | 1077 | if self.config.get_snapshots_mode() == 'ssh': | ||
1115 | 1078 | ssh = True | ||
1116 | 1079 | (ssh_host, ssh_port, ssh_user, ssh_path) = self.config.get_ssh_host_port_user_path(profile_id) | ||
1117 | 1080 | ssh_cipher = self.config.get_ssh_cipher(profile_id) | ||
1118 | 1081 | if ssh_cipher == 'default': | ||
1119 | 1082 | ssh_cipher_suffix = '' | ||
1120 | 1083 | else: | ||
1121 | 1084 | ssh_cipher_suffix = '-c %s' % ssh_cipher | ||
1122 | 1085 | rsync_ssh_suffix = '--rsh="ssh -p %s %s" "%s@%s:' % ( str(ssh_port), ssh_cipher_suffix, ssh_user, ssh_host ) | ||
1123 | 1086 | cmd_ssh = 'ssh -p %s %s %s@%s ' % ( str(ssh_port), ssh_cipher_suffix, ssh_user, ssh_host ) | ||
1124 | 1087 | new_snapshot_path_ssh = self.get_snapshot_path_ssh( new_snapshot_id ) | ||
1125 | 1088 | new_snapshot_path_to_ssh = self.get_snapshot_path_to_ssh( new_snapshot_id ) | ||
1126 | 1089 | |||
1127 | 1019 | if os.path.exists( new_snapshot_path ): | 1090 | if os.path.exists( new_snapshot_path ): |
1128 | 1020 | #self._execute( "find \"%s\" -type d -exec chmod +w {} \;" % new_snapshot_path ) | 1091 | #self._execute( "find \"%s\" -type d -exec chmod +w {} \;" % new_snapshot_path ) |
1129 | 1021 | #self._execute( "chmod -R u+rwx \"%s\"" % new_snapshot_path ) | 1092 | #self._execute( "chmod -R u+rwx \"%s\"" % new_snapshot_path ) |
1130 | @@ -1101,7 +1172,11 @@ | |||
1131 | 1101 | logger.info( "Compare with old snapshot: %s" % prev_snapshot_id ) | 1172 | logger.info( "Compare with old snapshot: %s" % prev_snapshot_id ) |
1132 | 1102 | 1173 | ||
1133 | 1103 | prev_snapshot_folder = self.get_snapshot_path_to( prev_snapshot_id ) | 1174 | prev_snapshot_folder = self.get_snapshot_path_to( prev_snapshot_id ) |
1134 | 1175 | prev_snapshot_folder_ssh = self.get_snapshot_path_to_ssh( prev_snapshot_id ) | ||
1135 | 1104 | cmd = rsync_prefix + ' -i --dry-run --out-format="BACKINTIME: %i %n%L"' + rsync_suffix + '"' + prev_snapshot_folder + '"' | 1176 | cmd = rsync_prefix + ' -i --dry-run --out-format="BACKINTIME: %i %n%L"' + rsync_suffix + '"' + prev_snapshot_folder + '"' |
1136 | 1177 | if ssh: | ||
1137 | 1178 | cmd = rsync_prefix + ' -i --dry-run --out-format="BACKINTIME: %i %n%L"' + rsync_suffix | ||
1138 | 1179 | cmd += rsync_ssh_suffix + prev_snapshot_folder_ssh + '"' | ||
1139 | 1105 | params = [ prev_snapshot_folder, False ] | 1180 | params = [ prev_snapshot_folder, False ] |
1140 | 1106 | #try_cmd = self._execute_output( cmd, self._exec_rsync_compare_callback, prev_snapshot_name ) | 1181 | #try_cmd = self._execute_output( cmd, self._exec_rsync_compare_callback, prev_snapshot_name ) |
1141 | 1107 | self.append_to_take_snapshot_log( '[I] ' + cmd, 3 ) | 1182 | self.append_to_take_snapshot_log( '[I] ' + cmd, 3 ) |
1142 | @@ -1135,21 +1210,33 @@ | |||
1143 | 1135 | #if force or len( ignore_folders ) == 0: | 1210 | #if force or len( ignore_folders ) == 0: |
1144 | 1136 | 1211 | ||
1145 | 1137 | prev_snapshot_path = self.get_snapshot_path_to( prev_snapshot_id ) | 1212 | prev_snapshot_path = self.get_snapshot_path_to( prev_snapshot_id ) |
1146 | 1213 | prev_snapshot_path_ssh = self.get_snapshot_path_to_ssh( prev_snapshot_id ) | ||
1147 | 1138 | 1214 | ||
1148 | 1139 | #make source snapshot folders rw to allow cp -al | 1215 | #make source snapshot folders rw to allow cp -al |
1150 | 1140 | self._execute( "find \"%s\" -type d -exec chmod u+wx {} \\;" % prev_snapshot_path ) #Debian patch | 1216 | if not ssh: |
1151 | 1217 | self._execute( "find \"%s\" -type d -exec chmod u+wx {} \\;" % prev_snapshot_path ) #Debian patch | ||
1152 | 1218 | else: | ||
1153 | 1219 | self._execute( cmd_ssh + '\'find \"%s\" -type d -exec chmod u+wx \"{}\" \\;\'' % prev_snapshot_path_ssh ) #Debian patch | ||
1154 | 1141 | 1220 | ||
1155 | 1142 | #clone snapshot | 1221 | #clone snapshot |
1156 | 1143 | cmd = "cp -aRl \"%s\"* \"%s\"" % ( prev_snapshot_path, new_snapshot_path_to ) | 1222 | cmd = "cp -aRl \"%s\"* \"%s\"" % ( prev_snapshot_path, new_snapshot_path_to ) |
1157 | 1223 | if ssh: | ||
1158 | 1224 | cmd = cmd_ssh + "cp -aRl \"%s\"* \"%s\"" % ( prev_snapshot_path_ssh, new_snapshot_path_to_ssh ) | ||
1159 | 1144 | self.append_to_take_snapshot_log( '[I] ' + cmd, 3 ) | 1225 | self.append_to_take_snapshot_log( '[I] ' + cmd, 3 ) |
1160 | 1145 | cmd_ret_val = self._execute( cmd ) | 1226 | cmd_ret_val = self._execute( cmd ) |
1161 | 1146 | self.append_to_take_snapshot_log( "[I] returns: %s" % cmd_ret_val, 3 ) | 1227 | self.append_to_take_snapshot_log( "[I] returns: %s" % cmd_ret_val, 3 ) |
1162 | 1147 | 1228 | ||
1163 | 1148 | #make source snapshot folders read-only | 1229 | #make source snapshot folders read-only |
1165 | 1149 | self._execute( "find \"%s\" -type d -exec chmod a-w {} \\;" % prev_snapshot_path ) #Debian patch | 1230 | if not ssh: |
1166 | 1231 | self._execute( "find \"%s\" -type d -exec chmod a-w {} \\;" % prev_snapshot_path ) #Debian patch | ||
1167 | 1232 | else: | ||
1168 | 1233 | self._execute( cmd_ssh + '\'find \"%s\" -type d -exec chmod a-w \"{}\" \\;\'' % prev_snapshot_path_ssh ) #Debian patch | ||
1169 | 1150 | 1234 | ||
1170 | 1151 | #make snapshot items rw to allow xopy xattr | 1235 | #make snapshot items rw to allow xopy xattr |
1172 | 1152 | self._execute( "chmod -R a+w \"%s\"" % new_snapshot_path ) | 1236 | if not ssh: |
1173 | 1237 | self._execute( "chmod -R a+w \"%s\"" % new_snapshot_path ) | ||
1174 | 1238 | else: | ||
1175 | 1239 | self._execute( cmd_ssh + "chmod -R a+w \"%s\"" % new_snapshot_path_ssh ) | ||
1176 | 1153 | 1240 | ||
1177 | 1154 | #else: | 1241 | #else: |
1178 | 1155 | # for folder in include_folders: | 1242 | # for folder in include_folders: |
1179 | @@ -1165,6 +1252,8 @@ | |||
1180 | 1165 | #sync changed folders | 1252 | #sync changed folders |
1181 | 1166 | logger.info( "Call rsync to take the snapshot" ) | 1253 | logger.info( "Call rsync to take the snapshot" ) |
1182 | 1167 | cmd = rsync_prefix + ' -v ' + rsync_suffix + '"' + new_snapshot_path_to + '"' | 1254 | cmd = rsync_prefix + ' -v ' + rsync_suffix + '"' + new_snapshot_path_to + '"' |
1183 | 1255 | if ssh: | ||
1184 | 1256 | cmd = rsync_prefix + ' -v ' + rsync_suffix + rsync_ssh_suffix + new_snapshot_path_to_ssh + '"' | ||
1185 | 1168 | self.set_take_snapshot_message( 0, _('Take snapshot') ) | 1257 | self.set_take_snapshot_message( 0, _('Take snapshot') ) |
1186 | 1169 | 1258 | ||
1187 | 1170 | params = [False] | 1259 | params = [False] |
1188 | @@ -1174,12 +1263,19 @@ | |||
1189 | 1174 | has_errors = False | 1263 | has_errors = False |
1190 | 1175 | if params[0]: | 1264 | if params[0]: |
1191 | 1176 | if not self.config.continue_on_errors(): | 1265 | if not self.config.continue_on_errors(): |
1194 | 1177 | self._execute( "find \"%s\" -type d -exec chmod u+wx {} \\;" % new_snapshot_path ) #Debian patch | 1266 | if not ssh: |
1195 | 1178 | self._execute( "rm -rf \"%s\"" % new_snapshot_path ) | 1267 | self._execute( "find \"%s\" -type d -exec chmod u+wx {} \\;" % new_snapshot_path ) #Debian patch |
1196 | 1268 | self._execute( "rm -rf \"%s\"" % new_snapshot_path ) | ||
1197 | 1269 | else: | ||
1198 | 1270 | self._execute( cmd_ssh + '\'find \"%s\" -type d -exec chmod u+wx \"{}\" \\;\'' % new_snapshot_path_ssh ) #Debian patch | ||
1199 | 1271 | self._execute( cmd_ssh + "rm -rf \"%s\"" % new_snapshot_path_ssh ) | ||
1200 | 1179 | 1272 | ||
1201 | 1180 | #fix previous snapshot: make read-only again | 1273 | #fix previous snapshot: make read-only again |
1202 | 1181 | if len( prev_snapshot_id ) > 0: | 1274 | if len( prev_snapshot_id ) > 0: |
1204 | 1182 | self._execute( "chmod -R a-w \"%s\"" % self.get_snapshot_path_to( prev_snapshot_id ) ) | 1275 | if not ssh: |
1205 | 1276 | self._execute( "chmod -R a-w \"%s\"" % self.get_snapshot_path_to( prev_snapshot_id ) ) | ||
1206 | 1277 | else: | ||
1207 | 1278 | self._execute( cmd_ssh + "chmod -R a-w \"%s\"" % self.get_snapshot_path_to_ssh( prev_snapshot_id ) ) | ||
1208 | 1183 | 1279 | ||
1209 | 1184 | return [ False, True ] | 1280 | return [ False, True ] |
1210 | 1185 | 1281 | ||
1211 | @@ -1199,12 +1295,34 @@ | |||
1212 | 1199 | path_to_explore = self.get_snapshot_path_to( new_snapshot_id ).rstrip( '/' ) | 1295 | path_to_explore = self.get_snapshot_path_to( new_snapshot_id ).rstrip( '/' ) |
1213 | 1200 | fileinfo_dict = {} | 1296 | fileinfo_dict = {} |
1214 | 1201 | 1297 | ||
1221 | 1202 | for path, dirs, files in os.walk( path_to_explore ): | 1298 | permission_done = False |
1222 | 1203 | dirs.extend( files ) | 1299 | if ssh: |
1223 | 1204 | for item in dirs: | 1300 | path_to_explore_ssh = self.get_snapshot_path_to_ssh( new_snapshot_id ).rstrip( '/' ) |
1224 | 1205 | item_path = os.path.join( path, item )[ len( path_to_explore ) : ] | 1301 | cmd = ['ssh', '-p', str(ssh_port)] |
1225 | 1206 | fileinfo_dict[item_path] = 1 | 1302 | if not ssh_cipher == 'default': |
1226 | 1207 | self._save_path_info( fileinfo, item_path ) | 1303 | cmd.extend(['-c', ssh_cipher]) |
1227 | 1304 | cmd.extend(['%s@%s' % (ssh_user, ssh_host)]) | ||
1228 | 1305 | cmd.extend(['find', path_to_explore_ssh, '-name', '\*', '-print']) | ||
1229 | 1306 | |||
1230 | 1307 | find = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr = subprocess.PIPE) | ||
1231 | 1308 | output, err = find.communicate() | ||
1232 | 1309 | if len(err) > 0: | ||
1233 | 1310 | logger.warning('Save permission over ssh failed. Retry normal methode') | ||
1234 | 1311 | else: | ||
1235 | 1312 | for line in output.split('\n'): | ||
1236 | 1313 | if not len(line) == 0: | ||
1237 | 1314 | item_path = line[ len( path_to_explore_ssh ) : ] | ||
1238 | 1315 | fileinfo_dict[item_path] = 1 | ||
1239 | 1316 | self._save_path_info( fileinfo, item_path ) | ||
1240 | 1317 | permission_done = True | ||
1241 | 1318 | |||
1242 | 1319 | if not permission_done: | ||
1243 | 1320 | for path, dirs, files in os.walk( path_to_explore ): | ||
1244 | 1321 | dirs.extend( files ) | ||
1245 | 1322 | for item in dirs: | ||
1246 | 1323 | item_path = os.path.join( path, item )[ len( path_to_explore ) : ] | ||
1247 | 1324 | fileinfo_dict[item_path] = 1 | ||
1248 | 1325 | self._save_path_info( fileinfo, item_path ) | ||
1249 | 1208 | 1326 | ||
1250 | 1209 | # We now copy on forehand, so copying afterwards is not necessary anymore | 1327 | # We now copy on forehand, so copying afterwards is not necessary anymore |
1251 | 1210 | ##copy ignored folders | 1328 | ##copy ignored folders |
1252 | @@ -1267,7 +1385,11 @@ | |||
1253 | 1267 | 1385 | ||
1254 | 1268 | #rename snapshot | 1386 | #rename snapshot |
1255 | 1269 | snapshot_path = self.get_snapshot_path( snapshot_id ) | 1387 | snapshot_path = self.get_snapshot_path( snapshot_id ) |
1257 | 1270 | os.system( "mv \"%s\" \"%s\"" % ( new_snapshot_path, snapshot_path ) ) | 1388 | snapshot_path_ssh = self.get_snapshot_path_ssh( snapshot_id ) |
1258 | 1389 | if not ssh: | ||
1259 | 1390 | os.system( "mv \"%s\" \"%s\"" % ( new_snapshot_path, snapshot_path ) ) | ||
1260 | 1391 | else: | ||
1261 | 1392 | os.system( cmd_ssh + "mv \"%s\" \"%s\"" % ( new_snapshot_path_ssh, snapshot_path_ssh ) ) | ||
1262 | 1271 | if not os.path.exists( snapshot_path ): | 1393 | if not os.path.exists( snapshot_path ): |
1263 | 1272 | logger.error( "Can't rename %s to %s" % ( new_snapshot_path, snapshot_path ) ) | 1394 | logger.error( "Can't rename %s to %s" % ( new_snapshot_path, snapshot_path ) ) |
1264 | 1273 | self.set_take_snapshot_message( 1, _('Can\'t rename %s to %s') % ( new_snapshot_path, snapshot_path ) ) | 1395 | self.set_take_snapshot_message( 1, _('Can\'t rename %s to %s') % ( new_snapshot_path, snapshot_path ) ) |
1265 | @@ -1275,11 +1397,17 @@ | |||
1266 | 1275 | return [ False, True ] | 1397 | return [ False, True ] |
1267 | 1276 | 1398 | ||
1268 | 1277 | #make new snapshot read-only | 1399 | #make new snapshot read-only |
1270 | 1278 | self._execute( "chmod -R a-w \"%s\"" % snapshot_path ) | 1400 | if not ssh: |
1271 | 1401 | self._execute( "chmod -R a-w \"%s\"" % snapshot_path ) | ||
1272 | 1402 | else: | ||
1273 | 1403 | self._execute( cmd_ssh + "chmod -R a-w \"%s\"" % snapshot_path_ssh ) | ||
1274 | 1279 | 1404 | ||
1275 | 1280 | #fix previous snapshot: make read-only again | 1405 | #fix previous snapshot: make read-only again |
1276 | 1281 | if len( prev_snapshot_id ) > 0: | 1406 | if len( prev_snapshot_id ) > 0: |
1278 | 1282 | self._execute( "chmod -R a-w \"%s\"" % self.get_snapshot_path_to( prev_snapshot_id ) ) | 1407 | if not ssh: |
1279 | 1408 | self._execute( "chmod -R a-w \"%s\"" % self.get_snapshot_path_to( prev_snapshot_id ) ) | ||
1280 | 1409 | else: | ||
1281 | 1410 | self._execute( cmd_ssh + "chmod -R a-w \"%s\"" % self.get_snapshot_path_to_ssh( prev_snapshot_id ) ) | ||
1282 | 1283 | 1411 | ||
1283 | 1284 | return [ True, has_errors ] | 1412 | return [ True, has_errors ] |
1284 | 1285 | 1413 | ||
1285 | 1286 | 1414 | ||
1286 | === added file 'common/sshtools.py' | |||
1287 | --- common/sshtools.py 1970-01-01 00:00:00 +0000 | |||
1288 | +++ common/sshtools.py 2012-10-23 19:40:24 +0000 | |||
1289 | @@ -0,0 +1,309 @@ | |||
1290 | 1 | # Copyright (c) 2012 Germar Reitze | ||
1291 | 2 | # | ||
1292 | 3 | # This program is free software; you can redistribute it and/or modify | ||
1293 | 4 | # it under the terms of the GNU General Public License as published by | ||
1294 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
1295 | 6 | # (at your option) any later version. | ||
1296 | 7 | # | ||
1297 | 8 | # This program is distributed in the hope that it will be useful, | ||
1298 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1299 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1300 | 11 | # GNU General Public License for more details. | ||
1301 | 12 | # | ||
1302 | 13 | # You should have received a copy of the GNU General Public License along | ||
1303 | 14 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
1304 | 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
1305 | 16 | |||
1306 | 17 | import os | ||
1307 | 18 | import grp | ||
1308 | 19 | import subprocess | ||
1309 | 20 | import gettext | ||
1310 | 21 | import string | ||
1311 | 22 | import random | ||
1312 | 23 | import tempfile | ||
1313 | 24 | from time import sleep | ||
1314 | 25 | |||
1315 | 26 | import config | ||
1316 | 27 | import mount | ||
1317 | 28 | import logger | ||
1318 | 29 | import tools | ||
1319 | 30 | |||
1320 | 31 | _=gettext.gettext | ||
1321 | 32 | |||
1322 | 33 | class SSH(mount.MountControl): | ||
1323 | 34 | """ | ||
1324 | 35 | Mount remote path with sshfs. The real take_snapshot process will use | ||
1325 | 36 | rsync over ssh. Other commands run remote over ssh. | ||
1326 | 37 | """ | ||
1327 | 38 | def __init__(self, cfg = None, profile_id = None, hash_id = None, tmp_mount = False, **kwargs): | ||
1328 | 39 | self.config = cfg | ||
1329 | 40 | if self.config is None: | ||
1330 | 41 | self.config = config.Config() | ||
1331 | 42 | |||
1332 | 43 | self.profile_id = profile_id | ||
1333 | 44 | if not self.profile_id: | ||
1334 | 45 | self.profile_id = self.config.get_current_profile() | ||
1335 | 46 | |||
1336 | 47 | self.tmp_mount = tmp_mount | ||
1337 | 48 | self.hash_id = hash_id | ||
1338 | 49 | |||
1339 | 50 | #init MountControl | ||
1340 | 51 | mount.MountControl.__init__(self) | ||
1341 | 52 | |||
1342 | 53 | self.all_kwargs = {} | ||
1343 | 54 | |||
1344 | 55 | #First we need to map the settings. | ||
1345 | 56 | self.setattr_kwargs('mode', self.config.get_snapshots_mode(self.profile_id), **kwargs) | ||
1346 | 57 | self.setattr_kwargs('hash_collision', self.config.get_hash_collision(), **kwargs) | ||
1347 | 58 | #start editing from here--------------------------------------------------------- | ||
1348 | 59 | self.setattr_kwargs('user', self.config.get_ssh_user(self.profile_id), **kwargs) | ||
1349 | 60 | self.setattr_kwargs('host', self.config.get_ssh_host(self.profile_id), **kwargs) | ||
1350 | 61 | self.setattr_kwargs('port', self.config.get_ssh_port(self.profile_id), **kwargs) | ||
1351 | 62 | self.setattr_kwargs('path', self.config.get_snapshots_path_ssh(self.profile_id), **kwargs) | ||
1352 | 63 | self.setattr_kwargs('cipher', self.config.get_ssh_cipher(self.profile_id), **kwargs) | ||
1353 | 64 | |||
1354 | 65 | self.set_default_args() | ||
1355 | 66 | |||
1356 | 67 | self.symlink_subfolder = None | ||
1357 | 68 | self.user_host_path = '%s@%s:%s' % (self.user, self.host, self.path) | ||
1358 | 69 | self.log_command = '%s: %s' % (self.mode, self.user_host_path) | ||
1359 | 70 | |||
1360 | 71 | def _mount(self): | ||
1361 | 72 | """mount the service""" | ||
1362 | 73 | sshfs = ['sshfs', '-p', str(self.port)] | ||
1363 | 74 | if not self.cipher == 'default': | ||
1364 | 75 | sshfs.extend(['-o', 'Ciphers=%s' % self.cipher]) | ||
1365 | 76 | sshfs.extend([self.user_host_path, self.mountpoint]) | ||
1366 | 77 | #bugfix: sshfs doesn't mount if locale in LC_ALL is not available on remote host | ||
1367 | 78 | #LANG or other envirnoment variable are no problem. | ||
1368 | 79 | env = os.environ.copy() | ||
1369 | 80 | if 'LC_ALL' in env.keys(): | ||
1370 | 81 | env['LC_ALL'] = 'C' | ||
1371 | 82 | try: | ||
1372 | 83 | subprocess.check_call(sshfs, env = env) | ||
1373 | 84 | except subprocess.CalledProcessError as ex: | ||
1374 | 85 | raise mount.MountException( _('Can\'t mount %s') % ' '.join(sshfs)) | ||
1375 | 86 | |||
1376 | 87 | def _umount(self): | ||
1377 | 88 | """umount the service""" | ||
1378 | 89 | try: | ||
1379 | 90 | subprocess.check_call(['fusermount', '-u', self.mountpoint]) | ||
1380 | 91 | except subprocess.CalledProcessError as ex: | ||
1381 | 92 | raise mount.MountException( _('Can\'t unmount sshfs %s') % self.mountpoint) | ||
1382 | 93 | |||
1383 | 94 | def pre_mount_check(self, first_run = False): | ||
1384 | 95 | """check what ever conditions must be given for the mount to be done successful | ||
1385 | 96 | raise MountException( _('Error discription') ) if service can not mount | ||
1386 | 97 | return True if everything is okay | ||
1387 | 98 | all pre|post_[u]mount_check can also be used to prepare things or clean up""" | ||
1388 | 99 | self.check_ping_host() | ||
1389 | 100 | if first_run: | ||
1390 | 101 | self.check_fuse() | ||
1391 | 102 | self.check_known_hosts() | ||
1392 | 103 | self.check_login() | ||
1393 | 104 | if first_run: | ||
1394 | 105 | self.check_cipher() | ||
1395 | 106 | self.check_remote_folder() | ||
1396 | 107 | if first_run: | ||
1397 | 108 | self.check_remote_commands() | ||
1398 | 109 | return True | ||
1399 | 110 | |||
1400 | 111 | def post_mount_check(self): | ||
1401 | 112 | """check if mount was successful | ||
1402 | 113 | raise MountException( _('Error discription') ) if not""" | ||
1403 | 114 | return True | ||
1404 | 115 | |||
1405 | 116 | def pre_umount_check(self): | ||
1406 | 117 | """check if service is safe to umount | ||
1407 | 118 | raise MountException( _('Error discription') ) if not""" | ||
1408 | 119 | return True | ||
1409 | 120 | |||
1410 | 121 | def post_umount_check(self): | ||
1411 | 122 | """check if umount successful | ||
1412 | 123 | raise MountException( _('Error discription') ) if not""" | ||
1413 | 124 | return True | ||
1414 | 125 | |||
1415 | 126 | def check_fuse(self): | ||
1416 | 127 | """check if sshfs is installed and user is part of group fuse""" | ||
1417 | 128 | if not self.pathexists('sshfs'): | ||
1418 | 129 | raise mount.MountException( _('sshfs not found. Please install e.g. \'apt-get install sshfs\'') ) | ||
1419 | 130 | user = self.config.get_user() | ||
1420 | 131 | fuse_grp_members = grp.getgrnam('fuse')[3] | ||
1421 | 132 | if not user in fuse_grp_members: | ||
1422 | 133 | raise mount.MountException( _('%s is not member of group \'fuse\'.\n Run \'sudo adduser %s fuse\'. To apply changes logout and login again.\nLook at \'man backintime\' for further instructions.') % (user, user)) | ||
1423 | 134 | |||
1424 | 135 | def pathexists(self, filename): | ||
1425 | 136 | """Checks if 'filename' is present in the system PATH. | ||
1426 | 137 | In other words, it checks if os.execvp(filename, ...) will work. | ||
1427 | 138 | shameless stolen from GnuPGInterface;)""" | ||
1428 | 139 | pathenv = os.getenv("PATH") | ||
1429 | 140 | path = pathenv.split(":") | ||
1430 | 141 | for directory in path: | ||
1431 | 142 | fullpath = os.path.join(directory, filename) | ||
1432 | 143 | if (os.path.exists(fullpath)): | ||
1433 | 144 | return True | ||
1434 | 145 | return False | ||
1435 | 146 | |||
1436 | 147 | def check_login(self): | ||
1437 | 148 | """check passwordless authentication to host""" | ||
1438 | 149 | ssh = ['ssh', '-o', 'PreferredAuthentications=publickey'] | ||
1439 | 150 | ssh.extend(['-p', str(self.port), self.user + '@' + self.host]) | ||
1440 | 151 | ssh.extend(['echo', '"Hello"']) | ||
1441 | 152 | try: | ||
1442 | 153 | subprocess.check_call(ssh, stdout=open(os.devnull, 'w')) | ||
1443 | 154 | except subprocess.CalledProcessError: | ||
1444 | 155 | raise mount.MountException( _('Password-less authentication for %s@%s failed. Look at \'man backintime\' for further instructions.') % (self.user, self.host)) | ||
1445 | 156 | |||
1446 | 157 | def check_cipher(self): | ||
1447 | 158 | """check if both host and localhost support cipher""" | ||
1448 | 159 | if not self.cipher == 'default': | ||
1449 | 160 | ssh = ['ssh'] | ||
1450 | 161 | ssh.extend(['-o', 'Ciphers=%s' % self.cipher]) | ||
1451 | 162 | ssh.extend(['-p', str(self.port), self.user + '@' + self.host, 'echo', '"Hello"']) | ||
1452 | 163 | err = subprocess.Popen(ssh, stdout=open(os.devnull, 'w'), stderr=subprocess.PIPE).communicate()[1] | ||
1453 | 164 | if err: | ||
1454 | 165 | raise mount.MountException( _('Cipher %s failed for %s:\n%s') % (self.cipher, self.host, err)) | ||
1455 | 166 | |||
1456 | 167 | def benchmark_cipher(self, size = '40'): | ||
1457 | 168 | import tempfile | ||
1458 | 169 | temp = tempfile.mkstemp()[1] | ||
1459 | 170 | print('create random data file') | ||
1460 | 171 | subprocess.call(['dd', 'if=/dev/urandom', 'of=%s' % temp, 'bs=1M', 'count=%s' % size]) | ||
1461 | 172 | keys = self.config.SSH_CIPHERS.keys() | ||
1462 | 173 | keys.sort() | ||
1463 | 174 | for cipher in keys: | ||
1464 | 175 | if cipher == 'default': | ||
1465 | 176 | continue | ||
1466 | 177 | print('%s:' % cipher) | ||
1467 | 178 | for i in range(2): | ||
1468 | 179 | subprocess.call(['scp', '-p', str(self.port), '-c', cipher, temp, self.user_host_path]) | ||
1469 | 180 | subprocess.call(['ssh', '%s@%s' % (self.user, self.host), 'rm', os.path.join(self.path, os.path.basename(temp))]) | ||
1470 | 181 | os.remove(temp) | ||
1471 | 182 | |||
1472 | 183 | def check_known_hosts(self): | ||
1473 | 184 | """check ssh_known_hosts""" | ||
1474 | 185 | output = subprocess.Popen(['ssh-keygen', '-F', self.host], stdout=subprocess.PIPE).communicate()[0] #subprocess.check_output doesn't exist in Python 2.6 (Debian squeeze default) | ||
1475 | 186 | if output.find('Host %s found' % self.host) < 0: | ||
1476 | 187 | raise mount.MountException( _('%s not found in ssh_known_hosts.') % self.host) | ||
1477 | 188 | |||
1478 | 189 | def check_remote_folder(self): | ||
1479 | 190 | """check if remote folder exists and is write- and executable. | ||
1480 | 191 | Create folder if it doesn't exist.""" | ||
1481 | 192 | cmd = 'd=0;' | ||
1482 | 193 | cmd += '[[ -e %s ]] || d=1;' % self.path #path doesn't exist. set d=1 to indicate | ||
1483 | 194 | cmd += '[[ $d -eq 1 ]] && mkdir %s; err=$?;' % self.path #create path, get errorcode from mkdir | ||
1484 | 195 | cmd += '[[ $d -eq 1 ]] && exit $err;' #return errorcode from mkdir | ||
1485 | 196 | cmd += '[[ -d %s ]] || exit 11;' % self.path #path is no directory | ||
1486 | 197 | cmd += '[[ -w %s ]] || exit 12;' % self.path #path is not writeable | ||
1487 | 198 | cmd += '[[ -x %s ]] || exit 13;' % self.path #path is not executable | ||
1488 | 199 | cmd += 'exit 20' #everything is fine | ||
1489 | 200 | try: | ||
1490 | 201 | subprocess.check_call(['ssh', '-p', str(self.port), self.user + '@' + self.host, cmd], stdout=open(os.devnull, 'w')) | ||
1491 | 202 | except subprocess.CalledProcessError as ex: | ||
1492 | 203 | if ex.returncode == 20: | ||
1493 | 204 | #clean exit | ||
1494 | 205 | pass | ||
1495 | 206 | elif ex.returncode == 11: | ||
1496 | 207 | raise mount.MountException( _('Remote path exists but is not a directory:\n %s') % self.path) | ||
1497 | 208 | elif ex.returncode == 12: | ||
1498 | 209 | raise mount.MountException( _('Remote path is not writeable:\n %s') % self.path) | ||
1499 | 210 | elif ex.returncode == 13: | ||
1500 | 211 | raise mount.MountException( _('Remote path is not executable:\n %s') % self.path) | ||
1501 | 212 | else: | ||
1502 | 213 | raise mount.MountException( _('Couldn\'t create remote path:\n %s') % self.path) | ||
1503 | 214 | else: | ||
1504 | 215 | #returncode is 0 | ||
1505 | 216 | logger.info('Create remote folder %s' % self.path) | ||
1506 | 217 | |||
1507 | 218 | def check_ping_host(self): | ||
1508 | 219 | try: | ||
1509 | 220 | subprocess.check_call(['ping', '-q', '-c3', '-l3', self.host], stdout = open(os.devnull, 'w') ) | ||
1510 | 221 | except subprocess.CalledProcessError: | ||
1511 | 222 | raise mount.MountException( _('Ping %s failed. Host is down or wrong address.') % self.host) | ||
1512 | 223 | |||
1513 | 224 | def check_remote_commands(self): | ||
1514 | 225 | """try all relevant commands for take_snapshot on remote host. | ||
1515 | 226 | specialy embedded Linux devices using 'BusyBox' sometimes doesn't | ||
1516 | 227 | support everything that is need to run backintime. | ||
1517 | 228 | also check for hardlink-support on remote host. | ||
1518 | 229 | """ | ||
1519 | 230 | #check rsync | ||
1520 | 231 | tmp_file = tempfile.mkstemp()[1] | ||
1521 | 232 | rsync = tools.get_rsync_prefix( self.config ) + ' --dry-run --chmod=Du+wx %s ' % tmp_file | ||
1522 | 233 | |||
1523 | 234 | if self.cipher == 'default': | ||
1524 | 235 | ssh_cipher_suffix = '' | ||
1525 | 236 | else: | ||
1526 | 237 | ssh_cipher_suffix = '-c %s' % self.cipher | ||
1527 | 238 | rsync += '--rsh="ssh -p %s %s" ' % ( str(self.port), ssh_cipher_suffix) | ||
1528 | 239 | rsync += '"%s@%s:%s"' % (self.user, self.host, self.path) | ||
1529 | 240 | |||
1530 | 241 | #use os.system for compatiblity with snapshots.py | ||
1531 | 242 | err = os.system(rsync) | ||
1532 | 243 | if err: | ||
1533 | 244 | os.remove(tmp_file) | ||
1534 | 245 | raise mount.MountException( _('Remote host %s doesn\'t support \'%s\':\n%s\nLook at \'man backintime\' for further instructions') % (self.host, rsync, err)) | ||
1535 | 246 | os.remove(tmp_file) | ||
1536 | 247 | |||
1537 | 248 | #check cp chmod find and rm | ||
1538 | 249 | remote_tmp_dir = os.path.join(self.path, 'tmp_%s' % self.random_id()) | ||
1539 | 250 | cmd = 'tmp=%s ; ' % remote_tmp_dir | ||
1540 | 251 | #first define a function to clean up and exit | ||
1541 | 252 | cmd += 'cleanup(){ ' | ||
1542 | 253 | cmd += '[[ -e $tmp/a ]] && rm $tmp/a >/dev/null 2>&1; ' | ||
1543 | 254 | cmd += '[[ -e $tmp/b ]] && rm $tmp/b >/dev/null 2>&1; ' | ||
1544 | 255 | cmd += '[[ -e $tmp ]] && rmdir $tmp >/dev/null 2>&1; ' | ||
1545 | 256 | cmd += 'exit $1; }; ' | ||
1546 | 257 | #create tmp_RANDOM dir and file a | ||
1547 | 258 | cmd += '[[ -e $tmp ]] || mkdir $tmp; touch $tmp/a; ' | ||
1548 | 259 | #try to create hardlink b from a | ||
1549 | 260 | cmd += 'echo \"cp -aRl SOURCE DEST\"; cp -aRl $tmp/a $tmp/b >/dev/null; err_cp=$?; ' | ||
1550 | 261 | cmd += '[[ $err_cp -ne 0 ]] && cleanup $err_cp; ' | ||
1551 | 262 | #list inodes of a and b | ||
1552 | 263 | cmd += 'ls -i $tmp/a; ls -i $tmp/b; ' | ||
1553 | 264 | #try to chmod | ||
1554 | 265 | cmd += 'echo \"chmod u+rw FILE\"; chmod u+rw $tmp/a >/dev/null; err_chmod=$?; ' | ||
1555 | 266 | cmd += '[[ $err_chmod -ne 0 ]] && cleanup $err_chmod; ' | ||
1556 | 267 | #try to find and chmod | ||
1557 | 268 | cmd += 'echo \"find PATH -type f -exec chmod u-wx \"{}\" \\;\"; ' | ||
1558 | 269 | cmd += 'find $tmp -type f -exec chmod u-wx \"{}\" \\; >/dev/null; err_find=$?; ' | ||
1559 | 270 | cmd += '[[ $err_find -ne 0 ]] && cleanup $err_find; ' | ||
1560 | 271 | #try to rm -rf | ||
1561 | 272 | cmd += 'echo \"rm -rf PATH\"; rm -rf $tmp >/dev/null; err_rm=$?; ' | ||
1562 | 273 | cmd += '[[ $err_rm -ne 0 ]] && cleanup $err_rm; ' | ||
1563 | 274 | #if we end up here, everything should be fine | ||
1564 | 275 | cmd += 'echo \"done\"' | ||
1565 | 276 | output, err = subprocess.Popen(['ssh', '-p', str(self.port), self.user + '@' + self.host, cmd], | ||
1566 | 277 | stdout=subprocess.PIPE, | ||
1567 | 278 | stderr=subprocess.PIPE).communicate() | ||
1568 | 279 | |||
1569 | 280 | ## print('ERROR: %s' % err) | ||
1570 | 281 | ## print('OUTPUT: %s' % output) | ||
1571 | 282 | output_split = output.split('\n') | ||
1572 | 283 | while True: | ||
1573 | 284 | if len(output_split) > 0 and len(output_split[-1]) == 0: | ||
1574 | 285 | output_split = output_split[:-1] | ||
1575 | 286 | else: | ||
1576 | 287 | break | ||
1577 | 288 | if err or not output_split[-1].startswith('done'): | ||
1578 | 289 | for command in ('cp', 'chmod', 'find', 'rm'): | ||
1579 | 290 | if output_split[-1].startswith(command): | ||
1580 | 291 | raise mount.MountException( _('Remote host %s doesn\'t support \'%s\':\n%s\nLook at \'man backintime\' for further instructions') % (self.host, output_split[-1], err)) | ||
1581 | 292 | raise mount.MountException( _('Check commands on host %s returned unknown error:\n%s\nLook at \'man backintime\' for further instructions') % (self.host, err)) | ||
1582 | 293 | |||
1583 | 294 | i = 1 | ||
1584 | 295 | inode1 = 'ABC' | ||
1585 | 296 | inode2 = 'DEF' | ||
1586 | 297 | for line in output_split: | ||
1587 | 298 | if line.startswith('cp'): | ||
1588 | 299 | try: | ||
1589 | 300 | inode1 = output_split[i].split(' ')[0] | ||
1590 | 301 | inode2 = output_split[i+1].split(' ')[0] | ||
1591 | 302 | except IndexError: | ||
1592 | 303 | pass | ||
1593 | 304 | if not inode1 == inode2: | ||
1594 | 305 | raise mount.MountException( _('Remote host %s doesn\'t support hardlinks') % self.host) | ||
1595 | 306 | i += 1 | ||
1596 | 307 | |||
1597 | 308 | def random_id(self, size=6, chars=string.ascii_uppercase + string.digits): | ||
1598 | 309 | return ''.join(random.choice(chars) for x in range(size)) | ||
1599 | 0 | 310 | ||
1600 | === modified file 'common/tools.py' | |||
1601 | --- common/tools.py 2012-02-20 13:12:41 +0000 | |||
1602 | +++ common/tools.py 2012-10-23 19:40:24 +0000 | |||
1603 | @@ -399,7 +399,27 @@ | |||
1604 | 399 | obj = os.stat(path) | 399 | obj = os.stat(path) |
1605 | 400 | unique_key = (obj.st_size, int(obj.st_mtime)) | 400 | unique_key = (obj.st_size, int(obj.st_mtime)) |
1606 | 401 | return unique_key | 401 | return unique_key |
1608 | 402 | 402 | ||
1609 | 403 | def check_cron_pattern(str): | ||
1610 | 404 | '''check if str look like '0,10,13,15,17,20,23' or '*/6' ''' | ||
1611 | 405 | if str.find(' ') >= 0: | ||
1612 | 406 | return False | ||
1613 | 407 | try: | ||
1614 | 408 | if str.startswith('*/'): | ||
1615 | 409 | if int(str[2:]) <= 24: | ||
1616 | 410 | return True | ||
1617 | 411 | else: | ||
1618 | 412 | return False | ||
1619 | 413 | list = str.split(',') | ||
1620 | 414 | for s in list: | ||
1621 | 415 | if int(s) <= 24: | ||
1622 | 416 | continue | ||
1623 | 417 | else: | ||
1624 | 418 | return False | ||
1625 | 419 | return True | ||
1626 | 420 | except ValueError: | ||
1627 | 421 | return False | ||
1628 | 422 | |||
1629 | 403 | # | 423 | # |
1630 | 404 | # | 424 | # |
1631 | 405 | class UniquenessSet: | 425 | class UniquenessSet: |
1632 | 406 | 426 | ||
1633 | === modified file 'gnome/app.py' | |||
1634 | --- gnome/app.py 2012-02-22 09:55:08 +0000 | |||
1635 | +++ gnome/app.py 2012-10-23 19:40:24 +0000 | |||
1636 | @@ -44,6 +44,7 @@ | |||
1637 | 44 | import snapshots | 44 | import snapshots |
1638 | 45 | import guiapplicationinstance | 45 | import guiapplicationinstance |
1639 | 46 | import tools | 46 | import tools |
1640 | 47 | import mount | ||
1641 | 47 | 48 | ||
1642 | 48 | import settingsdialog | 49 | import settingsdialog |
1643 | 49 | import logviewdialog | 50 | import logviewdialog |
1644 | @@ -309,20 +310,31 @@ | |||
1645 | 309 | 310 | ||
1646 | 310 | if not self.config.is_configured(): | 311 | if not self.config.is_configured(): |
1647 | 311 | return | 312 | return |
1655 | 312 | 313 | ||
1656 | 313 | if self.snapshots.has_old_snapshots(): | 314 | if self.snapshots.has_old_snapshots(): |
1657 | 314 | settingsdialog.SettingsDialog( self.config, self.snapshots, self ).update_snapshots_location() | 315 | settingsdialog.SettingsDialog( self.config, self.snapshots, self ).update_snapshots_location() |
1658 | 315 | 316 | ||
1659 | 316 | profile_id = self.config.get_current_profile() | 317 | profile_id = self.config.get_current_profile() |
1660 | 317 | if not self.config.can_backup( profile_id ): | 318 | |
1661 | 318 | messagebox.show_error( self.window, self.config, _('Can\'t find snapshots folder.\nIf it is on a removable drive please plug it and then press OK') ) | 319 | #mount |
1662 | 320 | try: | ||
1663 | 321 | mnt = mount.Mount(cfg = self.config, profile_id = profile_id) | ||
1664 | 322 | hash_id = mnt.mount() | ||
1665 | 323 | except mount.MountException as ex: | ||
1666 | 324 | messagebox.show_error( self.window, self.config, str(ex) ) | ||
1667 | 325 | sys.exit(1) | ||
1668 | 326 | else: | ||
1669 | 327 | self.config.set_current_hash_id(hash_id) | ||
1670 | 328 | |||
1671 | 329 | if not self.config.can_backup( profile_id ): | ||
1672 | 330 | messagebox.show_error( self.window, self.config, _('Can\'t find snapshots folder.\nIf it is on a removable drive please plug it and then press OK') ) | ||
1673 | 319 | 331 | ||
1674 | 320 | self.update_profiles() | 332 | self.update_profiles() |
1675 | 321 | self.update_backup_info() | 333 | self.update_backup_info() |
1676 | 322 | gobject.timeout_add( 1000, self.update_backup_info ) | 334 | gobject.timeout_add( 1000, self.update_backup_info ) |
1677 | 323 | 335 | ||
1678 | 324 | def on_combo_profiles_changed( self, *params ): | 336 | def on_combo_profiles_changed( self, *params ): |
1680 | 325 | if self.disable_combo_changed: | 337 | if self.disable_combo_changed: |
1681 | 326 | return | 338 | return |
1682 | 327 | 339 | ||
1683 | 328 | iter = self.combo_profiles.get_active_iter() | 340 | iter = self.combo_profiles.get_active_iter() |
1684 | @@ -335,6 +347,7 @@ | |||
1685 | 335 | if not first_update_all and profile_id == self.config.get_current_profile(): | 347 | if not first_update_all and profile_id == self.config.get_current_profile(): |
1686 | 336 | return | 348 | return |
1687 | 337 | 349 | ||
1688 | 350 | self.remount(profile_id, self.config.get_current_profile()) | ||
1689 | 338 | self.config.set_current_profile( profile_id ) | 351 | self.config.set_current_profile( profile_id ) |
1690 | 339 | self.first_update_all = False | 352 | self.first_update_all = False |
1691 | 340 | self.update_all( first_update_all ) | 353 | self.update_all( first_update_all ) |
1692 | @@ -352,7 +365,7 @@ | |||
1693 | 352 | iter = self.store_profiles.append( [ self.config.get_profile_name( profile_id ), profile_id ] ) | 365 | iter = self.store_profiles.append( [ self.config.get_profile_name( profile_id ), profile_id ] ) |
1694 | 353 | if profile_id == self.config.get_current_profile(): | 366 | if profile_id == self.config.get_current_profile(): |
1695 | 354 | select_iter = iter | 367 | select_iter = iter |
1697 | 355 | 368 | ||
1698 | 356 | self.disable_combo_changed = False | 369 | self.disable_combo_changed = False |
1699 | 357 | 370 | ||
1700 | 358 | if not select_iter is None: | 371 | if not select_iter is None: |
1701 | @@ -467,6 +480,15 @@ | |||
1702 | 467 | self.fill_places() | 480 | self.fill_places() |
1703 | 468 | self.fill_time_line( False ) | 481 | self.fill_time_line( False ) |
1704 | 469 | self.update_folder_view( 1, selected_file, show_snapshots ) | 482 | self.update_folder_view( 1, selected_file, show_snapshots ) |
1705 | 483 | |||
1706 | 484 | def remount( self, new_profile_id, old_profile_id): | ||
1707 | 485 | try: | ||
1708 | 486 | mnt = mount.Mount(cfg = self.config, profile_id = old_profile_id) | ||
1709 | 487 | hash_id = mnt.remount(new_profile_id) | ||
1710 | 488 | except mount.MountException as ex: | ||
1711 | 489 | messagebox.show_error( self.window, self.config, str(ex) ) | ||
1712 | 490 | else: | ||
1713 | 491 | self.config.set_current_hash_id(hash_id) | ||
1714 | 470 | 492 | ||
1715 | 471 | def places_pix_renderer_function( self, column, renderer, model, iter, user_data ): | 493 | def places_pix_renderer_function( self, column, renderer, model, iter, user_data ): |
1716 | 472 | if len( model.get_value( iter, 1 ) ) == 0: | 494 | if len( model.get_value( iter, 1 ) ) == 0: |
1717 | @@ -731,6 +753,13 @@ | |||
1718 | 731 | self.config.set_int_value( 'gnome.main_window.hpaned2', main_window_hpaned2 ) | 753 | self.config.set_int_value( 'gnome.main_window.hpaned2', main_window_hpaned2 ) |
1719 | 732 | self.config.set_str_value( 'gnome.last_path', self.folder_path ) | 754 | self.config.set_str_value( 'gnome.last_path', self.folder_path ) |
1720 | 733 | self.config.set_bool_value( 'gnome.show_hidden_files', self.show_hidden_files ) | 755 | self.config.set_bool_value( 'gnome.show_hidden_files', self.show_hidden_files ) |
1721 | 756 | |||
1722 | 757 | #mount | ||
1723 | 758 | try: | ||
1724 | 759 | mnt = mount.Mount(cfg = self.config) | ||
1725 | 760 | mnt.umount(self.config.current_hash_id) | ||
1726 | 761 | except mount.MountException as ex: | ||
1727 | 762 | messagebox.show_error( self.window, self.config, str(ex) ) | ||
1728 | 734 | 763 | ||
1729 | 735 | self.config.save() | 764 | self.config.save() |
1730 | 736 | self.window.destroy() | 765 | self.window.destroy() |
1731 | @@ -966,10 +995,22 @@ | |||
1732 | 966 | def on_btn_settings_clicked( self, button ): | 995 | def on_btn_settings_clicked( self, button ): |
1733 | 967 | snapshots_full_path = self.config.get_snapshots_full_path() | 996 | snapshots_full_path = self.config.get_snapshots_full_path() |
1734 | 968 | include_folders = self.config.get_include() | 997 | include_folders = self.config.get_include() |
1735 | 998 | hash_id = self.config.current_hash_id | ||
1736 | 969 | 999 | ||
1737 | 970 | settingsdialog.SettingsDialog( self.config, self.snapshots, self ).run() | 1000 | settingsdialog.SettingsDialog( self.config, self.snapshots, self ).run() |
1738 | 1001 | |||
1739 | 1002 | #mount | ||
1740 | 1003 | try: | ||
1741 | 1004 | mnt = mount.Mount(cfg = self.config) | ||
1742 | 1005 | new_hash_id = mnt.remount(self.config.get_current_profile()) | ||
1743 | 1006 | except mount.MountException as ex: | ||
1744 | 1007 | messagebox.show_error( self.window, self.config, str(ex) ) | ||
1745 | 1008 | else: | ||
1746 | 1009 | self.config.set_current_hash_id(new_hash_id) | ||
1747 | 971 | 1010 | ||
1749 | 972 | if snapshots_full_path != self.config.get_snapshots_full_path() or include_folders != self.config.get_include(): | 1011 | if snapshots_full_path != self.config.get_snapshots_full_path() \ |
1750 | 1012 | or include_folders != self.config.get_include() \ | ||
1751 | 1013 | or hash_id != self.config.current_hash_id: | ||
1752 | 973 | self.update_all( False ) | 1014 | self.update_all( False ) |
1753 | 974 | self.update_profiles() | 1015 | self.update_profiles() |
1754 | 975 | 1016 | ||
1755 | 976 | 1017 | ||
1756 | === modified file 'gnome/man/C/backintime-gnome.1.gz' | |||
1757 | 977 | Binary files gnome/man/C/backintime-gnome.1.gz 2012-03-06 21:23:31 +0000 and gnome/man/C/backintime-gnome.1.gz 2012-10-23 19:40:24 +0000 differ | 1018 | Binary files gnome/man/C/backintime-gnome.1.gz 2012-03-06 21:23:31 +0000 and gnome/man/C/backintime-gnome.1.gz 2012-10-23 19:40:24 +0000 differ |
1758 | === modified file 'gnome/settingsdialog.glade' | |||
1759 | --- gnome/settingsdialog.glade 2012-02-20 11:30:09 +0000 | |||
1760 | +++ gnome/settingsdialog.glade 2012-10-23 19:40:24 +0000 | |||
1761 | @@ -177,239 +177,607 @@ | |||
1762 | 177 | <property name="border_width">5</property> | 177 | <property name="border_width">5</property> |
1763 | 178 | <property name="spacing">5</property> | 178 | <property name="spacing">5</property> |
1764 | 179 | <child> | 179 | <child> |
1766 | 180 | <object class="GtkFrame" id="frame1"> | 180 | <object class="GtkFrame" id="select_modes"> |
1767 | 181 | <property name="visible">True</property> | 181 | <property name="visible">True</property> |
1768 | 182 | <property name="can_focus">False</property> | 182 | <property name="can_focus">False</property> |
1769 | 183 | <property name="label_xalign">0</property> | 183 | <property name="label_xalign">0</property> |
1770 | 184 | <property name="shadow_type">none</property> | 184 | <property name="shadow_type">none</property> |
1771 | 185 | <child> | 185 | <child> |
1772 | 186 | <object class="GtkHBox" id="hbox_select_modes"> | ||
1773 | 187 | <property name="visible">True</property> | ||
1774 | 188 | <property name="can_focus">False</property> | ||
1775 | 189 | <child> | ||
1776 | 190 | <object class="GtkLabel" id="lbl_select_modes"> | ||
1777 | 191 | <property name="visible">True</property> | ||
1778 | 192 | <property name="can_focus">False</property> | ||
1779 | 193 | <property name="xalign">1</property> | ||
1780 | 194 | <property name="label" translatable="yes">Mode:</property> | ||
1781 | 195 | </object> | ||
1782 | 196 | <packing> | ||
1783 | 197 | <property name="expand">False</property> | ||
1784 | 198 | <property name="fill">True</property> | ||
1785 | 199 | <property name="position">0</property> | ||
1786 | 200 | </packing> | ||
1787 | 201 | </child> | ||
1788 | 202 | <child> | ||
1789 | 203 | <object class="GtkComboBox" id="combo_modes"> | ||
1790 | 204 | <property name="visible">True</property> | ||
1791 | 205 | <property name="can_focus">False</property> | ||
1792 | 206 | <signal name="changed" handler="on_combo_modes_changed" swapped="no"/> | ||
1793 | 207 | </object> | ||
1794 | 208 | <packing> | ||
1795 | 209 | <property name="expand">True</property> | ||
1796 | 210 | <property name="fill">True</property> | ||
1797 | 211 | <property name="position">1</property> | ||
1798 | 212 | </packing> | ||
1799 | 213 | </child> | ||
1800 | 214 | </object> | ||
1801 | 215 | </child> | ||
1802 | 216 | </object> | ||
1803 | 217 | <packing> | ||
1804 | 218 | <property name="expand">False</property> | ||
1805 | 219 | <property name="fill">True</property> | ||
1806 | 220 | <property name="position">0</property> | ||
1807 | 221 | </packing> | ||
1808 | 222 | </child> | ||
1809 | 223 | <child> | ||
1810 | 224 | <object class="GtkFrame" id="mode_local"> | ||
1811 | 225 | <property name="can_focus">False</property> | ||
1812 | 226 | <property name="label_xalign">0</property> | ||
1813 | 227 | <property name="shadow_type">none</property> | ||
1814 | 228 | <child> | ||
1815 | 186 | <object class="GtkAlignment" id="alignment1"> | 229 | <object class="GtkAlignment" id="alignment1"> |
1816 | 187 | <property name="visible">True</property> | 230 | <property name="visible">True</property> |
1817 | 188 | <property name="can_focus">False</property> | 231 | <property name="can_focus">False</property> |
1818 | 189 | <property name="left_padding">12</property> | 232 | <property name="left_padding">12</property> |
1819 | 190 | <child> | 233 | <child> |
1850 | 191 | <object class="GtkVBox" id="vbox1"> | 234 | <object class="GtkHBox" id="hbox4"> |
1851 | 192 | <property name="visible">True</property> | 235 | <property name="visible">True</property> |
1852 | 193 | <property name="can_focus">False</property> | 236 | <property name="can_focus">False</property> |
1853 | 194 | <property name="spacing">2</property> | 237 | <child> |
1854 | 195 | <child> | 238 | <object class="GtkEntry" id="edit_where"> |
1855 | 196 | <object class="GtkHBox" id="hbox4"> | 239 | <property name="visible">True</property> |
1856 | 197 | <property name="visible">True</property> | 240 | <property name="can_focus">True</property> |
1857 | 198 | <property name="can_focus">False</property> | 241 | <property name="editable">False</property> |
1858 | 199 | <child> | 242 | <property name="invisible_char">•</property> |
1859 | 200 | <object class="GtkEntry" id="edit_where"> | 243 | <property name="primary_icon_activatable">False</property> |
1860 | 201 | <property name="visible">True</property> | 244 | <property name="secondary_icon_activatable">False</property> |
1861 | 202 | <property name="can_focus">True</property> | 245 | <property name="primary_icon_sensitive">True</property> |
1862 | 203 | <property name="editable">False</property> | 246 | <property name="secondary_icon_sensitive">True</property> |
1863 | 204 | <property name="invisible_char">•</property> | 247 | </object> |
1864 | 205 | <property name="primary_icon_activatable">False</property> | 248 | <packing> |
1865 | 206 | <property name="secondary_icon_activatable">False</property> | 249 | <property name="expand">True</property> |
1866 | 207 | <property name="primary_icon_sensitive">True</property> | 250 | <property name="fill">True</property> |
1867 | 208 | <property name="secondary_icon_sensitive">True</property> | 251 | <property name="position">0</property> |
1868 | 209 | </object> | 252 | </packing> |
1869 | 210 | <packing> | 253 | </child> |
1870 | 211 | <property name="expand">True</property> | 254 | <child> |
1871 | 212 | <property name="fill">True</property> | 255 | <object class="GtkButton" id="btn_where"> |
1872 | 213 | <property name="position">0</property> | 256 | <property name="visible">True</property> |
1873 | 214 | </packing> | 257 | <property name="can_focus">True</property> |
1874 | 215 | </child> | 258 | <property name="receives_default">True</property> |
1875 | 216 | <child> | 259 | <property name="use_action_appearance">False</property> |
1876 | 217 | <object class="GtkButton" id="btn_where"> | 260 | <signal name="clicked" handler="on_btn_where_clicked" swapped="no"/> |
1877 | 218 | <property name="visible">True</property> | 261 | <child> |
1878 | 219 | <property name="can_focus">True</property> | 262 | <object class="GtkImage" id="image1"> |
1879 | 220 | <property name="receives_default">True</property> | 263 | <property name="visible">True</property> |
1880 | 264 | <property name="can_focus">False</property> | ||
1881 | 265 | <property name="stock">gtk-directory</property> | ||
1882 | 266 | </object> | ||
1883 | 267 | </child> | ||
1884 | 268 | </object> | ||
1885 | 269 | <packing> | ||
1886 | 270 | <property name="expand">False</property> | ||
1887 | 271 | <property name="fill">True</property> | ||
1888 | 272 | <property name="position">1</property> | ||
1889 | 273 | </packing> | ||
1890 | 274 | </child> | ||
1891 | 275 | </object> | ||
1892 | 276 | </child> | ||
1893 | 277 | </object> | ||
1894 | 278 | </child> | ||
1895 | 279 | <child type="label"> | ||
1896 | 280 | <object class="GtkLabel" id="lbl_where"> | ||
1897 | 281 | <property name="visible">True</property> | ||
1898 | 282 | <property name="can_focus">False</property> | ||
1899 | 283 | <property name="label" translatable="yes"><b>Where to save snapshots</b></property> | ||
1900 | 284 | <property name="use_markup">True</property> | ||
1901 | 285 | </object> | ||
1902 | 286 | </child> | ||
1903 | 287 | </object> | ||
1904 | 288 | <packing> | ||
1905 | 289 | <property name="expand">False</property> | ||
1906 | 290 | <property name="fill">True</property> | ||
1907 | 291 | <property name="position">10</property> | ||
1908 | 292 | </packing> | ||
1909 | 293 | </child> | ||
1910 | 294 | <child> | ||
1911 | 295 | <object class="GtkFrame" id="mode_ssh"> | ||
1912 | 296 | <property name="can_focus">False</property> | ||
1913 | 297 | <property name="label_xalign">0</property> | ||
1914 | 298 | <property name="shadow_type">none</property> | ||
1915 | 299 | <child> | ||
1916 | 300 | <object class="GtkAlignment" id="alignment_ssh"> | ||
1917 | 301 | <property name="visible">True</property> | ||
1918 | 302 | <property name="can_focus">False</property> | ||
1919 | 303 | <property name="left_padding">12</property> | ||
1920 | 304 | <child> | ||
1921 | 305 | <object class="GtkVBox" id="vbox_ssh"> | ||
1922 | 306 | <property name="visible">True</property> | ||
1923 | 307 | <property name="can_focus">False</property> | ||
1924 | 308 | <property name="spacing">2</property> | ||
1925 | 309 | <child> | ||
1926 | 310 | <object class="GtkHBox" id="hbox_ssh1"> | ||
1927 | 311 | <property name="visible">True</property> | ||
1928 | 312 | <property name="can_focus">False</property> | ||
1929 | 313 | <property name="spacing">2</property> | ||
1930 | 314 | <child> | ||
1931 | 315 | <object class="GtkLabel" id="lbl_ssh_host"> | ||
1932 | 316 | <property name="visible">True</property> | ||
1933 | 317 | <property name="can_focus">False</property> | ||
1934 | 318 | <property name="xalign">1</property> | ||
1935 | 319 | <property name="label" translatable="yes">Host:</property> | ||
1936 | 320 | <property name="width_chars">8</property> | ||
1937 | 321 | </object> | ||
1938 | 322 | <packing> | ||
1939 | 323 | <property name="expand">False</property> | ||
1940 | 324 | <property name="fill">True</property> | ||
1941 | 325 | <property name="position">0</property> | ||
1942 | 326 | </packing> | ||
1943 | 327 | </child> | ||
1944 | 328 | <child> | ||
1945 | 329 | <object class="GtkEntry" id="txt_ssh_host"> | ||
1946 | 330 | <property name="visible">True</property> | ||
1947 | 331 | <property name="can_focus">True</property> | ||
1948 | 332 | <property name="invisible_char">•</property> | ||
1949 | 333 | <property name="primary_icon_activatable">False</property> | ||
1950 | 334 | <property name="secondary_icon_activatable">False</property> | ||
1951 | 335 | <property name="primary_icon_sensitive">True</property> | ||
1952 | 336 | <property name="secondary_icon_sensitive">True</property> | ||
1953 | 337 | </object> | ||
1954 | 338 | <packing> | ||
1955 | 339 | <property name="expand">True</property> | ||
1956 | 340 | <property name="fill">True</property> | ||
1957 | 341 | <property name="position">1</property> | ||
1958 | 342 | </packing> | ||
1959 | 343 | </child> | ||
1960 | 344 | <child> | ||
1961 | 345 | <object class="GtkLabel" id="lbl_ssh_port"> | ||
1962 | 346 | <property name="visible">True</property> | ||
1963 | 347 | <property name="can_focus">False</property> | ||
1964 | 348 | <property name="xalign">1</property> | ||
1965 | 349 | <property name="label" translatable="yes">Port:</property> | ||
1966 | 350 | <property name="width_chars">8</property> | ||
1967 | 351 | </object> | ||
1968 | 352 | <packing> | ||
1969 | 353 | <property name="expand">False</property> | ||
1970 | 354 | <property name="fill">True</property> | ||
1971 | 355 | <property name="position">2</property> | ||
1972 | 356 | </packing> | ||
1973 | 357 | </child> | ||
1974 | 358 | <child> | ||
1975 | 359 | <object class="GtkEntry" id="txt_ssh_port"> | ||
1976 | 360 | <property name="visible">True</property> | ||
1977 | 361 | <property name="can_focus">True</property> | ||
1978 | 362 | <property name="invisible_char">•</property> | ||
1979 | 363 | <property name="primary_icon_activatable">False</property> | ||
1980 | 364 | <property name="secondary_icon_activatable">False</property> | ||
1981 | 365 | <property name="primary_icon_sensitive">True</property> | ||
1982 | 366 | <property name="secondary_icon_sensitive">True</property> | ||
1983 | 367 | </object> | ||
1984 | 368 | <packing> | ||
1985 | 369 | <property name="expand">True</property> | ||
1986 | 370 | <property name="fill">True</property> | ||
1987 | 371 | <property name="position">3</property> | ||
1988 | 372 | </packing> | ||
1989 | 373 | </child> | ||
1990 | 374 | <child> | ||
1991 | 375 | <object class="GtkLabel" id="lbl_ssh_user"> | ||
1992 | 376 | <property name="visible">True</property> | ||
1993 | 377 | <property name="can_focus">False</property> | ||
1994 | 378 | <property name="xalign">1</property> | ||
1995 | 379 | <property name="label" translatable="yes">User:</property> | ||
1996 | 380 | <property name="width_chars">8</property> | ||
1997 | 381 | </object> | ||
1998 | 382 | <packing> | ||
1999 | 383 | <property name="expand">False</property> | ||
2000 | 384 | <property name="fill">True</property> | ||
2001 | 385 | <property name="position">4</property> | ||
2002 | 386 | </packing> | ||
2003 | 387 | </child> | ||
2004 | 388 | <child> | ||
2005 | 389 | <object class="GtkEntry" id="txt_ssh_user"> | ||
2006 | 390 | <property name="visible">True</property> | ||
2007 | 391 | <property name="can_focus">True</property> | ||
2008 | 392 | <property name="invisible_char">•</property> | ||
2009 | 393 | <property name="primary_icon_activatable">False</property> | ||
2010 | 394 | <property name="secondary_icon_activatable">False</property> | ||
2011 | 395 | <property name="primary_icon_sensitive">True</property> | ||
2012 | 396 | <property name="secondary_icon_sensitive">True</property> | ||
2013 | 397 | </object> | ||
2014 | 398 | <packing> | ||
2015 | 399 | <property name="expand">True</property> | ||
2016 | 400 | <property name="fill">True</property> | ||
2017 | 401 | <property name="position">5</property> | ||
2018 | 402 | </packing> | ||
2019 | 403 | </child> | ||
2020 | 404 | </object> | ||
2021 | 405 | <packing> | ||
2022 | 406 | <property name="expand">True</property> | ||
2023 | 407 | <property name="fill">True</property> | ||
2024 | 408 | <property name="position">0</property> | ||
2025 | 409 | </packing> | ||
2026 | 410 | </child> | ||
2027 | 411 | <child> | ||
2028 | 412 | <object class="GtkHBox" id="hbox_ssh2"> | ||
2029 | 413 | <property name="visible">True</property> | ||
2030 | 414 | <property name="can_focus">False</property> | ||
2031 | 415 | <property name="spacing">2</property> | ||
2032 | 416 | <child> | ||
2033 | 417 | <object class="GtkLabel" id="lbl_ssh_path"> | ||
2034 | 418 | <property name="visible">True</property> | ||
2035 | 419 | <property name="can_focus">False</property> | ||
2036 | 420 | <property name="xalign">1</property> | ||
2037 | 421 | <property name="label" translatable="yes">Path:</property> | ||
2038 | 422 | <property name="width_chars">8</property> | ||
2039 | 423 | </object> | ||
2040 | 424 | <packing> | ||
2041 | 425 | <property name="expand">False</property> | ||
2042 | 426 | <property name="fill">True</property> | ||
2043 | 427 | <property name="position">0</property> | ||
2044 | 428 | </packing> | ||
2045 | 429 | </child> | ||
2046 | 430 | <child> | ||
2047 | 431 | <object class="GtkEntry" id="txt_ssh_path"> | ||
2048 | 432 | <property name="visible">True</property> | ||
2049 | 433 | <property name="can_focus">True</property> | ||
2050 | 434 | <property name="invisible_char">•</property> | ||
2051 | 435 | <property name="primary_icon_activatable">False</property> | ||
2052 | 436 | <property name="secondary_icon_activatable">False</property> | ||
2053 | 437 | <property name="primary_icon_sensitive">True</property> | ||
2054 | 438 | <property name="secondary_icon_sensitive">True</property> | ||
2055 | 439 | </object> | ||
2056 | 440 | <packing> | ||
2057 | 441 | <property name="expand">True</property> | ||
2058 | 442 | <property name="fill">True</property> | ||
2059 | 443 | <property name="position">1</property> | ||
2060 | 444 | </packing> | ||
2061 | 445 | </child> | ||
2062 | 446 | <child> | ||
2063 | 447 | <object class="GtkLabel" id="lbl_ssh_cipher"> | ||
2064 | 448 | <property name="visible">True</property> | ||
2065 | 449 | <property name="can_focus">False</property> | ||
2066 | 450 | <property name="xalign">1</property> | ||
2067 | 451 | <property name="label" translatable="yes">Cipher:</property> | ||
2068 | 452 | <property name="width_chars">8</property> | ||
2069 | 453 | </object> | ||
2070 | 454 | <packing> | ||
2071 | 455 | <property name="expand">False</property> | ||
2072 | 456 | <property name="fill">True</property> | ||
2073 | 457 | <property name="position">2</property> | ||
2074 | 458 | </packing> | ||
2075 | 459 | </child> | ||
2076 | 460 | <child> | ||
2077 | 461 | <object class="GtkComboBox" id="combo_ssh_cipher"> | ||
2078 | 462 | <property name="visible">True</property> | ||
2079 | 463 | <property name="can_focus">False</property> | ||
2080 | 464 | </object> | ||
2081 | 465 | <packing> | ||
2082 | 466 | <property name="expand">True</property> | ||
2083 | 467 | <property name="fill">True</property> | ||
2084 | 468 | <property name="position">3</property> | ||
2085 | 469 | </packing> | ||
2086 | 470 | </child> | ||
2087 | 471 | </object> | ||
2088 | 472 | <packing> | ||
2089 | 473 | <property name="expand">True</property> | ||
2090 | 474 | <property name="fill">True</property> | ||
2091 | 475 | <property name="position">1</property> | ||
2092 | 476 | </packing> | ||
2093 | 477 | </child> | ||
2094 | 478 | </object> | ||
2095 | 479 | </child> | ||
2096 | 480 | </object> | ||
2097 | 481 | </child> | ||
2098 | 482 | <child type="label"> | ||
2099 | 483 | <object class="GtkLabel" id="lbl_ssh"> | ||
2100 | 484 | <property name="visible">True</property> | ||
2101 | 485 | <property name="can_focus">False</property> | ||
2102 | 486 | <property name="label" translatable="yes"><b>SSH Settings</b></property> | ||
2103 | 487 | <property name="use_markup">True</property> | ||
2104 | 488 | </object> | ||
2105 | 489 | </child> | ||
2106 | 490 | </object> | ||
2107 | 491 | <packing> | ||
2108 | 492 | <property name="expand">False</property> | ||
2109 | 493 | <property name="fill">True</property> | ||
2110 | 494 | <property name="position">20</property> | ||
2111 | 495 | </packing> | ||
2112 | 496 | </child> | ||
2113 | 497 | <child> | ||
2114 | 498 | <object class="GtkFrame" id="mode_dummy"> | ||
2115 | 499 | <property name="can_focus">False</property> | ||
2116 | 500 | <property name="label_xalign">0</property> | ||
2117 | 501 | <property name="shadow_type">none</property> | ||
2118 | 502 | <child> | ||
2119 | 503 | <object class="GtkAlignment" id="alignment_dummy"> | ||
2120 | 504 | <property name="visible">True</property> | ||
2121 | 505 | <property name="can_focus">False</property> | ||
2122 | 506 | <property name="left_padding">12</property> | ||
2123 | 507 | <child> | ||
2124 | 508 | <object class="GtkHBox" id="hbox_dummy"> | ||
2125 | 509 | <property name="visible">True</property> | ||
2126 | 510 | <property name="can_focus">False</property> | ||
2127 | 511 | <property name="spacing">2</property> | ||
2128 | 512 | <child> | ||
2129 | 513 | <object class="GtkLabel" id="lbl_dummy_host"> | ||
2130 | 514 | <property name="visible">True</property> | ||
2131 | 515 | <property name="can_focus">False</property> | ||
2132 | 516 | <property name="xalign">1</property> | ||
2133 | 517 | <property name="label" translatable="yes">Host:</property> | ||
2134 | 518 | <property name="width_chars">8</property> | ||
2135 | 519 | </object> | ||
2136 | 520 | <packing> | ||
2137 | 521 | <property name="expand">False</property> | ||
2138 | 522 | <property name="fill">True</property> | ||
2139 | 523 | <property name="position">0</property> | ||
2140 | 524 | </packing> | ||
2141 | 525 | </child> | ||
2142 | 526 | <child> | ||
2143 | 527 | <object class="GtkEntry" id="txt_dummy_host"> | ||
2144 | 528 | <property name="visible">True</property> | ||
2145 | 529 | <property name="can_focus">True</property> | ||
2146 | 530 | <property name="invisible_char">•</property> | ||
2147 | 531 | <property name="primary_icon_activatable">False</property> | ||
2148 | 532 | <property name="secondary_icon_activatable">False</property> | ||
2149 | 533 | <property name="primary_icon_sensitive">True</property> | ||
2150 | 534 | <property name="secondary_icon_sensitive">True</property> | ||
2151 | 535 | </object> | ||
2152 | 536 | <packing> | ||
2153 | 537 | <property name="expand">True</property> | ||
2154 | 538 | <property name="fill">True</property> | ||
2155 | 539 | <property name="position">1</property> | ||
2156 | 540 | </packing> | ||
2157 | 541 | </child> | ||
2158 | 542 | <child> | ||
2159 | 543 | <object class="GtkLabel" id="lbl_dummy_port"> | ||
2160 | 544 | <property name="visible">True</property> | ||
2161 | 545 | <property name="can_focus">False</property> | ||
2162 | 546 | <property name="xalign">1</property> | ||
2163 | 547 | <property name="label" translatable="yes">Port:</property> | ||
2164 | 548 | <property name="width_chars">8</property> | ||
2165 | 549 | </object> | ||
2166 | 550 | <packing> | ||
2167 | 551 | <property name="expand">False</property> | ||
2168 | 552 | <property name="fill">True</property> | ||
2169 | 553 | <property name="position">2</property> | ||
2170 | 554 | </packing> | ||
2171 | 555 | </child> | ||
2172 | 556 | <child> | ||
2173 | 557 | <object class="GtkEntry" id="txt_dummy_port"> | ||
2174 | 558 | <property name="visible">True</property> | ||
2175 | 559 | <property name="can_focus">True</property> | ||
2176 | 560 | <property name="invisible_char">•</property> | ||
2177 | 561 | <property name="primary_icon_activatable">False</property> | ||
2178 | 562 | <property name="secondary_icon_activatable">False</property> | ||
2179 | 563 | <property name="primary_icon_sensitive">True</property> | ||
2180 | 564 | <property name="secondary_icon_sensitive">True</property> | ||
2181 | 565 | </object> | ||
2182 | 566 | <packing> | ||
2183 | 567 | <property name="expand">True</property> | ||
2184 | 568 | <property name="fill">True</property> | ||
2185 | 569 | <property name="position">3</property> | ||
2186 | 570 | </packing> | ||
2187 | 571 | </child> | ||
2188 | 572 | <child> | ||
2189 | 573 | <object class="GtkLabel" id="lbl_dummy_user"> | ||
2190 | 574 | <property name="visible">True</property> | ||
2191 | 575 | <property name="can_focus">False</property> | ||
2192 | 576 | <property name="xalign">1</property> | ||
2193 | 577 | <property name="label" translatable="yes">User:</property> | ||
2194 | 578 | <property name="width_chars">8</property> | ||
2195 | 579 | </object> | ||
2196 | 580 | <packing> | ||
2197 | 581 | <property name="expand">False</property> | ||
2198 | 582 | <property name="fill">True</property> | ||
2199 | 583 | <property name="position">4</property> | ||
2200 | 584 | </packing> | ||
2201 | 585 | </child> | ||
2202 | 586 | <child> | ||
2203 | 587 | <object class="GtkEntry" id="txt_dummy_user"> | ||
2204 | 588 | <property name="visible">True</property> | ||
2205 | 589 | <property name="can_focus">True</property> | ||
2206 | 590 | <property name="invisible_char">•</property> | ||
2207 | 591 | <property name="primary_icon_activatable">False</property> | ||
2208 | 592 | <property name="secondary_icon_activatable">False</property> | ||
2209 | 593 | <property name="primary_icon_sensitive">True</property> | ||
2210 | 594 | <property name="secondary_icon_sensitive">True</property> | ||
2211 | 595 | </object> | ||
2212 | 596 | <packing> | ||
2213 | 597 | <property name="expand">True</property> | ||
2214 | 598 | <property name="fill">True</property> | ||
2215 | 599 | <property name="position">5</property> | ||
2216 | 600 | </packing> | ||
2217 | 601 | </child> | ||
2218 | 602 | </object> | ||
2219 | 603 | </child> | ||
2220 | 604 | </object> | ||
2221 | 605 | </child> | ||
2222 | 606 | <child type="label"> | ||
2223 | 607 | <object class="GtkLabel" id="lbl_dummy"> | ||
2224 | 608 | <property name="visible">True</property> | ||
2225 | 609 | <property name="can_focus">False</property> | ||
2226 | 610 | <property name="label" translatable="yes"><b>Dummy Settings</b></property> | ||
2227 | 611 | <property name="use_markup">True</property> | ||
2228 | 612 | </object> | ||
2229 | 613 | </child> | ||
2230 | 614 | </object> | ||
2231 | 615 | <packing> | ||
2232 | 616 | <property name="expand">False</property> | ||
2233 | 617 | <property name="fill">True</property> | ||
2234 | 618 | <property name="position">800</property> | ||
2235 | 619 | </packing> | ||
2236 | 620 | </child> | ||
2237 | 621 | <child> | ||
2238 | 622 | <object class="GtkFrame" id="advanced"> | ||
2239 | 623 | <property name="visible">True</property> | ||
2240 | 624 | <property name="can_focus">False</property> | ||
2241 | 625 | <property name="label_xalign">0</property> | ||
2242 | 626 | <property name="shadow_type">none</property> | ||
2243 | 627 | <child> | ||
2244 | 628 | <object class="GtkExpander" id="expander1"> | ||
2245 | 629 | <property name="visible">True</property> | ||
2246 | 630 | <property name="can_focus">True</property> | ||
2247 | 631 | <child> | ||
2248 | 632 | <object class="GtkAlignment" id="alignment3"> | ||
2249 | 633 | <property name="visible">True</property> | ||
2250 | 634 | <property name="can_focus">False</property> | ||
2251 | 635 | <property name="left_padding">12</property> | ||
2252 | 636 | <child> | ||
2253 | 637 | <object class="GtkVBox" id="vbox3"> | ||
2254 | 638 | <property name="visible">True</property> | ||
2255 | 639 | <property name="can_focus">False</property> | ||
2256 | 640 | <property name="spacing">2</property> | ||
2257 | 641 | <child> | ||
2258 | 642 | <object class="GtkCheckButton" id="cb_auto_host_user_profile"> | ||
2259 | 643 | <property name="label" translatable="yes">Auto Host / User / Profile Id</property> | ||
2260 | 644 | <property name="visible">True</property> | ||
2261 | 645 | <property name="can_focus">True</property> | ||
2262 | 646 | <property name="receives_default">False</property> | ||
2263 | 221 | <property name="use_action_appearance">False</property> | 647 | <property name="use_action_appearance">False</property> |
2275 | 222 | <signal name="clicked" handler="on_btn_where_clicked" swapped="no"/> | 648 | <property name="draw_indicator">True</property> |
2276 | 223 | <child> | 649 | <signal name="toggled" handler="on_cb_auto_host_user_profile_toggled" swapped="no"/> |
2277 | 224 | <object class="GtkImage" id="image1"> | 650 | </object> |
2278 | 225 | <property name="visible">True</property> | 651 | <packing> |
2279 | 226 | <property name="can_focus">False</property> | 652 | <property name="expand">True</property> |
2280 | 227 | <property name="stock">gtk-directory</property> | 653 | <property name="fill">True</property> |
2281 | 228 | </object> | 654 | <property name="position">0</property> |
2282 | 229 | </child> | 655 | </packing> |
2283 | 230 | </object> | 656 | </child> |
2284 | 231 | <packing> | 657 | <child> |
2285 | 232 | <property name="expand">False</property> | 658 | <object class="GtkHBox" id="hbox8"> |
2286 | 659 | <property name="visible">True</property> | ||
2287 | 660 | <property name="can_focus">False</property> | ||
2288 | 661 | <property name="spacing">2</property> | ||
2289 | 662 | <child> | ||
2290 | 663 | <object class="GtkLabel" id="lbl_host"> | ||
2291 | 664 | <property name="visible">True</property> | ||
2292 | 665 | <property name="can_focus">False</property> | ||
2293 | 666 | <property name="xalign">1</property> | ||
2294 | 667 | <property name="label" translatable="yes">Host:</property> | ||
2295 | 668 | <property name="width_chars">8</property> | ||
2296 | 669 | </object> | ||
2297 | 670 | <packing> | ||
2298 | 671 | <property name="expand">False</property> | ||
2299 | 672 | <property name="fill">True</property> | ||
2300 | 673 | <property name="position">0</property> | ||
2301 | 674 | </packing> | ||
2302 | 675 | </child> | ||
2303 | 676 | <child> | ||
2304 | 677 | <object class="GtkEntry" id="txt_host"> | ||
2305 | 678 | <property name="visible">True</property> | ||
2306 | 679 | <property name="can_focus">True</property> | ||
2307 | 680 | <property name="invisible_char">•</property> | ||
2308 | 681 | <property name="primary_icon_activatable">False</property> | ||
2309 | 682 | <property name="secondary_icon_activatable">False</property> | ||
2310 | 683 | <property name="primary_icon_sensitive">True</property> | ||
2311 | 684 | <property name="secondary_icon_sensitive">True</property> | ||
2312 | 685 | </object> | ||
2313 | 686 | <packing> | ||
2314 | 687 | <property name="expand">True</property> | ||
2315 | 688 | <property name="fill">True</property> | ||
2316 | 689 | <property name="position">1</property> | ||
2317 | 690 | </packing> | ||
2318 | 691 | </child> | ||
2319 | 692 | <child> | ||
2320 | 693 | <object class="GtkLabel" id="lbl_user"> | ||
2321 | 694 | <property name="visible">True</property> | ||
2322 | 695 | <property name="can_focus">False</property> | ||
2323 | 696 | <property name="xalign">1</property> | ||
2324 | 697 | <property name="label" translatable="yes">User:</property> | ||
2325 | 698 | <property name="width_chars">8</property> | ||
2326 | 699 | </object> | ||
2327 | 700 | <packing> | ||
2328 | 701 | <property name="expand">False</property> | ||
2329 | 702 | <property name="fill">True</property> | ||
2330 | 703 | <property name="position">2</property> | ||
2331 | 704 | </packing> | ||
2332 | 705 | </child> | ||
2333 | 706 | <child> | ||
2334 | 707 | <object class="GtkEntry" id="txt_user"> | ||
2335 | 708 | <property name="visible">True</property> | ||
2336 | 709 | <property name="can_focus">True</property> | ||
2337 | 710 | <property name="invisible_char">•</property> | ||
2338 | 711 | <property name="primary_icon_activatable">False</property> | ||
2339 | 712 | <property name="secondary_icon_activatable">False</property> | ||
2340 | 713 | <property name="primary_icon_sensitive">True</property> | ||
2341 | 714 | <property name="secondary_icon_sensitive">True</property> | ||
2342 | 715 | </object> | ||
2343 | 716 | <packing> | ||
2344 | 717 | <property name="expand">True</property> | ||
2345 | 718 | <property name="fill">True</property> | ||
2346 | 719 | <property name="position">3</property> | ||
2347 | 720 | </packing> | ||
2348 | 721 | </child> | ||
2349 | 722 | <child> | ||
2350 | 723 | <object class="GtkLabel" id="lbl_profile"> | ||
2351 | 724 | <property name="visible">True</property> | ||
2352 | 725 | <property name="can_focus">False</property> | ||
2353 | 726 | <property name="xalign">1</property> | ||
2354 | 727 | <property name="label" translatable="yes">Profile Id:</property> | ||
2355 | 728 | <property name="width_chars">8</property> | ||
2356 | 729 | </object> | ||
2357 | 730 | <packing> | ||
2358 | 731 | <property name="expand">False</property> | ||
2359 | 732 | <property name="fill">True</property> | ||
2360 | 733 | <property name="position">4</property> | ||
2361 | 734 | </packing> | ||
2362 | 735 | </child> | ||
2363 | 736 | <child> | ||
2364 | 737 | <object class="GtkEntry" id="txt_profile"> | ||
2365 | 738 | <property name="visible">True</property> | ||
2366 | 739 | <property name="can_focus">True</property> | ||
2367 | 740 | <property name="invisible_char">•</property> | ||
2368 | 741 | <property name="primary_icon_activatable">False</property> | ||
2369 | 742 | <property name="secondary_icon_activatable">False</property> | ||
2370 | 743 | <property name="primary_icon_sensitive">True</property> | ||
2371 | 744 | <property name="secondary_icon_sensitive">True</property> | ||
2372 | 745 | </object> | ||
2373 | 746 | <packing> | ||
2374 | 747 | <property name="expand">True</property> | ||
2375 | 748 | <property name="fill">True</property> | ||
2376 | 749 | <property name="position">5</property> | ||
2377 | 750 | </packing> | ||
2378 | 751 | </child> | ||
2379 | 752 | </object> | ||
2380 | 753 | <packing> | ||
2381 | 754 | <property name="expand">True</property> | ||
2382 | 233 | <property name="fill">True</property> | 755 | <property name="fill">True</property> |
2383 | 234 | <property name="position">1</property> | 756 | <property name="position">1</property> |
2384 | 235 | </packing> | 757 | </packing> |
2385 | 236 | </child> | 758 | </child> |
2386 | 237 | </object> | 759 | </object> |
2551 | 238 | <packing> | 760 | </child> |
2552 | 239 | <property name="expand">True</property> | 761 | </object> |
2553 | 240 | <property name="fill">True</property> | 762 | </child> |
2554 | 241 | <property name="position">0</property> | 763 | <child type="label"> |
2555 | 242 | </packing> | 764 | <object class="GtkLabel" id="label7"> |
2556 | 243 | </child> | 765 | <property name="visible">True</property> |
2557 | 244 | <child> | 766 | <property name="can_focus">False</property> |
2558 | 245 | <object class="GtkExpander" id="expander1"> | 767 | <property name="label" translatable="yes">Advanced</property> |
2559 | 246 | <property name="visible">True</property> | 768 | </object> |
2560 | 247 | <property name="can_focus">True</property> | 769 | </child> |
2397 | 248 | <child> | ||
2398 | 249 | <object class="GtkAlignment" id="alignment3"> | ||
2399 | 250 | <property name="visible">True</property> | ||
2400 | 251 | <property name="can_focus">False</property> | ||
2401 | 252 | <property name="left_padding">12</property> | ||
2402 | 253 | <child> | ||
2403 | 254 | <object class="GtkVBox" id="vbox3"> | ||
2404 | 255 | <property name="visible">True</property> | ||
2405 | 256 | <property name="can_focus">False</property> | ||
2406 | 257 | <property name="spacing">2</property> | ||
2407 | 258 | <child> | ||
2408 | 259 | <object class="GtkCheckButton" id="cb_auto_host_user_profile"> | ||
2409 | 260 | <property name="label" translatable="yes">Auto Host / User / Profile Id</property> | ||
2410 | 261 | <property name="visible">True</property> | ||
2411 | 262 | <property name="can_focus">True</property> | ||
2412 | 263 | <property name="receives_default">False</property> | ||
2413 | 264 | <property name="use_action_appearance">False</property> | ||
2414 | 265 | <property name="draw_indicator">True</property> | ||
2415 | 266 | <signal name="toggled" handler="on_cb_auto_host_user_profile_toggled" swapped="no"/> | ||
2416 | 267 | </object> | ||
2417 | 268 | <packing> | ||
2418 | 269 | <property name="expand">True</property> | ||
2419 | 270 | <property name="fill">True</property> | ||
2420 | 271 | <property name="position">0</property> | ||
2421 | 272 | </packing> | ||
2422 | 273 | </child> | ||
2423 | 274 | <child> | ||
2424 | 275 | <object class="GtkHBox" id="hbox8"> | ||
2425 | 276 | <property name="visible">True</property> | ||
2426 | 277 | <property name="can_focus">False</property> | ||
2427 | 278 | <property name="spacing">2</property> | ||
2428 | 279 | <child> | ||
2429 | 280 | <object class="GtkLabel" id="lbl_host"> | ||
2430 | 281 | <property name="visible">True</property> | ||
2431 | 282 | <property name="can_focus">False</property> | ||
2432 | 283 | <property name="xalign">1</property> | ||
2433 | 284 | <property name="yalign">0.5899999737739563</property> | ||
2434 | 285 | <property name="label" translatable="yes">Host:</property> | ||
2435 | 286 | </object> | ||
2436 | 287 | <packing> | ||
2437 | 288 | <property name="expand">True</property> | ||
2438 | 289 | <property name="fill">True</property> | ||
2439 | 290 | <property name="position">0</property> | ||
2440 | 291 | </packing> | ||
2441 | 292 | </child> | ||
2442 | 293 | <child> | ||
2443 | 294 | <object class="GtkEntry" id="txt_host"> | ||
2444 | 295 | <property name="visible">True</property> | ||
2445 | 296 | <property name="can_focus">True</property> | ||
2446 | 297 | <property name="invisible_char">•</property> | ||
2447 | 298 | <property name="primary_icon_activatable">False</property> | ||
2448 | 299 | <property name="secondary_icon_activatable">False</property> | ||
2449 | 300 | <property name="primary_icon_sensitive">True</property> | ||
2450 | 301 | <property name="secondary_icon_sensitive">True</property> | ||
2451 | 302 | </object> | ||
2452 | 303 | <packing> | ||
2453 | 304 | <property name="expand">True</property> | ||
2454 | 305 | <property name="fill">True</property> | ||
2455 | 306 | <property name="position">1</property> | ||
2456 | 307 | </packing> | ||
2457 | 308 | </child> | ||
2458 | 309 | <child> | ||
2459 | 310 | <object class="GtkLabel" id="lbl_user"> | ||
2460 | 311 | <property name="visible">True</property> | ||
2461 | 312 | <property name="can_focus">False</property> | ||
2462 | 313 | <property name="xalign">1</property> | ||
2463 | 314 | <property name="label" translatable="yes">User:</property> | ||
2464 | 315 | </object> | ||
2465 | 316 | <packing> | ||
2466 | 317 | <property name="expand">True</property> | ||
2467 | 318 | <property name="fill">True</property> | ||
2468 | 319 | <property name="position">2</property> | ||
2469 | 320 | </packing> | ||
2470 | 321 | </child> | ||
2471 | 322 | <child> | ||
2472 | 323 | <object class="GtkEntry" id="txt_user"> | ||
2473 | 324 | <property name="visible">True</property> | ||
2474 | 325 | <property name="can_focus">True</property> | ||
2475 | 326 | <property name="invisible_char">•</property> | ||
2476 | 327 | <property name="primary_icon_activatable">False</property> | ||
2477 | 328 | <property name="secondary_icon_activatable">False</property> | ||
2478 | 329 | <property name="primary_icon_sensitive">True</property> | ||
2479 | 330 | <property name="secondary_icon_sensitive">True</property> | ||
2480 | 331 | </object> | ||
2481 | 332 | <packing> | ||
2482 | 333 | <property name="expand">True</property> | ||
2483 | 334 | <property name="fill">True</property> | ||
2484 | 335 | <property name="position">3</property> | ||
2485 | 336 | </packing> | ||
2486 | 337 | </child> | ||
2487 | 338 | <child> | ||
2488 | 339 | <object class="GtkLabel" id="lbl_profile"> | ||
2489 | 340 | <property name="visible">True</property> | ||
2490 | 341 | <property name="can_focus">False</property> | ||
2491 | 342 | <property name="xalign">1</property> | ||
2492 | 343 | <property name="label" translatable="yes">Profile Id:</property> | ||
2493 | 344 | </object> | ||
2494 | 345 | <packing> | ||
2495 | 346 | <property name="expand">True</property> | ||
2496 | 347 | <property name="fill">True</property> | ||
2497 | 348 | <property name="position">4</property> | ||
2498 | 349 | </packing> | ||
2499 | 350 | </child> | ||
2500 | 351 | <child> | ||
2501 | 352 | <object class="GtkEntry" id="txt_profile"> | ||
2502 | 353 | <property name="visible">True</property> | ||
2503 | 354 | <property name="can_focus">True</property> | ||
2504 | 355 | <property name="invisible_char">•</property> | ||
2505 | 356 | <property name="primary_icon_activatable">False</property> | ||
2506 | 357 | <property name="secondary_icon_activatable">False</property> | ||
2507 | 358 | <property name="primary_icon_sensitive">True</property> | ||
2508 | 359 | <property name="secondary_icon_sensitive">True</property> | ||
2509 | 360 | </object> | ||
2510 | 361 | <packing> | ||
2511 | 362 | <property name="expand">True</property> | ||
2512 | 363 | <property name="fill">True</property> | ||
2513 | 364 | <property name="position">5</property> | ||
2514 | 365 | </packing> | ||
2515 | 366 | </child> | ||
2516 | 367 | </object> | ||
2517 | 368 | <packing> | ||
2518 | 369 | <property name="expand">True</property> | ||
2519 | 370 | <property name="fill">True</property> | ||
2520 | 371 | <property name="position">1</property> | ||
2521 | 372 | </packing> | ||
2522 | 373 | </child> | ||
2523 | 374 | </object> | ||
2524 | 375 | </child> | ||
2525 | 376 | </object> | ||
2526 | 377 | </child> | ||
2527 | 378 | <child type="label"> | ||
2528 | 379 | <object class="GtkLabel" id="label7"> | ||
2529 | 380 | <property name="visible">True</property> | ||
2530 | 381 | <property name="can_focus">False</property> | ||
2531 | 382 | <property name="label" translatable="yes">Advanced</property> | ||
2532 | 383 | </object> | ||
2533 | 384 | </child> | ||
2534 | 385 | </object> | ||
2535 | 386 | <packing> | ||
2536 | 387 | <property name="expand">True</property> | ||
2537 | 388 | <property name="fill">True</property> | ||
2538 | 389 | <property name="position">1</property> | ||
2539 | 390 | </packing> | ||
2540 | 391 | </child> | ||
2541 | 392 | </object> | ||
2542 | 393 | </child> | ||
2543 | 394 | </object> | ||
2544 | 395 | </child> | ||
2545 | 396 | <child type="label"> | ||
2546 | 397 | <object class="GtkLabel" id="label4"> | ||
2547 | 398 | <property name="visible">True</property> | ||
2548 | 399 | <property name="can_focus">False</property> | ||
2549 | 400 | <property name="label" translatable="yes"><b>Where to save snapshots</b></property> | ||
2550 | 401 | <property name="use_markup">True</property> | ||
2561 | 402 | </object> | 770 | </object> |
2562 | 403 | </child> | 771 | </child> |
2563 | 404 | </object> | 772 | </object> |
2564 | 405 | <packing> | 773 | <packing> |
2565 | 406 | <property name="expand">False</property> | 774 | <property name="expand">False</property> |
2566 | 407 | <property name="fill">True</property> | 775 | <property name="fill">True</property> |
2568 | 408 | <property name="position">0</property> | 776 | <property name="position">900</property> |
2569 | 409 | </packing> | 777 | </packing> |
2570 | 410 | </child> | 778 | </child> |
2571 | 411 | <child> | 779 | <child> |
2573 | 412 | <object class="GtkFrame" id="frame2"> | 780 | <object class="GtkFrame" id="shedule"> |
2574 | 413 | <property name="visible">True</property> | 781 | <property name="visible">True</property> |
2575 | 414 | <property name="can_focus">False</property> | 782 | <property name="can_focus">False</property> |
2576 | 415 | <property name="label_xalign">0</property> | 783 | <property name="label_xalign">0</property> |
2577 | @@ -423,7 +791,7 @@ | |||
2578 | 423 | <object class="GtkTable" id="grid1"> | 791 | <object class="GtkTable" id="grid1"> |
2579 | 424 | <property name="visible">True</property> | 792 | <property name="visible">True</property> |
2580 | 425 | <property name="can_focus">False</property> | 793 | <property name="can_focus">False</property> |
2582 | 426 | <property name="n_rows">4</property> | 794 | <property name="n_rows">5</property> |
2583 | 427 | <property name="n_columns">2</property> | 795 | <property name="n_columns">2</property> |
2584 | 428 | <property name="column_spacing">5</property> | 796 | <property name="column_spacing">5</property> |
2585 | 429 | <property name="row_spacing">4</property> | 797 | <property name="row_spacing">4</property> |
2586 | @@ -438,6 +806,19 @@ | |||
2587 | 438 | </packing> | 806 | </packing> |
2588 | 439 | </child> | 807 | </child> |
2589 | 440 | <child> | 808 | <child> |
2590 | 809 | <object class="GtkLabel" id="lbl_backup_time_custom"> | ||
2591 | 810 | <property name="visible">True</property> | ||
2592 | 811 | <property name="can_focus">False</property> | ||
2593 | 812 | <property name="xalign">1</property> | ||
2594 | 813 | <property name="ypad">6</property> | ||
2595 | 814 | <property name="label" translatable="yes">Hours:</property> | ||
2596 | 815 | </object> | ||
2597 | 816 | <packing> | ||
2598 | 817 | <property name="top_attach">4</property> | ||
2599 | 818 | <property name="x_options">GTK_FILL</property> | ||
2600 | 819 | </packing> | ||
2601 | 820 | </child> | ||
2602 | 821 | <child> | ||
2603 | 441 | <object class="GtkLabel" id="lbl_backup_time"> | 822 | <object class="GtkLabel" id="lbl_backup_time"> |
2604 | 442 | <property name="visible">True</property> | 823 | <property name="visible">True</property> |
2605 | 443 | <property name="can_focus">False</property> | 824 | <property name="can_focus">False</property> |
2606 | @@ -477,6 +858,22 @@ | |||
2607 | 477 | </packing> | 858 | </packing> |
2608 | 478 | </child> | 859 | </child> |
2609 | 479 | <child> | 860 | <child> |
2610 | 861 | <object class="GtkEntry" id="txt_backup_time_custom"> | ||
2611 | 862 | <property name="visible">True</property> | ||
2612 | 863 | <property name="can_focus">True</property> | ||
2613 | 864 | <property name="tooltip_text" translatable="yes">hours seperate by comma ( e.g. 8,12,18,23) or */3 for periodic backups every 3 hours (cron style)</property> | ||
2614 | 865 | <property name="invisible_char">•</property> | ||
2615 | 866 | <property name="primary_icon_activatable">False</property> | ||
2616 | 867 | <property name="secondary_icon_activatable">False</property> | ||
2617 | 868 | <property name="primary_icon_sensitive">True</property> | ||
2618 | 869 | <property name="secondary_icon_sensitive">True</property> | ||
2619 | 870 | </object> | ||
2620 | 871 | <packing> | ||
2621 | 872 | <property name="left_attach">1</property> | ||
2622 | 873 | <property name="top_attach">4</property> | ||
2623 | 874 | </packing> | ||
2624 | 875 | </child> | ||
2625 | 876 | <child> | ||
2626 | 480 | <object class="GtkComboBox" id="cb_backup_time"> | 877 | <object class="GtkComboBox" id="cb_backup_time"> |
2627 | 481 | <property name="visible">True</property> | 878 | <property name="visible">True</property> |
2628 | 482 | <property name="can_focus">False</property> | 879 | <property name="can_focus">False</property> |
2629 | @@ -522,7 +919,7 @@ | |||
2630 | 522 | <packing> | 919 | <packing> |
2631 | 523 | <property name="expand">False</property> | 920 | <property name="expand">False</property> |
2632 | 524 | <property name="fill">True</property> | 921 | <property name="fill">True</property> |
2634 | 525 | <property name="position">1</property> | 922 | <property name="position">950</property> |
2635 | 526 | </packing> | 923 | </packing> |
2636 | 527 | </child> | 924 | </child> |
2637 | 528 | <child> | 925 | <child> |
2638 | @@ -549,7 +946,7 @@ | |||
2639 | 549 | <packing> | 946 | <packing> |
2640 | 550 | <property name="expand">True</property> | 947 | <property name="expand">True</property> |
2641 | 551 | <property name="fill">True</property> | 948 | <property name="fill">True</property> |
2643 | 552 | <property name="position">2</property> | 949 | <property name="position">1000</property> |
2644 | 553 | </packing> | 950 | </packing> |
2645 | 554 | </child> | 951 | </child> |
2646 | 555 | </object> | 952 | </object> |
2647 | 556 | 953 | ||
2648 | === modified file 'gnome/settingsdialog.py' | |||
2649 | --- gnome/settingsdialog.py 2012-02-20 11:18:31 +0000 | |||
2650 | +++ gnome/settingsdialog.py 2012-10-23 19:40:24 +0000 | |||
2651 | @@ -30,6 +30,7 @@ | |||
2652 | 30 | import config | 30 | import config |
2653 | 31 | import messagebox | 31 | import messagebox |
2654 | 32 | import tools | 32 | import tools |
2655 | 33 | import mount | ||
2656 | 33 | 34 | ||
2657 | 34 | 35 | ||
2658 | 35 | _=gettext.gettext | 36 | _=gettext.gettext |
2659 | @@ -74,7 +75,8 @@ | |||
2660 | 74 | 'on_combo_profiles_changed': self.on_combo_profiles_changed, | 75 | 'on_combo_profiles_changed': self.on_combo_profiles_changed, |
2661 | 75 | 'on_btn_where_clicked': self.on_btn_where_clicked, | 76 | 'on_btn_where_clicked': self.on_btn_where_clicked, |
2662 | 76 | 'on_cb_backup_mode_changed': self.on_cb_backup_mode_changed, | 77 | 'on_cb_backup_mode_changed': self.on_cb_backup_mode_changed, |
2664 | 77 | 'on_cb_auto_host_user_profile_toggled': self.update_host_user_profile | 78 | 'on_cb_auto_host_user_profile_toggled': self.update_host_user_profile, |
2665 | 79 | 'on_combo_modes_changed': self.on_combo_modes_changed | ||
2666 | 78 | } | 80 | } |
2667 | 79 | 81 | ||
2668 | 80 | builder.connect_signals(signals) | 82 | builder.connect_signals(signals) |
2669 | @@ -98,6 +100,26 @@ | |||
2670 | 98 | 100 | ||
2671 | 99 | self.disable_combo_changed = False | 101 | self.disable_combo_changed = False |
2672 | 100 | 102 | ||
2673 | 103 | #snapshots mode (local, ssh, ...) | ||
2674 | 104 | self.store_modes = gtk.ListStore(str, str) | ||
2675 | 105 | keys = self.config.SNAPSHOT_MODES.keys() | ||
2676 | 106 | keys.sort() | ||
2677 | 107 | for key in keys: | ||
2678 | 108 | self.store_modes.append([self.config.SNAPSHOT_MODES[key][1], key]) | ||
2679 | 109 | |||
2680 | 110 | self.combo_modes = get( 'combo_modes' ) | ||
2681 | 111 | self.combo_modes.set_model( self.store_modes ) | ||
2682 | 112 | |||
2683 | 113 | self.combo_modes.clear() | ||
2684 | 114 | text_renderer = gtk.CellRendererText() | ||
2685 | 115 | self.combo_modes.pack_start( text_renderer, True ) | ||
2686 | 116 | self.combo_modes.add_attribute( text_renderer, 'text', 0 ) | ||
2687 | 117 | |||
2688 | 118 | self.mode = None | ||
2689 | 119 | self.mode_local = get('mode_local') | ||
2690 | 120 | self.mode_ssh = get('mode_ssh') | ||
2691 | 121 | ## self.mode_dummy = get('mode_dummy') | ||
2692 | 122 | |||
2693 | 101 | #set current folder | 123 | #set current folder |
2694 | 102 | #self.fcb_where = get( 'fcb_where' ) | 124 | #self.fcb_where = get( 'fcb_where' ) |
2695 | 103 | #self.fcb_where.set_show_hidden( self.parent.show_hidden_files ) | 125 | #self.fcb_where.set_show_hidden( self.parent.show_hidden_files ) |
2696 | @@ -110,6 +132,31 @@ | |||
2697 | 110 | self.txt_user = get('txt_user') | 132 | self.txt_user = get('txt_user') |
2698 | 111 | self.lbl_profile = get('lbl_profile') | 133 | self.lbl_profile = get('lbl_profile') |
2699 | 112 | self.txt_profile = get('txt_profile') | 134 | self.txt_profile = get('txt_profile') |
2700 | 135 | |||
2701 | 136 | #ssh | ||
2702 | 137 | self.txt_ssh_host = get('txt_ssh_host') | ||
2703 | 138 | self.txt_ssh_port = get('txt_ssh_port') | ||
2704 | 139 | self.txt_ssh_user = get('txt_ssh_user') | ||
2705 | 140 | self.txt_ssh_path = get('txt_ssh_path') | ||
2706 | 141 | |||
2707 | 142 | self.store_ssh_cipher = gtk.ListStore(str, str) | ||
2708 | 143 | keys = self.config.SSH_CIPHERS.keys() | ||
2709 | 144 | keys.sort() | ||
2710 | 145 | for key in keys: | ||
2711 | 146 | self.store_ssh_cipher.append([self.config.SSH_CIPHERS[key], key]) | ||
2712 | 147 | |||
2713 | 148 | self.combo_ssh_cipher = get( 'combo_ssh_cipher' ) | ||
2714 | 149 | self.combo_ssh_cipher.set_model( self.store_ssh_cipher ) | ||
2715 | 150 | |||
2716 | 151 | self.combo_ssh_cipher.clear() | ||
2717 | 152 | text_renderer = gtk.CellRendererText() | ||
2718 | 153 | self.combo_ssh_cipher.pack_start( text_renderer, True ) | ||
2719 | 154 | self.combo_ssh_cipher.add_attribute( text_renderer, 'text', 0 ) | ||
2720 | 155 | |||
2721 | 156 | ## #dummy | ||
2722 | 157 | ## self.txt_dummy_host = get('txt_dummy_host') | ||
2723 | 158 | ## self.txt_dummy_port = get('txt_dummy_port') | ||
2724 | 159 | ## self.txt_dummy_user = get('txt_dummy_user') | ||
2725 | 113 | 160 | ||
2726 | 114 | #automatic backup mode store | 161 | #automatic backup mode store |
2727 | 115 | self.store_backup_mode = gtk.ListStore( str, int ) | 162 | self.store_backup_mode = gtk.ListStore( str, int ) |
2728 | @@ -136,6 +183,10 @@ | |||
2729 | 136 | for t in xrange( 1, 8 ): | 183 | for t in xrange( 1, 8 ): |
2730 | 137 | self.store_backup_weekday.append( [ datetime.date(2011, 11, 6 + t).strftime("%A"), t ] ) | 184 | self.store_backup_weekday.append( [ datetime.date(2011, 11, 6 + t).strftime("%A"), t ] ) |
2731 | 138 | 185 | ||
2732 | 186 | #custom backup time | ||
2733 | 187 | self.txt_backup_time_custom = get('txt_backup_time_custom') | ||
2734 | 188 | self.lbl_backup_time_custom = get('lbl_backup_time_custom') | ||
2735 | 189 | |||
2736 | 139 | #per directory schedule | 190 | #per directory schedule |
2737 | 140 | #self.cb_per_directory_schedule = get( 'cb_per_directory_schedule' ) | 191 | #self.cb_per_directory_schedule = get( 'cb_per_directory_schedule' ) |
2738 | 141 | #self.lbl_schedule = get( 'lbl_schedule' ) | 192 | #self.lbl_schedule = get( 'lbl_schedule' ) |
2739 | @@ -184,7 +235,17 @@ | |||
2740 | 184 | self.store_exclude = gtk.ListStore( str, str ) | 235 | self.store_exclude = gtk.ListStore( str, str ) |
2741 | 185 | self.list_exclude.set_model( self.store_exclude ) | 236 | self.list_exclude.set_model( self.store_exclude ) |
2742 | 186 | 237 | ||
2744 | 187 | get( 'lbl_highly_recommended_excluded' ).set_text( ', '.join(self.config.DEFAULT_EXCLUDE) ) | 238 | exclude = '' |
2745 | 239 | i = 1 | ||
2746 | 240 | prev_lines = 0 | ||
2747 | 241 | for ex in self.config.DEFAULT_EXCLUDE: | ||
2748 | 242 | exclude += ex | ||
2749 | 243 | if i < len(self.config.DEFAULT_EXCLUDE): | ||
2750 | 244 | exclude += ', ' | ||
2751 | 245 | if len(exclude)-prev_lines > 80: | ||
2752 | 246 | exclude += '\n' | ||
2753 | 247 | prev_lines += len(exclude) | ||
2754 | 248 | get( 'lbl_highly_recommended_excluded' ).set_text( exclude ) | ||
2755 | 188 | 249 | ||
2756 | 189 | #setup automatic backup mode | 250 | #setup automatic backup mode |
2757 | 190 | self.cb_backup_mode = get( 'cb_backup_mode' ) | 251 | self.cb_backup_mode = get( 'cb_backup_mode' ) |
2758 | @@ -228,6 +289,8 @@ | |||
2759 | 228 | 289 | ||
2760 | 229 | self.lbl_backup_weekday = get( 'lbl_backup_weekday' ) | 290 | self.lbl_backup_weekday = get( 'lbl_backup_weekday' ) |
2761 | 230 | 291 | ||
2762 | 292 | self.hbox_backup_time = get( 'hbox_backup_time' ) | ||
2763 | 293 | |||
2764 | 231 | #setup remove old backups older than | 294 | #setup remove old backups older than |
2765 | 232 | self.edit_remove_old_backup_value = get( 'edit_remove_old_backup_value' ) | 295 | self.edit_remove_old_backup_value = get( 'edit_remove_old_backup_value' ) |
2766 | 233 | self.cb_remove_old_backup_unit = get( 'cb_remove_old_backup_unit' ) | 296 | self.cb_remove_old_backup_unit = get( 'cb_remove_old_backup_unit' ) |
2767 | @@ -376,6 +439,15 @@ | |||
2768 | 376 | self.lbl_backup_day.hide() | 439 | self.lbl_backup_day.hide() |
2769 | 377 | self.cb_backup_day.hide() | 440 | self.cb_backup_day.hide() |
2770 | 378 | 441 | ||
2771 | 442 | if backup_mode == self.config.CUSTOM_HOUR: | ||
2772 | 443 | self.lbl_backup_time_custom.show() | ||
2773 | 444 | self.txt_backup_time_custom.show() | ||
2774 | 445 | self.txt_backup_time_custom.set_sensitive( True ) | ||
2775 | 446 | self.txt_backup_time_custom.set_text( self.config.get_custom_backup_time( self.profile_id ) ) | ||
2776 | 447 | else: | ||
2777 | 448 | self.lbl_backup_time_custom.hide() | ||
2778 | 449 | self.txt_backup_time_custom.hide() | ||
2779 | 450 | |||
2780 | 379 | def update_host_user_profile( self, *params ): | 451 | def update_host_user_profile( self, *params ): |
2781 | 380 | value = not self.cb_auto_host_user_profile.get_active() | 452 | value = not self.cb_auto_host_user_profile.get_active() |
2782 | 381 | self.lbl_host.set_sensitive( value ) | 453 | self.lbl_host.set_sensitive( value ) |
2783 | @@ -384,6 +456,20 @@ | |||
2784 | 384 | self.txt_user.set_sensitive( value ) | 456 | self.txt_user.set_sensitive( value ) |
2785 | 385 | self.lbl_profile.set_sensitive( value ) | 457 | self.lbl_profile.set_sensitive( value ) |
2786 | 386 | self.txt_profile.set_sensitive( value ) | 458 | self.txt_profile.set_sensitive( value ) |
2787 | 459 | |||
2788 | 460 | def on_combo_modes_changed(self, *params): | ||
2789 | 461 | iter = self.combo_modes.get_active_iter() | ||
2790 | 462 | if iter is None: | ||
2791 | 463 | return | ||
2792 | 464 | |||
2793 | 465 | active_mode = self.store_modes.get_value( iter, 1 ) | ||
2794 | 466 | if active_mode != self.mode: | ||
2795 | 467 | for mode in self.config.SNAPSHOT_MODES.keys(): | ||
2796 | 468 | if active_mode == mode: | ||
2797 | 469 | getattr(self, 'mode_%s' % mode).show() | ||
2798 | 470 | else: | ||
2799 | 471 | getattr(self, 'mode_%s' % mode).hide() | ||
2800 | 472 | self.mode = active_mode | ||
2801 | 387 | 473 | ||
2802 | 388 | def on_combo_profiles_changed( self, *params ): | 474 | def on_combo_profiles_changed( self, *params ): |
2803 | 389 | if self.disable_combo_changed: | 475 | if self.disable_combo_changed: |
2804 | @@ -425,10 +511,22 @@ | |||
2805 | 425 | else: | 511 | else: |
2806 | 426 | self.btn_edit_profile.set_sensitive( True ) | 512 | self.btn_edit_profile.set_sensitive( True ) |
2807 | 427 | self.btn_remove_profile.set_sensitive( True ) | 513 | self.btn_remove_profile.set_sensitive( True ) |
2808 | 514 | |||
2809 | 515 | #set mode | ||
2810 | 516 | i = 0 | ||
2811 | 517 | iter = self.store_modes.get_iter_first() | ||
2812 | 518 | default_mode = self.config.get_snapshots_mode(self.profile_id) | ||
2813 | 519 | while not iter is None: | ||
2814 | 520 | if self.store_modes.get_value( iter, 1 ) == default_mode: | ||
2815 | 521 | self.combo_modes.set_active( i ) | ||
2816 | 522 | break | ||
2817 | 523 | iter = self.store_modes.iter_next( iter ) | ||
2818 | 524 | i = i + 1 | ||
2819 | 525 | self.on_combo_modes_changed() | ||
2820 | 428 | 526 | ||
2821 | 429 | #set current folder | 527 | #set current folder |
2822 | 430 | #self.fcb_where.set_filename( self.config.get_snapshots_path() ) | 528 | #self.fcb_where.set_filename( self.config.get_snapshots_path() ) |
2824 | 431 | self.edit_where.set_text( self.config.get_snapshots_path( self.profile_id ) ) | 529 | self.edit_where.set_text( self.config.get_snapshots_path( self.profile_id, mode = 'local' ) ) |
2825 | 432 | self.cb_auto_host_user_profile.set_active( self.config.get_auto_host_user_profile( self.profile_id ) ) | 530 | self.cb_auto_host_user_profile.set_active( self.config.get_auto_host_user_profile( self.profile_id ) ) |
2826 | 433 | host, user, profile = self.config.get_host_user_profile( self.profile_id ) | 531 | host, user, profile = self.config.get_host_user_profile( self.profile_id ) |
2827 | 434 | self.txt_host.set_text( host ) | 532 | self.txt_host.set_text( host ) |
2828 | @@ -436,6 +534,27 @@ | |||
2829 | 436 | self.txt_profile.set_text( profile ) | 534 | self.txt_profile.set_text( profile ) |
2830 | 437 | self.update_host_user_profile() | 535 | self.update_host_user_profile() |
2831 | 438 | 536 | ||
2832 | 537 | #ssh | ||
2833 | 538 | self.txt_ssh_host.set_text( self.config.get_ssh_host( self.profile_id ) ) | ||
2834 | 539 | self.txt_ssh_port.set_text( str(self.config.get_ssh_port( self.profile_id )) ) | ||
2835 | 540 | self.txt_ssh_user.set_text( self.config.get_ssh_user( self.profile_id ) ) | ||
2836 | 541 | self.txt_ssh_path.set_text( self.config.get_snapshots_path_ssh( self.profile_id ) ) | ||
2837 | 542 | #set chipher | ||
2838 | 543 | i = 0 | ||
2839 | 544 | iter = self.store_ssh_cipher.get_iter_first() | ||
2840 | 545 | default_mode = self.config.get_ssh_cipher(self.profile_id) | ||
2841 | 546 | while not iter is None: | ||
2842 | 547 | if self.store_ssh_cipher.get_value( iter, 1 ) == default_mode: | ||
2843 | 548 | self.combo_ssh_cipher.set_active( i ) | ||
2844 | 549 | break | ||
2845 | 550 | iter = self.store_ssh_cipher.iter_next( iter ) | ||
2846 | 551 | i = i + 1 | ||
2847 | 552 | |||
2848 | 553 | ## #dummy | ||
2849 | 554 | ## self.txt_dummy_host.set_text( self.config.get_dummy_host( self.profile_id ) ) | ||
2850 | 555 | ## self.txt_dummy_port.set_text( str(self.config.get_dummy_port( self.profile_id )) ) | ||
2851 | 556 | ## self.txt_dummy_user.set_text( self.config.get_dummy_user( self.profile_id ) ) | ||
2852 | 557 | |||
2853 | 439 | #per directory schedule | 558 | #per directory schedule |
2854 | 440 | #self.cb_per_directory_schedule.set_active( self.config.get_per_directory_schedule() ) | 559 | #self.cb_per_directory_schedule.set_active( self.config.get_per_directory_schedule() ) |
2855 | 441 | 560 | ||
2856 | @@ -504,6 +623,9 @@ | |||
2857 | 504 | 623 | ||
2858 | 505 | self.on_cb_backup_mode_changed() | 624 | self.on_cb_backup_mode_changed() |
2859 | 506 | 625 | ||
2860 | 626 | #setup custom backup time | ||
2861 | 627 | self.txt_backup_time_custom.set_text( self.config.get_custom_backup_time( self.profile_id ) ) | ||
2862 | 628 | |||
2863 | 507 | #setup remove old backups older than | 629 | #setup remove old backups older than |
2864 | 508 | enabled, value, unit = self.config.get_remove_old_snapshots( self.profile_id ) | 630 | enabled, value, unit = self.config.get_remove_old_snapshots( self.profile_id ) |
2865 | 509 | 631 | ||
2866 | @@ -589,7 +711,12 @@ | |||
2867 | 589 | def save_profile( self ): | 711 | def save_profile( self ): |
2868 | 590 | #profile_id = self.config.get_current_profile() | 712 | #profile_id = self.config.get_current_profile() |
2869 | 591 | #snapshots path | 713 | #snapshots path |
2871 | 592 | snapshots_path = self.edit_where.get_text() | 714 | iter = self.combo_modes.get_active_iter() |
2872 | 715 | mode = self.store_modes.get_value( iter, 1 ) | ||
2873 | 716 | if self.config.SNAPSHOT_MODES[mode][0] is None: | ||
2874 | 717 | snapshots_path = self.edit_where.get_text() | ||
2875 | 718 | else: | ||
2876 | 719 | snapshots_path = self.config.get_snapshots_path(self.profile_id, mode = mode, tmp_mount = True) | ||
2877 | 593 | 720 | ||
2878 | 594 | #hack | 721 | #hack |
2879 | 595 | if snapshots_path.startswith( '//' ): | 722 | if snapshots_path.startswith( '//' ): |
2880 | @@ -611,6 +738,49 @@ | |||
2881 | 611 | while not iter is None: | 738 | while not iter is None: |
2882 | 612 | exclude_list.append( self.store_exclude.get_value( iter, 0 ) ) | 739 | exclude_list.append( self.store_exclude.get_value( iter, 0 ) ) |
2883 | 613 | iter = self.store_exclude.iter_next( iter ) | 740 | iter = self.store_exclude.iter_next( iter ) |
2884 | 741 | |||
2885 | 742 | if self.store_backup_mode.get_value( self.cb_backup_mode.get_active_iter(), 1 ) == self.config.CUSTOM_HOUR: | ||
2886 | 743 | if not tools.check_cron_pattern(self.txt_backup_time_custom.get_text()): | ||
2887 | 744 | self.error_handler( _('Custom Hours can only be a comma seperate list of hours (e.g. 8,12,18,23) or */3 for periodic backups every 3 hours') ) | ||
2888 | 745 | return False | ||
2889 | 746 | |||
2890 | 747 | mount_kwargs = {} | ||
2891 | 748 | |||
2892 | 749 | #ssh settings | ||
2893 | 750 | ssh_host = self.txt_ssh_host.get_text() | ||
2894 | 751 | ssh_port = self.txt_ssh_port.get_text() | ||
2895 | 752 | ssh_user = self.txt_ssh_user.get_text() | ||
2896 | 753 | ssh_path = self.txt_ssh_path.get_text() | ||
2897 | 754 | iter = self.combo_ssh_cipher.get_active_iter() | ||
2898 | 755 | ssh_cipher = self.store_ssh_cipher.get_value( iter, 1 ) | ||
2899 | 756 | if mode == 'ssh': | ||
2900 | 757 | mount_kwargs = { 'host': ssh_host, 'port': int(ssh_port), 'user': ssh_user, 'path': ssh_path, 'cipher': ssh_cipher } | ||
2901 | 758 | |||
2902 | 759 | ## #dummy settings | ||
2903 | 760 | ## dummy_host = self.txt_dummy_host.get_text() | ||
2904 | 761 | ## dummy_port = self.txt_dummy_port.get_text() | ||
2905 | 762 | ## dummy_user = self.txt_dummy_user.get_text() | ||
2906 | 763 | ## if mode == 'dummy': | ||
2907 | 764 | ## #values must have exactly the same Type (str, int or bool) | ||
2908 | 765 | ## #as they are set in config or you will run into false-positive | ||
2909 | 766 | ## #HashCollision warnings | ||
2910 | 767 | ## mount_kwargs = { 'host': dummy_host, 'port': int(dummy_port), 'user': dummy_user } | ||
2911 | 768 | |||
2912 | 769 | if not self.config.SNAPSHOT_MODES[mode][0] is None: | ||
2913 | 770 | #pre_mount_check | ||
2914 | 771 | mnt = mount.Mount(cfg = self.config, profile_id = self.profile_id, tmp_mount = True) | ||
2915 | 772 | try: | ||
2916 | 773 | mnt.pre_mount_check(mode = mode, first_run = True, **mount_kwargs) | ||
2917 | 774 | except mount.MountException as ex: | ||
2918 | 775 | self.error_handler(str(ex)) | ||
2919 | 776 | return False | ||
2920 | 777 | |||
2921 | 778 | #okay, lets try to mount | ||
2922 | 779 | try: | ||
2923 | 780 | hash_id = mnt.mount(mode = mode, check = False, **mount_kwargs) | ||
2924 | 781 | except mount.MountException as ex: | ||
2925 | 782 | self.error_handler(str(ex)) | ||
2926 | 783 | return False | ||
2927 | 614 | 784 | ||
2928 | 615 | #check if back folder changed | 785 | #check if back folder changed |
2929 | 616 | #if len( self.config.get_snapshots_path() ) > 0 and self.config.get_snapshots_path() != snapshots_path: | 786 | #if len( self.config.get_snapshots_path() ) > 0 and self.config.get_snapshots_path() != snapshots_path: |
2930 | @@ -620,7 +790,21 @@ | |||
2931 | 620 | #ok let's save to config | 790 | #ok let's save to config |
2932 | 621 | self.config.set_auto_host_user_profile( self.cb_auto_host_user_profile.get_active(), self.profile_id ) | 791 | self.config.set_auto_host_user_profile( self.cb_auto_host_user_profile.get_active(), self.profile_id ) |
2933 | 622 | self.config.set_host_user_profile( self.txt_host.get_text(), self.txt_user.get_text(), self.txt_profile.get_text(), self.profile_id ) | 792 | self.config.set_host_user_profile( self.txt_host.get_text(), self.txt_user.get_text(), self.txt_profile.get_text(), self.profile_id ) |
2935 | 623 | self.config.set_snapshots_path( snapshots_path, self.profile_id ) | 793 | self.config.set_snapshots_path( snapshots_path, self.profile_id , mode) |
2936 | 794 | |||
2937 | 795 | self.config.set_snapshots_mode(mode, self.profile_id) | ||
2938 | 796 | |||
2939 | 797 | #save ssh | ||
2940 | 798 | self.config.set_ssh_host(ssh_host, self.profile_id) | ||
2941 | 799 | self.config.set_ssh_port(ssh_port, self.profile_id) | ||
2942 | 800 | self.config.set_ssh_user(ssh_user, self.profile_id) | ||
2943 | 801 | self.config.set_snapshots_path_ssh(ssh_path, self.profile_id) | ||
2944 | 802 | self.config.set_ssh_cipher(ssh_cipher, self.profile_id) | ||
2945 | 803 | |||
2946 | 804 | ## #save dummy | ||
2947 | 805 | ## self.config.set_dummy_host(dummy_host, self.profile_id) | ||
2948 | 806 | ## self.config.set_dummy_port(dummy_port, self.profile_id) | ||
2949 | 807 | ## self.config.set_dummy_user(dummy_user, self.profile_id) | ||
2950 | 624 | 808 | ||
2951 | 625 | #if not msg is None: | 809 | #if not msg is None: |
2952 | 626 | # messagebox.show_error( self.dialog, self.config, msg ) | 810 | # messagebox.show_error( self.dialog, self.config, msg ) |
2953 | @@ -634,6 +818,7 @@ | |||
2954 | 634 | self.config.set_automatic_backup_time( self.store_backup_time.get_value( self.cb_backup_time.get_active_iter(), 1 ), self.profile_id ) | 818 | self.config.set_automatic_backup_time( self.store_backup_time.get_value( self.cb_backup_time.get_active_iter(), 1 ), self.profile_id ) |
2955 | 635 | self.config.set_automatic_backup_day( self.store_backup_day.get_value( self.cb_backup_day.get_active_iter(), 1 ), self.profile_id ) | 819 | self.config.set_automatic_backup_day( self.store_backup_day.get_value( self.cb_backup_day.get_active_iter(), 1 ), self.profile_id ) |
2956 | 636 | self.config.set_automatic_backup_weekday( self.store_backup_weekday.get_value( self.cb_backup_weekday.get_active_iter(), 1 ), self.profile_id ) | 820 | self.config.set_automatic_backup_weekday( self.store_backup_weekday.get_value( self.cb_backup_weekday.get_active_iter(), 1 ), self.profile_id ) |
2957 | 821 | self.config.set_custom_backup_time( self.txt_backup_time_custom.get_text(), self.profile_id ) | ||
2958 | 637 | 822 | ||
2959 | 638 | #auto-remove snapshots | 823 | #auto-remove snapshots |
2960 | 639 | self.config.set_remove_old_snapshots( | 824 | self.config.set_remove_old_snapshots( |
2961 | @@ -674,6 +859,15 @@ | |||
2962 | 674 | self.config.set_preserve_xattr( self.cb_preserve_xattr.get_active(), self.profile_id ) | 859 | self.config.set_preserve_xattr( self.cb_preserve_xattr.get_active(), self.profile_id ) |
2963 | 675 | self.config.set_copy_unsafe_links( self.cb_copy_unsafe_links.get_active(), self.profile_id ) | 860 | self.config.set_copy_unsafe_links( self.cb_copy_unsafe_links.get_active(), self.profile_id ) |
2964 | 676 | self.config.set_copy_links( self.cb_copy_links.get_active(), self.profile_id ) | 861 | self.config.set_copy_links( self.cb_copy_links.get_active(), self.profile_id ) |
2965 | 862 | |||
2966 | 863 | #umount | ||
2967 | 864 | if not self.config.SNAPSHOT_MODES[mode][0] is None: | ||
2968 | 865 | try: | ||
2969 | 866 | mnt.umount(hash_id = hash_id) | ||
2970 | 867 | except mount.MountException as ex: | ||
2971 | 868 | self.error_handler(str(ex)) | ||
2972 | 869 | return False | ||
2973 | 870 | return True | ||
2974 | 677 | 871 | ||
2975 | 678 | def update_remove_old_backups( self, button ): | 872 | def update_remove_old_backups( self, button ): |
2976 | 679 | enabled = self.cb_remove_old_backup.get_active() | 873 | enabled = self.cb_remove_old_backup.get_active() |
2977 | @@ -716,7 +910,7 @@ | |||
2978 | 716 | 910 | ||
2979 | 717 | self.config.clear_handlers() | 911 | self.config.clear_handlers() |
2980 | 718 | self.dialog.destroy() | 912 | self.dialog.destroy() |
2982 | 719 | 913 | ||
2983 | 720 | def update_snapshots_location( self ): | 914 | def update_snapshots_location( self ): |
2984 | 721 | '''Update snapshot location dialog''' | 915 | '''Update snapshot location dialog''' |
2985 | 722 | self.config.set_question_handler( self.question_handler ) | 916 | self.config.set_question_handler( self.question_handler ) |
2986 | @@ -852,7 +1046,8 @@ | |||
2987 | 852 | self.dialog.destroy() | 1046 | self.dialog.destroy() |
2988 | 853 | 1047 | ||
2989 | 854 | def validate( self ): | 1048 | def validate( self ): |
2991 | 855 | self.save_profile() | 1049 | if not self.save_profile(): |
2992 | 1050 | return False | ||
2993 | 856 | 1051 | ||
2994 | 857 | if not self.config.check_config(): | 1052 | if not self.config.check_config(): |
2995 | 858 | return False | 1053 | return False |
2996 | 859 | 1054 | ||
2997 | === modified file 'kde4/app.py' | |||
2998 | --- kde4/app.py 2012-03-05 16:04:13 +0000 | |||
2999 | +++ kde4/app.py 2012-10-23 19:40:24 +0000 | |||
3000 | @@ -37,6 +37,7 @@ | |||
3001 | 37 | import logger | 37 | import logger |
3002 | 38 | import snapshots | 38 | import snapshots |
3003 | 39 | import guiapplicationinstance | 39 | import guiapplicationinstance |
3004 | 40 | import mount | ||
3005 | 40 | 41 | ||
3006 | 41 | from PyQt4.QtGui import * | 42 | from PyQt4.QtGui import * |
3007 | 42 | from PyQt4.QtCore import * | 43 | from PyQt4.QtCore import * |
3008 | @@ -318,6 +319,17 @@ | |||
3009 | 318 | settingsdialog.SettingsDialog( self ).update_snapshots_location() | 319 | settingsdialog.SettingsDialog( self ).update_snapshots_location() |
3010 | 319 | 320 | ||
3011 | 320 | profile_id = cfg.get_current_profile() | 321 | profile_id = cfg.get_current_profile() |
3012 | 322 | |||
3013 | 323 | #mount | ||
3014 | 324 | try: | ||
3015 | 325 | mnt = mount.Mount(cfg = self.config, profile_id = profile_id) | ||
3016 | 326 | hash_id = mnt.mount() | ||
3017 | 327 | except mount.MountException as ex: | ||
3018 | 328 | KMessageBox.error( self, QString.fromUtf8( str(ex) ) ) | ||
3019 | 329 | sys.exit(1) | ||
3020 | 330 | else: | ||
3021 | 331 | self.config.set_current_hash_id(hash_id) | ||
3022 | 332 | |||
3023 | 321 | if not cfg.can_backup( profile_id ): | 333 | if not cfg.can_backup( profile_id ): |
3024 | 322 | KMessageBox.error( self, QString.fromUtf8( _('Can\'t find snapshots folder.\nIf it is on a removable drive please plug it and then press OK') ) ) | 334 | KMessageBox.error( self, QString.fromUtf8( _('Can\'t find snapshots folder.\nIf it is on a removable drive please plug it and then press OK') ) ) |
3025 | 323 | 335 | ||
3026 | @@ -378,6 +390,13 @@ | |||
3027 | 378 | self.config.set_int_value( 'kde4.main_window.files_view.sort.column', self.list_files_view.header().sortIndicatorSection() ) | 390 | self.config.set_int_value( 'kde4.main_window.files_view.sort.column', self.list_files_view.header().sortIndicatorSection() ) |
3028 | 379 | self.config.set_bool_value( 'kde4.main_window.files_view.sort.ascending', self.list_files_view.header().sortIndicatorOrder() == Qt.AscendingOrder ) | 391 | self.config.set_bool_value( 'kde4.main_window.files_view.sort.ascending', self.list_files_view.header().sortIndicatorOrder() == Qt.AscendingOrder ) |
3029 | 380 | 392 | ||
3030 | 393 | #umount | ||
3031 | 394 | try: | ||
3032 | 395 | mnt = mount.Mount(cfg = self.config) | ||
3033 | 396 | mnt.umount(self.config.current_hash_id) | ||
3034 | 397 | except mount.MountException as ex: | ||
3035 | 398 | KMessageBox.error( self, QString.fromUtf8( str(ex) ) ) | ||
3036 | 399 | |||
3037 | 381 | self.config.save() | 400 | self.config.save() |
3038 | 382 | 401 | ||
3039 | 383 | event.accept() | 402 | event.accept() |
3040 | @@ -419,8 +438,18 @@ | |||
3041 | 419 | return | 438 | return |
3042 | 420 | 439 | ||
3043 | 421 | if profile_id != self.config.get_current_profile(): | 440 | if profile_id != self.config.get_current_profile(): |
3044 | 441 | self.remount(profile_id, self.config.get_current_profile()) | ||
3045 | 422 | self.config.set_current_profile( profile_id ) | 442 | self.config.set_current_profile( profile_id ) |
3046 | 423 | self.update_profile() | 443 | self.update_profile() |
3047 | 444 | |||
3048 | 445 | def remount( self, new_profile_id, old_profile_id): | ||
3049 | 446 | try: | ||
3050 | 447 | mnt = mount.Mount(cfg = self.config, profile_id = old_profile_id) | ||
3051 | 448 | hash_id = mnt.remount(new_profile_id) | ||
3052 | 449 | except mount.MountException as ex: | ||
3053 | 450 | KMessageBox.error( self, QString.fromUtf8( str(ex) ) ) | ||
3054 | 451 | else: | ||
3055 | 452 | self.config.set_current_hash_id(hash_id) | ||
3056 | 424 | 453 | ||
3057 | 425 | def get_default_startup_folder_and_file( self ): | 454 | def get_default_startup_folder_and_file( self ): |
3058 | 426 | last_path = self.config.get_str_value( 'gnome.last_path', '' ) | 455 | last_path = self.config.get_str_value( 'gnome.last_path', '' ) |
3059 | @@ -779,6 +808,8 @@ | |||
3060 | 779 | 808 | ||
3061 | 780 | def on_btn_settings_clicked( self ): | 809 | def on_btn_settings_clicked( self ): |
3062 | 781 | if QDialog.Accepted == settingsdialog.SettingsDialog( self ).exec_(): | 810 | if QDialog.Accepted == settingsdialog.SettingsDialog( self ).exec_(): |
3063 | 811 | profile_id = self.config.get_current_profile() | ||
3064 | 812 | self.remount(profile_id, profile_id) | ||
3065 | 782 | self.update_profiles() | 813 | self.update_profiles() |
3066 | 783 | 814 | ||
3067 | 784 | def on_btn_about_clicked( self ): | 815 | def on_btn_about_clicked( self ): |
3068 | 785 | 816 | ||
3069 | === modified file 'kde4/man/C/backintime-kde4.1.gz' | |||
3070 | 786 | Binary files kde4/man/C/backintime-kde4.1.gz 2012-03-06 21:23:31 +0000 and kde4/man/C/backintime-kde4.1.gz 2012-10-23 19:40:24 +0000 differ | 817 | Binary files kde4/man/C/backintime-kde4.1.gz 2012-03-06 21:23:31 +0000 and kde4/man/C/backintime-kde4.1.gz 2012-10-23 19:40:24 +0000 differ |
3071 | === modified file 'kde4/settingsdialog.py' | |||
3072 | --- kde4/settingsdialog.py 2012-02-20 11:30:09 +0000 | |||
3073 | +++ kde4/settingsdialog.py 2012-10-23 19:40:24 +0000 | |||
3074 | @@ -32,6 +32,7 @@ | |||
3075 | 32 | import config | 32 | import config |
3076 | 33 | import tools | 33 | import tools |
3077 | 34 | import kde4tools | 34 | import kde4tools |
3078 | 35 | import mount | ||
3079 | 35 | 36 | ||
3080 | 36 | 37 | ||
3081 | 37 | _=gettext.gettext | 38 | _=gettext.gettext |
3082 | @@ -102,9 +103,25 @@ | |||
3083 | 102 | tab_widget = QWidget( self ) | 103 | tab_widget = QWidget( self ) |
3084 | 103 | self.tabs_widget.addTab( tab_widget, QString.fromUtf8( _( 'General' ) ) ) | 104 | self.tabs_widget.addTab( tab_widget, QString.fromUtf8( _( 'General' ) ) ) |
3085 | 104 | layout = QVBoxLayout( tab_widget ) | 105 | layout = QVBoxLayout( tab_widget ) |
3087 | 105 | 106 | ||
3088 | 107 | #select mode | ||
3089 | 108 | self.mode = None | ||
3090 | 109 | vlayout = QVBoxLayout() | ||
3091 | 110 | layout.addLayout( vlayout ) | ||
3092 | 111 | |||
3093 | 112 | self.lbl_modes = QLabel( QString.fromUtf8( _( 'Mode:' ) ), self ) | ||
3094 | 113 | vlayout.addWidget( self.lbl_modes ) | ||
3095 | 114 | |||
3096 | 115 | self.combo_modes = KComboBox( self ) | ||
3097 | 116 | vlayout.addWidget( self.combo_modes ) | ||
3098 | 117 | store_modes = {} | ||
3099 | 118 | for key in self.config.SNAPSHOT_MODES.keys(): | ||
3100 | 119 | store_modes[key] = self.config.SNAPSHOT_MODES[key][1] | ||
3101 | 120 | self.fill_combo( self.combo_modes, store_modes ) | ||
3102 | 121 | |||
3103 | 106 | #Where to save snapshots | 122 | #Where to save snapshots |
3104 | 107 | group_box = QGroupBox( self ) | 123 | group_box = QGroupBox( self ) |
3105 | 124 | self.mode_local = group_box | ||
3106 | 108 | group_box.setTitle( QString.fromUtf8( _( 'Where to save snapshots' ) ) ) | 125 | group_box.setTitle( QString.fromUtf8( _( 'Where to save snapshots' ) ) ) |
3107 | 109 | layout.addWidget( group_box ) | 126 | layout.addWidget( group_box ) |
3108 | 110 | 127 | ||
3109 | @@ -120,7 +137,75 @@ | |||
3110 | 120 | self.btn_snapshots_path = KPushButton( KIcon( 'folder' ), '', self ) | 137 | self.btn_snapshots_path = KPushButton( KIcon( 'folder' ), '', self ) |
3111 | 121 | hlayout.addWidget( self.btn_snapshots_path ) | 138 | hlayout.addWidget( self.btn_snapshots_path ) |
3112 | 122 | QObject.connect( self.btn_snapshots_path, SIGNAL('clicked()'), self.on_btn_snapshots_path_clicked ) | 139 | QObject.connect( self.btn_snapshots_path, SIGNAL('clicked()'), self.on_btn_snapshots_path_clicked ) |
3114 | 123 | 140 | ||
3115 | 141 | #SSH | ||
3116 | 142 | group_box = QGroupBox( self ) | ||
3117 | 143 | self.mode_ssh = group_box | ||
3118 | 144 | group_box.setTitle( QString.fromUtf8( _( 'SSH Settings' ) ) ) | ||
3119 | 145 | layout.addWidget( group_box ) | ||
3120 | 146 | |||
3121 | 147 | vlayout = QVBoxLayout( group_box ) | ||
3122 | 148 | |||
3123 | 149 | hlayout1 = QHBoxLayout() | ||
3124 | 150 | vlayout.addLayout( hlayout1 ) | ||
3125 | 151 | hlayout2 = QHBoxLayout() | ||
3126 | 152 | vlayout.addLayout( hlayout2 ) | ||
3127 | 153 | |||
3128 | 154 | self.lbl_ssh_host = QLabel( QString.fromUtf8( _( 'Host:' ) ), self ) | ||
3129 | 155 | hlayout1.addWidget( self.lbl_ssh_host ) | ||
3130 | 156 | self.txt_ssh_host = KLineEdit( self ) | ||
3131 | 157 | hlayout1.addWidget( self.txt_ssh_host ) | ||
3132 | 158 | |||
3133 | 159 | self.lbl_ssh_port = QLabel( QString.fromUtf8( _( 'Port:' ) ), self ) | ||
3134 | 160 | hlayout1.addWidget( self.lbl_ssh_port ) | ||
3135 | 161 | self.txt_ssh_port = KLineEdit( self ) | ||
3136 | 162 | hlayout1.addWidget( self.txt_ssh_port ) | ||
3137 | 163 | |||
3138 | 164 | self.lbl_ssh_user = QLabel( QString.fromUtf8( _( 'User:' ) ), self ) | ||
3139 | 165 | hlayout1.addWidget( self.lbl_ssh_user ) | ||
3140 | 166 | self.txt_ssh_user = KLineEdit( self ) | ||
3141 | 167 | hlayout1.addWidget( self.txt_ssh_user ) | ||
3142 | 168 | |||
3143 | 169 | self.lbl_ssh_path = QLabel( QString.fromUtf8( _( 'Path:' ) ), self ) | ||
3144 | 170 | hlayout2.addWidget( self.lbl_ssh_path ) | ||
3145 | 171 | self.txt_ssh_path = KLineEdit( self ) | ||
3146 | 172 | hlayout2.addWidget( self.txt_ssh_path ) | ||
3147 | 173 | |||
3148 | 174 | self.lbl_ssh_cipher = QLabel( QString.fromUtf8( _( 'Cipher:' ) ), self ) | ||
3149 | 175 | hlayout2.addWidget( self.lbl_ssh_cipher ) | ||
3150 | 176 | self.combo_ssh_cipher = KComboBox( self ) | ||
3151 | 177 | hlayout2.addWidget( self.combo_ssh_cipher ) | ||
3152 | 178 | self.fill_combo( self.combo_ssh_cipher, self.config.SSH_CIPHERS ) | ||
3153 | 179 | |||
3154 | 180 | ## #Dummy | ||
3155 | 181 | ## group_box = QGroupBox( self ) | ||
3156 | 182 | ## self.mode_dummy = group_box | ||
3157 | 183 | ## group_box.setTitle( QString.fromUtf8( _( 'Dummy Settings' ) ) ) | ||
3158 | 184 | ## layout.addWidget( group_box ) | ||
3159 | 185 | ## | ||
3160 | 186 | ## vlayout = QVBoxLayout( group_box ) | ||
3161 | 187 | ## | ||
3162 | 188 | ## hlayout = QHBoxLayout() | ||
3163 | 189 | ## vlayout.addLayout( hlayout ) | ||
3164 | 190 | ## | ||
3165 | 191 | ## self.lbl_dummy_host = QLabel( QString.fromUtf8( _( 'Host:' ) ), self ) | ||
3166 | 192 | ## hlayout.addWidget( self.lbl_dummy_host ) | ||
3167 | 193 | ## self.txt_dummy_host = KLineEdit( self ) | ||
3168 | 194 | ## hlayout.addWidget( self.txt_dummy_host ) | ||
3169 | 195 | ## | ||
3170 | 196 | ## self.lbl_dummy_port = QLabel( QString.fromUtf8( _( 'Port:' ) ), self ) | ||
3171 | 197 | ## hlayout.addWidget( self.lbl_dummy_port ) | ||
3172 | 198 | ## self.txt_dummy_port = KLineEdit( self ) | ||
3173 | 199 | ## hlayout.addWidget( self.txt_dummy_port ) | ||
3174 | 200 | ## | ||
3175 | 201 | ## self.lbl_dummy_user = QLabel( QString.fromUtf8( _( 'User:' ) ), self ) | ||
3176 | 202 | ## hlayout.addWidget( self.lbl_dummy_user ) | ||
3177 | 203 | ## self.txt_dummy_user = KLineEdit( self ) | ||
3178 | 204 | ## hlayout.addWidget( self.txt_dummy_user ) | ||
3179 | 205 | |||
3180 | 206 | QObject.connect( self.combo_modes, SIGNAL('currentIndexChanged(int)'), self.on_combo_modes_changed ) | ||
3181 | 207 | self.on_combo_modes_changed() | ||
3182 | 208 | |||
3183 | 124 | #host, user, profile id | 209 | #host, user, profile id |
3184 | 125 | hlayout = QHBoxLayout() | 210 | hlayout = QHBoxLayout() |
3185 | 126 | layout.addLayout( hlayout ) | 211 | layout.addLayout( hlayout ) |
3186 | @@ -200,6 +285,14 @@ | |||
3187 | 200 | for t in xrange( 0, 2300, 100 ): | 285 | for t in xrange( 0, 2300, 100 ): |
3188 | 201 | self.combo_automatic_snapshots_time.addItem( QIcon(), QString.fromUtf8( datetime.time( t/100, t%100 ).strftime("%H:%M") ), QVariant( t ) ) | 286 | self.combo_automatic_snapshots_time.addItem( QIcon(), QString.fromUtf8( datetime.time( t/100, t%100 ).strftime("%H:%M") ), QVariant( t ) ) |
3189 | 202 | 287 | ||
3190 | 288 | self.lbl_automatic_snapshots_time_custom = QLabel( QString.fromUtf8( _( 'Hours:' ) ), self ) | ||
3191 | 289 | self.lbl_automatic_snapshots_time_custom.setContentsMargins( 5, 0, 0, 0 ) | ||
3192 | 290 | self.lbl_automatic_snapshots_time_custom.setAlignment( Qt.AlignRight | Qt.AlignVCenter ) | ||
3193 | 291 | glayout.addWidget( self.lbl_automatic_snapshots_time_custom, 4, 0 ) | ||
3194 | 292 | |||
3195 | 293 | self.txt_automatic_snapshots_time_custom = KLineEdit( self ) | ||
3196 | 294 | glayout.addWidget( self.txt_automatic_snapshots_time_custom, 4, 1 ) | ||
3197 | 295 | |||
3198 | 203 | QObject.connect( self.combo_automatic_snapshots, SIGNAL('currentIndexChanged(int)'), self.current_automatic_snapshot_changed ) | 296 | QObject.connect( self.combo_automatic_snapshots, SIGNAL('currentIndexChanged(int)'), self.current_automatic_snapshot_changed ) |
3199 | 204 | 297 | ||
3200 | 205 | # | 298 | # |
3201 | @@ -470,6 +563,13 @@ | |||
3202 | 470 | self.update_profiles() | 563 | self.update_profiles() |
3203 | 471 | 564 | ||
3204 | 472 | def update_automatic_snapshot_time( self, backup_mode ): | 565 | def update_automatic_snapshot_time( self, backup_mode ): |
3205 | 566 | if backup_mode == self.config.CUSTOM_HOUR: | ||
3206 | 567 | self.lbl_automatic_snapshots_time_custom.show() | ||
3207 | 568 | self.txt_automatic_snapshots_time_custom.show() | ||
3208 | 569 | else: | ||
3209 | 570 | self.lbl_automatic_snapshots_time_custom.hide() | ||
3210 | 571 | self.txt_automatic_snapshots_time_custom.hide() | ||
3211 | 572 | |||
3212 | 473 | if backup_mode == self.config.WEEK: | 573 | if backup_mode == self.config.WEEK: |
3213 | 474 | self.lbl_automatic_snapshots_weekday.show() | 574 | self.lbl_automatic_snapshots_weekday.show() |
3214 | 475 | self.combo_automatic_snapshots_weekday.show() | 575 | self.combo_automatic_snapshots_weekday.show() |
3215 | @@ -542,7 +642,23 @@ | |||
3216 | 542 | self.btn_remove_profile.setEnabled( True ) | 642 | self.btn_remove_profile.setEnabled( True ) |
3217 | 543 | 643 | ||
3218 | 544 | #TAB: General | 644 | #TAB: General |
3220 | 545 | self.edit_snapshots_path.setText( QString.fromUtf8( self.config.get_snapshots_path() ) ) | 645 | #mode |
3221 | 646 | self.set_combo_value( self.combo_modes, self.config.get_snapshots_mode(), type = 'str' ) | ||
3222 | 647 | |||
3223 | 648 | #local | ||
3224 | 649 | self.edit_snapshots_path.setText( QString.fromUtf8( self.config.get_snapshots_path( mode = 'local') ) ) | ||
3225 | 650 | |||
3226 | 651 | #ssh | ||
3227 | 652 | self.txt_ssh_host.setText( QString.fromUtf8( self.config.get_ssh_host() ) ) | ||
3228 | 653 | self.txt_ssh_port.setText( QString.fromUtf8( str(self.config.get_ssh_port()) ) ) | ||
3229 | 654 | self.txt_ssh_user.setText( QString.fromUtf8( self.config.get_ssh_user() ) ) | ||
3230 | 655 | self.txt_ssh_path.setText( QString.fromUtf8( self.config.get_snapshots_path_ssh() ) ) | ||
3231 | 656 | self.set_combo_value( self.combo_ssh_cipher, self.config.get_ssh_cipher(), type = 'str' ) | ||
3232 | 657 | |||
3233 | 658 | ## #dummy | ||
3234 | 659 | ## self.txt_dummy_host.setText( QString.fromUtf8( self.config.get_dummy_host() ) ) | ||
3235 | 660 | ## self.txt_dummy_port.setText( QString.fromUtf8( self.config.get_dummy_port() ) ) | ||
3236 | 661 | ## self.txt_dummy_user.setText( QString.fromUtf8( self.config.get_dummy_user() ) ) | ||
3237 | 546 | 662 | ||
3238 | 547 | self.cb_auto_host_user_profile.setChecked( self.config.get_auto_host_user_profile() ) | 663 | self.cb_auto_host_user_profile.setChecked( self.config.get_auto_host_user_profile() ) |
3239 | 548 | host, user, profile = self.config.get_host_user_profile() | 664 | host, user, profile = self.config.get_host_user_profile() |
3240 | @@ -555,6 +671,7 @@ | |||
3241 | 555 | self.set_combo_value( self.combo_automatic_snapshots_time, self.config.get_automatic_backup_time() ) | 671 | self.set_combo_value( self.combo_automatic_snapshots_time, self.config.get_automatic_backup_time() ) |
3242 | 556 | self.set_combo_value( self.combo_automatic_snapshots_day, self.config.get_automatic_backup_day() ) | 672 | self.set_combo_value( self.combo_automatic_snapshots_day, self.config.get_automatic_backup_day() ) |
3243 | 557 | self.set_combo_value( self.combo_automatic_snapshots_weekday, self.config.get_automatic_backup_weekday() ) | 673 | self.set_combo_value( self.combo_automatic_snapshots_weekday, self.config.get_automatic_backup_weekday() ) |
3244 | 674 | self.txt_automatic_snapshots_time_custom.setText( self.config.get_custom_backup_time() ) | ||
3245 | 558 | self.update_automatic_snapshot_time( self.config.get_automatic_backup_mode() ) | 675 | self.update_automatic_snapshot_time( self.config.get_automatic_backup_mode() ) |
3246 | 559 | 676 | ||
3247 | 560 | #TAB: Include | 677 | #TAB: Include |
3248 | @@ -619,13 +736,76 @@ | |||
3249 | 619 | self.update_min_free_space() | 736 | self.update_min_free_space() |
3250 | 620 | 737 | ||
3251 | 621 | def save_profile( self ): | 738 | def save_profile( self ): |
3252 | 739 | if self.combo_automatic_snapshots.itemData( self.combo_automatic_snapshots.currentIndex() ).toInt()[0] == self.config.CUSTOM_HOUR: | ||
3253 | 740 | if not tools.check_cron_pattern(str( self.txt_automatic_snapshots_time_custom.text().toUtf8() ) ): | ||
3254 | 741 | self.error_handler( _('Custom Hours can only be a comma seperate list of hours (e.g. 8,12,18,23) or */3 for periodic backups every 3 hours') ) | ||
3255 | 742 | return False | ||
3256 | 743 | |||
3257 | 744 | #mode | ||
3258 | 745 | mode = str( self.combo_modes.itemData( self.combo_modes.currentIndex() ).toString().toUtf8() ) | ||
3259 | 746 | self.config.set_snapshots_mode( mode ) | ||
3260 | 747 | mount_kwargs = {} | ||
3261 | 748 | |||
3262 | 749 | #ssh | ||
3263 | 750 | ssh_host = str( self.txt_ssh_host.text().toUtf8() ) | ||
3264 | 751 | ssh_port = str( self.txt_ssh_port.text().toUtf8() ) | ||
3265 | 752 | ssh_user = str( self.txt_ssh_user.text().toUtf8() ) | ||
3266 | 753 | ssh_path = str( self.txt_ssh_path.text().toUtf8() ) | ||
3267 | 754 | ssh_cipher = str( self.combo_ssh_cipher.itemData( self.combo_ssh_cipher.currentIndex() ).toString().toUtf8() ) | ||
3268 | 755 | if mode == 'ssh': | ||
3269 | 756 | mount_kwargs = { 'host': ssh_host, 'port': int(ssh_port), 'user': ssh_user, 'path': ssh_path, 'cipher': ssh_cipher } | ||
3270 | 757 | |||
3271 | 758 | ## #dummy | ||
3272 | 759 | ## dummy_host = str( self.txt_dummy_host.text().toUtf8() ) | ||
3273 | 760 | ## dummy_port = str( self.txt_dummy_port.text().toUtf8() ) | ||
3274 | 761 | ## dummy_user = str( self.txt_dummy_user.text().toUtf8() ) | ||
3275 | 762 | ## if mode == 'dummy': | ||
3276 | 763 | ## #values must have exactly the same Type (str, int or bool) | ||
3277 | 764 | ## #as they are set in config or you will run into false-positive | ||
3278 | 765 | ## #HashCollision warnings | ||
3279 | 766 | ## mount_kwargs = { 'host': dummy_host, 'port': int(dummy_port), 'user': dummy_user } | ||
3280 | 767 | |||
3281 | 768 | if not self.config.SNAPSHOT_MODES[mode][0] is None: | ||
3282 | 769 | #pre_mount_check | ||
3283 | 770 | mnt = mount.Mount(cfg = self.config, tmp_mount = True) | ||
3284 | 771 | try: | ||
3285 | 772 | mnt.pre_mount_check(mode = mode, first_run = True, **mount_kwargs) | ||
3286 | 773 | except mount.MountException as ex: | ||
3287 | 774 | self.error_handler(str(ex)) | ||
3288 | 775 | return False | ||
3289 | 776 | |||
3290 | 777 | #okay, lets try to mount | ||
3291 | 778 | try: | ||
3292 | 779 | hash_id = mnt.mount(mode = mode, check = False, **mount_kwargs) | ||
3293 | 780 | except mount.MountException as ex: | ||
3294 | 781 | self.error_handler(str(ex)) | ||
3295 | 782 | return False | ||
3296 | 783 | |||
3297 | 622 | #snapshots path | 784 | #snapshots path |
3298 | 623 | self.config.set_auto_host_user_profile( self.cb_auto_host_user_profile.isChecked() ) | 785 | self.config.set_auto_host_user_profile( self.cb_auto_host_user_profile.isChecked() ) |
3299 | 624 | self.config.set_host_user_profile( | 786 | self.config.set_host_user_profile( |
3300 | 625 | str( self.txt_host.text().toUtf8() ), | 787 | str( self.txt_host.text().toUtf8() ), |
3301 | 626 | str( self.txt_user.text().toUtf8() ), | 788 | str( self.txt_user.text().toUtf8() ), |
3302 | 627 | str( self.txt_profile.text().toUtf8() ) ) | 789 | str( self.txt_profile.text().toUtf8() ) ) |
3304 | 628 | self.config.set_snapshots_path( str( self.edit_snapshots_path.text().toUtf8() ) ) | 790 | |
3305 | 791 | if self.config.SNAPSHOT_MODES[mode][0] is None: | ||
3306 | 792 | snapshots_path = str( self.edit_snapshots_path.text().toUtf8() ) | ||
3307 | 793 | else: | ||
3308 | 794 | snapshots_path = self.config.get_snapshots_path(mode = mode, tmp_mount = True) | ||
3309 | 795 | |||
3310 | 796 | self.config.set_snapshots_path( snapshots_path, mode = mode ) | ||
3311 | 797 | |||
3312 | 798 | #save ssh | ||
3313 | 799 | self.config.set_ssh_host(ssh_host) | ||
3314 | 800 | self.config.set_ssh_port(ssh_port) | ||
3315 | 801 | self.config.set_ssh_user(ssh_user) | ||
3316 | 802 | self.config.set_snapshots_path_ssh(ssh_path) | ||
3317 | 803 | self.config.set_ssh_cipher(ssh_cipher) | ||
3318 | 804 | |||
3319 | 805 | ## #save dummy | ||
3320 | 806 | ## self.config.set_dummy_host(dummy_host) | ||
3321 | 807 | ## self.config.set_dummy_port(dummy_port) | ||
3322 | 808 | ## self.config.set_dummy_user(dummy_user) | ||
3323 | 629 | 809 | ||
3324 | 630 | #include list | 810 | #include list |
3325 | 631 | include_list = [] | 811 | include_list = [] |
3326 | @@ -648,6 +828,7 @@ | |||
3327 | 648 | self.config.set_automatic_backup_time( self.combo_automatic_snapshots_time.itemData( self.combo_automatic_snapshots_time.currentIndex() ).toInt()[0] ) | 828 | self.config.set_automatic_backup_time( self.combo_automatic_snapshots_time.itemData( self.combo_automatic_snapshots_time.currentIndex() ).toInt()[0] ) |
3328 | 649 | self.config.set_automatic_backup_weekday( self.combo_automatic_snapshots_weekday.itemData( self.combo_automatic_snapshots_weekday.currentIndex() ).toInt()[0] ) | 829 | self.config.set_automatic_backup_weekday( self.combo_automatic_snapshots_weekday.itemData( self.combo_automatic_snapshots_weekday.currentIndex() ).toInt()[0] ) |
3329 | 650 | self.config.set_automatic_backup_day( self.combo_automatic_snapshots_day.itemData( self.combo_automatic_snapshots_day.currentIndex() ).toInt()[0] ) | 830 | self.config.set_automatic_backup_day( self.combo_automatic_snapshots_day.itemData( self.combo_automatic_snapshots_day.currentIndex() ).toInt()[0] ) |
3330 | 831 | self.config.set_custom_backup_time( str( self.txt_automatic_snapshots_time_custom.text().toUtf8() ) ) | ||
3331 | 651 | 832 | ||
3332 | 652 | #auto-remove | 833 | #auto-remove |
3333 | 653 | self.config.set_remove_old_snapshots( | 834 | self.config.set_remove_old_snapshots( |
3334 | @@ -684,6 +865,15 @@ | |||
3335 | 684 | self.config.set_preserve_xattr( self.cb_preserve_xattr.isChecked() ) | 865 | self.config.set_preserve_xattr( self.cb_preserve_xattr.isChecked() ) |
3336 | 685 | self.config.set_copy_unsafe_links( self.cb_copy_unsafe_links.isChecked() ) | 866 | self.config.set_copy_unsafe_links( self.cb_copy_unsafe_links.isChecked() ) |
3337 | 686 | self.config.set_copy_links( self.cb_copy_links.isChecked() ) | 867 | self.config.set_copy_links( self.cb_copy_links.isChecked() ) |
3338 | 868 | |||
3339 | 869 | #umount | ||
3340 | 870 | if not self.config.SNAPSHOT_MODES[mode][0] is None: | ||
3341 | 871 | try: | ||
3342 | 872 | mnt.umount(hash_id = hash_id) | ||
3343 | 873 | except mount.MountException as ex: | ||
3344 | 874 | self.error_handler(str(ex)) | ||
3345 | 875 | return False | ||
3346 | 876 | return True | ||
3347 | 687 | 877 | ||
3348 | 688 | def error_handler( self, message ): | 878 | def error_handler( self, message ): |
3349 | 689 | KMessageBox.error( self, QString.fromUtf8( message ) ) | 879 | KMessageBox.error( self, QString.fromUtf8( message ) ) |
3350 | @@ -777,14 +967,18 @@ | |||
3351 | 777 | for key in keys: | 967 | for key in keys: |
3352 | 778 | combo.addItem( QIcon(), QString.fromUtf8( dict[ key ] ), QVariant( key ) ) | 968 | combo.addItem( QIcon(), QString.fromUtf8( dict[ key ] ), QVariant( key ) ) |
3353 | 779 | 969 | ||
3355 | 780 | def set_combo_value( self, combo, value ): | 970 | def set_combo_value( self, combo, value, type = 'int' ): |
3356 | 781 | for i in xrange( combo.count() ): | 971 | for i in xrange( combo.count() ): |
3358 | 782 | if value == combo.itemData( i ).toInt()[0]: | 972 | if type == 'int' and value == combo.itemData( i ).toInt()[0]: |
3359 | 973 | combo.setCurrentIndex( i ) | ||
3360 | 974 | break | ||
3361 | 975 | if type == 'str' and value == combo.itemData( i ).toString(): | ||
3362 | 783 | combo.setCurrentIndex( i ) | 976 | combo.setCurrentIndex( i ) |
3363 | 784 | break | 977 | break |
3364 | 785 | 978 | ||
3365 | 786 | def validate( self ): | 979 | def validate( self ): |
3367 | 787 | self.save_profile() | 980 | if not self.save_profile(): |
3368 | 981 | return False | ||
3369 | 788 | 982 | ||
3370 | 789 | if not self.config.check_config(): | 983 | if not self.config.check_config(): |
3371 | 790 | return False | 984 | return False |
3372 | @@ -886,7 +1080,21 @@ | |||
3373 | 886 | if not self.question_handler( _('Are you sure you want to change snapshots folder ?') ): | 1080 | if not self.question_handler( _('Are you sure you want to change snapshots folder ?') ): |
3374 | 887 | return | 1081 | return |
3375 | 888 | self.edit_snapshots_path.setText( QString.fromUtf8( self.config.prepare_path( path ) ) ) | 1082 | self.edit_snapshots_path.setText( QString.fromUtf8( self.config.prepare_path( path ) ) ) |
3377 | 889 | 1083 | ||
3378 | 1084 | def on_combo_modes_changed(self, *params): | ||
3379 | 1085 | if len(params) == 0: | ||
3380 | 1086 | index = self.combo_modes.currentIndex() | ||
3381 | 1087 | else: | ||
3382 | 1088 | index = params[0] | ||
3383 | 1089 | active_mode = str( self.combo_modes.itemData( index ).toString().toUtf8() ) | ||
3384 | 1090 | if active_mode != self.mode: | ||
3385 | 1091 | for mode in self.config.SNAPSHOT_MODES.keys(): | ||
3386 | 1092 | if active_mode == mode: | ||
3387 | 1093 | getattr(self, 'mode_%s' % mode).show() | ||
3388 | 1094 | else: | ||
3389 | 1095 | getattr(self, 'mode_%s' % mode).hide() | ||
3390 | 1096 | self.mode = active_mode | ||
3391 | 1097 | |||
3392 | 890 | def accept( self ): | 1098 | def accept( self ): |
3393 | 891 | if self.validate(): | 1099 | if self.validate(): |
3394 | 892 | KDialog.accept( self ) | 1100 | KDialog.accept( self ) |