~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/medium.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-29 22:03:03 UTC
  • mfrom: (5416.2.6 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20100929220303-cr95h8iwtggco721
(mbp) Add 'break-lock --force'

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
 
34
import socket
 
35
import thread
36
36
import weakref
 
37
 
37
38
from bzrlib import (
38
39
    debug,
39
40
    errors,
40
 
    osutils,
41
41
    symbol_versioning,
42
42
    trace,
43
43
    ui,
46
46
from bzrlib.smart import client, protocol, request, vfs
47
47
from bzrlib.transport import ssh
48
48
""")
49
 
 
50
 
 
51
 
# We must not read any more than 64k at a time so we don't risk "no buffer
52
 
# space available" errors on some platforms.  Windows in particular is likely
53
 
# to give error 10053 or 10055 if we read more than 64k from a socket.
54
 
_MAX_READ_SIZE = 64 * 1024
55
 
 
 
49
from bzrlib import osutils
 
50
 
 
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
56
56
 
57
57
def _get_protocol_factory_for_bytes(bytes):
58
58
    """Determine the right protocol factory for 'bytes'.
274
274
    def _serve_one_request_unguarded(self, protocol):
275
275
        while protocol.next_read_size():
276
276
            # We can safely try to read large chunks.  If there is less data
277
 
            # than _MAX_READ_SIZE ready, the socket wil just return a short
278
 
            # read immediately rather than block.
279
 
            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)
280
280
            if bytes == '':
281
281
                self.finished = True
282
282
                return
285
285
        self._push_back(protocol.unused_data)
286
286
 
287
287
    def _read_bytes(self, desired_count):
288
 
        return _read_bytes_from_socket(
289
 
            self.socket.recv, desired_count, self._report_activity)
 
288
        return osutils.read_bytes_from_socket(
 
289
            self.socket, self._report_activity)
290
290
 
291
291
    def terminate_due_to_error(self):
292
292
        # TODO: This should log to a server log file, but no such thing
295
295
        self.finished = True
296
296
 
297
297
    def _write_out(self, bytes):
 
298
        tstart = osutils.timer_func()
298
299
        osutils.send_all(self.socket, bytes, self._report_activity)
 
300
        if 'hpss' in debug.debug_flags:
 
301
            thread_id = thread.get_ident()
 
302
            trace.mutter('%12s: [%s] %d bytes to the socket in %.3fs'
 
303
                         % ('wrote', thread_id, len(bytes),
 
304
                            osutils.timer_func() - tstart))
299
305
 
300
306
 
301
307
class SmartServerPipeStreamMedium(SmartServerStreamMedium):
472
478
        if not line.endswith('\n'):
473
479
            # end of file encountered reading from server
474
480
            raise errors.ConnectionReset(
475
 
                "please check connectivity and permissions")
 
481
                "Unexpected end of message. Please check connectivity "
 
482
                "and permissions, and report a bug if problems persist.")
476
483
        return line
477
484
 
478
485
    def _read_line(self):
487
494
class _DebugCounter(object):
488
495
    """An object that counts the HPSS calls made to each client medium.
489
496
 
490
 
    When a medium is garbage-collected, or failing that when atexit functions
491
 
    are run, the total number of calls made on that medium are reported via
492
 
    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.
493
500
    """
494
501
 
495
502
    def __init__(self):
496
503
        self.counts = weakref.WeakKeyDictionary()
497
504
        client._SmartClient.hooks.install_named_hook(
498
505
            'call', self.increment_call_count, 'hpss call counter')
499
 
        atexit.register(self.flush_all)
 
506
        bzrlib.global_state.cleanups.add_cleanup(self.flush_all)
500
507
 
501
508
    def track(self, medium):
