Us pssh in another python script

890 views
Skip to first unread message

chonidev

unread,
Oct 30, 2011, 2:31:21 PM10/30/11
to parallel-ssh
Hello,

I try to use pssh in another python script. To make this, I call pssh
with Subprocess but I 'm using th -A switch of pssh, and not able to
retrieve the ouptut of pssh wich ask me for the password, and I'm not
able to write the password to the process. Do you ahave a solution ?

Regards,

Andrew McNabb

unread,
Oct 31, 2011, 12:22:04 PM10/31/11
to parall...@googlegroups.com

PSSH uses Python's getpass.getpass to ask for the password. Its
documentation shows that it sends the prompt to the tty (and uses stderr
as a fallback). This would indeed be problematic inside another Python
script.

My opinion (and I'd love to hear any contrary opinions) is that the best
way to support your use case is to allow psshlib to be used directly
from other scripts. There is already some support for this, but you
would be the first person to take advantage of this. To use psshlib,
you would essentially duplicate the do_pssh function, with appropriate
customization for your specific needs.

Would you like to be the guinea pig for this feature? If so, there are
a couple of things to pay attention to:

1) The Manager and PasswordServer classes would need to be able to take
the password as an argument.

2) Note that psshlib will reset some signal handlers. In your case, you
can just reset them after psshlib finishes running. Ideally, we should
put the signal-handling code in a separate process, so that it's not a
problem (this hasn't happened yet because no one has used psshlib
separately).

3) It would be very helpful if you would update us on how things go. If
using psshlib works, and if you were able to provide some documentation,
then this would be more readily available for other future users.

Any thoughts? Or did this scare you off? :)

--
Andrew McNabb
http://www.mcnabbs.org/andrew/
PGP Fingerprint: 8A17 B57C 6879 1863 DE55 8012 AB4D 6098 8826 6868

Alex Davies

