Bernd Wurst commited on 2007-03-09 07:15:52
Zeige 9 geänderte Dateien mit 1 Einfügungen und 801 Löschungen.
... | ... |
@@ -1,64 +0,0 @@ |
1 |
-# ChangeLog for app-backup/duplicity |
|
2 |
-# Copyright 2002-2006 Gentoo Foundation; Distributed under the GPL v2 |
|
3 |
-# $Header: /var/cvsroot/gentoo-x86/app-backup/duplicity/ChangeLog,v 1.8 2006/04/30 16:06:59 dertobi123 Exp $ |
|
4 |
- |
|
5 |
- 30 Apr 2006; Tobias Scherbaum <dertobi123@gentoo.org> |
|
6 |
- duplicity-0.4.2.ebuild: |
|
7 |
- ppc stable |
|
8 |
- |
|
9 |
- 27 Apr 2006; Marien Zwart <marienz@gentoo.org> |
|
10 |
- files/digest-duplicity-0.4.1, files/digest-duplicity-0.4.2, Manifest: |
|
11 |
- Fixing SHA256 digest for real, pass three... |
|
12 |
- |
|
13 |
- 27 Apr 2006; Marien Zwart <marienz@gentoo.org> |
|
14 |
- files/digest-duplicity-0.4.1, files/digest-duplicity-0.4.2, Manifest: |
|
15 |
- Fixing SHA256 digest, pass two. |
|
16 |
- |
|
17 |
- 17 Apr 2006; <ticho@gentoo.org> duplicity-0.4.2.ebuild: |
|
18 |
- Stable on x86. |
|
19 |
- |
|
20 |
-*duplicity-0.4.2 (12 Mar 2006) |
|
21 |
- |
|
22 |
- 12 Mar 2006; <ticho@gentoo.org> +duplicity-0.4.2.ebuild: |
|
23 |
- Version bump. Closes bug #125928, reported by Jiri Tyr <jiri.tyr at |
|
24 |
- e-learning.vslib.cz>. |
|
25 |
- |
|
26 |
- 15 Oct 2005; Joseph Jezak <josejx@gentoo.org> duplicity-0.4.1.ebuild: |
|
27 |
- Marked ppc stable for bug #106067. |
|
28 |
- |
|
29 |
- 04 Jul 2005; Robin H. Johnson <robbat2@gentoo.org> +metadata.xml, |
|
30 |
- +duplicity-0.4.1.ebuild: |
|
31 |
- Moved from net-misc/duplicity to app-backup/duplicity. |
|
32 |
- |
|
33 |
- 10 May 2005; David Holm <dholm@gentoo.org> duplicity-0.4.1.ebuild: |
|
34 |
- Added to ~ppc. |
|
35 |
- |
|
36 |
- 16 Nov 2004; Andrej Kacian <ticho@gentoo.org> -duplicity-0.4.0.ebuild: |
|
37 |
- Removed 0.4.0 from portage. |
|
38 |
- |
|
39 |
- 10 Nov 2004; Andrej Kacian <ticho@gentoo.org> duplicity-0.4.1.ebuild: |
|
40 |
- Fixed librsync dependancy. Closes bug #70595, reported by Dave S |
|
41 |
- <jk@pusspaws.net>. |
|
42 |
- |
|
43 |
- 01 Nov 2004; Andrej Kacian <ticho@gentoo.org> duplicity-0.4.1.ebuild: |
|
44 |
- Stable on x86 |
|
45 |
- |
|
46 |
- 24 Oct 2004; Andrej Kacian <ticho@gentoo.org> +metadata.xml, |
|
47 |
- duplicity-0.4.0.ebuild, duplicity-0.4.1.ebuild: |
|
48 |
- Revamped DEPEND and RDEPEND. Made 0.4.0 depend on librsync <=0.9.5.1. This |
|
49 |
- closes #67940, reported by Dave S <jk@pusspaws.net>. Set myself as |
|
50 |
- maintainer (and added missing metadata.xml). |
|
51 |
- |
|
52 |
- 01 Jul 2004; Jon Hood <squinky86@gentoo.org> duplicity-0.4.0.ebuild, |
|
53 |
- duplicity-0.4.1.ebuild: |
|
54 |
- change virtual/glibc to virtual/libc, add IUSE |
|
55 |
- |
|
56 |
-*duplicity-0.4.1 (19 Oct 2003) |
|
57 |
- |
|
58 |
- 19 Oct 2003; Martin Holzer <mholzer@gentoo.org> duplicity-0.4.1.ebuild: |
|
59 |
- Version bumped. Closes #30179. |
|
60 |
- |
|
61 |
-*duplicity-0.4.0 (14 May 2003) |
|
62 |
- |
|
63 |
- 14 May 2003; Martin Holzer <mholzer@gentoo.org> duplicity-0.4.0.ebuild : |
|
64 |
- Initial Version submitted by rob holland <robh@gentoo.org> in #20693 |
... | ... |
@@ -1,28 +0,0 @@ |
1 |
-AUX duplicity-0.4.2-timeout-fixed-backends.py 21964 RMD160 cb0ba56204b2e7786e40f74f44e4cc3b1d4d7f48 SHA1 10c726c8b030df15169035132abdcc612526a2f8 SHA256 ed9885708025607991c7a7e38194b1dbb7886cda15c1f150616b234e8d17a66f |
|
2 |
-MD5 61d0b9a78dc5c56f6a91f92621b18bd1 files/duplicity-0.4.2-timeout-fixed-backends.py 21964 |
|
3 |
-RMD160 cb0ba56204b2e7786e40f74f44e4cc3b1d4d7f48 files/duplicity-0.4.2-timeout-fixed-backends.py 21964 |
|
4 |
-SHA256 ed9885708025607991c7a7e38194b1dbb7886cda15c1f150616b234e8d17a66f files/duplicity-0.4.2-timeout-fixed-backends.py 21964 |
|
5 |
-DIST duplicity-0.4.1.tar.gz 101626 RMD160 192ce5111fbe408d5927292ae8b5eb8bc21319bd SHA256 b46ff5fe7c89283c635e2a94294d28ec8a501a9d58c41f5672943fb0e5daa725 |
|
6 |
-DIST duplicity-0.4.2.tar.gz 103183 RMD160 c6c86f397e43b7d5f63965d69f3328daa601d00b SHA1 37e861218800910fab7590f45520e0f1d8b318d4 SHA256 5fdf8aeb32bb4c09e3c9d5c4150245a71d757d31d9bb341524de75e06421e176 |
|
7 |
-EBUILD duplicity-0.4.1.ebuild 703 RMD160 a52bcc6a407764788cb039999284fe7c6cb6370b SHA1 2991533ae94bb5f257ba733d8987af447bcbc3cd SHA256 4053ca1afcfe01cb93467a5682d1edbc855b27dc288f790be28ac65224b37058 |
|
8 |
-MD5 ed7b72a8780e38926badaa0bf866ac8d duplicity-0.4.1.ebuild 703 |
|
9 |
-RMD160 a52bcc6a407764788cb039999284fe7c6cb6370b duplicity-0.4.1.ebuild 703 |
|
10 |
-SHA256 4053ca1afcfe01cb93467a5682d1edbc855b27dc288f790be28ac65224b37058 duplicity-0.4.1.ebuild 703 |
|
11 |
-EBUILD duplicity-0.4.2.ebuild 971 RMD160 4c97979827820363b78c2f2fc42824574d1dca37 SHA1 2f7f6eb94ccf3e6d7174ef2996ee000bb3f0be08 SHA256 e682a7d29e628db56f46a6b1e624189d7146ad741dc6cd82b6db1f9bfbbe76b7 |
|
12 |
-MD5 5725f0dfb95d3a8487d03fa071c06053 duplicity-0.4.2.ebuild 971 |
|
13 |
-RMD160 4c97979827820363b78c2f2fc42824574d1dca37 duplicity-0.4.2.ebuild 971 |
|
14 |
-SHA256 e682a7d29e628db56f46a6b1e624189d7146ad741dc6cd82b6db1f9bfbbe76b7 duplicity-0.4.2.ebuild 971 |
|
15 |
-MISC ChangeLog 2376 RMD160 e07cb927b91de7a24214867724478ae7cd0c72bc SHA1 59d1f6d02f22eb398bd38acb7edd2901b605dff6 SHA256 6871437952da46a60888f349516b959be9da053b8c75b9142216c59166b13fdf |
|
16 |
-MD5 48cff4f1ce16bc800cc7869e57fb0e53 ChangeLog 2376 |
|
17 |
-RMD160 e07cb927b91de7a24214867724478ae7cd0c72bc ChangeLog 2376 |
|
18 |
-SHA256 6871437952da46a60888f349516b959be9da053b8c75b9142216c59166b13fdf ChangeLog 2376 |
|
19 |
-MISC metadata.xml 250 RMD160 b80f36e207a57583a06b1ddb538793eac15397fe SHA1 7aa5562e71958d16a80076a7cdd0e4002eeb99a0 SHA256 5e1a7d2736303cec3dc620e1951c9a809be04bfe07760d89741f824b7760bfaf |
|
20 |
-MD5 bd9e210457c22384582e7c1394314d3f metadata.xml 250 |
|
21 |
-RMD160 b80f36e207a57583a06b1ddb538793eac15397fe metadata.xml 250 |
|
22 |
-SHA256 5e1a7d2736303cec3dc620e1951c9a809be04bfe07760d89741f824b7760bfaf metadata.xml 250 |
|
23 |
-MD5 66ccbbbe31ea6d7d887f161ca910c6af files/digest-duplicity-0.4.1 247 |
|
24 |
-RMD160 d3757d5050f0cf4495ab1c6abc051a360c06cee4 files/digest-duplicity-0.4.1 247 |
|
25 |
-SHA256 c547e600067221be62b903f26b628cb244acf5f3ca4c71b3bc8591b585a0c484 files/digest-duplicity-0.4.1 247 |
|
26 |
-MD5 6d9ae5885e91f1ad9c46ecab2e4ae8c8 files/digest-duplicity-0.4.2 247 |
|
27 |
-RMD160 3ff2413e0aa240285377c4c987043995968edca2 files/digest-duplicity-0.4.2 247 |
|
28 |
-SHA256 3abc53cada3b4e46dba6de362adf476af57166107f8055b2aa29c0bebf8008fd files/digest-duplicity-0.4.2 247 |
... | ... |
@@ -1,26 +0,0 @@ |
1 |
-# Copyright 1999-2005 Gentoo Foundation |
|
2 |
-# Distributed under the terms of the GNU General Public License v2 |
|
3 |
-# $Header: /var/cvsroot/gentoo-x86/app-backup/duplicity/duplicity-0.4.1.ebuild,v 1.2 2005/10/15 20:37:48 josejx Exp $ |
|
4 |
- |
|
5 |
-IUSE="" |
|
6 |
-DESCRIPTION="duplicity is a secure backup system using gnupg to encrypt data" |
|
7 |
-HOMEPAGE="http://www.nongnu.org/duplicity/" |
|
8 |
-SRC_URI="http://savannah.nongnu.org/download/${PN}/${P}.tar.gz" |
|
9 |
- |
|
10 |
-LICENSE="GPL-2" |
|
11 |
-SLOT="0" |
|
12 |
-KEYWORDS="ppc x86" |
|
13 |
- |
|
14 |
-DEPEND="virtual/libc |
|
15 |
- >=dev-lang/python-2.3 |
|
16 |
- >=net-libs/librsync-0.9.6" |
|
17 |
-RDEPEND="${DEPEND} |
|
18 |
- app-crypt/gnupg" |
|
19 |
- |
|
20 |
-src_compile() { |
|
21 |
- python setup.py build || die "compile failed" |
|
22 |
-} |
|
23 |
- |
|
24 |
-src_install() { |
|
25 |
- python setup.py install --prefix=${D}/usr |
|
26 |
-} |
... | ... |
@@ -1,43 +0,0 @@ |
1 |
-# Copyright 1999-2006 Gentoo Foundation |
|
2 |
-# Distributed under the terms of the GNU General Public License v2 |
|
3 |
-# $Header: /var/cvsroot/gentoo-x86/app-backup/duplicity/duplicity-0.4.2.ebuild,v 1.3 2006/04/30 16:06:59 dertobi123 Exp $ |
|
4 |
- |
|
5 |
-inherit distutils |
|
6 |
- |
|
7 |
-IUSE="" |
|
8 |
-DESCRIPTION="duplicity is a secure backup system using gnupg to encrypt data" |
|
9 |
-HOMEPAGE="http://www.nongnu.org/duplicity/" |
|
10 |
-SRC_URI="http://savannah.nongnu.org/download/${PN}/${P}.tar.gz" |
|
11 |
- |
|
12 |
-LICENSE="GPL-2" |
|
13 |
-SLOT="0" |
|
14 |
-KEYWORDS="ppc x86" |
|
15 |
- |
|
16 |
-DEPEND="virtual/libc |
|
17 |
- >=dev-lang/python-2.3 |
|
18 |
- >=net-libs/librsync-0.9.6" |
|
19 |
-RDEPEND="${DEPEND} |
|
20 |
- app-crypt/gnupg" |
|
21 |
- |
|
22 |
-src_unpack() { |
|
23 |
- unpack ${A} |
|
24 |
- cp ${FILESDIR}/duplicity-0.4.2-timeout-fixed-backends.py ${S}/src/backends.py |
|
25 |
-} |
|
26 |
- |
|
27 |
-src_compile() { |
|
28 |
- distutils_src_compile |
|
29 |
-} |
|
30 |
- |
|
31 |
-src_install() { |
|
32 |
- python setup.py install --prefix=${D}/usr |
|
33 |
-} |
|
34 |
- |
|
35 |
-pkg_postinst() { |
|
36 |
- python_version |
|
37 |
- python_mod_optimize /usr/lib/python${PYVER}/site-packages/duplicity |
|
38 |
-} |
|
39 |
- |
|
40 |
-pkg_postrm() { |
|
41 |
- python_version |
|
42 |
- python_mod_cleanup |
|
43 |
-} |
... | ... |
@@ -1,624 +0,0 @@ |
1 |
-# Copyright 2002 Ben Escoto |
|
2 |
-# |
|
3 |
-# This file is part of duplicity. |
|
4 |
-# |
|
5 |
-# Duplicity is free software; you can redistribute it and/or modify it |
|
6 |
-# under the terms of the GNU General Public License as published by the |
|
7 |
-# Free Software Foundation; either version 2 of the License, or (at your |
|
8 |
-# option) any later version. |
|
9 |
-# |
|
10 |
-# Duplicity is distributed in the hope that it will be useful, but |
|
11 |
-# WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 |
-# General Public License for more details. |
|
14 |
-# |
|
15 |
-# You should have received a copy of the GNU General Public License |
|
16 |
-# along with duplicity; if not, write to the Free Software Foundation, |
|
17 |
-# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
18 |
- |
|
19 |
-"""Provides functions and classes for getting/sending files to destination""" |
|
20 |
- |
|
21 |
-import os, types, ftplib, tempfile, time, sys |
|
22 |
-import log, path, dup_temp, file_naming |
|
23 |
-import socket |
|
24 |
- |
|
25 |
-# TODO: move into globals? |
|
26 |
-socket.setdefaulttimeout(10) |
|
27 |
- |
|
28 |
-class BackendException(Exception): pass |
|
29 |
-class ParsingException(Exception): pass |
|
30 |
- |
|
31 |
-def get_backend(url_string): |
|
32 |
- """Return Backend object from url string, or None if not a url string |
|
33 |
- |
|
34 |
- url strings are like |
|
35 |
- scp://foobar:password@hostname.net:124/usr/local. If a protocol |
|
36 |
- is unsupported a fatal error will be raised. |
|
37 |
- |
|
38 |
- """ |
|
39 |
- global protocol_class_dict |
|
40 |
- try: pu = ParsedUrl(url_string) |
|
41 |
- except ParsingException: return None |
|
42 |
- |
|
43 |
- try: backend_class = protocol_class_dict[pu.protocol] |
|
44 |
- except KeyError: log.FatalError("Unknown protocol '%s'" % (pu.protocol,)) |
|
45 |
- return backend_class(pu) |
|
46 |
- |
|
47 |
-class ParsedUrl: |
|
48 |
- """Contains information gleaned from a generic url""" |
|
49 |
- protocol = None # set to string like "ftp" indicating protocol |
|
50 |
- suffix = None # Set to everything after protocol:// |
|
51 |
- |
|
52 |
- server = None # First part of suffix (part before '/') |
|
53 |
- path = None # Second part of suffix (part after '/') |
|
54 |
- |
|
55 |
- host = None # Set to host, if can be extracted |
|
56 |
- user = None # Set to user, as in ftp://user@host/whatever |
|
57 |
- port = None # Set to port, like scp://host:port/foo |
|
58 |
- |
|
59 |
- def __init__(self, url_string): |
|
60 |
- """Create ParsedUrl object, process url_string""" |
|
61 |
- self.url_string = url_string |
|
62 |
- self.set_protocol_suffix() |
|
63 |
- self.set_server_path() |
|
64 |
- self.set_host_user_port() |
|
65 |
- |
|
66 |
- def bad_url(self, message = None): |
|
67 |
- """Report a bad url, using message if given""" |
|
68 |
- if message: |
|
69 |
- err_string = "Bad URL string '%s': %s" % (self.url_string, message) |
|
70 |
- else: err_string = "Bad URL string '%s'" % (self.url_string,) |
|
71 |
- raise ParsingException(err_string) |
|
72 |
- |
|
73 |
- def set_protocol_suffix(self): |
|
74 |
- """Parse self.url_string, setting self.protocol and self.suffix""" |
|
75 |
- colon_position = self.url_string.find(":") |
|
76 |
- if colon_position < 1: self.bad_url("No colon (:) found") |
|
77 |
- self.protocol = self.url_string[:colon_position] |
|
78 |
- if self.url_string[colon_position+1:colon_position+3] != '//': |
|
79 |
- self.bad_url("first colon not followed by '//'") |
|
80 |
- self.suffix = self.url_string[colon_position+3:] |
|
81 |
- |
|
82 |
- def set_server_path(self): |
|
83 |
- """Set self.server and self.path from self.suffix""" |
|
84 |
- comps = self.suffix.split('/') |
|
85 |
- assert len(comps) > 0 |
|
86 |
- self.server = comps[0] |
|
87 |
- if len(comps) > 1: |
|
88 |
- self.path = '/'.join(comps[1:]) |
|
89 |
- |
|
90 |
- def set_host_user_port(self): |
|
91 |
- """Set self.host, self.user, and self.port from self.server""" |
|
92 |
- if not self.server: return |
|
93 |
- |
|
94 |
- # Extract port |
|
95 |
- port_comps = self.server.split(":") |
|
96 |
- if len(port_comps) >= 2: |
|
97 |
- try: self.port = int(port_comps[-1]) |
|
98 |
- except ValueError: user_host = self.server |
|
99 |
- else: user_host = ":".join(port_comps[:-1]) |
|
100 |
- else: user_host = self.server |
|
101 |
- |
|
102 |
- # Set user and host |
|
103 |
- user_comps = user_host.split("@") |
|
104 |
- if len(user_comps) >= 2: |
|
105 |
- self.user = user_comps[0] |
|
106 |
- self.host = "@".join(user_comps[1:]) |
|
107 |
- else: self.host = user_host |
|
108 |
- |
|
109 |
- |
|
110 |
-class Backend: |
|
111 |
- """Represent a connection to the destination device/computer |
|
112 |
- |
|
113 |
- Classes that subclass this should implement the put, get, list, |
|
114 |
- and delete methods. |
|
115 |
- |
|
116 |
- """ |
|
117 |
- def put(self, source_path, remote_filename = None): |
|
118 |
- """Transfer source_path (Path object) to remote_filename (string) |
|
119 |
- |
|
120 |
- If remote_filename is None, get the filename from the last |
|
121 |
- path component of pathname. |
|
122 |
- |
|
123 |
- """ |
|
124 |
- if not remote_filename: remote_filename = source_path.get_filename() |
|
125 |
- pass |
|
126 |
- |
|
127 |
- def get(self, remote_filename, local_path): |
|
128 |
- """Retrieve remote_filename and place in local_path""" |
|
129 |
- local_path.setdata() |
|
130 |
- pass |
|
131 |
- |
|
132 |
- def list(self): |
|
133 |
- """Return list of filenames (strings) present in backend""" |
|
134 |
- pass |
|
135 |
- |
|
136 |
- def delete(self, filename_list): |
|
137 |
- """Delete each filename in filename_list, in order if possible""" |
|
138 |
- pass |
|
139 |
- |
|
140 |
- def run_command(self, commandline): |
|
141 |
- """Run given commandline with logging and error detection""" |
|
142 |
- log.Log("Running '%s'" % commandline, 5) |
|
143 |
- if os.system(commandline): |
|
144 |
- raise BackendException("Error running '%s'" % commandline) |
|
145 |
- |
|
146 |
- def popen(self, commandline): |
|
147 |
- """Run command and return stdout results""" |
|
148 |
- log.Log("Reading results of '%s'" % commandline, 5) |
|
149 |
- fout = os.popen(commandline) |
|
150 |
- results = fout.read() |
|
151 |
- if fout.close(): |
|
152 |
- raise BackendException("Error running '%s'" % commandline) |
|
153 |
- return results |
|
154 |
- |
|
155 |
- def get_fileobj_read(self, filename, parseresults = None): |
|
156 |
- """Return fileobject opened for reading of filename on backend |
|
157 |
- |
|
158 |
- The file will be downloaded first into a temp file. When the |
|
159 |
- returned fileobj is closed, the temp file will be deleted. |
|
160 |
- |
|
161 |
- """ |
|
162 |
- if not parseresults: |
|
163 |
- parseresults = file_naming.parse(filename) |
|
164 |
- assert parseresults, "Filename not correctly parsed" |
|
165 |
- tdp = dup_temp.new_tempduppath(parseresults) |
|
166 |
- self.get(filename, tdp) |
|
167 |
- tdp.setdata() |
|
168 |
- return tdp.filtered_open_with_delete("rb") |
|
169 |
- |
|
170 |
- def get_fileobj_write(self, filename, parseresults = None, |
|
171 |
- sizelist = None): |
|
172 |
- """Return fileobj opened for writing, write to backend on close |
|
173 |
- |
|
174 |
- The file will be encoded as specified in parseresults (or as |
|
175 |
- read from the filename), and stored in a temp file until it |
|
176 |
- can be copied over and deleted. |
|
177 |
- |
|
178 |
- If sizelist is not None, it should be set to an empty list. |
|
179 |
- The number of bytes will be inserted into the list. |
|
180 |
- |
|
181 |
- """ |
|
182 |
- if not parseresults: |
|
183 |
- parseresults = file_naming.parse(filename) |
|
184 |
- assert parseresults, "Filename %s not correctly parsed" % filename |
|
185 |
- tdp = dup_temp.new_tempduppath(parseresults) |
|
186 |
- |
|
187 |
- def close_file_hook(): |
|
188 |
- """This is called when returned fileobj is closed""" |
|
189 |
- self.put(tdp, filename) |
|
190 |
- if sizelist is not None: |
|
191 |
- tdp.setdata() |
|
192 |
- sizelist.append(tdp.getsize()) |
|
193 |
- tdp.delete() |
|
194 |
- |
|
195 |
- fh = dup_temp.FileobjHooked(tdp.filtered_open("wb")) |
|
196 |
- fh.addhook(close_file_hook) |
|
197 |
- return fh |
|
198 |
- |
|
199 |
- def get_data(self, filename, parseresults = None): |
|
200 |
- """Retrieve a file from backend, process it, return contents""" |
|
201 |
- fin = self.get_fileobj_read(filename, parseresults) |
|
202 |
- buf = fin.read() |
|
203 |
- assert not fin.close() |
|
204 |
- return buf |
|
205 |
- |
|
206 |
- def put_data(self, buffer, filename, parseresults = None): |
|
207 |
- """Put buffer into filename on backend after processing""" |
|
208 |
- fout = self.get_fileobj_write(filename, parseresults) |
|
209 |
- fout.write(buffer) |
|
210 |
- assert not fout.close() |
|
211 |
- |
|
212 |
- def close(self): |
|
213 |
- """This is called when a connection is no longer needed""" |
|
214 |
- pass |
|
215 |
- |
|
216 |
- |
|
217 |
-class LocalBackend(Backend): |
|
218 |
- """Use this backend when saving to local disk |
|
219 |
- |
|
220 |
- Urls look like file://testfiles/output. Relative to root can be |
|
221 |
- gotten with extra slash (file:///usr/local). |
|
222 |
- |
|
223 |
- """ |
|
224 |
- def __init__(self, parsed_url): |
|
225 |
- self.remote_pathdir = path.Path(parsed_url.suffix) |
|
226 |
- |
|
227 |
- def put(self, source_path, remote_filename = None, rename = None): |
|
228 |
- """If rename is set, try that first, copying if doesn't work""" |
|
229 |
- if not remote_filename: remote_filename = source_path.get_filename() |
|
230 |
- target_path = self.remote_pathdir.append(remote_filename) |
|
231 |
- log.Log("Writing %s" % target_path.name, 6) |
|
232 |
- if rename: |
|
233 |
- try: source_path.rename(target_path) |
|
234 |
- except OSError: pass |
|
235 |
- else: return |
|
236 |
- target_path.writefileobj(source_path.open("rb")) |
|
237 |
- |
|
238 |
- def get(self, filename, local_path): |
|
239 |
- """Get file and put in local_path (Path object)""" |
|
240 |
- source_path = self.remote_pathdir.append(filename) |
|
241 |
- local_path.writefileobj(source_path.open("rb")) |
|
242 |
- |
|
243 |
- def list(self): |
|
244 |
- """List files in that directory""" |
|
245 |
- return self.remote_pathdir.listdir() |
|
246 |
- |
|
247 |
- def delete(self, filename_list): |
|
248 |
- """Delete all files in filename list""" |
|
249 |
- assert type(filename_list) is not types.StringType |
|
250 |
- try: |
|
251 |
- for filename in filename_list: |
|
252 |
- self.remote_pathdir.append(filename).delete() |
|
253 |
- except OSError, e: raise BackendException(str(e)) |
|
254 |
- |
|
255 |
- |
|
256 |
-# The following can be redefined to use different shell commands from |
|
257 |
-# ssh or scp or to add more arguments. However, the replacements must |
|
258 |
-# have the same syntax. Also these strings will be executed by the |
|
259 |
-# shell, so shouldn't have strange characters in them. |
|
260 |
-ssh_command = "ssh" |
|
261 |
-scp_command = "scp" |
|
262 |
-sftp_command = "sftp" |
|
263 |
- |
|
264 |
-class scpBackend(Backend): |
|
265 |
- """This backend copies files using scp. List not supported""" |
|
266 |
- def __init__(self, parsed_url): |
|
267 |
- """scpBackend initializer""" |
|
268 |
- self.host_string = parsed_url.server # of form user@hostname:port |
|
269 |
- self.remote_dir = parsed_url.path # can be empty string |
|
270 |
- if self.remote_dir: self.remote_prefix = self.remote_dir + "/" |
|
271 |
- else: self.remote_prefix = "" |
|
272 |
- |
|
273 |
- def put(self, source_path, remote_filename = None): |
|
274 |
- """Use scp to copy source_dir/filename to remote computer""" |
|
275 |
- if not remote_filename: remote_filename = source_path.get_filename() |
|
276 |
- commandline = "%s %s %s:%s%s" % \ |
|
277 |
- (scp_command, source_path.name, self.host_string, |
|
278 |
- self.remote_prefix, remote_filename) |
|
279 |
- self.run_command(commandline) |
|
280 |
- |
|
281 |
- def get(self, remote_filename, local_path): |
|
282 |
- """Use scp to get a remote file""" |
|
283 |
- commandline = "%s %s:%s%s %s" % \ |
|
284 |
- (scp_command, self.host_string, self.remote_prefix, |
|
285 |
- remote_filename, local_path.name) |
|
286 |
- self.run_command(commandline) |
|
287 |
- local_path.setdata() |
|
288 |
- if not local_path.exists(): |
|
289 |
- raise BackendException("File %s not found" % local_path.name) |
|
290 |
- |
|
291 |
- def list(self): |
|
292 |
- """List files available for scp |
|
293 |
- |
|
294 |
- Note that this command can get confused when dealing with |
|
295 |
- files with newlines in them, as the embedded newlines cannot |
|
296 |
- be distinguished from the file boundaries. |
|
297 |
- |
|
298 |
- """ |
|
299 |
- commandline = ("echo -e 'cd %s\nls -1' | %s -b - %s" % |
|
300 |
- (self.remote_dir, sftp_command, self.host_string)) |
|
301 |
- l = self.popen(commandline).split('\n')[2:] # omit sftp prompts |
|
302 |
- return filter(lambda x: x, l) |
|
303 |
- |
|
304 |
- def delete(self, filename_list): |
|
305 |
- """Runs ssh rm to delete files. Files must not require quoting""" |
|
306 |
- assert len(filename_list) > 0 |
|
307 |
- pathlist = map(lambda fn: self.remote_prefix + fn, filename_list) |
|
308 |
- del_prefix = "echo 'rm " |
|
309 |
- del_postfix = ("' | %s -b - %s 1>/dev/null" % |
|
310 |
- (sftp_command, self.host_string)) |
|
311 |
- for fn in filename_list: |
|
312 |
- commandline = del_prefix + self.remote_prefix + fn + del_postfix |
|
313 |
- self.run_command(commandline) |
|
314 |
- |
|
315 |
- |
|
316 |
-class sftpBackend(Backend): |
|
317 |
- """This backend uses sftp to perform file operations""" |
|
318 |
- pass # Do this later |
|
319 |
- |
|
320 |
- |
|
321 |
-class ftpBackend(Backend): |
|
322 |
- """Connect to remote store using File Transfer Protocol""" |
|
323 |
- RETRY_SLEEP = 10 # time in seconds before reconnecting on errors (gets multiplied with the try counter) |
|
324 |
- RETRIES = 15 # number of retries |
|
325 |
- |
|
326 |
- def __init__(self, parsed_url): |
|
327 |
- """Create a new ftp backend object, log in to host""" |
|
328 |
- self.parsed_url = parsed_url |
|
329 |
- self.connect() |
|
330 |
- |
|
331 |
- def connect(self): |
|
332 |
- """Connect to self.parsed_url""" |
|
333 |
- self.ftp = ftplib.FTP() |
|
334 |
- self.is_connected = False |
|
335 |
- if self.parsed_url.port is None: |
|
336 |
- self.error_wrap('connect', self.parsed_url.host) |
|
337 |
- else: self.error_wrap('connect', self.parsed_url.host, |
|
338 |
- self.parsed_url.port) |
|
339 |
- self.is_connected = True |
|
340 |
- |
|
341 |
- if self.parsed_url.user is not None: |
|
342 |
- self.error_wrap('login', self.parsed_url.user, self.get_password()) |
|
343 |
- else: self.error_wrap('login') |
|
344 |
- self.ftp.cwd(self.parsed_url.path) |
|
345 |
- |
|
346 |
- def error_wrap(self, command, *args): |
|
347 |
- """Run self.ftp.command(*args), but raise BackendException on error""" |
|
348 |
- |
|
349 |
- # Log FTP command: |
|
350 |
- if command is 'login': |
|
351 |
- if log.verbosity > 8: |
|
352 |
- # Log full args at level 9: |
|
353 |
- log.Log("FTP: %s %s" % (command,args), 9) |
|
354 |
- else: |
|
355 |
- # replace password with stars: |
|
356 |
- log_args = list(args) |
|
357 |
- log_args[1] = '*' * len(log_args[1]) |
|
358 |
- log.Log("FTP: %s %s" % (command,log_args), 5) |
|
359 |
- else: |
|
360 |
- log.Log("FTP: %s %s" % (command,args), 5) |
|
361 |
- |
|
362 |
- # Execute: |
|
363 |
- tries = 0 |
|
364 |
- while( True ): |
|
365 |
- tries = tries+1 |
|
366 |
- try: |
|
367 |
- return ftplib.FTP.__dict__[command](self.ftp, *args) |
|
368 |
- except ftplib.all_errors, e: |
|
369 |
- if "450" in str(e) and command == 'nlst': |
|
370 |
- # 450 on list isn't an error, but indicates an empty dir |
|
371 |
- return [] |
|
372 |
- |
|
373 |
- if tries > self.RETRIES: |
|
374 |
- # Give up: |
|
375 |
- log.FatalError("Catched exception %s%s (%d exceptions in total), giving up.." % (sys.exc_info()[0],sys.exc_info()[1],tries,)) |
|
376 |
- raise BackendException(e) |
|
377 |
- |
|
378 |
- # Sleep and retry (after trying to reconnect, if possible): |
|
379 |
- sleep_time = self.RETRY_SLEEP * tries; |
|
380 |
- log.Warn("Catched exception %s%s (#%d), sleeping %ds before retry.." % (sys.exc_info()[0],sys.exc_info()[1],tries,sleep_time,)) |
|
381 |
- time.sleep(sleep_time) |
|
382 |
- try: |
|
383 |
- if self.is_connected: |
|
384 |
- self.connect() |
|
385 |
- return ftplib.FTP.__dict__[command](self.ftp, *args) |
|
386 |
- except ftplib.all_errors, e: |
|
387 |
- continue |
|
388 |
- else: break |
|
389 |
- |
|
390 |
- def get_password(self): |
|
391 |
- """Get ftp password using environment if possible""" |
|
392 |
- try: return os.environ['FTP_PASSWORD'] |
|
393 |
- except KeyError: |
|
394 |
- log.Log("FTP_PASSWORD not set, using empty ftp password", 3) |
|
395 |
- return '' |
|
396 |
- |
|
397 |
- def put(self, source_path, remote_filename = None): |
|
398 |
- """Transfer source_path to remote_filename""" |
|
399 |
- if not remote_filename: remote_filename = source_path.get_filename() |
|
400 |
- source_file = source_path.open("rb") |
|
401 |
- log.Log("Saving %s on FTP server" % (remote_filename,), 5) |
|
402 |
- self.error_wrap('storbinary', "STOR "+remote_filename, source_file) |
|
403 |
- assert not source_file.close() |
|
404 |
- |
|
405 |
- def get(self, remote_filename, local_path): |
|
406 |
- """Get remote filename, saving it to local_path""" |
|
407 |
- target_file = local_path.open("wb") |
|
408 |
- log.Log("Retrieving %s from FTP server" % (remote_filename,), 5) |
|
409 |
- self.error_wrap('retrbinary', "RETR "+remote_filename, |
|
410 |
- target_file.write) |
|
411 |
- assert not target_file.close() |
|
412 |
- local_path.setdata() |
|
413 |
- |
|
414 |
- def list(self): |
|
415 |
- """List files in directory""" |
|
416 |
- log.Log("Listing files on FTP server", 5) |
|
417 |
- # Some ftp servers raise error 450 if the directory is empty |
|
418 |
- try: return self.error_wrap('nlst') |
|
419 |
- except BackendException, e: |
|
420 |
- if "450" in str(e) or "500" in str(e) or "550" in str(e): |
|
421 |
- return [] |
|
422 |
- raise |
|
423 |
- |
|
424 |
- def delete(self, filename_list): |
|
425 |
- """Delete files in filename_list""" |
|
426 |
- for filename in filename_list: |
|
427 |
- log.Log("Deleting %s from FTP server" % (filename,), 5) |
|
428 |
- self.error_wrap('delete', filename) |
|
429 |
- |
|
430 |
- def close(self): |
|
431 |
- """Shut down connection""" |
|
432 |
- try: self.error_wrap('quit') |
|
433 |
- except BackendException, e: |
|
434 |
- if "104" in str(e): return |
|
435 |
- raise |
|
436 |
- |
|
437 |
- |
|
438 |
-class rsyncBackend(Backend): |
|
439 |
- """Connect to remote store using rsync |
|
440 |
- |
|
441 |
- rsync backend contributed by Sebastian Wilhelmi <seppi@seppi.de> |
|
442 |
- |
|
443 |
- """ |
|
444 |
- def __init__(self, parsed_url): |
|
445 |
- """rsyncBackend initializer""" |
|
446 |
- self.url_string = parsed_url.url_string |
|
447 |
- if self.url_string[-1] != '/': |
|
448 |
- self.url_string += '/' |
|
449 |
- |
|
450 |
- def put(self, source_path, remote_filename = None): |
|
451 |
- """Use rsync to copy source_dir/filename to remote computer""" |
|
452 |
- if not remote_filename: remote_filename = source_path.get_filename() |
|
453 |
- remote_path = os.path.join (self.url_string, remote_filename) |
|
454 |
- commandline = "rsync %s %s" % (source_path.name, remote_path) |
|
455 |
- self.run_command(commandline) |
|
456 |
- |
|
457 |
- def get(self, remote_filename, local_path): |
|
458 |
- """Use rsync to get a remote file""" |
|
459 |
- remote_path = os.path.join (self.url_string, remote_filename) |
|
460 |
- commandline = "rsync %s %s" % (remote_path, local_path.name) |
|
461 |
- self.run_command(commandline) |
|
462 |
- local_path.setdata() |
|
463 |
- if not local_path.exists(): |
|
464 |
- raise BackendException("File %s not found" % local_path.name) |
|
465 |
- |
|
466 |
- def list(self): |
|
467 |
- """List files""" |
|
468 |
- def split (str): |
|
469 |
- line = str.split () |
|
470 |
- if len (line) > 4 and line[4] != '.': |
|
471 |
- return line[4] |
|
472 |
- else: |
|
473 |
- return None |
|
474 |
- commandline = "rsync %s" % self.url_string |
|
475 |
- return filter (lambda x: x, map (split, self.popen(commandline).split('\n'))) |
|
476 |
- |
|
477 |
- def delete(self, filename_list): |
|
478 |
- """Delete files.""" |
|
479 |
- delete_list = filename_list |
|
480 |
- dont_delete_list = [] |
|
481 |
- for file in self.list (): |
|
482 |
- if file in delete_list: |
|
483 |
- delete_list.remove (file) |
|
484 |
- else: |
|
485 |
- dont_delete_list.append (file) |
|
486 |
- if len (delete_list) > 0: |
|
487 |
- raise BackendException("Files %s not found" % str (delete_list)) |
|
488 |
- |
|
489 |
- dir = tempfile.mktemp () |
|
490 |
- exclude_name = tempfile.mktemp () |
|
491 |
- exclude = open (exclude_name, 'w') |
|
492 |
- to_delete = [exclude_name] |
|
493 |
- os.mkdir (dir) |
|
494 |
- for file in dont_delete_list: |
|
495 |
- path = os.path.join (dir, file) |
|
496 |
- to_delete.append (path) |
|
497 |
- f = open (path, 'w') |
|
498 |
- f.close () |
|
499 |
- print >>exclude, file |
|
500 |
- exclude.close () |
|
501 |
- commandline = ("rsync --recursive --delete --exclude-from=%s %s/ %s" % |
|
502 |
- (exclude_name, dir, self.url_string)) |
|
503 |
- self.run_command(commandline) |
|
504 |
- for file in to_delete: |
|
505 |
- os.unlink (file) |
|
506 |
- os.rmdir (dir) |
|
507 |
- |
|
508 |
- |
|
509 |
-class BitBucketBackend(Backend): |
|
510 |
- """Backend for accessing Amazon S3 using the bitbucket.py module. |
|
511 |
- |
|
512 |
- This backend supports access to Amazon S3 (http://aws.amazon.com/s3) |
|
513 |
- using a mix of environment variables and URL's. The access key and |
|
514 |
- secret key are taken from the environment variables S3KEY and S3SECRET |
|
515 |
- and the bucket name from the url. For example (in BASH): |
|
516 |
- |
|
517 |
- $ export S3KEY='44CF9590006BF252F707' |
|
518 |
- $ export S3SECRET='OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV' |
|
519 |
- $ duplicity /home/me s3+http://bucket_name |
|
520 |
- |
|
521 |
- Note: / is disallowed in bucket names in case prefix support is implemented |
|
522 |
- in future. |
|
523 |
- |
|
524 |
- TODO: |
|
525 |
- - support bucket prefixes with url's like s3+http://bucket_name/prefix |
|
526 |
- - bitbucket and amazon s3 are currently not very robust. We provide a |
|
527 |
- simplistic way of trying to re-connect and re-try an operation when |
|
528 |
- it fails. This is just a band-aid and should be removed if bitbucket |
|
529 |
- becomes more robust. |
|
530 |
- - Logging of actions. |
|
531 |
- - Better error messages for failures. |
|
532 |
- """ |
|
533 |
- |
|
534 |
- def __init__(self, parsed_url): |
|
535 |
- import bitbucket |
|
536 |
- self.module = bitbucket |
|
537 |
- self.bucket_name = parsed_url.suffix |
|
538 |
- if '/' in self.bucket_name: |
|
539 |
- raise NotImplementedError("/ disallowed in bucket names and " |
|
540 |
- "bucket prefixes not supported.") |
|
541 |
- self.access_key = os.environ["S3KEY"] |
|
542 |
- self.secret_key = os.environ["S3SECRET"] |
|
543 |
- self._connect() |
|
544 |
- |
|
545 |
- def _connect(self): |
|
546 |
- self.connection = self.module.connect(access_key=self.access_key, |
|
547 |
- secret_key=self.secret_key) |
|
548 |
- self.bucket = self.connection.get_bucket(self.bucket_name) |
|
549 |
- # populate the bitbucket cache we do it here to be sure that |
|
550 |
- # even on re-connect we have a list of all keys on the server |
|
551 |
- self.bucket.fetch_all_keys() |
|
552 |
- |
|
553 |
- def _logException(self, message=None): |
|
554 |
- # Simply dump the exception onto stderr since formatting it |
|
555 |
- # ourselves looks dangerous. |
|
556 |
- if message is not None: |
|
557 |
- sys.stderr.write(message) |
|
558 |
- sys.excepthook(*sys.exc_info()) |
|
559 |
- |
|
560 |
- def put(self, source_path, remote_filename = None): |
|
561 |
- """Transfer source_path (Path object) to remote_filename (string) |
|
562 |
- |
|
563 |
- If remote_filename is None, get the filename from the last |
|
564 |
- path component of pathname. |
|
565 |
- |
|
566 |
- """ |
|
567 |
- if not remote_filename: |
|
568 |
- remote_filename = source_path.get_filename() |
|
569 |
- bits = self.module.Bits(filename=source_path.name) |
|
570 |
- try: |
|
571 |
- self.bucket[remote_filename] = bits |
|
572 |
- except: |
|
573 |
- self._logException("Error sending file %s, attempting to " |
|
574 |
- "re-connect.\n Got this Traceback:\n" |
|
575 |
- % remote_filename) |
|
576 |
- self._connect() |
|
577 |
- self.bucket[remote_filename] = bits |
|
578 |
- |
|
579 |
- def get(self, remote_filename, local_path): |
|
580 |
- """Retrieve remote_filename and place in local_path""" |
|
581 |
- local_path.setdata() |
|
582 |
- try: |
|
583 |
- bits = self.bucket[remote_filename] |
|
584 |
- bits.to_file(local_path.name) |
|
585 |
- except: |
|
586 |
- self._logException("Error getting file %s, attempting to " |
|
587 |
- "re-connect.\n Got this Traceback:\n" |
|
588 |
- % remote_filename) |
|
589 |
- self._connect() |
|
590 |
- bits = self.bucket[remote_filename] |
|
591 |
- bits.to_file(local_path.name) |
|
592 |
- local_path.setdata() |
|
593 |
- |
|
594 |
- def list(self): |
|
595 |
- """Return list of filenames (strings) present in backend""" |
|
596 |
- try: |
|
597 |
- keys = self.bucket.keys() |
|
598 |
- except: |
|
599 |
- self._logException("Error getting bucket keys, attempting to " |
|
600 |
- "re-connect.\n Got this Traceback:\n") |
|
601 |
- self._connect() |
|
602 |
- keys = self.bucket.keys() |
|
603 |
- return keys |
|
604 |
- |
|
605 |
- def delete(self, filename_list): |
|
606 |
- """Delete each filename in filename_list, in order if possible""" |
|
607 |
- for file in filename_list: |
|
608 |
- try: |
|
609 |
- del self.bucket[file] |
|
610 |
- except: |
|
611 |
- self._logException("Error deleting file %s, attempting to " |
|
612 |
- "re-connect.\n Got this Traceback:\n" |
|
613 |
- % file) |
|
614 |
- self._connect() |
|
615 |
- del self.bucket[file] |
|
616 |
- |
|
617 |
-# Dictionary relating protocol strings to backend_object classes. |
|
618 |
-protocol_class_dict = {"scp": scpBackend, |
|
619 |
- "ssh": scpBackend, |
|
620 |
- "file": LocalBackend, |
|
621 |
- "ftp": ftpBackend, |
|
622 |
- "rsync": rsyncBackend, |
|
623 |
- "s3+http": BitBucketBackend} |
|
624 |
- |
... | ... |
@@ -56,6 +56,7 @@ RDEPEND=">=dev-python/twisted-1.3.0 |
56 | 56 |
>=dev-python/nevow-0.4.1 |
57 | 57 |
>=dev-python/imaging-1.1" |
58 | 58 |
|
59 |
+PYTRANSPORT_CONFIG=config_example.xml |
|
59 | 60 |
|
60 | 61 |
|
61 | 62 |
|
... | ... |
@@ -105,7 +106,6 @@ pytransport_install_libs() { |
105 | 106 |
return 0 |
106 | 107 |
} |
107 | 108 |
|
108 |
-PYTRANSPORT_CONFIG=config_example.xml |
|
109 | 109 |
|
110 | 110 |
pytransport_install_config() { |
111 | 111 |
insinto /etc/jabber |
112 | 112 |