package ARC0mod; ###################################################################### # DISCLAIMER ###################################################################### # This module is obsolete and deprecated starting from ARC 6.0 # and all the modules depending on it are candidates for deprecation. # Please DO NOT build new LRMS modules based on this one but follow # the indications in # LRMSInfo.pm # instead. ###################################################################### # # Loads ARC0.6 LRMS modules for use with ARC1 # # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # !!!! WARNING: DO *NOT* DEVELOP NEW MODULES BASED ON WHAT FOLLOWS !!! # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # THIS MODULE IS MAINTANED TO RUN LEGACY CODE. # THE INFORMATION BELOW IS KEPT FOR MAINTENANCE REFERENCE. # THIS MODULE WILL BE DEPRECATED. # ALL THE MODULES BASED ON THIS WILL BE REMOVED IN THE FUTURE. # IF YOU PLAN TO DEVELOP A NEW PERL LRMS MODULE, # READ THE DISCLAIMER ABOVE. # # !!! LEAGACY DO NOT USE FOR FUTURE DEVELOPMENTS - SEE DISCLAIMER !!!! # To include a new (ARC 0.6) LRMS plugin: # # 1. Each LRMS specific module needs to provide subroutines # cluster_info, queue_info, jobs_info, and users_info. # # 2. References to subroutines defined in new LRMS modules are added # to the select_lrms subroutine in this module, and the module reference # itself, naturally. # # NB: ARC0 modules use minutes for time units. ARC1 modules use seconds. # # !!! LEAGACY DO NOT USE FOR FUTURE DEVELOPMENTS - SEE DISCLAIMER !!!! require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(get_lrms_info get_lrms_options_schema); use LogUtils; use strict; our $log = LogUtils->getLogger(__PACKAGE__); our %modnames = ( PBS => "PBS", PBSPRO => "PBSPRO", SGE => "SGE", LL => "LL", LSF => "LSF", CONDOR => "Condor", SLURM => "SLURM", BOINC => "Boinc", FORK => "Fork" ); # Whether the module implements support for listing nodes. our $has_nodes = 1; # input: lrmsname, loglevel sub load_lrms($$) { my $lrms_name = uc(shift); my $loglevel = uc(shift); my $module = $modnames{$lrms_name}; $log->error("No ARC0 module for $lrms_name") unless $module; eval { require "$module.pm" }; $log->error("Failed to load LRMS module $module: $@") if $@; import $module qw(cluster_info queue_info jobs_info users_info); eval { import $module qw(nodes_info) }; if ($@) { $log->debug("LRMS module $module does not export 'nodes_info'"); $has_nodes=0; } $LogUtils::default_logger = LogUtils->getLogger($module); $LogUtils::loglevel = $loglevel; } # Just generic options, cannot assume anything LRMS specific here sub get_lrms_options_schema { return { 'lrms' => '', # name of the LRMS module 'queues' => { # queue names are keys in this hash '*' => { 'users' => [ '' ] # list of user IDs to query in the LRMS } }, 'jobs' => [ '' ], # list of jobs IDs to query in the LRMS 'controldir' => '*', # path to controldir, taken from main config 'loglevel' => '' # infoproviders loglevel } } sub get_lrms_info($) { my $options = shift; my %cluster_config = %$options; delete $cluster_config{queues}; delete $cluster_config{jobs}; my $lrms_info = {cluster => {}, queues => {}, jobs => {}}; my $cluster_info = { cluster_info(\%cluster_config) }; delete $cluster_info->{queue}; $lrms_info->{cluster} = delete_empty($cluster_info); $lrms_info->{nodes} = { nodes_info(\%cluster_config) } if $has_nodes; for my $qname ( keys %{$options->{queues}} ) { my %queue_config = (%cluster_config, %{$options->{queues}{$qname}}); delete $queue_config{users}; my $jids = $options->{jobs}; # TODO: interface change: jobs under each queue my $jobs_info = { jobs_info(\%queue_config, $qname, $jids) }; for my $job ( values %$jobs_info ) { $job->{status} ||= 'EXECUTED'; delete_empty($job); } $lrms_info->{jobs} = { %{$lrms_info->{jobs}}, %$jobs_info }; my $queue_info = { queue_info(\%queue_config, $qname) }; $lrms_info->{queues}{$qname} = delete_empty($queue_info); my $users = $options->{queues}{$qname}{users}; $queue_info->{users} = { users_info(\%queue_config, $qname, $users) }; for my $user ( values %{$queue_info->{users}} ) { my $freecpus = $user->{freecpus}; $user->{freecpus} = split_freecpus($freecpus) if defined $freecpus; delete_empty($user); } $queue_info->{acl_users} = $queue_config{acl_users} if defined $queue_config{acl_users}; } # ARC0 LRMS plugins use minutes. Convert to seconds here. for my $queue (values %{$lrms_info->{queues}}) { $queue->{minwalltime} = int 60*$queue->{minwalltime} if $queue->{minwalltime}; $queue->{mincputime} = int 60*$queue->{mincputime} if $queue->{mincputime}; $queue->{maxwalltime} = int 60*$queue->{maxwalltime} if $queue->{maxwalltime}; $queue->{maxcputime} = int 60*$queue->{maxcputime} if $queue->{maxcputime}; $queue->{defaultwallt} = int 60*$queue->{defaultwallt} if $queue->{defaultwallt}; $queue->{defaultcput} = int 60*$queue->{defaultcput} if $queue->{defaultcput}; } for my $job (values %{$lrms_info->{jobs}}) { $job->{reqcputime} = int 60*$job->{reqcputime} if $job->{reqcputime}; $job->{reqwalltime} = int 60*$job->{reqwalltime} if $job->{reqwalltime}; $job->{cputime} = int 60*$job->{cputime} if $job->{cputime}; $job->{walltime} = int 60*$job->{walltime} if $job->{walltime}; delete $job->{nodes} unless ( defined $job->{nodes} && @{$job->{nodes}} ) ; delete $job->{comment} unless (defined $job->{comment} && @{$job->{comment}} ); } return $lrms_info; } sub delete_empty($) { my $hashref = shift; foreach my $k ( keys %$hashref) { delete $hashref->{$k} if ! defined $hashref->{$k} || $hashref->{$k} eq ''; } return $hashref; } # Convert frecpus string into a hash. # Example: "6 11:2880 23:1440" --> { 6 => 0, 11 => 2880, 23 => 1440 } # OBS: Assuming the function cpu vs. time is monotone, this transformation is safe. sub split_freecpus($) { my $freecpus_string = shift; my $freecpus_hash = {}; for my $countsecs (split ' ', $freecpus_string) { if ($countsecs =~ /^(\d+)(?::(\d+))?$/) { $freecpus_hash->{$1} = $2 || 0; # 0 means unlimited } else { $log->warning("Bad freecpus string: $freecpus_string"); return {}; } } return $freecpus_hash; } 1;