~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

Expanded the Transport test suite. Including delete, copy, move, etc. Updated SftpTransport to conform.

Show diffs side-by-side

added added

removed removed

Lines of Context:
320
320
            if self._do_prefetch and hasattr(f, 'prefetch'):
321
321
                f.prefetch()
322
322
            return f
323
 
        except (IOError, paramiko.SSHException), x:
324
 
            raise NoSuchFile('Error retrieving %s: %s' % (path, str(x)), x)
 
323
        except (IOError, paramiko.SSHException), e:
 
324
            self._translate_io_exception(e, path, ': error retrieving')
325
325
 
326
326
    def get_partial(self, relpath, start, length=None):
327
327
        """
412
412
            path = self._abspath(relpath)
413
413
            self._sftp.mkdir(path)
414
414
        except (paramiko.SSHException, IOError), e:
415
 
            self._translate_io_exception(e, relpath, ': unable to mkdir')
416
 
 
417
 
    def _translate_io_exception(self, e, relpath, more_info=''):
 
415
            self._translate_io_exception(e, relpath, ': unable to mkdir',
 
416
                failure_exc=FileExists)
 
417
 
 
418
    def _translate_io_exception(self, e, path, more_info='', failure_exc=NoSuchFile):
 
419
        """Translate a paramiko or IOError into a friendlier exception.
 
420
 
 
421
        :param e: The original exception
 
422
        :param path: The path in question when the error is raised
 
423
        :param more_info: Extra information that can be included,
 
424
                          such as what was going on
 
425
        :param failure_exc: Paramiko has the super fun ability to raise completely
 
426
                           opaque errors that just set "e.args = ('Failure',)" with
 
427
                           no more information.
 
428
                           This sometimes means FileExists, but it also sometimes
 
429
                           means NoSuchFile
 
430
        """
418
431
        # paramiko seems to generate detailless errors.
419
 
        self._translate_error(e, relpath, raise_generic=False)
 
432
        self._translate_error(e, path, raise_generic=False)
420
433
        if hasattr(e, 'args'):
421
434
            if (e.args == ('No such file or directory',) or
422
435
                e.args == ('No such file',)):
423
 
                raise NoSuchFile(relpath, str(e) + more_info)
 
436
                raise NoSuchFile(path, str(e) + more_info)
424
437
            if (e.args == ('mkdir failed',)):
425
 
                raise FileExists(relpath, str(e) + more_info)
 
438
                raise FileExists(path, str(e) + more_info)
426
439
            # strange but true, for the paramiko server.
427
440
            if (e.args == ('Failure',)):
428
 
                raise FileExists(relpath, str(e) + more_info)
 
441
                raise failure_exc(path, str(e) + more_info)
429
442
        raise e
430
443
 
431
444
    def append(self, relpath, f):
437
450
            path = self._abspath(relpath)
438
451
            fout = self._sftp.file(path, 'ab')
439
452
            self._pump(f, fout)
440
 
        except (IOError, paramiko.SSHException), x:
441
 
            raise TransportError('Unable to append file %r' % (path,), x)
 
453
        except (IOError, paramiko.SSHException), e:
 
454
            self._translate_io_exception(e, relpath, ': unable to append')
442
455
 
443
456
    def copy(self, rel_from, rel_to):
444
457
        """Copy the item at rel_from to the location at rel_to"""
469
482
                    fout.close()
470
483
            finally:
471
484
                fin.close()
472
 
        except (IOError, paramiko.SSHException), x:
473
 
            raise TransportError('Unable to copy %r to %r' % (path_from, path_to), x)
 
485
        except (IOError, paramiko.SSHException), e:
 
486
            self._translate_io_exception(e, path_from, ': unable copy to: %r' % path_to)
474
487
 
475
488
    def copy_to(self, relpaths, other, pb=None):
476
489
        """Copy a set of entries from self into another Transport.
505
518
        path_to = self._abspath(rel_to)
506
519
        try:
507
520
            self._sftp.rename(path_from, path_to)
508
 
        except (IOError, paramiko.SSHException), x:
509
 
            raise TransportError('Unable to move %r to %r' % (path_from, path_to), x)
 
521
        except (IOError, paramiko.SSHException), e:
 
522
            self._translate_io_exception(e, path_from, ': unable to move to: %r' % path_to)
510
523
 
511
524
    def delete(self, relpath):
512
525
        """Delete the item at relpath"""
513
526
        path = self._abspath(relpath)
514
527
        try:
515
528
            self._sftp.remove(path)
516
 
        except (IOError, paramiko.SSHException), x:
517
 
            raise TransportError('Unable to delete %r' % (path,), x)
 
529
        except (IOError, paramiko.SSHException), e:
 
530
            self._translate_io_exception(e, path, ': unable to delete')
518
531
            
519
532
    def listable(self):
520
533
        """Return True if this store supports listing."""
528
541
        path = self._abspath(relpath)
529
542
        try:
530
543
            return self._sftp.listdir(path)
531
 
        except (IOError, paramiko.SSHException), x:
532
 
            raise TransportError('Unable to list folder %r' % (path,), x)
 
544
        except (IOError, paramiko.SSHException), e:
 
545
            self._translate_io_exception(e, path, ': failed to list_dir')
533
546
 
534
547
    def stat(self, relpath):
535
548
        """Return the stat information for a file."""
536
549
        path = self._abspath(relpath)
537
550
        try:
538
551
            return self._sftp.stat(path)
539
 
        except (IOError, paramiko.SSHException), x:
540
 
            raise TransportError('Unable to stat %r' % (path,), x)
 
552
        except (IOError, paramiko.SSHException), e:
 
553
            self._translate_io_exception(e, path, ': unable to stat')
541
554
 
542
555
    def lock_read(self, relpath):
543
556
        """
603
616
            try:
604
617
                port = int(port)
605
618
            except ValueError:
 
619
                # TODO: Should this be ConnectionError?
606
620
                raise TransportError('%s: invalid port number' % port)
607
621
        host = urllib.unquote(host)
608
622
 
654
668
        try:
655
669
            t = paramiko.Transport((self._host, self._port or 22))
656
670
            t.start_client()
657
 
        except paramiko.SSHException:
658
 
            raise TransportError('Unable to reach SSH host %s:%d' % (self._host, self._port))
 
671
        except paramiko.SSHException, e:
 
672
            raise ConnectionError('Unable to reach SSH host %s:%d' %
 
673
                                  (self._host, self._port), e)
659
674
            
660
675
        server_key = t.get_remote_server_key()
661
676
        server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
685
700
        
686
701
        try:
687
702
            self._sftp = t.open_sftp_client()
688
 
        except paramiko.SSHException:
689
 
            raise BzrError('Unable to find path %s on SFTP server %s' % \
690
 
                (self._path, self._host))
 
703
        except paramiko.SSHException, e:
 
704
            raise ConnectionError('Unable to start sftp client %s:%d' %
 
705
                                  (self._host, self._port), e)
691
706
 
692
707
    def _sftp_auth(self, transport):
693
708
        # paramiko requires a username, but it might be none if nothing was supplied
735
750
                user=username, host=self._host)
736
751
        try:
737
752
            transport.auth_password(username, password)
738
 
        except paramiko.SSHException:
739
 
            raise TransportError('Unable to authenticate to SSH host as %s@%s' % \
740
 
                (username, self._host))
 
753
        except paramiko.SSHException, e:
 
754
            raise ConnectionError('Unable to authenticate to SSH host as %s@%s' %
 
755
                                  (username, self._host), e)
741
756
 
742
757
    def _try_pkey_auth(self, transport, pkey_class, username, filename):
743
758
        filename = os.path.expanduser('~/.ssh/' + filename)
785
800
            handle = msg.get_string()
786
801
            return SFTPFile(self._sftp, handle, 'w', -1)
787
802
        except (paramiko.SSHException, IOError), e:
788
 
            self._translate_io_exception(e, relpath, ': unable to open')
 
803
            self._translate_io_exception(e, relpath, ': unable to open',
 
804
                failure_exc=FileExists)
789
805