502
509
        """Start tracking calls made to a medium.
518
525
        # Increment the count in the WeakKeyDictionary
519
526
        value = self.counts[params.medium]
520
527
        value['count'] += 1
521
 
        request_method = request.request_handlers.get(params.method)
 
528
        try:
 
529
            request_method = request.request_handlers.get(params.method)
 
530
        except KeyError:
 
531
            # A method we don't know about doesn't count as a VFS method.
 
532
            return
522
533
        if issubclass(request_method, vfs.VfsRequest):
523
534
            value['vfs_count'] += 1
524
535
 
596
607
            # which is newer than a previously supplied older-than version.
597
608
            # This indicates that some smart verb call is not guarded
598
609
            # appropriately (it should simply not have been tried).
599
 
            raise AssertionError(
 
610
            trace.mutter(
600
611
                "_remember_remote_is_before(%r) called, but "
601
612
                "_remember_remote_is_before(%r) was called previously."
602
 
                % (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
603
620
        self._remote_version_is_before = version_tuple
604
621
 
605
622
    def protocol_version(self):
716
733
 
717
734
    def _read_bytes(self, count):
718
735
        """See SmartClientStreamMedium._read_bytes."""
719
 
        bytes = self._readable_pipe.read(count)
 
736
        bytes_to_read = min(count, _MAX_READ_SIZE)
 
737
        bytes = self._readable_pipe.read(bytes_to_read)
720
738
        self._report_activity(len(bytes), 'read')
721
739
        return bytes
722
740
 
723
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
 
724
754
class SmartSSHClientMedium(SmartClientStreamMedium):
725
 
    """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
    """
726
760
 
727
 
    def __init__(self, host, port=None, username=None, password=None,
728
 
            base=None, vendor=None, bzr_remote_path=None):
 
761
    def __init__(self, base, ssh_params, vendor=None):
729
762
        """Creates a client that will connect on the first use.
730
763
 
 
764
        :param ssh_params: A SSHParams instance.
731
765
        :param vendor: An optional override for the ssh vendor to use. See
732
766
            bzrlib.transport.ssh for details on ssh vendors.
