Distributed Shell

This script is the most important script we have for TLL cluster management. It is powerful at executing commands remotely at properly configured cluster. It basicly utilized ssh remote execution mechanism, so that the command issued ont the headnode can be run smoothly on all the other nodes.

The most important part of deploying this utility is to configure password-less ssh login for an administrator account throughout the all the cluster nodes. My other post will show how to set up password-less SSH login.

Below is the source code of “dsh”

#!/usr/bin/perl 

## Trivially modified to use SSH by The BioTeam Inc.

## further improved locally by Chen Peng, for TLL admin tasks, July 2003
## 1. improved help
## 2. can execute multiple commands at the remote host
## 3. improved support for interactive access

###################################################
# dsh - distributed shell
#
# Author : Jason Rappleye, UB Center for Computational Research
#          rappleye@ccr.buffalo.edu
###################################################

use IO::File;
use Term::ReadLine;

$beowulfRoot="/usr/local/dsh-nodegroups"; # group definition repository

$numColls=0;
$numHosts=0;
$justList=0;
$quiet=0;

#-------------------
# subroutes
#-------------------

sub usage {# display the usage for this script
	print <<_END_OF_USAGE;

dsh.pl [Distribluted Shell, modified version for TLL management]

usage: dsh.pl [-options] commands
	-N group1,group2,...
	   add these node groups to the working collective
	-w host1,host2,host3...
	   add hostnames to working collective
	-l list the working collective for command execution
	-q run the command and print less result
	-d n: sleep for n seconds between command execution on each host

dsh starts with interactive mode if the commands are empty.
	!command : execute locally
	command  : execute remotely

_END_OF_USAGE
}

sub executeCommand {# the real magic of ssh
  my $cmd=shift @_;
  print ":" x 50 . "\nCMD::$cmd\n" unless $quiet;

  for ($i=0; $i<$numHosts; $i++) {
    $handle=new IO::File;
    print "-" x 50 ."\nconnecting to $hosts[$i]...\n" unless $quiet;
    open($handle, "ssh $hosts[$i] '$cmd' |");
    print ">>>HOSTNAME :: $hosts[$i]\n" if $quiet;
    while (<$handle>) {
    	if ($quiet) {print "$_";}
    	else {print "$hosts[$i]:: \t$_"; }
    }
    close $handle;
    print "\njob finishes on $hosts[$i]\n" unless $quiet;
    sleep $delay if defined $delay;
  }
}

#-------------------
# main codes start
#-------------------

if ($#ARGV==-1 && !defined $ENV{"WCOLL"}) {
  &usage;
  exit;
}

if (defined $ENV{"BEOWULF_ROOT"}) {
  $beowulfRoot=$ENV{"BEOWULF_ROOT"};
}

## process command line args

ARG: for ($i=0; $i<=$#ARGV; $i++) {
 SWITCH: {
    # need to break when $ARGV[$i] doesn't start with a '-'
    if (! $ARGV[$i]=~/^\-/) {
      # this only breaks out of the case! need to end the loop...
      break;
    }
    if ($ARGV[$i] eq "-w") {
      @csv=split(",", $ARGV[++$i]);
      foreach $e (@csv) {$hosts[$numHosts++]=$e;}
      last SWITCH;
    }
    if ($ARGV[$i] eq "-N") {
      @csv=split(",", $ARGV[++$i]);
      foreach $e (@csv) {$wcoll[$numColls++]="$beowulfRoot/$e";}
      last SWITCH;
    }
    if ($ARGV[$i] eq "-l") {
      $justList=1;
      last SWITCH;
    }
    if ($ARGV[$i] eq "-q") {
      $quiet=1;
      last SWITCH;
    }
    if ($ARGV[$i] eq "-d") {
      $delay=$ARGV[++$i];
      last SWITCH;
    }
    last ARG;
  }
}

$cmdStart=$i; # the location of real command in ARGV

## quote shell metacharacters (>,>>,<,<<,|,;,*)

for ($j=$cmdStart; $j<=$#ARGV; $j++) {
  if    ($ARGV[$j]=~/^\>$/) 	{push @cmd, "\">\""; }
  elsif ($ARGV[$j]=~/^\>\>$/) {push @cmd, "\">>\"";}
  elsif ($ARGV[$j]=~/^\<$/) 	{push @cmd, "\"<\"";}
  elsif ($ARGV[$j]=~/^\<\<$/) {push @cmd, "\"<<\"";}
  elsif ($ARGV[$j]=~/^\|$/) 	{push @cmd, "\"|\"";}
  elsif ($ARGV[$j]=~/^\;$/) 	{push @cmd, "\";\"";}
  elsif ($ARGV[$j]=~/^\*$/) 	{push @cmd, "\\*";}
  else {push @cmd, ($ARGV[$j]);}
}

$cmd=join(" ", @cmd);

## make sure we have some hosts to work with!

if ($numColls==0 && $numHosts==0) {
  if (defined $ENV{"WCOLL"}) {
    $wcoll[$numColls++]=$ENV{"WCOLL"};
  } else {
    print "dsh: error: No working collective defined\n";
    exit 1;
  }
}

## expand working collectives into hostnames
for ($i=0; $i<$numColls; $i++) {
  open (I, "$wcoll[$i]") ||
  die "Couldn't open node group $wcoll[$i] : $!\n";
  while (<I>) {
    chop $_;
    if (! /^\#/ && ! /^\s*$/) {$hosts[$numHosts++]=$_;}
  }
  close I;
}

if ($justList) {# show the host list only
  for ($i=0; $i<$numHosts; $i++) {print "$hosts[$i]\n";}
  exit 0;
}

if ($cmd eq "") {# enter interactive mode
  $term=Term::ReadLine->new("DSH");
  while ($cmd=$term->readline("dsh> ")) {
    if ($cmd=~/^\!/) {
      $cmd=~s/^!/;
      @output=`$cmd`;
      print @output;
    } else {&executeCommand($cmd);}
  }
} else {
  &executeCommand($cmd);
}

__END__


相关日志

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>