DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world
Parallel SSH Sessions
Run the same command on lots of hosts in parallel via SSH.
Put the command(s) you want to run in a shell script called 'to_send.sh' in the current directory.
You need Parallel::ForkManager from <a href="http://search.cpan.org/dist/Parallel-ForkManager/">http://search.cpan.org/dist/Parallel-ForkManager/</a>
If you can't install it systemwide you can put ForkManager.pm in a directory called Parallel in the current directory.
#!/usr/bin/perl -w
#
#
#
use strict;
use Parallel::ForkManager;
my $max_procs = 30;
my @hosts;
my $timeout = 30;
my $remote_file = '/tmp/parallel-job.sh';
my $send_file = 'to_send.sh';
my $input = shift;
die "Usage: $0 FILE\n" if ! $input;
open (INPUT, $input) || die "Can't read '$input': $!\n";
while (defined (my $line = <INPUT>)) {
chomp $line;
push @hosts, $line;
}
close INPUT;
#
# hash to resolve PID's back to child specific information
#
my $pm = new Parallel::ForkManager($max_procs);
my $left = @hosts;
# Setup a callback for when a child finishes up so we can
# get it's exit code
$pm->run_on_finish(
sub { my ($pid, $exit_code, $ident) = @_;
print STDERR "$ident -> finished. PID:$pid exit:$exit_code\n";
$left--;
}
);
$pm->run_on_start(
sub { my ($pid,$ident)=@_;
print STDERR "$ident -> started. PID:$pid\n";
}
);
$pm->run_on_wait(
sub {
printf STDERR "Status: %d host%s left.\n", $left, $left == 1 ? "" : "s";
},
0.5
);
my $ssh_string = '/usr/bin/ssh -o StrictHostKeyChecking=no';
my $send_cmd = "cat <<EOT >$remote_file\n";
open (FILE, $send_file) || die "Can't read '$send_file': $!\n";
while (defined (my $line = <FILE>)) {
$line =~ s/\$/\\\$/g;
$send_cmd .= $line;
}
close FILE;
$send_cmd .= "rm $remote_file\nEOT\n/bin/sh $remote_file\n";
for my $child ( 0 .. $#hosts ) {
my $host = $hosts[$child];
my $pid = $pm->start($host) and next;
#
# This code is the child process
#
my @args = (qq~$ssh_string $host '$send_cmd'~);
my $return = eval {
print STDERR "Status: host:$host done:$child left:$left\n";
local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
alarm $timeout;
return system(@args);
alarm 0;
};
$return = 25443 if ! defined $return;
if ( $return != 0 ) {
my $exit_value = $return >> 8;
my $signal_num = $return & 127;
my $dumped_core = $return & 128;
if ( $exit_value == 254 ) {
printf "%25s : Logins disabled\n", $host;
}
elsif ( $exit_value == 1 ) {
printf "%25s : Connection timed out\n", $host;
}
elsif ( $exit_value == 99 && $signal_num == 99 ) {
#
# Normally due to a password prompt or very slow host
#
printf "%25s : Session timed out\n", $host;
}
elsif ( $exit_value == 255 && $signal_num == 0 ) {
printf "%25s : Connection failed\n", $host;
}
else {
printf "%25s : SSH Returned error: [$exit_value] [$signal_num] [$dumped_core]\n", $host;
}
}
print STDERR "$host -> $child finishing...\n";
$pm->finish($child); # pass an exit code to finish
}
print STDERR "Waiting for last hosts...\n";
$pm->wait_all_children;
print STDERR "All hosts done.\n";