unread,
Jul 3, 2012, 7:18:09 AM7/3/12
to parall...@googlegroups.com
So I wanted to give this a go. I have tried to produce a super-simple proof of concept, and was hoping to avoid modifying the psshlib code at all (I am using SSH keys, which 'just work' when I run pssh, i.e. the command "pssh -h test -l root "uname -a"" works without modification.

My initial attempt is here, but all the SSH commands are returning code 255.

The first problem is in askpass_client.py; _executable_path is set wrong. Since psshlib is in in the global import path, it would not be hard to rewrite this to find the correct path, but even when I hardcode it correctly in executable_path() it does not work.

However, i'm really not sure where it is picking up the key - if I pprint the environ = dict(os.environ) line in task.py (80), I dont see anything relating to pssh or ~/.ssh/ or in fact anything (except SSH_AGENT_PID) from the working pssh shell.

Suggestions welcome!

Thanks,

-Alex

import psshlib
from psshlib.manager import Manager,FatalError
from psshlib.task import Task
import sys

class Opts:
    def __init__(self):
        self.send_input = None
        self.par = 100
        self.timeout = 20
        self.askpass = None
        self.outdir = None
        self.errdir = None
        self.user = 'root'
        self.verbose = None
        self.inline_stdout = None
        self.extra = None
        self.print_out = None
        self.options = None
        self.inline = None
        self.host_strings = None
        return

# All these have a root public key configured (/home/adavies/.ssh/id_rsa)
hosts = ("one", "two", "three")

command = 'uname -a'
opts = Opts()

manager = psshlib.manager.Manager(opts)
for host in hosts:
        cmd = ['ssh', host, '-i', '/home/adavies/.ssh/id_rsa', '-o', 'SendEnv=PSSH_NODENUM PSSH_HOST', command]
        t = Task(host, "22", "root", cmd, opts)
        manager.add_task(t)
try:
        statuses = manager.run()
except FatalError, e:
    print e
    sys.exit(1)

if min(statuses) < 0:
        print "At least one process was killed."
        sys.exit(3)

for status in statuses:
        if status == 255:
            print "At least one process exited with code 255"
            sys.exit(4)
for status in statuses:
        if status != 0:
            print "At least one process exited with non-zero"
            sys.exit(5)


Andrew McNabb

unread,
Jul 3, 2012, 11:59:42 AM7/3/12
to parall...@googlegroups.com
On Tue, Jul 03, 2012 at 04:18:09AM -0700, Alex Davies wrote:
> So I wanted to give this a go. I have tried to produce a super-simple proof
> of concept, and was hoping to avoid modifying the psshlib code at all (I am
> using SSH keys, which 'just work' when I run pssh, i.e. the command "pssh
> -h test -l root "uname -a"" works without modification.
>
> My initial attempt is here, but all the SSH commands are returning code 255.

It looks like you're really close because your script runs correctly on
my machine. Try setting `self.verbose = True` and `self.inline_stdout =
True` to get more information about why the SSH commands are failing.

> The first problem is in askpass_client.py; _executable_path is set wrong.
> Since psshlib is in in the global import path, it would not be hard to
> rewrite this to find the correct path, but even when I hardcode it
> correctly in executable_path() it does not work.

What specific error message is it giving? What is the actual path of
pssh-askpass on your machine?

> However, i'm really not sure where it is picking up the key - if I pprint
> the environ = dict(os.environ) line in task.py (80), I dont see anything
> relating to pssh or ~/.ssh/ or in fact anything (except SSH_AGENT_PID) from
> the working pssh shell.

Are you referring to the ssh key? The default for ssh should be to look
at ~/.ssh for keys, so if you don't explicitly specify where to look for
a key, it should look there.

Alex Davies

unread,
Jul 9, 2012, 7:59:13 AM7/9/12
to parall...@googlegroups.com
Thanks for your reply. I figured it out - the "user" option in both places I was defining it is not actually working. Adding "-l", "root" to the line, like this, works:

        cmd = ['ssh', host, '-l', 'root', '-i', '/home/adavies/.ssh/id_rsa', '-o', 'SendEnv=PSSH_NODENUM PSSH_HOST', command]

-Alex

Alex Davies

unread,
Jul 10, 2012, 10:51:10 AM7/10/12
to parall...@googlegroups.com
So now I want to get the stdout and stderr, rather than just the exit code. 

I notice that run() method in manager.py just has a list of tasks in self.done, so I tried changing it away from returning statuses = [task.exitstatus for task in self.done] to returning a list of task objects, and then checking each item for .exitcode, .stdout and .stderr. I also hacked the task.py to read() on the .stderr, but this sort of sucks.

I notice there is a neat way of binding handlers onto stderr/out with the handle_stderr and handle_stdout methods. Does anyone have an example of how to use this? My goal is to get (code,stdout,stderr) tuple per task (or similar), ideally without modifying the psshlib code significantly.

Thanks!

-Alex

Andrew McNabb

unread,
Jul 10, 2012, 11:59:18 AM7/10/12
to parall...@googlegroups.com
I think this is a perfectly reasonable request but will require adding a
bit more to the psshlib API. In a nutshell, you want to tie in to the
`outputbuffer` and `errorbuffer` attributes of the `Task` class. The
tricky part is that the outputbuffer and errorbuffer only get
constructed if `inline` or `inline_stdout` is set, so we'll need to
provide a way to say that this is a script that will use outputbuffer
and errorbuffer. Does that make sense?

I think it would be great to have this built in to pssh, so feel free to
create an issue on the Issue Tracker and to submit patches as you make
progress. The Issue Tracker is probably the best place to discuss the
details of which changes to make.

Thanks for your work on this. I think it will be a great addition to
pssh.

Alex Davies

unread,
Jul 10, 2012, 4:16:46 PM7/10/12
to parall...@googlegroups.com
Issue 73 submitted, and i'll see if I have a little time to figure this out and submit a patch or two.

Thanks!

-Alex
--
Alex Davies

This email and any files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the sender immediately by e-mail and delete this e-mail permanently.

Reply all
Reply to author
Forward
0 new messages