#!/usr/bin/env perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_monitoring::rmcmetrix;

#Modules to use:
#use threads;
#use threads::shared;

BEGIN
{
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}

use lib "$::XCATROOT/lib/perl";
use xCAT::Table;
use xCAT::Utils;
use xCAT_monitoring::rrdutil;

1;

#metrix{Attr}{NodeNameList}{Name} = value
#NodeNameList could be NodeNameList | summary | number
#Attr	could be Attribute | summary | number
%metrix = {};

######################################
#sub print_metrix
#Description:
#	to print all content of %metrix, for debug
#Input:
#	None
#Return:
#	None
#####################################
sub print_metrix
{
    my $key1;
    my $key2;
    my $key3;
    my $value;
    while (($key1, $key2) = each %metrix) {
        if (!exists($metrix{$key1}{$key2})) {
            print "$key1=>$key2\n";
            next;
        }
        while (($key2, $key3) = each %{ $metrix{$key1} }) {
            if (!exists($metrix{$key1}{$key2}{$key3})) {
                print "$key1.$key2=>$key3\n";
            }
            while (($key3, $value) = each %{ $metrix{$key1}{$key2} }) {
                print "$key1.$key2.$key3=>$value\n";
            }
        }
    }

}

#######################################
#sub update_metrix2rrd
#Description:
#	update RRDtool database based on content of %metrix,
#	all statistic be stored as GAUGE in RRDtool,
#Input:
#	$step the base interval in seconds with which data will be fed into the RRD
#Return:
#	None
#####################################

sub update_metrix2rrd
{
    my $step = shift @_;
    my $ret  = 0;
    my $attr;
    my $nnlist;
    my $name;
    my $value;
    my $rmcrrdroot   = "/var/rrd";
    my $rrddir       = undef;
    my $rrd          = undef;
    my $process_time = xCAT::Utils->runcmd("date +%s", 0);
    $process_time = (int $process_time / $step) * $step;
    my $temp = undef;

    while (($attr, $nnlist) = each %metrix) {
        while (($nnlist, $name) = each %{ $metrix{$attr} }) {
            if ($nnlist eq 'number') {
                next;
            }
            $rrddir = "$rmcrrdroot/$nnlist";
            if (!-d $rrddir) {
                xCAT::Utils->runcmd("mkdir -p $rrddir");
            }
            if ($nnlist eq 'summary') {
                $rrd  = "$rrddir/" . "$attr.rrd";
                $temp = $metrix{$attr}{summary} / $metrix{$attr}{number};
                $ret = xCAT_monitoring::rrdutil::push_data_to_rrd($rrd, $temp, $metrix{$attr}{number}, $step, $process_time, 'GAUGE');
                if ($ret != 0) {
                    return ($ret, "Can't push data to $rrd\n");
                }
            } else {
                while (($name, $value) = each %{ $metrix{$attr}{$nnlist} }) {
                    if ($name eq 'number') {
                        next;
                    }
                    if ($name eq 'summary') {
                        $rrd = "$rrddir/$attr.rrd";
                        $temp = $metrix{$attr}{$nnlist}{summary} / $metrix{$attr}{$nnlist}{number};
                        $ret = xCAT_monitoring::rrdutil::push_data_to_rrd($rrd, $temp, $metrix{$attr}{$nnlist}{number}, $step, $process_time, 'GAUGE');
                        if ($ret != 0) {
                            return ($ret, "Can't push data to $rrd\n");
                        }
                    } else {
                        $rrd = "$rrddir/$attr" . "_$name.rrd";
                        xCAT_monitoring::rrdutil::push_data_to_rrd($rrd, $metrix{$attr}{$nnlist}{$name}, 'null', $step, $process_time, 'GAUGE');
                        if ($ret != 0) {
                            return ($ret, "Can't push data to $rrd\n");
                        }
                    }
                }
            }
        }
    }
    return (0, "Success");

}

