1 |
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- |
2 |
# |
3 |
# Copyright 2002 Ben Escoto <ben@emerose.org> |
4 |
# Copyright 2007 Kenneth Loafman <kenneth@loafman.com> |
5 |
# |
6 |
# This file is part of duplicity. |
7 |
# |
8 |
# Duplicity is free software; you can redistribute it and/or modify it |
9 |
# under the terms of the GNU General Public License as published by the |
10 |
# Free Software Foundation; either version 2 of the License, or (at your |
11 |
# option) any later version. |
12 |
# |
13 |
# Duplicity is distributed in the hope that it will be useful, but |
14 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 |
# General Public License for more details. |
17 |
# |
18 |
# You should have received a copy of the GNU General Public License |
19 |
# along with duplicity; if not, write to the Free Software Foundation, |
20 |
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 |
|
22 |
import os |
23 |
import os.path |
24 |
import tempfile |
25 |
|
26 |
import duplicity.backend |
27 |
from duplicity.errors import * |
28 |
from duplicity import tempdir |
29 |
|
30 |
class RsyncBackend(duplicity.backend.Backend): |
31 |
"""Connect to remote store using rsync |
32 |
|
33 |
rsync backend contributed by Sebastian Wilhelmi <seppi@seppi.de> |
34 |
|
35 |
""" |
36 |
def __init__(self, parsed_url): |
37 |
"""rsyncBackend initializer""" |
38 |
duplicity.backend.Backend.__init__(self, parsed_url) |
39 |
if parsed_url.netloc.find('@') < 0: |
40 |
user = "" |
41 |
host = parsed_url.netloc |
42 |
mynetloc = host |
43 |
else: |
44 |
user, host = parsed_url.netloc.split('@') |
45 |
if parsed_url.password: |
46 |
user = user.split(':')[0] |
47 |
mynetloc = '%s@%s' % (user, host) |
48 |
# module url: rsync://user@host::/modname/path |
49 |
# rsync via ssh/rsh: rsync://user@host//some_absolute_path |
50 |
# -or- rsync://user@host/some_relative_path |
51 |
if parsed_url.netloc.endswith("::"): |
52 |
# its a module path |
53 |
self.url_string = "%s%s" % (mynetloc, parsed_url.path.lstrip('/')) |
54 |
elif parsed_url.path.startswith("//"): |
55 |
# its an absolute path |
56 |
self.url_string = "%s:/%s" % (mynetloc.rstrip(':'), parsed_url.path.lstrip('/')) |
57 |
else: |
58 |
# its a relative path |
59 |
self.url_string = "%s:%s" % (mynetloc.rstrip(':'), parsed_url.path.lstrip('/')) |
60 |
if self.url_string[-1] != '/': |
61 |
self.url_string += '/' |
62 |
|
63 |
def put(self, source_path, remote_filename = None): |
64 |
"""Use rsync to copy source_dir/filename to remote computer""" |
65 |
if not remote_filename: |
66 |
remote_filename = source_path.get_filename() |
67 |
remote_path = os.path.join(self.url_string, remote_filename) |
68 |
commandline = "rsync %s %s" % (source_path.name, remote_path) |
69 |
self.run_command(commandline) |
70 |
|
71 |
def get(self, remote_filename, local_path): |
72 |
"""Use rsync to get a remote file""" |
73 |
remote_path = os.path.join (self.url_string, remote_filename) |
74 |
commandline = "rsync %s %s" % (remote_path, local_path.name) |
75 |
self.run_command(commandline) |
76 |
local_path.setdata() |
77 |
if not local_path.exists(): |
78 |
raise BackendException("File %s not found" % local_path.name) |
79 |
|
80 |
def list(self): |
81 |
"""List files""" |
82 |
def split (str): |
83 |
line = str.split () |
84 |
if len (line) > 4 and line[4] != '.': |
85 |
return line[4] |
86 |
else: |
87 |
return None |
88 |
commandline = "rsync %s" % self.url_string |
89 |
return filter(lambda x: x, map (split, self.popen(commandline).split('\n'))) |
90 |
|
91 |
def delete(self, filename_list): |
92 |
"""Delete files.""" |
93 |
delete_list = filename_list |
94 |
dont_delete_list = [] |
95 |
for file in self.list (): |
96 |
if file in delete_list: |
97 |
delete_list.remove (file) |
98 |
else: |
99 |
dont_delete_list.append (file) |
100 |
if len (delete_list) > 0: |
101 |
raise BackendException("Files %s not found" % str (delete_list)) |
102 |
|
103 |
dir = tempfile.mkdtemp() |
104 |
exclude, exclude_name = tempdir.default().mkstemp_file() |
105 |
to_delete = [exclude_name] |
106 |
for file in dont_delete_list: |
107 |
path = os.path.join (dir, file) |
108 |
to_delete.append (path) |
109 |
f = open (path, 'w') |
110 |
print >>exclude, file |
111 |
f.close() |
112 |
exclude.close() |
113 |
commandline = ("rsync --recursive --delete --exclude-from=%s %s/ %s" % |
114 |
(exclude_name, dir, self.url_string)) |
115 |
self.run_command(commandline) |
116 |
for file in to_delete: |
117 |
os.unlink (file) |
118 |
os.rmdir (dir) |
119 |
|
120 |
duplicity.backend.register_backend("rsync", RsyncBackend) |