Mini Shell
"""Convenience classes for paramiko"""
import stat
import paramiko
from rads.exceptions import SSHError
class SSHConn(paramiko.SSHClient):
"""Context manager that handles SSH connections.
Most exceptions may raise SSHError, but accessing the sftp attribute or
paramiko functions inherited from paramiko.SSHClient directly may raise
socket.error or paramiko.ssh_exception.SSHException"""
def __init__(self, sftp, **kwargs):
"""Args for the context manager are read from here.
If you don't need sftp functionality, set sftp=False"""
super(SSHConn, self).__init__()
self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self._kwargs = kwargs
self._use_sftp = sftp
self.sftp = None
def __enter__(self):
"""Connects to SSH using the context manager 'with' statement"""
try:
self.connect(**self._kwargs)
if self._use_sftp:
self.sftp = self.open_sftp()
except Exception as exc:
raise SSHError(exc)
return self
def __exit__(self, exc_type, exc_val, traceback):
"""Disconnects from SSH on un-indent, even if an exception is raised"""
if self._use_sftp:
self.sftp.close()
self.close()
def listdir(self, path, _type=None):
"""List contents of a remote dir similar to os.listdir()"""
try:
items = []
for lstat in self.sftp.listdir_iter(path):
if _type is None or _type(lstat.st_mode):
items.append(lstat.filename)
return items
except Exception as exc:
raise SSHError(exc)
def ls_dirs(self, path):
"""listdir() but only folders"""
return self.listdir(path, _type=stat.S_ISDIR)
def ls_files(self, path):
"""listdir() but only files"""
return self.listdir(path, _type=stat.S_ISREG)
def run(self, cmd):
"""Execute a command and return a CmdResult object"""
try:
return CmdResult(self, cmd)
except Exception as exc:
raise SSHError(exc)
class CmdResult(object):
"""Holds a command result"""
def __init__(self, conn, cmd):
chan = conn.get_transport().open_session()
chan.set_environment_variable('shell', 'xterm')
chan.exec_command(cmd)
stdout = chan.makefile('rb', -1)
stderr = chan.makefile_stderr('rb', -1)
self.stdout = stdout.read()
self.stderr = stderr.read()
if self.stderr.startswith('stdin: is not a tty\n'):
# this is caused by 'mesg y' in /etc/bashrc. Using a pty would
# avoid that, but then mix stdout and stderr which is not wanted.
self.stderr = self.stderr[20:]
self.rcode = chan.recv_exit_status()
chan.close()
Zerion Mini Shell 1.0