I am writing a script in Python for login to ssh and read the output of commands just executed. I am using paramiko package for this. I am trying to execute command "top" and get its output printed on the console. However, I am not able to do this. Please find the snippet:
import sys
import time
import select
import paramiko
host = 'localhost'
i = 1
#
# Try to connect to the host.
# Retry a few times if it fails.
#
while True:
print 'Trying to connect to %s (%i/30)' % (host, i)
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, port=22, username='dummy', password='dummy')
print "Connected to %s" % host
break
except paramiko.AuthenticationException:
print "Authentication failed when connecting to %s" % host
sys.exit(1)
except:
print "Could not SSH to %s, waiting for it to start" % host
i += 1
time.sleep(2)
# If we could not connect within time limit
if i == 30:
print "Could not connect to %s. Giving up" % host
sys.exit(1)
# Send the command (non-blocking)
stdin, stdout, stderr = ssh.exec_command("uname")
# Wait for the command to terminate
while not stdout.channel.exit_status_ready():
# Only print data if there is data to read in the channel
if stdout.channel.recv_ready():
rl, wl, xl = select.select([stdout.channel], [], [], 0.0)
if len(rl) > 0:
# Print data from stdout
print '-------------------------------'
print stdout.channel.recv(1024)
print '-------------------------------'
# Send the command (non-blocking)
stdin, stdout, stderr = ssh.exec_command("top -n 1")
# Wait for the command to terminate
while not stdout.channel.exit_status_ready():
# Only print data if there is data to read in the channel
if stdout.channel.recv_ready():
rl, wl, xl = select.select([stdout.channel], [], [], 0.0)
if len(rl) > 0:
# Print data from stdout
print '-------------------------------'
print stdout.channel.recv(1024)
print '-------------------------------'
# Send the command (non-blocking)
stdin, stdout, stderr = ssh.exec_command("uname")
# Wait for the command to terminate
while not stdout.channel.exit_status_ready():
# Only print data if there is data to read in the channel
if stdout.channel.recv_ready():
rl, wl, xl = select.select([stdout.channel], [], [], 0.0)
if len(rl) > 0:
# Print data from stdout
print '-------------------------------'
print stdout.channel.recv(1024)
print '-------------------------------'
#
# Disconnect from the host
#
print "Command done, closing SSH connection"
ssh.close()
Output:
Trying to connect to localhost (1/30)
Connected to localhost
Linux
-------------------------------
Linux
Command done, closing SSH connection
I am not sure, where I am doing wrong. I am able to get output of other linux commands though. But not sure, why top command's output is not getting printed.
as Jason S pointed out
stdin, stdout, stderr = ssh.exec_command("top -b -n1")
print stdout.read()
works just fine.
top normally uses curses for display rather than just printing. Try the -b for batch option along with the -n 1 you have (top options vary by platform, check the manpage). And in the future, try isolating the problem more - if you were to invoke top via ssh on the command line without your script you would still have an issue.
Related
So I've been struggling to suppress my terminal output whenever I send a packet. I just want to validate the response (0,2 or else) so my terminal won't get spammed with the standard "ping statistics, packets received, packet loss". How would I go about doing this code-wise? I'm not looking for a terminal / bash "way".
def ping():
hosts = open('hosts.txt', 'r')
for host_ip in hosts:
res = subprocess.call(['ping', '-c', '1', host_ip])
if res == 0:
print "ping to", host_ip, "OK"
elif res == 2:
print "no response from", host_ip
else:
print "ping to", host_ip, "failed!"
I just pinged to google dns servers using python's subprocess.Popen class and it returns nothing to the terminal except the returncode I printed in code
import subprocess
process = subprocess.Popen(['ping','-c' ,'1', '8.8.8.8'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
returncode = process.returncode
print(returncode)
OUTPUT
0
I'm rewriting a Bash script I wrote into Python. The crux of that script was
ssh -t first.com "ssh second.com very_remote_command"
I'm having a problem with the nested authentication with paramiko. I wasn't able to find any examples dealing with my precise situation, but I was able to find examples with sudo on a remote host.
The first method writes to stdin
ssh.connect('127.0.0.1', username='jesse', password='lol')
stdin, stdout, stderr = ssh.exec_command("sudo dmesg")
stdin.write('lol\n')
stdin.flush()
The second creates a channel and uses the socket-like send and recv.
I was able to get stdin.write to work with sudo, but it doesn't work with ssh on the remote host.
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('first.com', username='luser', password='secret')
stdin, stdout, stderr = ssh.exec_command('ssh luser#second.com')
stdin.write('secret')
stdin.flush()
print '---- out ----'
print stdout.readlines()
print '---- error ----'
print stderr.readlines()
ssh.close()
...prints...
---- out ----
[]
---- error ----
['Pseudo-terminal will not be allocated because stdin is not a terminal.\r\n', 'Permission denied, please try again.\r\n', 'Permission denied, please try again.\r\n', 'Permission denied (publickey,password,keyboard-interactive).\r\n']
The pseudo-terminal error reminded me of the -t flag in my original command, so I switched to the second method, using a Channel. Instead of ssh.exec_command and later, I have:
t = ssh.get_transport()
chan = t.open_session()
chan.get_pty()
print '---- send ssh cmd ----'
print chan.send('ssh luser#second.com')
print '---- recv ----'
print chan.recv(9999)
chan = t.open_session()
print '---- send password ----'
print chan.send('secret')
print '---- recv ----'
print chan.recv(9999)
...but it prints '---- send ssh cmd ----' and just hangs until I kill the process.
I'm new to Python and none too knowledgeable about networks. In the first case, why does sending the password work with sudo but not with ssh? Are the prompts different? Is paramiko even the right library for this?
I managed to find a solution, but it requires a little manual work. If anyone have a better solution, please tell me.
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('first.com', username='luser', password='secret')
chan = ssh.invoke_shell()
# Ssh and wait for the password prompt.
chan.send('ssh second.com\n')
buff = ''
while not buff.endswith('\'s password: '):
resp = chan.recv(9999)
buff += resp
# Send the password and wait for a prompt.
chan.send('secret\n')
buff = ''
while not buff.endswith('some-prompt$ '):
resp = chan.recv(9999)
buff += resp
# Execute whatever command and wait for a prompt again.
chan.send('ls\n')
buff = ''
while not buff.endswith('some-prompt$ '):
resp = chan.recv(9999)
buff += resp
# Now buff has the data I need.
print 'buff', buff
ssh.close()
The thing to note is that instead of this
t = ssh.get_transport()
chan = t.open_session()
chan.get_pty()
...you want this
chan = ssh.invoke_shell()
It reminds me of when I tried to write a TradeWars script when I was a kid and gave up coding for ten years. :)
Here is a small example using paramiko only (and port forwarding):
import paramiko as ssh
class SSHTool():
def __init__(self, host, user, auth,
via=None, via_user=None, via_auth=None):
if via:
t0 = ssh.Transport(via)
t0.start_client()
t0.auth_password(via_user, via_auth)
# setup forwarding from 127.0.0.1:<free_random_port> to |host|
channel = t0.open_channel('direct-tcpip', host, ('127.0.0.1', 0))
self.transport = ssh.Transport(channel)
else:
self.transport = ssh.Transport(host)
self.transport.start_client()
self.transport.auth_password(user, auth)
def run(self, cmd):
ch = self.transport.open_session()
ch.set_combine_stderr(True)
ch.exec_command(cmd)
retcode = ch.recv_exit_status()
buf = ''
while ch.recv_ready():
buf += ch.recv(1024)
return (buf, retcode)
# The example below is equivalent to
# $ ssh 10.10.10.10 ssh 192.168.1.1 uname -a
# The code above works as if these 2 commands were executed:
# $ ssh -L <free_random_port>:192.168.1.1:22 10.10.10.10
# $ ssh 127.0.0.1:<free_random_port> uname -a
host = ('192.168.1.1', 22)
via_host = ('10.10.10.10', 22)
ssht = SSHTool(host, 'user1', 'pass1',
via=via_host, via_user='user2', via_auth='pass2')
print ssht.run('uname -a')
You can create ssh connection using channel from another ssh connection. See here for more detail.
For a ready made solution check out pxssh from the pxpect project. Look at the sshls.py and ssh_tunnel.py examples.
http://www.noah.org/wiki/Pexpect
Sinas's answer works well but didn't provide all the output from very long commands for me. However, using chan.makefile() allows me to retrieve all the output.
The below works on a system that requires tty and also prompts for sudo password
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
ssh.connect("10.10.10.1", 22, "user", "password")
chan=ssh.get_transport().open_session()
chan.get_pty()
f = chan.makefile()
chan.exec_command("sudo dmesg")
chan.send("password\n")
print f.read()
ssh.close()
I've been trying to run commands on an Overture Box using paramiko to establish the SSH connection.
My script does connect properly to the box, no issues are seen, the problem comes when the script runs the exec_command(cmd) method:
Traceback (most recent call last):
File "test.py", line 39, in <module>
stdin, stdout, stderr = ssh.exec_command("version")
File "/opt/csw/lib/python2.7/site-packages/paramiko/client.py", line 345, in exec_command
chan.exec_command(command)
File "/opt/csw/lib/python2.7/site-packages/paramiko/channel.py", line 60, in _check
return func(self, *args, **kwds)
File "/opt/csw/lib/python2.7/site-packages/paramiko/channel.py", line 229, in exec_command
self._wait_for_event()
File "/opt/csw/lib/python2.7/site-packages/paramiko/channel.py", line 1086, in _wait_for_event
raise e
paramiko.ssh_exception.SSHException: Channel closed.
Same script using a Linux Box as the target host yields the proper results
The code is borrowed, I just found the snippet Googling:
import sys
import time
import select
import paramiko
host = raw_input("Hostname?: ")
i = 1
password = raw_input("Password?: ")
#
# Try to connect to the host.
# Retry a few times if it fails.
#
while True:
print "Trying to connect to %s (%i/30)" % (host, i)
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, port=22, username="username", password=password)
print "Connected to %s" % host
break
except paramiko.AuthenticationException:
print "Authentication failed when connecting to %s" % host
sys.exit(1)
except:
print "Could not SSH to %s, waiting for it to start" % host
i += 1
time.sleep(2)
# If we could not connect within time limit
if i == 30:
print "Could not connect to %s. Giving up" % host
sys.exit(1)
# Send the command (non-blocking)
stdin, stdout, stderr = ssh.exec_command("version")
# Wait for the command to terminate
while not stdout.channel.exit_status_ready():
# Only print data if there is data to read in the channel
if stdout.channel.recv_ready():
rl, wl, xl = select.select([stdout.channel], [], [], 0.0)
if len(rl) > 0:
# Print data from stdout
print stdout.channel.recv(1024),
#
# Disconnect from the host
#
print "Command done, closing SSH connection"
ssh.close()
I found other questions where it's said that the issue might be related to the SFTP settings in the host, but I'm able to do:
sftp username#hostname
I've Googled for a while but I can't find anything helpful so far.
Thanks in advance for your help.
EDIT: Almost forgot, I know the issue is exactly at:
stdin, stdout, stderr = ssh.exec_command("version")
I've run the whole script interactively and it breaks when I run that line.
Could be that the Overture does not support the SSH exec_command request.
I have needed to use transport.open_session(), and session.get_pty() and session.invoke_shell() to setup an interactive shell session, then use session.send() and session.recv() to write/read to the shell session for Cisco switches and other network appliances, when exec_command() is not available.
here is a workaround for this,
get a transport for your connection.
open a session.
then exec_command(command) and thenrecv()`, no need for sleep.
client.connect("host", port=22, username="user",password= "pass",look_for_keys=False, allow_agent=False)
transport=client.get_transport()
channel = t.open_session()
channel.exec_command(command)
out = channel.recv(-1)
print(out.decode())
so I was working with paramiko for some basic SSH testing and I'm not getting any output into stdout. Heres my code.
import paramiko
client=paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
com="ls ~/desktop"
client.connect('MyIPAddress',MyPortNumber, username='username', password='password')
output=""
stdin, stdout, stderr = client.exec_command(com)
print "ssh succuessful. Closing connection"
client.close()
print "Connection closed"
stdout=stdout.readlines()
print stdout
print com
for line in stdout:
output=output+line
if output!="":
print output
else:
print "There was no output for this command"
So whenever I run this, the command is executed (as seen by if I do something like a cp, the file is copied), but I always get "There was no output for this command". When stdout=stdout.readlines() is printed, [] is the output. In addition, if I add a print statement into the for loop, it never gets run. Could someone help me out here? Thanks!
You have closed the connection before reading lines:
import paramiko
client=paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
com="ls ~/desktop"
client.connect('MyIPAddress',MyPortNumber, username='username', password='password')
output=""
stdin, stdout, stderr = client.exec_command(com)
print "ssh succuessful. Closing connection"
stdout=stdout.readlines()
client.close()
print "Connection closed"
print stdout
print com
for line in stdout:
output=output+line
if output!="":
print output
else:
print "There was no output for this command"
The code in the accepted answer may hang, if the command produces also an error output. See Paramiko ssh die/hang with big output.
An easy solution, if you do not mind merging stdout and stderr, is to combine them into one stream using Channel.set_combine_stderr:
stdin, stdout, stderr = client.exec_command(command)
stdout.channel.set_combine_stderr(True)
output = stdout.readlines()
If you need to read the outputs separately, see Run multiple commands in different SSH servers in parallel using Python Paramiko.
*Interactive example :
====Part 1, this show the sh output in server ,at the end of is ">"
need some input to continual or exit ======
selilsosx045:uecontrol-CXC_173_6456-R32A01 lteue$ ./uecontrol.sh -host localhost
UE Control:Start UE control using:
UE Control:java -Dlogdir= -Duecontrol.configdir=./etc -jar ./server/server-R32A01.jar -host localhost
Loading properties from file /Users/lteue/Downloads/uecontrol-CXC_173_6456-R32A01/etc/uecontrol.properties
Starting remote CLI towards host localhost
Enter the command Q to exit the CLI, or the command HELP
to get information on available commands.
The CLI is ready for input.
uec>
===========Pyhton code with peramiko ============*
Try below method : while not stdout.channel.exit_status_ready():
def shCommand(server_list):
server_IP = server_list[0]
username = server_list[1]
password = server_list[2]
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(server_IP,22,username, password)strong text
commandList = ['list \n']
alldata = getUeInfo(ssh,commandList)
ssh.close()
def getUeInfo(ssh,commandList):
data_buffer = ""
num_of_input = 0
stdin, stdout, stderr = ssh.exec_command('cmd')
while not stdout.channel.exit_status_ready():
solo_line = ""
if stdout.channel.recv_ready():
solo_line = stdout.channel.recv(1024) # Retrieve the first 1024 bytes
data_buffer += solo_line
if(cmp(solo_line,'uec> ') ==0 ): #len of solo should be 5 ,
if num_of_input == 0 :
data_buffer = ""
for cmd in commandList :
#print cmd
stdin.channel.send(cmd)
num_of_input += 1
if num_of_input == 1 :
stdin.channel.send('q \n')
num_of_input += 1
return data_buffer
As #jabaldonedo said you closed your SSHClient connection before reading the stdout. SSHClient can be used as a context manager. Using the SSHClient as a context manager helps prevent you from trying to access stdout and stderr after the ssh connection is closed. The resulting code in Python3 syntax looks like:
from paramiko import AutoAddPolicy, SSHClient
with SSHClient() as client:
client.set_missing_host_key_policy(AutoAddPolicy)
client.connect(
'MyIPAddress',
MyPortNumber,
username='username',
password='password'
)
com = "ls ~/desktop"
stdin, stdout, stderr = client.exec_command(com)
output = ''
for line in stdout.readlines()
output += line
if output:
print(output)
else:
print("There was no output for this command")
# Program to print the output in console/interpreter/shell in runtime without using stdout.
import paramiko
import xlrd
import time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
loc = ('/Users/harshgow/Documents/PYTHON_WORK/labcred.xlsx')
wo = xlrd.open_workbook(loc)
sheet = wo.sheet_by_index(0)
Host = sheet.cell_value(0, 1)
Port = int(sheet.cell_value(3, 1))
User = sheet.cell_value(1, 1)
Pass = sheet.cell_value(2, 1)
def details(Host, Port, User, Pass):
time.sleep(2)
ssh.connect(Host, Port, User, Pass)
print('connected to ip ', Host)
stdin = ssh.exec_command("")
remote_conn = ssh.invoke_shell()
print("Interactive SSH session established")
output = remote_conn.recv(1000)
remote_conn.send("\n")
remote_conn.send("xstatus Cameras\n")
time.sleep(5)
output = remote_conn.recv(10000)
print(output)
details(Host, Port, User, Pass)
client = paramiko.SSHClient()
stdin, stdout, stderr = client.exec_command(command)
Is there any way to get the command return code?
It's hard to parse all stdout/stderr and know whether the command finished successfully or not.
A much easier example that doesn't involve invoking the "lower level" channel class directly (i.e. - NOT using the client.get_transport().open_session() command):
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('blahblah.com')
stdin, stdout, stderr = client.exec_command("uptime")
print stdout.channel.recv_exit_status() # status is 0
stdin, stdout, stderr = client.exec_command("oauwhduawhd")
print stdout.channel.recv_exit_status() # status is 127
SSHClient is a simple wrapper class around the more lower-level functionality in Paramiko. The API documentation lists a recv_exit_status() method on the Channel class.
A very simple demonstration script:
import paramiko
import getpass
pw = getpass.getpass()
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
client.connect('127.0.0.1', password=pw)
while True:
cmd = raw_input("Command to run: ")
if cmd == "":
break
chan = client.get_transport().open_session()
print "running '%s'" % cmd
chan.exec_command(cmd)
print "exit status: %s" % chan.recv_exit_status()
client.close()
Example of its execution:
$ python sshtest.py
Password:
Command to run: true
running 'true'
exit status: 0
Command to run: false
running 'false'
exit status: 1
Command to run:
$
Thanks for JanC, I added some modification for the example and tested in Python3, it really useful for me.
import paramiko
import getpass
pw = getpass.getpass()
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
#client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
def start():
try :
client.connect('127.0.0.1', port=22, username='ubuntu', password=pw)
return True
except Exception as e:
#client.close()
print(e)
return False
while start():
key = True
cmd = input("Command to run: ")
if cmd == "":
break
chan = client.get_transport().open_session()
print("running '%s'" % cmd)
chan.exec_command(cmd)
while key:
if chan.recv_ready():
print("recv:\n%s" % chan.recv(4096).decode('ascii'))
if chan.recv_stderr_ready():
print("error:\n%s" % chan.recv_stderr(4096).decode('ascii'))
if chan.exit_status_ready():
print("exit status: %s" % chan.recv_exit_status())
key = False
client.close()
client.close()
In my case, output buffering was the problem. Because of buffering, the outputs from the application doesn't come out non-blocking way. You can find the answer about how to print output without buffering in here: Disable output buffering. For short, just run python with -u option like this:
> python -u script.py