#!/usr/bin/perl
# $Id: oarnodesetting,v 1.24 2005/04/04 13:11:55 capitn Exp $
# change node state dynamically

use strict;
use DBI();
use Data::Dumper;
use oar_iolib;
use oar_conflib qw(init_conf dump_conf get_conf is_conf);
use Sys::Hostname;
use Getopt::Long;
use IO::Socket::INET;

$| = 1;

my $hostname ;
my $base;
my $maxWeight;
my $Weight;
my $state ;
my $node_info;
my $expiryDate;
my $desktopComputing;
my $nowaitMode;
my @properties;

# Get OAR configuration
init_conf("oar.conf");
my $remote_host = get_conf("SERVER_HOSTNAME");
my $remote_port = get_conf("SERVER_PORT");

# print explainations for the user and exit
sub usage {
    print <<EOS;
Usage: $0 [OPTION]...
This tool allows to change the state of a node in OAR
Options are:
 -s, --state=STATE               set the new state of the node
 -h, --hostname=HOSTNAME         set the node hostname
 -w, --max-weight=WEIGHT         set the max weight of the node
 -e, --expiry-date=DATE          set the node expiry date
     --desktop-computing         declare the node as a desktop-computing node
 -p, --property="PROPERTY=VALUE" set the property of the node to the given value
 -n, --nowait                    do not wait job end when the node switches to Absent or Dead
N.B.:
 - Only the --state or -s is mandatory.
 - Allowed states are: Alive, Absent or Dead
 - If not specified, the hostname will be retrieved via the hostname command
 - If hostname does not exist yet in OAR database, a new node is added
 - Default max-weight is 1
 - Max-weight is only used when the node is created
 - Default expiry date is 0000-00-00 00:00:00, which means the node never expires
EOS
    exit 1;
}

# notify Almighty
# arg1 --> type of notification
sub notifyAlmighty($){
    my $sig = shift;

    my $socket = IO::Socket::INET->new( PeerAddr => $remote_host,
                                        PeerPort => $remote_port,
                                        Proto => "tcp",
                                        Type  => SOCK_STREAM)
    or die("Couldn't connect executor $remote_host:$remote_port\n");
    if (defined($socket)){
        print $socket "$sig\n";
        #my $answer=<$socket>;
        #return "Almigthy answers : $answer\n";
    }
}

sub set_properties($$$){
    my $base = shift;
    my $hostname = shift;
    my $arrayProp = shift;

    foreach my $p (@{$arrayProp}){
        if ($p =~ m/(.+)\s*=\s*(.+)/m){
            print("Update property $1 with value $2 ...");
            my $ret = iolib::set_node_property($base,$hostname,$1,$2);
            if ($ret == 0){
                print("DONE\n");
            }else{
                print("ERROR (wrong property or wrong value)\n");
            }
        }else{
            print("/!\\ Bad property syntax : -p property=value\n");
        }
    }
}

# Options on arg command line
Getopt::Long::Configure ("gnu_getopt");
GetOptions ("state|s=s" => \$state,
            "hostname|h=s"   => \$hostname,
            "max-weight|w=s" => \$maxWeight,
	    "expiry-date|e=s" => \$expiryDate,
            "desktop-computing" => \$desktopComputing,
            "nowait|n" => \$nowaitMode,
            "property|p=s" => \@properties
           );

(defined($state) && (($state eq 'Alive') || ($state eq 'Absent') || ($state eq 'Dead'))) or usage();
defined $hostname or $hostname = hostname();
defined $maxWeight or $maxWeight = 1;
print("$hostname -- > $state\n");

my $base = iolib::connect() or die "can not connect to the data base\n";

$node_info = iolib::get_node_info($base,$hostname);

if ($node_info eq undef){
    print("new node\n");
    $base->do("LOCK TABLE nodes WRITE, nodeProperties WRITE, nodeState_log WRITE");
    print("$hostname added in the database\n");
    iolib::add_node($base, $hostname, $state, $maxWeight, $desktopComputing);
    #iolib::set_node_nextState($base,$hostname,$state);
    defined $expiryDate and iolib::set_node_expiryDate($base,$hostname,"\"$expiryDate\"");
    defined @properties and set_properties($base,$hostname,\@properties);
    $base->do("UNLOCK TABLES");
    notifyAlmighty("ChState");
}else{
    print("update node $hostname to $state state\n");
    $base->do("LOCK TABLE nodes WRITE, nodeProperties WRITE");
    iolib::set_node_nextState($base,$hostname,$state);
    defined $expiryDate and iolib::set_node_expiryDate($base,$hostname,"\"$expiryDate\"");
    defined @properties and set_properties($base,$hostname,\@properties);
    $base->do("UNLOCK TABLES");
    notifyAlmighty("ChState");
    if (($state eq 'Dead') || ($state eq 'Absent')){
        if ($nowaitMode){
            print("Done\n");
        }else{
            print("Check jobs to delete on $hostname :\n");
            my @jobs = iolib::get_host_job_distinct($base,$hostname);
            my $jobInfo;
            foreach my $i (sort(@jobs)){
                $jobInfo = {'state' => 'Running'};
                # active waiting : it is not very nice but it works!!
                print("\t$i ");
                my $timeCount = 0;
                while ((($jobInfo->{'state'} eq "Running") || ($jobInfo->{'state'} eq "toLaunch") || ($jobInfo->{'state'} eq "Launching")) && ($timeCount < 20)){
                    $jobInfo = iolib::get_job($base,$i);
                    sleep(1);
                    print(".");
                    $timeCount++;
                }
                print(" Deleted\n");
            }
            print("Check done\n");
        }
    }elsif ($state eq 'Alive'){
        print("Done\n");
    }else{
        die("Internal error, BAD STATE $state\n");
    }
}

iolib::disconnect($base);

