paramiko exec_command hangs after a few commands - python

im connecting a linux server with sshclient .And then, im connecting cisco routers via telnet on this server. I'm connecting server and execute telnet command perfectly but in second or third telnet command code get stucked and doesnt throw error.Here is part of my code:
def __init__(self):
self.pre_client=paramiko.SSHClient()
self.pre_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
sellf.pre_client.connect("server",username="user",password="password")
self.client=self.pre_client.invoke_shell()
def connect(self,ip):
o=self.client.recv(1024)
print o
self.client.exec_command("telnet %s\n"%(ip))
while True:
o=self.client.recv(1024)
print o
#EXECUTE COMMAND ON ROUTER
self.client.exec_command("exit\n")
if 'exit' in o:
break
Why it get stuck on this command? How can i handle it?

I think I'd need to see more code to find what's wrong. If you don't have a specific reason for needing to use a Paramiko Channel, life will probably be a lot easier for you if you just use a Paramiko Client. This is a rough snippet from an old script of mine:
def ssh_connect(usr, pswds, host):
# Open Connection - auto add policy
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Retry with multiple passwords
for pswd in pswds:
try:
ssh.connect(host, username=usr, password=pswd)
return ssh
except:
pass
else:
print("Could not login to: " + host)
return None
def send_command(conn, command):
try:
stdin, stdout, stderr = conn.exec_command(command)
if stdout:
for str in stdout:
sys.stdout.write("\t%s" % str)
return True
if stderr:
for str in stderr:
sys.stderr.write("\t%s" % str)
return False
else:
print("\n")
return True
except paramiko.ssh_exception.SSHException as e:
print(e.message)
return False
And of course, call them with:
conn = ssh_connect(login, passwords, host)
send_command(conn, command)

Related

How to check if the created file using Paramiko exec_command exists

I'm trying to open a file which got created as part of the Paramiko exec_command. The file gets created, but when I check if the file exists, it always returns false. How do I check if the file exists and then open the file for reading?
The code written is:
ip = '10.30.2.104'
username = 'root'
password = 'docker'
def checkSSH(ip, username, password, retries=1):
ssh = SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.load_system_host_keys()
for x in range(retries):
try:
ssh.connect(ip, username=username, password=password, timeout=10)
return ssh
except (
paramiko.BadHostKeyException, paramiko.AuthenticationException, paramiko.SSHException, socket.error) as e:
print('Exception occured.. {0}'.format(e))
return False
hdl = checkSSH(ip, username, password)
cmd = 'ls > test.log'
hdl.exec_command(cmd)
a = os.path.exists('/root/test.log')
print(a) >>> if the file exists, this returns true as per the documentation but in this case, it always returns false.
First, you are not waiting for the command to complete.
For that see:
Wait to finish command executed with Python Paramiko
Once the command completes, you can retrieve its exit code using Channel.recv_exit_status():
How can you get the SSH return code using Paramiko?
If it returns 0, you know that everything went fine and no additional check is needed.
Anyway, if you want to check, use SFTP (SFTPClient.stat):
sftp = hdl.open_sftp()
try:
sftp.stat('/root/test.log')
print("exists")
except Exception as e:
print("something is wrong: " + e)
Though the execution of ls command seems strange to me.
If you want to retrieve the directory listing, use SFTP: SFTPClient.listdir_attr

How to keep ssh connection open and doing multiple requests and outputs within python

