Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
mxq/web/pages/mxq/mxq.in
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
executable file
978 lines (813 sloc)
27.2 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/perl | |
use strict; | |
use warnings; | |
use LWP; | |
use CGI qw(:standard); | |
use CGI::Carp qw(fatalsToBrowser); | |
use DBI; | |
our %GROUP_STATUS_NAME; # ( 0=>'OK',...) | |
our %JOB_STATUS_NAME; | |
our %DAEMON_STATUS_NAME; | |
our $dbh; | |
our $hostconfig; | |
our $q; | |
our $LIMIT=100000; # max rows | |
sub STYLE { | |
return <<'EOF'; | |
<style> | |
body { background:#ddd; } | |
div.dumper {border: 2px solid black; border-radius: 5px; margin: 10px 0; padding: 0 10px;background: #faa; } | |
table.jobs, table.jobs th,table.jobs td {font-family:monospace; border: 1px solid black; border-collapse: collapse;} | |
table.groups, table.groups th,table.groups td {font-family:monospace; border: 1px solid black; border-collapse: collapse;} | |
table.server, table.server th,table.server td {font-family:monospace; border: 1px solid black; border-collapse: collapse;} | |
td.number { text-align: right; } | |
tr.bg0 { background: #F0F0F0; } | |
tr.bg1 { background: #E0E0E0; } | |
</style> | |
EOF | |
} | |
sub HEAD { | |
return "<!DOCTYPE html><html>\n<head>\n".STYLE()."</head>\n"; | |
} | |
sub selfurl { | |
my ($path_info)=@_; | |
return $q->url().$path_info; | |
} | |
sub my_url { | |
my ($module,$params)=@_; | |
my $uri=new URI(selfurl("/$module")); | |
$uri->query_form($params,';'); | |
return $uri; | |
} | |
sub hostconfig_init { | |
open my $in,'<','/etc/hostconfig' or die "/etc/hostconfig: $!\n"; | |
while (<$in>) { | |
s/#.*//; | |
chomp; | |
my ($host,$attr,$value)=split ' ',$_,3; | |
$host or next; | |
$attr or next; | |
$attr eq 'mxqd' and $hostconfig->{$host}=1; | |
} | |
} | |
sub db_init { | |
open my $test,"<@MXQ_MYSQL_RO_DEFAULT_FILE@" or die "@MXQ_MYSQL_RO_DEFAULT_FILE@: $!\n"; | |
$dbh= DBI->connect('DBI:mysql:mysql_read_default_file=@MXQ_MYSQL_RO_DEFAULT_FILE@',undef,undef, { RaiseError => 1, AutoCommit => 1 }); | |
# mxq_group.h | |
register_group_status( | |
OK => 0, | |
CANCELLED => 99, | |
); | |
# mxq_job.h | |
register_job_status( | |
INQ => 0, | |
ASSIGNED => 100, | |
LOADED => 150, | |
RUNNING => 200, | |
UNKNOWN_RUN => 250, | |
EXTRUNNING => 300, | |
STOPPED => 350, | |
EXIT => 1024, | |
KILLING => 399, | |
KILLED => 400, | |
FAILED => 750, | |
UNKNOWN_PRE => 755, | |
CANCELLING => 989, | |
CANCELLED => 990, | |
UNKNOWN => 999, | |
FINISHED => 1000, | |
); | |
# mxq_daemon.h | |
register_daemon_status( | |
IDLE => 0, # /* no jobs are running */ | |
RUNNING => 10, # /* some jobs are running */ | |
WAITING => 20, # /* waiting for more slots to return */ | |
FULL => 30, # /* all slots are running/occupied */ | |
BACKFILL => 40, # /* more slots are running */ | |
CPUOPTIMAL => 50, # /* all cpus are running */ | |
TERMINATING => 200, # /* server is going down */ | |
EXITED => 250, # /* server exited */ | |
CRASHED => 255, # /* server exited with unknown status */ | |
); | |
} | |
sub register_status { | |
my ($hashref)=shift; | |
while (my ($name,$value)=splice(@_,0,2)) { | |
$hashref->{$value}=$name; | |
} | |
} | |
sub get_status { | |
my ($hashref,$in)=@_; | |
return $hashref->{$in}||sprintf('UNKNOWN(%d)',$in); | |
} | |
sub register_group_status { register_status(\%GROUP_STATUS_NAME,@_) } | |
sub register_job_status { register_status(\%JOB_STATUS_NAME,@_) } | |
sub register_daemon_status { register_status(\%DAEMON_STATUS_NAME,@_) } | |
sub group_status { return get_status(\%GROUP_STATUS_NAME,@_) } | |
sub job_status { return get_status(\%JOB_STATUS_NAME,@_) } | |
sub daemon_status { return get_status(\%DAEMON_STATUS_NAME,@_) } | |
sub humanSeconds { | |
my ($seconds)=@_; | |
$seconds<60 and return sprintf ('%2ds',$seconds); | |
$seconds<3600 and return sprintf ('%2dm %2ds',$seconds/60,$seconds%60); | |
$seconds<86400 and return sprintf ('%2dh %2dm',$seconds/3600,$seconds%3600/60); | |
return sprintf('%2dd %2dh',$seconds/86400,$seconds%86400/3600); | |
} | |
sub size { | |
# ' 0 B ' | |
# ' 1023 B ' | |
# ' 1.0 KiB' | |
# '1023.9 MiB' | |
# ' 1.0 GiB' | |
my ($s) = @_; | |
$s < 1024 and return sprintf('%6d B ', $s); | |
for my $unit (qw(KiB MiB GiB TiB PiB EiB ZiB YiB)) { | |
$s /= 1024; | |
$s < 1024 and return sprintf ('%6.1f %s', $s, $unit); | |
} | |
return sprintf('%6.1f %s', $s, 'YiB'); | |
} | |
sub days { | |
my $s = shift; | |
my $n = shift; | |
if ($s == 0) { | |
return '0 secs'; | |
} | |
my @T=(); | |
my $t = int($s/(60*60*24)); | |
push @T, ($t > 0) ? $t : 0; # days | |
$s -= $t*(60*60*24); | |
$t = int($s/(60*60)); | |
push @T, ($t > 0) ? $t : 0; # hrs | |
$s -= $t*(60*60); | |
$t = int($s/(60)); | |
push @T, ($t > 0) ? $t : 0; # min | |
$s -= $t*60; | |
$t = int($s); | |
push @T, ($t > 0) ? $t : 0; # sec | |
my @L; | |
for my $x ('days','hrs','min','secs') { | |
my $y = shift @T; | |
if ($y != 0) { | |
if (defined($n)) { | |
$n--; | |
last if ($n < 0); | |
} | |
push @L,"$y $x"; | |
} | |
} | |
return join(' ',@L); | |
} | |
sub split_cmd { | |
my ($in)=@_; | |
$in=~s/\\0/ /g; | |
return $in; | |
} | |
sub colorize { | |
my ($color,$html) = @_; | |
return $html if ($html eq "0"); # zeros remain black | |
return "<font color=\"$color\">$html</font>"; | |
} | |
sub colorize_status { | |
my ($status)=@_; | |
return colorize("green", $status) if $status =~ m/FINISHED/; | |
return colorize("orange",$status) if $status =~ m/RUNNING/; | |
return colorize("blue", $status) if $status =~ m/INQ/; | |
return colorize("red", $status) if $status =~ m/KILL|FAIL/; | |
return $status; | |
} | |
sub DataDumper { | |
use Data::Dumper; | |
$Data::Dumper::Indent=0; | |
return $q->div({class=>'dumper'},escapeHTML(Dumper(@_))); | |
} | |
sub job_table_pending { | |
my ($sql_clause,@bind_args)=@_; | |
my $out; | |
my @cols=qw(job_id user_name date_submit job_threads); | |
my $sth=$dbh->prepare('SELECT '.join(',',@cols).' FROM mxq_job,mxq_group WHERE mxq_job.group_id=mxq_group.group_id AND job_status=0 ORDER BY job_status,date_submit'); | |
$sth->execute(@bind_args); | |
$out.='<table class="jobs">'; | |
$out.=$q->Tr($q->th(\@cols)); | |
my $cnt=0; | |
while (my $row=$sth->fetchrow_arrayref()) { | |
my ($job_id,$user_name,$date_submit,$job_threads)=@$row; | |
$out.=$q->Tr( | |
$q->td({class=>'number'},$q->a({href=>selfurl("/job/$job_id")},$job_id)), | |
$q->td($user_name), | |
$q->td($date_submit), | |
$q->td({class=>'number'},$job_threads), | |
); | |
++$cnt == $LIMIT and last; | |
} | |
$out.='</table>'; | |
if ($cnt==$LIMIT) { | |
$out.=$q->p($q->em("(truncated after $LIMIT rows)")); | |
} | |
return $out; | |
} | |
sub job_table_running { | |
my ($sql_clause,@bind_args)=@_; | |
my $out; | |
my @cols=qw(job_id user_name date_start host_hostname host_pid host_slots); | |
my $sth=$dbh->prepare('SELECT '.join(',',@cols).' FROM mxq_job,mxq_group WHERE mxq_job.group_id=mxq_group.group_id AND job_status=200 ORDER BY job_status,date_start'); | |
$sth->execute(@bind_args); | |
$out.='<table class="jobs">'; | |
$out.=$q->Tr($q->th(\@cols)); | |
while (my $row=$sth->fetchrow_arrayref()) { | |
my ($job_id,$user_name,$date_start,$host_hostname,$host_pid,$host_slots)=@$row; | |
$out.=$q->Tr( | |
$q->td({class=>'number'},$q->a({href=>selfurl("/job/$job_id")},$job_id)), | |
$q->td($user_name), | |
$q->td($date_start), | |
$q->td($host_hostname), | |
$q->td({class=>'number'},$host_pid), | |
$q->td({class=>'number'},$host_slots), | |
); | |
} | |
$out.='</table>'; | |
} | |
sub job_table_of_group { | |
my ($group_id,$job_status_filter)=@_; | |
my $out; | |
my @scols=qw(job_id job_status qtime rtime host_hostname host_pid); | |
my $jobfilter = defined($job_status_filter) ? "AND mxq_job.job_status=$job_status_filter" : ''; | |
my $sth=$dbh->prepare(<<"__EOF__"); | |
SELECT | |
job_id, | |
job_status, | |
date_submit, | |
date_start, | |
date_end, | |
host_hostname, | |
host_pid, | |
timestampdiff(SECOND,date_submit,date_start), | |
timestampdiff(SECOND,date_submit,now()), | |
timestampdiff(SECOND,date_start,date_end), | |
timestampdiff(SECOND,date_start,now()), | |
UNIX_TIMESTAMP(date_submit) as ds | |
FROM mxq_job,mxq_group | |
WHERE mxq_job.group_id=mxq_group.group_id AND mxq_job.group_id=? | |
$jobfilter | |
ORDER BY job_id DESC | |
__EOF__ | |
$sth->execute($group_id); | |
$out.='<table class="jobs">'; | |
$out.=$q->Tr($q->th(\@scols)); | |
my $lds; | |
my $col=1; | |
my $cnt=0; | |
while (my $row=$sth->fetchrow_arrayref()) { | |
my ($job_id,$job_status,$date_submit,$date_start,$date_end,$host_hostname,$host_pid, | |
$qtime,$qtime_now,$rtime,$rtime_now,$ds)=@$row; | |
if (defined($qtime)) { | |
$qtime = humanSeconds($qtime); | |
} else { | |
$qtime = colorize("gray",humanSeconds($qtime_now)); | |
} | |
if (defined($rtime)) { | |
$rtime = humanSeconds($rtime); | |
} else { | |
$rtime = colorize("gray",humanSeconds($rtime_now)); | |
} | |
my $date_info = "submit: $date_submit\nstart: $date_start\nend: $date_end"; | |
if (defined $lds && abs($lds-$ds)>10) { | |
$col = 1-$col; | |
$out.=$q->Tr($q->td(' ')); | |
} | |
$lds=$ds; | |
$out.=$q->Tr( | |
{class=>'bg'.$col}, | |
$q->td({class=>'number'},$q->a({href=>selfurl("/job/$job_id")},$job_id)), | |
$q->td(colorize_status(job_status($job_status))), | |
$q->td("<span title=\"$date_info\">$qtime</span>"), | |
$q->td("<span title=\"$date_info\">$rtime</span>"), | |
$q->td($host_hostname), | |
$q->td({class=>'number'},$host_pid), | |
); | |
++$cnt == $LIMIT and last; | |
} | |
$out.='</table>'; | |
if ($cnt==$LIMIT) { | |
$out.=$q->p($q->em("(truncated after $LIMIT rows)")); | |
} | |
return $out; | |
} | |
sub job_status_filter_buttons { | |
my $group_id = shift; | |
my @r=(); | |
push @r,$q->a({href=>selfurl("/group/$group_id")},'ALL'); | |
for my $js ( 0, 200, 1000, 750, 400 ) { | |
push @r,$q->a({href=>selfurl("/group/$group_id/$js")},job_status($js)); | |
} | |
return '<p>'.join(' | ',@r).'</p>'; | |
} | |
sub group_details_raw { | |
my ($o)=@_; | |
my $group_name=escapeHTML($o->{'group_name'}); | |
my $job_command=escapeHTML($o->{'job_command'}); | |
my $group_status_text=group_status($o->{'group_status'}); | |
my $job_tmpdir_size = $o->{'job_tmpdir_size'} ? "$o->{'job_tmpdir_size'} GiB" : '-'; | |
return <<"__EOF__" | |
<pre> | |
group_name : $group_name | |
group_status : $group_status_text | |
group_flags : $o->{group_flags} | |
group_priority : $o->{group_priority} | |
group_blacklist: $o->{group_blacklist} | |
group_whitelist: $o->{group_whitelist} | |
prerequisites : $o->{prerequisites} | |
tags : $o->{tags} | |
user_uid : $o->{user_uid} | |
user_name : $o->{user_name} | |
user_gid : $o->{user_gid} | |
user_group : $o->{user_group} | |
job_command : $job_command | |
job_threads : $o->{job_threads} | |
job_memory : $o->{job_memory} MiB | |
job_time : $o->{job_time} minutes | |
job_gpu : $o->{job_gpu} | |
job_tmpdir_size : $job_tmpdir_size | |
job_max_per_node : $o->{job_max_per_node} | |
group_jobs : $o->{group_jobs} | |
group_jobs_inq : $o->{group_jobs_inq} | |
group_jobs_running : $o->{group_jobs_running} | |
group_jobs_finished : $o->{group_jobs_finished} | |
group_jobs_failed : $o->{group_jobs_failed} | |
group_jobs_cancelled : $o->{group_jobs_cancelled} | |
group_jobs_unknown : $o->{group_jobs_unknown} | |
group_jobs_restarted : $o->{group_jobs_restarted} | |
group_slots_running: : $o->{group_slots_running} | |
group_mtime : $o->{group_mtime} | |
group_date_end : $o->{group_date_end} | |
stats_max_sumrss : $o->{stats_max_sumrss} kiB | |
stats_max_maxrss : $o->{stats_max_maxrss} | |
stats_max_utime_sec : $o->{stats_max_utime_sec} | |
stats_max_stime_sec : $o->{stats_max_stime_sec} | |
stats_max_real_sec : $o->{stats_max_real_sec} | |
stats_total_utime_sec : $o->{stats_total_utime_sec} | |
stats_total_stime_sec : $o->{stats_total_stime_sec} | |
stats_total_real_sec : $o->{stats_total_real_sec} | |
stats_total_wait_sec : $o->{stats_total_wait_sec} | |
stats_wait_sec : $o->{stats_wait_sec} | |
stats_run_sec : $o->{stats_run_sec} | |
stats_idle_sec : $o->{stats_idle_sec} | |
stats_total_utime_sec_finished : $o->{stats_total_utime_sec_finished} | |
stats_total_stime_sec_finished : $o->{stats_total_stime_sec_finished} | |
stats_total_real_sec_finished : $o->{stats_total_real_sec_finished} | |
stats_total_wait_sec_finished : $o->{stats_total_wait_sec_finished} | |
</pre> | |
__EOF__ | |
} | |
sub group_details_raw_by_group_id { | |
my ($group_id)=@_; | |
my $sth=$dbh->prepare('SELECT * FROM mxq_group WHERE group_id=? LIMIT 1',undef); | |
$sth->execute($group_id); | |
my $o=$sth->fetchrow_hashref('NAME_lc'); | |
return group_details_raw($o); | |
} | |
sub group_details { | |
my ($group_id)=@_; | |
$dbh or db_init(); | |
my $out=''; | |
my $sth=$dbh->prepare('SELECT * FROM mxq_group WHERE group_id=? LIMIT 1',undef); | |
$sth->execute($group_id); | |
my %o=%{$sth->fetchrow_hashref('NAME_lc')}; | |
my $group_status_text=group_status($o{'group_status'}); | |
my $group_name=escapeHTML($o{group_name}); | |
my $job_command=escapeHTML($o{job_command}); | |
$out.=<<"__EOF__"; | |
<h3>Summary</h3> | |
<pre> | |
$o{user_name} submitted | |
$job_command | |
in group "$group_name", the group status is $group_status_text. | |
</pre> | |
<span style="cursor:pointer;" onclick="document.getElementById('group_details').style.display='block';document.getElementById('an').style.display='none'; document.getElementById('zu').style.display='block'" id="an" style="display: block"> | |
<h3 style="color:blue;">[show group details ...]</h3> | |
</span> | |
<span onclick="document.getElementById('group_details').style.display='none'; document.getElementById('an').style.display='block';document.getElementById('zu').style.display='none'" id="zu" style="display: none;cursor:pointer"> | |
<h3 style="color:blue;">[hide group details]</h3> | |
</span> | |
<div id="group_details" style="display: none"> | |
__EOF__ | |
$out.=group_details_raw(\%o); | |
$out.=<<'__EOF__'; | |
</div> | |
__EOF__ | |
return $out; | |
} | |
sub group { | |
my ($group_id,$job_status_filter)=@_; | |
my $out=h1('MXQ Group '.$group_id); | |
$out.=group_details($group_id); | |
my $what = defined($job_status_filter) ? job_status($job_status_filter) . ' jobs' : 'Jobs'; | |
$out.=h2($what . ' of this group ').job_status_filter_buttons($group_id).job_table_of_group($group_id,$job_status_filter); | |
return $out; | |
} | |
sub server_detail { | |
my ($daemon_id) = @_; | |
$dbh or db_init(); | |
my $out=h1('MXQ DAEMON '.$daemon_id); | |
my @cols=qw( | |
daemon_id daemon_name | |
status hostname mxq_version boot_id pid_starttime | |
daemon_pid daemon_slots daemon_memory daemon_maxtime | |
daemon_memory_limit_slot_soft daemon_memory_limit_slot_hard | |
daemon_jobs_running daemon_slots_running | |
daemon_threads_running daemon_memory_used | |
mtime daemon_start daemon_stop daemon_flags tags prerequisites | |
daemon_gpus_max daemon_gpus_used | |
); | |
my $sth=$dbh->prepare('SELECT '.join(',',@cols).' FROM mxq_daemon WHERE daemon_id=? LIMIT 1', undef); | |
$sth->execute($daemon_id); | |
my %o=%{$sth->fetchrow_hashref('NAME_lc')}; | |
$out.=<<"EOF"; | |
<pre> | |
daemon_id : $o{daemon_id} | |
daemon_name : $o{daemon_name} | |
status : $o{status} | |
hostname : $o{hostname} | |
mxq_version : $o{mxq_version} | |
boot_id ; $o{boot_id} | |
pid_starttime : $o{pid_starttime} | |
daemon_pid : $o{daemon_pid} | |
daemon_slots : $o{daemon_slots} | |
daemon_memory : $o{daemon_memory} | |
daemon_maxtime : $o{daemon_maxtime} | |
daemon_gpus_max: $o{daemon_gpus_max} | |
daemon_memory_limit_slot_soft : $o{daemon_memory_limit_slot_soft} | |
daemon_memory_limit_slot_hard : $o{daemon_memory_limit_slot_hard} | |
daemon_jobs_running : $o{daemon_jobs_running} | |
daemon_slots_running : $o{daemon_slots_running} | |
daemon_threads_running : $o{daemon_threads_running} | |
daemon_memory_used : $o{daemon_memory_used} | |
daemon_gpus_used : $o{daemon_gpus_used} | |
mtime : $o{mtime} | |
daemon_start : $o{daemon_start} | |
daemon_stop : $o{daemon_stop} | |
daemon_flags : $o{daemon_flags} | |
tags : $o{tags} | |
prerequisites : $o{prerequisites} | |
</pre> | |
EOF | |
} | |
sub job { | |
my ($job_id)=@_; | |
my $out=h1('MXQ JOB '.$job_id); | |
$dbh or db_init(); | |
my $sth=$dbh->prepare('SELECT *,timestampdiff(MINUTE,date_start,now()) as t,timestampdiff(MINUTE,date_start,date_end) as t2 FROM mxq_job WHERE job_id=? LIMIT 1',undef); | |
$sth->execute($job_id); | |
my %o=%{$sth->fetchrow_hashref('NAME_lc')}; | |
my $job_status_text=job_status($o{'job_status'}); | |
my $job_umask_text=sprintf('%03O',$o{job_umask}); | |
my $link_group_id=a({href=>selfurl("/group/$o{group_id}")},$o{group_id}); | |
my $job_argv=escapeHTML(split_cmd($o{job_argv})); | |
my $job_workdir=escapeHTML($o{job_workdir}); | |
my $job_stdout=escapeHTML($o{job_stdout}); | |
my $job_stderr=escapeHTML($o{job_stderr}); | |
my $ago = $job_status_text eq 'RUNNING' && defined $o{t} ? "($o{t} minutes ago)" : ''; | |
my $rt = defined $o{t2} ? "($o{t2} minutes runtime)" : ''; | |
defined $_ or $_='<null>' for values %o; | |
$out.=<<"EOF"; | |
<pre> | |
job_status : $job_status_text | |
job_flags : $o{job_flags} | |
job_priority : $o{job_priority} | |
group_id : $link_group_id | |
job_workdir : $job_workdir | |
job_argc : $o{job_argc} | |
job_argv : $job_argv | |
job_stdout : $job_stdout | |
job_stderr : $job_stderr | |
job_umask: : $job_umask_text | |
host_submit : $o{host_submit} | |
server_id : $o{server_id} | |
host_id : $o{host_id} | |
host_hostname : $o{host_hostname} | |
host_pid : $o{host_pid} | |
host_slots : $o{host_slots} | |
host_cpu_set : $o{host_cpu_set} | |
date_submit : $o{date_submit} | |
date_start : $o{date_start} $ago | |
date_end : $o{date_end} $rt | |
stats_max_sumrss : $o{stats_max_sumrss} kiB | |
stats_status : $o{stats_status} | |
stats_utime_sec : $o{stats_utime_sec} | |
stats_utime_usec : $o{stats_utime_usec} | |
stats_stime_sec : $o{stats_stime_sec} | |
stats_stime_usec : $o{stats_stime_usec} | |
stats_real_sec : $o{stats_real_sec} | |
stats_real_usec : $o{stats_real_usec} | |
stats_maxrss : $o{stats_maxrss} | |
stats_minflt : $o{stats_minflt} | |
stats_majflt : $o{stats_majflt} | |
stats_nswap : $o{stats_nswap} | |
stats_inblock : $o{stats_inblock} | |
stats_oublock : $o{stats_oublock} | |
stats_nvcsw : $o{stats_nvcsw} | |
stats_nivcsw : $o{stats_nivcsw} | |
</pre> | |
EOF | |
$out .= "<h2>group details</h2>"; | |
$out.=group_details_raw_by_group_id($o{group_id}); | |
return $out; | |
} | |
sub group_table { | |
my ($sql_clause,$sql_binds,$days)=@_; | |
$sql_clause||='true'; | |
my $out; | |
my (@cols,@head); | |
$out .= '<h2>Active Groups</h2>'; | |
@cols=qw( | |
group_id group_name job_threads | |
job_memory | |
job_time | |
job_tmpdir_size | |
job_gpu | |
user_name | |
group_status | |
group_jobs | |
group_jobs_inq group_jobs_running | |
group_jobs_finished group_jobs_failed group_jobs_cancelled group_jobs_unknown | |
); | |
@head=qw( | |
group<br>id | |
group<br>name | |
req.<br>threads | |
req.<br>memory | |
req.<br>runtime | |
req.<br>tmpdir | |
req.<br>gpu | |
user<br>name | |
group<br>status | |
jobs | |
inq | |
running | |
finished | |
failed | |
cancelled | |
unknown | |
); | |
my $sth=$dbh->prepare('SELECT '.join(',',@cols).' FROM mxq_group WHERE '.$sql_clause.' AND (group_jobs_running>0 OR group_jobs_inq>0) ORDER BY group_id DESC'); | |
$sth->execute(@$sql_binds); | |
$out.='<table class="groups">'; | |
$out.=$q->Tr($q->th(\@head)); | |
while (my $row=$sth->fetchrow_arrayref()) { | |
my ($group_id,$group_name,$job_threads, | |
$job_memory,$job_time,$job_tmpdir_size,$job_gpu, | |
$user_name,$group_status, | |
$group_jobs,$group_jobs_inq,$group_jobs_running,$group_jobs_finished,$group_jobs_failed,$group_jobs_cancelled,$group_jobs_unknown | |
)=@$row; | |
$out.=$q->Tr( | |
$q->td({class=>'number'},$q->a({href=>selfurl("/group/$group_id")},$group_id)), | |
$q->td($group_name), | |
$q->td({class=>'number'},$job_threads), | |
$q->td({class=>'number'},size($job_memory*1048576)), | |
$q->td({class=>'number'},days($job_time*60)), | |
$q->td({class=>'number'}, $job_tmpdir_size ? size($job_tmpdir_size*1073741824) : '-'), | |
$q->td({class=>'number'}, $job_gpu ? $job_gpu : ""), | |
$q->td($q->a({href=>my_url('groups',{user_name=>$user_name})},$user_name)), | |
$q->td(group_status($group_status)), | |
$q->td({class=>'number'},$group_jobs), | |
$q->td({class=>'number'},colorize("blue",$group_jobs_inq)), | |
$q->td({class=>'number'},colorize("orange",$group_jobs_running)), | |
$q->td({class=>'number'},colorize("green",$group_jobs_finished)), | |
$q->td({class=>'number'},colorize("red",$group_jobs_failed)), | |
$q->td({class=>'number'},colorize("red",$group_jobs_cancelled)), | |
$q->td({class=>'number'},colorize("red",$group_jobs_unknown)) | |
); | |
} | |
$out.='</table>'; | |
$out .= "<h2>Finished Groups (last $days days) </h2>"; | |
@cols=qw( | |
group_id group_name job_threads | |
job_memory | |
stats_max_sumrss | |
job_time | |
stats_run_sec | |
stats_idle_sec | |
user_name | |
group_status | |
group_jobs | |
group_jobs_finished group_jobs_failed group_jobs_cancelled group_jobs_unknown | |
); | |
@head=qw( | |
group<br>id | |
group<br>name | |
req.<br>threads | |
req.<br>memory | |
used<br>memory | |
req.<br>runtime | |
used<br>runtime | |
user<br>name | |
group<br>status | |
jobs | |
fin | |
fail | |
canc | |
unkn | |
); | |
$sth=$dbh->prepare('SELECT '.join(',',@cols).' FROM mxq_group WHERE '.$sql_clause.' AND (group_jobs_running=0 AND group_jobs_inq=0) AND group_mtime >= DATE_SUB(NOW(),INTERVAL ? DAY) ORDER BY group_id DESC'); | |
$sth->execute(@$sql_binds,$days); | |
$out.='<table class="groups">'; | |
$out.=$q->Tr($q->th(\@head)); | |
while (my $row=$sth->fetchrow_arrayref()) { | |
my ($group_id,$group_name,$job_threads, | |
$job_memory, | |
$stats_max_sumrss, | |
$job_time, | |
$stats_run_sec, | |
$stats_idle_sec, | |
$user_name,$group_status, | |
$group_jobs,$group_jobs_finished,$group_jobs_failed,$group_jobs_cancelled,$group_jobs_unknown | |
)=@$row; | |
$out.=$q->Tr( | |
$q->td({class=>'number'},$q->a({href=>selfurl("/group/$group_id")},$group_id)), | |
$q->td($group_name), | |
$q->td({class=>'number'},$job_threads), | |
$q->td({class=>'number'},size($job_memory*1048576)), | |
$q->td({class=>'number'},size($stats_max_sumrss*1024)), | |
$q->td({class=>'number'},humanSeconds($job_time*60)), | |
$q->td({class=>'number'},humanSeconds($stats_run_sec+$stats_idle_sec,2)), | |
$q->td($q->a({href=>my_url('groups',{user_name=>$user_name})},$user_name)), | |
$q->td(group_status($group_status)), | |
$q->td({class=>'number'},$group_jobs), | |
$q->td({class=>'number'},colorize("green",$group_jobs_finished)), | |
$q->td({class=>'number'},colorize("red",$group_jobs_failed)), | |
$q->td({class=>'number'},colorize("red",$group_jobs_cancelled)), | |
$q->td({class=>'number'},colorize("red",$group_jobs_unknown)) | |
); | |
} | |
$out.='</table>'; | |
return $out; | |
} | |
sub groups { | |
my @sql_clauses; | |
my @sql_binds; | |
my $user_name_info=''; | |
if ($q->param('user_name')) { | |
push @sql_clauses,'user_name = ?'; | |
push @sql_binds,$q->param('user_name'); | |
$user_name_info=' submitted by '. $q->param('user_name'); | |
} | |
my $days = 7; | |
if ($q->param('days')) { | |
if ( $q->param('days') =~ /^\d+$/ ) { | |
$days = $q->param('days'); | |
} | |
} | |
my $out=h1("MXQ Groups$user_name_info"); | |
$dbh or db_init(); | |
$out.=group_table(join('AND',@sql_clauses),\@sql_binds,$days); | |
return $out; | |
} | |
sub active_jobs() { | |
$dbh or db_init(); | |
return h1('MXQ Running Jobs').job_table_running().h1('MXQ Pending Jobs').job_table_pending(); | |
} | |
sub server() { | |
$dbh or db_init(); | |
$hostconfig or hostconfig_init(); | |
my $dead_hosts = $hostconfig; | |
my $out=h1('MXQ Servers'); | |
my @cols=qw( | |
daemon_id daemon_name status hostname mxq_version boot_id pid_starttime | |
daemon_pid daemon_slots daemon_memory daemon_maxtime | |
daemon_memory_limit_slot_soft daemon_memory_limit_slot_hard | |
daemon_jobs_running daemon_slots_running | |
daemon_threads_running daemon_memory_used | |
mtime daemon_start daemon_stop | |
daemon_gpus_max daemon_gpus_used | |
); | |
my $sth=$dbh->prepare('SELECT '.join(',',@cols).' FROM mxq_daemon WHERE status<=200 ORDER BY hostname,daemon_name'); | |
$sth->execute(); | |
$out.='<table class="server">'; | |
$out.=$q->Tr($q->th([ | |
'id', | |
'name', | |
'status', | |
'host', | |
'version', | |
# 'boot_id', | |
# 'pid_starttime', | |
'pid', | |
'slots', | |
'memory', | |
'GPUs', | |
'max.<br>time', | |
# 'time', | |
'slotmem_soft<br>available', | |
# 'slotmem_hard', | |
'jobs<br>in use', | |
'slots<br>in use', | |
'threads<br>in use', | |
'memory<br>in use', | |
'GPUs<br>in use', | |
# 'mtime', | |
# 'start', | |
# 'stop', | |
])); | |
my %S; | |
while (my $row=$sth->fetchrow_arrayref()) { | |
my ( | |
$daemon_id,$daemon_name,$status,$hostname,$mxq_version,$boot_id,$pid_starttime, | |
$daemon_pid,$daemon_slots,$daemon_memory,$daemon_maxtime, | |
$daemon_memory_limit_slot_soft,$daemon_memory_limit_slot_hard, | |
$daemon_jobs_running,$daemon_slots_running, | |
$daemon_threads_running,$daemon_memory_used, | |
$mtime,$daemon_start,$daemon_stop, | |
$daemon_gpus_max, $daemon_gpus_used | |
) = @$row; | |
$hostname =~s/\.molgen\.mpg\.de$//; | |
$S{daemon_slots} += $daemon_slots; | |
$S{daemon_slots_dist}->{$daemon_slots}++; | |
$S{daemon_slots_running} += $daemon_slots_running; | |
$S{daemon_memory_sum} += $daemon_memory; | |
$S{daemon_memory_used_sum} += $daemon_memory_used; | |
$S{daemon_gpus_max_sum} += $daemon_gpus_max; | |
$S{daemon_gpus_used_sum} += $daemon_gpus_used; | |
$S{servers}++; | |
delete($dead_hosts->{$hostname}); | |
$out.=$q->Tr( | |
$q->td({class=>'number'},$q->a({href=>selfurl("/server/$daemon_id")},$daemon_id)), | |
$q->td($daemon_name), | |
$q->td(daemon_status($status)), | |
$q->td($hostname), | |
$q->td($mxq_version), | |
# $q->td($boot_id), | |
# $q->td($pid_starttime), | |
$q->td({class=>'number'},$daemon_pid), | |
$q->td({class=>'number'},$daemon_slots), | |
$q->td({class=>'number'},size($daemon_memory*1048576)), | |
$q->td({class=>'number'},$daemon_gpus_max), | |
$q->td({class=>'number'},$daemon_maxtime ? $daemon_maxtime : ''), | |
$q->td({class=>'number'},size($daemon_memory_limit_slot_soft*1048576)), | |
# $q->td({class=>'number'},$daemon_memory_limit_slot_hard), | |
$q->td({class=>'number'},$daemon_jobs_running), | |
$q->td({class=>'number'},$daemon_slots_running), | |
$q->td({class=>'number'},$daemon_threads_running), | |
$q->td({class=>'number'},size($daemon_memory_used*1048576)), | |
$q->td({class=>'number'},$daemon_gpus_used), | |
# $q->td($mtime), | |
# $q->td($daemon_start), | |
# $q->td($daemon_stop), | |
); | |
} | |
map { | |
$out.=$q->Tr( $q->td(0),$q->td('-'),$q->td('no mxqd'),$q->td($_),$q->td(' '),$q->td(' '),$q->td(' '),$q->td(' '),$q->td(' '),$q->td(' '),$q->td(' '),$q->td(' '),$q->td(' '),$q->td(' '),$q->td(' '),$q->td(' ')); | |
} keys %{$dead_hosts}; | |
$out.=$q->Tr($q->td({colspan=>16},' ')); | |
my $dist = join(', ',map {"$S{daemon_slots_dist}->{$_}x$_"} sort {$b <=> $a} keys %{$S{daemon_slots_dist}}); | |
$out.=$q->Tr( | |
$q->td({class=>'center', colspan=>3},$S{servers}.' servers'), | |
$q->td($dist), | |
$q->td({class=>'center', colspan=>3},$S{daemon_slots}.' cores'), | |
$q->td({class=>'number'},size($S{daemon_memory_sum}*1048576)), | |
$q->td({class=>'number'},$S{daemon_gpus_max_sum}), | |
$q->td(' '), | |
$q->td(' '), | |
$q->td(' '), | |
$q->td({class=>'number'},$S{daemon_slots_running}), | |
$q->td(' '), | |
$q->td({class=>'number'},size($S{daemon_memory_used_sum}*1048576)), | |
$q->td({class=>'number'},$S{daemon_gpus_used_sum}) | |
); | |
$out.='</table>'; | |
return $out; | |
} | |
sub top_menu { | |
return '<table class="menu">' . $q->Tr( | |
$q->td(a({href=>selfurl('/groups')},'groups')),$q->td(' '), | |
$q->td(a({href=>selfurl('/active_jobs')},'active_jobs')),$q->td(' '), | |
$q->td(a({href=>selfurl('/server')},'server')) | |
).'</table>'; | |
} | |
$q=new CGI; | |
my $path_info=$q->path_info()||''; | |
#print header(); | |
#print "path info: $path_info<br>"; | |
#print "url : ".$q->url()."<br>"; | |
#print "url rel: ".$q->url(-relative=>1)."<br>"; | |
#print "xx ",selfurl('/groups').'<br>'; | |
#exit; | |
$path_info=~s/\/$//; | |
if ($path_info eq '') { | |
print header().HEAD().top_menu().groups(); | |
} elsif ($path_info eq '/server') { | |
print header().HEAD().top_menu().server(); | |
} elsif ($path_info eq '/groups') { | |
print header().HEAD().top_menu().groups(); | |
} elsif ($path_info eq '/active_jobs') { | |
print header().HEAD().top_menu().active_jobs(); | |
} elsif ($path_info =~ /\/group\/(\d+)$/) { | |
print header().HEAD().top_menu().group($1); | |
} elsif ($path_info =~ /\/group\/(\d+)\/(\d+)$/) { | |
print header().HEAD().top_menu().group($1,$2); | |
} elsif ($path_info =~ /\/job\/(\d+)$/) { | |
print header().HEAD().top_menu().job($1); | |
} elsif ($path_info =~ /\/server\/(\d+)$/) { | |
print header().HEAD().top_menu().server_detail($1); | |
} else { | |
print header(-status => 404).HEAD().top_menu().'<h1>not found</h1>'; | |
} |