#!/usr/bin/perl

=head1 NAME
 
pscheduler-stats - Get statistics about a pSConfig agent
 
=head1 SYNOPSIS
 
pscheduler-stats [options]
 
 Options:
   -h | --help          help message
   -f | --format        output format (text|json|prometheus)
   -d | --logdir DIR    directory containing log files to parse

=head1 ARGUMENTS

=head1 OPTIONS
 
=over 8
 
=item B<-h | --help>
 
Prints a help message and exits

=item B<-f | --format>

Output format to use.  Known format are:
- text (default)
- json
- prometheus

=item B<-d | --logdir DIR>
 
Directory containing log files to parse

=back
 
=head1 DESCRIPTION
 
B<This program> will parse the tasks log of a pSConfig agent and print statistics
 
=cut
use strict;
use warnings;

use FindBin qw($Bin);
use lib "$Bin/../../lib";
use Getopt::Long qw(GetOptions);
use Pod::Usage;

use File::ReadBackwards;
use Time::Piece;

#parse options
my $format = 'text';
my $logdir;
my $help;
GetOptions(
    'help|h' => \$help,
    'format|f=s' => \$format,
    'logdir|d=s' => \$logdir,
) or pod2usage({ -verbose => 0, -exitval => 2 });

#parse arguments
if($help){
    pod2usage({ -exitval => 0, -verbose => 2, -noperldoc => 1});
}elsif(@ARGV != 0){
    my $exitval = 2;
    pod2usage({
        -message => "This command does not accept arguments",
        -exitval => $exitval,
        -verbose => 0   
    });
    exit $exitval;
}


#Set file names
unless($logdir){
    $logdir = '/var/log/perfsonar';
}
my $AGENT_LOG_FILE = "$logdir/psconfig-pscheduler-agent.log";
my $TASKS_LOG_FILE = "$logdir/psconfig-pscheduler-agent-tasks.log";
my $TRANSACTIONS_LOG_FILE = "$logdir/psconfig-pscheduler-agent-tasks.log";

#TODO: Handle rotation

#find guid
my ($start, $end, $pid, $guid);
my $agent_log = File::ReadBackwards->new($AGENT_LOG_FILE) or die "Unable to open $AGENT_LOG_FILE: $!\n" ;
my $agent_log_line;
while( defined ($agent_log_line = $agent_log->readline) ){
    if(!$guid && $agent_log_line =~ /(.+) INFO pid=(.+?) prog=.+? line=.+? guid=(.+?) msg=Agent completed running$/){
        $end = $1;
        $pid = $2;
        $guid = $3;
    }elsif($guid && $agent_log_line =~ /(.+) INFO pid=$pid prog=.+? line=.+? guid=$guid msg=Running agent\.\.\.$/){
        $start = $1;
        last;
    }
}
unless($guid){
    print STDERR "Unable to find last guid in $AGENT_LOG_FILE. Make sure the agent has completed at least one run.\n";
    exit 1;
}

#parse tasks
my $sums = { 
    'total' => 0,
    'by_src' => {},
};
my $tasks_log = File::ReadBackwards->new($TASKS_LOG_FILE) or die "Unable to open $TASKS_LOG_FILE: $!\n" ;
my $tasks_log_line;
while( defined ($tasks_log_line = $tasks_log->readline) ){
    if($tasks_log_line =~ /^.+ INFO guid=$guid (.+) task=.+$/){
        $sums->{'total'}++;
        #parse context
        my $ctx = $1;
        my $ctx_map = {};
        while($ctx =~ /(\w+?)=(.+?)( |$)/g){
            $ctx_map->{$1} = $2;
        }
        #add to sums
        if($ctx_map->{'config_src'}){
            $sums->{'by_src'}->{$ctx_map->{'config_src'}} = {'total' => 0, 'by_url' => {} } unless($sums->{'by_src'}->{$ctx_map->{'config_src'}}); 
            $sums->{'by_src'}->{$ctx_map->{'config_src'}}->{'total'}++;
            my $by_url = $sums->{'by_src'}->{$ctx_map->{'config_src'}}->{'by_url'};
            if($ctx_map->{'config_url'}){
                $by_url->{$ctx_map->{'config_url'}} = 0 unless($by_url->{$ctx_map->{'config_url'}}); 
                $by_url->{$ctx_map->{'config_url'}}++;
            }elsif($ctx_map->{'config_file'}){
                $by_url->{$ctx_map->{'config_file'}} = 0 unless($by_url->{$ctx_map->{'config_file'}}); 
                $by_url->{$ctx_map->{'config_file'}}++;
            }
        }
    }
}

