Paramiko recv_ready() returns false values - python

I am trying to execute a number of commands remotely using paramiko, however the recv_ready() does not return the correct value.
For example after a pwd \n command it will continuously report that the channel is not still ready (obviously false). For some commands it works properly e.g. ls.
Is there something wrong with what I am doing, or is there an issue with paramiko?
import paramiko
import re
import time
def sudo_ssh(hostname, usernameIn, passIn, cmd):
# Create an SSH client
client = paramiko.SSHClient()
# Make sure that we add the remote server's SSH key automatically
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to the client
client.connect(hostname, username=usernameIn, password=passIn)
# Create a raw shell
channel = client.invoke_shell()
# Send the sudo command
for command in cmd:
print("CMD= " + command + "\n")
time.sleep(1)
# wait until channel is ready
while not channel.recv_ready() :
print("NOT READY " + str(channel.recv_ready()) + "\n \n")
time.sleep(1)
# Send the command
channel.send(command)
channel.send("\n")
# Wait a bit, if necessary
time.sleep(1)
# Flush the receive buffer
receive_buffer = channel.recv(4096)
# If promted send the sudo pass
if re.search(b".*\[sudo\].*", receive_buffer):
time.sleep(1)
print(" TYPING SUDO PASSWORD .... \n")
channel.send( "sudoPass" + "\n" )
receive_buffer = channel.recv(4096)
# Print the receive buffer, if necessary
print(receive_buffer)
print("Executed all of the commands. Now will exit \n")
client.close()
com = []
com.append("sudo ls")
com.append("cd /home/user/Downloads")
com.append("sleep 5")
com.append("ls")
com.append("pwd")
com.append("cd /opt/")
sudo_ssh("myhost.com", "user", "pass", com)

The recv_ready method is to check if the data of channel is ready to read or not i.e. data is buffered or not. It doesn't check if channel itself is ready, see - recv_ready().
So you should move the recv_ready() while loop just before the receive_buffer = channel.recv(4096) to make it work.

Related

Paramiko shell - determine when shell is ready for next command? [duplicate]

This question already has answers here:
Reading command output with Paramiko invoke_shell/send/recv never finishes
(1 answer)
Execute multiple dependent commands individually with Paramiko and find out when each command finishes
(1 answer)
Closed 6 months ago.
I'm fairly new to Python, but trying to create a small script that will SSH into a server on my work and navigate through various menus. Normally I would do that with Putty client.
I did succeed in this using code below:
from socket import setdefaulttimeout
import time
import paramiko
from getpass import getpass
from prompt_toolkit import ANSI
# Connection parameters for SSH + create connection.
hostname = 'workhostname'
port = 22
user = input('User (server): ')
passwd = getpass('Password (server): ')
programlogin = input('User (hyperspace): ')
programpass = getpass('Password (hyperspace): ')
exportdir = "/home/" + user + "/PYTHONTEST1"
commandsequence = ["2", "1", programlogin, programpass, "", "6", "DEP", "", "1", "7", "6", "", "17030", "24650", "", "995", "1121042", "1121806", "", exportdir, "", ""]
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname,port=port,username=user,password=passwd)
# Create shell via paramiko.
chan = client.invoke_shell(term='xterm')
# Time.sleep for more than 5 seconds to make sure that the program initial screen pause of 5 seconds is passed.
# "This message will disapper in 5 seconds"
time.sleep(7)
# Loop through command sequence.
for command in commandsequence:
# Send command.
chan.send(command + '\r')
time.sleep(2.3)
print(chan.recv(4096).decode('ISO-8859-1')) # For debugging purposes - to check whats actually going on in the console
if command == programpass:
print("Command: PASSWORD HIDDEN completed")
else:
print("Command: " + command + " completed")
# Trying to create some loop to check if all bytes has been received from the channel. loops out, if not ready after 10 checks.
"""
counter = 0
while not chan.recv_ready():
print("Not ready")
time.sleep(1)
if counter < 10:
counter += 1
else:
break
"""
# Get all bytes + decode the data (so it is readable through print)
#s = chan.recv(4096).decode()
s = chan.recv(4096).decode('ISO-8859-1')
print(s)
client.close()
However this only works because I give the client enough time (time.sleep(2.3)) between each command. I have read somewhere that paramikos exec command is the only real reliable way to tell if the command was actually completed. However I don't think I will be able to use the exec command to navigate this "program" that I'm facing when doing the shell approach. I can use linux terminal commands like "hostname" to get that returned, but I have no idea how to start the program and navigate through it this way.
Will I somehow be able to tell, by reading the chan.recv() if I'm done receiving output from the server, instead of "blindly" trust some high timer? - Or what would the approach be?

