Python paramiko ssh channel send() does not work occasionally - python

I am running python 2.6.6 (64 bit) on windows 7 with paramiko version 1.12.0.
I have a script that runs an executable on a remote unix machine. The script works most of the time (50-70%) but doesn't work occasionally . Here is a simplified version of my script.
import paramiko
import logging
logger = paramiko.util.logging.getLogger()
logger.setLevel(logging.DEBUG)
paramiko.util.log_to_file("filename.log")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, 22, username, password)
chan = ssh.invoke_shell()
chan.send("cd /home/usr/exe_loc\n")
buff = _read_buffer(chan)
print(buff)
send_code = chan.send("nohup exe_name -r1234 &\n")
print("exec exit code = %d"%send_code)
_read_buffer prints buffer and works as expected.
Even when the script fails to run the executable, the 'cd' command is still printed by the buffer and the send_code is > 0 meaning the command was sent successfully(?).
I compared 'filename.log' file generated by successful run, failed run. The only difference is the failed run has an additional line (below) at the end of the file.
DEB [20160212-16:04:31.512] thr=1 paramiko.transport: EOF in transport thread
Also, this happens more frequently when I run the script after a long break. When I run this back to back for debugging it rarely fails.
Any ideas on why this could be going on here? Let me know if I can provide additional information.

Related

Pexpect won't pass commands to Panduit PDU (Smartzone G5) via SSH

Problem
I've got a program using "Pexpect" to send commands over SSH. Its purpose is to SSH into a Panduit PDU (smartzone G5) and turn off port 1.
I can SSH into the PDU and run dev outlet 1 1 off and get the port to turn off, but it doesn't work when I try to run the same command from my program. It puzzles me as this method is no different than me logging in via SSH using iterm and typing. Is this a me problem or just how the PDU behaves?
What I've Tried
I've tested pexpect thoroughly on a Linux server using mkdir and touch to create directories and files so I know it works without issue there, but when I use it on the Panduit PDU the PDU ignores the commands.
I use time.sleep() to allow the PDU to catch up as it is a slow system and seems to have a queue for multiple commands.
It's not a module issue or interpreter issue or anything silly like that; the program itself is fine. (Though I'm sure it's not up to standards or safe but who cares if I can't get the program to work in the first place seriously.)
Why Pexpect?
I must use Pexpect as Paramiko and Fabric don't work for this particular connection.
I must specify ssh -F /dev/null admin#ipaddress otherwise the PDU rejects the SSH session and I cannot get pxssh or fabric or paramiko to do this so that's why I don't use them.
I'm aware of stuff like .set_missing_host_key_policy(paramiko.AutoAddPolicy()) for Paramiko and s = pxssh.pxssh(options={"StrictHostKeyChecking": "no", "UserKnownHostsFile": "/dev/null"}) and they do not work. Hence Pexpect.
Code
import os
import pexpect
import time
import sys
server_ip = "192.168.0.1"
server_user = "admin"
server_pass = "password"
child = pexpect.spawn('bash')
child.logfile_read = sys.stdout.buffer
child.expect('')
# This is the only way to access the PDU, Paramiko wont work either.
child.sendline('ssh -F /dev/null %s#%s -oStrictHostKeyChecking=no' % (server_user, server_ip))
child.expect("admin#192.168.0.1's password:")
child.sendline(server_pass)
child.expect('PANDUIT>')
time.sleep(3) # Wait for PDU to catch up or it wont recognise the command
child.sendline('dev outlet 1 1 off') # Power off port 1
print('\nfinished') # \n is there otherwise it gets sucked into the stdout
For reference this is what happens when I use Iterm to manually connect and enter commands, it works perfectly.
I've tried to use child.expect('SUCCESS') but it can't be seen. The only thing pexpect seems to be able to find inside the PDU is child.expect('\n') which I think may be from a buffer or something.
~/Documents ❯ ssh -F /dev/null admin#192.168.0.1
admin#192.168.0.1's password:
Type ? for command listing
sys PDU system configure and setting
net PDU net application configure and setting
usr PDU user operation
dev PDU device setting
pwr PDU power setting
PANDUIT>dev outlet 1 1 off
SUCCESS
And this is what my code outputs:
~/Documents❯ python3 code.py
ssh -F /dev/null admin#192.168.0.1 -oStrictHostKeyChecking=no
The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
bash-3.2$ ssh -F /dev/null admin#192.168.0.1 -oStrictHostKeyChecking=no
admin#192.168.0.1's password:
Type ? for command listing
sys PDU system configure and setting
net PDU net application configure and setting
usr PDU user operation
dev PDU device setting
pwr PDU power setting
PANDUIT>
finished

Paramiko connection succeeds on linux but fails on windows

