~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ssh.py

  • Committer: Alexander Belchenko
  • Date: 2006-09-05 07:37:01 UTC
  • mto: (1711.9.17 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1984.
  • Revision ID: bialix@ukr.net-20060905073701-93e7c0a44dd7ee05
small but important fix for 'make installer' dependencies

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
from bzrlib.config import config_dir, ensure_config_dir_exists
28
28
from bzrlib.errors import (ConnectionError,
29
29
                           ParamikoNotPresent,
30
 
                           SocketConnectionError,
31
30
                           TransportError,
32
 
                           UnknownSSH,
33
31
                           )
34
32
 
35
33
from bzrlib.osutils import pathjoin
39
37
try:
40
38
    import paramiko
41
39
except ImportError, e:
42
 
    # If we have an ssh subprocess, we don't strictly need paramiko for all ssh
43
 
    # access
44
 
    paramiko = None
 
40
    raise ParamikoNotPresent(e)
45
41
else:
46
42
    from paramiko.sftp_client import SFTPClient
47
43
 
105
101
    # A check for putty's plink or lsh would go here.
106
102
 
107
103
    mutter('falling back to paramiko implementation')
108
 
    _ssh_vendor = ParamikoVendor()
 
104
    _ssh_vendor = ssh.ParamikoVendor()
109
105
    return _ssh_vendor
110
106
 
111
107
 
 
108
 
112
109
def _ignore_sigint():
113
110
    # TODO: This should possibly ignore SIGHUP as well, but bzr currently
114
111
    # doesn't handle it itself.
156
153
        raise NotImplementedError(self.connect_sftp)
157
154
 
158
155
    def connect_ssh(self, username, password, host, port, command):
159
 
        """Make an SSH connection.
 
156
        """Make an SSH connection, and return a pipe-like object.
160
157
        
161
 
        :returns: something with a `close` method, and a `get_filelike_channels`
162
 
            method that returns a pair of (read, write) filelike objects.
 
158
        (This is currently unused, it's just here to indicate future directions
 
159
        for this code.)
163
160
        """
164
161
        raise NotImplementedError(self.connect_ssh)
165
162
        
166
 
    def _raise_connection_error(self, host, port=None, orig_error=None,
167
 
                                msg='Unable to connect to SSH host'):
168
 
        """Raise a SocketConnectionError with properly formatted host.
169
 
 
170
 
        This just unifies all the locations that try to raise ConnectionError,
171
 
        so that they format things properly.
172
 
        """
173
 
        raise SocketConnectionError(host=host, port=port, msg=msg,
174
 
                                    orig_error=orig_error)
175
 
 
176
163
 
177
164
class LoopbackVendor(SSHVendor):
178
165
    """SSH "vendor" that connects over a plain TCP socket, not SSH."""
182
169
        try:
183
170
            sock.connect((host, port))
184
171
        except socket.error, e:
185
 
            self._raise_connection_error(host, port=port, orig_error=e)
 
172
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
 
173
                                  % (host, port, e))
186
174
        return SFTPClient(LoopbackSFTP(sock))
187
175
 
188
176
register_ssh_vendor('loopback', LoopbackVendor())
189
177
 
190
178
 
191
 
class _ParamikoSSHConnection(object):
192
 
    def __init__(self, channel):
193
 
        self.channel = channel
194
 
 
195
 
    def get_filelike_channels(self):
196
 
        return self.channel.makefile('rb'), self.channel.makefile('wb')
197
 
 
198
 
    def close(self):
199
 
        return self.channel.close()
200
 
 
201
 
 
202
179
class ParamikoVendor(SSHVendor):
203
180
    """Vendor that uses paramiko."""
204
181
 
205
 
    def _connect(self, username, password, host, port):
 
182
    def connect_sftp(self, username, password, host, port):
206
183
        global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
207
184
        
208
185
        load_host_keys()
212
189
            t.set_log_channel('bzr.paramiko')
213
190
            t.start_client()
214
191
        except (paramiko.SSHException, socket.error), e:
215
 
            self._raise_connection_error(host, port=port, orig_error=e)
 
192
            raise ConnectionError('Unable to reach SSH host %s:%s: %s' 
 
193
                                  % (host, port, e))
216
194
            
217
195
        server_key = t.get_remote_server_key()
218
196
        server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
225
203
            our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
226
204
        else:
227
205
            warning('Adding %s host key for %s: %s' % (keytype, host, server_key_hex))
228
 
            add = getattr(BZR_HOSTKEYS, 'add', None)
229
 
            if add is not None: # paramiko >= 1.X.X
230
 
                BZR_HOSTKEYS.add(host, keytype, server_key)
231
 
            else:
232
 
                BZR_HOSTKEYS.setdefault(host, {})[keytype] = server_key
 
206
            if host not in BZR_HOSTKEYS:
 
207
                BZR_HOSTKEYS[host] = {}
 
208
            BZR_HOSTKEYS[host][keytype] = server_key
233
209
            our_server_key = server_key
234
210
            our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
235
211
            save_host_keys()
241
217
                ['Try editing %s or %s' % (filename1, filename2)])
242
218
 
243
219
        _paramiko_auth(username, password, host, t)
244
 
        return t
245
220
        
246
 
    def connect_sftp(self, username, password, host, port):
247
 
        t = self._connect(username, password, host, port)
248
 
        try:
249
 
            return t.open_sftp_client()
250
 
        except paramiko.SSHException, e:
251
 
            self._raise_connection_error(host, port=port, orig_error=e,
252
 
                                         msg='Unable to start sftp client')
253
 
 
254
 
    def connect_ssh(self, username, password, host, port, command):
255
 
        t = self._connect(username, password, host, port)
256
 
        try:
257
 
            channel = t.open_session()
258
 
            cmdline = ' '.join(command)
259
 
            channel.exec_command(cmdline)
260
 
            return _ParamikoSSHConnection(channel)
261
 
        except paramiko.SSHException, e:
262
 
            self._raise_connection_error(host, port=port, orig_error=e,
263
 
                                         msg='Unable to invoke remote bzr')
264
 
 
265
 
if paramiko is not None:
266
 
    register_ssh_vendor('paramiko', ParamikoVendor())
 
221
        try:
 
222
            sftp = t.open_sftp_client()
 
223
        except paramiko.SSHException, e:
 
224
            raise ConnectionError('Unable to start sftp client %s:%d' %
 
225
                                  (host, port), e)
 
226
        return sftp
 
227
 
 
228
register_ssh_vendor('paramiko', ParamikoVendor())
267
229
 
268
230
 
269
231
class SubprocessVendor(SSHVendor):
270
232
    """Abstract base class for vendors that use pipes to a subprocess."""
271
233
    
272
 
    def _connect(self, argv):
273
 
        proc = subprocess.Popen(argv,
274
 
                                stdin=subprocess.PIPE,
275
 
                                stdout=subprocess.PIPE,
276
 
                                **os_specific_subprocess_params())
277
 
        return SSHSubprocess(proc)
278
 
 
279
234
    def connect_sftp(self, username, password, host, port):
280
235
        try:
281
236
            argv = self._get_vendor_specific_argv(username, host, port,
282
237
                                                  subsystem='sftp')
283
 
            sock = self._connect(argv)
 
238
            proc = subprocess.Popen(argv,
 
239
                                    stdin=subprocess.PIPE,
 
240
                                    stdout=subprocess.PIPE,
 
241
                                    **os_specific_subprocess_params())
 
242
            sock = SSHSubprocess(proc)
284
243
            return SFTPClient(sock)
285
244
        except (EOFError, paramiko.SSHException), e:
286
 
            self._raise_connection_error(host, port=port, orig_error=e)
287
 
        except (OSError, IOError), e:
288
 
            # If the machine is fast enough, ssh can actually exit
289
 
            # before we try and send it the sftp request, which
290
 
            # raises a Broken Pipe
291
 
            if e.errno not in (errno.EPIPE,):
292
 
                raise
293
 
            self._raise_connection_error(host, port=port, orig_error=e)
294
 
 
295
 
    def connect_ssh(self, username, password, host, port, command):
296
 
        try:
297
 
            argv = self._get_vendor_specific_argv(username, host, port,
298
 
                                                  command=command)
299
 
            return self._connect(argv)
300
 
        except (EOFError), e:
301
 
            self._raise_connection_error(host, port=port, orig_error=e)
302
 
        except (OSError, IOError), e:
303
 
            # If the machine is fast enough, ssh can actually exit
304
 
            # before we try and send it the sftp request, which
305
 
            # raises a Broken Pipe
306
 
            if e.errno not in (errno.EPIPE,):
307
 
                raise
308
 
            self._raise_connection_error(host, port=port, orig_error=e)
 
245
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
 
246
                                  % (host, port, e))
 
247
        except (OSError, IOError), e:
 
248
            # If the machine is fast enough, ssh can actually exit
 
249
            # before we try and send it the sftp request, which
 
250
            # raises a Broken Pipe
 
251
            if e.errno not in (errno.EPIPE,):
 
252
                raise
 
253
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
 
254
                                  % (host, port, e))
309
255
 
310
256
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
311
257
                                  command=None):
522
468
        self.proc.stdout.close()
523
469
        self.proc.wait()
524
470
 
525
 
    def get_filelike_channels(self):
526
 
        return (self.proc.stdout, self.proc.stdin)
527
471