asyncssh
asyncssh copied to clipboard
Suppress banner/MOTD of run output
Hi,
The run command output includes the banner set in the device and there is no way to suppress it. Code:
conn = await asyncssh.connect(host=self.request.deviceIPAddress,
port=22,
username=self.request.userName,
password=cli_password,
client_keys=None,
known_hosts=None,
connect_timeout=15)
output = await conn.run('show version')
print(output.stdout)
output.stdout returns the command output along with the banner/MOTD set in the device.
====================================================================
***** This is a Pre-Produciton Lab device *******
****** This device is used for testing *********
=====================================================================
Cisco IOS XE Software, Version 17.03.03
Cisco IOS Software [Amsterdam], ASR1000 Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 17.3.3, RELEASE SOFTWARE (fc7)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2021 by Cisco Systems, Inc.
Compiled Thu 04-Mar-21 12:36 by mcpre
asyncssh version 2.9.0
I really need only the command output of the show command here. Is there any option to hide the banner text from the run command output?
Unfortunately, there's nothing asyncssh can really do to help you find the boundary between the banner and the beginning of command output, at least not if the target device repeats the banner on every command you run.
If you can control what goes in the banner, you could try searching for some unique text in that, and that could help you strip out the pieces you don't need. Alternately, if you know the command you are running is always a fixed number of lines of output, you could just throw away everything before that last number of lines. If there's a command that lets you echo some text, you could also try doing that before the command you want the output of, and searching for that text as a way to know where the split is. In the end, though, it's really between you and the target system.
@ronf Passing the quite argument like this ssh -q user@device or 'ssh -o LogLevel=quiet user@device' suppresses the banner. I would like to try the same with asyncssh.
How do I pass the -q or -o LogLevel=quiet argument while opening up the connection?
Are you sure it is the "-q" that's making the difference here? That appears to be a local flag which is not shared with the SSH server, so there shouldn't be any way for that to impact what the server prints in the session you open to it.
Just to be sure, I did a capture of the output from OpenSSH with and without the "-q" option, and I didn't see anything different in any of the protocol messages. Reading over the description of LogLevel, the same thing appears to be true there. It is all about local logging done by the OpenSSH client, but this log level isn't shared with the server in any way that I can see.
There is a concept of an "SSH authentication banner", and it looks like running OpenSSH with "-q" does prevent an OpenSSH client from displaying that banner prior to performing authentication. However, that would not affect the output on any session you open after authentication completes, which is what you are getting back when you make a call to conn.run() and print output.stdout.
By default, AsyncSSH running as a client ignore any auth banner sent by the server, unless you implement the callback function auth_banner_received() on a subclass of SSHClient. So, even without anything equivalent to "-q", AsyncSSH should already be discarding any auth banner the server sends.
You are right. -q suppress only the authentication banner set prior authentication.
The banner displayed in my case is shown after a successful authentication in the below sequence
> ssh username@devicename
> password:
### Banner displayed ###
> show command
command output without banner
As conn.run('show command') returns both banner and the output of show command, I am thinking of running a simple command ( ex: show clock ) and ignore it's command output and then execute the actual show command. The issue with this approach is the asyncssh client closes the underlying connection after running a command, hence I need to establish a new connection for the second command to run - which did not help in this scenario to remove the banner.
If the banner is coming out after the password prompt, it isn't the auth banner, so it's probably written to every SSH session you start up before writing the first command line prompt. In that case, it would show up in the run() output as you saw.
If you want to interact with a shell instead of running a single command and closing the session, you'll need to use something like create_process() or open_session() on the connection and not pass in any command. You can then do your own writing to stdin of this remote process to send commands, and then read output back from stdout/stderr. The readuntil() call on the SSHReader class can be used to look for the command prompt, to know when you can send a new command (discarding the output before that prior to sending your first command). See #280 for an example of this.
By the way, even when using run(), you don't need to establish a new connection every time. You can open the connection once and then call run() repeatedly on it for each command, so you authenticate only once for all of them. However, this probably won't help with the banner issue. Also, not all devices support multiple sessions on a single connection. For those, you'd need to either open a new connection every time or do something like what's described above to send multiple commands over a single SSH session.
I appreciate your quick response. I tried your approach to use open_session() and it works. The one change I had to make is the separator to use #. However for command which returns a large output which output --MORE-- or similar lines to prompt the user to enter a newline is not working with readuntil(separator). Can you share a documentation/example to handler large output without user interaction?
Running multiple commands in the same session also works with this approach on the device I tested with. Thanks.
Typically, the "--MORE--" is handled by sending a command to disable paging on the device. The exact command varies from one device to another, but here are a couple I've run across:
"terminal length 0" on some Cisco devices "set cli screen-length 0" on some Juniper devices
Once you send this command (followed by a newline), you should be able to receive large amounts of output without sending input to go to the next page.
As for the separator, just make sure what you set is something in readuntil() that's unique enough to not match any of the command output. Otherwise, you might not get all the output, as you'd be returning before all the output is read.
Running multiple command on a single session should always work fine. The case that doesn't always work is opening multiple sessions on a single connection and running commands in separate sessions (to make it easier to know when you have all the output without having to scan for a prompt).