#!/usr/bin/perl

use strict;
use warnings;
use open ':std', ':encoding(UTF-8)';

use Net::OpenSSH;

use lib '/app/tools/common/lib';
use Common::Session;
use Common::Util(qw/trimspaces/);
use Common::RunCommand(qw/execute/);

use lib '/app/tools/admin/lib';
use Admin::Sys::Host;

my ( $opt, $usage ) = clopt(
    [ 'admin|m'         => 'Execute command on admin hosts' ],
    [ 'all|A'           => 'Execute command on all hosts' ],
    [ 'appserver|a'     => 'Execute command on app hosts' ],
    [ 'database|d'      => 'Execute command on db hosts' ],
    [ 'destination|D=s' => 'Destination for scp file' ],
    [ 'dqr|q'           => 'Execute command on dev/qa/release hosts' ],
    [ 'file|F=s'        => 'File to put on server', { 'implies' => { 'command' => 1 } } ],
    [ 'filter|f=s'      => 'Filter out hosts "," list' ],
    [ 'help|?'   => "print usage message and exit", { 'shortcircuit' => 1 } ],
    [ 'host|h=s' => 'Single host to execute command' ],
    [ 'list|l'      => 'List hostname and IP', { 'implies' => { 'command' => 1 } } ],
    [ 'webserver|w' => 'Execute command on web hosts' ],
    ## required at the end b/c of implied opts
    [ 'command|c=s' => 'Command to execute REQ unless --list or --put', { 'required' => 1 } ],
);
if ( $opt->help ) {
    print $usage->text . "\n";
    exit 0;
}

if ( $opt->file and !$opt->destination ) {
    print "ERR --file requires --destination";
    print $usage->text . "\n";
    exit 1;
}

my $hosts;
my $hostConf = Admin::Sys::Host->instance;
if ( defined $opt->host and $opt->host !~ m/^172/ ) {
    my ($grep) = grep /$opt->{'host'}/, @{ $hostConf->all };
    push @{$hosts}, $grep;
} elsif ( defined $opt->host and $opt->host =~ m/^172/ ) {
    push @{$hosts}, $opt->host;
} else {
    foreach my $meth (qw/all webserver database appserver admin dqr/) {
        $hosts = $hostConf->{$meth} if defined $opt->{$meth};
    }
    if ( defined $opt->list and not defined $hosts ) {
        $hosts = $hostConf->{'all'};
    }
}

my $filter;
if ( $opt->filter ) {
    my @filters = split ',', $opt->filter;
    map { $filter->{$_}++ } @filters;
}

foreach my $host ( @{$hosts} ) {
    next if defined $filter and exists $filter->{$host};
    if ( $opt->list ) {
        my $output = execute( "nslookup $host", "nout" );
        my $ip;
        if ( $output->[2] ) {
            $ip = "#127.0.0.1";
            $host .= " not in resolv.conf";
        } else {
            my $addy = execute( "nslookup $host | grep -i -A1 name| grep -i address|cut -d':' -f2 | tr -d '\n' ", "nout" );
            $ip = $addy->[0];
        }
        printf( "%-16s%-16s\n", trimspaces($ip), $host );
    } elsif ( $opt->destination ) {
        header($host);
        my $scp = "scp -rq $opt->{'file'} $host:$opt->{'destination'}";
        print "$scp\n";
        system($scp);
    } else {
        header($host);
        my $ssh = Net::OpenSSH->new( $host, 'master_opts' => ['-q'] );
        my $cmd = $opt->command;
        my ( $rout, $pid ) = $ssh->pipe_out($cmd);
        while (<$rout>) {
            chomp;
            print "$_\n";
        }
        close $rout;
    }
}

sub header {
    my $host = shift;
    print "~" x 80 . "\n";
    print "$host\n";
    print "~" x 80 . "\n";
}
