#!/usr/bin/perl -w
require 5.003;
use strict;
use integer;
#
# executes a command on or for a set of hosts,
# setting `host' and `hid' shell variables for each host
#
die("usage: $0 <address_range|host_group_name> [[-] <command> [arguments]]\n") if @ARGV < 1;
my ($HostGroup, @Cmd) = @ARGV;
# these are primary host addresses, not aliases!
my %Hosts = (
cl1 => ['10.0.13.61-62'],
sv1 => ['10.0.13.129-130'],
);
# convert address ranges to address array
foreach my $v (values %Hosts) {
my @addrs = ();
foreach my $addr (@{$v}) {
push @addrs, &range2arr($addr);
}
@{$v} = @addrs;
}
# form groups
foreach my $k (keys %Hosts) {
my ($id) = ($k =~ /^cl(\d+)$/);
next unless defined $id;
my @gr = (@{$Hosts{"cl$id"}}, @{$Hosts{"sv$id"}});
$Hosts{"gr$id"} = [ @gr ];
}
my @hosts = defined $Hosts{$HostGroup} ?
@{$Hosts{$HostGroup}} : (&range2arr($HostGroup));
die("host group $HostGroup is unknown; stopped") unless @hosts;
if (!@Cmd) {
print join(' ', @hosts), "\n";
exit 0;
}
print("hosts: ", join(' ', @hosts), "\n");
my $local = $Cmd[0] eq '-'; shift @Cmd if $local;
my $cmd = join(' ', @Cmd);
my $hid = 0;
my %nameIds = ( clt => 0, srv => 0 );
foreach my $host (@hosts) {
print("==> $host\n");
$ENV{'host'} = $host;
$ENV{'hid'} = ++$hid;
my $name = &isClient($host) ? 'clt' : 'srv';
$nameIds{$name}++;
$ENV{'hname'} = $name . $nameIds{$name};
if ($local) {
warn("- $cmd\n");
system("$cmd") == 0 and next;
} else {
system("ssh $host $cmd") == 0 and next;
}
print("failure on host $host\n");
}
exit(0);
sub isClient($) {
my $host = shift;
my ($n) = ($host =~ /(\d)$/);
return undef unless defined $n;
return ($n & 1) > 0;
}
sub range2cls {
my %h = (&range2arr(shift));
return sort keys %h;
}
sub range2svs {
my %h = (&range2arr(shift));
return sort values %h;
}
sub range2arr {
my $range = shift;
my @specs = split(/\./, $range);
return () unless @specs > 1;
my @bins = ();
foreach my $spec (@specs) {
my ($min, $max) = $spec =~ /-/ ?
($spec =~ /^(\d+)-(\d+)$/) : ($spec =~ /^(\d+)$/);
return undef unless defined $min;
$max = $min if !defined($max);
push @bins, { min=>$min, max=>$max, pos=>$min };
}
my @res = ();
while (1) {
push @res, &curAddr(\@bins);
} continue {
last unless nextIter(\@bins);
}
return @res;
}
sub nextIter {
my ($bins, $level) = @_;
$level = $#{$bins} if !defined $level;
return undef if $level < 0;
my $b = $bins->[$level];
if ($b->{pos} >= $b->{max}) {
$b->{pos} = $b->{min};
return &nextIter($bins, $level-1);
}
$b->{pos}++;
if ($b->{pos} % 255 == 0) { # skip 255s
$b->{pos}++;
}
return 1;
}
sub curAddr {
my $bins = shift;
my $addr = '';
for (my $i = 0; $i <= $#{$bins}; ++$i) {
my $b = $bins->[$i];
if ($b->{max} > 255) { # spill
$addr .= sprintf("%d.", $b->{pos} / 255);
}
$addr .= sprintf("%d.", $b->{pos} % 255);
}
chop($addr);
return $addr;
}
sub odd {
my $h = shift;
return ($h & 1) > 0
}
syntax highlighted by Code2HTML, v. 0.9.1