Using process.communicate after popen fails - python

I'm trying to send command using ssh spawn to remote machine.
I'm sending the command using Popen() and I can see the command was done but after that I'm trying to use communicate() (to close the session and get a return code) and the program get stuck.
cmd = """expect -c 'spawn ssh -o LogLevel=quiet """ \
"""-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PreferredAuthentications=publickey """ \
"""-o ConnectTimeout=10 -2 -o CheckHostIP=no -i {0} {1}#{2} ;\
"""expect "#";send "mkdir test9\n" ;interact'""".format(self.sshKey, SSHUser, self.hostname)
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p_stdoutdata, p_stderrdata = process.communicate()
When I'm reaching the communicate the program gets stuck.

Don't use Popen to call ssh. Instead, use a library that does it all in-process in Python, such as the excellent Paramiko. In your case it might look something like this:
import paramiko
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
client.connect(self.hostname, 22, SSHUser, password)
sftp = paramiko.SFTPClient.from_transport(client.get_transport())
sftp.mkdir("test9")
sftp.close()
client.close()

Related

How is it different to start a service using paramiko in linux rather than executing another commands such as pkill?

Following this post (Running Sudo Command with paramiko) I was able to run commands as sudo remotely.
I can execute sudo pkill -2 pure-ftpd successfully, but when I try to execute sudo service pure-ftpd start I can't see any effect on the server although I see that the output in stdout and stderr is correct.
Here is my code:
class RemoteCmdSender(object):
def __init__(self, host, usr=None, passwd=None):
self.host = host
self.usr = usr
self.passwd = str(passwd)
def send_cmd_as_bash(self, cmd):
client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect(hostname=self.host, username=self.usr,
password=self.passwd)
transport = client.get_transport()
session = transport.open_session()
session.get_pty('bash')
session.exec_command(cmd)
stdin = session.makefile('wb', -1)
stdin.write(self.passwd.strip() + '\n')
stdin.flush()
stdout = session.makefile('rb', -1).read()
stderr = session.makefile_stderr('rb', -1).read()
client.close()
return stdout, stderr
and the execution:
print cmd_sender.send_cmd_as_bash("sudo service pure-ftpd")
output:
Starting ftp server: Running: /usr/sbin/pure-ftpd -l pam -l puredb:/etc/pure-ftpd/pureftpd.pdb -E -O clf:/var/log/pure-ftpd/transfer.log -8 UTF-8 -u 1000 -B\r\n
Which is consistent with the output that I get if I log to the server using ssh and write sudo service pure-ftpd start in the bash.
PS: I want to make clear that both commands works correctly when run from an ssh session using bash

python popen stdout strderr return from ssh

I have a funtion which builds a ssh sommand lines and passes this to popen so I can run commands on a remote box ( I know I could use Fabirc or paramiko but for interests sake I wanted to do this).
so the fuction call is a wrapper arround Poepn (partly)
def runcmd(cmd, host=None, stdout=None, stderr=None, both=None, shell=None, user=None)
I invoke it for test with
runcmd('cd ~/app-scripts; grep "below and commment" *',user="testuser")
and the command it build up is shown as
ssh -F /home/testuser/.ssh/config testuser#localhost 'cd ~/app-scripts; grep "below and commment" *'
This runs on the "remote" host fine and the stdout correctly show the results --
however the stderr also contains all the login messages from the remote host.
The internal fuction call to Popen is
p = Popen(sshcmd,stdout=PIPE,stderr=PIPE,shell=True)
stdout_data,stderr_data = p.communicate()
What am i missing?

using subprocess to ssh and execute commands

I need to ssh into the server and execute few commands and process the response using subprocess. Here's my code
command = 'ssh -t -t buildMachine.X.lan; sudo su - buildbot ; build-set sets/set123'
print "submitting command"
result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
print "got response"
response,err = result.communicate()
print response
This is getting stuck. I have seen other threads talking about passing a list instead of string to subprocess and removing shell=True.. I did that too but didn't work.
Ultimately I need the result of last command i.e. build-set in order to extract some information out of it.. help?
I figured the solution by using univerio's comment
The command needs to be
command = 'ssh -t -t buildMachine.X.lan \'sudo su - buildbot \'build-set sets/set123\'\''
Individual commands are like argument to previous command. This works.

subprocess timeout when exceeds a certain time limit

I'm using the subprocess module of python to run an ssh command over my servers for collecting their disk usage. The one thing on which i'm stuck is if the ssh is not configured in any server then subprocess prompt for the password input which makes my whole script stuck and then i have to voluntarily kill the script itself. I just want it to let go all the servers which asks for password prompt(where ssh is not configured) and continue processing the rest.
def MyFunction(server):
msg=""
ps = subprocess.Popen("ssh -l mygroup %s 'df -k /some/directory'" % server,stdout=subprocess.PIPE,shell=True)
out, err = ps.communicate()
if err != None:
msg += "\n"+err
else:
msg = out
return msg
server_list= ['server A','server B','server C','server D']
for server in server_list:
Final_msg+=MyFunction(server)
Any help would be appreciated! :)
If it is just the thing that you want to avoid ssh ask you for anything, then you can forbid it to do so.
You can use the SSH option
BatchMode
If set to “yes”, passphrase/password querying will be disabled.
This option is useful in scripts and other batch jobs where no user is present to supply the password.
The argument must be “yes” or “no”. The default is “no”.
So just add -o BatchMode=yes:
ps = subprocess.Popen("ssh -o BatchMode=yes -l mygroup %s 'df -k /some/directory'" % server, stdout=subprocess.PIPE, shell=True)
BTW, why do you need shell=True here? Better do
ps = subprocess.Popen(["ssh", "-o", "BatchMode=yes", "-l", "mygroup", server, "df -k /some/directory"], stdout=subprocess.PIPE)
as it is cleaner, safer and internally simpler.

Paramiko and Pseudo-tty Allocation

I'm trying to use Paramiko to connect to a remote host and execute a number of text file substitutions.
i, o, e = client.exec_command("perl -p -i -e 's/" + initial + "/"
+ replaced + "/g'" + conf);
Some of these commands need to be run as sudo, which results in:
sudo: sorry, you must have a tty to
run sudo
I can force pseudo-tty allocation with the -t switch and ssh.
Is it possible to do the same thing using paramiko?
Actually it's quite simple. Just:
stdin, stdout, stderr = client.exec_command(command, get_pty=True)
The following code works for me:
#!/usr/bin/env python
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('localhost',username='root',password='secret')
chan = ssh.get_transport().open_session()
chan.get_pty()
chan.exec_command('tty')
print(chan.recv(1024))
This was just assembled from looking at a few examples online... not sure if its the "right" way.
I think you want the invoke_shell method of the SSHClient object (I'd love to give a URL but the paramiko docs at lag.net are frame-heavy and just won't show me a specific URL for a given spot in the docs) -- it gives you a Channel, on which you can do exec_command and the like, but does that through a pseudo-terminal (complete with terminal type and numbers of rows and columns!-) which seems to be what you're asking for.
According to the sudo manpage:
The -S (stdin) option causes sudo to read the password from the standard input instead of the terminal device. The
password must be followed by a newline character.
You can write to the stdin because it is a file object with write():
import paramiko
client = paramiko.client.SSHClient()
client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
client.connect(hostname='localhost', port=22, username='user', password='password')
stdin, stdout, stderr = client.exec_command('sudo -S aptitude update')
stdin.write('password\n')
stdin.flush()
# print the results
print stdout.read()
client.close()

Categories

Resources