App-SSH-Cluster
view release on metacpan or search on metacpan
lib/App/SSH/Cluster.pm view on Meta::CPAN
package App::SSH::Cluster;
$App::SSH::Cluster::VERSION = '0.004';
# ABSTRACT: CLI to Net::OpenSSH that runs the same command via SSH on many remote servers at the same time
use strict;
use warnings;
use List::MoreUtils qw(all);
use MooseX::App::Simple;
use MooseX::Types::Moose qw/HashRef Str/;
use Net::OpenSSH::Parallel;
use YAML::Tiny;
option 'command' => (
is => 'ro',
isa => Str,
required => 1,
cmd_aliases => [qw(c)],
documentation => 'command to run on remote servers',
);
option 'config_file' => (
is => 'ro',
isa => Str,
default => "$ENV{HOME}/.app-clusterssh.yml",
cmd_aliases => [qw(p)],
cmd_flag => 'config-file',
);
has '_config' => (
is => 'ro',
isa => HashRef,
builder => '_build_config',
init_arg => undef,
lazy => 1,
);
sub run {
my ($self) = @_;
$self->_validate_config;
my $parallel_executor = Net::OpenSSH::Parallel->new;
my $global_identity_file = $self->_config->{identity_file};
my $global_username = $self->_config->{user};
my @hosts = @{ $self->_config->{servers} };
foreach my $host ( @hosts ) {
my $hostname = $host->{hostname};
my $username = $host->{user} // $global_username;
my $identity_file
= $host->{identity_file} // $global_identity_file;
$host->{STDOUT} = "/tmp/$hostname.out";
open my $fh, '>', $host->{STDOUT}
or die "Unable to open file '$host->{STDOUT}' for writing: $!";
$parallel_executor->add_host(
$hostname,
user => $username,
key_path => $identity_file,
default_stdout_fh => $fh,
);
}
$parallel_executor->push('*', 'command' => $self->command);
$parallel_executor->run;
foreach my $host ( @hosts ) {
open my $fh, '<', $host->{STDOUT}
or die "Unable to open file '$host->{STDOUT}' for reading: $!";
print "$host->{hostname}: [@{[$self->command]}]\n";
print '-' x 80 . "\n";
{
local $/ = undef;
my $output = <$fh>;
print "$output\n";
}
close $fh;
}
}
sub _build_config {
my ($self) = @_;
return YAML::Tiny->read( $self->config_file )->[0];
}
sub _validate_config {
my ($self) = @_;
die "No 'servers' key found in " . $self->config_file
unless exists $self->_config->{servers};
die "Existing 'servers' key found in " . $self->config_file . ", but has no servers listed"
if exists $self->_config->{servers}
&& ref $self->_config->{servers} ne 'ARRAY'
|| (
ref $self->_config->{servers} eq 'ARRAY'
&& @{$self->_config->{servers}} == 0
);
foreach my $key ( qw(hostname identity_file user) ) {
$self->_has_config_key($key);
}
}
sub _has_config_key {
my ($self, $key) = @_;
die "No '$key' key found in " . $self->config_file
unless exists $self->_config->{$key}
|| all { exists $_->{$key} } @{ $self->_config->{servers} };
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
App::SSH::Cluster - CLI to Net::OpenSSH that runs the same command via SSH on many remote servers at the same time
=head1 VERSION
version 0.004
=head1 SYNOPSIS
use App::SSH::Cluster;
App::SSH::Cluster->new_with_options;
=head1 DESCRIPTION
Simple application to execute the same remote command across one or more remote servers. This module does *not* handle errors that are generated from remote commands and currently does not log the STDERR of the remote commands that are executed. Any ...
=head1 NAME
App::SSH::Cluster
=head1 ATTRIBUTES
=over 4
=item C<command>
( run in 1.250 second using v1.01-cache-2.11-cpan-39bf76dae61 )