[Oar-commits] OAR branch 2.5 updated. 2.5.3+rc4-8-ga7d3d8a

Nicolas Capit capitn at ff-scm-v4-prod.irisa.fr
Wed Apr 3 21:15:35 CEST 2013


The branch, 2.5 has been updated
       via  a7d3d8aea4a1e9280fcf69ed5e2ef1113d6546b0 (commit)
      from  a495ec0661207bf6ec565d7f0d0bef44d9a5ec7d (commit)


- Log -----------------------------------------------------------------
commit a7d3d8a
Author: capitn <nicolas.capit at imag.fr>
Date:   Tue Apr 2 18:53:50 2013 +0200

    [scheduler] add quota feature
    
    Add quota which limits the number of used resources at a time depending of
    the job attributes: queue, project, types, user
    (available with the scheduler
    "oar_sched_gantt_with_timesharing_and_fairsharing_and_quotas")
---
 CHANGELOG                                          |    4 +
 Makefiles/server.mk                                |    1 +
 docs/documentation/doc_mechanisms.rst              |   21 +
 sources/core/common-libs/lib/OAR/IO.pm             |    3 +-
 ...ntt_with_timesharing_and_fairsharing_and_quotas |  648 ++++++++++++++
 .../core/modules/scheduler/scheduler_quotas.conf   |   60 ++
 .../OAR/Schedulers/GanttHoleStorage_with_quotas.pm |  906 ++++++++++++++++++++
 .../core/server/lib/OAR/Schedulers/QuotaStorage.pm |  244 ++++++
 8 files changed, 1886 insertions(+), 1 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 7e34e0a..8db0008 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -26,6 +26,10 @@ version 2.5.3:
   - Fix stdout/stderr bug: check the allowed characters in the path given by
     the users
   - Fix: the user shell (bash) didn't source /etc/bash.bashrc in batch jobs.
+  - Add quota which limits the number of used resources at a time depending of
+    the job attributes: queue, project, types, user
+    (available with the scheduler
+    "oar_sched_gantt_with_timesharing_and_fairsharing_and_quotas")
 
 version 2.5.2:
 --------------
diff --git a/Makefiles/server.mk b/Makefiles/server.mk
index 9a18a32..0d627e1e 100644
--- a/Makefiles/server.mk
+++ b/Makefiles/server.mk
@@ -19,6 +19,7 @@ OARDIR_DATAFILES = $(SRCDIR)/modules/runner/oarexec
 	  
 OARSCHEDULER_BINFILES = $(SRCDIR)/modules/scheduler/oar_sched_gantt_with_timesharing \
 		        $(SRCDIR)/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing \
+		        $(SRCDIR)/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing_and_quotas \
 		        $(SRCDIR)/modules/scheduler/oar_sched_gantt_with_timesharing_and_placeholder \
 		        $(SRCDIR)/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing_and_placeholder  
 OARCONFDIR_BINFILES = $(SRCDIR)/tools/oar_phoenix.pl
diff --git a/docs/documentation/doc_mechanisms.rst b/docs/documentation/doc_mechanisms.rst
index e77f302..642868f 100644
--- a/docs/documentation/doc_mechanisms.rst
+++ b/docs/documentation/doc_mechanisms.rst
@@ -330,6 +330,27 @@ project with `Kadeploy <http://ka-tools.imag.fr/>`_ tools.
 When you submit a job you have to use "-t deploy" option of oarsub_ to
 specify that this is a deploy job.
 
+Quotas
+------
+The administrator can limit the number of resources used by user, job types,
+project ans queue (or a combination of them).
+This feature acts like quotas. When one of the defined rules is reached then
+next jobs will not be scheduled at this time. The scheduler will find another
+slot when the quotas will be satisfied.
+
+This feature is available in queues which use the scheduler
+"oar_sched_gantt_with_timesharing_and_fairsharing_and_quotas".
+
+The quota rules are defined in "/etc/oar/scheduler_quotas.conf".
+
+By default no quota is applied.
+
+*Note1*: Quotas are applied globally, only the jobs of the type "container" are
+not taken in account (but the inner jobs are used to compute the quotas).
+
+*Note2*: Besteffort jobs are not taken in account except in the besteffort
+queue.
+
 Desktop computing
 -----------------
 
