solaris7 has asked for the wisdom of the Perl Monks concerning the following question:

Hi Everyone, I have been searching for a way to do parallel ssh in perl, i.e. I want to ssh into two hosts and then ping each other and see the output. After a lot of searching I have found that I need to use the Expect and ForkManager modules to accomplish this. However, I have heard that fork manager is very finicky when it comes to ssh and weird things happen! Can anyone guide me on this?

Replies are listed 'Best First'.
Re: Parallel SSH
by Khen1950fx (Canon) on Apr 10, 2011 at 22:23 UTC
Re: Parallel SSH
by chrestomanci (Priest) on Apr 11, 2011 at 08:17 UTC

    Where did you read that Fork Manager works badly with SSH?

    About 18 months ago, I was working on a database backup tool that made heavy use of both Parallel::ForkManager and ssh. I did not see any problems with using the two together.

    The basic algorithm was: (pseudo-code)

    use Parallel::ForkManager; use Net::SSH::Perl; $pm = new Parallel::ForkManager(10); foreach my $server (@big_list) { my $pid = $pm->start and next; ssh('root@'.$server, '/opt/bin/some_backup_script'); $pm->finish; } print "All backups done";
      The problem with Net::SSH::Perl and fork is that it does not support opening the connection from some process and then reusing it from its children.

        OK, I see the difference. In my experience with the backup script above, I was forking first, and then opening ssh connections in the child processes.

        You are investigating opening ssh connections, and then forking afterwards.

        I think you will always find problems with this, however you do it. The problem is that when you fork both the parent and the child retain all open file descriptors including network sockets. For some of those file descriptors it is probably harmless, or even desirable behaviour that both parent and child keep the file descriptor, but in the case of a state-full protocol such as ssh, it will almost certainly lead to problems.

        Like any well engineered security product, ssh (RFC 4251) will include protection against replay attacks, most likely via some sort of sequence number. If you start and ssh connection, and then fork then both parent and child will inherit copies of the connection object, with a sequence number. If both parent and child then use their connections to talk to the server, then the ssh demon on the server will see the sequence number go backwards which would normally only happen if a cracker was attempting a replay attack, so the server will close or otherwise reject the connection.

Re: Parallel SSH
by zentara (Cardinal) on Apr 11, 2011 at 16:03 UTC
    Have you considered threads?
    #!/usr/bin/perl use warnings; use strict; use threads; my $thr; for(1..10){ $thr = threads->new( \&sub1 )->detach; # Spawn the thread } #sleep 15; try with and without this line while(1){ #print "\n\n\n\t\t",'num threads ',$thr->list(),"\n\n\n"; print "\n\n1\n\n"; sleep 1; } sub sub1 { use Net::SSH2; # assuming a user named 'z' for demonstration # connecting to localhost, so you need your sshd running # see maillist archives at # http://lists.sourceforge.net/lists/listinfo/ssh-sftp-perl-users # for deeper discussions my $ssh2 = Net::SSH2->new(); $ssh2->connect('localhost') or die "Unable to connect Host $@ \n"; $ssh2->auth_password('z','ztester') or die "Unable to login $@ \n"; #shell use my $chan = $ssh2->channel(); $chan->blocking(0); $chan->shell(); print $chan "ls -la\n"; print "LINE : $_" while <$chan>; print $chan "who\n"; print "LINE : $_" while <$chan>; print $chan "date\n"; print "LINE : $_" while <$chan>; $chan->close; }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: Parallel SSH
by Anonymous Monk on Apr 11, 2011 at 13:13 UTC
    Parallel::Forkmanager problematic? I would say the opposite. E.g. this CPAN distribution I rely on uses Parallel::Forkmanager and command line ssh for setting remote passwords. I use it without any problems: http://search.cpan.org/dist/App-Unix-RPasswd/
