GithubHelp home page GithubHelp logo

ronf / asyncssh Goto Github PK

View Code? Open in Web Editor NEW
1.5K 1.5K 143.0 5.82 MB

AsyncSSH is a Python package which provides an asynchronous client and server implementation of the SSHv2 protocol on top of the Python asyncio framework.

License: Eclipse Public License 2.0

Python 100.00%

asyncssh's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

asyncssh's Issues

Ssh agent forwarding

Thanks a lot for getting the ssh-agent hooked up. It's working great and we're now 90% of the way to using asyncssh pretty heavily in production, but hit another roadblock which should be a bit easier to solve.

We rely on agent forwarding to grant access to some of our servers and/or git repositories. Afaik it works fairly simply, by just opening a unix socket on the remote side and forwarding all read/write traffic to the local socket.

I'm busy investigating implementing it outside of the library, but it likely makes sense as an additional feature inside asyncssh too.

Reading `~/.ssh/known_hosts` runs on every connection

The library I've been working on connects to a large number of hosts simultaneously which seems to break the asyncssh known_hosts reading implementation. As far as I can tell it's designed to scan through and decode the entire file for every connection -- which causes obvious problems.

I don't know if you want to try cache the scan internally, or if you'd rather I passed in the known_hosts variable into create_connection... at which point the known_hosts._parse_entries function should be properly exposed.

asyncssh should have debug logging

asyncssh should have a package logger with ability to set the logging level of the logger and get debug and info logging level information of relevant information from various classes and functions inside the package

`connect()` needs a timeout parameter

I'm writing a tool that spins up and manages instances on Amazon EC2.

When you launch a new instance on EC2, SSH takes some time to come up. So I periodically try connecting to the instance to find out when SSH becomes available. My tool does this for potentially hundreds of instances that get launched at once.

To minimize the amount of time between when SSH becomes available and I find out about it, I want to check for connectivity on a frequent basis. To do that I need a way to timeout connection attempts and try again. This doesn't appear to be possible at the moment.

Specifically, it looks like connect() and create_connection() are lacking a timeout parameter, or something similar.

So the below snippet, for example, takes well over a minute to error out because SSH is not available, when I want it to timeout after, say, only 3 seconds.

@asyncio.coroutine
def conn_error():
    with (yield from asyncssh.connect(host='google.com')) as conn:
        stdin, stdout, stderr = yield from conn.open_session('echo "Test"')
        output = yield from stdout.read()
        print(output, end='')


if __name__ == '__main__':
    asyncio.get_event_loop().run_until_complete(conn_error())

Does it make sense to add a timeout parameter to the various connect() methods? Paramiko's connect() supports this, which is how I am able to do this currently.

data should be bytes

Currently data_received(self, data, datatype) is giving str,
and it is decoded by session_encoding. default is utf-8.
however I think data should be bytes or bytearray.

reason 1

for examaple

# test.py
import sys
import random
import time

data = 'あいうえお'.encode('utf-8')

while True:
    r = random.randint(0, len(data))
    sys.stdout.buffer.write(data[:r])
    sys.stdout.buffer.flush()
    time.sleep(0.5)
    sys.stdout.buffer.write(data[r:])
    sys.stdout.buffer.flush()
    sys.stdout.buffer.write(b'\r\n')
    sys.stdout.buffer.flush()

server is https://gist.github.com/sharow/809242c0c12d66d0594f

# client run:
python test.py | ssh -o StrictHostKeyChecking=no -p 2200 [email protected]

and server say

SSH connection received from x.x.x.x
あいうえお
あいうえお
あいうえお
あいうえ
Exception in callback SSHConnection._cleanup(DisconnectErr...ecode error',))
handle: <Handle SSHConnection._cleanup(DisconnectErr...ecode error',))>
Traceback (most recent call last):
  File "/usr/lib/python3.4/asyncio/events.py", line 120, in _run
    self._callback(*self._args)
  File "/usr/lib/python3.4/site-packages/asyncssh/connection.py", line 394, in _cleanup
    self._owner.connection_lost(exc)
  File "simple_server.py", line 31, in connection_lost
    raise exc
  File "/usr/lib/python3.4/site-packages/asyncssh/connection.py", line 473, in data_received
    while self._inpbuf and self._recv_handler():
  File "/usr/lib/python3.4/site-packages/asyncssh/connection.py", line 694, in _recv_packet
    processed = self.process_packet(pkttype, packet)
  File "/usr/lib/python3.4/site-packages/asyncssh/packet.py", line 161, in process_packet
    self.packet_handlers[pkttype](self, pkttype, packet)
  File "/usr/lib/python3.4/site-packages/asyncssh/connection.py", line 1485, in _process_channel_msg
    chan.process_packet(pkttype, packet)
  File "/usr/lib/python3.4/site-packages/asyncssh/packet.py", line 161, in process_packet
    self.packet_handlers[pkttype](self, pkttype, packet)
  File "/usr/lib/python3.4/site-packages/asyncssh/channel.py", line 329, in _process_data
    self._accept_data(data)
  File "/usr/lib/python3.4/site-packages/asyncssh/channel.py", line 229, in _accept_data
    self._deliver_data(data, datatype)
  File "/usr/lib/python3.4/site-packages/asyncssh/channel.py", line 201, in _deliver_data
    'Unicode decode error') from None
asyncssh.misc.DisconnectError: Disconnect Error: Unicode decode error

payload is correct utf-8. data is arrived but divided sometime.
that mean.. session middleware shouldn't care about encoding.

reason 2

encoding is depend on client. NOT SSH SEVER.

also paramiko channel.recv() is giving byte.

Example crashing with Python 3.4

I was trying to run the example that echoes "Hello", and found errors in python 3.4 in mac os

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/tasks.py", line 93, in del
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/futures.py", line 218, in del
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/base_events.py", line 941, in call_exception_handler
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/logging/init.py", line 1303, in error
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/logging/init.py", line 1409, in _log
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/logging/init.py", line 1419, in handle
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/logging/init.py", line 1489, in callHandlers
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/logging/init.py", line 853, in handle
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/logging/init.py", line 984, in emit
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/logging/init.py", line 906, in handleError
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/traceback.py", line 169, in print_exception
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/traceback.py", line 153, in _format_exception_iter
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/traceback.py", line 18, in _format_list_iter
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/traceback.py", line 65, in _extract_tb_or_stack_iter
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/linecache.py", line 15, in getline
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/linecache.py", line 41, in getlines
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/linecache.py", line 126, in updatecache
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/tokenize.py", line 437, in open
AttributeError: 'module' object has no attribute 'open'

No trusted server host keys available

hi all,

I just want run hello world example of asyncssh .

My python version is : 3.5

python3.5 simple_client.py
SSH connection failed: Disconnect Error: No trusted server host keys available

I had already run simple_server.py from another terminal .

After check source code of simple_client.py , it just try to connect localhost .

so , where is the location for trusted server host keys for localhost ?

thanks in advance .

protect known_hosts parsing

my known_hosts file had an odd line - that contained a single =
in that case a simple client as copied from here crahsed with an exception
IMHO such lines could just be ignored, which is what ssh does apparently

~/git/asyncio-samples $ ./myssh2
Exception in callback SSHConnection.connection_made(<_SelectorSoc...e, bufsize=0>>)
handle: <Handle SSHConnection.connection_made(<_SelectorSoc...e, bufsize=0>>)>
Traceback (most recent call last):
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/events.py", line 125, in _run
    self._callback(*self._args)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/asyncssh/connection.py", line 456, in connection_made
    self._connection_made()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/asyncssh/connection.py", line 1753, in _connection_made
self._peer_addr, self._port)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/asyncssh/known_hosts.py", line 159, in match_known_hosts
entries = _parse_entries(known_hosts)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/asyncssh/known_hosts.py", line 94, in _parse_entries
line) from None
ValueError: Invalid known hosts entry: =
SSH connection failed: Disconnect Error: Connection lost

