« yumのrepository mirrorをUSにする | メイン | デブサミ2007の発表資料 Nagiosのプラグインの話 »

同じコマンドを複数のサーバで実行

Assurerにシェルモードが追加されたので、もういいかなという感じですが、自分の使っているスクリプトをさらしてみる。

sshで1つのコマンドを複数のサーバに順番に実行させていけます。
「password:」のプロンプトに対して、expectを使って自動で入力する機能もあるので、sudoなどにも対応

mssh -f list.txt -u user -p -- id


-f ファイル名: ホスト名が書いてあるファイル名。1行1ホスト
-u ユーザ名: ログインするユーザ名
-p: パスワードを使うフラグ
--: これ以降にコマンド
 
以下ソース

#!/usr/bin/perl

package MSH;

use strict;
use warnings;
use Expect;

sub new {
    my $class = shift;
    bless {@_}, $class;
}

sub log {
    my $self = shift;
    $self->{_log} && print @_;
}

sub execute {
    my $self = shift;
    my @cmd  = @_;

    my $user = $self->{user} || die;
    my $host = $self->{host} || die;
    my $pass = $self->{pass};
    my $cmd = join " ", @cmd;
    $cmd or die;

    $self->{_log} = 1;
    $self->log(<<EOF);
[$user\@$host] CMD: $cmd
EOF

    my @t = qw(ssh);
    push @t, "-l", $user, $host, @cmd;

    my $ssh = Expect->spawn(@t)
      || die "Warning: ssh failed on $host\n";
    $ssh->log_stdout(0);
    $ssh->log_file( sub { $self->log(shift) } );

    $ssh->expect(
        180,
        [
            qr/assword:\s*$/ =>
              sub {
                  $self->log("\n");
                  $self->{_log} = 0;
                  $ssh->send("$pass\n");
                  exp_continue;
              }
        ],
        [
            qr/.+/ =>
              sub {
                  $self->{_log} = 1;
                  exp_continue;
              }
        ],
        [
            timeout => 
              sub {
                  die "timeout.\n"
              }
        ],
    );
    $ssh->hard_close();
    print "\n";
}

1;

package main;

use strict;
use warnings;
use IO::Prompt;
use Getopt::Long;

GetOptions(
    'f=s'      => \my $list,
    'u|user=s' => \my $user,
    'p'        => \my $with_passwd,
);

my @args = @ARGV;
@ARGV=();

my $passwd;
if ( $with_passwd ) {
    $passwd = prompt("$user password: ", -echo=>"*");
    $passwd or die;
}

open(my $fh, $list);
my @hosts = <$fh>;

foreach my $host ( @hosts ) {
    chomp $host;chomp $host;

    my $msh = MSH->new(
        host => $host,
        user => $user,
        pass => $passwd
    );
    $msh->execute(@args);
}