#!/usr/bin/perl
#
# This file is part of the CernVM File System.
#
# Client for the stratum agent, interactively triggers replication
#

use strict;
use warnings;
use Data::Dumper;
use Curses::UI;
use Getopt::Std;
use IO::Handle;
use JSON;
use LWP::UserAgent;


# Usage and version information
# ------------------------------------------------------------------------------

my $VERSION = 1.0;
my $CONTACT = "jblomer\@cern.ch";

sub main::HELP_MESSAGE {
  print "Triggers replication on CernVM-FS stratum 1 servers.\n";
  print "\nUsage:\n  $0 [options] [REST URL, REST URL, ...]\n\n";
  print "Examples:  $0 -u urls.db\n";
  print "           $0 http://cvmfs-stratum-one.cern.ch:7999/cvmfs/cernvm-prod.cern.ch\n";
  print "\nOptions: \n";
  print "  -u  URL data file\n";
  print "  -b  Batch mode, no pretty-printed user interface\n";
}

sub main::VERSION_MESSAGE {
  print "$0 version $VERSION (contact: $CONTACT)\n";
}

sub Draw {
  my ($cui) = (shift);
  my $batch_mode = $cui->userdata();
  $cui->draw() if (!$batch_mode);
}

sub Print {
  my ($message, $batch_mode) = (shift, shift);
  STDOUT->printflush("$message") if ($batch_mode);
}

my %url_db;
my $batch_mode;

# Option parsing
$Getopt::Std::STANDARD_HELP_VERSION = 1;
our($opt_u, $opt_b, $opt_h, $opt_v);
getopts('u:bhv');
if ($opt_v) {
  VERSION_MESSAGE();
  exit 0;
}
if ($opt_h) {
  VERSION_MESSAGE();
  HELP_MESSAGE();
  exit 0;
}
if ($opt_u) {
  %url_db = do "$opt_u";
} else {
  %url_db = do "$0.data" if (-e "$0.data");
}
$batch_mode = $opt_b;

while (my $url = shift) {
  my %endpoint;
  $url_db{'endpoints'}{$url}{'name'} = $url;
}
my $num_servers = keys %{$url_db{'endpoints'}};

my $cui = Curses::UI->new(-color_support => 1, -userdata => $batch_mode);
$cui->leave_curses() if ($batch_mode);
my $wnd_main = $cui->add(undef, 'Window',);
my $color_running = 'cyan';
my $color_fail = 'red';
my $color_success = 'green';

my $retcode = 0;

# Trigger replication jobs
my $last_ypos = 0;
foreach my $endpoint (keys %{$url_db{'endpoints'}}) {
  $endpoint =~ m,.*/cvmfs/([^/]*)$,;
  my $fqrn = $1;
  my $name = $url_db{'endpoints'}{$endpoint}{'name'};

  my $container = $wnd_main->add(
    undef, 'Container',
    -title => $endpoint,
    -border => 1,
    -height => $wnd_main->height() / $num_servers,
    -y => $last_ypos,
    -fg => $color_running,
    -bfg => $color_running,
    -tfg => $color_running,
  );
  $last_ypos += int($wnd_main->height() / $num_servers);
  my $textviewer = $container->add(undef, 'TextViewer', -text => '[querying]');
  $url_db{'endpoints'}{$endpoint}{'window'} = $container;
  $url_db{'endpoints'}{$endpoint}{'textviewer'} = $textviewer;
  Draw($cui);

  my $trigger = `echo '{}' | /usr/bin/cvmfs_swissknife letter -s \\
    -c /etc/cvmfs/keys/$fqrn.crt -k /etc/cvmfs/keys/$fqrn.key -f $fqrn`;
  die "cannot construct signed message for $fqrn" if ($? != 0);
  chomp($trigger);

  Print("sending trigger to $endpoint... ", $batch_mode);
  my $post_url = $endpoint . '/api/v1/replicate/new';
  my $request = HTTP::Request->new (
    POST => $post_url,
    HTTP::Headers->new (
      'content-type' => "text/plain",
    ),
    $trigger);
  my $ua = LWP::UserAgent->new;
  $ua->timeout(5);
  my $response = $ua->simple_request($request);

  if (not $response->is_success) {
    $retcode = 1;
    $url_db{'endpoints'}{$endpoint}{'status'} = 'stuck';
    $url_db{'endpoints'}{$endpoint}{'stderr'} =
      "cannot trigger replication at $endpoint:\n" .
      $response->decoded_content;
    $textviewer->set_color_fg($color_fail);
    $container->set_color_bfg($color_fail);
    $container->set_color_tfg($color_fail);
    $textviewer->text($url_db{'endpoints'}{$endpoint}{'stderr'});
    Print("FAIL!\n\r", $batch_mode);
  } else {
    my $job_desc = decode_json($response->decoded_content);
    $url_db{'endpoints'}{$endpoint}{'job_id'} = $job_desc->{'job_id'};
    $url_db{'endpoints'}{$endpoint}{'status'} = 'running';
    my $base_url = $endpoint . '/api/v1/replicate/' . $job_desc->{'job_id'};
    $container->title($base_url);
    Print("ok [$base_url]\n\r", $batch_mode);
  }
  Draw($cui);
}

Print("waiting for replication...\n\r", $batch_mode);

while (1) {
  my $num_todo = 0;
  foreach my $endpoint (keys %{$url_db{'endpoints'}}) {
    my $status = $url_db{'endpoints'}{$endpoint}{'status'};
    next if ($status ne 'running');

    $num_todo++;
    my $container = $url_db{'endpoints'}{$endpoint}{'window'};
    my $textviewer = $url_db{'endpoints'}{$endpoint}{'textviewer'};
    my $job_id = $url_db{'endpoints'}{$endpoint}{'job_id'};
    my $base_url = $endpoint . '/api/v1/replicate/' . $job_id;
    my $ua = LWP::UserAgent->new;
    $ua->timeout(5);
    my $response = $ua->get($base_url . '/status');
    my $status_desc = decode_json($response->decoded_content);
    if ($status_desc->{'status'} eq 'done') {
      $url_db{'endpoints'}{$endpoint}{'status'} = 'done';
      if ($status_desc->{'exit_code'} == 0) {
        $textviewer->set_color_fg($color_success);
        $container->set_color_bfg($color_success);
        $container->set_color_tfg($color_success);
      } else {
        $retcode = 1;
        $textviewer->set_color_fg($color_fail);
        $container->set_color_bfg($color_fail);
        $container->set_color_tfg($color_fail);
      }
    }
    $response = $ua->get($base_url . '/tail');
    $textviewer->text("[...]\n" . $response->decoded_content);
    Draw($cui);
  }
  sleep 1;

  last if ($num_todo == 0);
}

$cui->leave_curses();
foreach my $endpoint (keys %{$url_db{'endpoints'}}) {
  my $status = $url_db{'endpoints'}{$endpoint}{'status'};
  my $name = $url_db{'endpoints'}{$endpoint}{'name'};
  print("$name: $status\n\r");
}

exit $retcode;