Windows IPv6 Support

@ronf default socket binding on Windows dies on me with the following error (using devel branch, Windows 8.1 64-bit, Python 3.4 64-bit):

$ python forwarder.py
Traceback (most recent call last):
  File "forwarder.py", line 17, in <module>
    get_event_loop().run_until_complete(run_client())
  File "C:\Python34\lib\asyncio\base_events.py", line 316, in run_until_complete
    return future.result()
  File "C:\Python34\lib\asyncio\futures.py", line 275, in result
    raise self._exception
  File "C:\Python34\lib\asyncio\tasks.py", line 236, in _step
    result = coro.send(value)
  File "forwarder.py", line 11, in run_client
    listener = yield from conn.forward_local_port('', 8450, '127.0.0.1', 8450)
  File "C:\Python34\lib\site-packages\asyncssh\connection.py", line 1874, in forward_local_port
    listen_port))
  File "C:\Python34\lib\site-packages\asyncssh\listener.py", line 217, in create_tcp_forward_listener
    sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, True)
AttributeError: 'module' object has no attribute 'IPPROTO_IPV6'

I was trying a simple TCP port forwarding:

#!/usr/bin/env python3
from asyncio import coroutine, get_event_loop
from getpass import getpass
from sys import exit

from asyncssh import connect, Error

@coroutine
def run_client():
    with (yield from connect('something.com', username='ubuntu')) as conn:
        listener = yield from conn.forward_local_port('', 8080, '127.0.0.1', 8080)
        yield from listener.wait_closed()

    yield from conn.wait_closed()

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

Workaround is to pass a non-empty, IPv4-based IP address as the first argument to conn.forward_local_port.

This is apparently an issue in Python itself:

http://bugs.python.org/issue6926

Where they suggest you hardcode the constant, or otherwise avoid its use.

Old Tornado discussion:

https://groups.google.com/forum/#!topic/python-tornado/fBsiswcLL9E

Add CONTRIBUTING guide

Are there basic instructions anywhere on how to run unit tests and other such things for new/aspiring AsyncSSH contributors?

Many projects typically have a CONTRIBUTING doc at the root of the project with this kind of info. Here is neovim's contributing guide, for example.

AsyncSSH's guide obviously does not need to be as long, but basic instructions on what to do, for example, when you want to contribute a patch would be useful.

Piping binary data around seems to break things.

I've been working on something to take screenshots from some of our kiosks over SSH but binary data seems to break things. I get an empty SSHCompletedProcess back:

SSHCompletedProcess(exit_status=None, exit_signal=None, stdout='', stderr='')

This isn't just redirection (per my example below). Text works fine. And if I remotely pipe binary data through base64 (for example), I can get it back centrally and then reprocess it back into binary... But that's more computation and more code. It'd be nice if asyncssh handled binary natively.

import os, asyncio, asyncssh

CMD_SCREENSHOT = 'DISPLAY=:0 import -window root png:-'
CMD_SCREENSHOT = 'ls -Al'

screenshot_directory = os.path.join(settings.MEDIA_ROOT, 'kiosks')
if not os.path.exists(screenshot_directory):
    os.mkdir(screenshot_directory)  # just in case

async def run_client(kiosk, via=None):
    kwargs = {
        # connection info
    }
    async with asyncssh.connect(**kwargs) as conn:
        result = await conn.run(
            CMD_SCREENSHOT,
            check=True,
            stdout=os.path.join(screenshot_directory, '%s.png' % kiosk.pk)
        )
        print(result)

asyncio.get_event_loop().run_until_complete(asyncio.gather(*(
    run_client(kiosk)
    for kiosk in Kiosk.objects.filter(...)
), return_exceptions=True))

Connection errors when opening many parallel sessions

Occasionally when opening many sessions at the same time over a single connection asyncssh 1.5.3 seems to hit some race condition. The python backtrace doesn't look particularly interesting but is available at https://gist.github.com/qix/67a6d94f5307c8699afcbcd61c50c463 if it might help.

What did hint towards the race condition is the following output in auth.log:

May 27 18:46:30 sensu1 sshd[719]: error: buffer_get_ret: trying to get more bytes 19 than in buffer 14
May 27 18:46:30 sensu1 sshd[719]: error: buffer_get_string_ret: buffer_get failed
May 27 18:46:30 sensu1 sshd[719]: fatal: buffer_get_string: buffer error
May 27 18:46:30 sensu1 sshd[719]: pam_unix(sshd:session): session closed for user josh
May 27 18:46:30 sensu1 sshd[720]: error: buffer_get_ret: trying to get more bytes 20 than in buffer 15
May 27 18:46:30 sensu1 sshd[720]: error: buffer_get_string_ret: buffer_get failed
May 27 18:46:30 sensu1 sshd[720]: fatal: buffer_get_string: buffer error
May 27 18:46:30 sensu1 sshd[720]: pam_unix(sshd:session): session closed for user josh
May 27 18:46:30 sensu1 sshd[831]: fatal: mm_request_receive: read: Connection reset by peer
May 27 18:46:30 sensu1 sshd[832]: fatal: mm_request_send: write: Broken pipe
May 27 18:46:30 sensu1 sshd[830]: fatal: mm_request_send: write: Broken pipe
May 27 18:46:30 sensu1 sshd[828]: fatal: mm_request_send: write: Broken pipe
May 27 18:46:30 sensu1 sshd[834]: fatal: mm_request_receive: read: Connection reset by peer
May 27 18:46:30 sensu1 sshd[835]: fatal: mm_request_send: write: Broken pipe
May 27 18:46:30 sensu1 sshd[836]: fatal: mm_request_send: write: Broken pipe
May 27 18:46:30 sensu1 sshd[833]: fatal: mm_request_send: write: Broken pipe
May 27 18:46:30 sensu1 sshd[837]: fatal: mm_request_send: write: Broken pipe
May 27 18:46:30 sensu1 sshd[838]: fatal: mm_request_send: write: Broken pipe
May 27 18:46:30 sensu1 sshd[829]: fatal: mm_request_send: write: Broken pipe

Performance Issues when Forwarding TCP

I'd like to know if anyone knows of performance enhancements we can apply to/with AsyncSSH to boost performance for this use case.

I am noticing a large amount of overhead when forwarding TCP connections via AsyncSSH vs the OpenSSH client binary.

For example, here are some quick timings using OpenSSH on Windows (8.1, OpenSSH_7.0p1, OpenSSL 1.0.2d 9 Jul 2015) and Ubuntu (14.04 LTS, OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.6, OpenSSL 1.0.1f 6 Jan 2014); forwarding command similar to ssh -L 443:localhost:443 [email protected]:

##### Windows OpenSSH Client Binary (-L) #####
$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore"); from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
10 loops, best of 3: 13.8 msec per loop

$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore"); from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
10 loops, best of 3: 18.6 msec per loop

##### Ubuntu OpenSSH Client Binary (-L) #####
$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore");  from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
100 loops, best of 3: 11.1 msec per loop

$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore");  from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
100 loops, best of 3: 11.5 msec per loop

Then I tried using AsyncSSH with basically the following script:

#!/usr/bin/env python3
from asyncio import coroutine, get_event_loop, set_event_loop
from sys import exit

from asyncssh import connect, Error

@coroutine
def run_client():
    with (yield from connect('something.com', username='ubuntu')) as conn:
        listener = yield from conn.forward_local_port('127.0.0.1', 443, '127.0.0.1', 443)
        yield from listener.wait_closed()

    yield from conn.wait_closed()

