~dave2010/backintime/confirm-restore

« back to all changes in this revision

Viewing changes to backup.py

  • Committer: Oprea Dan
  • Date: 2008-10-28 10:48:58 UTC
  • Revision ID: doprea@vbox-20081028104858-ppxs7v1430ta29qy
Tags: version-1.7.2, version_1.7.2
Add project

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#    Back In Time
 
2
#    Copyright (C) 2008 Oprea Dan
 
3
#
 
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.
 
8
#
 
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.
 
13
#
 
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.
 
17
 
 
18
 
 
19
import os
 
20
import os.path
 
21
import datetime
 
22
import gettext
 
23
import statvfs
 
24
 
 
25
import config
 
26
 
 
27
 
 
28
_=gettext.gettext
 
29
 
 
30
 
 
31
class Backup:
 
32
        def __init__( self ):
 
33
                self.config = config.Config()
 
34
                self.lockFile = None
 
35
 
 
36
        def execute( self, cmd, log = None ):
 
37
                print "Execute: %s" % cmd
 
38
                if not log is None:
 
39
                        cmd = "%s 2>&1 >\"%s\"" % ( cmd, log )
 
40
                os.system( cmd )
 
41
 
 
42
        def execute2( self, cmd, log = None ):
 
43
                print "Execute: %s" % cmd
 
44
                if not log is None:
 
45
                        cmd = "%s 2>&1 >\"%s\"" % ( cmd, log )
 
46
 
 
47
                pipe = os.popen( cmd, 'r' )
 
48
                cmdOutput = pipe.read()
 
49
                pipe.close()
 
50
                return cmdOutput
 
51
 
 
52
        def log( self, message, log = None ):
 
53
                print message
 
54
                if not log is None:
 
55
                        os.system( "echo \"%s\" 2>&1 >\"%s\"" % ( message, log ) )
 
56
 
 
57
        def lock( self ):
 
58
                if not self.lockFile is None:
 
59
                        return False
 
60
 
 
61
                lockFile = self.config.lockFile()
 
62
                if os.path.exists( lockFile ):
 
63
                        return False
 
64
 
 
65
                try:
 
66
                        file = os.open( lockFile, os.O_WRONLY + os.O_CREAT + os.O_EXCL )
 
67
                        os.close( file )
 
68
                except:
 
69
                        return False
 
70
 
 
71
                self.lockFile = lockFile
 
72
                return True
 
73
 
 
74
        def unlock( self, force = False ):
 
75
                lockFile = self.lockFile
 
76
                self.lockFile = None
 
77
 
 
78
                if force:
 
79
                        if lockFile is None:
 
80
                                lockFile = self.config.lockFile()
 
81
                
 
82
                if lockFile is None:
 
83
                        return False
 
84
 
 
85
                self.execute( "rm \"%s\"" % lockFile )
 
86
 
 
87
                if os.path.exists( lockFile ):
 
88
                        self.lockFile = lockFile
 
89
                        return False
 
90
 
 
91
                return True
 
92
 
 
93
        def isBusy( self ):
 
94
                return os.path.exists( self.config.lockFile() )
 
95
 
 
96
        def getBackupList( self ):
 
97
                biglist = []
 
98
                backupPath = self.config.backupPath()
 
99
 
 
100
                try:
 
101
                        biglist = os.listdir( backupPath )
 
102
                except:
 
103
                        pass
 
104
 
 
105
                list = []
 
106
                
 
107
                for item in biglist:
 
108
                        if len( item ) != 15:
 
109
                                continue
 
110
                        if os.path.isdir( os.path.join( backupPath, item ) ):
 
111
                                list.append( item )
 
112
 
 
113
                list.sort( reverse = True )
 
114
                return list
 
115
 
 
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 )
 
123
 
 
124
                #create exclude patterns string
 
125
                rsync_exclude = ''
 
126
                for exclude in self.config.excludePatterns().split( ':' ):
 
127
                        rsync_exclude += " --exclude=\"%s\"" % exclude
 
128
 
 
129
                changed_folders = folders_to_backup
 
130
 
 
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' )
 
135
 
 
136
                        prev_backup_path = self.config.backupPath( old_backup_list[ 0 ] )
 
