~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/medium.py

  • Committer: Vincent Ladeuil
  • Date: 2011-09-24 09:03:00 UTC
  • mto: This revision was merged to the branch mainline in revision 6164.
  • Revision ID: v.ladeuil+lp@free.fr-20110924090300-83d9iotci3431put
Remove dead code (test suite agree it's dead ;)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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
28
28
import sys
29
29
import urllib
30
30
 
 
31
import bzrlib
31
32
from bzrlib.lazy_import import lazy_import
32
33
lazy_import(globals(), """
33
 
import atexit
34
34
import socket
35
35
import thread
36
36
import weakref
38
38
from bzrlib import (
39
39
    debug,
40
40
    errors,
41
 
    symbol_versioning,
42
41
    trace,
43
42
    ui,
44
43
    urlutils,
45
44
    )
 
45
from bzrlib.i18n import gettext
46
46
from bzrlib.smart import client, protocol, request, vfs
47
47
from bzrlib.transport import ssh
48
48
""")
491
491
        return self._medium._get_line()
492
492
 
493
493
 
 
494
class _VfsRefuser(object):
 
495
    """An object that refuses all VFS requests.
 
496
 
 
497
    """
 
498
 
 
499
    def __init__(self):
 
500
        client._SmartClient.hooks.install_named_hook(
 
501
            'call', self.check_vfs, 'vfs refuser')
 
502
 
 
503
    def check_vfs(self, params):
 
504
        try:
 
505
            request_method = request.request_handlers.get(params.method)
 
506
        except KeyError:
 
507
            # A method we don't know about doesn't count as a VFS method.
 
508
            return
 
509
        if issubclass(request_method, vfs.VfsRequest):
 
510
            raise errors.HpssVfsRequestNotAllowed(params.method, params.args)
 
511
 
 
512
 
494
513
class _DebugCounter(object):
495
514
    """An object that counts the HPSS calls made to each client medium.
496
515
 
497
 
    When a medium is garbage-collected, or failing that when atexit functions
498
 
    are run, the total number of calls made on that medium are reported via
499
 
    trace.note.
 
516
    When a medium is garbage-collected, or failing that when
 
517
    bzrlib.global_state exits, the total number of calls made on that medium
 
518
    are reported via trace.note.
500
519
    """
501
520
 
502
521
    def __init__(self):
503
522
        self.counts = weakref.WeakKeyDictionary()
504
523
        client._SmartClient.hooks.install_named_hook(
505
524
            'call', self.increment_call_count, 'hpss call counter')
506
 
        atexit.register(self.flush_all)
 
525
        bzrlib.global_state.cleanups.add_cleanup(self.flush_all)
507
526
 
508
527
    def track(self, medium):
509
528
        """Start tracking calls made to a medium.
543
562
        value['count'] = 0
544
563
        value['vfs_count'] = 0
545
564
        if count != 0:
546
 
            trace.note('HPSS calls: %d (%d vfs) %s',
547
 
                       count, vfs_count, medium_repr)
 
565
            trace.note(gettext('HPSS calls: {0} ({1} vfs) {2}').format(
 
566
                       count, vfs_count, medium_repr))
548
567
 
549
568
    def flush_all(self):
550
569
        for ref in list(self.counts.keys()):
551
570
            self.done(ref)
552
571
 
553
572
_debug_counter = None
 
573
_vfs_refuser = None
554
574
 
555
575
 
556
576
class SmartClientMedium(SmartMedium):
573
593
            if _debug_counter is None:
574
594
                _debug_counter = _DebugCounter()
575
595
            _debug_counter.track(self)
 
596
        if 'hpss_client_no_vfs' in debug.debug_flags:
 
597
            global _vfs_refuser
 
598
            if _vfs_refuser is None:
 
599
                _vfs_refuser = _VfsRefuser()
576
600
 
577
601
    def _is_remote_before(self, version_tuple):
578
602
        """Is it possible the remote side supports RPCs for a given version?
715
739
    """A client medium using simple pipes.
716
740
 
717
741
    This client does not manage the pipes: it assumes they will always be open.
718
 
 
719
 
    Note that if readable_pipe.read might raise IOError or OSError with errno
720
 
    of EINTR, it must be safe to retry the read.  Plain CPython fileobjects
721
 
    (such as used for sys.stdin) are safe.
722
742
    """
723
743
 
724
744
    def __init__(self, readable_pipe, writeable_pipe, base):
737
757
 
738
758
    def _read_bytes(self, count):
739
759
        """See SmartClientStreamMedium._read_bytes."""
740
 
        bytes = osutils.until_no_eintr(self._readable_pipe.read, count)
 
760
        bytes_to_read = min(count, _MAX_READ_SIZE)
 
761
        bytes = self._readable_pipe.read(bytes_to_read)
741
762
        self._report_activity(len(bytes), 'read')
742
763
        return bytes
743
764
 
744
765
 
 
766
class SSHParams(object):
 
767
    """A set of parameters for starting a remote bzr via SSH."""
 
768
 
 
769
    def __init__(self, host, port=None, username=None, password=None,
 
770
            bzr_remote_path='bzr'):
 
771
        self.host = host
 
772
        self.port = port
 
773
        self.username = username
 
774
        self.password = password
 
775
        self.bzr_remote_path = bzr_remote_path
 
776
 
 
777
 
745
778
class SmartSSHClientMedium(SmartClientStreamMedium):
746
 
    """A client medium using SSH."""
 
779
    """A client medium using SSH.
 
780
    
 
781
    It delegates IO to a SmartClientSocketMedium or
 
782
    SmartClientAlreadyConnectedSocketMedium (depending on platform).
 
783
    """
747
784
 
748
 
    def __init__(self, host, port=None, username=None, password=None,
749
 
            base=None, vendor=None, bzr_remote_path=None):
 
785
    def __init__(self, base, ssh_params, vendor=None):
750
786
        """Creates a client that will connect on the first use.
751
787
 
 
788
        :param ssh_params: A SSHParams instance.
752
789
        :param vendor: An optional override for the ssh vendor to use. See
753
790
            bzrlib.transport.ssh for details on ssh vendors.
754
791
        """
755
 
        self._connected = False
756
 
        self._host = host
757
 
        self._password = password
758
 
        self._port = port
759
 
        self._username = username
 
792
        self._real_medium = None
 
793
        self._ssh_params = ssh_params
760
794
        # for the benefit of progress making a short description of this
761
795
        # transport
762
796
        self._scheme = 'bzr+ssh'
764
798
        # _DebugCounter so we have to store all the values used in our repr
765
799
        # method before calling the super init.
766
800
        SmartClientStreamMedium.__init__(self, base)
767
 
        self._read_from = None
 
801
        self._vendor = vendor
768
802
        self._ssh_connection = None
769
 
        self._vendor = vendor
770
 
        self._write_to = None
771
 
        self._bzr_remote_path = bzr_remote_path
772
803
 
773
804
    def __repr__(self):
774
 
        if self._port is None:
 
805
        if self._ssh_params.port is None:
775
806
            maybe_port = ''
776
807
        else:
777
 
            maybe_port = ':%s' % self._port
 
808
            maybe_port = ':%s' % self._ssh_params.port
778
809
        return "%s(%s://%s@%s%s/)" % (
779
810
            self.__class__.__name__,
780
811
            self._scheme,
781
 
            self._username,
782
 
            self._host,
 
812
            self._ssh_params.username,
 
813
            self._ssh_params.host,
783
814
            maybe_port)
784
815
 
785
816
    def _accept_bytes(self, bytes):
786
817
        """See SmartClientStreamMedium.accept_bytes."""
787
818
        self._ensure_connection()
788
 
        self._write_to.write(bytes)
789
 
        self._report_activity(len(bytes), 'write')
 
819
        self._real_medium.accept_bytes(bytes)
790
820
 
791
821
    def disconnect(self):
792
822
        """See SmartClientMedium.disconnect()."""
793
 
        if not self._connected:
794
 
            return
795
 
        self._read_from.close()
796
 
        self._write_to.close()
797
 
        self._ssh_connection.close()
798
 
        self._connected = False
 
823
        if self._real_medium is not None:
 
824
            self._real_medium.disconnect()
 
825
            self._real_medium = None
 
826
        if self._ssh_connection is not None:
 
827
            self._ssh_connection.close()
 
828
            self._ssh_connection = None
799
829
 
800
830
    def _ensure_connection(self):
801
831
        """Connect this medium if not already connected."""
802
 
        if self._connected:
 
832
        if self._real_medium is not None:
803
833
            return
804
834
        if self._vendor is None:
805
835
            vendor = ssh._get_ssh_vendor()
806
836
        else:
807
837
            vendor = self._vendor
808
 
        self._ssh_connection = vendor.connect_ssh(self._username,
809
 
                self._password, self._host, self._port,
810
 
                command=[self._bzr_remote_path, 'serve', '--inet',
 
838
        self._ssh_connection = vendor.connect_ssh(self._ssh_params.username,
 
839
                self._ssh_params.password, self._ssh_params.host,
 
840
                self._ssh_params.port,
 
841
                command=[self._ssh_params.bzr_remote_path, 'serve', '--inet',
811
842
                         '--directory=/', '--allow-writes'])
812
 
        self._read_from, self._write_to = \
813
 
            self._ssh_connection.get_filelike_channels()
814
 
        self._connected = True
 
843
        io_kind, io_object = self._ssh_connection.get_sock_or_pipes()
 
844
        if io_kind == 'socket':
 
845
            self._real_medium = SmartClientAlreadyConnectedSocketMedium(
 
846
                self.base, io_object)
 
847
        elif io_kind == 'pipes':
 
848
            read_from, write_to = io_object
 
849
            self._real_medium = SmartSimplePipesClientMedium(
 
850
                read_from, write_to, self.base)
 
851
        else:
 
852
            raise AssertionError(
 
853
                "Unexpected io_kind %r from %r"
 
854
                % (io_kind, self._ssh_connection))
815
855
 
816
856
    def _flush(self):
817
857
        """See SmartClientStreamMedium._flush()."""
818
 
        self._write_to.flush()
 
858
        self._real_medium._flush()
819
859
 
820
860
    def _read_bytes(self, count):
821
861
        """See SmartClientStreamMedium.read_bytes."""
822
 
        if not self._connected:
 
862
        if self._real_medium is None:
823
863
            raise errors.MediumNotConnected(self)
824
 
        bytes_to_read = min(count, _MAX_READ_SIZE)
825
 
        bytes = self._read_from.read(bytes_to_read)
826
 
        self._report_activity(len(bytes), 'read')
827
 
        return bytes
 
864
        return self._real_medium.read_bytes(count)
828
865
 
829
866
 
830
867
# Port 4155 is the default port for bzr://, registered with IANA.
832
869
BZR_DEFAULT_PORT = 4155
833
870
 
834
871
 
835
 
class SmartTCPClientMedium(SmartClientStreamMedium):
836
 
    """A client medium using TCP."""
 
872
class SmartClientSocketMedium(SmartClientStreamMedium):
 
873
    """A client medium using a socket.
 
874
    
 
875
    This class isn't usable directly.  Use one of its subclasses instead.
 
876
    """
837
877
 
838
 
    def __init__(self, host, port, base):
839
 
        """Creates a client that will connect on the first use."""
 
878
    def __init__(self, base):
840
879
        SmartClientStreamMedium.__init__(self, base)
 
880
        self._socket = None
841
881
        self._connected = False
842
 
        self._host = host
843
 
        self._port = port
844
 
        self._socket = None
845
882
 
846
883
    def _accept_bytes(self, bytes):
847
884
        """See SmartClientMedium.accept_bytes."""
848
885
        self._ensure_connection()
849
886
        osutils.send_all(self._socket, bytes, self._report_activity)
850
887
 
 
888
    def _ensure_connection(self):
 
889
        """Connect this medium if not already connected."""
 
890
        raise NotImplementedError(self._ensure_connection)
 
891
 
 
892
    def _flush(self):
 
893
        """See SmartClientStreamMedium._flush().
 
894
 
 
895
        For sockets we do no flushing. For TCP sockets we may want to turn off
 
896
        TCP_NODELAY and add a means to do a flush, but that can be done in the
 
897
        future.
 
898
        """
 
899
 
 
900
    def _read_bytes(self, count):
 
901
        """See SmartClientMedium.read_bytes."""
 
902
        if not self._connected:
 
903
            raise errors.MediumNotConnected(self)
 
904
        return osutils.read_bytes_from_socket(
 
905
            self._socket, self._report_activity)
 
906
 
851
907
    def disconnect(self):
852
908
        """See SmartClientMedium.disconnect()."""
853
909
        if not self._connected:
856
912
        self._socket = None
857
913
        self._connected = False
858
914
 
 
915
 
 
916
class SmartTCPClientMedium(SmartClientSocketMedium):
 
917
    """A client medium that creates a TCP connection."""
 
918
 
 
919
    def __init__(self, host, port, base):
 
920
        """Creates a client that will connect on the first use."""
 
921
        SmartClientSocketMedium.__init__(self, base)
 
922
        self._host = host
 
923
        self._port = port
 
924
 
859
925
    def _ensure_connection(self):
860
926
        """Connect this medium if not already connected."""
861
927
        if self._connected:
895
961
                    (self._host, port, err_msg))
896
962
        self._connected = True
897
963
 
898
 
    def _flush(self):
899
 
        """See SmartClientStreamMedium._flush().
900
 
 
901
 
        For TCP we do no flushing. We may want to turn off TCP_NODELAY and
902
 
        add a means to do a flush, but that can be done in the future.
903
 
        """
904
 
 
905
 
    def _read_bytes(self, count):
906
 
        """See SmartClientMedium.read_bytes."""
907
 
        if not self._connected:
908
 
            raise errors.MediumNotConnected(self)
909
 
        return osutils.read_bytes_from_socket(
910
 
            self._socket, self._report_activity)
 
964
 
 
965
class SmartClientAlreadyConnectedSocketMedium(SmartClientSocketMedium):
 
966
    """A client medium for an already connected socket.
 
967
    
 
968
    Note that this class will assume it "owns" the socket, so it will close it
 
969
    when its disconnect method is called.
 
970
    """
 
971
 
 
972
    def __init__(self, base, sock):
 
973
        SmartClientSocketMedium.__init__(self, base)
 
974
        self._socket = sock
 
975
        self._connected = True
 
976
 
 
977
    def _ensure_connection(self):
 
978
        # Already connected, by definition!  So nothing to do.
 
979
        pass
911
980
 
912
981
 
913
982
class SmartClientStreamMediumRequest(SmartClientMediumRequest):