2
# Copyright (C) 2008 Oprea Dan
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License along
15
# with this program; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33
self.config = config.Config()
36
def execute( self, cmd, log = None ):
37
print "Execute: %s" % cmd
39
cmd = "%s 2>&1 >\"%s\"" % ( cmd, log )
42
def execute2( self, cmd, log = None ):
43
print "Execute: %s" % cmd
45
cmd = "%s 2>&1 >\"%s\"" % ( cmd, log )
47
pipe = os.popen( cmd, 'r' )
48
cmdOutput = pipe.read()
52
def log( self, message, log = None ):
55
os.system( "echo \"%s\" 2>&1 >\"%s\"" % ( message, log ) )
58
if not self.lockFile is None:
61
lockFile = self.config.lockFile()
62
if os.path.exists( lockFile ):
66
file = os.open( lockFile, os.O_WRONLY + os.O_CREAT + os.O_EXCL )
71
self.lockFile = lockFile
74
def unlock( self, force = False ):
75
lockFile = self.lockFile
80
lockFile = self.config.lockFile()
85
self.execute( "rm \"%s\"" % lockFile )
87
if os.path.exists( lockFile ):
88
self.lockFile = lockFile
94
return os.path.exists( self.config.lockFile() )
96
def getBackupList( self ):
98
backupPath = self.config.backupPath()
101
biglist = os.listdir( backupPath )
108
if len( item ) != 15:
110
if os.path.isdir( os.path.join( backupPath, item ) ):
113
list.sort( reverse = True )
116
def _backup( self, backup_path, log ):
117
#check only existing paths
118
all_folders_to_backup = self.config.includeFolders().split( ':' )
119
folders_to_backup = []
120
for folder in all_folders_to_backup:
121
if os.path.isdir( folder ):
122
folders_to_backup.append( folder )
124
#create exclude patterns string
126
for exclude in self.config.excludePatterns().split( ':' ):
127
rsync_exclude += " --exclude=\"%s\"" % exclude
129
changed_folders = folders_to_backup
131
#check previous backup
132
old_backup_list = self.getBackupList()
133
if len( old_backup_list ) > 0:
134
self.log( '[backup_] Compare with old backup' )
136
prev_backup_path = self.config.backupPath( old_backup_list[ 0 ] )
140
for folder in folders_to_backup:
141
prev_backup_folder_path = os.path.join( prev_backup_path, 'backup', folder[ 1 : ] )
143
if os.path.isdir( prev_backup_folder_path ):
144
cmd = "diff -qr " + rsync_exclude + " \"%s/\" \"%s/\"" % ( folder, prev_backup_folder_path )
145
if len( self.execute2( cmd ) ) > 0:
146
changed_folders.append( folder )
147
else: #folder don't exists in backup
148
changed_folders.append( folder )
150
#check if something changed
151
if len( changed_folders ) == 0:
152
print '[backup_] Nothing changed, no back needed'
156
self.execute( "mkdir -p \"%s\"" % backup_path )
157
cmd = "cp -al \"%s/\"* \"%s\"" % ( prev_backup_path, backup_path )
159
cmd = "chmod -R a+w \"%s\"" % backup_path
162
#create new backup folder
163
self.execute( "mkdir -p \"%s\"" % backup_path )
164
if not os.path.exists( backup_path ):
165
print "[backup_] Can't create %s" % backup_path
168
#sync changed folders
169
print '[backup_] Start rsync'
170
for folder in changed_folders:
171
backup_folder_path = os.path.join( backup_path, 'backup', folder[ 1 : ] )
172
self.execute( "mkdir -p \"%s\"" % backup_folder_path, log )
173
cmd = "rsync -av --delete --one-file-system " + rsync_exclude + " \"%s/\" \"%s/\"" % ( folder, backup_folder_path )
174
self.execute( cmd, log )
176
#make new folder read-only
177
self.execute( "chmod -R a-w \"%s\"" % backup_path )
180
def freeSpace( self, log ):
182
if self.config.removeOldBackups():
183
listBackups = self.getBackupList()
184
listBackups.reverse()
186
oldBackupDate = self.config.backupName( self.config.removeOldBackupsDate() )
188
self.log( "[freeSpace] Remove backups older then: %s" % oldBackupDate, log )
191
if len( listBackups ) <= 1:
193
if listBackups[0] >= oldBackupDate:
196
path = self.config.backupPath( listBackups[0] )
197
cmd = "chmod -R a+w \"%s\"" % path
198
self.execute( cmd, log )
199
cmd = "rm -rf \"%s\"" % path
200
self.execute( cmd, log )
204
#try to keep min free space
205
if self.config.minFreeSpace():
206
minValue = self.config.minFreeSpaceValueInMb()
208
self.log( "[freeSpace] Min free disk space: %s Mb" % minValue, log )
210
listBackups = self.getBackupList()
211
listBackups.reverse()
214
if len( listBackups ) <= 1:
217
info = os.statvfs( self.config.backupBasePath() )
218
realValue = info[ statvfs.F_FRSIZE ] * info[ statvfs.F_BAVAIL ] / ( 1024 * 1024 )
219
print "Free disk space: %s Mb" % realValue
221
if realValue >= minValue:
224
path = self.config.backupPath( listBackups[0] )
225
cmd = "chmod -R a+w \"%s\"" % path
226
self.execute( cmd, log )
227
cmd = "rm -rf \"%s\"" % path
228
self.execute( cmd, log )
234
backup_date = datetime.datetime.today()
236
if not self.config.canBackup():
237
print '[backup] not configured or backup path don\'t exists'
241
print '[backup] isBusy'
245
print '[backup] lock failed'
248
print '[backup] LOCK'
252
backup_path = self.config.backupPath( backup_date )
253
backup_log_path = os.path.join( backup_path, 'log.txt' )
255
if os.path.exists( backup_path ):
256
print "[backup] %s already exists" % backup_path
260
retVal = self._backup( backup_path, backup_log_path )
265
os.system( "rm -rf \"%s\"" % backup_path )
268
self.freeSpace( None )
272
os.system( 'sleep 2' ) #max 1 backup / second
274
print '[backup] UNLOCK'
278
if __name__ == '__main__':