137
                        changed_folders = []
 
138
 
 
139
                        #check for changes
 
140
                        for folder in folders_to_backup:
 
141
                                prev_backup_folder_path = os.path.join( prev_backup_path, 'backup', folder[ 1 : ] )
 
142
 
 
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 )
 
149
 
 
150
                        #check if something changed
 
151
                        if len( changed_folders ) == 0:
 
152
                                print '[backup_] Nothing changed, no back needed'
 
153
                                return False
 
154
                
 
155
                        #create hard links
 
156
                        self.execute( "mkdir -p \"%s\"" % backup_path )
 
157
                        cmd = "cp -al \"%s/\"* \"%s\"" % ( prev_backup_path, backup_path )
 
158
                        self.execute( cmd )
 
159
                        cmd = "chmod -R a+w \"%s\"" % backup_path
 
160
                        self.execute( cmd )
 
161
 
 
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
 
166
                        return False
 
167
 
 
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 )
 
175
 
 
176
                #make new folder read-only
 
177
                self.execute( "chmod -R a-w \"%s\"" % backup_path )
 
178
                return True
 
179
 
 
180
        def freeSpace( self, log ):
 
181
                #remove old backups
 
182
                if self.config.removeOldBackups():
 
183
                        listBackups = self.getBackupList()
 
184
                        listBackups.reverse()
 
185
 
 
186
                        oldBackupDate = self.config.backupName( self.config.removeOldBackupsDate() )
 
187
 
 
188
                        self.log( "[freeSpace] Remove backups older then: %s" % oldBackupDate, log )
 
189
 
 
190
                        while True:
 
191
                                if len( listBackups ) <= 1:
 
192
                                        break
 
193
                                if listBackups[0] >= oldBackupDate:
 
194
                                        break
 
195
 
 
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 )
 
201
 
 
202
                                del listBackups[0]
 
203
 
 
204
                #try to keep min free space
 
205
                if self.config.minFreeSpace():
 
206
                        minValue = self.config.minFreeSpaceValueInMb()
 
207
 
 
208
                        self.log( "[freeSpace] Min free disk space: %s Mb" % minValue, log )
 
209
 
 
210
                        listBackups = self.getBackupList()
 
211
                        listBackups.reverse()
 
212
 
 
213
                        while True:
 
214
                                if len( listBackups ) <= 1:
 
215
                                        break
 
216
 
 
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
 
220
 
 
221
                                if realValue >= minValue:
 
222
                                        break
 
223
 
 
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 )
 
229
 
 
230
                                del listBackups[0]
 
231
 
 
232
 
 
233
        def backup( self ):
 
234
                backup_date = datetime.datetime.today()
 
235
 
 
236
                if not self.config.canBackup():
 
237
                        print '[backup] not configured or backup path don\'t exists'
 
238
                        return False
 
239
 
 
240
                if self.isBusy():
 
241
                        print '[backup] isBusy'
 
242
                        return False
 
243
 
 
244
                if not self.lock():
 
245
                        print '[backup] lock failed'
 
246
                        return False
 
247
                
 
248
                print '[backup] LOCK'
 
249
 
 
250
                retVal = False
 
251
                
 
252
                backup_path = self.config.backupPath( backup_date )
 
253
                backup_log_path = os.path.join( backup_path, 'log.txt' )
 
254
 
 
255
                if os.path.exists( backup_path ):
 
256
                        print "[backup] %s already exists" % backup_path
 
257
                        retVal = True
 
258
                else:
 
259
                        #try:
 
260
                        retVal = self._backup( backup_path, backup_log_path )
 
261
                        #except:
 
262
                        #       retVal = False
 
263
 
 
264
                if not retVal:
 
265
                        os.system( "rm -rf \"%s\"" % backup_path )
 
266
                
 
267
                #try:
 
268
                self.freeSpace( None )
 
269
                #except:
 
270
                #       pass
 
271
 
 
272
                os.system( 'sleep 2' ) #max 1 backup / second
 
273
                self.unlock()
 
274
                print '[backup] UNLOCK'
 
275
                return retVal
 
276
 
 
277
 
 
278
if __name__ == '__main__':
 
279
        Backup().backup()
 
280