Re: Parallel SSH
by salva (Canon) on Apr 12, 2011 at 08:50 UTC
Re: Parallel SSH
by casiano (Pilgrim) on May 25, 2011 at 21:36 UTC
    Here is an example if how to make parallel calls using GRID::Machine::Group.

    It Computes the number π (3.14159...) using numerical integration.

    To understand it, take into account that the area under the curve 1/(1+x**2) between 0 and 1 is π/4:

    #!/usr/bin/perl -w use strict; use GRID::Machine; use GRID::Machine::Group; use List::Util qw(sum); use Time::HiRes qw(time gettimeofday tv_interval); my @MACHINE_NAMES = split /\s+/, $ENV{MACHINES}; my @m = map { GRID::Machine->new(host => $_, wait => 5, survive => 1) +} @MACHINE_NAMES; my $c = GRID::Machine::Group->new(cluster => [ @m ]); $c->sub(suma_areas => q{ my ($id, $N, $np) = @_; my $sum = 0; for (my $i = $id; $i < $N; $i += $np) { my $x = ($i + 0.5) / $N; $sum += 4 / (1 + $x * $x); } $sum /= $N; }); my ($N, $np, $pi) = (1e7, 4, 0); my @args = map { [$_, $N, $np] } 0..$np-1; my $t0 = [gettimeofday]; $pi = sum($c->suma_areas(args => \@args)->Results); my $elapsed = tv_interval ($t0); print "Pi = $pi. N = $N Time = $elapsed\n";
    When executed in a single machine, it took 5.387676 seconds:
    ~/grid-machine$ export MACHINES=local ~/grid-machine$ perl examples/pi.pl Pi = 3.14159265358969. N = 10000000 Time = 5.387676
    When executed in a cluster with two nodes we get a speedup of 1.81 = 5.387676/2.977237:
    ~/grid-machine$ export MACHINES='local imac' ~/grid-machine$ echo $MACHINES local imac ~/grid-machine$ perl examples/pi.pl Pi = 3.14159265358969. N = 10000000 Time = 2.977237
Re: Parallel SSH
by casiano (Pilgrim) on May 27, 2011 at 08:23 UTC
    You can use the fork method of a GRID::Machine object. (See GRID::Machine::Process)

    $ cat -n fork5.pl 1 #!/usr/bin/perl -w 2 use strict; 3 use GRID::Machine; 4 use Data::Dumper; 5 6 my $host = $ENV{GRID_REMOTE_MACHINE}; 7 my $machine = GRID::Machine->new( host => $host ); 8 9 my $p = $machine->fork( q{ 10 11 print "stdout: Hello from process $$. args = (@_)\n"; 12 print STDERR "stderr: Hello from process $$\n"; 13 14 use List::Util qw{sum}; 15 return { s => sum(@_), args => [ @_ ] }; 16 }, 17 args => [ 1..4 ], 18 ); 19 20 # GRID::Machine::Process objects are overloaded 21 print "Doing something while $p is still alive ...\n" if $p; 22 23 my $r = $machine->waitpid($p); 24 25 print "Result from process '$p': ",Dumper($r),"\n"; 26 print "GRID::Machine::Process::Result objects are overloaded i +n a string context:\n$r\n";
    When executed, the former program produces an output similar to this:

    $ perl fork5.pl Doing something while 5220:5230:some.machine:5234:5237 is still aliv +e ... Result from process '5220:5230:some.machine:5234:5237': $VAR1 = bles +s( { 'machineID' => 0, 'stderr' => 'stderr: Hello from process 5237 ', 'descriptor' => 'some.machine:5234:5237', 'status' => 0, 'waitpid' => 5237, 'errmsg' => '', 'stdout' => 'stdout: Hello from process 5237. args += (1 2 3 4) ', 'results' => [ { 'args' => [ 1, 2, 3, 4 ], 's' => 1 +0 } ] }, 'GRID::Machine::Process::Result' ); GRID::Machine::Process::Result objects are overloaded in a string co +ntext: stdout: Hello from process 5237. args = (1 2 3 4) stderr: Hello from process 5237
    The fork method returns a GRID::Machine::Process object. The first argument must be a string containing the code that will be executed by the forked process in the remote machine. Such code is always called in a list context.