How can i change server directory in python from client?

i'm trying to do client-server project. In this project i have to send linux command from client to server. Now i can send some commands like a ls, pwd etc. and they are running correctly and i can read output in client terminal but when i try to send "cd" command, i don't get any error but the directory in server doesn't change. If i use os.chdir(os.path.abspath(data)) command instead of subprocess.check_output , it can change directory but it is useless because i can send a other commands like a ls, pwd , mkdir etc. Thanks for your help
server side:
def threaded(c):
while True:
# data received from client
data = c.recv(1024)
if not data:
print('Bye')
break
try:
data_o = subprocess.check_output(data, shell=True)
except subprocess.CalledProcessError as e:
c.send(b'failed\n')
print(e.output)
if(len(data_o) > 0):
c.send(data_o)
else:
c.send(b'There is no terminal output.')
# connection closed
c.close()
client side:
while True:
# message sent to server
s.send(message.encode('ascii'))
# messaga received from server
data = s.recv(1024)
# print the received message
print('Received from the server :',str(data.decode('ascii')))
# ask the client whether he wants to continue
ans = input('\nDo you want to continue(y/n) :')
if ans == 'y':
message = input("enter message")
continue
else:
break
# close the connection
s.close()
You could check if the command being sent is equal to cd and change the runtime behavior based on that.
data_spl = data.split()
if data_spl[0] == 'cd':
data_o = os.chdir(os.path.abspath(data_spl[1]))
else:
data_o = subprocess.check_output(data, shell=True)

Paramiko - ssh to console server, having to hit return for script to continue

Just using my first paramiko script, we have an opengear console server, so I'm trying to automate setup of any device we plug into it.
The open gear listens for ssh connections on ports, for example a device in port 1 would be 3001. I am connecting to a device on port 8, which works and my script runs, but for some reason, after I get the "Interactive SSH session established" message, I need to hit return on the session to make it run (so I have a ssh session and the script does too, its shared).
It just waits there until I hit return, I've tried sending returns as you can see but they don't work, only a manual return works, which is odd because technically they are the same thing?
import paramiko
import time
def disable_paging(remote_conn):
'''Disable paging on a Cisco router'''
remote_conn.send("terminal length 0\n")
time.sleep(1)
# Clear the buffer on the screen
output = remote_conn.recv(1000)
return output
if __name__ == '__main__':
# VARIABLES THAT NEED CHANGED
ip = '192.168.1.10'
username = 'root'
password = 'XXXXXX'
port = 3008
# Create instance of SSHClient object
remote_conn_pre = paramiko.SSHClient()
# Automatically add untrusted hosts (make sure okay for security policy in your environment)
remote_conn_pre.set_missing_host_key_policy(
paramiko.AutoAddPolicy())
# initiate SSH connection
remote_conn_pre.connect(ip, username=username, password=password,port=port, look_for_keys=False, allow_agent=False)
print "SSH connection established to %s" % ip
# Use invoke_shell to establish an 'interactive session'
remote_conn = remote_conn_pre.invoke_shell()
print "Interactive SSH session established"
time.sleep(1)
remote_conn.send("\n")
# Strip the initial router prompt
#output = remote_conn.recv(1000)
# See what we have
#print output
# Turn off paging
#disable_paging(remote_conn)
# clear any config sessions
is_global = remote_conn.recv(1024)
if ")#" in is_global:
remote_conn.send("end\n")
time.sleep(2)
# if not in enable mode go to enable mode
is_enable = remote_conn.recv(1024)
if ">" in is_enable:
remote_conn.send("enable\n")
time.sleep(1)
remote_conn.send("conf t\n")
remote_conn.send("int g0/0/1\n")
remote_conn.send("ip address 192.168.1.21 255.255.255.0\n")
remote_conn.send("no shut\n")
remote_conn.send("end\n")
# Wait for the command to complete
time.sleep(2)
remote_conn.send("ping 192.168.1.1\n")
time.sleep(1)
output = remote_conn.recv(5000)
print output
I tried this and saw that
is_global = remote_conn.recv(1024)
hangs,
Are you sure '192.168.1.10' sends somthing to be received ?
Try setting a timeout
remote_conn.settimeout(3)
3 seconds for example, do it after this line:
remote_conn = remote_conn_pre.invoke_shell()
this way the recv func does not hang and continues when timeout expires
works for me
first send some command "ls -ltr\n" and then call sleep
remote_conn.send("ls -ltr\n")
time.sleep(1)
Try running your command in a debugger and find out what line is waiting for input. You might also try sending \r or \r\n instead if just \n. Remember the enter key is really ^M
You might also try turning on detailed logging.
import logging
# ...
logging.getLogger("paramiko").setLevel(logging.DEBUG)
ive found another module (netmiko) which does exactly what i want and does all these checks. ive since abandoned trying to do it myself when someone else has already done it better.
use Netmiko! :)

