1
import rdiff_backup.Main
11
from LogHandler import LogHandler
12
from ConfigParser import SafeConfigParser, NoOptionError
14
base = os.environ['HOME']
15
setspath = "%s/.%s/sets/" % (base, version.APPPATH)
17
class RdiffError(Exception):
18
"""Called when running rdiff-backup fails."""
19
def __init__(self, status):
23
return "rdiff-backup exit with status %d\n" % self.status
27
reload(rdiff_backup.Main)
30
"""Finds all backup sets in the user's home directory"""
33
if os.path.exists(setspath):
34
sets = [x for x in os.listdir(setspath) if os.path.isdir(setspath+x)]
37
setopts = {'path': set}
38
if not os.path.exists(os.path.join(setspath, set, "filelist")):
39
print _("No 'filelist' for %s") % set
41
setopts['filelist_inc'] = []
42
setopts['filelist_exc'] = []
43
filelist = open(os.path.join(setspath, set, "filelist")).readlines()
48
setopts['filelist_exc'].append(l[2:].strip())
50
setopts['filelist_inc'].append(l[2:].strip())
53
cp = SafeConfigParser()
54
if cp.read(setspath+set+"/set.ini") == []:
55
print _("Couldn't read in description file: %s") % setspath+set+"/set.ini"
58
setopts['name'] = cp.get('Set', 'name')
59
setopts['desc'] = cp.get('Set', 'desc')
60
for key, val in cp.items('Set'):
62
backupsets.append(setopts)
63
except NoOptionError, e:
64
print _("Couldn't parse %(filename)s fully: %(error)s") % {'filename':setspath+set+"/set.ini", 'error':e}
66
def CheckDestination(path, ignore_rdiff_data=True):
67
"""Checks if the path is writeable, and if it's got something in it"""
68
if not os.access(path, os.R_OK|os.W_OK|os.X_OK):
69
return (False, 'no_permission')
70
if os.path.abspath(path) == os.path.abspath(os.environ['HOME']):
71
return (False, 'is_home_dir')
72
if len(os.listdir(path)) != 0:
73
if os.listdir(path).count('rdiff-backup-data') == 1 and ignore_rdiff_data:
76
return (False, 'not_empty')
77
# OK to write to this path
80
def ParseRestoreSrc(path):
84
contents = os.listdir(path)
89
if contents.count('%s.set' % version.APPPATH) == 1:
90
cp = SafeConfigParser()
91
cp.read(os.path.join(path, '%s.set' % version.APPPATH))
93
for key, val in cp.items('Set'):
95
ret['increments'] = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
98
args = ['--list-increments', '--parsable-output', '--terminal-verbosity', '0', path.replace("rdiff-backup-data", "")]
103
"""rdiff-backup <= 1.1.9 has a bug where it can't restore from a read-only
104
location unless it's run as root. Unfortunately there's not much we can
105
do until 1.2.0(?) becomes mainstream. We deal with that bug here."""
106
sys.stdout = sys.__stdout__
107
for f in os.listdir(path):
108
if f[:15] == "current_mirror." or f[:11] == "increments.":
109
rawtime = f.replace("current_mirror.", "").replace("increments.", "").replace(".data", "")
111
ret['increments'].insert_before(ret['increments'][0].iter, (rawtime, rawtime))
112
except IndexError, e:
113
ret['increments'].append((rawtime, rawtime))
114
ret['readonly'] = True
116
sys.stdout = sys.__stdout__
117
for inc in str(log).splitlines():
118
timestamp = inc.split()[0]
120
ret['increments'].insert_before(ret['increments'][0].iter, (time.ctime(float(timestamp)), timestamp))
121
except IndexError, e:
122
ret['increments'].append((time.ctime(float(timestamp)), timestamp))
124
elif contents.count('rdiff-backup-data') == 1:
125
return ParseRestoreSrc(os.path.join(path, 'rdiff-backup-data'))
128
"""don't really need to recurse all the way down the tree :)
130
for d in [os.path.join(path, x) for x in contents if os.path.isdir(os.path.join(path, x))]:
131
ret = ParseRestoreSrc(d)
138
def ParseRestoreSSHSrc(user, host, path):
139
path = path.replace("/rdiff-backup-data", "")
143
'%s@%s:%s/rdiff-backup-data/%s.set' % (user, host, path, version.APPPATH),
144
'/tmp/%s.ssh.set' % version.APPPATH]
145
scp = subprocess.Popen(args, shell=False)
146
while scp.poll() is None:
148
cp = SafeConfigParser()
149
if cp.read('/tmp/%s.ssh.set' % version.APPPATH) == []:
152
for key, val in cp.items('Set'):
154
ret['increments'] = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
157
sshpath = "%s@%s::%s" % (user, host, path)
158
args = ['--list-increments', '--parsable-output', '--terminal-verbosity', '0', sshpath]
164
sys.stdout = sys.__stdout__
165
for inc in str(log).splitlines():
166
timestamp = inc.split()[0]
168
ret['increments'].insert_before(ret['increments'][0].iter, (time.ctime(float(timestamp)), timestamp))
169
except IndexError, e:
170
ret['increments'].append((time.ctime(float(timestamp)), timestamp))
174
return os.path.exists(setspath+name)
177
"""Prints a list of all backup sets"""
178
for name,extra in backupsets.items():
179
print _("name:") + (" %s" % name) + "\n"\
180
+ _("title:") + (" %s" % extra['info'][0]) + "\n"\
181
+ _("description:") + (" %s" % extra['info'][1]) + "\n"\
182
+ _("default destination:") + (" %s" % extra['default_dest'])\
185
def WriteSet(name, desc, filelist, overwrite=False, extrakeys=None):
186
"""Writes set.ini and filelist to a new or existing set directory"""
188
os.makedirs(setspath+name)
190
if overwrite and str(e)[7:9]=="17":
195
cp = SafeConfigParser()
196
cp.add_section("Set")
197
cp.set("Set", "name", str(name))
198
cp.set("Set", "desc", str(desc))
199
if extrakeys is not None:
200
for key,val in extrakeys.items():
201
cp.set("Set", key, str(val))
202
inifile = open(setspath+name+"/set.ini", "w")
206
flist = open(setspath+name+"/filelist", "w")
208
for path, include in filelist:
210
includefiles += "+ %s\n" % path # add these to the end of the file
212
flist.write("- %s\n" % path)
213
flist.write(includefiles)
214
flist.write("- **\n")
218
if not SetExists(name):
219
print _("The set '%s' does not exist.") % name
222
os.remove(setspath+name+"/set.ini")
223
os.remove(setspath+name+"/filelist")
224
os.rmdir(setspath+name)
228
def SysExit(status=0):
229
raise RdiffError(status)
231
def BackupSet(set, dest, output=sys.__stdout__, err_output=sys.__stderr__,\
232
copy_set_file=True, is_ssh=False):
233
"""Performs a backup of the specified set to the path dest"""
234
if not SetExists(set['path']):
235
print _("Backup set %s not found.") % set
237
exitfunction = sys.exit
239
arglist = ['--terminal-verbosity', '5', '--verbosity', '9']
241
arglist.extend(['--exclude', dest])
242
arglist.extend(['--include-globbing-filelist', setspath+set['path']+"/filelist", "/", dest])
244
sys.stderr = err_output
247
shutil.copyfile(os.path.join(setspath, set['path'], "set.ini"), os.path.join(dest, "rdiff-backup-data", "%s.set" % version.APPPATH))
248
sys.stderr = sys.__stderr__
249
sys.stdout = sys.__stdout__
250
sys.exit = exitfunction
253
def Run_rdiff(arglist):
254
print >>sys.__stdout__, arglist
255
rdiff_backup.Main.parse_cmdlineoptions(arglist)
256
if rdiff_backup.Main.Globals.version > "0.13":
257
rdiff_backup.Main.check_action()
258
if rdiff_backup.Main.Globals.version < "0.13":
259
rdiff_backup.Main.set_action()
260
cmdpairs = rdiff_backup.Main.SetConnections.get_cmd_pairs(rdiff_backup.Main.args, rdiff_backup.Main.remote_schema, rdiff_backup.Main.remote_cmd)
261
rdiff_backup.Main.Security.initialize(rdiff_backup.Main.action or "mirror", cmdpairs)
262
rps = map(rdiff_backup.Main.SetConnections.cmdpair2rp, cmdpairs)
263
if rdiff_backup.Main.Globals.version > "0.13":
264
rdiff_backup.Main.final_set_action(rps)
265
rdiff_backup.Main.misc_setup(rps)
266
rdiff_backup.Main.take_action(rps)
267
rdiff_backup.Main.cleanup()
270
def RestoreSet(src_path, dst_path, output=sys.__stdout__, err_output=sys.__stderr__, increment="now"):
271
"""Restores the rdiff-backup backup from src to dst_path"""
272
exitfunction = sys.exit
274
arglist = ['--force', '--terminal-verbosity', '5', '--verbosity', '9', '-r', increment, src_path, dst_path]
276
sys.stderr = err_output
280
sys.stderr.write(_("Error:") + (" %s\n" % e)
281
+ _("rdiff-backup version: ") + "%s\n%s " % (rdiff_backup.Main.Globals.version, version.APPNAME)
282
+ _("version:") + " %s\n" % version.VERSION
283
+ _("Please e-mail the contents of this text box to:")
284
+ "andy@andrewprice.me.uk")
285
raise AssertionError(e)
287
sys.stdout = sys.__stdout__
288
sys.stderr = sys.__stderr__
289
sys.exit = exitfunction