11
from LogHandler import LogHandler
19
class DestinationError(Exception):
21
FAIL_RD = 2 # Read perm on destination
22
FAIL_WR = 4 # Write perm on destination
23
FAIL_EX = 8 # Execute perm on destination
24
FAIL_HM = 16 # Destination is homedir
25
FAIL_MK = 32 # Could not make destination directory
26
WARN_NEMPTY = 64 # Not empty
30
FAIL_RD: _("No read permission on destination directory"),
31
FAIL_WR: _("No write permission on destination directory"),
32
FAIL_EX: _("No execute permission on destionation directory"),
33
FAIL_HM: _("Destination is home directory. This would remove the files in your home directory. Please choose a different location."),
34
FAIL_MK: _("Could not create destination directory"),
35
WARN_NEMPTY: _("Destination directory is not empty"),
39
An exception to be raised when the backup destination is unsuitable.
41
def __init__(self, code, path, msg=None):
42
Exception.__init__(self)
44
self.message = "%s: %s" % (self.msgs[code], path)
46
self.message = "%s: %s (%s)" % (self.msgs[code], path, msg)
52
class BackupError(Exception):
54
An exception to be raised when something goes wrong during a backup.
60
The base class which provides local backup functionality.
64
Initialise a Backup object
67
self.progress_cbs = []
68
self.destination = None
72
def set_destination(self, destination):
74
Set the destination directory for the backup.
76
if os.path.abspath(destination) == os.path.abspath(os.environ['HOME']):
77
self.destination = None
78
raise DestinationError(DestinationError.FAIL_HM,
80
self.destination = destination
82
def add_progress_cb(self, progress_cb, data=None):
84
Set the signal handler for reporting backup progress.
85
If data is provided, it will be passed to the handler.
87
self.progress_cbs.append((progress_cb,data))
89
def report_progress(self, status):
91
Report progress percentage to the progress callbacks.
93
for cb, data in self.progress_cbs:
95
cb(self.progress, status, data)
97
def check_destination(self):
99
Check the destination directory is OK to back up to.
101
if not os.path.exists(self.destination):
103
self.report_progress(
104
_("Creating destination directory"))
105
os.makedirs(self.destination)
107
raise DestinationError(DestinationError.FAIL_MK,
108
self.destination, e.strerror)
110
home = os.environ['HOME']
111
if os.path.abspath(self.destination) == os.path.abspath(home):
112
raise DestinationError(DestinationError.FAIL_HM,
114
if not os.access(self.destination, os.R_OK):
115
raise DestinationError(DestinationError.FAIL_RD,
117
if not os.access(self.destination, os.W_OK):
118
raise DestinationError(DestinationError.FAIL_WR,
120
if not os.access(self.destination, os.X_OK):
121
raise DestinationError(DestinationError.FAIL_EX,
124
ls = os.listdir(self.destination)
126
if ls.count('rdiff-backup-data') == 0:
127
raise DestinationError(
128
DestinationError.WARN_NEMPTY, self.destination)
131
def __analyze_source(self, bset, ui):
133
Check files are ok to backup and count them.
136
self.report_progress(_("Analyzing source files"))
138
for path in bset.files_include:
139
if path == bset.dest:
140
ui.statuswin.addmsg(_("Destination directory in backup source. Omitting."))
141
elif os.path.isdir(path):
142
for dirname, dirs, files in os.walk(path):
144
fullpath = os.path.join(dirname, f)
145
if fullpath in bset.files_exclude:
147
if fullpath == bset.dest:
148
ui.statuswin.addmsg(_("NOTE: Destination directory inside backup set. Omitting."))
149
elif os.path.islink(fullpath):
151
elif os.access(fullpath, os.R_OK):
154
fullpath = os.path.join(dirname, d)
155
if fullpath in bset.files_exclude:
158
if os.access(fullpath, os.R_OK|os.X_OK):
160
self.report_progress("%s: %d" % (_("Files found"), count))
161
elif os.access(path, os.R_OK):
164
self.report_progress(_("Backup source analysis complete."))
167
def do_backup(self, backupset, ui, remote=False):
169
Run a local backup of the given backup set.
171
filecount = self.__analyze_source(backupset, ui)
172
self.report_progress(_("Running backup"))
173
stdout = LogHandler(ui.statuswin, ui.widgets, filecount, True)
174
stderr = LogHandler(ui.statuswin)
176
self.report_progress(_("Creating backup"))
179
rdiff_interface.BackupSet(
184
copy_set_file = not remote,
187
except rdiff_interface.RdiffError, e:
189
raise BackupError(_("Backup failed: %s") % str(e))
194
self.report_progress(_("Backup complete"))
196
sys.stdout = sys.__stdout__
197
sys.stderr = sys.__stderr__
201
class CDBackup(Backup):
203
Provides methods for backing up files to a CD or DVD.
205
def __init__(self, tmpdir='/tmp'):
207
Initialise a CDBackup object.
209
Backup.__init__(self)
210
self.destination = os.path.join(tmpdir, "%s.cdimage" %
212
self.isopath = os.path.join(tmpdir, "%s.iso" % version.APPPATH)
213
gobject.threads_init()
215
def check_destination(self):
217
Clear the temporary directory before checking it.
220
shutil.rmtree(self.destination)
222
pass # Didn't exist in the first place
224
Backup.check_destination(self)
226
def show_iso_progress(self, progress):
228
Helper function for reporting iso image creation progress.
230
self.progress = progress
231
self.report_progress(_("Creating CD image"))
233
def create_iso(self, backupset, ui):
235
Create an iso image using the Mkisofs class.
237
self.report_progress(_("Creating temporary backup"))
238
self.do_backup(backupset, ui)
239
isomaker = mkisofs.Mkisofs()
240
isomaker.set_progress_hook(self.show_iso_progress)
241
isomaker.create_iso(self.destination, self.isopath)
242
isoret = isomaker.get_retval()
244
errmsg = os.strerror(isoret)
245
msg = _("Backup failed; could not create CD image %(filename)s: %(error)s\n") %\
246
{'filename':self.isopath, 'error':errmsg}
247
raise BackupError(msg)
249
def burn_iso(self, drive, cd_progress_cb):
251
Burn the iso image to CD/DVD.
253
self.report_progress(_("Writing image to CD/DVD"))
255
track = braseroburn.TrackImage()
256
track.set_source(self.isopath)
258
session = braseroburn.SessionCfg()
259
session.add_track(track)
260
session.set_burner(drive)
262
options = braseroburn.BurnOptions(session)
265
if err != gtk.RESPONSE_OK:
267
self.report_progress("")
268
raise BackupError(_("An error occurred while burning the CD."))
270
cdburner = braseroburn.BurnDialog()
272
if not cdburner.run(session):
275
self.report_progress("")
276
raise BackupError(_("An error occurred while burning the CD."))
280
self.report_progress(_("Cleaning up temporary files"))
282
shutil.rmtree(dest_path)
283
os.unlink(self.isopath)
288
self.report_progress(_("Backup complete"))
290
class RemoteBackup(Backup):
292
Provides methods for backing up files to a remote server.
295
def set_destination(self, user, path, host):
297
For remote backup, the destination must include a username, a
298
remote path and a hostname.
302
error_string += _("\nUser")
304
error_string += _("\nHost")
306
error_string += _("\nPath")
308
raise DestinationError(DestinationError.FAIL_MISC, "",
309
_("Missing fields:%s") % error_string)
311
self.destination = "%s@%s::%s" % (user, host, path)
317
def do_remote_backup(self, backupset, ui):
321
self.do_backup(backupset, ui, remote = True)
323
inifile = os.path.join(rdiff_interface.setspath,
324
backupset.path, "set.ini")
325
setfile = os.path.join(self.path, "rdiff-backup-data",
326
"%s.set" % version.APPPATH)
327
args = "scp %s %s@%s:%s" % (inifile, self.user, self.host, setfile)
328
scp = subprocess.Popen(args, shell=True)
329
while scp.poll() is None:
330
self.report_progress(_("Transferring backup set data"))
333
_("Backup completed, but could not copy the backup set data file. You can "
334
"manually copy this file from\n%(source)s\n to\n%(filepath)s (on host "
335
"%(hostname)s)") % {'source':inifile, 'filepath':setfile, 'hostname':self.host})
338
self.report_progress(_("Backup completed"))
341
_("An error occurred while transferring '%s'.") %\