Paramiko - python SSH - multiple command under a single channel

i have read other Stackoverflow threads on this. Those are older posts, i would like to get the latest update.
Is it possible to send multiple commands over single channel in Paramiko ? or is it still not possible ?
If so, is there any other library which can do the same.
Example scenario, automating the Cisco router confi. : User need to first enter "Config t" before entering the other other commands. Its currently not possible in paramiko.
THanks.
if you are planning to use the exec_command() method provided within the paramiko API , you would be limited to send only a single command at a time , as soon as the command has been executed the channel is closed.
The below excerpt from Paramiko API docs .
exec_command(self, command) source code Execute a command on the
server. If the server allows it, the channel will then be directly
connected to the stdin, stdout, and stderr of the command being
executed.
When the command finishes executing, the channel will be closed and
can't be reused. You must open a new channel if you wish to execute
another command.
but since transport is also a form of socket , you can send commands without using the exec_command() method, using barebone socket programming.
Incase you have a defined set of commands then both pexpect and exscript can be used , where you read a set of commands form a file and send them across the channel.
See my answer here or this page
import threading, paramiko
strdata=''
fulldata=''
class ssh:
shell = None
client = None
transport = None
def __init__(self, address, username, password):
print("Connecting to server on ip", str(address) + ".")
self.client = paramiko.client.SSHClient()
self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
self.client.connect(address, username=username, password=password, look_for_keys=False)
self.transport = paramiko.Transport((address, 22))
self.transport.connect(username=username, password=password)
thread = threading.Thread(target=self.process)
thread.daemon = True
thread.start()
def closeConnection(self):
if(self.client != None):
self.client.close()
self.transport.close()
def openShell(self):
self.shell = self.client.invoke_shell()
def sendShell(self, command):
if(self.shell):
self.shell.send(command + "\n")
else:
print("Shell not opened.")
def process(self):
global strdata, fulldata
while True:
# Print data when available
if self.shell is not None and self.shell.recv_ready():
alldata = self.shell.recv(1024)
while self.shell.recv_ready():
alldata += self.shell.recv(1024)
strdata = strdata + str(alldata)
fulldata = fulldata + str(alldata)
strdata = self.print_lines(strdata) # print all received data except last line
def print_lines(self, data):
last_line = data
if '\n' in data:
lines = data.splitlines()
for i in range(0, len(lines)-1):
print(lines[i])
last_line = lines[len(lines) - 1]
if data.endswith('\n'):
print(last_line)
last_line = ''
return last_line
sshUsername = "SSH USERNAME"
sshPassword = "SSH PASSWORD"
sshServer = "SSH SERVER ADDRESS"
connection = ssh(sshServer, sshUsername, sshPassword)
connection.openShell()
connection.send_shell('cmd1')
connection.send_shell('cmd2')
connection.send_shell('cmd3')
time.sleep(10)
print(strdata) # print the last line of received data
print('==========================')
print(fulldata) # This contains the complete data received.
print('==========================')
connection.close_connection()
Have a look at parallel-ssh:
from pssh.pssh2_client import ParallelSSHClient
cmds = ['my cmd1', 'my cmd2']
hosts = ['myhost']
client = ParallelSSHClient(hosts)
for cmd in cmds:
output = client.run_command(cmd)
# Wait for completion
client.join(output)
Single client, multiple commands over same SSH session and optionally multiple hosts in parallel - also non-blocking.
I find this simple to understand and use. Code provides 2 examples with singlehost and multihost. Also added example where you can login to a second user and continue your commands with that user.
More info can be found in here: https://parallel-ssh.readthedocs.io/en/latest/advanced.html?highlight=channel#interactive-shells
from pssh.clients import SSHClient
from pssh.exceptions import Timeout
from pssh.clients import ParallelSSHClient
from pssh.config import HostConfig
def singleHost():
host_ = "10.3.0.10"
pwd_ = "<pwd>"
pwd_root = "<root pwd>"
user_ = "<user>"
client = SSHClient(host_, user=user_, password=pwd_, timeout=4, num_retries=1)
#####
shell = client.open_shell(read_timeout=2)
shell.run("whoami")
# login as new user example
shell.run("su - root")
shell.stdin.write(pwd_root + "\n")
shell.stdin.flush()
shell.run("pwd")
try:
# Reading Partial Shell Output, with 'timeout' > client.open_shell(read_timeout=2)
for line in shell.stdout:
print(line)
except Timeout:
pass
shell.run("whoami")
shell.run("cd ..")
print(".......")
try:
# Reading Partial Shell Output, with 'timeout' > client.open_shell(read_timeout=2)
for line in shell.stdout:
print(line)
except Timeout:
pass
shell.close()
def multiHost():
pwd_ = "<pwd>"
user_ = "<user>"
workingIP_list = ["10.3.0.10", "10.3.0.10"]
host_config_ = []
# HostConfig is needed one per each 'workingIP_list'
host_config_.append(HostConfig(user=user_, password=pwd_))
host_config_.append(HostConfig(user=user_, password=pwd_))
client_ = ParallelSSHClient(workingIP_list, host_config=host_config_, num_retries=1, timeout=3)
# now you have an open shell
shells = client_.open_shell(read_timeout=2)
command = "pwd"
client_.run_shell_commands(shells, command)
try:
# Reading Partial Shell Output, with 'timeout' > client_.open_shell(read_timeout=2)
for line in shells[0].stdout:
print(line)
except Timeout:
pass
print(".......")
command = "cd repo/"
client_.run_shell_commands(shells, command)
command = "pwd"
client_.run_shell_commands(shells, command)
#Joined on shells are closed and may not run any further commands.
client_.join_shells(shells)
for shell in shells:
for line in shell.stdout:
print(line)
print(shell.exit_code)
if __name__ == '__main__':
print("singleHost example:")
singleHost()
print("multiHost example:")
multiHost()