Because this question seems to aim somewhere else I am going to point my problem here:
In my python script I am using multiple requests to a remote server using ssh:
def ssh(command):
command = 'ssh SERVER "command"'
output = subprocess.check_output(
command,
stderr=subprocess.STDOUT,
shell=True,
universal_newlines=True
)
return output
here I will get the content of file1 as output.
I have now multiple methods which use this function:
def show_one():
ssh('cat file1')
def show_two():
ssh('cat file2')
def run():
one = show_one()
print(one)
two = show_two()
print(two)
Executing run() will open and close the ssh connection for each show_* method which makes it pretty slow.
Solutions:
I can put:
Host SERVER
ControlMaster auto
ControlPersist yes
ControlPath ~/.ssh/socket-%r#%h:%p
into my .ssh/config but I would like to solve this within python.
There is the ssh flag -T to keep a connection open, and in the before mentioned Question one answer was to use this with Popen() and p.communicate() but it is not possible to get the output between the communicates because it throws an error ValueError: Cannot send input after starting communication
I could somehow change my functions to execute a single ssh command like echo "--show1--"; cat file1; echo "--show2--"; cat file2 but this looks hacky to me and I hope there is a better method to just keep the ssh connection open and use it like normal.
What I would like to have: For example a pythonic/bashic to do the same as I can configure in the .ssh/config (see 1.) to declare a specific socket for the connection and explicitly open, use, close it
Try to create ssh object from class and pass it to the functions:
import paramiko
from pythonping import ping
from scp import SCPClient
class SSH():
def __init__(self, ip='192.168.1.1', username='user', password='pass',connect=True,Timeout=10):
self.ip = ip
self.username = username
self.password = password
self.Timeout=Timeout
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if connect:
self.OpenConnection()
self.scp = SCPClient(self.ssh.get_transport())
def OpenConnection(self):
try:
skip_ping = False
ping_res=False
log.info('Sending ping to host (timeout=3,count=3) :'+self.ip)
try:
PingRes = ping(target=self.ip,timeout=3,count=3, verbose=True)
log.info('Ping to host result :' + str(PingRes.success()))
ping_res=PingRes.success()
except:
skip_ping=True
if ping_res or skip_ping:
log.info('Starting to open connection....')
self.ssh.connect(hostname=self.ip, username=self.username, password=self.password, timeout=self.Timeout, auth_timeout=self.Timeout,banner_timeout=self.Timeout)
self.scp = SCPClient(self.ssh.get_transport())
log.info('Connection open')
return True
else:
log.error('ssh OpenConnection failed: No Ping to host')
return False
myssh = SSH(ip='192.168.1.1',password='mypass',username='myusername')
the ping result is wrapped in try catch because sometimes my machine return an error you can remove it and just verify a ping to the host.
The self.scp is for file transfer.

How to skip lines when printing output from Paramiko SSH

So I built a program that prints out the login logs of my ubuntu server using tail -f.
The program uses Paramiko to connect via ssh and runs the command to tail the logs.
The program works but it prints out the motd from the server which is unnecessary.
I've tried splicing using itertools.
Tried using next().
Still doesn't work.
Here's my code:
import yaml, paramiko, getpass, traceback, time, itertools
from paramiko_expect import SSHClientInteraction
with open("config.yaml", "r") as yamlfile:
cfg = yaml.load(yamlfile, Loader=yaml.FullLoader)
def main():
command = "sudo tail -f /var/log/auth.log"
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
server_pw = getpass.getpass("Enter the password for your account %s on %s:" % (cfg['ssh_config']['username'], cfg['ssh_config']['host']))
sudo_pw = getpass.getpass("Enter the sudo password for %s on %s: " % (cfg['ssh_config']['username'], cfg['ssh_config']['host']))
ssh.connect(hostname = cfg['ssh_config']['host'], username = cfg['ssh_config']['username'], port = cfg['ssh_config']['port'], password = server_pw)
interact = SSHClientInteraction(ssh, timeout=10, display=False)
interact.send(command)
interact.send(sudo_pw + "\n")
with open(interact.tail(line_prefix=cfg['ssh_config']['servername']+': ')) as tail:
for line in itertools.islice(tail, 17, None):
print(line)
except KeyboardInterrupt:
print('Ctrl+C interruption detected, stopping tail')
except Exception:
traceback.print_exc()
finally:
try:
ssh.close()
except:
pass
if __name__ == '__main__':
main()
You get MOTD because you are opening an interactive shell session. I do not think you need that, quite on the contrary.
Use SSHClient.exec_command instead:
stdin, stdout, stderr = ssh.exec_command(command, get_pty=True)
stdin.write(sudo_pw + "\n")
stdin.flush()
for line in iter(stdout.readline, ""):
print(line, end="")
Related questions:
Get output from a Paramiko SSH exec_command continuously
Pass input/variables to command/script over SSH using Python Paramiko
What is the difference between exec_command and send with invoke_shell() on Paramiko?
Obligatory warning: Do not use AutoAddPolicy – You are losing a protection against MITM attacks by doing so. For a correct solution, see Paramiko "Unknown Server".

Paramiko sending text to psuedo shell

