#!/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 [[-] [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 }