47
50
self.plugin_manager = pluginmanager.PluginManager()
49
52
def get_snapshot_id( self, date ):
50
if type( date ) is datetime.datetime:
51
return date.strftime( '%Y%m%d-%H%M%S' )
53
if type( date ) is datetime.date:
54
return date.strftime( '%Y%m%d-000000' )
56
if type( date ) is str:
53
profile_id = self.config.get_current_profile()
54
tag = self.config.get_tag( profile_id )
56
if type( date ) is datetime.datetime:
57
snapshot_id = date.strftime( '%Y%m%d-%H%M%S' ) + '-' + tag
60
if type( date ) is datetime.date:
61
snapshot_id = date.strftime( '%Y%m%d-000000' ) + '-' + tag
64
if type( date ) is str:
70
def get_snapshot_old_id( self, date ):
71
profile_id = self.config.get_current_profile()
73
if type( date ) is datetime.datetime:
74
snapshot_id = date.strftime( '%Y%m%d-%H%M%S' )
77
if type( date ) is datetime.date:
78
snapshot_id = date.strftime( '%Y%m%d-000000' )
81
if type( date ) is str:
61
87
def get_snapshot_path( self, date ):
62
return os.path.join( self.config.get_snapshots_full_path(), self.get_snapshot_id( date ) )
88
profile_id = self.config.get_current_profile()
89
path = os.path.join( self.config.get_snapshots_full_path( profile_id ), self.get_snapshot_id( date ) )
90
if os.path.exists( path ):
93
other_folders = self.config.get_other_folders_paths()
94
for folder in other_folders:
95
path_other = os.path.join( folder, self.get_snapshot_id( date ) )
96
if os.path.exists( path_other ):
99
old_path = os.path.join( self.config.get_snapshots_full_path( profile_id ), self.get_snapshot_old_id( date ) )
100
if os.path.exists( path ):
103
other_folders = self.config.get_other_folders_paths()
104
for folder in other_folders:
105
path_other = os.path.join( folder, self.get_snapshot_old_id( date ) )
106
if os.path.exists( path_other ):
64
112
def get_snapshot_info_path( self, date ):
65
113
return os.path.join( self.get_snapshot_path( date ), 'info' )
257
305
backup_suffix = '.backup.' + datetime.date.today().strftime( '%Y%m%d' )
258
306
#cmd = "rsync -avR --copy-unsafe-links --whole-file --backup --suffix=%s --chmod=+w %s/.%s %s" % ( backup_suffix, self.get_snapshot_path_to( snapshot_id ), path, '/' )
259
cmd = "rsync -avRAXE --whole-file --backup --suffix=%s " % backup_suffix
260
cmd = cmd + '--chmod=+w '
307
cmd = "rsync -avRAXEH --whole-file --backup --suffix=%s " % backup_suffix
308
#cmd = cmd + '--chmod=+w '
261
309
cmd = cmd + "\"%s.%s\" %s" % ( self.get_snapshot_path_to( snapshot_id ), path, '/' )
262
310
self._execute( cmd )
264
312
#restore permissions
313
logger.info( "Restore permissions" )
265
314
file_info_dict = self.load_fileinfo_dict( snapshot_id, info_file.get_int_value( 'snapshot_version' ) )
266
315
if len( file_info_dict ) > 0:
290
339
self._restore_path_info( item_path, file_info_dict )
292
341
def get_snapshots_list( self, sort_reverse = True ):
294
snapshots_path = self.config.get_snapshots_full_path()
297
biglist = os.listdir( snapshots_path )
304
if len( item ) != 15:
306
if os.path.isdir( os.path.join( snapshots_path, item ) ):
342
'''Returns a list with the snapshot_ids of all snapshots in the snapshots folder'''
344
profile_id = self.config.get_current_profile()
345
snapshots_path = self.config.get_snapshots_full_path( profile_id )
348
biglist = os.listdir( snapshots_path )
355
if len( item ) != 15 and len( item ) != 19:
357
if os.path.isdir( os.path.join( snapshots_path, item, 'backup' ) ):
360
list.sort( reverse = sort_reverse )
363
def get_snapshots_and_other_list( self, sort_reverse = True ):
364
'''Returns a list with the snapshot_ids, and paths, of all snapshots in the snapshots_folder and the other_folders'''
367
profile_id = self.config.get_current_profile()
368
snapshots_path = self.config.get_snapshots_full_path( profile_id )
369
snapshots_other_paths = self.config.get_other_folders_paths()
372
biglist = os.listdir( snapshots_path )
379
if len( item ) != 15 and len( item ) != 19:
381
if os.path.isdir( os.path.join( snapshots_path, item, 'backup' ) ):
382
#a = ( item, snapshots_path )
386
if len( snapshots_other_paths ) > 0:
387
for folder in snapshots_other_paths:
390
folderlist = os.listdir( folder )
394
for member in folderlist:
395
if len( member ) != 15 and len( member ) != 19:
397
if os.path.isdir( os.path.join( folder, member, 'backup' ) ):
398
#a = ( member, folder )
399
list.append( member )
309
401
list.sort( reverse = sort_reverse )
316
408
path = self.get_snapshot_path( snapshot_id )
317
cmd = "chmod -R a+rwx \"%s\"" % path
409
#cmd = "chmod -R a+rwx \"%s\"" % path
410
cmd = "find \"%s\" -type d -exec chmod u+wx {} \\;" % path #Debian patch
318
411
self._execute( cmd )
319
412
cmd = "rm -rfv \"%s\"" % path
320
413
self._execute( cmd )
415
def copy_snapshot( self, snapshot_id, new_folder ):
416
'''Copies a known snapshot to a new location'''
417
current.path = self.get_snapshot_path( snapshot_id )
418
#need to implement hardlinking to existing folder -> cp newest snapshot folder, rsync -aEAXHv --delete to this folder
419
cmd = "cp -al \"%s\"* \"%s\"" % ( current_path, new_folder )
420
logger.info( '%s is copied to folder %s' %( snapshot_id, new_folder ) )
322
423
def _get_last_snapshot_info( self ):
371
472
if not self.config.is_configured():
372
473
logger.warning( 'Not configured' )
373
474
self.plugin_manager.on_error( 1 ) #not configured
475
elif self.config.is_no_on_battery_enabled() and tools.on_battery():
476
logger.info( 'Deferring backup while on battery' )
477
logger.warning( 'Backup not performed' )
478
elif self.config.get_update_other_folders() == True:
479
logger.info( 'The application needs to change the backup format. Start the GUI to proceed. (As long as you do not you will not be able to make new snapshots!)' )
480
logger.warning( 'Backup not performed' )
375
482
instance = applicationinstance.ApplicationInstance( self.config.get_take_snapshot_instance_file(), False )
376
483
if not instance.check():
377
484
logger.warning( 'A backup is already running' )
378
485
self.plugin_manager.on_error( 2 ) #a backup is already running
487
if self.config.is_no_on_battery_enabled () and not tools.power_status_available():
488
logger.warning( 'Backups disabled on battery but power status is not available' )
380
490
instance.start_application()
381
491
logger.info( 'Lock' )
393
503
logger.info( 'Nothing to do' )
395
505
self.plugin_manager.on_process_begins() #take snapshot process begin
506
logger.info( "on process begins" )
397
507
self.set_take_snapshot_message( 0, '...' )
399
if not self.config.can_backup():
508
profile_id = self.config.get_current_profile()
509
logger.info( "Profile_id: %s" % profile_id )
511
if not self.config.can_backup( profile_id ):
400
512
if self.plugin_manager.has_gui_plugins() and self.config.is_notify_enabled():
401
513
for counter in xrange( 30, 0, -1 ):
402
514
self.set_take_snapshot_message( 1,
407
519
if self.config.can_backup():
410
if not self.config.can_backup():
522
if not self.config.can_backup( profile_id ):
411
523
logger.warning( 'Can\'t find snapshots folder !' )
412
524
self.plugin_manager.on_error( 3 ) #Can't find snapshots directory (is it on a removable drive ?)
414
526
snapshot_id = self.get_snapshot_id( now )
415
527
snapshot_path = self.get_snapshot_path( snapshot_id )
417
529
if os.path.exists( snapshot_path ):
418
530
logger.warning( "Snapshot path \"%s\" already exists" % snapshot_path )
419
531
self.plugin_manager.on_error( 4, snapshot_id ) #This snapshots already exists
652
773
self.set_take_snapshot_message( 0, _('Create hard-links') )
653
774
logger.info( "Create hard-links" )
655
if force or len( ignore_folders ) == 0:
656
cmd = "cp -al \"%s\"* \"%s\"" % ( self.get_snapshot_path_to( prev_snapshot_id ), new_snapshot_path_to )
659
for folder in include_folders:
660
prev_path = self.get_snapshot_path_to( prev_snapshot_id, folder )
661
new_path = self.get_snapshot_path_to( new_snapshot_id, folder )
662
tools.make_dirs( new_path )
663
cmd = "cp -alb \"%s\"* \"%s\"" % ( prev_path, new_path )
776
# When schedule per included folders is enabled this did not work (cp -alb iso cp -al?)
777
# This resulted in a complete rsync for the whole snapshot consuming time and space
778
# The ignored folders were copied afterwards. To solve this, the whole last snapshot is now hardlinked
779
# and rsync is called only for the folders that should be synced (without --delete-excluded).
780
#if force or len( ignore_folders ) == 0:
781
cmd = "cp -al \"%s\"* \"%s\"" % ( self.get_snapshot_path_to( prev_snapshot_id ), new_snapshot_path_to )
784
# for folder in include_folders:
785
# prev_path = self.get_snapshot_path_to( prev_snapshot_id, folder )
786
# new_path = self.get_snapshot_path_to( new_snapshot_id, folder )
787
# tools.make_dirs( new_path )
788
# cmd = "cp -alb \"%s\"* \"%s\"" % ( prev_path, new_path )
789
# self._execute( cmd )
666
791
if not self._create_directory( new_snapshot_path_to ):
669
794
#sync changed folders
670
795
logger.info( "Call rsync to take the snapshot" )
671
cmd = rsync_prefix + ' -v --delete-excluded ' + rsync_suffix + '"' + new_snapshot_path_to + '"'
796
cmd = rsync_prefix + ' -v ' + rsync_suffix + '"' + new_snapshot_path_to + '"' # do not delete the excluded, as we will miss the hardlinks with files or folders that are scheduled for a later time
672
797
self.set_take_snapshot_message( 0, _('Take snapshot') )
673
798
self._execute( cmd, self._exec_rsync_callback )
687
812
fileinfo_dict[item_path] = 1
688
813
self._save_path_info( fileinfo, item_path )
690
#copy ignored folders
691
if not force and len( prev_snapshot_id ) > 0 and len( ignore_folders ) > 0:
692
prev_fileinfo_dict = self.load_fileinfo_dict( prev_snapshot_id )
694
for folder in ignore_folders:
695
prev_path = self.get_snapshot_path_to( prev_snapshot_id, folder )
696
new_path = self.get_snapshot_path_to( new_snapshot_id, folder )
697
tools.make_dirs( new_path )
698
cmd = "cp -alb \"%s/\"* \"%s\"" % ( prev_path, new_path )
701
if len( prev_fileinfo_dict ) > 0:
702
#save permissions for all items to folder
704
prev_path_items = folder.strip( '/' ).split( '/' )
706
item_path = os.path.join( item_path, item )
707
if item_path not in fileinfo_dict and item_path in prev_fileinfo_dict:
708
self._save_path_info_line( fileinfo, item_path, prev_fileinfo_dict[item_path] )
710
#save permission for all items in folder
711
for path, dirs, files in os.walk( new_path ):
714
item_path = os.path.join( path, item )[ len( path_to_explore ) : ]
715
if item_path not in fileinfo_dict and item_path in prev_fileinfo_dict:
716
self._save_path_info_line( fileinfo, item_path, prev_fileinfo_dict[item_path] )
815
# We now copy on forehand, so copying afterwards is not necessary anymore
816
##copy ignored folders
817
#if not force and len( prev_snapshot_id ) > 0 and len( ignore_folders ) > 0:
818
# prev_fileinfo_dict = self.load_fileinfo_dict( prev_snapshot_id )
820
# for folder in ignore_folders:
821
# prev_path = self.get_snapshot_path_to( prev_snapshot_id, folder )
822
# new_path = self.get_snapshot_path_to( new_snapshot_id, folder )
823
# tools.make_dirs( new_path )
824
# cmd = "cp -alb \"%s/\"* \"%s\"" % ( prev_path, new_path )
825
# self._execute( cmd )
827
# if len( prev_fileinfo_dict ) > 0:
828
# #save permissions for all items to folder
830
# prev_path_items = folder.strip( '/' ).split( '/' )
832
# item_path = os.path.join( item_path, item )
833
# if item_path not in fileinfo_dict and item_path in prev_fileinfo_dict:
834
# self._save_path_info_line( fileinfo, item_path, prev_fileinfo_dict[item_path] )
836
# #save permission for all items in folder
837
# for path, dirs, files in os.walk( new_path ):
838
# dirs.extend( files )
840
# item_path = os.path.join( path, item )[ len( path_to_explore ) : ]
841
# if item_path not in fileinfo_dict and item_path in prev_fileinfo_dict:
842
# self._save_path_info_line( fileinfo, item_path, prev_fileinfo_dict[item_path] )
847
logger.info( "Create info file" )
848
machine = socket.gethostname()
849
user = os.environ['LOGNAME']
850
profile_id = self.config.get_current_profile()
851
tag = self.config.get_tag( profile_id )
721
852
info_file = configfile.ConfigFile()
722
info_file.set_int_value( 'snapshot_version', 1 )
853
info_file.set_int_value( 'snapshot_version', self.SNAPSHOT_VERSION )
854
info_file.set_str_value( 'snapshot_date', snapshot_id[0:15] )
855
info_file.set_str_value( 'snapshot_machine', machine )
856
info_file.set_str_value( 'snapshot_user', user )
857
info_file.set_int_value( 'snapshot_profile_id', profile_id )
858
info_file.set_int_value( 'snapshot_tag', tag )
723
859
info_file.save( self.get_snapshot_info_path( new_snapshot_id ) )