1
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
3
# Copyright 2009 Eric EJ Johnson <ej.johnson@rackspace.com>
5
# This file is part of duplicity.
7
# Duplicity is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by the
9
# Free Software Foundation; either version 2 of the License, or (at your
10
# option) any later version.
12
# Duplicity is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with duplicity; if not, write to the Free Software Foundation,
19
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24
import duplicity.backend
25
from duplicity import globals
26
from duplicity import log
27
from duplicity.errors import *
28
from duplicity.util import exception_traceback
30
class CloudFilesBackend(duplicity.backend.Backend):
32
Backend for Rackspace's CloudFiles
34
def __init__(self, parsed_url):
36
from cloudfiles import Connection
37
from cloudfiles.errors import ResponseError
38
from cloudfiles import consts
40
raise BackendException("This backend requires the cloudfiles "
41
"library available from Rackspace.")
43
self.resp_exc = ResponseError
46
if not os.environ.has_key('CLOUDFILES_USERNAME'):
47
raise BackendException('CLOUDFILES_USERNAME environment variable'
50
if not os.environ.has_key('CLOUDFILES_APIKEY'):
51
raise BackendException('CLOUDFILES_APIKEY environment variable not set.')
53
conn_kwargs['username'] = os.environ['CLOUDFILES_USERNAME']
54
conn_kwargs['api_key'] = os.environ['CLOUDFILES_APIKEY']
56
if os.environ.has_key('CLOUDFILES_AUTHURL'):
57
conn_kwargs['authurl'] = os.environ['CLOUDFILES_AUTHURL']
59
conn_kwargs['authurl'] = consts.default_authurl
61
container = parsed_url.path.lstrip('/')
64
conn = Connection(**conn_kwargs)
66
log.FatalError("Connection failed, please check your credentials: %s %s"
67
% (e.__class__.__name__, str(e)),
68
log.ErrorCode.connection_failed)
69
self.container = conn.create_container(container)
71
def put(self, source_path, remote_filename = None):
72
if not remote_filename:
73
remote_filename = source_path.get_filename()
75
for n in range(1, globals.num_retries+1):
76
log.Info("Uploading '%s/%s' " % (self.container, remote_filename))
78
sobject = self.container.create_object(remote_filename)
79
sobject.load_from_filename(source_path.name)
81
except self.resp_exc, error:
82
log.Warn("Upload of '%s' failed (attempt %d): CloudFiles returned: %s %s"
83
% (remote_filename, n, error.status, error.reason))
85
log.Warn("Upload of '%s' failed (attempt %s): %s: %s"
86
% (remote_filename, n, e.__class__.__name__, str(e)))
87
log.Debug("Backtrace of previous error: %s"
88
% exception_traceback())
90
log.Warn("Giving up uploading '%s' after %s attempts"
91
% (remote_filename, globals.num_retries))
92
raise BackendException("Error uploading '%s'" % remote_filename)
94
def get(self, remote_filename, local_path):
95
for n in range(1, globals.num_retries+1):
96
log.Info("Downloading '%s/%s'" % (self.container, remote_filename))
98
sobject = self.container.create_object(remote_filename)
99
f = open(local_path.name, 'w')
100
for chunk in sobject.stream():
104
except self.resp_exc, resperr:
105
log.Warn("Download of '%s' failed (attempt %s): CloudFiles returned: %s %s"
106
% (remote_filename, n, resperr.status, resperr.reason))
108
log.Warn("Download of '%s' failed (attempt %s): %s: %s"
109
% (remote_filename, n, e.__class__.__name__, str(e)))
110
log.Debug("Backtrace of previous error: %s"
111
% exception_traceback())
113
log.Warn("Giving up downloading '%s' after %s attempts"
114
% (remote_filename, globals.num_retries))
115
raise BackendException("Error downloading '%s/%s'"
116
% (self.container, remote_filename))
119
keys = self.container.list_objects()
120
log.Debug("Listed container '%s'" % self.container)
123
def delete(self, filename_list):
124
for file in filename_list:
125
self.container.delete_object(file)
126
log.Debug("Deleted '%s/%s'" % (self.container, file))
128
duplicity.backend.register_backend("cf+http", CloudFilesBackend)