~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

  • Committer: Andrew Bennetts
  • Date: 2010-01-12 03:53:21 UTC
  • mfrom: (4948 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4964.
  • Revision ID: andrew.bennetts@canonical.com-20100112035321-hofpz5p10224ryj3
Merge lp:bzr, resolving conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Implementation of Transport over SFTP, using paramiko."""
18
18
 
96
96
 
97
97
class SFTPLock(object):
98
98
    """This fakes a lock in a remote location.
99
 
    
 
99
 
100
100
    A present lock is indicated just by the existence of a file.  This
101
 
    doesn't work well on all transports and they are only used in 
 
101
    doesn't work well on all transports and they are only used in
102
102
    deprecated storage formats.
103
103
    """
104
 
    
 
104
 
105
105
    __slots__ = ['path', 'lock_path', 'lock_file', 'transport']
106
106
 
107
107
    def __init__(self, path, transport):
349
349
 
350
350
    def _remote_path(self, relpath):
351
351
        """Return the path to be passed along the sftp protocol for relpath.
352
 
        
 
352
 
353
353
        :param relpath: is a urlencoded string.
354
354
        """
355
355
        relative = urlutils.unescape(relpath).encode('utf-8')
406
406
        """
407
407
        try:
408
408
            self._get_sftp().stat(self._remote_path(relpath))
 
409
            # stat result is about 20 bytes, let's say
 
410
            self._report_activity(20, 'read')
409
411
            return True
410
412
        except IOError:
411
413
            return False
416
418
        :param relpath: The relative path to the file
417
419
        """
418
420
        try:
 
421
            # FIXME: by returning the file directly, we don't pass this
 
422
            # through to report_activity.  We could try wrapping the object
 
423
            # before it's returned.  For readv and get_bytes it's handled in
 
424
            # the higher-level function.
 
425
            # -- mbp 20090126
419
426
            path = self._remote_path(relpath)
420
427
            f = self._get_sftp().file(path, mode='rb')
421
428
            if self._do_prefetch and (getattr(f, 'prefetch', None) is not None):
502
509
            #      sticky bit. So it is probably best to stop chmodding, and
503
510
            #      just tell users that they need to set the umask correctly.
504
511
            #      The attr.st_mode = mode, in _sftp_open_exclusive
505
 
            #      will handle when the user wants the final mode to be more 
506
 
            #      restrictive. And then we avoid a round trip. Unless 
 
512
            #      will handle when the user wants the final mode to be more
 
513
            #      restrictive. And then we avoid a round trip. Unless
507
514
            #      paramiko decides to expose an async chmod()
508
515
 
509
516
            # This is designed to chmod() right before we close.
510
 
            # Because we set_pipelined() earlier, theoretically we might 
 
517
            # Because we set_pipelined() earlier, theoretically we might
511
518
            # avoid the round trip for fout.close()
512
519
            if mode is not None:
513
520
                self._get_sftp().chmod(tmp_abspath, mode)
555
562
                                                 ': unable to open')
556
563
 
557
564
                # This is designed to chmod() right before we close.
558
 
                # Because we set_pipelined() earlier, theoretically we might 
 
565
                # Because we set_pipelined() earlier, theoretically we might
559
566
                # avoid the round trip for fout.close()
560
567
                if mode is not None:
561
568
                    self._get_sftp().chmod(abspath, mode)
612
619
 
613
620
    def iter_files_recursive(self):
614
621
        """Walk the relative paths of all files in this transport."""
 
622
        # progress is handled by list_dir
615
623
        queue = list(self.list_dir('.'))
616
624
        while queue:
617
625
            relpath = queue.pop(0)
628
636
        else:
629
637
            local_mode = mode
630
638
        try:
 
639
            self._report_activity(len(abspath), 'write')
631
640
            self._get_sftp().mkdir(abspath, local_mode)
 
641
            self._report_activity(1, 'read')
632
642
            if mode is not None:
633
643
                # chmod a dir through sftp will erase any sgid bit set
634
644
                # on the server side.  So, if the bit mode are already
656
666
    def open_write_stream(self, relpath, mode=None):
657
667
        """See Transport.open_write_stream."""
658
668
        # initialise the file to zero-length
659
 
        # this is three round trips, but we don't use this 
660
 
        # api more than once per write_group at the moment so 
 
669
        # this is three round trips, but we don't use this
 
670
        # api more than once per write_group at the moment so
661
671
        # it is a tolerable overhead. Better would be to truncate
662
672
        # the file after opening. RBC 20070805
663
673
        self.put_bytes_non_atomic(relpath, "", mode)
686
696
        :param failure_exc: Paramiko has the super fun ability to raise completely
687
697
                           opaque errors that just set "e.args = ('Failure',)" with
688
698
                           no more information.
689
 
                           If this parameter is set, it defines the exception 
 
699
                           If this parameter is set, it defines the exception
690
700
                           to raise in these cases.
691
701
        """
692
702
        # paramiko seems to generate detailless errors.
701
711
            # strange but true, for the paramiko server.
702
712
            if (e.args == ('Failure',)):
703
713
                raise failure_exc(path, str(e) + more_info)
 
714
            # Can be something like args = ('Directory not empty:
 
715
            # '/srv/bazaar.launchpad.net/blah...: '
 
716
            # [Errno 39] Directory not empty',)
 
717
            if (e.args[0].startswith('Directory not empty: ')
 
718
                or getattr(e, 'errno', None) == errno.ENOTEMPTY):
 
719
                raise errors.DirectoryNotEmpty(path, str(e))
704
720
            mutter('Raising exception with args %s', e.args)
705
721
        if getattr(e, 'errno', None) is not None:
706
722
            mutter('Raising exception with errno %s', e.errno)
733
749
 
734
750
    def _rename_and_overwrite(self, abs_from, abs_to):
735
751
        """Do a fancy rename on the remote server.
736
 
        
 
752
 
737
753
        Using the implementation provided by osutils.
738
754
        """
739
755
        try:
758
774
            self._get_sftp().remove(path)
759
775
        except (IOError, paramiko.SSHException), e:
760
776
            self._translate_io_exception(e, path, ': unable to delete')
761
 
            
 
777
 
762
778
    def external_url(self):
763
779
        """See bzrlib.transport.Transport.external_url."""
764
780
        # the external path for SFTP is the base
779
795
        path = self._remote_path(relpath)
780
796
        try:
781
797
            entries = self._get_sftp().listdir(path)
 
798
            self._report_activity(sum(map(len, entries)), 'read')
782
799
        except (IOError, paramiko.SSHException), e:
783
800
            self._translate_io_exception(e, path, ': failed to list_dir')
784
801
        return [urlutils.escape(entry) for entry in entries]
841
858
        """
842
859
        # TODO: jam 20060816 Paramiko >= 1.6.2 (probably earlier) supports
843
860
        #       using the 'x' flag to indicate SFTP_FLAG_EXCL.
844
 
        #       However, there is no way to set the permission mode at open 
 
861
        #       However, there is no way to set the permission mode at open
845
862
        #       time using the sftp_client.file() functionality.
846
863
        path = self._get_sftp()._adjust_cwd(abspath)
847
864
        # mutter('sftp abspath %s => %s', abspath, path)
848
865
        attr = SFTPAttributes()
849
866
        if mode is not None:
850
867
            attr.st_mode = mode
851
 
        omode = (SFTP_FLAG_WRITE | SFTP_FLAG_CREATE 
 
868
        omode = (SFTP_FLAG_WRITE | SFTP_FLAG_CREATE
852
869
                | SFTP_FLAG_TRUNC | SFTP_FLAG_EXCL)
853
870
        try:
854
871
            t, msg = self._get_sftp()._request(CMD_OPEN, path, omode, attr)
900
917
        self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
901
918
        self._socket.bind(('localhost', 0))
902
919
        self._socket.listen(1)
903
 
        self.port = self._socket.getsockname()[1]
 
920
        self.host, self.port = self._socket.getsockname()[:2]
904
921
        self._stop_event = threading.Event()
905
922
 
906
923
    def stop(self):
932
949
                # probably a failed test; unit test thread will log the
933
950
                # failure/error
934
951
                sys.excepthook(*sys.exc_info())
935
 
                warning('Exception from within unit test server thread: %r' % 
 
952
                warning('Exception from within unit test server thread: %r' %
936
953
                        x)
937
954
 
938
955
 
949
966
 
950
967
    Not all methods are implemented, this is deliberate as this class is not a
951
968
    replacement for the builtin sockets layer. fileno is not implemented to
952
 
    prevent the proxy being bypassed. 
 
969
    prevent the proxy being bypassed.
953
970
    """
954
971
 
955
972
    simulated_time = 0
957
974
        "close", "getpeername", "getsockname", "getsockopt", "gettimeout",
958
975
        "setblocking", "setsockopt", "settimeout", "shutdown"])