diff --git a/sources/core/common-libs/lib/OAR/IO.pm b/sources/core/common-libs/lib/OAR/IO.pm
index 139317b..52a5f59 100644
--- a/sources/core/common-libs/lib/OAR/IO.pm
+++ b/sources/core/common-libs/lib/OAR/IO.pm
@@ -5949,7 +5949,7 @@ sub create_a_queue($$$$){
 #return a hashtable : job_id --> [start_time,walltime,queue_name,\@resources,state]
 sub get_gantt_scheduled_jobs($){
     my $dbh = shift;
-    my $sth = $dbh->prepare("SELECT j.job_id, g2.start_time, m.moldable_walltime, g1.resource_id, j.queue_name, j.state, j.job_user, j.job_name,m.moldable_id,j.suspended
+    my $sth = $dbh->prepare("SELECT j.job_id, g2.start_time, m.moldable_walltime, g1.resource_id, j.queue_name, j.state, j.job_user, j.job_name,m.moldable_id,j.suspended,j.project
                              FROM gantt_jobs_resources g1, gantt_jobs_predictions g2, moldable_job_descriptions m, jobs j
                              WHERE
                                 m.moldable_index = \'CURRENT\'
@@ -5971,6 +5971,7 @@ sub get_gantt_scheduled_jobs($){
             $res{$ref[0]}->[6] = $ref[7];
             $res{$ref[0]}->[7] = $ref[8];
             $res{$ref[0]}->[8] = $ref[9];
+            $res{$ref[0]}->[9] = $ref[10];
             push(@order,$ref[0]);
         }
         push(@{$res{$ref[0]}->[3]}, $ref[3]);
diff --git a/sources/core/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing_and_quotas b/sources/core/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing_and_quotas
new file mode 100755
index 0000000..ef70d26
--- /dev/null
+++ b/sources/core/modules/scheduler/oar_sched_gantt_with_timesharing_and_fairsharing_and_quotas
@@ -0,0 +1,648 @@
+#!/usr/bin/perl
+# $Id$
+#-d:DProf
+
+use strict;
+use DBI();
+use OAR::IO;
+use Data::Dumper;
+use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category);
+use OAR::Conf qw(init_conf dump_conf get_conf is_conf get_conf_with_default_param);
+use OAR::Schedulers::GanttHoleStorage_with_quotas;
+use OAR::Schedulers::QuotaStorage;
+use Storable qw(dclone);
+use Time::HiRes qw(gettimeofday);
+
+# Log category
+set_current_log_category('scheduler');
+my $scheduler_name = "oar_sched_gantt_with_timesharing_and_fairsharing_with_quotas";
+my $Quota_file = "/etc/oar/scheduler_quotas.conf";
+
+init_conf($ENV{OARCONFFILE});
+
+###############################################################################
+# Fairsharing parameters #
+##########################
+# Avoid problems if there are too many waiting jobs
+my $Karma_max_number_of_jobs_treated_per_user = 30;
+if (is_conf("SCHEDULER_FAIRSHARING_MAX_JOB_PER_USER")) {
+    $Karma_max_number_of_jobs_treated_per_user = get_conf("SCHEDULER_FAIRSHARING_MAX_JOB_PER_USER");
+}
+# number of seconds to consider for the fairsharing
+my $Karma_window_size = 3600 * 30 * 24;
+if (is_conf("SCHEDULER_FAIRSHARING_WINDOW_SIZE")) {
+    $Karma_window_size = get_conf("SCHEDULER_FAIRSHARING_WINDOW_SIZE");
+}
+# specify the target percentages for project names (0 if not specified)
+my $Karma_project_targets = { first => 75, default => 25 };
+if (is_conf("SCHEDULER_FAIRSHARING_PROJECT_TARGETS")) {
+    $Karma_project_targets = eval(get_conf("SCHEDULER_FAIRSHARING_PROJECT_TARGETS").";");
+    if ($@) {
+        oar_error("[$scheduler_name] Syntax error in configuration file: SCHEDULER_FAIRSHARING_PROJECT_TARGETS\n");
+        exit (1);
+    }
+}
+
+# specify the target percentages for users (0 if not specified)
+my $Karma_user_targets = { oar => 100 };
+if (is_conf("SCHEDULER_FAIRSHARING_USER_TARGETS")) {
+    $Karma_user_targets = eval(get_conf("SCHEDULER_FAIRSHARING_USER_TARGETS").";");
+    if ($@) {
+        oar_error("[$scheduler_name] Syntax error in configuration file: SCHEDULER_FAIRSHARING_USER_TARGETS\n");
+        exit (1);
+    }
+}
+#print Dumper($Karma_user_targets);
+# weight given to each criteria
+my $Karma_coeff_project_consumption = 0;
+if (is_conf("SCHEDULER_FAIRSHARING_COEF_PROJECT")) {
+    $Karma_coeff_project_consumption = get_conf("SCHEDULER_FAIRSHARING_COEF_PROJECT");
+} 
+my $Karma_coeff_user_consumption = 2;
+if (is_conf("SCHEDULER_FAIRSHARING_COEF_USER")) {
+    $Karma_coeff_user_consumption = get_conf("SCHEDULER_FAIRSHARING_COEF_USER");
+}
+my $Karma_coeff_user_asked_consumption = 1;
+if (is_conf("SCHEDULER_FAIRSHARING_COEF_USER_ASK")) {
+    $Karma_coeff_user_asked_consumption = get_conf("SCHEDULER_FAIRSHARING_COEF_USER_ASK");
+}
+
+###############################################################################
+# Tokens #
+##########
+my $Token_scripts = {};
+if (is_conf("SCHEDULER_TOKEN_SCRIPTS")) {
+    $Token_scripts = eval(get_conf("SCHEDULER_TOKEN_SCRIPTS").";");
+    if ($@) {
+        oar_error("[$scheduler_name] Syntax error in configuration file: SCHEDULER_TOKEN_SCRIPTS\n");
+        exit (1);
+    }
+}
+###############################################################################
+# Quotas #
+##########
+my ($Gantt_quotas, $err) = OAR::Schedulers::QuotaStorage::read_conf_file("$Quota_file");
+if (defined($err)){
+    oar_error("[$scheduler_name] $err; QUOTA DISABLED\n");
+}
+###############################################################################
+my $initial_time = time();
+my $timeout = 10;
+my $Minimum_timeout_per_job = 0;
+if (is_conf("SCHEDULER_TIMEOUT")){
+    $timeout = get_conf("SCHEDULER_TIMEOUT");
+}
+
+# Constant duration time of a besteffort job
+my $besteffort_duration = 5*60;
+
+# $security_time_overhead is the security time (second) used to be sure there
+# are no problem with overlaping jobs
+my $security_time_overhead = 60;
+if (is_conf("SCHEDULER_JOB_SECURITY_TIME")){
+    $security_time_overhead = get_conf("SCHEDULER_JOB_SECURITY_TIME");
+}
+
+my $minimum_hole_time = 0;
+if (is_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME")){
+    $minimum_hole_time = get_conf("SCHEDULER_GANTT_HOLE_MINIMUM_TIME");
+}
+
+my $Order_part = get_conf("SCHEDULER_RESOURCE_ORDER");
+
+my @Sched_available_suspended_resource_type;
+my $sched_available_suspended_resource_type_tmp = get_conf("SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE");
+if (!defined($sched_available_suspended_resource_type_tmp)){
+    push(@Sched_available_suspended_resource_type, "default");
+}else{
+    @Sched_available_suspended_resource_type = split(" ",$sched_available_suspended_resource_type_tmp);
+}
+
+# Look at resources that we must add for each job
+my $Resources_to_always_add_type = get_conf("SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE");
+my @Resources_to_always_add = ();
+
+my $Max_nb_processes = get_conf_with_default_param("SCHEDULER_NB_PROCESSES",1);
+
+my $current_time ;
+
+my $queue;
+if (defined($ARGV[0]) && defined($ARGV[1]) && $ARGV[1] =~ m/\d+/m) {
+    $queue = $ARGV[0];
+    $current_time = $ARGV[1];
+}else{
+    oar_error("[$scheduler_name] No queue specified on command line\n");
+    exit(1);
+}
+
+# Init
+my $base = OAR::IO::connect();
+my $base_ro = OAR::IO::connect_ro();
+
+oar_debug("[$scheduler_name] Starting scheduler for queue $queue at time $current_time\n");
+
+# First check states of resources that we must add for every job
+if (defined($Resources_to_always_add_type)){
+    my $tmp_result_state_resources = OAR::IO::get_specific_resource_states($base,$Resources_to_always_add_type);
+    if ($#{$tmp_result_state_resources->{"Suspected"}} >= 0){
+        oar_warn("[$scheduler_name] Some of the resources matching the SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE configuration directive are Suspected. No job can be scheduled. Exiting\n");
+        exit(1);
+    }else{
+        if (defined($tmp_result_state_resources->{"Alive"})){
+            @Resources_to_always_add = @{$tmp_result_state_resources->{"Alive"}};
+            oar_debug("[$scheduler_name] The following Alive resources matching the SCHEDULER_RESOURCES_ALWAYS_ASSIGNED_TYPE configuration directive will be added to every job: @Resources_to_always_add\n");
+        }
+    }
+}
+
+
+my $timesharing_gantts;
+# Create the Gantt Diagrams
+#Init the gantt chart with all resources
+my $All_resource_list_vec = '';
+my $max_resources = 1;
+foreach my $r (OAR::IO::list_resources($base)){
+    vec($All_resource_list_vec,$r->{resource_id},1) = 1;
+    $max_resources = $r->{resource_id} if ($r->{resource_id} > $max_resources);
+}
+
+my %Container_gantt_hash;
+$Container_gantt_hash{0} = OAR::Schedulers::GanttHoleStorage_with_quotas::new($max_resources, $minimum_hole_time);
+OAR::Schedulers::GanttHoleStorage_with_quotas::add_new_resources($Container_gantt_hash{0}, $All_resource_list_vec);
+my $quota_accounting_data = OAR::Schedulers::QuotaStorage::new();
+
+sub parse_timesharing($$$){
+    my $str = shift;
+    my $job_user = shift;
+    my $job_name = shift;
+            
+    my $user = "*";
+    my $name = "*";
+    foreach my $s (split(',', $str)){
+        if ($s =~ m/^\s*([\w\*]+)\s*$/m){
+            if ($1 eq "user"){
+                $user = $job_user;
+            }elsif (($1 eq "name") and ($job_name ne "")){
+                $name = $job_name;
+            }
+        }
+    }
+
+    return($user,$name);
+}
+
+# Token feature (see oar.conf)
+# Keep the track of the usage for each token
+my %Token_values;
+
+oar_debug("[$scheduler_name] Begin phase 1 (running jobs)\n");
+# Take care of currently scheduled jobs (gantt in the database)
+my ($order,%already_scheduled_jobs) = OAR::IO::get_gantt_scheduled_jobs($base);
+foreach my $i (@{$order}){
+    my $types = OAR::IO::get_current_job_types($base,$i);
+    my @type_list = sort(keys(%{$types}));
+    # Do not take care of besteffort jobs
+    if ((! defined($types->{besteffort})) or ($queue eq "besteffort")){
+        my @resource_list = @{$already_scheduled_jobs{$i}->[3]};
+        my $job_duration = $already_scheduled_jobs{$i}->[1];
+        if ($already_scheduled_jobs{$i}->[4] eq "Suspended"){
+            # Remove resources of the type specified in SCHEDULER_AVAILABLE_SUSPENDED_RESOURCE_TYPE
+            @resource_list = OAR::IO::get_job_current_resources($base, $already_scheduled_jobs{$i}->[7],\@Sched_available_suspended_resource_type);
+            next if ($#resource_list < 0);
+        }
+        if ($already_scheduled_jobs{$i}->[8] eq "YES"){
+            # This job was suspended so we must recalculate the walltime
+            $job_duration += OAR::IO::get_job_suspended_sum_duration($base,$i,$current_time);
+        }
+
+        my $vec = '';
+        foreach my $r (@resource_list){
+            vec($vec,$r,1) = 1;
+        }
+        my $gantt_to_use = $Container_gantt_hash{0};
+        if (defined($types->{container})){
+            oar_debug("[$scheduler_name] [$i] container job: create a new gantt for container $i\n");
+            $Container_gantt_hash{$i} = OAR::Schedulers::GanttHoleStorage_with_quotas::new_with_1_hole($max_resources, $minimum_hole_time, $already_scheduled_jobs{$i}->[0], $job_duration + $security_time_overhead, $vec, $All_resource_list_vec);
+            $gantt_to_use = $Container_gantt_hash{$i};
+        }else{
+            OAR::Schedulers::QuotaStorage::update_accounting_counters(
+                                                $quota_accounting_data,
+                                                unpack("%32b*",$vec),
+                                                $already_scheduled_jobs{$i}->[0],
+                                                $job_duration + $security_time_overhead,
+                                                $already_scheduled_jobs{$i}->[2],
+                                                $already_scheduled_jobs{$i}->[9],
+                                                \@type_list,
+                                                $already_scheduled_jobs{$i}->[5]
+                                                                     );
+        }
+        #Handle a job within a container
+        my $container_num = 0;
+        if (defined($types->{inner}) and ($types->{inner} =~ /^(\d+)$/)){
+            if (defined($Container_gantt_hash{$1})){
+                $gantt_to_use = $Container_gantt_hash{$1};
+                $container_num = $1;
+                oar_debug("[$scheduler_name] [$i] inner job: using container $container_num\n");
+            }else{
+                oar_debug("[$scheduler_name] [$i] inner job: container $1 does not exist, use container 0.\n");
+            }
+        }
+        my $user;
+        my $name;
+        if (defined($types->{timesharing})){
+            ($user, $name) = parse_timesharing($types->{timesharing}, $already_scheduled_jobs{$i}->[5], $already_scheduled_jobs{$i}->[6]);
+            if (!defined($timesharing_gantts->{$container_num}->{$user}->{$name})){
+                $timesharing_gantts->{$container_num}->{$user}->{$name} = dclone($gantt_to_use);
+                oar_debug("[$scheduler_name] [$i] timesharing job: cloned a new gantt for ($container_num, $user, $name)\n");
+            }
+        }
+
+        #Fill all other gantts
+        foreach my $g (keys(%Container_gantt_hash)){
+            if ($i != $g){
+                oar_debug("[$scheduler_name] [$i] add job occupation in gantt of container $g\n");
+                OAR::Schedulers::GanttHoleStorage_with_quotas::set_occupation(
+                                                    $Container_gantt_hash{$g},
+                                                    $already_scheduled_jobs{$i}->[0],
+                                                    $job_duration + $security_time_overhead,
+                                                    $vec
+                                                                             );
+            }
+            #print("\nOOOOOOOOOO $i - $g $current_time\n");
+            #OAR::Schedulers::GanttHoleStorage_with_quotas::pretty_print($Container_gantt_hash{$g});
+        }
+        foreach my $c (keys(%{$timesharing_gantts})){
+            foreach my $u (keys(%{$timesharing_gantts->{$c}})){
+                foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){
+                    if ((!defined($user)) or (!defined($name)) or (($u ne $user) or ($n ne $name))){
+                        oar_debug("[$scheduler_name] [$i] add job occupation in gantt of timesharing ($c, $u, $n)\n");
+                        OAR::Schedulers::GanttHoleStorage_with_quotas::set_occupation(
+                                                            $timesharing_gantts->{$c}->{$u}->{$n},
+                                                            $already_scheduled_jobs{$i}->[0],
+                                                            $job_duration + $security_time_overhead,
+                                                            $vec
+                                                                                     );
+                    } else {
+                        oar_debug("[$scheduler_name] [$i] skip timesharing job occupation in gantt of timesharing ($c, $u, $n)\n");
+                    }
+                }
+            }
+        }
+    }
+}
+
+oar_debug("[$scheduler_name] End phase 1 (running jobs)\n");
+
+# End of the initialisation
+# Begining of the real scheduling
+
+# Get list of Alive resources
+my $alive_resources_vector = '';
+foreach my $r (OAR::IO::get_resources_in_state($base,"Alive")){
+    vec($alive_resources_vector, $r->{resource_id}, 1) = 1;
+}
+
+# ENERGY SAVING: add fake occupations/holes from energy saving configuration 
+# CM part and Hulot part (wake up nodes in energy saving mode)
+if (is_conf("SCHEDULER_NODE_MANAGER_WAKE_UP_CMD") or (get_conf("ENERGY_SAVING_INTERNAL") eq "yes" and is_conf("ENERGY_SAVING_NODE_MANAGER_WAKE_UP_CMD"))){
+    oar_debug("[$scheduler_name] Begin EnergySaving phase\n");
+    # Check the resources that can be waked_up or shut down
+    my $upto_availability = OAR::IO::get_energy_saving_resources_availability($base, $current_time);
+    foreach my $t (keys(%{$upto_availability})){
+        my $vec = '';
+        foreach my $r (@{$upto_availability->{$t}}){
+            vec($alive_resources_vector, $r, 1) = 1;
+            vec($vec,$r,1) = 1;
+        }
+        #Fill all the gantts
+        foreach my $g (keys(%Container_gantt_hash)){
+            oar_debug("[$scheduler_name] Add EnergySaving occupation in gantt for container $g with available_upto = $t\n");
+            OAR::Schedulers::GanttHoleStorage_with_quotas::set_occupation(
+                                                $Container_gantt_hash{$g},
+                                                $t,
+                                                OAR::Schedulers::GanttHoleStorage_with_quotas::get_infinity_value(),
+                                                $vec
+                                              );
+        }
+
+        foreach my $c (keys(%{$timesharing_gantts})){
+            foreach my $u (keys(%{$timesharing_gantts->{$c}})){
+                foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){
+                    oar_debug("[$scheduler_name] Add EnergySaving occupation in gantt for timesharing ($c, $u, $n) with available_upto = $t\n");
+                    OAR::Schedulers::GanttHoleStorage_with_quotas::set_occupation(
+                                                    $timesharing_gantts->{$c}->{$u}->{$n},
+                                                    $t,
+                                                    OAR::Schedulers::GanttHoleStorage_with_quotas::get_infinity_value(),
+                                                    $vec
+                                                );
+                }
+            }
+        }
+    }
+    oar_debug("[$scheduler_name] End EnergySaving phase\n");
+}
+# CM part
+ 
+my @Dead_resources;
+foreach my $r (OAR::IO::get_resources_in_state($base,"Dead")){
+    push(@Dead_resources, $r->{resource_id});
+}
+
+oar_debug("[$scheduler_name] Begin phase 2 (waiting jobs)\n");
+my @jobs = OAR::IO::get_fairsharing_jobs_to_schedule($base,$queue,$Karma_max_number_of_jobs_treated_per_user);
+###############################################################################
+# Sort jobs depending on their previous usage
+# Karma sort algorithm
+my $Karma_sum_time = OAR::IO::get_sum_accounting_window($base,$queue,$current_time - $Karma_window_size,$current_time);
+$Karma_sum_time->{ASKED} = 1 if (!defined($Karma_sum_time->{ASKED}));
+$Karma_sum_time->{USED} = 1 if (!defined($Karma_sum_time->{USED}));
+
+my $Karma_projects = OAR::IO::get_sum_accounting_for_param($base,$queue,"accounting_project",$current_time - $Karma_window_size,$current_time);
+my $Karma_users = OAR::IO::get_sum_accounting_for_param($base,$queue,"accounting_user",$current_time - $Karma_window_size,$current_time);
+
+sub karma($){
+    my $j = shift;
+
+    my $note = 0;
+    $note = $Karma_coeff_project_consumption * (($Karma_projects->{$j->{project}}->{USED} / $Karma_sum_time->{USED}) - ($Karma_project_targets->{$j->{project}} / 100));
+    $note += $Karma_coeff_user_consumption * (($Karma_users->{$j->{job_user}}->{USED} / $Karma_sum_time->{USED}) - ($Karma_user_targets->{$j->{job_user}} / 100));
+    $note += $Karma_coeff_user_asked_consumption * (($Karma_users->{$j->{job_user}}->{ASKED} / $Karma_sum_time->{ASKED}) - ($Karma_user_targets->{$j->{job_user}} / 100));
+
+    return($note);
+}
+
+###############################################################################
+
+ at jobs = sort({karma($a) <=> karma($b)} @jobs);
+my $job_index = 0;
+while (($job_index <= $#jobs) and ((time() - $initial_time) < $timeout)){
+    my $j = $jobs[$job_index];
+    $job_index ++;
+    oar_debug("[$scheduler_name] [$j->{job_id}] Start scheduling (Karma note = ".karma($j).")\n");
+    
+    my $scheduler_init_date = $current_time;
+    # Search for dependencies
+    my $skip_job = 0;
+
+    # Check the job dependencies 
+    foreach my $d (OAR::IO::get_current_job_dependencies($base,$j->{job_id})){
+        next if ($skip_job == 1);
+        my $dep_job = OAR::IO::get_job($base,$d);
+        if (($dep_job->{state} ne "Terminated")){
+            my @date_tmp = OAR::IO::get_gantt_job_start_time($base,$d);
+            if (defined($date_tmp[0])){
+                my $mold_dep = OAR::IO::get_current_moldable_job($base,$date_tmp[1]);
+                my $sched_tmp = $date_tmp[0] + $mold_dep->{moldable_walltime};
+                if ($scheduler_init_date < $sched_tmp){
+                    $scheduler_init_date = $sched_tmp + (2 * $security_time_overhead);
+                }
+            }else{
+                my $message = "Dependency problem with the job $d (job in ERROR state)";
+                OAR::IO::set_job_message($base,$j->{job_id},$message);
+#                OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message);
+                oar_debug("[$scheduler_name] [$j->{job_id}] $message\n");
+                $skip_job = 1;
+                next;
+            }
+        }elsif (($dep_job->{job_type} eq "PASSIVE") and ($dep_job->{exit_code} != 0)){
+            my $message = "Dependency problem with the job $d (exit code != 0)";
+            OAR::IO::set_job_message($base,$j->{job_id},$message);
+#            OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message);
+            oar_debug("[$scheduler_name] [$j->{job_id}] $message\n");
+            $skip_job = 1;
+            next;
+        }
+    }
+    next if ($skip_job == 1);
+
+    my $gantt_to_use = $Container_gantt_hash{0};
+    my $container_num = 0;
+    my $types = OAR::IO::get_current_job_types($base,$j->{job_id});
+    my @type_list = sort(keys(%{$types}));
+    if (defined($types->{inner}) and ($types->{inner} =~ /^(\d+)$/)){
+        if (defined($Container_gantt_hash{$1})){
+            $gantt_to_use = $Container_gantt_hash{$1};
+            $container_num = $1;
+            oar_debug("[$scheduler_name] [$j->{job_id}] container job: using container $container_num\n");
+        }else{
+            oar_debug("[$scheduler_name] [$j->{job_id}] container job: container $1 does not exist.\n");
+            OAR::IO::set_job_message($base,$j->{job_id},"Container $1 does not exist");
+#            OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Container $1 does not exist");
+            next;
+        }
+    }
+    if (defined($types->{timesharing})){
+        my ($user, $name) = parse_timesharing($types->{timesharing}, $j->{job_user}, $j->{job_name});
+        if (!defined($timesharing_gantts->{$container_num}->{$user}->{$name})){
+            $timesharing_gantts->{$container_num}->{$user}->{$name} = dclone($gantt_to_use);
+            oar_debug("[$scheduler_name] [$j->{job_id}] timesharing job: cloned a new gantt for ($container_num, $user, $name)\n");
+        }
+        $gantt_to_use = $timesharing_gantts->{$container_num}->{$user}->{$name};
+        oar_debug("[$scheduler_name] [$j->{job_id}] timesharing job: use gantt for ($container_num, $user, $name)\n");
+    }
+    if (defined($types->{token})){
+        foreach my $t (keys(%{$types->{token}})){
+            next if ($skip_job == 1);
+            oar_debug("[$scheduler_name] [$j->{job_id}] Check token: $t ($types->{token}->{$t}).\n");
+            # Check if we must execute the corresponding script
+            if ((!defined($Token_values{$t})) and (defined($Token_scripts->{$t}))){
+                oar_debug("[$scheduler_name] [$j->{job_id}] Execute $Token_scripts->{$t}\n");
+                if (open(TOKSCRIPT, "$Token_scripts->{$t} |")){
+                    my $num = <TOKSCRIPT>;
+                    chop($num);
+                    if ($num =~ /^\d+$/){
+                        $Token_values{$t} = $num;
+                        oar_debug("[$scheduler_name] [$j->{job_id}]  $Token_scripts->{$t} returns $num\n");
+                    }else{
+                        oar_warn("[$scheduler_name] [$j->{job_id}] The token script $Token_scripts->{$t} does not return a number ($num).\n");
+                    }
+                    close(TOKSCRIPT);
+                }
+            }
+            if (defined($Token_values{$t})){
+                if ($Token_values{$t} < $types->{token}->{$t}){
+                    oar_debug("[$scheduler_name] [$j->{job_id}] No enough Tokens: $t ($Token_values{$t} < $types->{token}->{$t}).\n");
+                    OAR::IO::set_job_message($base,$j->{job_id},"No enough Token: $t ($Token_values{$t} < $types->{token}->{$t})");
+#                    OAR::IO::set_job_scheduler_info($base,$j->{job_id},"No enough Token: $t ($Token_values{$t} < $types->{token}->{$t})");
+                    $skip_job = 1;
+                }
+            }else{
+                oar_debug("[$scheduler_name] [$j->{job_id}] Token value cannot be retrieved ($t).\n");
+                OAR::IO::set_job_message($base,$j->{job_id},"Token value cannot be retrieved ($t)");
+#                OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Token value cannot be retrieved ($t)");
+                $skip_job = 1;
+            }
+        }
+        next if ($skip_job == 1);
+    }
+    #OAR::Schedulers::GanttHoleStorage_with_quotas::pretty_print($gantt_to_use);
+    #oar_debug("[$scheduler_name] [$j->{job_id}] Gantt data structure:\n".OAR::Schedulers::GanttHoleStorage_with_quotas::pretty_print($gantt_to_use));
+
+    my $job_properties = "\'1\'";
+    if ((defined($j->{properties})) and ($j->{properties} ne "")){
+        $job_properties = $j->{properties};
+    }
+    
+    # Choose the moldable job to schedule
+    my @moldable_results;
+    my $job_descriptions = OAR::IO::get_resources_data_structure_current_job($base,$j->{job_id});
+    foreach my $moldable (@{$job_descriptions}){
+    #my $moldable = $job_descriptions->[0];
+        my $duration;
+        if (defined($types->{besteffort})){
+            $duration = $besteffort_duration;
+        }else{
+            $duration = $moldable->[1] + $security_time_overhead;
+        }
+
+        my @tree_list;
+        foreach my $m (@{$moldable->[0]}){
+            my $tmp_properties = "\'1\'";
+            if ((defined($m->{property})) and ($m->{property} ne "")){
+                $tmp_properties = $m->{property};
+            }
+            my $tmp_tree = OAR::IO::get_possible_wanted_resources($base_ro,$alive_resources_vector,undef,\@Dead_resources,"$job_properties AND $tmp_properties", $m->{resources}, $Order_part);
+            push(@tree_list, $tmp_tree);
+            #my @leafs = OAR::Schedulers::ResourceTree::get_tree_leafs($tmp_tree);
+            #foreach my $l (@leafs){
+            #    vec($resource_id_used_list_vector, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1;
+            #}
+        }
+        my $gantt_timeout =  ($timeout - (time() - $initial_time)) / 4;
+        $gantt_timeout = $Minimum_timeout_per_job if ($gantt_timeout <= ($timeout / 8));
+        oar_debug("[$scheduler_name] [$j->{job_id}] find_first_hole with a timeout of $gantt_timeout\n");
+        my @hole;
+        if ($Max_nb_processes <= 1){
+            @hole = OAR::Schedulers::GanttHoleStorage_with_quotas::find_first_hole($gantt_to_use, $scheduler_init_date, $duration, \@tree_list,$gantt_timeout,$j->{queue_name},$j->{project},\@type_list,$j->{job_user},$Gantt_quotas,$quota_accounting_data);
+        }else{
+            oar_debug("[$scheduler_name] [$j->{job_id}] using Gantt PARALLEL algorithm\n");
+            @hole = OAR::Schedulers::GanttHoleStorage_with_quotas::find_first_hole_parallel($gantt_to_use, $scheduler_init_date, $duration, \@tree_list,$gantt_timeout,$Max_nb_processes, $j->{queue_name},$j->{project},\@type_list,$j->{job_user},$Gantt_quotas,$quota_accounting_data);
+        }
+#        print("[GANTT] 10 ".gettimeofday."\n");
+        my @res_trees;
+        my @resources;
+        foreach my $t (@{$hole[2]}){
+#        print("[GANTT] 11 ".gettimeofday."\n");
+            #my $minimal_tree = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($t);
+#        print("[GANTT] 12 ".gettimeofday."\n");
+            push(@res_trees, $t);
+            foreach my $r (OAR::Schedulers::ResourceTree::get_tree_leafs($t)){
+                push(@resources, OAR::Schedulers::ResourceTree::get_current_resource_value($r));
+            }
+#        print("[GANTT] 13 ".gettimeofday."\n");
+        }
+        push(@moldable_results, {
+                                    resources => \@resources,
+                                    start_date => $hole[0],
+                                    duration => $duration,
+                                    moldable_id => $moldable->[2],
+                                    walltime => $moldable->[1],
+                                    comment => $hole[1]
+                                });
+    }
+
+    # Choose moldable job which will finish the first
+    my $index_to_choose = -1;
+    my $best_stop_time;
+#        print("[GANTT] 14 ".gettimeofday."\n");
+    for (my $i=0; $i <= $#moldable_results; $i++){
+        #my @tmp_array = @{$moldable_results[$i]->{resources}};
+        if ($#{$moldable_results[$i]->{resources}} >= 0){
+            my $tmp_stop_date = $moldable_results[$i]->{start_date} + $moldable_results[$i]->{duration};
+            if ((!defined($best_stop_time)) or ($best_stop_time > $tmp_stop_date)){
+                $best_stop_time = $tmp_stop_date;
+                $index_to_choose = $i;
+            }
+        }
+    }
+    if ($index_to_choose >= 0){
+        # We can schedule the job
+#        print("[GANTT] 15 ".gettimeofday."\n");
+        my $vec = '';
+        foreach my $r (@{$moldable_results[$index_to_choose]->{resources}}){
+            vec($vec, $r, 1) = 1;
+        }
+        #Fill all other gantts
+        foreach my $g (keys(%Container_gantt_hash)){
+            if ($j->{job_id} != $g){
+                oar_debug("[$scheduler_name] [$j->{job_id}] add job occupation in gantt of container $g\n");
+                OAR::Schedulers::GanttHoleStorage_with_quotas::set_occupation(
+                                                    $Container_gantt_hash{$g},
+                                                    $moldable_results[$index_to_choose]->{start_date},
+                                                    $moldable_results[$index_to_choose]->{duration},
+                                                    $vec
+                                                  );
+            }
+        }
+        if (defined($types->{container})){
+            $Container_gantt_hash{$j->{job_id}} = OAR::Schedulers::GanttHoleStorage_with_quotas::new_with_1_hole($max_resources, $minimum_hole_time, $moldable_results[$index_to_choose]->{start_date}, $moldable_results[$index_to_choose]->{duration}, $vec, $All_resource_list_vec);
+        }else{
+            OAR::Schedulers::QuotaStorage::update_accounting_counters(
+                                               $quota_accounting_data,
+                                               unpack("%32b*",$vec),
+                                               $moldable_results[$index_to_choose]->{start_date},
+                                               $moldable_results[$index_to_choose]->{duration},
+                                               $j->{queue_name},
+                                               $j->{project},
+                                               \@type_list,
+                                               $j->{job_user}
+                                             );
+            #oar_debug("[$scheduler_name] [$j->{job_id}] ".OAR::Schedulers::QuotaStorage::pretty_print($quota_accounting_data));
+        }
+        foreach my $c (keys(%{$timesharing_gantts})){
+            foreach my $u (keys(%{$timesharing_gantts->{$c}})){
+#        print("[GANTT] 17 ".gettimeofday."\n");
+                foreach my $n (keys(%{$timesharing_gantts->{$c}->{$u}})){
+                    if (($gantt_to_use != $timesharing_gantts->{$c}->{$u}->{$n})){
+                        oar_debug("[$scheduler_name] [$j->{job_id}] add job occupation in gantt of timesharing ($c, $u, $n)\n");
+                        OAR::Schedulers::GanttHoleStorage_with_quotas::set_occupation(
+                                                            $timesharing_gantts->{$c}->{$u}->{$n},
+                                                            $moldable_results[$index_to_choose]->{start_date},
+                                                            $moldable_results[$index_to_choose]->{duration},
+                                                            $vec
+                                                          );
+                    } else {
+                        oar_debug("[$scheduler_name] [$j->{job_id}] skip timesharing job occupation in gantt of timesharing ($c, $u, $n)\n");
+                    }
+                }
+            }
+        }
+
+
+        #update database
+        push(@{$moldable_results[$index_to_choose]->{resources}}, at Resources_to_always_add);
+        OAR::IO::add_gantt_scheduled_jobs($base,$moldable_results[$index_to_choose]->{moldable_id}, $moldable_results[$index_to_choose]->{start_date},$moldable_results[$index_to_choose]->{resources});
+
+        # Feed message job field
+        my $message = OAR::IO::format_job_message_text($j->{job_name},$#{$moldable_results[$index_to_choose]->{resources}}+1,$moldable_results[$index_to_choose]->{walltime},$j->{job_type},$j->{reservation},$j->{queue_name},$j->{project},\@type_list,"Karma=".sprintf("%.3f",karma($j)).",$moldable_results[$index_to_choose]->{comment}");
+        OAR::IO::set_job_message($base,$j->{job_id},$message);
+#        OAR::IO::set_job_scheduler_info($base,$j->{job_id},"Karma = ".sprintf("%.3f",karma($j)));
+
+        # Update Token values
+        if (defined($types->{token}) and ($moldable_results[$index_to_choose]->{start_date} <= $current_time)){
+            foreach my $t (keys(%{$types->{token}})){
+                $Token_values{$t} = $Token_values{$t} - $types->{token}->{$t};
+                oar_debug("[$scheduler_name] Update TOKEN $t to $Token_values{$t}\n");
+            }
+        }
+    }else{
+        my $message = "No enough matching resources ($moldable_results[0]->{comment})";
+        OAR::IO::set_job_message($base,$j->{job_id},$message);
+#        OAR::IO::set_job_scheduler_info($base,$j->{job_id},$message);
+        oar_debug("[$scheduler_name] [$j->{job_id}] $message\n");
+    }
+#        print("[GANTT] 18 ".gettimeofday."\n");
+    oar_debug("[$scheduler_name] [$j->{job_id}] end scheduling\n");
+}
+oar_debug("[$scheduler_name] End phase 2 (waiting jobs)\n");
+
+
+OAR::IO::disconnect($base);
+OAR::IO::disconnect($base_ro);
+
+if ($job_index <= $#jobs){
+    oar_debug("[$scheduler_name] Warning: some jobs were not scheduled because the scheduler's timeout was reached ($timeout s)\n");
+}
+
+oar_debug("[$scheduler_name] End of scheduler for queue $queue\n");
+
diff --git a/sources/core/modules/scheduler/scheduler_quotas.conf b/sources/core/modules/scheduler/scheduler_quotas.conf
new file mode 100644
index 0000000..69723ab
--- /dev/null
+++ b/sources/core/modules/scheduler/scheduler_quotas.conf
@@ -0,0 +1,60 @@
+#########################
+# scheduler_quotas.conf #
+#############################################################################
+# Only available in the scheduler:
+#     - oar_sched_gantt_with_timesharing_and_fairsharing_and_quotas
+#
+# Implements quotas on the amount of resources used at a time
+# depending on:
+#
+#  - job queue name ("-q" oarsub option)
+#  - job project name ("--project" oarsub option)
+#  - job types ("-t" oarsub options)
+#  - job user
+# 
+# The lowest corresponding quota for each job is used depending (it depends on
+# the consumptions of the other jobs)
+#
+# Syntax is like:
+#
+#   $Gantt_quotas->{queue}->{project}->{type}->{user} = integer ;
+#
+#   '*' means "everything" when used in place of queue, project,
+#       type and user
+#   '/' means "per" when used in place of queue, project and user
+#       (NOT usable for type)
+#
+# Examples:
+#
+#   - No more than 100 resources used by 'john' at a time:
+#
+#       $Gantt_quotas->{'*'}->{'*'}->{'*'}->{'john'} = 100 ;
+#
+#   - No more than 150 resources used by job of besteffort type at a time:
+#
+#       $Gantt_quotas->{'*'}->{'*'}->{'besteffort'}->{'*'} = 150 ;
+#
+#   - No more than 200 resources used by jobs in the project "proj1" at a
+#     time:
+#
+#       $Gantt_quotas->{'*'}->{'proj1'}->{'*'}->{'*'} = 200 ;
+#
+#   - No more than 20 resources used by 'john' in the project 'proj12' at a
+#     time:
+#
+#       $Gantt_quotas->{'*'}->{'proj12'}->{'*'}->{'john'} = 20 ;
+#
+#   - No more than 80 resources used by jobs in the project "proj1" per user
+#     at a time:
+#
+#       $Gantt_quotas->{'*'}->{'proj1'}->{'*'}->{'/'} = 80 ;
+#
+#   - No more than 50 resources used per user per project at a time:
+#
+#       $Gantt_quotas->{'*'}->{'/'}->{'*'}->{'/'} = 50 ;
+#
+#############################################################################
+
+#$Gantt_quotas->{'*'}->{'*'}->{'*'}->{'john'} = 80 ;
+#$Gantt_quotas->{'*'}->{'proj1'}->{'*'}->{'/'} = 25 ;
+
diff --git a/sources/core/server/lib/OAR/Schedulers/GanttHoleStorage_with_quotas.pm b/sources/core/server/lib/OAR/Schedulers/GanttHoleStorage_with_quotas.pm
new file mode 100644
index 0000000..0ca9e59
--- /dev/null
+++ b/sources/core/server/lib/OAR/Schedulers/GanttHoleStorage_with_quotas.pm
@@ -0,0 +1,906 @@
+# $Id$
+package OAR::Schedulers::GanttHoleStorage_with_quotas;
+require Exporter;
+use OAR::Schedulers::ResourceTree;
+use OAR::Schedulers::QuotaStorage;
+use OAR::Modules::Judas qw(oar_debug oar_warn oar_error set_current_log_category);
+use Data::Dumper;
+use POSIX ":sys_wait_h";
+use POSIX qw(strftime);
+use Storable qw(store_fd fd_retrieve dclone);
+use warnings;
+use strict;
+
+# Note : All dates are in seconds
+# Resources are integer so we store them in bit vectors
+# Warning : this gantt cannot manage overlaping time slots
+
+# 2^32 is infinity in 32 bits stored time
+my $Infinity = 4294967296;
+
+# Prototypes
+# gantt chart management
+sub new($$);
+sub new_with_1_hole($$$$$$);
+sub add_new_resources($$);
+sub set_occupation($$$$);
+sub get_free_resources($$$);
+sub find_first_hole($$$$$$$$$$$);
+sub pretty_print($);
+sub get_infinity_value();
+
+###############################################################################
+
+sub get_infinity_value(){
+    return($Infinity);
+}
+
+sub pretty_print($){
+    my $gantt = shift;
+   
+    my $str = "";
+    my @bits = split(//, unpack("b*", $gantt->[0]->[2]));
+    $str .= "@bits\n";
+    foreach my $g (@{$gantt}){
+        $str .= "BEGIN : $g->[0](".strftime("%F %T",localtime($g->[0])).")\n";
+        foreach my $h (@{$g->[1]}){
+            @bits = split(//, unpack("b*", $h->[1]));
+            $str .= "  $h->[0](".strftime("%F %T",localtime($h->[0]))."): @bits\n";
+        }
+        $str .= "\n";
+    }
+    return($str);
+}
+
+# Creates an empty Gantt
+# arg : number of the max resource id
+sub new($$){
+    my $max_resource_number = shift;
+    my $minimum_hole_duration = shift;
+
+    $minimum_hole_duration = 0 if (!defined($minimum_hole_duration));
+
+    my $empty_vec = '';
+    vec($empty_vec, $max_resource_number, 1) = 0;
+
+    my $result =[                               # Gantt structure: a Gantt is defined as the list of the biggest holes
+                    [                           # (rectange shapes) where a job could be placed (holes obviously can overlap)
+                        0,                      # Each item of this subarray is a set of holes beginning a same time: t_start
+                        [                       # The set is stored as a sub-subarray of holes sorted by end time
+                            [                   # Holes are stored as arrays of 3 elements, with:
+                                $Infinity,      # - t_end: end time for the hole
+                                $empty_vec      # - vec: resource vector for the hole
+                            ]
+                        ],                      # The next 4 fields are only set in the first hole set (apply to the gantt):
+                        $empty_vec,             # - base resources vector for the gantt
+                        $empty_vec,             # - reference empty vec (filled with 0)
+                        $minimum_hole_duration, # - minimum duration time for a hole (see oar.conf)
+                        [$Infinity,$Infinity]   # - [t_start,t_end] of the last hole inpected in the previous find_first_hole calls, if a timeout was triggered.
+                    ]
+                ];
+    
+    return($result);
+}
+
+
+# Creates a Gantt with 1 hole
+# arg : number of the max resource id
+sub new_with_1_hole($$$$$$){
+    my $max_resource_number = shift;
+    my $minimum_hole_duration = shift;
+    my $date = shift;
+    my $duration = shift;
+    my $resources_vec = shift;
+    my $all_resources_vec = shift;
+
+    my $gantt = OAR::Schedulers::GanttHoleStorage_with_quotas::new($max_resource_number, $minimum_hole_duration);
+
+    # initiate the first hole with a fake date (ensure to keep it intact with all the configuration)
+    $gantt->[0]->[1]->[0]->[0] = 86400;
+   
+    # Init the whole resource list directly
+    $gantt->[0]->[2] = $all_resources_vec;
+    # Feed vector with enough 0
+    $resources_vec |= $gantt->[0]->[3];
+
+    # Create the only hole
+    $gantt->[1]->[0] = $date;
+    $gantt->[1]->[1] = [[($date + $duration), $resources_vec]];
+
+    return($gantt);
+}
+
+# Build a new gantt from the merger of two existing gantts
+# Algo: 
+# (1) First convert the gantts to stripes defined by the start and end times of all jobs
+#     The 2 striped gantts are then easy merged: for every jobs, gantt_stripe(t) |= stripe(job, t)
+# (2) Then convert the striped gantt back to the original gantt structure
+# arg : gantt ref1, gantt ref2
+sub merge_clone($$) {
+    my ($gantt1,$gantt2) = @_;
+    #pretty_print($gantt1);
+    #pretty_print($gantt2);
+
+    # Sanity check: are those 2 gantts compatible ?
+    if ($gantt1->[0]->[2] ne $gantt2->[0]->[2]) {
+    }
+
+    # Initialize the stipes by retrieving all jobs start and end times.
+    my $empty_vec = $gantt1->[0]->[3];
+    my $stripes = {};
+    foreach my $h (@$gantt1,@$gantt2) {
+        $stripes->{$h->[0]} =  $empty_vec;
+        foreach my $hh (@{$h->[1]}) {
+            $stripes->{$hh->[0]} = $empty_vec;
+        }
+    }
+    #foreach my $l (sort keys(%$stripes)) {
+    #   print(strftime("%F_%T",localtime($l)).": ".unpack("b*", $stripes->{$l})."\n");
+    #}
+
+    # Fill the strips with all jobs
+    foreach my $h (@$gantt1,@$gantt2) {
+        my $t0 = $h->[0];
+        foreach my $hh (@{$h->[1]}) {
+            my $t1 = $hh->[0];
+            foreach my $l (sort keys(%$stripes)) {
+                if ($t0 < $l and $l <= $t1) {
+                    #print("-> ".strftime("%F_%T",localtime($l))." in ]".strftime("%F_%T",localtime($t0)).", ".strftime("%F_%T",localtime($t1))."] = ".unpack("b*", $stripes->{$l})." | ".unpack("b*", $hh->[1])."\n");
+                    $stripes->{$l} |= ($hh->[1]);
+                    #foreach my $l (sort keys(%$stripes)) {
+                    #    print(strftime("%F_%T",localtime($l)).": ".unpack("b*", $stripes->{$l})."\n");
+                    #}
+                }
+            }
+        }
+    }
+    #print("emtpy_vec= ".unpack("b*", $empty_vec)."\n");
+    #my $t=0;
+    #foreach my $l (sort keys(%$stripes)) {
+    #    print("t=".$t++." l=".strftime("%F_%T",localtime($l)).": ".unpack("b*", $stripes->{$l})."\n");
+    #}
+    
+    # Convert the striped gantt back to the original structure
+    my $merged_gantt = [];
+    my @times=sort keys(%$stripes);
+    # Try and find new hole start time for every stripe
+    for(my $t=0;$t<$#times;$t++) {
+        my $holes = [];
+        #print("test possible hole starting at $t (".strftime("%F_%T",localtime($times[$t])).") ? : ".unpack("b*", (~ $stripes->{$times[$t]} & $stripes->{$times[$t+1]}))."\n");
+        # We have a new hole if for any resource r, vec(r,t-1)=1 and vec(r,t)=0
+        if ((~ $stripes->{$times[$t]} & $stripes->{$times[$t+1]}) ne $empty_vec) {
+            #print "YES: hole at starttime: t=$t l=".$times[$t]." (".strftime("%F_%T",localtime($times[$t])).")\n";
+            my $vec = $stripes->{$times[$t+1]};
+            # Compute all holes with this start time, as long as the hole vector is not empty
+            while ($vec ne $empty_vec) {
+                #print("vec= ".unpack("b*", $vec)." \@t=".($t+1)." l=".$times[$t+1]." (".strftime("%F_%T",localtime($times[$t+1])).")\n");
+                my $tt = $t+1;
+                # Try and extend the hole to the next stripes: ok if for any r, vec(r,current_hole) => vec(r,tt+1)
+                while (($tt < $#times) and ((~ $vec | $stripes->{$times[$tt+1]}) eq ~ $empty_vec)) {
+                    #print("ok-> ".unpack("b*", $stripes->{$times[$tt]})." tt=".($tt+1)." l=".$times[$tt+1]." (".strftime("%F_%T",localtime($times[$tt+1])).")\n");
+                    $tt++;
+                }
+                # We now reached the end time for this hole
+                #print "endtime: $tt l=".$times[$tt]." (".strftime("%F_%T",localtime($times[$tt])).")\n";
+                push @$holes, [$times[$tt], $vec];
+                # Test if we did not reach the end of the stripes
+                if ($tt < $#times) {
+                    $vec &= $stripes->{$times[$tt+1]};
+                } else {
+                    $vec = $empty_vec;
+                }
+            }
+            # Store the new start time with its holes
+            push @$merged_gantt, [$times[$t], $holes, undef, undef, undef, undef ];
+        #} else {
+        #    print "NOP: no hole at starttime: t=$t l=".$times[$t]." (".strftime("%F_%T",localtime($times[$t])).")\n";
+        }
+    }
+    # Well done, now fill the global values of the gantt and return
+    $merged_gantt->[0]->[2] = $gantt1->[0]->[2];
+    $merged_gantt->[0]->[3] = $gantt1->[0]->[3];
+    $merged_gantt->[0]->[4] = $gantt1->[0]->[4];
+    $merged_gantt->[0]->[5] = $gantt1->[0]->[5];
+    #pretty_print($merged_gantt);
+    return $merged_gantt;
+}
+
+# Manage the different gantts used in the schedulers handling container, timesharing and placeholder
+# See oar_sched_gantt_with_timesharing_and_placeholder
+# This allows to factorize code since this function is called in the 2 phases (running jobs, and to schedule jobs)
+sub manage_gantt_for_timesharing_and_placeholder($$$$$$) {
+    my $Gantt = shift;
+    my $job_user = shift;
+    my $job_name = shift;
+    my $types = shift;
+    my $inner_id = shift;
+    my $log_prefix = shift;
+    my $set_placeholder_name = "";
+    my $use_placeholder_name = "";
+    my $timesharing_user = "";
+    my $timesharing_name = "";
+    if (defined($types->{set_placeholder})){ # A set_placeholder job cannot be use_placeholder or timesharing. 
+        $set_placeholder_name = $types->{set_placeholder};
+        oar_debug("$log_prefix job is ($inner_id,s:$set_placeholder_name,,)\n");
+        if (not defined($Gantt->{$inner_id}->{$set_placeholder_name}->{""}->{""})){
+            $Gantt->{$inner_id}->{$set_placeholder_name}->{""}->{""} = dclone($Gantt->{$inner_id}->{""}->{""}->{""});
+            oar_debug("$log_prefix set_placeholder job: cloned gantt ($inner_id,$set_placeholder_name,,) from ($inner_id,,,)\n");
+        }
+    } else {
+        if (defined($types->{use_placeholder})){
+            $use_placeholder_name = $types->{use_placeholder};
+        }
+        if (defined($types->{timesharing})){
+            $timesharing_user = "*";
+            $timesharing_name = "*";
+            foreach my $s (split(',', $types->{timesharing})){
+                if ($s =~ m/^\s*([\w\*]+)\s*$/m){
+                    if ($1 eq "user"){
+                        $timesharing_user = $job_user;
+                    }elsif (($1 eq "name") and ($job_name ne "")){
+                        $timesharing_name = $job_name;
+                    }
+                }
+            }
+        }
+        oar_debug("$log_prefix job is ($inner_id,u:$use_placeholder_name,$timesharing_user,$timesharing_name)\n");
+        if (not defined($Gantt->{$inner_id}->{$use_placeholder_name}->{$timesharing_user}->{$timesharing_name})) {
+            if (not defined($Gantt->{$inner_id}->{$use_placeholder_name}->{""}->{""}) and not defined($Gantt->{$inner_id}->{""}->{$timesharing_user}->{$timesharing_name})) {
+                $Gantt->{$inner_id}->{$use_placeholder_name}->{$timesharing_user}->{$timesharing_name} = dclone($Gantt->{$inner_id}->{""}->{""}->{""});
+                oar_debug("$log_prefix use_placeholder/timesharing job: cloned gantt ($inner_id,$use_placeholder_name,$timesharing_user,$timesharing_name) from ($inner_id,,,)\n");
+                if ($use_placeholder_name ne "") {
+                    $Gantt->{$inner_id}->{$use_placeholder_name}->{""}->{""} = dclone($Gantt->{$inner_id}->{""}->{""}->{""});
+                    oar_debug("$log_prefix use_placeholder/timesharing job: cloned gantt ($inner_id,$use_placeholder_name,,) from ($inner_id,,,)\n");
+                }
+                if ($timesharing_user ne "" or $timesharing_name ne "") {
+                    $Gantt->{$inner_id}->{""}->{$timesharing_user}->{$timesharing_user} = dclone($Gantt->{$inner_id}->{""}->{""}->{""});
+                    oar_debug("$log_prefix use_placeholder/timesharing job: cloned gantt ($inner_id,,$timesharing_user,$timesharing_name) from ($inner_id,,,)\n");
+                }
+            } elsif (not defined($Gantt->{$inner_id}->{$use_placeholder_name}->{""}->{""})) { #G($i,,$u,$n) is defined
+                $Gantt->{$inner_id}->{$use_placeholder_name}->{""}->{""} = dclone($Gantt->{$inner_id}->{""}->{""}->{""});
+                oar_debug("$log_prefix use_placeholder/timesharing job: cloned gantt ($inner_id,$use_placeholder_name,,) from ($inner_id,,,)\n");
+                if ($timesharing_user ne "" and $timesharing_name ne "") {
+                    $Gantt->{$inner_id}->{$use_placeholder_name}->{$timesharing_user}->{$timesharing_name} = dclone($Gantt->{$inner_id}->{""}->{$timesharing_user}->{$timesharing_name});
+                    oar_debug("$log_prefix use_placeholder/timesharing job: cloned gantt ($inner_id,$use_placeholder_name,$timesharing_user,$timesharing_name) from ($inner_id,,$timesharing_user,$timesharing_name)\n");
+                }
+            } elsif (not defined($Gantt->{$inner_id}->{""}->{$timesharing_user}->{$timesharing_name})) { # G($i,$p,,) is defined
+                $Gantt->{$inner_id}->{""}->{$timesharing_user}->{$timesharing_user} = dclone($Gantt->{$inner_id}->{""}->{""}->{""});
+                oar_debug("$log_prefix use_placeholder/timesharing job: cloned gantt ($inner_id,,$timesharing_user,$timesharing_name) from ($inner_id,,,)\n");
+                if ($use_placeholder_name ne "") {
+                    $Gantt->{$inner_id}->{$use_placeholder_name}->{$timesharing_user}->{$timesharing_name} = dclone($Gantt->{$inner_id}->{$use_placeholder_name}->{""}->{""});
+                    oar_debug("$log_prefix use_placeholder/timesharing job: cloned gantt ($inner_id,$use_placeholder_name,$timesharing_user,$timesharing_name) from ($inner_id,$use_placeholder_name,,)\n");
+                }
+            } else { # Both G($i,$p,,) and G($i,,$u,$n) are defined. We need to merge them to create G($i,$p,$u,$n) 
+                $Gantt->{$inner_id}->{$use_placeholder_name}->{$timesharing_user}->{$timesharing_name} = OAR::Schedulers::GanttHoleStorage_with_quotas::merge_clone($Gantt->{$inner_id}->{$use_placeholder_name}->{""}->{""},$Gantt->{$inner_id}->{""}->{$timesharing_user}->{$timesharing_name});
+                oar_debug("$log_prefix use_placeholder/timesharing job: merged gantt ($inner_id,$use_placeholder_name,$timesharing_user,$timesharing_name) from ($inner_id,$use_placeholder_name,,) and ($inner_id,,$timesharing_user,$timesharing_name)\n");
+            }
+        }
+    }
+    return ($set_placeholder_name, $use_placeholder_name, $timesharing_user, $timesharing_name);
+}
+
+# Fill the gantts different gantts used in the schedulers handling container, timesharing and placeholder
+# See oar_sched_gantt_with_timesharing_and_placeholder
+# This allows to factorize code since this function is called in the 2 phases (running jobs, and to schedule jobs)
+sub fill_gantts($$$$$$$$$$) {
+    my $Gantt = shift;
+    my $date = shift;
+    my $duration = shift;
+    my $resources_vec = shift;
+    my $inner_id = shift;
+    my $set_placeholder_name = shift;
+    my $use_placeholder_name = shift;
+    my $timesharing_user = shift;
+    my $timesharing_name = shift;
+    my $log_prefix = shift;
+    my $queue = shift;
+    my $project = shift;
+    my $types_arrayref = shift;
+    my $user = shift;
+
+    
+    foreach my $p (keys(%{$Gantt->{$inner_id}})){
+        foreach my $u (keys(%{$Gantt->{$inner_id}->{$p}})){
+            foreach my $n (keys(%{$Gantt->{$inner_id}->{$p}->{$u}})){
+                if (not (($p ne "" and $p eq $set_placeholder_name) or ($u ne "" and $u eq $timesharing_user and $n ne "" and $n eq $timesharing_name))){
+                    oar_debug("$log_prefix add job occupation in gantt ($inner_id,$p,$u,$n)\n");
+                    OAR::Schedulers::GanttHoleStorage_with_quotas::set_occupation( $Gantt->{$inner_id}->{$p}->{$u}->{$n},
+                                                        $date,
+                                                        $duration,
+                                                        $resources_vec
+                                                      );
+                } else {
+                    if ($set_placeholder_name ne "") {
+                        oar_debug("$log_prefix skip job occupation in gantt ($inner_id,$p,$u,$n) because job is ($inner_id,s:$set_placeholder_name,,)\n");
+                    } else {
+                        oar_debug("$log_prefix skip job occupation in gantt ($inner_id,$p,$u,$n) because job is ($inner_id,u:$use_placeholder_name,$timesharing_user,$timesharing_name)\n");
+                    }
+                }
+            }
+        }
+    }
+}
+
+# Adds and initializes new resources in the gantt
+# args : gantt ref, bit vector of resources
+sub add_new_resources($$) {
+    my ($gantt, $resources_vec) = @_;
+
+    # Feed vector with enough 0
+    $resources_vec |= $gantt->[0]->[3]; 
+    
+    # Verify which resources are not already inserted
+    my $resources_to_add_vec = $resources_vec & (~ $gantt->[0]->[2]);
+   
+    if (unpack("%32b*",$resources_to_add_vec) > 0){
+        # We need to insert new resources on all hole
+        my $g = 0;
+        while ($g <= $#{$gantt}){
+            # Add resources
+            if ($gantt->[$g]->[1]->[$#{$gantt->[$g]->[1]}]->[0] == $Infinity){
+                $gantt->[$g]->[1]->[$#{$gantt->[$g]->[1]}]->[1] |= $resources_to_add_vec;
+            }else{
+                push(@{$gantt->[$g]->[1]}, [$Infinity, $resources_vec]);
+            }
+            $g++;
+        }
+        # Keep already inserted resources in mind
+        $gantt->[0]->[2] |= $resources_vec;
+    }
+}
+
+
+# Inserts in the gantt new resource occupations
+# args : gantt ref, start slot date, slot duration, resources bit vector
+sub set_occupation($$$$){
+    my ($gantt, $date, $duration, $resources_vec) = @_;
+
+    # Feed vector with enough 0
+    $resources_vec |= $gantt->[0]->[3];
+
+    # If a resource was not initialized
+    add_new_resources($gantt,$resources_vec); # If it is not yet done
+
+    my $new_hole = [
+                        $date + $duration + 1,
+                        []
+                    ];
+    
+    my $g = 0;
+    while (($g <= $#{$gantt}) and ($gantt->[$g]->[0] <= $new_hole->[0])){
+        my $slot_deleted = 0;
+        # Look at all holes that are before the end of the occupation
+        if (($#{$gantt->[$g]->[1]} >= 0) and ($gantt->[$g]->[1]->[$#{$gantt->[$g]->[1]}]->[0] >= $date)){
+            # Look at holes with a bigger slot >= $date
+            my $h = 0;
+            my $slot_date_here = 0;
+            while ($h <= $#{$gantt->[$g]->[1]}){
+                # Look at all slots
+                $slot_date_here = 1 if ($gantt->[$g]->[1]->[$h]->[0] == $date);
+                if ($gantt->[$g]->[1]->[$h]->[0] > $date){
+                    # This slot ends after $date
+                    
+                    #print("-->[1]\n ".($date - $gantt->[$g]->[0])." -- $gantt->[0]->[4]\n<--[1]\n");
+                    
+                    if (($gantt->[$g]->[0] < $date) and ($slot_date_here == 0) and ($date - $gantt->[$g]->[0] > $gantt->[0]->[4])){
+                        # We must create a smaller slot (hole start time < $date)
+                        splice(@{$gantt->[$g]->[1]}, $h, 0, [ $date , $gantt->[$g]->[1]->[$h]->[1] ]);
+                        $h++;   # Go to the slot that we were on it before the splice
+                        $slot_date_here = 1;
+
+                        #print("-->[2]\n "); pretty_print($gantt); print("<--[2]\n");
+
+                    }
+                    # Add new slots in the new hole
+                    if (($new_hole->[0] < $gantt->[$g]->[1]->[$h]->[0]) and ($gantt->[$g]->[1]->[$h]->[0] - $new_hole->[0] > $gantt->[0]->[4])){
+                        # copy slot in the new hole if needed
+                        my $slot = 0;
+                        while (($slot <= $#{$new_hole->[1]}) and ($new_hole->[1]->[$slot]->[0] < $gantt->[$g]->[1]->[$h]->[0])){
+                            # Find right index in the sorted slot array
+                            $slot++;
+                        }
+                        if ($slot <= $#{$new_hole->[1]}){
+                            if ($new_hole->[1]->[$slot]->[0] == $gantt->[$g]->[1]->[$h]->[0]){
+                                # If the slot already exists
+                                $new_hole->[1]->[$slot]->[1] |= $gantt->[$g]->[1]->[$h]->[1];
+                            }else{
+                                # Insert the new slot
+                                splice(@{$new_hole->[1]}, $slot, 0, [$gantt->[$g]->[1]->[$h]->[0], $gantt->[$g]->[1]->[$h]->[1]]);
+                            }
+                        }elsif ($new_hole->[0] < $gantt->[$g]->[1]->[$h]->[0]){
+                            # There is no slot so we create one
+                            push(@{$new_hole->[1]}, [ $gantt->[$g]->[1]->[$h]->[0], $gantt->[$g]->[1]->[$h]->[1] ]);
+                            
+                            #print("-->[3]\n Add new hole $new_hole->[0]: $gantt->[$g]->[1]->[$h]->[0]:\n".Dumper($new_hole)."\n -->[3]\n");
+                        
+                        }
+                    }
+                    # Remove new occupied resources from the current slot
+                    $gantt->[$g]->[1]->[$h]->[1] &= (~ $resources_vec) ;
+                    if (unpack("%32b*",$gantt->[$g]->[1]->[$h]->[1]) == 0){
+                        # There is no free resource on this slot so we delete it
+                        
+                        #print("-->[4]\n Delete slot: $gantt->[$g]->[0],$gantt->[$g]->[1]->[$h]->[0] \n<--[4]\n");
+                        
+                        splice(@{$gantt->[$g]->[1]}, $h, 1);
+                        $h--;
+                        $slot_deleted = 1;
+
+                        #print("-->[5]\n "); pretty_print($gantt); print("<--[5]\n");
+
+                    }elsif ($h > 0){
+                        # check if this is the same hole than the previous one
+                        my $tmp_vec = $gantt->[$g]->[1]->[$h-1]->[1] ^ $gantt->[$g]->[1]->[$h]->[1];
+                        if (unpack("%32b*",$tmp_vec) == 0){
+                            splice(@{$gantt->[$g]->[1]}, $h-1, 1);
+                            $h--;
+                        }
+                    }
+                }
+                # Go to the next slot
+                $h++;
+            }
+        }
+        if (($slot_deleted == 1) and ($#{$gantt->[$g]->[1]} < 0)){
+            # There is no free slot on the current hole so we delete it
+            splice(@{$gantt}, $g, 1);
+            $g--;
+        }elsif($g > 0){
+            # Test if there is a same hole
+            my $different = 0;
+
+            #print("-->[6]\n ");pretty_print($gantt);print("<--[6]\n");
+            #print("-->[7]\nG-1=$gantt->[$g - 1]->[0]       G=$gantt->[$g]->[0] \n<--[7]\n");
+            
+            if ($#{$gantt->[$g - 1]->[1]} != $#{$gantt->[$g]->[1]}){
+                $different = 1;
+            }
+            my $tmp_h = 0;
+            while (($different == 0) and (defined($gantt->[$g]->[1]->[$tmp_h]))){
+                if ($gantt->[$g - 1]->[1]->[$tmp_h]->[0] != $gantt->[$g]->[1]->[$tmp_h]->[0]){
+                    $different = 1;
+                }else{
+                    my $tmp_vec = $gantt->[$g - 1]->[1]->[$tmp_h]->[1] ^ $gantt->[$g]->[1]->[$tmp_h]->[1];
+                    if (unpack("%32b*",$tmp_vec) != 0){
+                        $different = 1;
+                    }
+                }
+                $tmp_h++;
+            }
+            if ($different == 0){
+                
+                #print("-->[8]\n Delete Hole: $gantt->[$g]->[0] \n-->[8]\n");
+                
+                splice(@{$gantt}, $g, 1);
+                $g--;
+            }
+        }
+        # Go to the next hole
+        $g++;
+    }
+    
+    #print("-->[9]\n "); pretty_print($gantt); print("<--[9]\n");
+    
+    if ($#{$new_hole->[1]} >= 0){
+        # Add the new hole
+        if (($g > 0) and ($g - 1 <= $#{$gantt}) and ($gantt->[$g - 1]->[0] == $new_hole->[0])){
+            # Verify if the hole does not already exist
+            splice(@{$gantt}, $g - 1, 1, $new_hole);
+        }else{
+            splice(@{$gantt}, $g, 0, $new_hole);
+        }
+    }
+}
+
+# Find the first hole in the data structure that can fit the given slot
+sub find_hole($$$){
+    my ($gantt, $begin_date, $duration) = @_;
+
+    my $end_date = $begin_date + $duration;
+    my $g = 0;
+    while (($g < $#{$gantt}) and (($gantt->[$g+1]->[0] <= $begin_date) or (($g <= $#{$gantt}) and ($gantt->[$g]->[0] < $begin_date) and ($gantt->[$g]->[1]->[$#{$gantt->[$g]->[1]}]->[0] < $end_date)))){
+        $g++
+    }
+
+    return($g);
+}
+
+# Returns the vector of the maximum free resources at the given date for the given duration
+sub get_free_resources($$$){
+    my ($gantt, $begin_date, $duration) = @_;
+    
+    my $end_date = $begin_date + $duration;
+    my $hole_index = 0;
+    # search the nearest hole
+    while (($hole_index <= $#{$gantt}) and ($gantt->[$hole_index]->[0] < $begin_date) and
+            (($gantt->[$hole_index]->[1]->[$#{$gantt->[$hole_index]->[1]}]->[0] < $end_date) or 
+                (($hole_index + 1 <= $#{$gantt}) and $gantt->[$hole_index + 1]->[0] < $begin_date))){
+        $hole_index++;
+    }
+    return($gantt->[0]->[4]) if ($hole_index > $#{$gantt});
+    
+    my $h = 0;
+    while (($h <= $#{$gantt->[$hole_index]->[1]}) and ($gantt->[$hole_index]->[1]->[$h]->[0] < $end_date)){
+        $h++;
+    }
+    return($gantt->[$hole_index]->[1]->[$h]->[1]);
+}
+
+
+# Take a list of resource trees and find a hole that fit
+# args : gantt ref, initial time from which the search will begin, job duration, list of resource trees
+sub find_first_hole($$$$$$$$$$$){
+    my ($gantt, $initial_time, $duration, $tree_description_list, $timeout, $job_queue, $job_project, $job_types_arrayref, $job_user, $gantt_quotas,$accounting) = @_;
+
+    # $tree_description_list->[0]  --> First resource group corresponding tree
+    # $tree_description_list->[1]  --> Second resource group corresponding tree
+    # ...
+
+    # Test if all groups are populated
+    my $comment = "no_matching_slot";
+    my $return_infinity = 0;
+    my $g = 0;
+    while (($return_infinity == 0) and ($g <= $#$tree_description_list)){
+        if (!defined($tree_description_list->[$g])){
+            $return_infinity = 1;
+        }
+        $g++;
+    }
+    return ($Infinity, $comment, ()) if ($return_infinity > 0);
+
+    my @result_tree_list = ();
+    my $end_loop = 0;
+    my $current_time = $initial_time;
+    my $timeout_initial_time = time();
+    # begin research at the first potential hole
+    my $current_hole_index = find_hole($gantt, $initial_time, $duration);
+    my $h = 0;
+    while ($end_loop == 0){
+        # Go to a right hole
+        while (($current_hole_index <= $#{$gantt}) and
+                (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or
+                   (($initial_time > $gantt->[$current_hole_index]->[0]) and
+                        ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){
+            while (($h <= $#{$gantt->[$current_hole_index]->[1]}) and
+                    (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or
+                        (($initial_time > $gantt->[$current_hole_index]->[0]) and
+                        ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){
+                $h++;
+            }
+            if ($h > $#{$gantt->[$current_hole_index]->[1]}){
+                # in this hole no slot fits so we must search in the next hole
+                $h = 0;
+                $current_hole_index++;
+            }
+        }
+        if ($current_hole_index > $#{$gantt}){
+            # no hole fits
+            $current_time = $Infinity;
+            @result_tree_list = ();
+            $end_loop = 1;
+        }else{
+            #print("Treate hole $current_hole_index, $h : $gantt->[$current_hole_index]->[0] --> $gantt->[$current_hole_index]->[1]->[$h]->[0]\n");
+            $current_time = $gantt->[$current_hole_index]->[0] if ($initial_time < $gantt->[$current_hole_index]->[0]);
+            #Check all trees
+            my $tree_clone;
+            my $i = 0;
+            # Initiate already used resources with the empty vector
+            my $already_occupied_resources_vec = $gantt->[0]->[3]; 
+            my $result_tree_accounting_nbresources_used = 0;
+            do{
+                foreach my $l (OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone)){
+                    vec($already_occupied_resources_vec, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1;
+                }
+                # clone the tree, so we can work on it without damage
+                $tree_clone = OAR::Schedulers::ResourceTree::clone($tree_description_list->[$i]);
+                #Remove tree leafs that are not free
+                foreach my $l (OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone)){
+                    if ((!vec($gantt->[$current_hole_index]->[1]->[$h]->[1],OAR::Schedulers::ResourceTree::get_current_resource_value($l),1)) or
+                        (vec($already_occupied_resources_vec,OAR::Schedulers::ResourceTree::get_current_resource_value($l),1))
+                       ){
+                        OAR::Schedulers::ResourceTree::delete_subtree($l);
+                    }
+                }
+                #print(Dumper($tree_clone));
+                $tree_clone = OAR::Schedulers::ResourceTree::delete_tree_nodes_with_not_enough_resources($tree_clone);
+                $tree_clone = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($tree_clone);
+                
+                ## QUOTAS
+                # $current_time : start date of the hole
+                # $gantt->[$current_hole_index]->[1]->[$h]->[0] : stop date of the hole
+                if (defined($tree_clone)){
+                    # Keep in mind the number of resources used by previous groups of the job
+                    $result_tree_accounting_nbresources_used += OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone);
+                    my $gantt_next_hole_date_start = $Infinity;
+                    $gantt_next_hole_date_start = $gantt->[$current_hole_index+1]->[0] if ($current_hole_index < $#{$gantt});
+                    ($current_time,$comment) = OAR::Schedulers::QuotaStorage::check_quotas(
+                                                    $accounting,
+                                                    $gantt_quotas,
+                                                    $current_time,
+                                                    $gantt->[$current_hole_index]->[1]->[$h]->[0],
+                                                    $gantt_next_hole_date_start,
+                                                    $duration,
+                                                    $job_queue,$job_project,$job_types_arrayref,$job_user,
+                                                    $result_tree_accounting_nbresources_used
+                                                                                           );
+                    if (($current_time + $duration >= $gantt->[$current_hole_index]->[1]->[$h]->[0])
+                        or (($current_hole_index < $#{$gantt}) and ($gantt->[$current_hole_index+1]->[0] <= $current_time))
+                       ){
+                        $tree_clone = undef;
+                    }
+                }
+                ## QUOTAS
+                $result_tree_list[$i] = $tree_clone;
+                $i ++;
+            }while(defined($tree_clone) && ($i <= $#$tree_description_list));
+            if (defined($tree_clone)){
+                # We find the first hole
+                $end_loop = 1;
+            }else{
+                # Go to the next slot
+                if (($h >= $#{$gantt->[$current_hole_index]->[1]})
+                     or (($current_hole_index < $#{$gantt}) and ($gantt->[$current_hole_index+1]->[0] <= $current_time))
+                   ){
+                    $h = 0;
+                    $current_hole_index++;
+                }else{
+                    $h++;
+                }
+            }
+        }
+        # Check timeout
+        if (($current_hole_index <= $#{$gantt}) and
+            (((time() - $timeout_initial_time) >= $timeout) or
+            (($gantt->[$current_hole_index]->[0] == $gantt->[0]->[5]->[0]) and ($gantt->[$current_hole_index]->[1]->[$h]->[0] >= $gantt->[0]->[5]->[1])) or
+            ($gantt->[$current_hole_index]->[0] > $gantt->[0]->[5]->[0])) and
+            ($gantt->[$current_hole_index]->[0] > $initial_time)){
+            if (($gantt->[0]->[5]->[0] == $gantt->[$current_hole_index]->[0]) and
+                ($gantt->[0]->[5]->[1] > $gantt->[$current_hole_index]->[1]->[$h]->[0])){
+                $gantt->[0]->[5]->[1] = $gantt->[$current_hole_index]->[1]->[$h]->[0];
+            }elsif ($gantt->[0]->[5]->[0] > $gantt->[$current_hole_index]->[0]){
+                $gantt->[0]->[5]->[0] = $gantt->[$current_hole_index]->[0];
+                $gantt->[0]->[5]->[1] = $gantt->[$current_hole_index]->[1]->[$h]->[0];
+            }
+            #print("TTTTTTT $gantt->[0]->[5]->[0] $gantt->[0]->[5]->[1] -- $gantt->[$current_hole_index]->[0] $gantt->[$current_hole_index]->[1]->[$h]->[0]\n");
+            $current_time = $Infinity;
+            @result_tree_list = ();
+            $end_loop = 1;
+        }
+    }
+
+    return($current_time, $comment, \@result_tree_list);
+}
+
+# Take a list of resource trees and find a hole that fit
+# args : gantt ref, initial time from which the search will begin, job duration, list of resource trees
+sub find_first_hole_parallel($$$$$$$$$$$$){
+    my ($gantt, $initial_time, $duration, $tree_description_list, $timeout, $max_children, $job_queue, $job_project, $job_types_arrayref, $job_user, $gantt_quotas,$accounting) = @_;
+
+    # $tree_description_list->[0]  --> First resource group corresponding tree
+    # $tree_description_list->[1]  --> Second resource group corresponding tree
+    # ...
+
+    # Test if all groups are populated
+    my $comment = "no_matching_slot";
+    my $return_infinity = 0;
+    my $g = 0;
+    while (($return_infinity == 0) and ($g <= $#$tree_description_list)){
+        if (!defined($tree_description_list->[$g])){
+            $return_infinity = 1;
+        }
+        $g++;
+    }
+    return ($Infinity, $comment, ()) if ($return_infinity > 0);
+
+
+    my @result_tree_list = ();
+    my $end_loop = 0;
+    my $current_time = $initial_time;
+    my $timeout_initial_time = time();
+    my %children;
+    my $process_index = 0;
+    my $process_index_to_check = 0;
+    my @result_children;
+    # begin research at the first potential hole
+    my $current_hole_index = find_hole($gantt, $initial_time, $duration);
+    my $current_process_hole_index = 0;
+    my $h = 0;
+    while ($end_loop == 0){
+        # Go to a right hole
+        while (($current_hole_index <= $#{$gantt}) and
+                (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or
+                   (($initial_time > $gantt->[$current_hole_index]->[0]) and
+                        ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){
+            while (($h <= $#{$gantt->[$current_hole_index]->[1]}) and
+                    (($gantt->[$current_hole_index]->[0] + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0]) or
+                        (($initial_time > $gantt->[$current_hole_index]->[0]) and
+                        ($initial_time + $duration > $gantt->[$current_hole_index]->[1]->[$h]->[0])))){
+                $h++;
+            }
+            if ($h > $#{$gantt->[$current_hole_index]->[1]}){
+                # in this hole no slot fits so we must search in the next hole
+                $h = 0;
+                $current_hole_index++;
+            }
+        }
+        if (($current_hole_index > $#{$gantt}) and (keys(%children) <= 0)){
+            # no hole fits
+            $current_time = $Infinity;
+            @result_tree_list = ();
+            $end_loop = 1;
+        }else{
+            my $select_timeout = 0.1;
+            if (($current_hole_index <= $#{$gantt}) and (keys(%children) < $max_children)){
+                $select_timeout = 0;
+                $current_process_hole_index = $current_hole_index if ($process_index == 0);
+                my $P1;
+                my $P2;
+                pipe($P1,$P2);
+                my $pid = fork();
+                if ($pid == 0){
+                    #Child
+                    close($P1);
+                    #print "PID $$ : $process_index ($current_hole_index)\n";
+                    #print("Treate hole $current_hole_index, $h : $gantt->[$current_hole_index]->[0] --> $gantt->[$current_hole_index]->[1]->[$h]->[0]\n");
+                    $current_time = $gantt->[$current_hole_index]->[0] if ($initial_time < $gantt->[$current_hole_index]->[0]);
+                    #Check all trees
+                    my $tree_clone;
+                    my $tree_list;
+                    my $i = 0;
+                    # Initiate already used resources with the empty vector
+                    my $already_occupied_resources_vec = $gantt->[0]->[3];
+                    my $result_tree_accounting_nbresources_used = 0;
+                    do{
+                        foreach my $l (OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone)){
+                            vec($already_occupied_resources_vec, OAR::Schedulers::ResourceTree::get_current_resource_value($l), 1) = 1;
+                        }
+                        $tree_clone = $tree_description_list->[$i];
+                        #Remove tree leafs that are not free
+                        foreach my $l (OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone)){
+                            if ((!vec($gantt->[$current_hole_index]->[1]->[$h]->[1],OAR::Schedulers::ResourceTree::get_current_resource_value($l),1)) or
+                                (vec($already_occupied_resources_vec,OAR::Schedulers::ResourceTree::get_current_resource_value($l),1))
+                               ){
+                                OAR::Schedulers::ResourceTree::delete_subtree($l);
+                            }
+                        }
+                        #print(Dumper($tree_clone));
+                        $tree_clone = OAR::Schedulers::ResourceTree::delete_tree_nodes_with_not_enough_resources($tree_clone);
+                        $tree_clone = OAR::Schedulers::ResourceTree::delete_unnecessary_subtrees($tree_clone);
+
+                        ## QUOTAS
+                        # $current_time : start date of the hole
+                        # $gantt->[$current_hole_index]->[1]->[$h]->[0] : stop date of the hole
+                        if (defined($tree_clone)){
+                            # Keep in mind the number of resources used by previous groups of the job
+                            $result_tree_accounting_nbresources_used += OAR::Schedulers::ResourceTree::get_tree_leafs($tree_clone);
+                            my $gantt_next_hole_date_start = $Infinity;
+                            $gantt_next_hole_date_start = $gantt->[$current_hole_index+1]->[0] if ($current_hole_index < $#{$gantt});
+                            ($current_time, $comment) = OAR::Schedulers::QuotaStorage::check_quotas(
+                                                            $accounting,
+                                                            $gantt_quotas,
+                                                            $current_time,
+                                                            $gantt->[$current_hole_index]->[1]->[$h]->[0],
+                                                            $gantt_next_hole_date_start,
+                                                            $duration,
+                                                            $job_queue,$job_project,$job_types_arrayref,$job_user,
+                                                            $result_tree_accounting_nbresources_used
+                                                                                                   );
+                            if (($current_time + $duration >= $gantt->[$current_hole_index]->[1]->[$h]->[0])
+                                or (($current_hole_index < $#{$gantt}) and ($gantt->[$current_hole_index+1]->[0] <= $current_time))
+                               ){
+                                $tree_clone = undef;
+                            }
+                        }
+                        ## QUOTAS
+                        $tree_list->[$i] = $tree_clone;
+                        $i ++;
+                    }while(defined($tree_clone) && ($i <= $#$tree_description_list));
+
+                    my %result = (
+                        process_index => $process_index,
+                        current_time => $current_time,
+                        current_hole_index => $current_hole_index,
+                        comment => $comment
+                    );
+                    if (defined($tree_clone)){
+                        #print "PID $$; INDEX $process_index : I found a hole\n";
+                        $result{result_tree_list} = $tree_list;
+                    }else{
+                        $result{result_tree_list} = undef;
+                    }
+                    select($P2);
+                    $| = 1;
+                    store_fd(\%result, $P2);
+                    close($P2);
+                    exit(0);
+                }
+                #Father
+                $children{$pid} = {
+                                    process_index => $process_index,
+                                    pipe_read => $P1
+                                  };
+                $process_index++;
+                # Go to the next slot of this hole
+                if ($h >= $#{$gantt->[$current_hole_index]->[1]}){
+                    $h = 0;
+                    $current_hole_index++;
+                }else{
+                    $h++;
+                }
+            }
+            # check children results
+            my $rin = '';
+            foreach my $p (keys(%children)){
+                vec($rin, fileno($children{$p}->{pipe_read}), 1) = 1;
+            }
+            my $rout;
+            if (select($rout=$rin, undef, undef, $select_timeout)){
+                foreach my $p (keys(%children)){
+                    if (vec($rout,fileno($children{$p}->{pipe_read}),1)){
+                        my $fh = $children{$p}->{pipe_read};
+                        my $hash = fd_retrieve($fh);
+                        #print "MASTER child $children{$p}->{process_index} FINISHED\n";
+                        $result_children[$children{$p}->{process_index}] = $hash;
+                        delete($children{$p});
+                        close($fh);
+                    }
+                }
+            }
+
+            while ((defined($result_children[$process_index_to_check])) and ($end_loop == 0)){
+                if (defined($result_children[$process_index_to_check]->{result_tree_list})){
+                    # We find the first hole
+                    #print "MASTER using hole from process index $process_index_to_check\n";
+                    $current_time = $result_children[$process_index_to_check]->{current_time};
+                    @result_tree_list = @{$result_children[$process_index_to_check]->{result_tree_list}};
+                    $end_loop = 1;
+                }elsif (($result_children[$process_index_to_check]->{current_hole_index} < $#{$gantt})
+                        and ($result_children[$process_index_to_check]->{current_hole_index} >= $current_hole_index)
+                        and ($gantt->[$result_children[$process_index_to_check]->{current_hole_index}+1]->[0] <= $result_children[$process_index_to_check]->{current_time})
+                       ){
+                    # Quotas will be ok on the next hole: don't need to check the remaing holes with this start date
+                    $current_hole_index = $result_children[$process_index_to_check]->{current_hole_index} + 1;
+                }
+                $comment = $result_children[$process_index_to_check]->{comment};
+                $current_process_hole_index = $result_children[$process_index_to_check]->{current_hole_index};
+                $process_index_to_check++;
+            }
+            # Avoid zombies
+            my $kid = 1;
+            while ($kid > 0){
+                $kid = waitpid(-1, WNOHANG);
+            }
+        
+            # Check timeout
+            #print "CURRENT HOLE: $current_process_hole_index\n";
+            if (($end_loop == 0) and ($current_process_hole_index <= $#{$gantt}) and
+                (((time() - $timeout_initial_time) >= $timeout) or
+                (($gantt->[$current_process_hole_index]->[0] == $gantt->[0]->[5]->[0]) and ($gantt->[$current_process_hole_index]->[1]->[$h]->[0] >= $gantt->[0]->[5]->[1])) or
+                ($gantt->[$current_process_hole_index]->[0] > $gantt->[0]->[5]->[0])) and
+                ($gantt->[$current_process_hole_index]->[0] > $initial_time)){
+                if (($gantt->[0]->[5]->[0] == $gantt->[$current_process_hole_index]->[0]) and
+                    ($gantt->[0]->[5]->[1] > $gantt->[$current_process_hole_index]->[1]->[$h]->[0])){
+                    $gantt->[0]->[5]->[1] = $gantt->[$current_process_hole_index]->[1]->[$h]->[0];
+                }elsif ($gantt->[0]->[5]->[0] > $gantt->[$current_process_hole_index]->[0]){
+                    $gantt->[0]->[5]->[0] = $gantt->[$current_process_hole_index]->[0];
+                    $gantt->[0]->[5]->[1] = $gantt->[$current_process_hole_index]->[1]->[$h]->[0];
+                }
+                #print("TTTTTTT $gantt->[0]->[5]->[0] $gantt->[0]->[5]->[1] -- $gantt->[$current_hole_index]->[0] $gantt->[$current_hole_index]->[1]->[$h]->[0]\n");
+                $current_time = $Infinity;
+                @result_tree_list = ();
+                $end_loop = 1;
+            }
+        }
+    }
+
+    kill(9,keys(%children));
+    # Avoid zombies
+    my $kid = 1;
+    while ($kid > 0){
+        $kid = waitpid(-1, WNOHANG);
+    }
+    return($current_time, $comment, \@result_tree_list);
+}
+
+
+#return 1;
diff --git a/sources/core/server/lib/OAR/Schedulers/QuotaStorage.pm b/sources/core/server/lib/OAR/Schedulers/QuotaStorage.pm
new file mode 100644
index 0000000..27c2188
--- /dev/null
+++ b/sources/core/server/lib/OAR/Schedulers/QuotaStorage.pm
@@ -0,0 +1,244 @@
+# $Id$
+package OAR::Schedulers::QuotaStorage;
+require Exporter;
+use POSIX qw(strftime);
+use Storable qw(store_fd fd_retrieve dclone);
+use OAR::Schedulers::GanttHoleStorage_with_quotas;
+use warnings;
+use strict;
+use Time::HiRes qw(gettimeofday tv_interval);
+
+# Prototypes
+# quota data management
+sub new();
+sub read_conf_file($);
+sub update_accounting_counters($$$$$$$$);
+sub check_quotas($$$$$$$$$$$);
+sub pretty_print($);
+###############################################################################
+
+# Creates an accounting data structure for quotas
+sub new(){
+    my $accounting_counters_init;
+    # $accounting_counters_init->{'queue'}->{'project'}->{'type'}->{'user'} = nb_used_resources
+    $accounting_counters_init->{'*'}->{'*'}->{'*'}->{'*'} = 0;
+
+    return( [                       # Accounting data storage for quotas: an array of array
+                [                   # This is a chronological stack with:
+                    0,              # - t_start: counters are valid from this date to the next array entry
+                    $accounting_counters_init # - counters: hash ref of the accounting data    
+                ],
+                [
+                    OAR::Schedulers::GanttHoleStorage_with_quotas::get_infinity_value(),      # next array entry
+                    undef
+                ]
+            ]);
+}
+
+# Read configuration file
+sub read_conf_file($){
+    my ($quota_file) = @_;
+
+    my $msg;
+    my $Gantt_quotas;
+    # By default, no quota
+    $Gantt_quotas->{'*'}->{'*'}->{'*'}->{'*'} = -1;
+    if (open(QUOTAFILE, "< $quota_file")){
+        my $oldslurpmode = $/;
+        undef $/;
+        my $quota_file_content = <QUOTAFILE>;
+        $/ = $oldslurpmode;
+        close(QUOTAFILE);
+        eval($quota_file_content);
+        if ($@) {
+            $msg = "Syntax error in file $quota_file: $@";
+        }
+    }else{
+        $msg = "Cannot open  file $quota_file: $!";
+    }
+
+    return($Gantt_quotas, $msg);
+}
+
+# Return a string with the quota accounting data
+sub pretty_print($){
+    my ($accounting) = @_;
+   
+    my $str = "Accounting data for quotas:\n";
+    my $index = 0;
+    while ($index < $#{$accounting}){
+        my $step_ref = $accounting->[$index];
+        my $next_step_time = $accounting->[$index+1]->[0];
+        $str .= "  $index From $step_ref->[0](".strftime("%F %T",localtime($step_ref->[0])).") To $next_step_time(".strftime("%F %T",localtime($next_step_time))."):\n";
+        foreach my $i (sort(keys($step_ref->[1]))){
+            foreach my $j (sort(keys($step_ref->[1]->{$i}))){
+                foreach my $k (sort(keys($step_ref->[1]->{$i}->{$j}))){
+                    foreach my $l (sort(keys($step_ref->[1]->{$i}->{$j}->{$k}))){
+                        $str .= sprintf("    %16.16s > %16.16s > %10.10s > %10.10s = %i\n", $i, $j, $k, $l, $step_ref->[1]->{$i}->{$j}->{$k}->{$l});
+                    }
+                }
+            }
+        }
+        $index++;
+    }
+    return($str);
+}
+
+# Update accounting data
+sub update_accounting_counters($$$$$$$$){
+    my ( $accounting,
+         $nb_resources,
+         $date_start,
+         $duration,
+         $job_queue,
+         $job_project,
+         $job_types_arrayref,
+         $job_user) = @_;
+
+    #my $perf_time = [gettimeofday];
+    my $array_id_to_insert1 = -1;
+    my $array_id_to_insert2 = -1;
+    my $date_stop = $date_start + $duration + 1;
+    for (my $stackid = 0; $stackid < $#{$accounting}; $stackid++){
+        # Update existing slots with the new job
+        if (($accounting->[$stackid]->[0] >= $date_start) and ($accounting->[$stackid+1]->[0] <= $date_stop)){
+            update_accounting_slot_data($accounting->[$stackid]->[1], $job_queue, $job_project, $job_types_arrayref, $job_user, $nb_resources);
+        }
+        # Get the index of the first slot to add
+        if (($accounting->[$stackid]->[0] < $date_start) and ($accounting->[$stackid+1]->[0] > $date_start)){
+            $array_id_to_insert1 = $stackid;
+        }
+        # Get the index of the last slot to add
+        if (($accounting->[$stackid]->[0] < $date_stop) and ($accounting->[$stackid+1]->[0] > $date_stop)){
+            $array_id_to_insert2 = $stackid;
+            last();
+        }
+    }
+    # Add new slot (end of job)
+    if ($array_id_to_insert2 >= 0){
+        splice(@{$accounting}, $array_id_to_insert2 + 1, 0, [ $date_stop , dclone($accounting->[$array_id_to_insert2]->[1]) ]);
+        if ($accounting->[$array_id_to_insert2]->[0] >= $date_start){
+            update_accounting_slot_data($accounting->[$array_id_to_insert2]->[1], $job_queue, $job_project, $job_types_arrayref, $job_user, $nb_resources);
+        }
+    }
+    # Add new slot (start of job)
+    if ($array_id_to_insert1 >= 0){
+        splice(@{$accounting}, $array_id_to_insert1 + 1, 0, [ $date_start , dclone($accounting->[$array_id_to_insert1]->[1]) ]);
+        update_accounting_slot_data($accounting->[$array_id_to_insert1+1]->[1], $job_queue, $job_project, $job_types_arrayref, $job_user, $nb_resources);
+    }
+    #print("[QUOTA UPDATE ACCOUNTING] ".tv_interval($perf_time)."\n");
+}
+
+# Update the hash of a slot for accounting data
+# Internal function
+sub update_accounting_slot_data($$$$$$){
+    my ( $counter_hashref,
+         $queue,
+         $project,
+         $types_arrayref,
+         $user,
+         $nbresources) = @_;
+
+    foreach my $t (@{$types_arrayref},'*'){
+        $counter_hashref->{'*'}->{'*'}->{$t}->{'*'} += $nbresources;
+        $counter_hashref->{'*'}->{'*'}->{$t}->{$user} += $nbresources;
+        $counter_hashref->{'*'}->{$project}->{$t}->{'*'} += $nbresources;
+        $counter_hashref->{$queue}->{'*'}->{$t}->{'*'} += $nbresources;
+        $counter_hashref->{$queue}->{$project}->{$t}->{$user} += $nbresources;
+        $counter_hashref->{$queue}->{$project}->{$t}->{'*'} += $nbresources;
+        $counter_hashref->{$queue}->{'*'}->{$t}->{$user} += $nbresources;
+        $counter_hashref->{'*'}->{$project}->{$t}->{$user} += $nbresources;
+    }
+}
+
+# Check if the job fit the quotas and when
+# return the date when the quotas are satisfied
+sub check_quotas($$$$$$$$$$$){
+    my ( $accounting,
+         $gantt_quotas,
+         $current_time,
+         $gantt_hole_date_stop,
+         $gantt_next_hole_date_start,
+         $duration,
+         $job_queue,
+         $job_project,
+         $job_types_arrayref,
+         $job_user,
+         $nbresources_occupied_by_other_groups) = @_;
+    
+#    my $perf_time = [gettimeofday];
+    #pretty_print($accounting);
+    my $comment = "quota_ok";
+    my $qindex = 0;
+#    print("QUOTA CHECK INIT: From $current_time(".strftime("%F %T",localtime($current_time)).") Gantt_quotas{$job_queue}->{$job_project}->{@{$job_types_arrayref}}->{$job_user} >= $nbresources_occupied_by_other_groups ?\n");
+#    print(pretty_print($accounting));
+    
+    # Check if quotas are satisfied in this hole and when
+    while (($qindex < $#{$accounting})
+            and ($accounting->[$qindex]->[0] < $gantt_hole_date_stop) # quota beginning slot inside the hole
+            and ($current_time + $duration < $gantt_hole_date_stop) # quota slot not outside the hole
+            and ($accounting->[$qindex]->[0] - $current_time < $duration) # continue until the quota slot is big enough
+            and (($gantt_next_hole_date_start > 0) or ($gantt_next_hole_date_start > $current_time))
+          ){
+        if ($current_time < $accounting->[$qindex+1]->[0]){
+            # Check the whole quotas
+#            print("QUOTA CHECK: slot ($qindex) $accounting->[$qindex]->[0](".strftime("%F %T",localtime($accounting->[$qindex]->[0])).") --> $accounting->[$qindex+1]->[0](".strftime("%F %T",localtime($accounting->[$qindex+1]->[0])).")\n");
+            my $q_counter; my $p_counter; my $u_counter;
+            OUTER_LOOP:
+            foreach my $q (keys($gantt_quotas)){
+                if (($q eq $job_queue) or ($q eq '*') or ($q eq '/')){
+                    if ($q eq '/'){
+                        $q_counter = $job_queue;
+                    }else{
+                        $q_counter = $q;
+                    }
+                    foreach my $p (keys($gantt_quotas->{$q})){
+                        if (($p eq $job_project) or ($p eq '*') or ($p eq '/')){
+                            if ($p eq '/'){
+                                $p_counter = $job_project;
+                            }else{
+                                $p_counter = $p;
+                            }
+                            foreach my $t (keys($gantt_quotas->{$q}->{$p})){
+                                foreach my $job_t (@{$job_types_arrayref},'*'){
+                                    if ($t eq $job_t){
+                                        foreach my $u (keys($gantt_quotas->{$q}->{$p}->{$t})){
+                                            if (($u eq $job_user) or ($u eq '*') or ($u eq '/')){
+                                                if ($u eq '/'){
+                                                    $u_counter = $job_user;
+                                                }else{
+                                                    $u_counter = $u;
+                                                }
+                                                if ($gantt_quotas->{$q}->{$p}->{$t}->{$u} >= 0){
+                                                    # Get previous nb resources used by the previous group of the job
+                                                    my $tmp_account = $nbresources_occupied_by_other_groups;
+                                                    if (defined($accounting->[$qindex]->[1]->{$q_counter}->{$p_counter}->{$t}->{$u_counter})){
+                                                        # Add existing accounting data from the other jobs
+                                                        $tmp_account += $accounting->[$qindex]->[1]->{$q_counter}->{$p_counter}->{$t}->{$u_counter};
+                                                    }
+                                                    if ($gantt_quotas->{$q}->{$p}->{$t}->{$u} < $tmp_account){
+                                                        $comment = "quota_exceeded:Gantt_quotas->{$q}->{$p}->{$t}->{$u}=$tmp_account>$gantt_quotas->{$q}->{$p}->{$t}->{$u}";
+                                                        $current_time = $accounting->[$qindex+1]->[0];
+#                                                        print("QUOTA CHECK: $comment\n");
+                                                        last OUTER_LOOP;
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        $qindex++;
+#        print("QUOTA CHECK: Go to the next slot $qindex\n");
+    }
+#    print("QUOTA CHECK returns: $current_time(".strftime("%F %T",localtime($current_time)).")\n");
+#    print("[QUOTA CHECK PERF] ".tv_interval($perf_time)."\n");
+    return($current_time, $comment);
+}
+
+return 1;
-----------------------------------------------------------------------

Summary of changes:
 CHANGELOG                                          |    4 +
 Makefiles/server.mk                                |    1 +
 docs/documentation/doc_mechanisms.rst              |   21 ++
 sources/core/common-libs/lib/OAR/IO.pm             |    3 +-
 ...tt_with_timesharing_and_fairsharing_and_quotas} |  126 +++++++----
 .../core/modules/scheduler/scheduler_quotas.conf   |   60 +++++
 ...eStorage.pm => GanttHoleStorage_with_quotas.pm} |  134 ++++++++---
 .../core/server/lib/OAR/Schedulers/QuotaStorage.pm |  244 ++++++++++++++++++++
 8 files changed, 515 insertions(+), 78 deletions(-)
 copy sources/core/modules/scheduler/{oar_sched_gantt_with_timesharing_and_fairsharing => oar_sched_gantt_with_timesharing_and_fairsharing_and_quotas} (79%)
 create mode 100644 sources/core/modules/scheduler/scheduler_quotas.conf
 copy sources/core/server/lib/OAR/Schedulers/{GanttHoleStorage.pm => GanttHoleStorage_with_quotas.pm} (85%)
 create mode 100644 sources/core/server/lib/OAR/Schedulers/QuotaStorage.pm


hooks/post-receive
-- 
OAR



More information about the Oar-commits mailing list