Testing sending commands to IoT devices that drops the user into a python script instead of a bash shell.
i.e. in /etc/passwd - admin:x:1000:0:admin:/home/admin:/home/admin/IoT.py
So cant use the normal bash exec_commands to talk to the python script. Basically once it connects it needs the send a command string and receive the output. Below the script reads IP addresses from a text file, whats the correct way to send and receive commands
#!/usr/bin/env python
import paramiko
import sys
def LogonSSH(ipAddress) :
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
for line in open(ipAddress, "r").readlines() :
ipAddr = line.strip()
username='admin'
password='admin'
try :
print "[+] Trying %s with username: %s password: %s " % (ipAddr, username, password)
ssh.connect(ipAddr, username=username, password=password)
except paramiko.AuthenticationException:
print "[x] %s - could not log in" % (ipAddr)
continue
stdin, stdout, stderr = ssh.exec_command('help')
stdin.flush()
stdin.write('help\n')
out = stdout.read()
stdin.flush()
print out
ssh.close()
break
if __name__ == "__main__" :
LogonSSH(sys.argv[1])
Below changes might help you. Just make send and recv, recv only takes the last commands output, but you need to consider:
Put some sleep after sending command (just trial and error)
Some tricks on output data, because it has invisible string parts such as (<pre>..</pre>)
Some output data might be too big to handle > 65100 bytes of recv, just check Paramiko Docs for recv method.
#!/usr/bin/env python
import paramiko
import sys
def LogonSSH(ipAddress) :
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
for line in open(ipAddress, "r").readlines() :
ipAddr = line.strip()
username='admin'
password='admin'
try:
print "[+] Trying %s with username: %s password: %s " % (ipAddr, username, password)
ssh.connect(ipAddr, port=22, username=uname, password=pin, timeout=240,banner_timeout=250, auth_timeout=500)
my_ssh_shell = ssh.invoke_shell()
except Exception as ex:
print("Some exception occured:" + str(ex))
continue
my_ssh_shell.send(bytes("help" + "\x0D", 'utf-8'))
result_screen = my_ssh_shell.recv(65100).decode('ascii')
print("Your SSH -help- command returns:" + str(result_screen))
my_ssh_shell.close()
break
if __name__ == "__main__" :
LogonSSH(sys.argv[1])
Edit: Just change it to any exception for connection, and also some additional paramters on connection string.
The following code will get the MOTD, but send still not working
my_ssh_shell = ssh.invoke_shell()
my_ssh_shell.settimeout(2)
while True :
data = my_ssh_shell.recv(1024).decode('ascii')
if len(data) < 1 :break
print(data)
return
#### DOESNT SEND HERE OR GET RECEIVE RESULTS#########
# my_ssh_shell.send('help\n')
# while True :
# data = my_ssh_shell.recv(1024).decode('ascii')
# if len(data) < 1 :break
# print(data)
# return
my_ssh_shell.close()

It's possible anwser yes/no to a command on paramiko ssh? [duplicate]

I am having issues passing responses to a bash script on a remote server over SSH.
I am writing a program in Python 3.6.5 that will SSH to a remote Linux server.
On this remote Linux server there is a bash script that I am running which requires user input to fill in. For whatever reason I cannot pass a user input from my original python program over SSH and have it fill in the bash script user input questions.
main.py
from tkinter import *
import SSH
hostname = 'xxx'
username = 'xxx'
password = 'xxx'
class Connect:
def module(self):
name = input()
connection = SSH.SSH(hostname, username, password)
connection.sendCommand(
'cd xx/{}/xxxxx/ && source .cshrc && ./xxx/xxxx/xxxx/xxxxx'.format(path))
SSH.py
from paramiko import client
class SSH:
client = None
def __init__(self, address, username, password):
print("Login info sent.")
print("Connecting to server.")
self.client = client.SSHClient() # Create a new SSH client
self.client.set_missing_host_key_policy(client.AutoAddPolicy())
self.client.connect(
address, username=username, password=password, look_for_keys=False) # connect
def sendCommand(self, command):
print("Sending your command")
# Check if connection is made previously
if (self.client):
stdin, stdout, stderr = self.client.exec_command(command)
while not stdout.channel.exit_status_ready():
# Print stdout data when available
if stdout.channel.recv_ready():
# Retrieve the first 1024 bytes
alldata = stdout.channel.recv(1024)
while stdout.channel.recv_ready():
# Retrieve the next 1024 bytes
alldata += stdout.channel.recv(1024)
# Print as string with utf8 encoding
print(str(alldata, "utf8"))
else:
print("Connection not opened.")
The final /xxxxxx in class Connect is the remote script that is launched.
It will open a text response awaiting a format such as
What is your name:
and I cannot seem to find a way to properly pass the response to the script from my main.py file within the class Connect.
Every way I have tried to pass name as an argument or a variable the answer seems to just disappear (likely since it is trying to print it at the Linux prompt and not within the bash script)
I think using the read_until function to look for the : at the end of the question may work.
Suggestions?
Write the input that your command needs to the stdin:
stdin, stdout, stderr = self.client.exec_command(command)
stdin.write(name + '\n')
stdin.flush()
(You will of course need to propagate the name variable from module to sendCommand, but I assume you know how to do that part).

Categories