Skip to content
Permalink
1808c31c05
Switch branches/tags

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?
Go to file
 
 
Cannot retrieve contributors at this time
executable file 978 lines (813 sloc) 27.2 KB
#! /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('&nbsp;'));
}
$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('&nbsp;|&nbsp;',@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 $_='&lt;null&gt;' 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('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'),$q->td('&nbsp;'));
} keys %{$dead_hosts};
$out.=$q->Tr($q->td({colspan=>16},'&nbsp;'));
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('&nbsp;'),
$q->td('&nbsp;'),
$q->td('&nbsp;'),
$q->td({class=>'number'},$S{daemon_slots_running}),
$q->td('&nbsp;'),
$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('&nbsp;'),
$q->td(a({href=>selfurl('/active_jobs')},'active_jobs')),$q->td('&nbsp;'),
$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>';
}