733
767
        """
734
 
        self._connected = False
735
 
        self._host = host
736
 
        self._password = password
737
 
        self._port = port
738
 
        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'
739
773
        # SmartClientStreamMedium stores the repr of this object in its
740
774
        # _DebugCounter so we have to store all the values used in our repr
741
775
        # method before calling the super init.
742
776
        SmartClientStreamMedium.__init__(self, base)
743
 
        self._read_from = None
 
777
        self._vendor = vendor
744
778
        self._ssh_connection = None
745
 
        self._vendor = vendor
746
 
        self._write_to = None
747
 
        self._bzr_remote_path = bzr_remote_path
748
 
        # for the benefit of progress making a short description of this
749
 
        # transport
750
 
        self._scheme = 'bzr+ssh'
751
779
 
752
780
    def __repr__(self):
753
 
        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/)" % (
754
786
            self.__class__.__name__,
755
 
            self._connected,
756
 
            self._username,
757
 
            self._host,
758
 
            self._port)
 
787
            self._scheme,
 
788
            self._ssh_params.username,
 
789
            self._ssh_params.host,
 
790
            maybe_port)
759
791
 
760
792
    def _accept_bytes(self, bytes):
761
793
        """See SmartClientStreamMedium.accept_bytes."""
762
794
        self._ensure_connection()
763
 
        self._write_to.write(bytes)
764
 
        self._report_activity(len(bytes), 'write')
 
795
        self._real_medium.accept_bytes(bytes)
765
796
 
766
797
    def disconnect(self):
767
798
        """See SmartClientMedium.disconnect()."""
768
 
        if not self._connected:
769
 
            return
770
 
        self._read_from.close()
771
 
        self._write_to.close()
772
 
        self._ssh_connection.close()
773
 
        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
774
805
 
775
806
    def _ensure_connection(self):
776
807
        """Connect this medium if not already connected."""
777
 
        if self._connected:
 
808
        if self._real_medium is not None:
778
809
            return
779
810
        if self._vendor is None:
780
811
            vendor = ssh._get_ssh_vendor()
781
812
        else:
782
813
            vendor = self._vendor
783
 
        self._ssh_connection = vendor.connect_ssh(self._username,
784
 
                self._password, self._host, self._port,
785
 
                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',
786
818
                         '--directory=/', '--allow-writes'])
787
 
        self._read_from, self._write_to = \
788
 
            self._ssh_connection.get_filelike_channels()
789
 
        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))
790
831
 
791
832
    def _flush(self):
792
833
        """See SmartClientStreamMedium._flush()."""
793
 
        self._write_to.flush()
 
834
        self._real_medium._flush()
794
835
 
795
836
    def _read_bytes(self, count):
796
837
        """See SmartClientStreamMedium.read_bytes."""
797
 
        if not self._connected:
 
838
        if self._real_medium is None:
798
839
            raise errors.MediumNotConnected(self)
799
 
        bytes_to_read = min(count, _MAX_READ_SIZE)
800
 
        bytes = self._read_from.read(bytes_to_read)
801
 
        self._report_activity(len(bytes), 'read')
802
 
        return bytes
 
840
        return self._real_medium.read_bytes(count)
803
841
 
804
842
 
805
843
# Port 4155 is the default port for bzr://, registered with IANA.
807
845
BZR_DEFAULT_PORT = 4155
808
846
 
809
847
 
810
 
class SmartTCPClientMedium(SmartClientStreamMedium):
811
 
    """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
    """
812
853
 
813
 
    def __init__(self, host, port, base):
814
 
        """Creates a client that will connect on the first use."""
 
854
    def __init__(self, base):
815
855
        SmartClientStreamMedium.__init__(self, base)
 
856
        self._socket = None
816
857
        self._connected = False
817
 
        self._host = host
818
 
        self._port = port
819
 
        self._socket = None
820
858
 
821
859
    def _accept_bytes(self, bytes):
822
860
        """See SmartClientMedium.accept_bytes."""
823
861
        self._ensure_connection()
824
862
        osutils.send_all(self._socket, bytes, self._report_activity)
825
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
 
826
883
    def disconnect(self):
827
884
        """See SmartClientMedium.disconnect()."""
828
885
        if not self._connected:
831
888
        self._socket = None
832
889
        self._connected = False
833
890
 
 
891
 
 
892
class SmartTCPClientMedium(SmartClientSocketMedium):
 
893
    """A client medium that creates a TCP connection."""
 
894
 
 
895
    def __init__(self, host, port, base):
 
896
        """Creates a client that will connect on the first use."""
 
897
        SmartClientSocketMedium.__init__(self, base)
 
898
        self._host = host
 
899
        self._port = port
 
900
 
834
901
    def _ensure_connection(self):
835
902
        """Connect this medium if not already connected."""
836
903
        if self._connected:
870
937
                    (self._host, port, err_msg))
871
938
        self._connected = True
872
939
 
873
 
    def _flush(self):
874
 
        """See SmartClientStreamMedium._flush().
875
 
 
876
 
        For TCP we do no flushing. We may want to turn off TCP_NODELAY and
877
 
        add a means to do a flush, but that can be done in the future.
878
 
        """
879
 
 
880
 
    def _read_bytes(self, count):
881
 
        """See SmartClientMedium.read_bytes."""
882
 
        if not self._connected:
883
 
            raise errors.MediumNotConnected(self)
884
 
        return _read_bytes_from_socket(
885
 
            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
886
956
 
887
957
 
888
958
class SmartClientStreamMediumRequest(SmartClientMediumRequest):
925
995
        self._medium._flush()
926
996
 
927
997
 
928
 
def _read_bytes_from_socket(sock, desired_count, report_activity):
929
 
    # We ignore the desired_count because on sockets it's more efficient to
930
 
    # read large chunks (of _MAX_READ_SIZE bytes) at a time.
931
 
    try:
932
 
        bytes = osutils.until_no_eintr(sock, _MAX_READ_SIZE)
933
 
    except socket.error, e:
934
 
        if len(e.args) and e.args[0] in (errno.ECONNRESET, 10054):
935
 
            # The connection was closed by the other side.  Callers expect an
936
 
            # empty string to signal end-of-stream.
937
 
            bytes = ''
938
 
        else:
939
 
            raise
940
 
    else:
941
 
        report_activity(len(bytes), 'read')
942
 
    return bytes
943