~ubuntu-branches/ubuntu/natty/duplicity/natty-updates

« back to all changes in this revision

Viewing changes to src/backends/cloudfilesbackend.py

  • Committer: Bazaar Package Importer
  • Author(s): Michael Terry
  • Date: 2009-06-22 09:28:19 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20090622092819-u2jxolpt6i11qa58
Tags: 0.5.18-0ubuntu1
* New upstream release (LP: #390666)
* Fixes LP: #368062 and LP: #379648

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
 
2
#
 
3
# Copyright 2009 Eric EJ Johnson <ej.johnson@rackspace.com>
 
4
#
 
5
# This file is part of duplicity.
 
6
#
 
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.
 
11
#
 
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.
 
16
#
 
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
 
20
 
 
21
import os
 
22
import time
 
23
 
 
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
 
29
 
 
30
class CloudFilesBackend(duplicity.backend.Backend):
 
31
    """
 
32
    Backend for Rackspace's CloudFiles
 
33
    """
 
34
    def __init__(self, parsed_url):
 
35
        try:
 
36
            from cloudfiles import Connection
 
37
            from cloudfiles.errors import ResponseError
 
38
            from cloudfiles import consts
 
39
        except ImportError:
 
40
            raise BackendException("This backend requires the cloudfiles "
 
41
                                   "library available from Rackspace.")
 
42
 
 
43
        self.resp_exc = ResponseError
 
44
        conn_kwargs = {}
 
45
 
 
46
        if not os.environ.has_key('CLOUDFILES_USERNAME'):
 
47
            raise BackendException('CLOUDFILES_USERNAME environment variable'
 
48
                                   'not set.')
 
49
 
 
50
        if not os.environ.has_key('CLOUDFILES_APIKEY'):
 
51
            raise BackendException('CLOUDFILES_APIKEY environment variable not set.')
 
52
 
 
53
        conn_kwargs['username'] = os.environ['CLOUDFILES_USERNAME']
 
54
        conn_kwargs['api_key'] = os.environ['CLOUDFILES_APIKEY']
 
55
 
 
56
        if os.environ.has_key('CLOUDFILES_AUTHURL'):
 
57
            conn_kwargs['authurl'] = os.environ['CLOUDFILES_AUTHURL']
 
58
        else:
 
59
            conn_kwargs['authurl'] = consts.default_authurl
 
60
 
 
61
        container = parsed_url.path.lstrip('/')
 
62
 
 
63
        try:
 
64
            conn = Connection(**conn_kwargs)
 
65
        except Exception, e:
 
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)
 
70
 
 
71
    def put(self, source_path, remote_filename = None):
 
72
        if not remote_filename:
 
73
            remote_filename = source_path.get_filename()
 
74
 
 
75
        for n in range(1, globals.num_retries+1):
 
76
            log.Info("Uploading '%s/%s' " % (self.container, remote_filename))
 
77
            try:
 
78
                sobject = self.container.create_object(remote_filename)
 
79
                sobject.load_from_filename(source_path.name)
 
80
                return
 
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))
 
84
            except Exception, e:
 
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())
 
89
            time.sleep(30)
 
90
        log.Warn("Giving up uploading '%s' after %s attempts"
 
91
                 % (remote_filename, globals.num_retries))
 
92
        raise BackendException("Error uploading '%s'" % remote_filename)
 
93
 
 
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))
 
97
            try:
 
98
                sobject = self.container.create_object(remote_filename)
 
99
                f = open(local_path.name, 'w')
 
100
                for chunk in sobject.stream():
 
101
                    f.write(chunk)
 
102
                local_path.setdata()
 
103
                return
 
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))
 
107
            except Exception, e:
 
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())
 
112
            time.sleep(30)
 
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))
 
117
 
 
118
    def list(self):
 
119
        keys = self.container.list_objects()
 
120
        log.Debug("Listed container '%s'" % self.container)
 
121
        return keys
 
122
 
 
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))
 
127
 
 
128
duplicity.backend.register_backend("cf+http", CloudFilesBackend)