~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ssh.py

Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
from bzrlib.errors import (ConnectionError,
29
29
                           ParamikoNotPresent,
30
30
                           TransportError,
 
31
                           UnknownSSH,
31
32
                           )
32
33
 
33
34
from bzrlib.osutils import pathjoin
101
102
    # A check for putty's plink or lsh would go here.
102
103
 
103
104
    mutter('falling back to paramiko implementation')
104
 
    _ssh_vendor = ssh.ParamikoVendor()
 
105
    _ssh_vendor = ParamikoVendor()
105
106
    return _ssh_vendor
106
107
 
107
108
 
153
154
        raise NotImplementedError(self.connect_sftp)
154
155
 
155
156
    def connect_ssh(self, username, password, host, port, command):
156
 
        """Make an SSH connection, and return a pipe-like object.
 
157
        """Make an SSH connection.
157
158
        
158
 
        (This is currently unused, it's just here to indicate future directions
159
 
        for this code.)
 
159
        :returns: something with a `close` method, and a `get_filelike_channels`
 
160
            method that returns a pair of (read, write) filelike objects.
160
161
        """
161
162
        raise NotImplementedError(self.connect_ssh)
162
163
        
176
177
register_ssh_vendor('loopback', LoopbackVendor())
177
178
 
178
179
 
 
180
class _ParamikoSSHConnection(object):
 
181
    def __init__(self, channel):
 
182
        self.channel = channel
 
183
 
 
184
    def get_filelike_channels(self):
 
185
        return self.channel.makefile('rb'), self.channel.makefile('wb')
 
186
 
 
187
    def close(self):
 
188
        return self.channel.close()
 
189
 
 
190
 
179
191
class ParamikoVendor(SSHVendor):
180
192
    """Vendor that uses paramiko."""
181
193
 
182
 
    def connect_sftp(self, username, password, host, port):
 
194
    def _connect(self, username, password, host, port):
183
195
        global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
184
196
        
185
197
        load_host_keys()
217
229
                ['Try editing %s or %s' % (filename1, filename2)])
218
230
 
219
231
        _paramiko_auth(username, password, host, t)
 
232
        return t
220
233
        
 
234
    def connect_sftp(self, username, password, host, port):
 
235
        t = self._connect(username, password, host, port)
221
236
        try:
222
 
            sftp = t.open_sftp_client()
 
237
            return t.open_sftp_client()
223
238
        except paramiko.SSHException, e:
224
239
            raise ConnectionError('Unable to start sftp client %s:%d' %
225
240
                                  (host, port), e)
226
 
        return sftp
 
241
 
 
242
    def connect_ssh(self, username, password, host, port, command):
 
243
        t = self._connect(username, password, host, port)
 
244
        try:
 
245
            channel = t.open_session()
 
246
            cmdline = ' '.join(command)
 
247
            channel.exec_command(cmdline)
 
248
            return _ParamikoSSHConnection(channel)
 
249
        except paramiko.SSHException, e:
 
250
            raise ConnectionError('Unable to invoke remote bzr %s:%d' %
 
251
                                  (host, port), e)
227
252
 
228
253
register_ssh_vendor('paramiko', ParamikoVendor())
229
254
 
231
256
class SubprocessVendor(SSHVendor):
232
257
    """Abstract base class for vendors that use pipes to a subprocess."""
233
258
    
 
259
    def _connect(self, argv):
 
260
        proc = subprocess.Popen(argv,
 
261
                                stdin=subprocess.PIPE,
 
262
                                stdout=subprocess.PIPE,
 
263
                                **os_specific_subprocess_params())
 
264
        return SSHSubprocess(proc)
 
265
 
234
266
    def connect_sftp(self, username, password, host, port):
235
267
        try:
236
268
            argv = self._get_vendor_specific_argv(username, host, port,
237
269
                                                  subsystem='sftp')
238
 
            proc = subprocess.Popen(argv,
239
 
                                    stdin=subprocess.PIPE,
240
 
                                    stdout=subprocess.PIPE,
241
 
                                    **os_specific_subprocess_params())
242
 
            sock = SSHSubprocess(proc)
 
270
            sock = self._connect(argv)
243
271
            return SFTPClient(sock)
244
272
        except (EOFError, paramiko.SSHException), e:
245
273
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
253
281
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
254
282
                                  % (host, port, e))
255
283
 
 
284
    def connect_ssh(self, username, password, host, port, command):
 
285
        try:
 
286
            argv = self._get_vendor_specific_argv(username, host, port,
 
287
                                                  command=command)
 
288
            return self._connect(argv)
 
289
        except (EOFError), e:
 
290
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
 
291
                                  % (host, port, e))
 
292
        except (OSError, IOError), e:
 
293
            # If the machine is fast enough, ssh can actually exit
 
294
            # before we try and send it the sftp request, which
 
295
            # raises a Broken Pipe
 
296
            if e.errno not in (errno.EPIPE,):
 
297
                raise
 
298
            raise ConnectionError('Unable to connect to SSH host %s:%s: %s'
 
299
                                  % (host, port, e))
 
300
 
256
301
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
257
302
                                  command=None):
258
303
        """Returns the argument list to run the subprocess with.
468
513
        self.proc.stdout.close()
469
514
        self.proc.wait()
470
515
 
 
516
    def get_filelike_channels(self):
 
517
        return (self.proc.stdout, self.proc.stdin)
471
518