try:
    # Windows specific event loop
    #LOOP = ProactorEventLoop()
    #set_event_loop(LOOP)
    get_event_loop().run_until_complete(run_client())
except (OSError, Error) as exc:
    exit('SSH connection failed: ' + str(exc))

What I found on Windows:

##### Windows AsyncSSH (develop branch) TCP Forwarder with Proactor Event Loop #####
$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore"); from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
10 loops, best of 3: 229 msec per loop

$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore"); from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
10 loops, best of 3: 213 msec per loop


##### Windows AsyncSSH (develop branch) TCP Forwarder with Default Event Loop #####
$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore"); from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
10 loops, best of 3: 228 msec per loop

$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore"); from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
10 loops, best of 3: 226 msec per loop

and what I found on Ubuntu (practically the same overhead):

##### Ubuntu AsyncSSH (develop branch) TCP Forwarder with Default Event Loop #####
$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore");  from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
10 loops, best of 3: 201 msec per loop

$ python -mtimeit -s 'from warnings import filterwarnings; filterwarnings("ignore");  from requests import Session; s = Session()' 's.get("https://127.0.0.1", verify=False)'
10 loops, best of 3: 206 msec per loop

AsyncSSH's dependencies are not visible to pip

pip thinks that AsynsSSH does not have any dependencies.

$ pip3 show asyncssh

---
Metadata-Version: 1.1
Name: asyncssh
Version: 1.1.1
Summary: AsyncSSH: Asynchronous SSHv2 client and server library
Home-page: http://asyncssh.timeheart.net
Author: Ron Frederick
Author-email: [email protected]
License: Eclipse Public License v1.0
Location: /usr/local/lib/python3.4/site-packages
Requires: 

This may well be the case, but there are several modules mentioned in the README that the user has to install to enable additional functionality.

If you expect a large proportion of the user base to need any of these modules, I suggest capturing them as dependencies in PyPI so that pip will automatically install them when it installs AsyncSSH.

This greatly simplifies things for your users. I would venture that most people would expect things to just work after a single pip install.

For comparison, this is what pip sees for Pandas (note the Requires: line):

$ pip3 show pandas

---
Metadata-Version: 2.0
Name: pandas
Version: 0.16.0
Summary: Powerful data structures for data analysis, time series,and statistics
Home-page: http://pandas.pydata.org
Author: The PyData Development Team
Author-email: [email protected]
License: BSD
Location: /usr/local/lib/python3.4/site-packages
Requires: python-dateutil, pytz, numpy

Py3.5 async/await + sftp.posix_rename = TypeError: object generator can't be used in 'await' expression

While converting some of my code to new async syntax I'm got the following exception:

    await sftp.posix_rename(tmp, "/etc/test.conf")
TypeError: object generator can't be used in 'await' expression

Snippet of problem code:

        with (await asyncssh.connect(addr)) as conn:
            with (await conn.start_sftp_client()) as sftp:
                tmp = "/tmp/{}.cfg".format(randint(1e8, 1e9))
                cfg = await sftp.open(tmp, 'w')
                await cfg.write('\n\n'.join(config) + '\n')
                await cfg.close()
                await sftp.posix_rename(tmp, "/etc/test.conf")

After some googling I found similar (resolved) problem: aio-libs/aiomcache#7

I think that problem can be resolved by adding missing @asyncio.coroutine decorator to posix_rename method (and may be some others).

Docs for SSHClientConnection.get_extra_info() need a little elaboration

I have an SSH client connection that I reuse and pass around to various methods so as to avoid having to recreate the connection over and over.

Sometimes, I want those methods to print some informational messages about what they are doing and to which server, so I need a way to extract the server IP address from an existing SSH client.

I eventually figured out that the way to do this is as follows:

with (yield from asyncssh.connect('localhost')) as conn:
    (host, port, _, _) = conn.get_extra_info('peername')

This is not clear from the docs for get_extra_info(). It would be helpful if they linked to the appropriate Python docs (if any) that show all the supported values for the name input parameter.

An additional but separate suggestion would be to perhaps allow users to call get_extra_info() without any arguments and get back a dictionary of all the available information. That would help with discovery of the available properties.

asyncio.wait_for works incorrect for SSHStreamSession._block_read

When I want to to do consecuitive readline() with timeout via asyncio.wait_for got exception that stream still blocked. Fragment of example that leads to crash:

        stdin, stdout, stderr = yield from conn.open_session('')

        for op in ['ping 192.168.0.247 -c3', 'ping 192.168.0.2 -c3']:
            stdin.write(op + '\n')

            while not stdout.at_eof():
                try:
                    result = yield from asyncio.wait_for(stdout.readline(), 5)
                    print(op, '=', result, end='')
                except (asyncssh.BreakReceived, asyncssh.SignalReceived, asyncio.TimeoutError):
                    break
                except (asyncssh.TerminalSizeChanged):
                    continue

The exception here is:

