Merge lp:~jkrauss/duplicity/pyrax into lp:duplicity/0.6
- pyrax
- Merge into 0.6-series
Status: | Merged |
---|---|
Merged at revision: | 938 |
Proposed branch: | lp:~jkrauss/duplicity/pyrax |
Merge into: | lp:duplicity/0.6 |
Diff against target: |
251 lines (+214/-4) 2 files modified
bin/duplicity.1 (+20/-4) duplicity/backends/pyraxbackend.py (+194/-0) |
To merge this branch: | bzr merge lp:~jkrauss/duplicity/pyrax |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
duplicity-team | Pending | ||
Review via email:
|
Commit message
Description of the change
Rackspace has deprecated python-cloudfiles in favor of their pyrax
library, which consolidates all Rackspace Cloud API functionality into
a single library. Attached is a simple backend for pyrax, ported over
from cloudfiles. I tested it with Duplicity 0.6.21 on both Arch Linux
and FreeBSD 8.3.0.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
edso (ed.so) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Kenneth Loafman (kenneth-loafman) wrote : | # |
There really is no legacy issue here. I'm thinking of just deprecating the
cloudfiles backend later, leaving it in for a couple of releases, but
removing it fairly soon. If folks have trouble using the old cloudfiles
backend, they can switch sooner.
On Sun, Nov 24, 2013 at 6:41 AM, edso <email address hidden> wrote:
> we should probably integrate it similar to the ssh backends with a command
> line switch. this way it'll become clear that
>
> - the backends are for the same protocol/service
> - one is deprecated but still selectable for legacy compatibility
> - old users will be switched to the new backend automatically at some point
>
> ..ede
>
> --
> https:/
> You proposed lp:~jkrauss/duplicity/pyrax for merging.
>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
edso (ed.so) wrote : | # |
i remember you saying somthig similar when introducing the new ssh backend ;) .. ede
On 24.11.2013 15:39, Kenneth Loafman wrote:
> There really is no legacy issue here. I'm thinking of just deprecating the
> cloudfiles backend later, leaving it in for a couple of releases, but
> removing it fairly soon. If folks have trouble using the old cloudfiles
> backend, they can switch sooner.
>
>
>
> On Sun, Nov 24, 2013 at 6:41 AM, edso <email address hidden> wrote:
>
>> we should probably integrate it similar to the ssh backends with a command
>> line switch. this way it'll become clear that
>>
>> - the backends are for the same protocol/service
>> - one is deprecated but still selectable for legacy compatibility
>> - old users will be switched to the new backend automatically at some point
>>
>> ..ede
>>
>> --
>> https:/
>> You proposed lp:~jkrauss/duplicity/pyrax for merging.
>>
>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Kenneth Loafman (kenneth-loafman) wrote : | # |
With the ssh backend we had two backends registering the same protocols.
In this case the protocols are different (cf+http and cfpyrax+http). We
could have done the same for ssh, i.e. leave the original as 'ssh' and the
new one as 'sshparamiko'. That would be less complexity than the solution
we have (no option needed).
On Sun, Nov 24, 2013 at 8:51 AM, edso <email address hidden> wrote:
> i remember you saying somthig similar when introducing the new ssh backend
> ;) .. ede
>
> On 24.11.2013 15:39, Kenneth Loafman wrote:
> > There really is no legacy issue here. I'm thinking of just deprecating
> the
> > cloudfiles backend later, leaving it in for a couple of releases, but
> > removing it fairly soon. If folks have trouble using the old cloudfiles
> > backend, they can switch sooner.
> >
> >
> >
> > On Sun, Nov 24, 2013 at 6:41 AM, edso <email address hidden> wrote:
> >
> >> we should probably integrate it similar to the ssh backends with a
> command
> >> line switch. this way it'll become clear that
> >>
> >> - the backends are for the same protocol/service
> >> - one is deprecated but still selectable for legacy compatibility
> >> - old users will be switched to the new backend automatically at some
> point
> >>
> >> ..ede
> >>
> >> --
> >> https:/
> >> You proposed lp:~jkrauss/duplicity/pyrax for merging.
> >>
> >
>
> --
> https:/
> You proposed lp:~jkrauss/duplicity/pyrax for merging.
>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
edso (ed.so) wrote : | # |
i' argue it's till the same protocol 'cloudfiles stack'.. or did they change their API's as well?
i see why you think it's easier this way, having an option is just more elegant. users would use the new backend automatically, when we'd switch the default in the future.
your call.. just my 2 cents.. ede
On 24.11.2013 16:30, Kenneth Loafman wrote:
> With the ssh backend we had two backends registering the same protocols.
> In this case the protocols are different (cf+http and cfpyrax+http). We
> could have done the same for ssh, i.e. leave the original as 'ssh' and the
> new one as 'sshparamiko'. That would be less complexity than the solution
> we have (no option needed).
>
>
>
>
> On Sun, Nov 24, 2013 at 8:51 AM, edso <email address hidden> wrote:
>
>> i remember you saying somthig similar when introducing the new ssh backend
>> ;) .. ede
>>
>> On 24.11.2013 15:39, Kenneth Loafman wrote:
>>> There really is no legacy issue here. I'm thinking of just deprecating
>> the
>>> cloudfiles backend later, leaving it in for a couple of releases, but
>>> removing it fairly soon. If folks have trouble using the old cloudfiles
>>> backend, they can switch sooner.
>>>
>>>
>>>
>>> On Sun, Nov 24, 2013 at 6:41 AM, edso <email address hidden> wrote:
>>>
>>>> we should probably integrate it similar to the ssh backends with a
>> command
>>>> line switch. this way it'll become clear that
>>>>
>>>> - the backends are for the same protocol/service
>>>> - one is deprecated but still selectable for legacy compatibility
>>>> - old users will be switched to the new backend automatically at some
>> point
>>>>
>>>> ..ede
>>>>
>>>> --
>>>> https:/
>>>> You proposed lp:~jkrauss/duplicity/pyrax for merging.
>>>>
>>>
>>
>> --
>> https:/
>> You proposed lp:~jkrauss/duplicity/pyrax for merging.
>>
>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Kenneth Loafman (kenneth-loafman) wrote : | # |
OK. For consistency I'll make the change. Really not a problem.
On Sun, Nov 24, 2013 at 9:36 AM, edso <email address hidden> wrote:
> i' argue it's till the same protocol 'cloudfiles stack'.. or did they
> change their API's as well?
>
> i see why you think it's easier this way, having an option is just more
> elegant. users would use the new backend automatically, when we'd switch
> the default in the future.
>
> your call.. just my 2 cents.. ede
>
> On 24.11.2013 16:30, Kenneth Loafman wrote:
> > With the ssh backend we had two backends registering the same protocols.
> > In this case the protocols are different (cf+http and cfpyrax+http). We
> > could have done the same for ssh, i.e. leave the original as 'ssh' and
> the
> > new one as 'sshparamiko'. That would be less complexity than the
> solution
> > we have (no option needed).
> >
> >
> >
> >
> > On Sun, Nov 24, 2013 at 8:51 AM, edso <email address hidden> wrote:
> >
> >> i remember you saying somthig similar when introducing the new ssh
> backend
> >> ;) .. ede
> >>
> >> On 24.11.2013 15:39, Kenneth Loafman wrote:
> >>> There really is no legacy issue here. I'm thinking of just deprecating
> >> the
> >>> cloudfiles backend later, leaving it in for a couple of releases, but
> >>> removing it fairly soon. If folks have trouble using the old
> cloudfiles
> >>> backend, they can switch sooner.
> >>>
> >>>
> >>>
> >>> On Sun, Nov 24, 2013 at 6:41 AM, edso <email address hidden> wrote:
> >>>
> >>>> we should probably integrate it similar to the ssh backends with a
> >> command
> >>>> line switch. this way it'll become clear that
> >>>>
> >>>> - the backends are for the same protocol/service
> >>>> - one is deprecated but still selectable for legacy compatibility
> >>>> - old users will be switched to the new backend automatically at some
> >> point
> >>>>
> >>>> ..ede
> >>>>
> >>>> --
> >>>> https:/
> >>>> You proposed lp:~jkrauss/duplicity/pyrax for merging.
> >>>>
> >>>
> >>
> >> --
> >> https:/
> >> You proposed lp:~jkrauss/duplicity/pyrax for merging.
> >>
> >
>
> --
> https:/
> You proposed lp:~jkrauss/duplicity/pyrax for merging.
>
Preview Diff
1 | === modified file 'bin/duplicity.1' |
2 | --- bin/duplicity.1 2013-08-18 14:12:02 +0000 |
3 | +++ bin/duplicity.1 2013-11-23 11:28:28 +0000 |
4 | @@ -60,10 +60,14 @@ |
5 | .B boto version 2.0+ |
6 | - http://github.com/boto/boto |
7 | .TP |
8 | -.BR "cloudfiles backend" " (e.g. Rackspace Open Cloud)" |
9 | -.B Cloud Files Python API |
10 | +.BR "cloudfiles backend (deprecated)" " (e.g. Rackspace Open Cloud)" |
11 | +.B Cloud Files Python API (deprecated) |
12 | - http://www.rackspace.com/knowledge_center/article/python-api-installation-for-cloud-files |
13 | .TP |
14 | +.BR "cfpyrax backend" " (Rackspace Cloud)" |
15 | +.B Rackspace CloudFiles Pyrax API |
16 | +- http://docs.rackspace.com/sdks/guide/content/python.html |
17 | +.TP |
18 | .B "dpbx backend" (Dropbox) |
19 | .B Dropbox Python SDK |
20 | - https://www.dropbox.com/developers/reference/sdk |
21 | @@ -982,8 +986,12 @@ |
22 | Formats of each of the URL schemes follow: |
23 | .RS |
24 | .PP |
25 | +.BI "Rackspace Cloud Files" |
26 | +.br |
27 | cf+http://container_name |
28 | .br |
29 | +cfpyrax+http://container_name |
30 | +.br |
31 | See also |
32 | .B "A NOTE ON CLOUD FILES ACCESS" |
33 | .PP |
34 | @@ -1349,8 +1357,16 @@ |
35 | if /home/ben/1234567 existed. |
36 | |
37 | .SH A NOTE ON CLOUD FILES ACCESS |
38 | -Cloudfiles is Rackspace's implementation of OpenStack Object Storage |
39 | -protocol. |
40 | +Pyrax is Rackspace's next-generation Cloud management API, including |
41 | +Cloud Files access. The cfpyrax backend requires the pyrax library to |
42 | +be installed on the system. |
43 | +See |
44 | +.B REQUIREMENTS |
45 | +above. |
46 | + |
47 | +Cloudfiles is Rackspace's now deprecated implementation of OpenStack |
48 | +Object Storage protocol. Users wishing to use Duplicity with Rackspace |
49 | +Cloud Files should migrate to the new Pyrax plugin to ensure support. |
50 | |
51 | The backend requires python-cloudfiles to be installed on the system. |
52 | See |
53 | |
54 | === added file 'duplicity/backends/pyraxbackend.py' |
55 | --- duplicity/backends/pyraxbackend.py 1970-01-01 00:00:00 +0000 |
56 | +++ duplicity/backends/pyraxbackend.py 2013-11-23 11:28:28 +0000 |
57 | @@ -0,0 +1,194 @@ |
58 | +# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- |
59 | +# |
60 | +# Copyright 2013 J.P. Krauss <jkrauss@asymworks.com> |
61 | +# |
62 | +# This file is part of duplicity. |
63 | +# |
64 | +# Duplicity is free software; you can redistribute it and/or modify it |
65 | +# under the terms of the GNU General Public License as published by the |
66 | +# Free Software Foundation; either version 2 of the License, or (at your |
67 | +# option) any later version. |
68 | +# |
69 | +# Duplicity is distributed in the hope that it will be useful, but |
70 | +# WITHOUT ANY WARRANTY; without even the implied warranty of |
71 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
72 | +# General Public License for more details. |
73 | +# |
74 | +# You should have received a copy of the GNU General Public License |
75 | +# along with duplicity; if not, write to the Free Software Foundation, |
76 | +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
77 | + |
78 | +import os |
79 | +import time |
80 | + |
81 | +import duplicity.backend |
82 | +from duplicity import globals |
83 | +from duplicity import log |
84 | +from duplicity.errors import * #@UnusedWildImport |
85 | +from duplicity.util import exception_traceback |
86 | +from duplicity.backend import retry |
87 | + |
88 | +class PyraxBackend(duplicity.backend.Backend): |
89 | + """ |
90 | + Backend for Rackspace's CloudFiles using Pyrax |
91 | + """ |
92 | + def __init__(self, parsed_url): |
93 | + try: |
94 | + import pyrax |
95 | + except ImportError: |
96 | + raise BackendException("This backend requires the pyrax " |
97 | + "library available from Rackspace.") |
98 | + |
99 | + # Inform Pyrax that we're talking to Rackspace |
100 | + # per Jesus Monzon (gsusmonzon) |
101 | + pyrax.set_setting("identity_type", "rackspace") |
102 | + |
103 | + conn_kwargs = {} |
104 | + |
105 | + if not os.environ.has_key('CLOUDFILES_USERNAME'): |
106 | + raise BackendException('CLOUDFILES_USERNAME environment variable' |
107 | + 'not set.') |
108 | + |
109 | + if not os.environ.has_key('CLOUDFILES_APIKEY'): |
110 | + raise BackendException('CLOUDFILES_APIKEY environment variable not set.') |
111 | + |
112 | + conn_kwargs['username'] = os.environ['CLOUDFILES_USERNAME'] |
113 | + conn_kwargs['api_key'] = os.environ['CLOUDFILES_APIKEY'] |
114 | + |
115 | + if os.environ.has_key('CLOUDFILES_REGION'): |
116 | + conn_kwargs['region'] = os.environ['CLOUDFILES_REGION'] |
117 | + |
118 | + container = parsed_url.path.lstrip('/') |
119 | + |
120 | + try: |
121 | + pyrax.set_credentials(**conn_kwargs) |
122 | + except Exception, e: |
123 | + log.FatalError("Connection failed, please check your credentials: %s %s" |
124 | + % (e.__class__.__name__, str(e)), |
125 | + log.ErrorCode.connection_failed) |
126 | + |
127 | + self.client_exc = pyrax.exceptions.ClientException |
128 | + self.nso_exc = pyrax.exceptions.NoSuchObject |
129 | + self.cloudfiles = pyrax.cloudfiles |
130 | + self.container = pyrax.cloudfiles.create_container(container) |
131 | + |
132 | + def put(self, source_path, remote_filename = None): |
133 | + if not remote_filename: |
134 | + remote_filename = source_path.get_filename() |
135 | + |
136 | + for n in range(1, globals.num_retries+1): |
137 | + log.Info("Uploading '%s/%s' " % (self.container, remote_filename)) |
138 | + try: |
139 | + self.container.upload_file(source_path.name, remote_filename) |
140 | + return |
141 | + except self.client_exc, error: |
142 | + log.Warn("Upload of '%s' failed (attempt %d): pyrax returned: %s %s" |
143 | + % (remote_filename, n, error.__class__.__name__, error.message)) |
144 | + except Exception, e: |
145 | + log.Warn("Upload of '%s' failed (attempt %s): %s: %s" |
146 | + % (remote_filename, n, e.__class__.__name__, str(e))) |
147 | + log.Debug("Backtrace of previous error: %s" |
148 | + % exception_traceback()) |
149 | + time.sleep(30) |
150 | + log.Warn("Giving up uploading '%s' after %s attempts" |
151 | + % (remote_filename, globals.num_retries)) |
152 | + raise BackendException("Error uploading '%s'" % remote_filename) |
153 | + |
154 | + def get(self, remote_filename, local_path): |
155 | + for n in range(1, globals.num_retries+1): |
156 | + log.Info("Downloading '%s/%s'" % (self.container, remote_filename)) |
157 | + try: |
158 | + sobject = self.container.get_object(remote_filename) |
159 | + f = open(local_path.name, 'w') |
160 | + f.write(sobject.get()) |
161 | + local_path.setdata() |
162 | + return |
163 | + except self.nso_exc: |
164 | + return |
165 | + except self.client_exc, resperr: |
166 | + log.Warn("Download of '%s' failed (attempt %s): pyrax returned: %s %s" |
167 | + % (remote_filename, n, resperr.__class__.__name__, resperr.message)) |
168 | + except Exception, e: |
169 | + log.Warn("Download of '%s' failed (attempt %s): %s: %s" |
170 | + % (remote_filename, n, e.__class__.__name__, str(e))) |
171 | + log.Debug("Backtrace of previous error: %s" |
172 | + % exception_traceback()) |
173 | + time.sleep(30) |
174 | + log.Warn("Giving up downloading '%s' after %s attempts" |
175 | + % (remote_filename, globals.num_retries)) |
176 | + raise BackendException("Error downloading '%s/%s'" |
177 | + % (self.container, remote_filename)) |
178 | + |
179 | + def list(self): |
180 | + for n in range(1, globals.num_retries+1): |
181 | + log.Info("Listing '%s'" % (self.container)) |
182 | + try: |
183 | + # Cloud Files will return a max of 10,000 objects. We have |
184 | + # to make multiple requests to get them all. |
185 | + objs = self.container.get_object_names() |
186 | + keys = objs |
187 | + while len(objs) == 10000: |
188 | + objs = self.container.get_object_names(marker=keys[-1]) |
189 | + keys += objs |
190 | + return keys |
191 | + except self.client_exc, resperr: |
192 | + log.Warn("Listing of '%s' failed (attempt %s): pyrax returned: %s %s" |
193 | + % (self.container, n, resperr.__class__.__name__, resperr.message)) |
194 | + except Exception, e: |
195 | + log.Warn("Listing of '%s' failed (attempt %s): %s: %s" |
196 | + % (self.container, n, e.__class__.__name__, str(e))) |
197 | + log.Debug("Backtrace of previous error: %s" |
198 | + % exception_traceback()) |
199 | + time.sleep(30) |
200 | + log.Warn("Giving up listing of '%s' after %s attempts" |
201 | + % (self.container, globals.num_retries)) |
202 | + raise BackendException("Error listing '%s'" |
203 | + % (self.container)) |
204 | + |
205 | + def delete_one(self, remote_filename): |
206 | + for n in range(1, globals.num_retries+1): |
207 | + log.Info("Deleting '%s/%s'" % (self.container, remote_filename)) |
208 | + try: |
209 | + self.container.delete_object(remote_filename) |
210 | + return |
211 | + except self.client_exc, resperr: |
212 | + if n > 1 and resperr.status == 404: |
213 | + # We failed on a timeout, but delete succeeded on the server |
214 | + log.Warn("Delete of '%s' missing after retry - must have succeded earler" % remote_filename ) |
215 | + return |
216 | + log.Warn("Delete of '%s' failed (attempt %s): pyrax returned: %s %s" |
217 | + % (remote_filename, n, resperr.__class__.__name__, resperr.message)) |
218 | + except Exception, e: |
219 | + log.Warn("Delete of '%s' failed (attempt %s): %s: %s" |
220 | + % (remote_filename, n, e.__class__.__name__, str(e))) |
221 | + log.Debug("Backtrace of previous error: %s" |
222 | + % exception_traceback()) |
223 | + time.sleep(30) |
224 | + log.Warn("Giving up deleting '%s' after %s attempts" |
225 | + % (remote_filename, globals.num_retries)) |
226 | + raise BackendException("Error deleting '%s/%s'" |
227 | + % (self.container, remote_filename)) |
228 | + |
229 | + def delete(self, filename_list): |
230 | + for file_ in filename_list: |
231 | + self.delete_one(file_) |
232 | + log.Debug("Deleted '%s/%s'" % (self.container, file_)) |
233 | + |
234 | + @retry |
235 | + def _query_file_info(self, filename, raise_errors=False): |
236 | + try: |
237 | + sobject = self.container.get_object(filename) |
238 | + return {'size': sobject.total_bytes} |
239 | + except self.nso_exc: |
240 | + return {'size': -1} |
241 | + except Exception, e: |
242 | + log.Warn("Error querying '%s/%s': %s" |
243 | + "" % (self.container, |
244 | + filename, |
245 | + str(e))) |
246 | + if raise_errors: |
247 | + raise e |
248 | + else: |
249 | + return {'size': None} |
250 | + |
251 | +duplicity.backend.register_backend("cfpyrax+http", PyraxBackend) |
we should probably integrate it similar to the ssh backends with a command line switch. this way it'll become clear that
- the backends are for the same protocol/service
- one is deprecated but still selectable for legacy compatibility
- old users will be switched to the new backend automatically at some point
..ede