######################################
#sub parse_lsrsrc_output
#Description:
#	parse the output of lsrsrc and store the information to %metrix
#Input:
#	resource class
#	array of attributes
#	array of output of lsrsrc command
#Return:
#	0 Success
#	!0 Fail
#####################################
sub parse_lsrsrc_output
{
    my ($rsrc, $pattr, $output) = @_;
    my $nnlist = undef;
    my $name   = undef;
    my $line   = undef;
    my $attr   = undef;
    my @value  = ();
    my $i      = undef;
    my %count  = {};

    foreach $line (@$output) {
        if ($line =~ /^ERROR/) { next; }    #skip the lines with error
        @value = split /::/, $line;
        $name = $value[0];
        $name =~ s/[^A-Za-z0-9]+/'.'/;
        if ($name eq '') {
            $name = 'null';
        }
        $value[1] =~ /{(\w+)}/;
        $nnlist = $1;
        $i      = 2;
        foreach $attr (@$pattr) {
            if ($rsrc eq 'IBM.Processor') {
                $metrix{$attr}{$nnlist}{$name} += $value[$i];
            } else {
                $metrix{$attr}{$nnlist}{$name} = $value[$i];
            }
            $metrix{$attr}{$nnlist}{summary} += $value[$i];
            $metrix{$attr}{$nnlist}{number}  += 1;
            $i++;
        }
        if ($rsrc eq 'IBM.Processor') {
            $count{$nnlist}{$name} += 1;
        }
    }
    if ($rsrc eq 'IBM.Processor') {
        foreach $nnlist (keys %count) {
            foreach $name (keys %{ $count{$nnlist} }) {
                if ($count{$nnlist}{$name} > 1) {
                    foreach $attr (@$pattr) {
                        $metrix{$attr}{$nnlist}{$name} /= $count{$nnlist}{$name};
                    }
                }
            }
        }
    }


    return 0;

}

######################################
#sub getmetrix
#Description:
#	Get %metrix using lsrsrc-api, and store to RRD
#Input:
#	$rsrc	the resource class of RMC, such as "IBM.EthernetDevice"
#	$rname	the resource name of resouce class, if ($rname eq "__ALL__") then all
#		resource name will be monitoring
#	$attrlist the list of attributes of the monitoring resource
#	$minute the interval to collect data in minute
#Return:
#	0 Success
#	!0 Fail
#####################################
sub getmetrix
{
    my ($rsrc, $rname, $attrlist, $minute) = @_;
    my @attrs  = ();
    my $attr   = undef;
    my $nnlist = undef;
    my @names  = ();
    my $name   = undef;
    my @output = ();
    my $rrd    = undef;
    my $line   = undef;
    my $ret    = 0;
    my $msg    = undef;
    my $cmd    = undef;

    @attrs = split /,/, $attrlist;

    $attr = join '::', @attrs;

    #	if(xCAT::Utils->isMN()){
    #		if($rname eq "__ALL__"){
    #			$cmd = "CT_MANAGEMENT_SCOPE=1 lsrsrc-api -i -s $rsrc"."::::Name::NodeNameList::$attr";
    #			@output = xCAT::Utils->runcmd($cmd, 0);
    #			if($::RUNCMD_RC  != 0){
    #				$line = join '', @output;
    #				return ($::RUNCMD_RC, $line);
    #			}
    #			&parse_lsrsrc_output($rsrc, \@attrs, \@output);
    #		} else {
    #			@names = split /,/, $rname;
    #			foreach $name (@names){
    #				$cmd = "CT_MANAGEMENT_SCOPE=1 lsrsrc-api -i -s $rsrc"."::\'Name==\"$name\"\'::Name::NodeNameList::$attr";
    #				@output = xCAT::Utils->runcmd($cmd, 0);
    #				if($::RUNCMD_RC != 0){
    #					$line = join '', @output;
    #					return ($::RUNCMD_RC, $line);
    #				}
    #				&parse_lsrsrc_output($rsrc, \@attrs, \@output);
    #			}
    #		}
    #	}

    if ($rname eq "__ALL__") {
        $cmd = "CT_MANAGEMENT_SCOPE=3 lsrsrc-api -i -s $rsrc" . "::::Name::NodeNameList::$attr";
        @output = xCAT::Utils->runcmd($cmd, -1);

        #if($::RUNCMD_RC != 0){
        #	$line = join '', @output;
        #	return ($::RUNCMD_RC, $line);
        #}
        #print "+++++ rsrc=$rsrc\nattrs=@attrs\noutput=@output\n";
        &parse_lsrsrc_output($rsrc, \@attrs, \@output);
    } else {
        @names = split /,/, $rname;
        foreach $name (@names) {
            $cmd = "CT_MANAGEMENT_SCOPE=3 lsrsrc-api -i -s $rsrc" . "::\'Name==\"$name\"\'::Name::NodeNameList::$attr";
            @output = xCAT::Utils->runcmd($cmd, -1);

            #if($::RUNCMD_RC){
            #	$line = join '', @output;
            #	return ($::RUNCMD_RC, $line);
            #}
            #print "--- rsrc=$rsrc\nattrs=@attrs\noutput=@output\n";
            &parse_lsrsrc_output($rsrc, \@attrs, \@output);
        }
    }
    foreach $attr (keys %metrix) {
        foreach $nnlist (keys %{ $metrix{$attr} }) {
            if (($nnlist ne 'summary') && ($nnlist ne 'number')) {
                $metrix{$attr}{summary} += $metrix{$attr}{$nnlist}{summary};
                $metrix{$attr}{number}  += $metrix{$attr}{$nnlist}{number};
            }

        }
    }

    my $step = $minute * 60;
    ($ret, $msg) = &update_metrix2rrd($step);

    return ($ret, $msg);
}