Traceback (most recent call last):
  File "c2.py", line 39, in <module>
    asyncio.get_event_loop().run_until_complete(run_client())
  File "/usr/lib64/python3.4/asyncio/base_events.py", line 268, in run_until_complete
    return future.result()
  File "/usr/lib64/python3.4/asyncio/futures.py", line 277, in result
    raise self._exception
  File "/usr/lib64/python3.4/asyncio/tasks.py", line 236, in _step
    result = next(coro)
  File "c2.py", line 22, in run_client
    result = yield from asyncio.wait_for(stdout.readline(), 5)
  File "/usr/lib64/python3.4/asyncio/tasks.py", line 371, in wait_for
    return fut.result()
  File "/usr/lib64/python3.4/asyncio/futures.py", line 277, in result
    raise self._exception
  File "/usr/lib64/python3.4/asyncio/tasks.py", line 236, in _step
    result = next(coro)
  File "~/venv3/lib/python3.4/site-packages/asyncssh/stream.py", line 385, in readline
    yield from self._block_read(datatype)
  File "~/venv3/lib/python3.4/site-packages/asyncssh/stream.py", line 219, in _block_read
    raise RuntimeError('read called while another coroutine is '
RuntimeError: read called while another coroutine is already waiting to read

So if on the first try asyncio.wait_for generate asyncio.TimeoutError the SSHStreamSession._read_waiter still has the cancelled Task that leads to this exception.

Environment:
OS: Fedora 22 x86_64
Python 3.4.2 (default, Jul 9 2015, 17:24:30)
AsyncSSH 1.3.0

For me was ok just do _SSHStreamSession.unblock_read for the case if Task with _block_read was cancelled (see the 795a6f6).

Unable to get exit status

I tried modified sample from docs:

    import asyncio, asyncssh, sys

    class MySSHClientSession(asyncssh.SSHClientSession):
        def data_received(self, data, datatype):
            if datatype == asyncssh.EXTENDED_DATA_STDERR:
                print(data, end='', file=sys.stderr)
            else:
                print(data, end='')
        def exit_status_received(self, status):
            if status:
                print('Program exited with status %d' % status, file=sys.stderr)
            else:
                print('Program exited successfully')
        def connection_lost(self, exc):
            if exc:
                print('SSH session error: ' + str(exc), file=sys.stderr)

    @asyncio.coroutine
    def run_client():
        with (yield from asyncssh.connect('192.168.1.1', username="root", client_keys=["/home/eye/.ssh/test"])) as conn:
            chan, session = yield from conn.create_session(MySSHClientSession, 'ls abc')
            yield from chan.wait_closed()

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

and got:

$ python /tmp/d.py 
ls: abc: No such file or directory
$

Seems exit_status_received was not called at all.

Add basic tests for server and client

Since this project implements both SSH server and client, it would be great if for starters we had some simple tests that just spun up both a server and client and had them interact in some way.

We could probably even add a test that spins up 1000 clients for 1 server and confirms that they can all run some simple command "quickly".

Adding some tests like these would give contributors a simple framework for adding more such tests and would give us some good test coverage.

Create a higher-level API for common use cases

Asynchronous SSH is a must for those of us who want to efficiently interact with hundreds or even thousands of servers at once.

asyncssh stands out as the only Python SSH library (as far as I know) that's built from the ground up on the new asyncio module in Python 3.4. That's really great.

However, the API currently offered by asyncssh is relatively low-level.

For example, SSH concepts like channels and sessions are exposed to the user, even in cases where they don't need to be. And simple tasks like connecting to a remote host and running a command appear to require quite a bit of boilerplate code.

In contrast, consider some common SSH operations done with Paramiko, a popular, (synchronous) SSH library for Python:

import paramiko

with paramiko.client.SSHClient() as client:
    client.load_system_host_keys()
    client.connect(hostname='localhost')

    stdin, stdout, stderr = client.exec_command(command='cd Desktop; ls')
    print(stdout.read().decode("utf8"), end='')
    exit_status = stdout.channel.recv_exit_status()

    with client.open_sftp() as sftp_client:
        desktop_listing = sftp_client.listdir(path="Desktop")
        sftp_client.get(remotepath="remote/file.txt", localpath="Desktop/file.txt")

Now, Paramiko's API is not the end-all and be-all of APIs.

For example, the concept of channels also leaks through here for something very common like checking the exit code of your command. And there are several other improvements that can be made to it.

But it serves as an example of what a higher-level API might look like. With the above snippet of code we ran a command, checked various types of output, and even transferred a file.

An API like this is easier to get started with and broadens the appeal of the library.

It would be a great addition if asyncssh had a similar, high-level API that users could use for most tasks, while still allowing the more advanced folks to dig deeper when they need to. Of course, there will be special considerations to make when designing such an API due to the asynchronous nature of the library, but I think it can be done.

Does offering such a higher-level API fit into asyncssh's roadmap?

AsyncSSH uses version of Cryptography which in turn uses deprecated methods

Just a heads up about the deprecation warnings that show up with the current (1.3.0) version of AsyncSSH:

lib/python3.5/site-packages/cryptography/utils.py:71: DeprecationWarning: inspect.getargspec() is deprecated, use inspect.signature() instead

Deprecation warnings show up when you invoke Python with -Wdefault::DeprecationWarning.

It looks like the Cryptography folks have already fixed this issue, so consider this report a reminder to update AsyncSSH's dependencies when the Crypto folks make a new release.

`SFTP Error: Invalid response id` if I call `sftp.put()` followed by `sftp.chmod()`

I'm seeing something interesting. It looks like a bug:

import asyncio
import asyncssh


async def put_and_chmod():
    client = await asyncssh.connect('localhost')
    with (await client.start_sftp_client()) as sftp:
        await sftp.put('file', '/tmp/')
        await sftp.chmod('/tmp/file', int(0o755))


asyncio.get_event_loop().run_until_complete(
    put_and_chmod())

So I'm just copying a file and then calling chmod() on it. This is what I get:

---------------------------------------------------------------------------
SFTPError                                 Traceback (most recent call last)
<ipython-input-18-0d546dc9efb8> in <module>()
     11 
     12 asyncio.get_event_loop().run_until_complete(
---> 13     put_and_chmod())

/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py in run_until_complete(self, future)
    335             raise RuntimeError('Event loop stopped before Future completed.')
    336 
--> 337         return future.result()
    338 
    339     def stop(self):

/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/futures.py in result(self)
    272             self._tb_logger = None
    273         if self._exception is not None:
--> 274             raise self._exception
    275         return self._result
    276 

/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/tasks.py in _step(***failed resolving arguments***)
    239                 result = coro.send(None)
    240             else:
--> 241                 result = coro.throw(exc)
    242         except StopIteration as exc:
    243             self.set_result(exc.value)

<ipython-input-18-0d546dc9efb8> in put_and_chmod()
      7     with (await client.start_sftp_client()) as sftp:
      8         await sftp.put('TODO', '/tmp/')
----> 9         await sftp.chmod('/tmp/TODO', int(0o755))
     10 
     11 

.../venv/lib/python3.5/site-packages/asyncssh/sftp.py in chmod(self, path, mode)
   2259         """
   2260 
-> 2261         yield from self.setstat(path, SFTPAttrs(permissions=mode))
   2262 
   2263     @asyncio.coroutine

.../venv/lib/python3.5/site-packages/asyncssh/sftp.py in setstat(self, path, attrs)
   2175 
   2176         path = self.compose_path(path)
-> 2177         yield from self._handler.setstat(path, attrs)
   2178 
   2179     @asyncio.coroutine

.../venv/lib/python3.5/site-packages/asyncssh/sftp.py in _make_request(self, pkttype, *args)
    789         waiter = asyncio.Future(loop=self._loop)
    790         self._requests[pktid] = waiter
--> 791         resptype, resp = yield from waiter
    792 
    793         return_type = self._return_types.get(pkttype)

/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/futures.py in __iter__(self)
    356         if not self.done():
    357             self._blocking = True
--> 358             yield self  # This tells Task to wait for completion.
    359         assert self.done(), "yield from wasn't used with future"
    360         return self.result()  # May raise too.

/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/tasks.py in _wakeup(self, future)
    288     def _wakeup(self, future):
    289         try:
--> 290             future.result()
    291         except Exception as exc:
    292             # This may also be a cancellation.

/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/futures.py in result(self)
    272             self._tb_logger = None
    273         if self._exception is not None:
--> 274             raise self._exception
    275         return self._result
    276 

SFTPError: SFTP Error: Invalid response id

If I comment out either the put() or the chmod(), or if I change the order to call chmod() first and then put(), I don't get any error.

I'm running AsyncSSH 1.4.0 on Python 3.5.1.

If client_keys and known_hosts explicitly set to None, task finish with error.

In my case, you can not use authorization keys and I tried to turn the off.

yield from asyncssh.create_connection(self.MySSHClient,
                                              host=node.ip,
                                              username=node.login,
                                              password=node.password,
                                              client_keys=None,
                                              known_hosts=None)
)

but

ERROR:asyncio:Task exception was never retrieved
future: <Task finished coro=<connect_to_node() done, defined at /var/lib/kali2/kali2/models/manager_nodes.py:41> exception=TypeError("'NoneType' object is not iterable",)>
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/asyncio/tasks.py", line 236, in _step
    result = next(coro)
  File "/var/lib/kali2/kali2/models/manager_nodes.py", line 49, in connect_to_node
    known_hosts=None)
  File "/var/lib/kali2/.env/lib/python3.4/site-packages/asyncssh/connection.py", line 3944, in create_connection
    local_addr=local_addr)
  File "/usr/local/lib/python3.4/asyncio/base_events.py", line 579, in create_connection
    sock, protocol_factory, ssl, server_hostname)
  File "/usr/local/lib/python3.4/asyncio/base_events.py", line 591, in _create_connection_transport
    protocol = protocol_factory()
  File "/var/lib/kali2/.env/lib/python3.4/site-packages/asyncssh/connection.py", line 3932, in conn_factory
    rekey_seconds, auth_waiter)
  File "/var/lib/kali2/.env/lib/python3.4/site-packages/asyncssh/connection.py", line 1710, in __init__
    self._client_keys = _load_private_key_list(client_keys)
  File "/var/lib/kali2/.env/lib/python3.4/site-packages/asyncssh/connection.py", line 206, in _load_private_key_list
    return [_load_private_key(key) for key in keylist]
TypeError: 'NoneType' object is not iterable

Support basic SFTP operations

Following from the discussion here, this is a request for SFTP support in AsyncSSH.

I don't have any concrete proposal at this time, but I thought it would be useful to create this issue for tracking purposes.

Add Windows support

Hello.

I use Windows and got this error:

...
    import grp
ImportError: No module named 'grp'

Problems in many parallel sessions with Disconnect Error: Permission denied

Hello!
I think it's another problem - not like in 55 issue. I'm writing asynchronous library for connecting different network devices like cisco, mikrotik and other. And i have some problems with many parallel sessions. I wrote some simple test code for understanding problem:

import asyncio
import glob
import asyncssh
import argparse
import yaml

yaml_path = 'devices/'
credits_path = "credentials.yaml"
netdev_credits = yaml.load(open(credits_path, 'r'))


def create_parser():
    parser = argparse.ArgumentParser(description='Test asyncssh runner')
    parser.add_argument("--test", "-t", type=int, default=3, help='Test number')
    options = parser.parse_args()
    return options

async def task(host, user, passwd):
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
        print("{}: Trying to connect".format(host))
        await conn.run('show ver', check=True)
        print("{}: Connection successful".format(host))


async def run():
    option = create_parser()
    if option.test == 1:
        netdev_files = ['devices/test1.yaml']
    elif option.test == 2:
        netdev_files = ['devices/test2.yaml']
    else:
        netdev_files = ['devices/test1.yaml', 'devices/test2.yaml']

    print(option.test)
    tasks = []
    for f_in in netdev_files:
        devices = yaml.load(open(f_in, 'r'))
        for device in devices:
            param = {**netdev_credits, **device}
            tasks.append(task(param['host'], param['username'], param['password']))
    await asyncio.wait(tasks)


loop = asyncio.get_event_loop()
loop.run_until_complete(run())

I have two files with information about my devices: test1.yaml (https://gist.github.com/selfuryon/7596a7a7174160b68be8c9b14c0ffa38) and test2.yaml (https://gist.github.com/selfuryon/1b15357df00d06a7a344283dd62eb834). Also i have file with credentials (credentials.yaml) like this:

password: ***
username: ***

And sometimes, when i run test with number 3 (when i try to connect some more devices than in test 1 and 2, and i know that all connections to all devices are working)- i get exception:

host.yakovlev:AsyncBackup/ $ python3 test.py -t 1                                                                            [0:05:11]
1
192.168.15.1: Trying to connect
192.168.26.1: Trying to connect
192.168.32.1: Trying to connect
192.168.68.1: Trying to connect
192.168.72.1: Trying to connect
192.168.64.1: Trying to connect
192.168.38.1: Trying to connect
192.168.104.1: Trying to connect
192.168.106.1: Trying to connect
192.168.74.1: Trying to connect
192.168.56.1: Trying to connect
192.168.15.1: Connection successful
192.168.46.1: Trying to connect
192.168.32.1: Connection successful
192.168.26.1: Connection successful
192.168.68.1: Connection successful
192.168.28.1: Trying to connect
192.168.38.1: Connection successful
192.168.104.1: Connection successful
192.168.64.1: Connection successful
192.168.106.1: Connection successful
192.168.72.1: Connection successful
192.168.46.1: Connection successful
192.168.56.1: Connection successful
192.168.74.1: Connection successful
192.168.28.1: Connection successful
host.yakovlev:AsyncBackup/ $ python3 test.py -t 2                                                                            [0:04:39]
2
10.254.0.130: Trying to connect
192.168.16.1: Trying to connect
10.254.0.130: Connection successful
192.168.52.1: Trying to connect
192.168.80.1: Trying to connect
192.168.78.1: Trying to connect
192.168.42.1: Trying to connect
192.168.48.1: Trying to connect
192.168.76.1: Trying to connect
192.168.96.1: Trying to connect
192.168.70.1: Trying to connect
192.168.54.1: Trying to connect
10.240.0.129: Trying to connect
192.168.42.1: Connection successful
192.168.96.1: Connection successful
192.168.48.1: Connection successful
192.168.52.1: Connection successful
192.168.16.1: Connection successful
192.168.70.1: Connection successful
192.168.78.1: Connection successful
192.168.76.1: Connection successful
192.168.54.1: Connection successful
10.240.0.129: Connection successful
192.168.80.1: Connection successful
host.yakovlev:AsyncBackup/ $ python3 test.py -t 3                                                                            [0:04:20]
3
192.168.42.1: Trying to connect
10.254.0.130: Trying to connect
192.168.16.1: Trying to connect
192.168.42.1: Connection successful
192.168.26.1: Trying to connect
192.168.52.1: Trying to connect
192.168.80.1: Trying to connect
192.168.32.1: Trying to connect
192.168.15.1: Trying to connect
192.168.78.1: Trying to connect
192.168.76.1: Trying to connect
192.168.48.1: Trying to connect
192.168.96.1: Trying to connect
192.168.70.1: Trying to connect
10.254.0.130: Connection successful
192.168.16.1: Connection successful
192.168.26.1: Connection successful
192.168.52.1: Connection successful
192.168.80.1: Connection successful
192.168.32.1: Connection successful
192.168.15.1: Connection successful
192.168.78.1: Connection successful
192.168.76.1: Connection successful
192.168.48.1: Connection successful
192.168.96.1: Connection successful
192.168.70.1: Connection successful
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied
Task exception was never retrieved
future: <Task finished coro=<task() done, defined at test.py:19> exception=DisconnectError('Disconnect Error: Permission denied',)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "test.py", line 20, in task
    async with asyncssh.connect(host, username=user, password=passwd, known_hosts=None, client_keys=None) as conn:
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/misc.py", line 138, in __aenter__
    self._result = yield from self._coro
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4429, in connect
    conn, _ = yield from create_connection(None, host, port, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/asyncssh/connection.py", line 4219, in create_connection
    yield from auth_waiter
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Permission denied

But sometimes not:

host.yakovlev:AsyncBackup/ $ python3 test.py -t 3                                                                            [0:16:08]
3
10.254.0.130: Trying to connect
192.168.16.1: Trying to connect
192.168.26.1: Trying to connect
192.168.52.1: Trying to connect
192.168.15.1: Trying to connect
192.168.80.1: Trying to connect
192.168.78.1: Trying to connect
192.168.32.1: Trying to connect
192.168.42.1: Trying to connect
192.168.76.1: Trying to connect
192.168.48.1: Trying to connect
192.168.68.1: Trying to connect
192.168.70.1: Trying to connect
192.168.96.1: Trying to connect
192.168.72.1: Trying to connect
192.168.54.1: Trying to connect
192.168.64.1: Trying to connect
192.168.38.1: Trying to connect
192.168.104.1: Trying to connect
10.240.0.129: Trying to connect
192.168.74.1: Trying to connect
192.168.56.1: Trying to connect
192.168.46.1: Trying to connect
192.168.106.1: Trying to connect
192.168.15.1: Connection successful
192.168.42.1: Connection successful
192.168.52.1: Connection successful
192.168.16.1: Connection successful
10.254.0.130: Connection successful
192.168.96.1: Connection successful
192.168.26.1: Connection successful
192.168.48.1: Connection successful
192.168.78.1: Connection successful
192.168.32.1: Connection successful
192.168.70.1: Connection successful
192.168.68.1: Connection successful
192.168.76.1: Connection successful
192.168.54.1: Connection successful
192.168.38.1: Connection successful
192.168.104.1: Connection successful
192.168.64.1: Connection successful
192.168.106.1: Connection successful
192.168.28.1: Trying to connect
192.168.46.1: Connection successful
192.168.72.1: Connection successful
10.240.0.129: Connection successful
192.168.80.1: Connection successful
192.168.56.1: Connection successful
192.168.74.1: Connection successful
192.168.28.1: Connection successful

So my problem is when i get really more than about 30 sessions I can get asyncssh.misc.DisconnectError exceptions and i don't know why.
System information:

host.yakovlev:AsyncBackup/ $ cat /etc/*release*                                                                              [0:20:46]
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"
NAME="Ubuntu"
VERSION="16.04.1 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.1 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
UBUNTU_CODENAME=xenial
host.yakovlev:AsyncBackup/ $ pip3 list                                                                                       [0:40:21]
asyncssh (1.6.2)
cffi (1.8.3)
chardet (2.3.0)
command-not-found (0.3)
cryptography (1.5.2)
fail2ban (0.9.3)
gitdb (0.6.4)
GitPython (2.0.3)
idna (2.1)
Jinja2 (2.8)
language-selector (0.1)
MarkupSafe (0.23)
netdev (0.4.2)
netmiko (0.5.1)
paramiko (2.0.0)
pexpect (4.1.0)
pip (8.1.2)
ptyprocess (0.5.1)
pyaml (15.8.2)
pyasn1 (0.1.9)
pycparser (2.14)
pycurl (7.43.0)
pygobject (3.20.0)
pyinotify (0.9.6)
python-apt (1.1.0b1)
python-debian (0.1.27)
python-systemd (231)
PyYAML (3.11)
requests (2.9.1)
scp (0.10.2)
setuptools (28.0.0)
six (1.10.0)
smmap (0.9.0)
ssh-import-id (5.5)
ufw (0.35)
unattended-upgrades (0.1)
urllib3 (1.13.1)
wheel (0.29.0)
host.yakovlev:AsyncBackup/ $ python3 -V                                                                                      [0:40:28]
Python 3.5.2

Unable to establish connection for SSH keys generated with passphrase

I have generated private RSA key. For this configuration AsyncSSH can not establish connection for the client code:

    with (yield from asyncssh.connect('localhost', username='user', password='####')) as conn:
        stdin, stdout, stderr = yield from conn.open_session('bc')

as result I got an error:

asyncssh.public_key.KeyImportError: Passphrase must be specified to import encrypted private keys

Environment:
OS: Fedora 22 x86_64
Python 3.4.2 (default, Jul 9 2015, 17:24:30)
AsyncSSH 1.3.0

I have tried to resolve this issue by passing the passphrase as an argument for connection (96a9f5c) which works fine to me, at least as workaround.

connection to Cisco IOS devices fail with DisconnectError: ProtocolError: expected packet type 61, got 50

when connecting to a Cisco IOS router (specifically a 3945 running IOS 15.2(4)M6a), there is an exception thrown:

asyncssh.misc.DisconnectError: Disconnect Error: Protocol error: expected packet type 61, got 50

logger output

2015-03-03 22:18:26,983 - Utility - collect_configs3 - cs_ssh_get_config - INFO - starting ssh session to ash-39t-gsgo-oob-1 with username scrooks...
2015-03-03 22:18:30,321 - Utility - collect_configs3 - cs_ssh_get_config - ERROR - ssh connection failed hostname=ash-39t-gsgo-oob-1, ip=131.253.126.240 exc=Disconnect Error: Protocol error: expected packet type 61, got 50
Traceback (most recent call last):
File "/glusterfs/home/scrooks/bin/cybersonic/network/Utility.py", line 347, in cs_ssh_get_config
server_host_keys=None)
File "/glusterfs/lib/python3.4/site-packages/asyncssh/connection.py", line 3088, in create_connection
yield from auth_waiter
File "/glusterfs/lib/python3.4/asyncio/futures.py", line 348, in iter
yield self # This tells Task to wait for completion.
File "/glusterfs/lib/python3.4/asyncio/tasks.py", line 332, in _wakeup
value = future.result()
File "/glusterfs/lib/python3.4/asyncio/futures.py", line 243, in result
raise self._exception
asyncssh.misc.DisconnectError: Disconnect Error: Protocol error: expected packet type 61, got 50
2015-03-03 22:18:30,349 - Utility - collect_configs3 - cs_collect_config - INFO - finished collecting config for ash-39t-gsgo-oob-1...

the same code works fine for other types of devices which use an OpenSSH-based ssh server (Juniper routers, Cisco Nexus 3000 series switches)

code:

@asyncio.coroutine
def cs_ssh_get_config(self, hostname, ip, username, password, cmd, type):

    try:
        result = ""
        stderr_output = ""

        logger.info("starting ssh session to %s with username %s..." % (str(hostname), str(username)))

        if(type == "ios"):
            #kex_list = ["diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1"]
            kex_list = ["diffie-hellman-group1-sha1"]
            enc_list = ["aes128-cbc",]
            mac_list = ["hmac-md5"]
            #pdb.set_trace()
            conn, client = yield from asyncssh.create_connection(None,
                                                                 host=ip,
                                                                 username=username,
                                                                 password=password,
                                                                 mac_algs=mac_list,
                                                                 encryption_algs=enc_list,
                                                                 kex_algs=kex_list,
                                                                 client_keys=None,
                                                                 server_host_keys=None)
            #pdb.set_trace()
            if(conn):
                logger.info("connection extra info=%s" % (str(conn.get_extra_info())))
                logger.info("opening ssh session to hostname=%s, ip=%s" % (str(hostname), str(ip)))

                #pdb.set_trace()
                stdin, stdout, stderr = yield from conn.open_session(cmd, term_type="vt100")
                #pdb.set_trace()
            else:
                logger.exception("ssh connection == None, hostname=%s, ip=%s" %(str(hostname), str(ip)))
                return None
        else:
            conn, client = yield from asyncssh.create_connection(None, host=ip, username=username, password=password, server_host_keys=None)
            if(conn):

                logger.info("connection extra info=%s" % (str(conn.get_extra_info())))
                logger.info("opening ssh session to hostname=%s, ip=%s" % (str(hostname), str(ip)))
                stdin, stdout, stderr = yield from conn.open_session(cmd)
            else:
                logger.exception("ssh create_connection == None, hostname=%s, ip=%s" %(str(hostname), str(ip)))
                return None


        if(stdin and stdout and stderr):
            while not stdout.at_eof():
                result += yield from stdout.read()
            while not stderr.at_eof():
                stderr_output += yield from stderr.read()

            logger.info("closing ssh session to hostname=%s, ip=%s ..." % (str(hostname), str(ip)))
            conn.close()
            logger.info("closed ssh session to hostname=%s, ip=%s ..." % (str(hostname), str(ip)))
            return result

        else:
            logger.exception("ssh open_session() failed to hostname=%s, ip=%s" %(str(hostname), str(ip)))
            return None
    except (asyncssh.Error) as e:
        logger.exception("ssh connection failed hostname=%s, ip=%s exc=%s" % (str(hostname), str(ip), str(e)))
        return None

SFTP file like object

Hello, how can I put file like object (for ex. cStringIO) with sftp into remote host?

[Nice to have] Support script execution

For now users are to run command on remote host, but what if users want to run scripts? In most cases it would require to "re-invent the wheel" from time to time.

Make test of chacha20-poly1305 optional

Hey!

OpenSSL 1.0.2d, as installed on my system, is unable to handle chacha20-poly1305 cipher. Therefore, several tests are failing:

======================================================================
ERROR: test_key (tests.test_public_key.TestRSA) [Export OpenSSH private ([email protected])] (keytype=2048)
Check key import and export
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/build/python-asyncssh-1.3.0/tests/test_public_key.py", line 854, in check_openssh_private
    self.export_openssh_private(cipher)
  File "/build/python-asyncssh-1.3.0/tests/test_public_key.py", line 314, in export_openssh_private
    select_passphrase(cipher), cipher)
  File "/build/python-asyncssh-1.3.0/asyncssh/public_key.py", line 352, in write_private_key
    f.write(self.export_private_key(*args, **kwargs))
  File "/build/python-asyncssh-1.3.0/asyncssh/public_key.py", line 244, in export_private_key
    cipher_name) from None
asyncssh.pbe.KeyEncryptionError: Unknown cipher: [email protected]

As far as I know, no official version of OpenSSL supports this cipher.

Implement asynchronous context managers

Just a follow-up to our discussion on the user list for tracking purposes.

Instead of this:

async def run_client():
    with (await asyncssh.connect('localhost')) as conn:
        chan, session = await conn.create_session(MySSHClientSession, 'ls abc')
        await chan.wait_closed()

It would be great if we could say this:

async def run_client():
    async with asyncssh.connect('localhost') as conn:
        chan, session = await conn.create_session(MySSHClientSession, 'ls abc')
        await chan.wait_closed()

In other words, let's make the existing connection context managers asynchronous by implementing __aenter__() and __aexit__().

I think this can be done in a way that is backwards compatible with Python 3.4, since __aenter__() and __aexit__() just need to be coroutines and don't have to use the new async syntax.

AsyncSSH uses deprecated importlib method `find_loader()`

Running Python with the -Wdefault flag is a good way to show deprecation warnings, which are silenced by default. (You can also do -Wdefault::DeprecationWarning to show only those warnings and ignore, for example, PendingDeprecationWarning.)

This dug up warnings for the importlib.find_loader() method, which AsyncSSH uses but which is apparently deprecated.

/usr/local/lib/python3.4/site-packages/asyncssh/crypto/__init__.py:21: DeprecationWarning: Use importlib.util.find_spec() instead.
  pyca_available = importlib.find_loader('cryptography')
/usr/local/lib/python3.4/site-packages/asyncssh/crypto/__init__.py:22: DeprecationWarning: Use importlib.util.find_spec() instead.
  pycrypto_available =  importlib.find_loader('Crypto')

I'm guessing we should replace all instances of this method as indicated.

Empty line in known_hosts file makes create_connection choke

My known_hosts file somehow ended up having a leading blank line. This seems to cause create_connection() some trouble:

$ python3 asyncssh-example.py 
Traceback (most recent call last):
<snipped>
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/base_events.py", line 316, in run_until_complete
    return future.result()
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/futures.py", line 275, in result
    raise self._exception
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/tasks.py", line 238, in _step
    result = next(coro)
  File "asyncssh-example.py", line 8, in put_file
    conn, client = yield from asyncssh.create_connection(host=host, client_factory=None)
  File "/usr/local/lib/python3.4/site-packages/asyncssh/connection.py", line 3086, in create_connection
    local_addr=local_addr)
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/base_events.py", line 643, in create_connection
    sock, protocol_factory, ssl, server_hostname)
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/base_events.py", line 655, in _create_connection_transport
    protocol = protocol_factory()
  File "/usr/local/lib/python3.4/site-packages/asyncssh/connection.py", line 3082, in <lambda>
    auth_waiter)
  File "/usr/local/lib/python3.4/site-packages/asyncssh/connection.py", line 1315, in __init__
    server_host_keys = self._parse_known_hosts(host, port)
  File "/usr/local/lib/python3.4/site-packages/asyncssh/connection.py", line 1378, in _parse_known_hosts
    dest_hosts, key = line.split(None, 1)
ValueError: need more than 0 values to unpack

This is my environment:

$ python3 --version
Python 3.4.3
$ pip show asyncssh

---
Name: asyncssh
Version: 0.9.2
Location: /usr/local/lib/python2.7/site-packages
Requires: 

A known_hosts file probably shouldn't have blank lines, but I think AsyncSSH should be able to handle them without choking.

Send commands to switch

Hey,

I'm trying to connect to a procurve switch. I've tested the connection with putty and that seems to work.

ProCurve J9137A Switch 2520-8-PoE
Software revision S.14.03

As far as I understand it the switch does not have the sftp or tftp subsystem enabled. And I want my program to work as soon as putty "works".

My basic setup:

import asyncssh
import asyncio

async def tst():
    conn = await asyncssh.connect(ip4, username=username, password=password, known_hosts=None)
    print('opening')
    stdin, stdout, stderr = await conn.open_session(command='sh name', subsystem=None, term_type='ansi')
    response = await stdout.read()
    conn.close()
    return response

fut = asyncio.ensure_future(tst())
loop = asyncio.get_event_loop()
loop.run_until_complete(fut)

ssh-agent

Is there any plan to provide support for a local ssh-agent?

I'm trying to write a convenience/tooling wrapper, and perhaps a replacement for fabric/baker/etc. asyncssh fits the bill for the most part but the missing connection to a local ssh agent makes the library unusable.

Do you have any plan of including support? If not I can attempt an implementation of it as it is a necessity for the rest of my work.

Question about connection closing

Hi there
this is more a question than an issue I am afraid, so feel free to redirect me to another channel

I am having problems getting SshClient.connection_lost() to trigger

My understanding - which obviously is wrong - was that I would call close() on the connection object, and that would cause the callback to trigger; but of course since SshClientConnection.close() is sychroneous and not a coroutine, it seems that I should await for something after I call close(), but I can't seem to figure out for what I should await..

Not sure if I am making myself clear, I can elaborate of course if needed

Thanks a lot - and happy new year :)

Error while closing connection

When running slightly modified sample (https://github.com/ronf/asyncssh/blob/master/examples/simple_client.py)

(I've just changed credentials in create_connection)

import asyncio, asyncssh, sys
class MySSHClientSession(asyncssh.SSHClientSession):
    def data_received(self, data, datatype):
        print(data, end='')
    def connection_lost(self, exc):
        if exc:
            print('SSH session error: ' + str(exc), file=sys.stderr)
class MySSHClient(asyncssh.SSHClient):
    def connection_made(self, conn):
        print('Connection made to %s.' % conn.get_extra_info('peername')[0])
    def auth_completed(self):
        print('Authentication successful.')
@asyncio.coroutine
def run_client():
    conn, client = yield from asyncssh.create_connection(MySSHClient, 'localhost', username='root', password='123', port=1234, client_keys=[])
    with conn:
        chan, session = yield from conn.create_session(MySSHClientSession, 'env')
        yield from chan.wait_closed()
try:
    asyncio.get_event_loop().run_until_complete(run_client())
except (OSError, asyncssh.Error) as exc:
    sys.exit('SSH connection failed: ' + str(exc))

This error occured:

Connection made to 127.0.0.1.
Authentication successful.
SHELL=/bin/bash
SSH_CLIENT=172.17.42.1 49465 1234
USER=root
MAIL=/var/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
PWD=/root
SHLVL=1
HOME=/root
LOGNAME=root
SSH_CONNECTION=172.17.42.1 49465 172.17.0.5 1234
_=/usr/bin/env
Traceback (most recent call last):
  File "../assh.py", line 28, in <module>
    asyncio.get_event_loop().run_until_complete(run_client())
  File "/usr/lib/python3.4/asyncio/base_events.py", line 208, in run_until_complete
    return future.result()
  File "/usr/lib/python3.4/asyncio/futures.py", line 243, in result
    raise self._exception
  File "/usr/lib/python3.4/asyncio/tasks.py", line 302, in _step
    result = next(coro)
  File "../assh.py", line 25, in run_client
    yield from chan.wait_closed()
  File "/home/eye/code/rally-ci/.tox/py34/lib/python3.4/site-packages/asyncssh/connection.py", line 179, in __exit__
    if not self._loop.is_closed():
AttributeError: '_UnixSelectorEventLoop' object has no attribute 'is_closed'

pip freeze:

PyYAML==3.11
argparse==1.3.0
asyncssh==1.1.1
extras==0.0.3
fixtures==1.2.0
flake8==2.4.1
linecache2==1.0.0
mccabe==0.3
mock==1.0.1
pbr==1.0.1
pep8==1.5.7
pycrypto==2.6.1
pyflakes==0.8.1
python-mimeparse==0.1.4
python-subunit==1.1.0
-e git+ci49:~/rally-ci@296a4554e4f946b162f2d7a662f24b2d26640e55#egg=rallyci-master
six==1.9.0
testrepository==0.0.20
testtools==1.8.0
traceback2==1.4.0
unittest2==1.0.1
websockets==2.4

python --version
Python 3.4.0

Memory usage

Modified sample script from docs consumes a lot of memory over time.

Memory usage by process running script below was increased from 56M to 71M in 5 minutes.

import asyncio, asyncssh, sys

class MySSHClientSession(asyncssh.SSHClientSession):
    def data_received(self, data, datatype):
        print(data, end='')

    def connection_lost(self, exc):
        if exc:
            print('SSH session error: ' + str(exc), file=sys.stderr)

class MySSHClient(asyncssh.SSHClient):
    def connection_made(self, conn):
        print('Connection made to %s.' % conn.get_extra_info('peername')[0])

    def auth_completed(self):
        print('Authentication successful.')

@asyncio.coroutine
def run_client():
    conn, client = yield from asyncssh.create_connection(MySSHClient, 'localhost', client_keys=['/home/eye/.ssh/test'])

    with conn:
        chan, session = yield from conn.create_session(MySSHClientSession, 'env')
        yield from chan.wait_closed()

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

At the same time this script takes 17M at start, and isn't growing over time:

import asyncio, socket, sys

HTTP_REQUEST = b"""GET / HTTP/1.0
User-Agent: python

"""

class BMHTTPProtocol(asyncio.Protocol):

    def data_received(self, data):
        code = data.decode("ascii").split(" ")[1]


@asyncio.coroutine
def run_client():
    t, p = yield from asyncio.get_event_loop().create_connection(BMHTTPProtocol, "192.168.1.15", 80)
    t.write(HTTP_REQUEST)

try:
    while True:
        asyncio.get_event_loop().run_until_complete(run_client())
except OSError as exc:
    raise

Some details:

ubuntu 15.10
python 3.4.3-9ubuntu1
pip freeze

PyYAML==3.11
aiohttp==0.19.0
asyncssh==1.3.2
cffi==1.3.1
chardet==2.3.0
cryptography==1.1.2
flake8==2.5.1
idna==2.0
mccabe==0.3.1
pep8==1.5.7
py==1.4.31
pyasn1==0.1.9
pycparser==2.14
pyflakes==1.0.0
pytest==2.8.5
pytest-asyncio==0.2.0
six==1.10.0

get_exit_status() returning `None`

Hey,

It seems to happen on occasion, but the documentation over get_exit_status() seems problematic. The example code shows:

        output = yield from stdout.read()
        print(output, end='')
        status = stdout.channel.get_exit_status()

Often in production we're seeing status = None there every so often, unless I throw a yield from asyncio.sleep(1) on the line above it.

I tried the following but it didn't improve the situation:

    stdout_stream.channel.close()
    yield from stdout_stream.channel.wait_closed()

Perhaps there is a need for wait_for_exit? I'll probably be switching over to the ClientSession class which should allow me to more easily wait, so this isn't urgent on my side.

apparently a bug in asyncssh/asyncssh/known_hosts.py

Hi, I'm quite interested in your project, but unfortunately I've failed to run even simplest example with the following error:

Exception in callback SSHConnection.connection_made(<_SelectorSoc...e, bufsize=0>>)
handle: <Handle SSHConnection.connection_made(<_SelectorSoc...e, bufsize=0>>)>
Traceback (most recent call last):
  File "/usr/lib/python3.4/asyncio/events.py", line 120, in _run
    self._callback(*self._args)
  File "/home/kost/.virtualenvs/copr-py3/lib/python3.4/site-packages/asyncssh/connection.py", line 334, in connection_made
    self._connection_made()
  File "/home/kost/.virtualenvs/copr-py3/lib/python3.4/site-packages/asyncssh/connection.py", line 1505, in _connection_made
    self._peer_addr, self._port)
  File "/home/kost/.virtualenvs/copr-py3/lib/python3.4/site-packages/asyncssh/known_hosts.py", line 142, in match_known_hosts
    host_keys, ca_keys, revoked_keys = _match_entries(entries, host, addr, port)
  File "/home/kost/.virtualenvs/copr-py3/lib/python3.4/site-packages/asyncssh/known_hosts.py", line 113, in _match_entries
    if entry.matches(host, addr, ip):
TypeError: matches() takes 3 positional arguments but 4 were given
Exception in callback _SelectorSocketTransport._read_ready()
handle: <Handle _SelectorSocketTransport._read_ready()>
Traceback (most recent call last):
  File "/usr/lib/python3.4/asyncio/events.py", line 120, in _run
    self._callback(*self._args)
  File "/usr/lib/python3.4/asyncio/selector_events.py", line 669, in _read_ready
    self._protocol.data_received(data)
  File "/home/kost/.virtualenvs/copr-py3/lib/python3.4/site-packages/asyncssh/connection.py", line 349, in data_received
    while self._inpbuf and self._recv_handler():
  File "/home/kost/.virtualenvs/copr-py3/lib/python3.4/site-packages/asyncssh/connection.py", line 430, in _recv_version
    self._send_kexinit()
  File "/home/kost/.virtualenvs/copr-py3/lib/python3.4/site-packages/asyncssh/connection.py", line 642, in _send_kexinit
    host_key_algs = NameList(self._get_server_host_key_algs())
  File "/home/kost/.virtualenvs/copr-py3/lib/python3.4/site-packages/asyncssh/connection.py", line 1550, in _get_server_host_key_algs
    return self._server_host_key_algs
AttributeError: 'SSHClientConnection' object has no attribute '_server_host_key_algs'

It looks like that problem is here: https://github.com/ronf/asyncssh/blob/master/asyncssh/known_hosts.py#L31 and #L56:
PlainHost: def matches(self, host, addr, ip)
HashedHost: def matches(self, host, addr)
And line https://github.com/ronf/asyncssh/blob/master/asyncssh/known_hosts.py#L113
causes TypeError

right to handle connection break under ayncssh rules

Hi,

I've checked #21 about timeout issue and known that programming under asyncio is different from common rules.

So, whats the new ways or ideas to handle exceptions after connection created, such as

  • re-connect or handle it when connection break (hardware problem)
  • multiple connection (connection pool ?)
  • any farther.

e.g.

# ... some logic need a long time as below
#
stdin, stdout, stderr = yield from conn.open_session('sleep 50000;echo -n "Hello!"')

"""
but before its done, my laptop got WIFI signal lost for a moment,
i've got my Python application blocked
expect automate re-connect or exit gently that i can do myself a notice next step
"""

please update to work with github.com/pyca/bcrypt

The implementation of bcrypt of pyca isn't fully compatible with py-bcrypt, but lacks bcrypt.kdf(). Thus it isn't working together with asyncssh. Could you update asyncssh/public_key.py somehow to catch that? As a matter of fact the actual pypi/bcrypt became the newer/the other library in the meanwhile.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.