package Boinc; ###################################################################### # DISCLAIMER ###################################################################### # This module depends on ARC0mod.pm which is obsolete and deprecated # starting from ARC 6.0 # Please DO NOT build new LRMS modules based on this one but follow # the indications in # LRMSInfo.pm # instead. ###################################################################### use strict; use DBI; use POSIX qw(ceil floor); use Sys::Hostname; our @ISA = ('Exporter'); our @EXPORT_OK = ('cluster_info', 'queue_info', 'jobs_info', 'users_info'); use LogUtils ( 'start_logging', 'error', 'warning', 'debug' ); ########################################## # Saved private variables ########################################## our (%lrms_queue); our $running = undef; # total running jobs in a queue # the queue passed in the latest call to queue_info, jobs_info or users_info my $currentqueue = undef; # Resets queue-specific global variables if # the queue has changed since the last call sub init_globals($) { my $qname = shift; if (not defined $currentqueue or $currentqueue ne $qname) { $currentqueue = $qname; %lrms_queue = (); $running = undef; } } ########################################## # Private subs ########################################## sub db_conn($){ my $config=shift; my $DB_HOST=$$config{boinc_db_host}; my $DB_PORT=$$config{boinc_db_port}; my $DB_NAME=$$config{boinc_db_name}; my $DB_USER=$$config{boinc_db_user}; my $DB_PASS=$$config{boinc_db_pass}; my $dbh = DBI->connect("DBI:mysql:$DB_NAME;host=$DB_HOST:$DB_PORT","$DB_USER","$DB_PASS",{RaiseError=>1}); return $dbh; } sub get_total_cpus($){ # Total number of hosts that finished jobs recently my $config=shift; my $dbh = db_conn($config); my $where = ""; if (defined($$config{boinc_app_id})) { $where = " and appid=$$config{boinc_app_id}"; } my $sth = $dbh->prepare('select count(distinct hostid) from result where server_state=4'.$where); $sth->execute(); my $result = $sth->fetchrow_array(); if(defined($result)){ # Return a reasonable number to allow bootstrapping projects if($result < 100) {$result = 100;} return $result; } return 0; } sub get_max_cpus($){ # Total number of hosts with running and finished jobs my $config=shift; my $dbh = db_conn($config); my $where = ""; if (defined($$config{boinc_app_id})) { $where = " and appid=$$config{boinc_app_id}"; } my $sth = $dbh->prepare('select count(distinct hostid) from result where server_state=4'.$where); $sth->execute(); my $result = $sth->fetchrow_array(); if(defined($result)){return $result;} else{ return 0;} } sub get_jobs_in_que($){ # Unsent jobs on BOINC server my $config=shift; my $dbh = db_conn($config); my $where = ""; if (defined($$config{boinc_app_id})) { $where = " and appid=$$config{boinc_app_id}"; } my $sth = $dbh->prepare('select count(*) from result where server_state in (1,2)'.$where); $sth->execute(); my $result = $sth->fetchrow_array(); if(defined($result)){return $result;} else{ return 0;} } sub get_jobs_in_run($){ # Jobs in progress my $config=shift; my $dbh = db_conn($config); my $where = ""; if (defined($$config{boinc_app_id})) { $where = " and appid=$$config{boinc_app_id}"; } my $sth = $dbh->prepare('select count(*) from result where server_state=4'.$where); $sth->execute(); my $result = $sth->fetchrow_array(); if(defined($result)){return $result;} else{ return 0;} } sub get_jobs_status($){ # Convert BOINC status into ARC LRMS state for each job my $config=shift; my $dbh = db_conn($config); my $where = ""; if (defined($$config{boinc_app_id})) { $where = " and appid=$$config{boinc_app_id}"; } my $sth = $dbh->prepare('SELECT result.server_state, result.name, user.name FROM result LEFT JOIN user ON result.userid=user.id WHERE server_state in (1,2,4)'.$where); $sth->execute(); my (%jobs_status, @result,$job_status, $job_state, $job_name, $user_name); while(($job_state, $job_name, $user_name) = $sth->fetchrow_array()) { $job_status="Q"; my @tmp=split(/_/,$job_name); $job_name=$tmp[0]; if($job_state==4){$job_status="R";} # Strip out non-ascii $user_name =~ s/[^[:ascii:]]/_/g if $user_name; $jobs_status{$job_name}=[$job_status, $user_name]; } return \%jobs_status; } ############################################ # Public subs ############################################# sub cluster_info ($) { my ($config) = shift; my (%lrms_cluster); $lrms_cluster{lrms_type} = "boinc"; $lrms_cluster{lrms_version} = "1"; # only enforcing per-process cputime limit $lrms_cluster{has_total_cputime_limit} = 0; my ($total_cpus) = get_total_cpus($config); my ($max_cpus) = get_max_cpus($config); $lrms_cluster{totalcpus} = $total_cpus; $lrms_cluster{cpudistribution} = $lrms_cluster{totalcpus}."cpu:1"; my $que_jobs = get_jobs_in_que($config); my $run_jobs = get_jobs_in_run($config); $lrms_cluster{usedcpus} = $run_jobs; $lrms_cluster{runningjobs} = $lrms_cluster{usedcpus}; $lrms_cluster{queuedcpus} = $max_cpus-$total_cpus; $lrms_cluster{queuedjobs} = $que_jobs; $lrms_cluster{queue} = [ ]; return %lrms_cluster; } sub queue_info ($$) { my ($config) = shift; my ($qname) = shift; init_globals($qname); my ($total_cpus) = get_total_cpus($config); my ($max_cpus) = get_max_cpus($config); my $que_jobs = get_jobs_in_que($config); my $running = get_jobs_in_run($config); if (defined $running) { # job_info was already called, we know exactly how many grid jobs # are running $lrms_queue{running} = $running; } else { # assuming that the submitted grid jobs are cpu hogs, approximate # the number of running jobs with the number of running processes $lrms_queue{running}= 0; } $lrms_queue{totalcpus} = $total_cpus; $lrms_queue{status} = $lrms_queue{totalcpus}-$lrms_queue{running}; # reserve negative numbers for error states # Fork is not real LRMS, and cannot be in error state if ($lrms_queue{status}<0) { debug("lrms_queue{status} = $lrms_queue{status}"); $lrms_queue{status} = 0; } my $job_limit; $job_limit = 1000; $lrms_queue{maxrunning} = $job_limit; $lrms_queue{maxuserrun} = $job_limit; $lrms_queue{maxqueuable} = $job_limit; $lrms_queue{maxcputime} = ""; $lrms_queue{queued} = $que_jobs; $lrms_queue{mincputime} = ""; $lrms_queue{defaultcput} = ""; $lrms_queue{minwalltime} = ""; $lrms_queue{defaultwallt} = ""; $lrms_queue{maxwalltime} = $lrms_queue{maxcputime}; return %lrms_queue; } sub jobs_info ($$@) { my ($config) = shift; my ($qname) = shift; my ($jids) = shift; init_globals($qname); my (%lrms_jobs,$jstatus); $jstatus=get_jobs_status($config); foreach my $id (@$jids){ # Real hostname will be published when job finishes my $host = 'unknown@unknown'; if ($$jstatus{$id} and $$jstatus{$id}[1]) { $host = $$jstatus{$id}[1].'@unknown'; } $lrms_jobs{$id}{nodes} = [ $host ]; $lrms_jobs{$id}{mem} = 2000000000; $lrms_jobs{$id}{walltime} = ""; $lrms_jobs{$id}{cputime} = ""; $lrms_jobs{$id}{comment} = [ "LRMS: Running under boinc" ]; $lrms_jobs{$id}{reqwalltime} = ""; $lrms_jobs{$id}{reqcputime} = ""; $lrms_jobs{$id}{rank} = "0"; # Fix cores to 1 since volunteers download 1 task per core $lrms_jobs{$id}{cpus} = "1"; if(! exists $$jstatus{$id}) { $lrms_jobs{$id}{status} = "O"; } elsif($$jstatus{$id}[0] eq "R") { $lrms_jobs{$id}{status} = "R"; } elsif($$jstatus{$id}[0] eq "Q") { $lrms_jobs{$id}{status} = "Q"; } else { $lrms_jobs{$id}{status} = "O"; } } return %lrms_jobs; } sub users_info($$@) { my ($config) = shift; my ($qname) = shift; my ($accts) = shift; init_globals($qname); my (%lrms_users); # freecpus # queue length if ( ! exists $lrms_queue{status} ) { %lrms_queue = queue_info( $config, $qname ); } foreach my $u ( @{$accts} ) { $lrms_users{$u}{freecpus} = $lrms_queue{maxuserrun} - $lrms_queue{running}; $lrms_users{$u}{queuelength} = "$lrms_queue{queued}"; } return %lrms_users; } 1;