/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/smart/medium.py

  • Committer: Martin Pool
  • Date: 2010-10-08 04:38:25 UTC
  • mfrom: (5462 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5478.
  • Revision ID: mbp@sourcefrog.net-20101008043825-b181r8bo5r3qwb6j
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
24
24
bzrlib/transport/smart/__init__.py.
25
25
"""
26
26
 
27
 
import errno
28
27
import os
29
 
import socket
30
28
import sys
31
29
import urllib
32
30
 
 
31
import bzrlib
33
32
from bzrlib.lazy_import import lazy_import
34
33
lazy_import(globals(), """
35
 
import atexit
36
 
import threading
 
34
import socket
 
35
import thread
37
36
import weakref
38
37
 
39
38
from bzrlib import (
47
46
from bzrlib.smart import client, protocol, request, vfs
48
47
from bzrlib.transport import ssh
49
48
""")
50
 
#usually already imported, and getting IllegalScoperReplacer on it here.
51
49
from bzrlib import osutils
52
50
 
53
 
# We must not read any more than 64k at a time so we don't risk "no buffer
54
 
# space available" errors on some platforms.  Windows in particular is likely
55
 
# to give error 10053 or 10055 if we read more than 64k from a socket.
56
 
_MAX_READ_SIZE = 64 * 1024
57
 
 
 
51
# Throughout this module buffer size parameters are either limited to be at
 
52
# most _MAX_READ_SIZE, or are ignored and _MAX_READ_SIZE is used instead.
 
53
# For this module's purposes, MAX_SOCKET_CHUNK is a reasonable size for reads
 
54
# from non-sockets as well.
 
55
_MAX_READ_SIZE = osutils.MAX_SOCKET_CHUNK
58
56
 
59
57
def _get_protocol_factory_for_bytes(bytes):
60
58
    """Determine the right protocol factory for 'bytes'.
276
274
    def _serve_one_request_unguarded(self, protocol):
277
275
        while protocol.next_read_size():
278
276
            # We can safely try to read large chunks.  If there is less data
279
 
            # than _MAX_READ_SIZE ready, the socket wil just return a short
280
 
            # read immediately rather than block.
281
 
            bytes = self.read_bytes(_MAX_READ_SIZE)
 
277
            # than MAX_SOCKET_CHUNK ready, the socket will just return a
 
278
            # short read immediately rather than block.
 
279
            bytes = self.read_bytes(osutils.MAX_SOCKET_CHUNK)
282
280
            if bytes == '':
283
281
                self.finished = True
284
282
                return
287
285
        self._push_back(protocol.unused_data)
288
286
 
289
287
    def _read_bytes(self, desired_count):
290
 
        return _read_bytes_from_socket(
291
 
            self.socket.recv, desired_count, self._report_activity)
 
288
        return osutils.read_bytes_from_socket(
 
289
            self.socket, self._report_activity)
292
290
 
293
291
    def terminate_due_to_error(self):
294
292
        # TODO: This should log to a server log file, but no such thing
295
293
        # exists yet.  Andrew Bennetts 2006-09-29.
296
 
        osutils.until_no_eintr(self.socket.close)
 
294
        self.socket.close()
297
295
        self.finished = True
298
296
 
299
297
    def _write_out(self, bytes):
300
298
        tstart = osutils.timer_func()
301
299
        osutils.send_all(self.socket, bytes, self._report_activity)
302
300
        if 'hpss' in debug.debug_flags:
303
 
            cur_thread = threading.currentThread()
304
 
            thread_id = getattr(cur_thread, 'ident', None)
305
 
            if thread_id is None:
306
 
                thread_id = cur_thread.getName()
 
301
            thread_id = thread.get_ident()
307
302
            trace.mutter('%12s: [%s] %d bytes to the socket in %.3fs'
308
303
                         % ('wrote', thread_id, len(bytes),
309
304
                            osutils.timer_func() - tstart))
337
332
            bytes_to_read = protocol.next_read_size()
338
333
            if bytes_to_read == 0:
339
334
                # Finished serving this request.
340
 
                osutils.until_no_eintr(self._out.flush)
 
335
                self._out.flush()
341
336
                return
342
337
            bytes = self.read_bytes(bytes_to_read)
343
338
            if bytes == '':
344
339
                # Connection has been closed.
345
340
                self.finished = True
346
 
                osutils.until_no_eintr(self._out.flush)
 
341
                self._out.flush()
347
342
                return
348
343
            protocol.accept_bytes(bytes)
349
344
 
350
345
    def _read_bytes(self, desired_count):
351
 
        return osutils.until_no_eintr(self._in.read, desired_count)
 
346
        return self._in.read(desired_count)
352
347
 
353
348
    def terminate_due_to_error(self):
354
349
        # TODO: This should log to a server log file, but no such thing
355
350
        # exists yet.  Andrew Bennetts 2006-09-29.
356
 
        osutils.until_no_eintr(self._out.close)
 
351
        self._out.close()
357
352
        self.finished = True
358
353
 
359
354
    def _write_out(self, bytes):
360
 
        osutils.until_no_eintr(self._out.write, bytes)
 
355
        self._out.write(bytes)
361
356
 
362
357
 
363
358
class SmartClientMediumRequest(object):
499
494
class _DebugCounter(object):
500
495
    """An object that counts the HPSS calls made to each client medium.
501
496
 
502
 
    When a medium is garbage-collected, or failing that when atexit functions
503
 
    are run, the total number of calls made on that medium are reported via
504
 
    trace.note.
 
497
    When a medium is garbage-collected, or failing that when
 
498
    bzrlib.global_state exits, the total number of calls made on that medium
 
499
    are reported via trace.note.
505
500
    """
506
501
 
507
502
    def __init__(self):
508
503
        self.counts = weakref.WeakKeyDictionary()
509
504
        client._SmartClient.hooks.install_named_hook(
510
505
            'call', self.increment_call_count, 'hpss call counter')
511
 
        atexit.register(self.flush_all)
 
506
        bzrlib.global_state.cleanups.add_cleanup(self.flush_all)
512
507
 
513
508
    def track(self, medium):
514
509
        """Start tracking calls made to a medium.
612
607
            # which is newer than a previously supplied older-than version.
613
608
            # This indicates that some smart verb call is not guarded
614
609
            # appropriately (it should simply not have been tried).
615
 
            raise AssertionError(
 
610
            trace.mutter(
616
611
                "_remember_remote_is_before(%r) called, but "
617
612
                "_remember_remote_is_before(%r) was called previously."
618
 
                % (version_tuple, self._remote_version_is_before))
 
613
                , version_tuple, self._remote_version_is_before)
 
614
            if 'hpss' in debug.debug_flags:
 
615
                ui.ui_factory.show_warning(
 
616
                    "_remember_remote_is_before(%r) called, but "
 
617
                    "_remember_remote_is_before(%r) was called previously."
 
618
                    % (version_tuple, self._remote_version_is_before))
 
619
            return
619
620
        self._remote_version_is_before = version_tuple
620
621
 
621
622
    def protocol_version(self):
723
724
 
724
725
    def _accept_bytes(self, bytes):
725
726
        """See SmartClientStreamMedium.accept_bytes."""
726
 
        osutils.until_no_eintr(self._writeable_pipe.write, bytes)
 
727
        self._writeable_pipe.write(bytes)
727
728
        self._report_activity(len(bytes), 'write')
728
729
 
729
730
    def _flush(self):
730
731
        """See SmartClientStreamMedium._flush()."""
731
 
        osutils.until_no_eintr(self._writeable_pipe.flush)
 
732
        self._writeable_pipe.flush()
732
733
 
733
734
    def _read_bytes(self, count):
734
735
        """See SmartClientStreamMedium._read_bytes."""
735
 
        bytes = osutils.until_no_eintr(self._readable_pipe.read, count)
 
736
        bytes_to_read = min(count, _MAX_READ_SIZE)
 
737
        bytes = self._readable_pipe.read(bytes_to_read)
736
738
        self._report_activity(len(bytes), 'read')
737
739
        return bytes
738
740
 
739
741
 
 
742
class SSHParams(object):
 
743
    """A set of parameters for starting a remote bzr via SSH."""
 
744
 
 
745
    def __init__(self, host, port=None, username=None, password=None,
 
746
            bzr_remote_path='bzr'):
 
747
        self.host = host
 
748
        self.port = port
 
749
        self.username = username
 
750
        self.password = password
 
751
        self.bzr_remote_path = bzr_remote_path
 
752
 
 
753
 
740
754
class SmartSSHClientMedium(SmartClientStreamMedium):
741
 
    """A client medium using SSH."""
 
755
    """A client medium using SSH.
 
756
    
 
757
    It delegates IO to a SmartClientSocketMedium or
 
758
    SmartClientAlreadyConnectedSocketMedium (depending on platform).
 
759
    """
742
760
 
743
 
    def __init__(self, host, port=None, username=None, password=None,
744
 
            base=None, vendor=None, bzr_remote_path=None):
 
761
    def __init__(self, base, ssh_params, vendor=None):
745
762
        """Creates a client that will connect on the first use.
746
763
 
 
764
        :param ssh_params: A SSHParams instance.
747
765
        :param vendor: An optional override for the ssh vendor to use. See
748
766
            bzrlib.transport.ssh for details on ssh vendors.
749
767
        """
750
 
        self._connected = False
751
 
        self._host = host
752
 
        self._password = password
753
 
        self._port = port
754
 
        self._username = username
 
768
        self._real_medium = None
 
769
        self._ssh_params = ssh_params
 
770
        # for the benefit of progress making a short description of this
 
771
        # transport
 
772
        self._scheme = 'bzr+ssh'
755
773
        # SmartClientStreamMedium stores the repr of this object in its
756
774
        # _DebugCounter so we have to store all the values used in our repr
757
775
        # method before calling the super init.
758
776
        SmartClientStreamMedium.__init__(self, base)
759
 
        self._read_from = None
 
777
        self._vendor = vendor
760
778
        self._ssh_connection = None
761
 
        self._vendor = vendor
762
 
        self._write_to = None
763
 
        self._bzr_remote_path = bzr_remote_path
764
 
        # for the benefit of progress making a short description of this
765
 
        # transport
766
 
        self._scheme = 'bzr+ssh'
767
779
 
768
780
    def __repr__(self):
769
 
        return "%s(connected=%r, username=%r, host=%r, port=%r)" % (
 
781
        if self._ssh_params.port is None:
 
782
            maybe_port = ''
 
783
        else:
 
784
            maybe_port = ':%s' % self._ssh_params.port
 
785
        return "%s(%s://%s@%s%s/)" % (
770
786
            self.__class__.__name__,
771
 
            self._connected,
772
 
            self._username,
773
 
            self._host,
774
 
            self._port)
 
787
            self._scheme,
 
788
            self._ssh_params.username,
 
789
            self._ssh_params.host,
 
790
            maybe_port)
775
791
 
776
792
    def _accept_bytes(self, bytes):
777
793
        """See SmartClientStreamMedium.accept_bytes."""
778
794
        self._ensure_connection()
779
 
        osutils.until_no_eintr(self._write_to.write, bytes)
780
 
        self._report_activity(len(bytes), 'write')
 
795
        self._real_medium.accept_bytes(bytes)
781
796
 
782
797
    def disconnect(self):
783
798
        """See SmartClientMedium.disconnect()."""
784
 
        if not self._connected:
785
 
            return
786
 
        osutils.until_no_eintr(self._read_from.close)
787
 
        osutils.until_no_eintr(self._write_to.close)
788
 
        self._ssh_connection.close()
789
 
        self._connected = False
 
799
        if self._real_medium is not None:
 
800
            self._real_medium.disconnect()
 
801
            self._real_medium = None
 
802
        if self._ssh_connection is not None:
 
803
            self._ssh_connection.close()
 
804
            self._ssh_connection = None
790
805
 
791
806
    def _ensure_connection(self):
792
807
        """Connect this medium if not already connected."""
793
 
        if self._connected:
 
808
        if self._real_medium is not None:
794
809
            return
795
810
        if self._vendor is None:
796
811
            vendor = ssh._get_ssh_vendor()
797
812
        else:
798
813
            vendor = self._vendor
799
 
        self._ssh_connection = vendor.connect_ssh(self._username,
800
 
                self._password, self._host, self._port,
801
 
                command=[self._bzr_remote_path, 'serve', '--inet',
 
814
        self._ssh_connection = vendor.connect_ssh(self._ssh_params.username,
 
815
                self._ssh_params.password, self._ssh_params.host,
 
816
                self._ssh_params.port,
 
817
                command=[self._ssh_params.bzr_remote_path, 'serve', '--inet',
802
818
                         '--directory=/', '--allow-writes'])
803
 
        self._read_from, self._write_to = \
804
 
            self._ssh_connection.get_filelike_channels()
805
 
        self._connected = True
 
819
        io_kind, io_object = self._ssh_connection.get_sock_or_pipes()
 
820
        if io_kind == 'socket':
 
821
            self._real_medium = SmartClientAlreadyConnectedSocketMedium(
 
822
                self.base, io_object)
 
823
        elif io_kind == 'pipes':
 
824
            read_from, write_to = io_object
 
825
            self._real_medium = SmartSimplePipesClientMedium(
 
826
                read_from, write_to, self.base)
 
827
        else:
 
828
            raise AssertionError(
 
829
                "Unexpected io_kind %r from %r"
 
830
                % (io_kind, self._ssh_connection))
806
831
 
807
832
    def _flush(self):
808
833
        """See SmartClientStreamMedium._flush()."""
809
 
        self._write_to.flush()
 
834
        self._real_medium._flush()
810
835
 
811
836
    def _read_bytes(self, count):
812
837
        """See SmartClientStreamMedium.read_bytes."""
813
 
        if not self._connected:
 
838
        if self._real_medium is None:
814
839
            raise errors.MediumNotConnected(self)
815
 
        bytes_to_read = min(count, _MAX_READ_SIZE)
816
 
        bytes = osutils.until_no_eintr(self._read_from.read, bytes_to_read)
817
 
        self._report_activity(len(bytes), 'read')
818
 
        return bytes
 
840
        return self._real_medium.read_bytes(count)
819
841
 
820
842
 
821
843
# Port 4155 is the default port for bzr://, registered with IANA.
823
845
BZR_DEFAULT_PORT = 4155
824
846
 
825
847
 
826
 
class SmartTCPClientMedium(SmartClientStreamMedium):
827
 
    """A client medium using TCP."""
 
848
class SmartClientSocketMedium(SmartClientStreamMedium):
 
849
    """A client medium using a socket.
 
850
    
 
851
    This class isn't usable directly.  Use one of its subclasses instead.
 
852
    """
 
853
 
 
854
    def __init__(self, base):
 
855
        SmartClientStreamMedium.__init__(self, base)
 
856
        self._socket = None
 
857
        self._connected = False
 
858
 
 
859
    def _accept_bytes(self, bytes):
 
860
        """See SmartClientMedium.accept_bytes."""
 
861
        self._ensure_connection()
 
862
        osutils.send_all(self._socket, bytes, self._report_activity)
 
863
 
 
864
    def _ensure_connection(self):
 
865
        """Connect this medium if not already connected."""
 
866
        raise NotImplementedError(self._ensure_connection)
 
867
 
 
868
    def _flush(self):
 
869
        """See SmartClientStreamMedium._flush().
 
870
 
 
871
        For sockets we do no flushing. For TCP sockets we may want to turn off
 
872
        TCP_NODELAY and add a means to do a flush, but that can be done in the
 
873
        future.
 
874
        """
 
875
 
 
876
    def _read_bytes(self, count):
 
877
        """See SmartClientMedium.read_bytes."""
 
878
        if not self._connected:
 
879
            raise errors.MediumNotConnected(self)
 
880
        return osutils.read_bytes_from_socket(
 
881
            self._socket, self._report_activity)
 
882
 
 
883
    def disconnect(self):
 
884
        """See SmartClientMedium.disconnect()."""
 
885
        if not self._connected:
 
886
            return
 
887
        self._socket.close()
 
888
        self._socket = None
 
889
        self._connected = False
 
890
 
 
891
 
 
892
class SmartTCPClientMedium(SmartClientSocketMedium):
 
893
    """A client medium that creates a TCP connection."""
828
894
 
829
895
    def __init__(self, host, port, base):
830
896
        """Creates a client that will connect on the first use."""
831
 
        SmartClientStreamMedium.__init__(self, base)
832
 
        self._connected = False
 
897
        SmartClientSocketMedium.__init__(self, base)
833
898
        self._host = host
834
899
        self._port = port
835
 
        self._socket = None
836
 
 
837
 
    def _accept_bytes(self, bytes):
838
 
        """See SmartClientMedium.accept_bytes."""
839
 
        self._ensure_connection()
840
 
        osutils.send_all(self._socket, bytes, self._report_activity)
841
 
 
842
 
    def disconnect(self):
843
 
        """See SmartClientMedium.disconnect()."""
844
 
        if not self._connected:
845
 
            return
846
 
        osutils.until_no_eintr(self._socket.close)
847
 
        self._socket = None
848
 
        self._connected = False
849
900
 
850
901
    def _ensure_connection(self):
851
902
        """Connect this medium if not already connected."""
886
937
                    (self._host, port, err_msg))
887
938
        self._connected = True
888
939
 
889
 
    def _flush(self):
890
 
        """See SmartClientStreamMedium._flush().
891
 
 
892
 
        For TCP we do no flushing. We may want to turn off TCP_NODELAY and
893
 
        add a means to do a flush, but that can be done in the future.
894
 
        """
895
 
 
896
 
    def _read_bytes(self, count):
897
 
        """See SmartClientMedium.read_bytes."""
898
 
        if not self._connected:
899
 
            raise errors.MediumNotConnected(self)
900
 
        return _read_bytes_from_socket(
901
 
            self._socket.recv, count, self._report_activity)
 
940
 
 
941
class SmartClientAlreadyConnectedSocketMedium(SmartClientSocketMedium):
 
942
    """A client medium for an already connected socket.
 
943
    
 
944
    Note that this class will assume it "owns" the socket, so it will close it
 
945
    when its disconnect method is called.
 
946
    """
 
947
 
 
948
    def __init__(self, base, sock):
 
949
        SmartClientSocketMedium.__init__(self, base)
 
950
        self._socket = sock
 
951
        self._connected = True
 
952
 
 
953
    def _ensure_connection(self):
 
954
        # Already connected, by definition!  So nothing to do.
 
955
        pass
902
956
 
903
957
 
904
958
class SmartClientStreamMediumRequest(SmartClientMediumRequest):
941
995
        self._medium._flush()
942
996
 
943
997
 
944
 
def _read_bytes_from_socket(sock, desired_count, report_activity):
945
 
    # We ignore the desired_count because on sockets it's more efficient to
946
 
    # read large chunks (of _MAX_READ_SIZE bytes) at a time.
947
 
    try:
948
 
        bytes = osutils.until_no_eintr(sock, _MAX_READ_SIZE)
949
 
    except socket.error, e:
950
 
        if len(e.args) and e.args[0] in (errno.ECONNRESET, 10054):
951
 
            # The connection was closed by the other side.  Callers expect an
952
 
            # empty string to signal end-of-stream.
953
 
            bytes = ''
954
 
        else:
955
 
            raise
956
 
    else:
957
 
        report_activity(len(bytes), 'read')
958
 
    return bytes
959