#!/usr/bin/perl
# $Id: oarnodes 1813 2008-12-22 14:31:55Z bzizou $
# print OAR node properties
#
# EXAMPLES:
# oarnodes -l
#   => returns the complete list without information  - status = 0
# oarnodes -a
#   => returns the complete list with information  - status = 0
# oarnodes -s
#   => returns only the state of nodes - status = 0
# oarnodes -h|--help
#   => returns a help message - status = 0
# oarnodes host1 [.. hostn]
#   => returns the information for hostX - status is 0 for every host known - 1 otherwise
#

use strict;
use warnings;
use OAR::Conf qw(init_conf dump_conf get_conf is_conf);
use OAR::Tools;
use Data::Dumper;
use OAR::IO;
use Getopt::Long;
use OAR::Version;

my $Old_umask = sprintf("%lo",umask());
umask(oct("022"));

#Try to load XML module
my $XML_enabled = 1;
unless (eval "use XML::Dumper qw(pl2xml);1"){
    $XML_enabled = 0;
}

#Try to load YAML module
my $YAML_enabled = 1;
unless (eval "use YAML;1"){
    $YAML_enabled = 0;
}

# suitable Data::Dumper configuration for serialization
$Data::Dumper::Purity = 1;
$Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 0;
$Data::Dumper::Deepcopy = 1;

warn("WARNING: This command will be deprecated in a future release!\n");

Getopt::Long::Configure ("gnu_getopt");
my $stateMode;
my $usage;
my $listall;
my $listallwithinfo;
my @resources;
my $sql_property;
my $XML_mode;
my $YAML_mode;
my $DUMPER_mode;
my $Version;
my $Old;
my $smp;
my $events;
GetOptions ("state|s" => \$stateMode,
            "help|h"  => \$usage,
            "list|l"  => \$listall,
            "all|a"   => \$listallwithinfo,
            "events|e:s"  => \$events,
            "resource|r=i" => \@resources,
            "sql=s" => \$sql_property,
            "xml|X" => \$XML_mode,
            "yaml|Y" => \$YAML_mode,
            "dumper|D" => \$DUMPER_mode,
            "backward-compatible" => \$Old,
            "smp" => \$smp,
            "version|V" => \$Version
           ) or exit(1);

if ($usage){
    print <<EOS;
Usage: oarnodes [ -a | -r number | --sql sql_properties | -s | -l | -h | -V ]
[list of nodes] [ -e [date] | -X | -Y | -D | --backward-compatible [--smp]]
Display node informations
Options:
 -a, --all          show all nodes with their properties
 -r, --resource     show only a resource properties
 -s, --state        show only node states
 -l, --list         show only node list
 -e, --events       show events recorded for a node: either the last 20 if no 
                    date is given or all events since the specified date
     --sql          display resources which matches this sql where clause
                    (ex: "state = 'Suspected'")
 -D, --dumper       print result in DUMPER format
 -X, --xml          print result in XML format
 -Y, --yaml         print result in YAML format
     --backward-compatible   print informations of the form of 1.* OAR version
     --smp          backward-compatible suboption: prints one node per resource
                    (usefull for big smp systems) 
 -h, --help         show this help screen
 -V, --version      print OAR version number
EOS
    exit(0);
}

if (defined($Version)){
    print("OAR version : ".OAR::Version::get_version()."\n");
    exit(0);
}

my $exitstatus = 0;
my $base = OAR::IO::connect_ro();

sub print_event_logs($){
    my $event_hashref = shift;
    printf("%s| %s| %s: %s\n", 
        OAR::IO::local_to_sql($event_hashref->{'date'}),
        $event_hashref->{'job_id'},
        $event_hashref->{'type'},
        $event_hashref->{'description'}
    );
}

if (defined ($events)) {
    my $hostname = $ARGV[0];
    if (not defined($hostname)) {
        die("No hostname specified\n");
    }
    # here $events can contain the date from which we have to display info.
    my @events = OAR::IO::get_events_for_hostname($base, $hostname, $events);    
    foreach my $current_event (@events) {  
        print_event_logs($current_event);
    }
    OAR::IO::disconnect($base);
    exit 0;
}