######################################
#sub get_metrix_conf
#Description:
#	Get configure for table monsetting, and return an array
#Input:
#	None;
#Return:
#	an array of configuration (rsrc0, attrlist0, minute0, rsrc1, attrlist1,minute1, ...)
#####################################
sub get_metrix_conf
{
    my @conf      = ();
    my @tmp       = ();
    my $rsrc      = undef;
    my $namelist  = undef;
    my $attrlist  = undef;
    my $minute    = undef;
    my $key       = undef;
    my $value     = undef;
    my $conftable = xCAT::Table->new('monsetting');

    if ($conftable) {
        @tmp = $conftable->getAttribs({ 'name' => 'rmcmon' }, ('key', 'value'));
        foreach (@tmp) {
            $key   = $_->{key};
            $value = $_->{value};
            if ($key =~ /^rmetrics_(\S+)/) {
                push @conf, $1;
                if ($value =~ /\]/) {
                    ($namelist, $value) = split /\]/, $value;
                    $namelist =~ s/\[//;
                } else {
                    $namelist = "__ALL__";
                }
                push @conf, $namelist;

                ($attrlist, $minute) = split /:/, $value;
                push @conf, $attrlist;
                push @conf, $minute;
            }
        }
        $conftable->close;
    }
    return @conf;
}