So, I have this program I made that connects to another computer (which is a linux machine) through SSH in python with the help of the library paramiko. I've made all the development on my ubuntu machine at work and it runs just fine on my home gentoo setup. I've been asked to make it work on a windows machine, and I get errors I cannot quite figure out what they mean to be honest, I'm not really confortable in windows environments.
The code is the following :
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(
hostname=self.address,
username=self.username,
password=self.password
)
self.shell = self.ssh.invoke_shell(width=self.shell_width, height=self.shell_height)
self.transport = paramiko.Transport((self.address, self.port))
self.transport.connect(
username=self.username,
password=self.password
)
self.sftp = paramiko.SFTPClient.from_transport(self.transport)
So basically I have an interactive SSH shell I use to issue commands, and an SFTP connection as well to retrieve the files I created from the SSH session.
When running this code on windows :
Traceback (most recent call last):
File "C:/Users/labor/PycharmProjects/OPSS/OPSS.py", line 9, in <module>
shell.run("uptime")
File "C:\Python27\lib\site-packages\spur\ssh.py", line 162, in run
return self.spawn(*args, **kwargs).wait_for_result()
File "C:\Python27\lib\site-packages\spur\ssh.py", line 173, in spawn
channel = self._get_ssh_transport().open_session()
File "C:\Python27\lib\site-packages\spur\ssh.py", line 251, in _get_ssh_transport
raise self._connection_error(error)
spur.ssh.ConnectionError: Error creating SSH connection
Original error: [Errno 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
I don't really know what the system is trying to tell me when it says the party failed to respond because I can connect to it just fine with WinSCP and PuTTY from windows (which are the two programs I used to test both SSH and SFTP).
As you can tell I'm using PyCharm as my IDE, and I installed paramiko using its package manager on windows (on my linux machines I used pip).
Does anyone know what is happening ? And how can I make it work ? Thanks !

How to pass Unix Commands across network using python

So basically I have this remote computer with a bunch of files.
I want to run unix commands (such as ls or cat) and receive them locally.
Currently I have connected via python's sockets (I know the IP address of remote computer). But doing:
data = None
message = "ls\n"
sock.send(message)
while not data:
data = sock.recv(1024) <- stalls here forever
...
is not getting me anything.
There is an excellent Python library for this. It's called Paramiko: http://www.paramiko.org/
Paramiko is, among other things, an SSH client which lets you invoke programs on remote machines running sshd (which includes lots of standard servers).
You can use Python's subprocess module to accomplish your task. It is a built-in module and does not have much dependencies.
For your problem, I would suggest the Popen method, which runs command on remote computer and returns the result to your machine.
out = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
t = out.stdout.read() + out.stderr.read()
socket.send(t)
where cmd is your command which you want to execute.
This will return the result of the command to your screen.
Hope that helps !!!
This is what I did for your situation.
In terminal 1, I set up a remote shell over a socket using ncat, a nc variant:
$ ncat -l -v 50007 -e /bin/bash
In terminal 2, I connect to the socket with this Python code:
$ cat python-pass-unix-commands-socket.py
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('', 50007))
sock.send('ls\n')
data = sock.recv(1024)
print data
sock.close()
$ python pass-unix-commands-socket.py
This is the output I get in terminal 1 after running the command:
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Listening on :::50007
Ncat: Listening on 0.0.0.0:50007
Ncat: Connection from 127.0.0.1.
Ncat: Connection from 127.0.0.1:39507.
$
And in terminal 2:
$ python pass-unix-commands-socket.py
alternating-characters.in
alternating-characters.rkt
angry-children.in
angry-children.rkt
angry-professor.in
angry-professor.rkt
$

execute a program on a remote machine python

I can execute a script from python environment locally using subprocess but due to cross platform issues, I have to execute it on a remote server and get back the results on my local machine.
The directory parserpath contains some third party modules that can be executed using a script run.sh present in parserpath directory. However this parserpath directory is present on a remote server.
This is what I have, but this will work only if parserpath is a local directory. How can I ssh to a remote directory and run the script run.sh?
def run_parser(filename):
current_dir = os.getcwd()
parser_path="/parserpath"
os.chdir(parser_path)
subprocess.call("./run.sh " + filename, shell=True)
os.chdir(current_dir)
With most linux shells, you can run a command in a different working directory by executing a subshell as in
/home/usr> (cd /usr/local/bin;pwd)
/usr/local/bin
/home/usr>
You can do the same thing through ssh to the remote system. Depending on which ssh client you use, you may thin that up a bit. For instance, with paramikos exec_command, a new remote shell is created for each command so cd /path/on/remote/machine;./run.sh is sufficient.
A minimalist example for paramiko on python 2.x is
import sys
import paramiko
try:
hostname, username, password, targetpath = sys.argv[1:5]
except ValueError:
print("Failed, call with hostname username password targetpath")
command = "cd {};pwd".format(targetpath)
print("Command to send: {}".format(command))
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=hostname, username=username, password=password)
stdin, stdout, stderr = ssh.exec_command("cd {};pwd".format(targetpath))
print(stdout.read())
ssh.close()
python3 should be similar. There are other options like libssh2 bindings for python, pexpects ssh support and etc...
Use SSH keys to automate the process of logging in via SSH. Here is the following code to execute a script remotely.
ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
Try, ssh user#host sh path/run.sh

Is there a way to restart tomcat using paramiko?

when I try to run the following code:
import paramiko
s = paramiko.SSHClient()
s.set_missing_host_key_policy(paramiko.AutoAddPolicy())
s.connect(hostname="192.168.30.68", username="root", password="123456")
stdin, stdout, stderr = s.exec_command("sh /Application/tomcat/bin/startup.sh")
print stdout.read()
s.close()
I get the following output:
Neither the JAVA_HOME nor the JRE_HOME environment variable is defined
At least one of these environment variable is needed to run this program
The tomcat on the remote server is not started. Is there a way to resolve this problem?

Categories