if (($#resources >= 0) or (defined($sql_property))){
    if (defined($sql_property)){
        push(@resources, OAR::IO::get_resources_with_given_sql($base,$sql_property));
    }
    my @data_to_display;
    my %state_hash;
    foreach my $r (@resources) {
        my $resource_info = OAR::IO::get_resource_info($base,$r);
        if (!defined($resource_info)) {
            warn("/!\\ $r UNKNOWN\n");
            $exitstatus = 1;
            next;
        }
        $state_hash{$r} = $resource_info->{state};
        my %tmp_hash;
        $tmp_hash{state} = $resource_info->{state};
        $tmp_hash{network_address} = $resource_info->{network_address};
        $tmp_hash{properties} = $resource_info;
        #if ($resource_info->{state} eq "Alive") {    
            my @jobs = OAR::IO::get_resource_job($base,$resource_info->{resource_id});
            if ($#jobs >= 0){
                $tmp_hash{jobs} = \@jobs;
            }
        #}
        $tmp_hash{resource_id} = $r;
        push(@data_to_display, \%tmp_hash);
    }
    my $data_to_dump = \@data_to_display;
    if (defined($stateMode)){
        $data_to_dump = \%state_hash;
    }
    if (defined($DUMPER_mode)){
        print(Dumper($data_to_dump));
    }elsif(defined($XML_mode)){
        if ($XML_enabled == 1){
            my $dump = new XML::Dumper;
            $dump->dtd;
            print($dump->pl2xml($data_to_dump));
        }else{
            warn("XML module not available on the system. Ask your administrator to install it if needed.\n");
            $exitstatus = 5;
        }
    }elsif(defined($YAML_mode)){
        if ($YAML_enabled == 1){
            print(YAML::Dump($data_to_dump));
        }else{
            warn("YAML module not available on the system. Ask your administrator to install it if needed.\n");
            $exitstatus = 6;
        }
    }else{
        foreach my $h (@data_to_display){
            my %a = %{$h};
            if (defined($stateMode)){
                print("$a{resource_id} : $a{state}\n");
                next;
            }
            print("resource_id : $a{resource_id}\n");
            foreach my $b (keys(%a)){
                if ($b eq "properties"){
                    my $str = "properties : ";
                    foreach my $prop (sort(keys(%{$a{$b}}))){
                        if (OAR::Tools::check_resource_system_property($prop) == 0){
                            if (defined($a{$b}->{$prop})){
                                $str .= "$prop=$a{$b}->{$prop},";
                            }else{
                                $str .= "$prop=,";
                            }
                        }
                    }
                    chop($str);
                    print("$str\n");
                }elsif ($b eq "jobs"){
                    print("job : @{$a{$b}}\n");
                }elsif ($b eq "resource_id"){
                    next;
                }else{
                    print("$b : $a{$b}\n");
                }
            }
            print("\n");
        }
    }
    OAR::IO::disconnect($base);

    exit($exitstatus);
}


# Nodes list handling (set @nodes to what has been requested)
my @nodes;
if($ARGV[0]){
    @nodes = @ARGV;
}else{
    @nodes = OAR::IO::list_nodes($base);
}

if (defined($stateMode)){
    # Display the state of each resources
    my %data_to_display;
    foreach my $node ( @nodes ) {
        my @node_info = OAR::IO::get_node_info($base,$node);
        if ($#node_info >= 0){
            my %tmp_data;
            foreach my $n (@node_info){
                $tmp_data{$n->{resource_id}} = $n->{state};
            }
            $data_to_display{$node} = \%tmp_data;
        }else{
            $data_to_display{$node} = undef;
        }
    }
    if (defined($DUMPER_mode)){
        print(Dumper(\%data_to_display));
    }elsif(defined($XML_mode)){
        if ($XML_enabled == 1){
            my $dump = new XML::Dumper;
            $dump->dtd;
            print($dump->pl2xml(\%data_to_display));
        }else{
            warn("XML module not available on the system. Ask your administrator to install it if needed.\n");
            $exitstatus = 5;
        }
    }elsif(defined($YAML_mode)){
        if ($YAML_enabled == 1){
            print(YAML::Dump(\%data_to_display));
        }else{
            warn("YAML module not available on the system. Ask your administrator to install it if needed.\n");
            $exitstatus = 6;
        }
    }else{
        foreach my $d (sort(keys(%data_to_display))){
            print("$d\n");
            foreach my $e (sort({$a <=> $b} keys(%{$data_to_display{$d}}))){
                print("    $e : $data_to_display{$d}->{$e}\n");
            }
            print("\n");
        }
    }
}elsif(defined($listall)){
    # Simple list handling
    if (defined($DUMPER_mode)){
        print(Dumper(\@nodes));
    }elsif(defined($XML_mode)){
        if ($XML_enabled == 1){
            my $dump = new XML::Dumper;
            $dump->dtd;
            print($dump->pl2xml(\@nodes));
        }else{
            warn("XML module not available on the system. Ask your administrator to install it if needed.\n");
            $exitstatus = 5;
        }
    }elsif(defined($YAML_mode)){
        if ($YAML_enabled == 1){
            print(YAML::Dump(\@nodes));
        }else{
            warn("YAML module not available on the system. Ask your administrator to install it if needed.\n");
            $exitstatus = 6;
        }
    }else{
        foreach my $node ( @nodes ) {
            print($node,"\n");
        }
    }
}else{
    # Default display of oarnodes
    my %data_to_display;
    foreach my $node ( @nodes ) {
        my @node_info = OAR::IO::get_node_info($base,$node);
        if ($#node_info < 0) {
            warn("/!\\ $node UNKNOWN\n");
            $exitstatus = 1;
            next;
        }
        my %tmp_hash1;
        foreach my $r (@node_info){
            my %tmp_hash2;
            $tmp_hash2{state} = $r->{state};
            #if ($r->{state} eq "Alive") {    
                my @jobs = OAR::IO::get_resource_job($base,$r->{resource_id});
                if ($#jobs >= 0){
                    $tmp_hash2{jobs} = \@jobs;
                }
            #}
            $tmp_hash2{network_address} = $r->{network_address};
            my $properties = OAR::IO::get_resource_info($base,$r->{resource_id});
            $tmp_hash2{properties} = $properties;
            $tmp_hash1{$r->{resource_id}} = \%tmp_hash2;
        }
        $data_to_display{$node} = \%tmp_hash1;
    }
    if (defined($DUMPER_mode)){
        print(Dumper(\%data_to_display));
    }elsif(defined($XML_mode)){
        if ($XML_enabled == 1){
            my $dump = new XML::Dumper;
            $dump->dtd;
            print($dump->pl2xml(\%data_to_display));
        }else{
            warn("XML module not available on the system. Ask your administrator to install it if needed.\n");
            $exitstatus = 5;
        }
    }elsif(defined($YAML_mode)){
        if ($YAML_enabled == 1){
            print(YAML::Dump(\%data_to_display));
        }else{
            warn("YAML module not available on the system. Ask your administrator to install it if needed.\n");
            $exitstatus = 6;
        }
    }elsif($Old){
        my $Node_file_db_field_distinct_values = get_conf("NODE_FILE_DB_FIELD_DISTINCT_VALUES");
        $Node_file_db_field_distinct_values = OAR::Tools::get_default_node_file_db_field_distinct_values() if (!defined($Node_file_db_field_distinct_values));
        foreach my $aa (sort(keys(%data_to_display))){
            my @res = keys(%{$data_to_display{$aa}});
            my %tmp_already_there;
            my %tmp_already_there2;
            my $nb = 0;
            my $nb_jobs = 0;
            my $state;
            my $properties;
            my $hostname;
            my $job_str = '';
            my $pass = 0;
            #my %weight_index;
            foreach my $bb (sort({$a <=> $b} keys(%{$data_to_display{$aa}}))){
                my $cpuset;
                my @jobs;
                foreach my $cc (sort(keys(%{$data_to_display{$aa}->{$bb}}))){
                    if ($cc eq "properties"){
                        my $str = "properties = ";
                        foreach my $prop (sort(keys(%{$data_to_display{$aa}->{$bb}->{$cc}}))){
                            if ($prop eq "cpuset") { $cpuset=$data_to_display{$aa}->{$bb}->{$cc}->{$prop};}
                            if ($prop eq $Node_file_db_field_distinct_values) {
                                if (!defined($tmp_already_there{$data_to_display{$aa}->{$bb}->{$cc}->{$prop}})){
                                    $nb++;
                                    $tmp_already_there{$data_to_display{$aa}->{$bb}->{$cc}->{$prop}} = 1;
                                }
                            }
                            $pass = 1 if (($prop eq "type") and ($data_to_display{$aa}->{$bb}->{$cc}->{$prop} eq "frontal"));
                            if (defined($data_to_display{$aa}->{$bb}->{$cc}->{$prop})){
                                $str .= "$prop=$data_to_display{$aa}->{$bb}->{$cc}->{$prop},";
                            }else{
                                $str .= "$prop=,";
                            }
                        }
                        chop($str);
                        $properties = $str;
                    }elsif ($cc eq "jobs"){
                        #$nb_jobs += $#{$data_to_display{$aa}->{$bb}->{$cc}} + 1;
                        my $c=0;
                        foreach my $j (@{$data_to_display{$aa}->{$bb}->{$cc}}){
                            #if (!defined($weight_index{$aa})){
                            #    $weight_index{$aa} = 0;
                            #}else{
                            #    $weight_index{$aa}++;
                            #}
                            #$job_str .= "$weight_index{$aa}/$j.oarserver,";
                            if (!defined($tmp_already_there2{$data_to_display{$aa}->{$bb}->{properties}->{$Node_file_db_field_distinct_values}})){
                                $tmp_already_there2{$data_to_display{$aa}->{$bb}->{properties}->{$Node_file_db_field_distinct_values}} = 1;
                                $jobs[$c]="$j.oarserver";
                                $c++; 
                                $nb_jobs++;
                            }
                        }
                    }elsif ($cc eq "network_address"){
                        $hostname = $data_to_display{$aa}->{$res[0]}->{$cc};
                    }elsif ($cc eq "state"){
                        if ($smp) {
                            $state = $data_to_display{$aa}->{$bb}->{$cc};
                        }else{
                            $state = $data_to_display{$aa}->{$res[0]}->{$cc};
                        }
                    }
                }
                # Construct the jobs string
                my $c=0;
                foreach my $j (@jobs) {
                    if ($cpuset ne "" && !$smp) {
                        $job_str .= "$cpuset/$j,";
                    }else{
                        $job_str .= "$c/$j,";
                    }
                    $c++;
                }
                
                if ($smp) {
                    next if ($hostname eq "");
                    next if ($pass == 1);
                    printf("%s[%03d]\n",$hostname,$bb);
                    print("     pcpus = 1\n");
                    chop($job_str) if (defined($job_str));
                    print("     jobs = $job_str\n");
                    print("     weight = $nb_jobs\n");
                    print("     hostname = $hostname\n");
                    $state = "free" if ($state eq "Alive");
                    $state = "job" if ($job_str ne '');
                    print("     state = $state\n");
                    print("     $properties\n");
                    print("\n");
                    $job_str = "";
                }
            }
            if (!$smp) { 
                next if ($hostname eq "");
                next if ($pass == 1);
                print("$aa\n");
                print("     pcpus = $nb\n");
                chop($job_str) if (defined($job_str));
                print("     jobs = $job_str\n");
                print("     weight = $nb_jobs\n");
                print("     hostname = $hostname\n");
                $state = "free" if ($state eq "Alive");
                $state = "job" if ($nb == $nb_jobs);
                print("     state = $state\n");
                print("     $properties\n");
                print("\n");
            }
        }
    }else{
        foreach my $aa (sort(keys(%data_to_display))){
            print("$aa\n");
            foreach my $bb (sort({$a <=> $b} keys(%{$data_to_display{$aa}}))){
                print("    $bb\n");
                foreach my $cc (sort(keys(%{$data_to_display{$aa}->{$bb}}))){
                    if ($cc eq "properties"){
                        my $str = "properties : ";
                        foreach my $prop (sort(keys(%{$data_to_display{$aa}->{$bb}->{$cc}}))){
                            if (OAR::Tools::check_resource_system_property($prop) == 0){
                                if (defined($data_to_display{$aa}->{$bb}->{$cc}->{$prop})){
                                    $str .= "$prop=$data_to_display{$aa}->{$bb}->{$cc}->{$prop},";
                                }else{
                                    $str .= "$prop=,";
                                }
                            }
                        }
                        chop($str);
                        print("        $str\n");
                    }elsif ($cc eq "jobs"){
                        print("        jobs : @{$data_to_display{$aa}->{$bb}->{$cc}}\n");
                    }else{
                        print("        $cc : $data_to_display{$aa}->{$bb}->{$cc}\n");
                    }
                }
                print("\n");
            }
        }
    }
}
OAR::IO::disconnect($base);

exit($exitstatus);
