~jflaker/duplicity/BugFix1325215

« back to all changes in this revision

Viewing changes to duplicity/backends/ftpsbackend.py

  • Committer: Kenneth Loafman
  • Date: 2014-04-29 15:35:47 UTC
  • mfrom: (978.2.10 backend-unification)
  • Revision ID: kenneth@loafman.com-20140429153547-to2j1tyyl0ps1hi6
* Merged in lp:~mterry/duplicity/backend-unification
  - Reorganize and simplify backend code.  Specifically:
    - Formalize the expected API between backends and duplicity.  See the new
      file duplicity/backends/README for the instructions I've given authors.
    - Add some tests for our backend wrapper class as well as some tests for
      individual backends.  For several backends that have some commands do all
      the heavy lifting (hsi, tahoe, ftp), I've added fake little mock commands
      so that we can test them locally.  This doesn't truly test our integration
      with those commands, but at least lets us test the backend glue code.
    - Removed a lot of duplicate and unused code which backends were using (or
      not using).  This branch drops 700 lines of code (~20%)
      in duplicity/backends!
    - Simplified expectations of backends.  Our wrapper code now does all the
      retrying, and all the exception handling.  Backends can 'fire and forget'
      trusting our wrappers to give the user a reasonable error message.
      Obviously, backends can also add more details and make nicer error
      messages.  But they don't *have* to.
    - Separate out the backend classes from our wrapper class.  Now there is no
      possibility of namespace collision.  All our API methods use one
      underscore.  Anything else (zero or two underscores) are for the backend
      class's use.
    - Added the concept of a 'backend prefix' which is used by par2 and gio
      backends to provide generic support for "schema+" in urls -- like par2+
      or gio+.  I've since marked the '--gio' flag as deprecated, in favor of
      'gio+'.  Now you can even nest such backends like
      par2+gio+file://blah/blah.
    - The switch to control which cloudfiles backend had a typo.  I fixed this,
      but I'm not sure I should have?  If we haven't had complaints, maybe we
      can just drop the old backend.
    - I manually tested all the backends we have (except hsi and tahoe -- but
      those are simple wrappers around commands and I did test those via mocks
      per above).  I also added a bunch more manual backend tests to
      ./testing/manual/backendtest.py, which can now be run like the above to
      test all the files you have configured in config.py or you can pass it a
      URL which it will use for testing (useful for backend authors).

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
import duplicity.backend
29
29
from duplicity import globals
30
30
from duplicity import log
31
 
from duplicity.errors import *
32
31
from duplicity import tempdir
33
32
 
34
33
class FTPSBackend(duplicity.backend.Backend):
85
84
            os.write(self.tempfile, "user %s %s\n" % (self.parsed_url.username, self.password))
86
85
        os.close(self.tempfile)
87
86
 
88
 
        self.flags = "-f %s" % self.tempname
89
 
 
90
 
    def put(self, source_path, remote_filename = None):
91
 
        """Transfer source_path to remote_filename"""
92
 
        if not remote_filename:
93
 
            remote_filename = source_path.get_filename()
 
87
    def _put(self, source_path, remote_filename):
94
88
        remote_path = os.path.join(urllib.unquote(self.parsed_url.path.lstrip('/')), remote_filename).rstrip()
95
89
        commandline = "lftp -c 'source %s;put \'%s\' -o \'%s\''" % \
96
90
            (self.tempname, source_path.name, remote_path)
97
 
        l = self.run_command_persist(commandline)
 
91
        self.subprocess_popen(commandline)
98
92
 
99
 
    def get(self, remote_filename, local_path):
100
 
        """Get remote filename, saving it to local_path"""
 
93
    def _get(self, remote_filename, local_path):
101
94
        remote_path = os.path.join(urllib.unquote(self.parsed_url.path), remote_filename).rstrip()
102
95
        commandline = "lftp -c 'source %s;get %s -o %s'" % \
103
96
            (self.tempname, remote_path.lstrip('/'), local_path.name)
104
 
        self.run_command_persist(commandline)
105
 
        local_path.setdata()
 
97
        self.subprocess_popen(commandline)
106
98
 
107
99
    def _list(self):
108
 
        """List files in directory"""
109
100
        # Do a long listing to avoid connection reset
110
101
        remote_dir = urllib.unquote(self.parsed_url.path.lstrip('/')).rstrip()
111
102
        commandline = "lftp -c 'source %s;ls \'%s\''" % (self.tempname, remote_dir)
112
 
        l = self.popen_persist(commandline).split('\n')
 
103
        _, l, _ = self.subprocess_popen(commandline)
113
104
        # Look for our files as the last element of a long list line
114
 
        return [x.split()[-1] for x in l if x]
 
105
        return [x.split()[-1] for x in l.split('\n') if x]
115
106
 
116
 
    def delete(self, filename_list):
117
 
        """Delete files in filename_list"""
118
 
        filelist = ""
119
 
        for filename in filename_list:
120
 
            filelist += "\'%s\' " % filename
121
 
        if filelist.rstrip():
122
 
            remote_dir = urllib.unquote(self.parsed_url.path.lstrip('/')).rstrip()
123
 
            commandline = "lftp -c 'source %s;cd \'%s\';rm %s'" % (self.tempname, remote_dir, filelist.rstrip())
124
 
            self.popen_persist(commandline)
 
107
    def _delete(self, filename):
 
108
        remote_dir = urllib.unquote(self.parsed_url.path.lstrip('/')).rstrip()
 
109
        commandline = "lftp -c 'source %s;cd \'%s\';rm \'%s\''" % (self.tempname, remote_dir, filename)
 
110
        self.subprocess_popen(commandline)
125
111
 
126
112
duplicity.backend.register_backend("ftps", FTPSBackend)