Python ssh script loop problems

I'm no programmer, but I try modify the following script.
http://www.networking-forum.com/wiki/Python_SSH_Script
I would like to make the script a bit more efficient.
At the moment the for loop makes the script do a new login for each command.
I would like the script do one login for each device and run all commands with one output for each device.
Here is the for loop:
# This function loops through devices. No real need for a function here, just doing it.
def connect_to(x):
for device in x:
# This strips \n from end of each device (line) in the devices list
device = device.rstrip()
# This opens an SSH session and loops for every command in the file
for command in commands:
# This strips \n from end of each command (line) in the commands list
command = command.rstrip()
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(device, username=username, password=password)
stdin, stdout, stderr = ssh.exec_command(command)
output = open(device + ".out", "a")
output.write("\n\nCommand Issued: "+command+"\n")
output.writelines(stdout)
output.write("\n")
print "Your file has been updated, it is ", device+".out"
ssh.close()
connect_to(devices)
f1.close()
f2.close()
# END
After looking at the correct indentation found on your source check out the modifications below. This was inspired from this SO answer.
NOTE I do not have a target to ssh into and test these modifications.
def connect_to(x):
for device in x:
# Connect to the target
device = device.rstrip()
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(device, username=username, password=password)
# Open up a stream for the conncction
channel = ssh.invoke_shell()
ssh_stdin = channel.makefile('wb')
ssh_stdout = channel.makefile('rb')
output = open(device + ".out", "a")
# Send all of the commands to the open session
for command in commands:
# This strips \n from end of each command (line) in the commands list
command = command.rstrip()
# send the command
ssh_stdin.write(command)
# Update the local log file
output.write("\n\nCommand Issued: "+command+"\n")
output.writelines(ssh_stdout.read())
output.write("\n")
print "Your file has been updated, it is ", device+".out"
# Close the connection after all of the commands have been issued
output.close()
ssh_stdin.close()
ssh_stdout.close()
ssh.close()

Categories