Python asyncssh equivalent of paramiko Channel to display SSH prompt

407 Views Asked by At

I'm trying to establish an interactive SSH shell using asyncssh. So far I have the following code:

import asyncio, asyncssh, sys

async def run_client():
    async with asyncssh.connect(
        HOST, port=PORT, username=USER, password=PASSWORD,
        known_hosts=None) as conn:
        
        stdin, stdout, stderr = await conn.open_session()
        welcome = await stdout.readline()
        print(welcome)

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(run_client())
except (OSError, asyncssh.Error) as exc:
    sys.exit('SSH connection failed: ' + str(exc))

The connection is established successfully, but I can't get the created session to send any data (it awaits forever). Appart for the lack of interactiveness, I got the desired behavior with the following code using paramiko:

from paramiko import SSHClient, AutoAddPolicy

with SSHClient() as client:
    client.set_missing_host_key_policy(AutoAddPolicy())
    client.connect(hostname, port, username, password)
    
    with client.invoke_shell() as chan:
        chan.settimeout(2)
        ## wait until some data is available
        while not channel.recv_ready():
            time.sleep(0.3)
        ## get the data
        welcome = b""
        while channel.recv_ready():
            welcome += channel.recv(2**13)
            time.sleep(delay)
        print(welcome.decode("utf-8"))

Is there a way to do the same using asynssh? Basically I want to get a shell prompt displayed on the python console, and have it responding to writes on stdin.

1

There are 1 best solutions below

0
On BEST ANSWER

Since this is a very specific question, I opened an issue: https://github.com/ronf/asyncssh/issues/597 and the maintainer solved my problem. For the sake of completeness, here is an asyncssh code that does what I wanted.

import asyncio, asyncssh, sys

async def read_timed(stream: asyncssh.SSHReader,
                     timeout: float = 0.1,
                     bufsize: int = 1024)-> str:
    """Read data from a stream with a timeout."""
    ret = ''
    while True:
        try:
            ret += await asyncio.wait_for(stream.read(bufsize), timeout)
        except (asyncio.TimeoutError, asyncio.CancelledError):
            return ret

async def run_client():
    async with asyncssh.connect(
        HOST, port=PORT, username=USER, password=PASSWORD,
        known_hosts=None) as conn:
        
        async with conn.create_process(term_type="ansi") as proc:
            welcome = await read_timed(proc.stdout, timeout=0.3, bufsize=4096)
            print(welcome, end='')

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(run_client())
except (OSError, asyncssh.Error) as exc:
    sys.exit('SSH connection failed: ' + str(exc))