#Output
my $st = Time::Piece->strptime($start, "%Y/%m/%d %H:%M:%S");
my $et = Time::Piece->strptime($end, "%Y/%m/%d %H:%M:%S");
if($format eq 'json'){
    print "{\"pscheduler-agent\": {\"start-time\": ".$st->strftime("\"%Y-%m-%dT%H:%M:%S\"").
        ", \"end-time\": ".$et->strftime("\"%Y-%m-%dT%H:%M:%S\"").
        "}, \"pscheduler-tasks\": {\"total\": ".$sums->{'total'};
        foreach my $src(sort keys %{$sums->{'by_src'}}){
            my $src_stats = $sums->{'by_src'}->{$src};
            print ", \"".$src."\": {\"total\": ".$src_stats->{'total'};
            foreach my $url(sort keys %{$src_stats->{'by_url'}}){
                print ", \"".$url."\": ".$src_stats->{'by_url'}->{$url};
            }
            print "}";
        }
        print "}}";
    print "\n";
}elsif($format eq 'prometheus'){
    print "# HELP perfsonar_psconfig_pscheduler_agent_start_time Number of seconds since 1970 of psconfig-pscheduler-agent start time\n";
    print "# TYPE perfsonar_psconfig_pscheduler_agent_start_time gauge\n";
    print "perfsonar_psconfig_pscheduler_agent_start_time ".$et->epoch."\n";
    print "# HELP perfsonar_psconfig_pscheduler_agent_end_time Number of seconds since 1970 of psconfig-pscheduler-agent end time\n";
    print "# TYPE perfsonar_psconfig_pscheduler_agent_end_time gauge\n";
    print "perfsonar_psconfig_pscheduler_agent_end_time ".$st->epoch."\n";
    print "# HELP perfsonar_psconfig_pscheduler_tasks Number of tasks configured by pSconfig in pScheduler\n";
    print "# TYPE perfsonar_psconfig_pscheduler_tasks gauge\n";
    foreach my $src(sort keys %{$sums->{'by_src'}}){
        my $src_stats = $sums->{'by_src'}->{$src};
        foreach my $url(sort keys %{$src_stats->{'by_url'}}){
            print "perfsonar_psconfig_pscheduler_tasks{src=\"".$src."\",url=\"".$url."\"} ".$src_stats->{'by_url'}->{$url}."\n";
        }
    }
}else{
    print "Agent Last Run Start Time: $start\n";
    print "Agent Last Run End Time: $end\n";
    print "Agent Last Run Process ID (PID): $pid\n";
    print "Agent Last Run Log GUID: $guid\n";
    print "Total tasks managed by agent: " . $sums->{'total'} . "\n";
    foreach my $src(sort keys %{$sums->{'by_src'}}){
        my $src_stats = $sums->{'by_src'}->{$src};
        if($src eq 'remote'){
            print "From remote definitions: " . $src_stats->{'total'} . "\n";
        }elsif($src eq 'include'){
            print "From include files: " . $src_stats->{'total'} . "\n";
        }else{
            print "From $src: " . $src_stats->{'total'} . "\n";
        }
        foreach my $url(sort keys %{$src_stats->{'by_url'}}){
            print "    $url: " . $src_stats->{'by_url'}->{$url} . "\n";
        }
    }
}
_success();

#Subroutines
sub _success{
    exit 0;
}