959
976
 
960
 
    def __init__(self, sock, latency, bandwidth=1.0, 
 
977
    def __init__(self, sock, latency, bandwidth=1.0,
961
978
                 really_sleep=True):
962
 
        """ 
 
979
        """
963
980
        :param bandwith: simulated bandwith (MegaBit)
964
981
        :param really_sleep: If set to false, the SocketDelay will just
965
982
        increase a counter, instead of calling time.sleep. This is useful for
968
985
        self.sock = sock
969
986
        self.latency = latency
970
987
        self.really_sleep = really_sleep
971
 
        self.time_per_byte = 1 / (bandwidth / 8.0 * 1024 * 1024) 
 
988
        self.time_per_byte = 1 / (bandwidth / 8.0 * 1024 * 1024)
972
989
        self.new_roundtrip = False
973
990
 
974
991
    def sleep(self, s):
1028
1045
 
1029
1046
    def _get_sftp_url(self, path):
1030
1047
        """Calculate an sftp url to this server for path."""
1031
 
        return 'sftp://foo:bar@localhost:%d/%s' % (self._listener.port, path)
 
1048
        return 'sftp://foo:bar@%s:%d/%s' % (self._listener.host,
 
1049
                                            self._listener.port, path)
1032
1050
 
1033
1051
    def log(self, message):
1034
1052
        """StubServer uses this to log when a new server is created."""
1036
1054
 
1037
1055
    def _run_server_entry(self, sock):
1038
1056
        """Entry point for all implementations of _run_server.
1039
 
        
 
1057
 
1040
1058
        If self.add_latency is > 0.000001 then sock is given a latency adding
1041
1059
        decorator.
1042
1060
        """
1059
1077
        event = threading.Event()
1060
1078
        ssh_server.start_server(event, server)
1061
1079
        event.wait(5.0)
1062
 
    
1063
 
    def setUp(self, backing_server=None):
 
1080
 
 
1081
    def start_server(self, backing_server=None):
1064
1082
        # XXX: TODO: make sftpserver back onto backing_server rather than local
1065
1083
        # disk.
1066
1084
        if not (backing_server is None or
1085
1103
        self._listener.setDaemon(True)
1086
1104
        self._listener.start()
1087
1105
 
1088
 
    def tearDown(self):
1089
 
        """See bzrlib.transport.Server.tearDown."""
 
1106
    def stop_server(self):
1090
1107
        self._listener.stop()
1091
1108
        ssh._ssh_vendor_manager._cached_ssh_vendor = self._original_vendor
1092
1109
 
1185
1202
    It does this by serving from a deeply-nested directory that doesn't exist.
1186
1203
    """
1187
1204
 
1188
 
    def setUp(self, backing_server=None):
 
1205
    def start_server(self, backing_server=None):
1189
1206
        self._server_homedir = '/dev/noone/runs/tests/here'
1190
 
        super(SFTPSiblingAbsoluteServer, self).setUp(backing_server)
 
1207
        super(SFTPSiblingAbsoluteServer, self).start_server(backing_server)
1191
1208
 
1192
1209
 
1193
1210
def get_test_permutations():