1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import os, re
23 import tempfile
24
25 import duplicity.backend
26 from duplicity.errors import *
27 from duplicity import globals, tempdir, util
28
30 """Connect to remote store using rsync
31
32 rsync backend contributed by Sebastian Wilhelmi <seppi@seppi.de>
33 rsyncd auth, alternate port support
34 Copyright 2010 by Edgar Soldin <edgar.soldin@web.de>
35
36 """
38 """rsyncBackend initializer"""
39 duplicity.backend.Backend.__init__(self, parsed_url)
40 """
41 rsyncd module url: rsync://[user:password@]host[:port]::[/]modname/path
42 Note: 3.0.7 is picky about syntax use either 'rsync://' or '::'
43 cmd: rsync [--port=port] host::modname/path
44 -or-
45 rsync via ssh/rsh url: rsync://user@host[:port]://some_absolute_path
46 -or- rsync://user@host[:port]:/some_relative_path
47 cmd: rsync -e 'ssh [-p=port]' [user@]host:[/]path
48 """
49 host = parsed_url.hostname
50 port = ""
51
52 if 'RSYNC_RSH' in os.environ:
53 del os.environ['RSYNC_RSH']
54 if self.over_rsyncd():
55
56 (path, port) = self.get_rsync_path()
57 self.url_string = "%s::%s" % (host, path.lstrip('/:'))
58 if port:
59 port = " --port=%s" % port
60 else:
61 if parsed_url.path.startswith("//"):
62
63 self.url_string = "%s:/%s" % (host, parsed_url.path.lstrip('/'))
64 else:
65
66 self.url_string = "%s:%s" % (host, parsed_url.path.lstrip('/'))
67 if parsed_url.port:
68 port = " -p %s" % parsed_url.port
69
70 if self.url_string[-1] != '/':
71 self.url_string += '/'
72
73 if parsed_url.username:
74 if self.over_rsyncd():
75 os.environ['USER'] = parsed_url.username
76 else:
77 self.url_string = parsed_url.username + "@" + self.url_string
78
79 self.use_getpass = False
80 password = self.get_password()
81 if password:
82 os.environ['RSYNC_PASSWORD'] = password
83 if self.over_rsyncd():
84 portOption = port
85 else:
86 portOption = " -e 'ssh -oBatchMode=yes%s'" % port
87 rsyncOptions = globals.rsync_options
88 if rsyncOptions:
89 rsyncOptions= " " + rsyncOptions
90
91 self.cmd = "rsync%s%s" % (portOption, rsyncOptions)
92
94 url = self.parsed_url.url_string
95 if re.search('::[^:]*$', url):
96 return True
97 else:
98 return False
99
101 url = self.parsed_url.url_string
102 m = re.search("(:\d+|)?::([^:]*)$", url)
103 if m:
104 return m.group(2), m.group(1).lstrip(':')
105 raise InvalidBackendURL("Could not determine rsync path: %s"
106 "" % self.munge_password( url ) )
107
111
112 - def put(self, source_path, remote_filename = None):
113 """Use rsync to copy source_dir/filename to remote computer"""
114 if not remote_filename:
115 remote_filename = source_path.get_filename()
116 remote_path = os.path.join(self.url_string, remote_filename)
117 commandline = "%s %s %s" % (self.cmd, source_path.name, remote_path)
118 self.run_command(commandline)
119
120 - def get(self, remote_filename, local_path):
128
130 """List files"""
131 def split (str):
132 line = str.split ()
133 if len (line) > 4 and line[4] != '.':
134 return line[4]
135 else:
136 return None
137 commandline = "%s %s" % (self.cmd, self.url_string)
138 result, stdout = self.run_command(commandline)
139 return filter(lambda x: x, map (split, stdout.split('\n')))
140
141 - def delete(self, filename_list):
142 """Delete files."""
143 delete_list = filename_list
144 dont_delete_list = []
145 for file in self.list ():
146 if file in delete_list:
147 delete_list.remove (file)
148 else:
149 dont_delete_list.append (file)
150 if len (delete_list) > 0:
151 raise BackendException("Files %s not found" % str (delete_list))
152
153 dir = tempfile.mkdtemp()
154 exclude, exclude_name = tempdir.default().mkstemp_file()
155 to_delete = [exclude_name]
156 for file in dont_delete_list:
157 path = os.path.join (dir, file)
158 to_delete.append (path)
159 f = open (path, 'w')
160 print >>exclude, file
161 f.close()
162 exclude.close()
163 commandline = ("%s --recursive --delete --exclude-from=%s %s/ %s" %
164 (self.cmd, exclude_name, dir, self.url_string))
165 self.run_command(commandline)
166 for file in to_delete:
167 util.ignore_missing(os.unlink, file)
168 os.rmdir (dir)
169
170 duplicity.backend.register_backend("rsync", RsyncBackend)
171