######################################
#sub get_sum_metrix
#Description:
#	Consolidates data collected by SNs and MN  and stores to local RRD
#Input:
#	$attrlist the list of attributes of the monitoring resource
#Return:
#	0 Success
#	!0 Fail
#####################################
sub get_sum_metrix
{
    my $code = undef;
    my $msg  = undef;
    my ($attrlist, $minute) = @_;
    my $result    = undef;
    my @rmc_nodes = ();
    my @svc_nodes = ();
    my $node      = undef;
    my $temp      = undef;

    #	my @threads = ();
    #	my $current_thread = 0;
    my $i = undef;

    #	my %summary:shared; #summary{$attr}{$node}
    my %summary    = {};
    my @attributes = ();
    my $attribute  = undef;
    my $nodename   = undef;
    my $time       = undef;

    #	my $end:shared;
    my %summetrix = {};
    my $end       = undef;
    $end = xCAT::Utils->runcmd("date +%s", 0);

    #	my $step:shared;
    my $step = undef;
    $step = $minute * 60;

    #to share %summary
    @attributes = split /,/, $attrlist;

    #	foreach $attribute (@attributes){
    #		$summary{$attribute} = &share({});
    #	}
    $result = `lsrsrc-api -s IBM.MngNode::::Name 2>&1`;
    chomp($result);
    @rmc_nodes = split(/\n/, $result);
    foreach $node (@rmc_nodes) {
        if (xCAT::Utils->isSN($node)) {
            push @svc_nodes, $node;
        }
    }
    $node = `hostname`;
    chomp($node);
    push @svc_nodes, $node;
    foreach $node (@svc_nodes) {

        #		$threads[$current_thread] = threads->new(\&getsum, $attrlist, $node);
        #		$current_thread++;
        &getsum($attrlist, $node);
    }

    sub getsum {
        my ($attrs, $n) = @_;
        my @attr      = split /,/, $attrs;
        my $a         = undef;
        my $start     = undef;
        my $result    = undef;
        my $timestamp = undef;
        my $sum       = undef;
        my $num       = undef;
        my $localhost = `hostname`;
        chomp($localhost);

        foreach $a (@attr) {
            if (-f "/var/rrd/cluster/$a.rrd") {
                $start = `rrdtool last /var/rrd/cluster/$a.rrd`;
                chomp($start);
            } else {
                $start = ((int $end / $step) - 244) * $step;
            }
            if ($n eq $localhost) {
                $result = xCAT_monitoring::rrdutil::RRD_fetch("/var/rrd/summary/$a.rrd", $start, $end);
            } else {
                $result = xCAT_monitoring::rrdutil::RRD_fetch("summary/$a.rrd", $start, $end, $n);
            }
            my $line = pop(@$result);
            if ($line =~ /ERROR/) {
                return (-1, $line);
            } else {
                push @$result, $line;
            }

            #			$summary{$a}{$n} = &share({});
            foreach $line (@$result) {
                if ($line =~ /[NaNQ|nan]/) {
                    next;
                } elsif ($line =~ /^(\d+): (\S+) (\S+)/) {
                    $timestamp = $1;
                    $sum       = $2;
                    $num       = $3;

                    #					$summary{$a}{$n}{$timestamp} = &share({});
                    #					$summary{$a}{$n}{$timestamp}{sum} = &share({});
                    #					$summary{$a}{$n}{$timestamp}{num} = &share({});
                    $summetrix{$a}{$timestamp}{sum} += $sum * $num;
                    $summetrix{$a}{$timestamp}{num} += $num;
                }
            }
        }
        return (0, 'Success');
    }

    #	for($i=0; $i<$current_thread; $i++){
    #		($code, $msg) = $threads[$i]->join();
    #		if($code != 0){
    #			warn("$msg\n");
    #		}
    #	}

    #	my %summetrix = {};
    #	foreach $attribute (keys %summary){
    #		foreach $nodename (keys %{$summary{$attribute}}){
    #			foreach $time (keys %{$summary{$attribute}{$nodename}}){
    #				print "$attribute.$nodename.$time $summary{$attribute}{$nodename}{$time}{sum} $summary{$attribute}{$nodename}{$time}{num}\n";
    #				$temp = $summary{$attribute}{$nodename}{$time}{sum} * $summary{$attribute}{$nodename}{$time}{num};
    #				$summetrix{$attribute}{$time}{sum} += $temp;
    #				$summetrix{$attribute}{$time}{num} += $summary{$attribute}{$nodename}{$time}{num};
    #			}
    #		}

    #	}

    my $rrdcluster = "/var/rrd/cluster";
    if (!-d $rrdcluster) {
        system("mkdir -p $rrdcluster");
    }
    foreach $attribute (keys %summetrix) {
        my @times     = keys(%{ $summetrix{$attribute} });
        my @sorttimes = sort @times;
        foreach $time (@sorttimes) {
            $temp = $summetrix{$attribute}{$time}{sum} / $summetrix{$attribute}{$time}{num};
            $code = xCAT_monitoring::rrdutil::push_data_to_rrd("$rrdcluster/$attribute.rrd", $temp, $summetrix{$attribute}{$time}{num}, $step, $time, 'GAUGE');
            if ($code != 0) {
                return ($code, "Can't push data to $rrdcluster/$attribute.rrd");
            }
        }
    }

    return (0, 'Success');
}

