.
require_once("../inc/submit_db.inc");
require_once("../inc/util.inc");
require_once("../inc/result.inc");
require_once("../inc/submit_util.inc");
require_once("../project/project.inc");
error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('display_startup_errors', true);
define("PAGE_SIZE", 20);
function state_count($batches, $state) {
$n = 0;
foreach ($batches as $batch) {
if ($batch->state == $state) $n++;
}
return $n;
}
function show_all_link($batches, $state, $limit, $user, $app) {
$n = state_count($batches, $state);
if ($n > $limit) {
if ($user) $userid = $user->id;
else $userid = 0;
if ($app) $appid = $app->id;
else $appid = 0;
echo "Showing the most recent $limit of $n batches.
Show all $n
";
}
}
function show_in_progress($batches, $limit, $user, $app) {
$first = true;
$n = 0;
foreach ($batches as $batch) {
if ($batch->state != BATCH_STATE_IN_PROGRESS) continue;
if ($limit && $n == $limit) break;
$n++;
if ($first) {
$first = false;
echo "
Batches in progress
\n";
if ($limit) {
show_all_link($batches, BATCH_STATE_IN_PROGRESS, $limit, $user, $app);
}
start_table();
table_header(
"Name",
"ID",
"User",
"App",
"# jobs",
"Progress",
"Submitted",
"Logical end time
Determines priority"
);
}
$pct_done = (int)($batch->fraction_done*100);
table_row(
"id>$batch->name",
"id>$batch->id",
$batch->user_name,
$batch->app_name,
$batch->njobs,
"$pct_done%",
local_time_str($batch->create_time),
local_time_str($batch->logical_end_time)
);
}
if ($first) {
echo "No in-progress batches.\n";
} else {
end_table();
}
}
function show_complete($batches, $limit, $user, $app) {
$first = true;
$n = 0;
foreach ($batches as $batch) {
if ($batch->state != BATCH_STATE_COMPLETE) continue;
if ($limit && $n == $limit) break;
$n++;
if ($first) {
$first = false;
echo "
Completed batches
\n";
if ($limit) {
show_all_link($batches, BATCH_STATE_COMPLETE, $limit, $user, $app);
}
start_table();
table_header("name", "ID", "user", "app", "# jobs", "submitted");
}
table_row(
"id>$batch->name",
"id>$batch->id",
$batch->user_name,
$batch->app_name,
$batch->njobs,
local_time_str($batch->create_time)
);
}
if ($first) {
echo "No completed batches.\n";
} else {
end_table();
}
}
function show_aborted($batches, $limit, $user, $app) {
$first = true;
$n = 0;
foreach ($batches as $batch) {
if ($batch->state != BATCH_STATE_ABORTED) continue;
if ($limit && $n == $limit) break;
$n++;
if ($first) {
$first = false;
echo "
Aborted batches
\n";
if ($limit) {
show_all_link($batches, BATCH_STATE_ABORTED, $limit, $user, $app);
}
start_table();
table_header("name", "ID", "user", "app", "# jobs", "submitted");
}
table_row(
"id>$batch->name",
"id>$batch->id",
$batch->user_name,
$batch->app_name,
$batch->njobs,
local_time_str($batch->create_time)
);
}
if (!$first) {
end_table();
}
}
// fill in the app and user names in list of batches
//
function fill_in_app_and_user_names(&$batches) {
foreach ($batches as $batch) {
//if ($batch->state < BATCH_STATE_COMPLETE || $batch->fraction_done < 1) {
// $wus = BoincWorkunit::enum("batch = $batch->id");
// $batch = get_batch_params($batch, $wus);
//}
$app = BoincApp::lookup_id($batch->app_id);
if ($app) {
$batch->app_name = $app->name;
} else {
$batch->app_name = "unknown";
}
$user = BoincUser::lookup_id($batch->user_id);
if ($user) {
$batch->user_name = $user->name;
} else {
$batch->user_name = "missing user $batch->user_id";
}
}
}
// show a set of batches
//
function show_batches($batches, $limit, $user, $app) {
fill_in_app_and_user_names($batches);
show_in_progress($batches, $limit, $user, $app);
show_complete($batches, $limit, $user, $app);
show_aborted($batches, $limit, $user, $app);
}
// the job submission "home page":
// show the user's in-progress and completed batches,
// and a button for creating a new batch
//
function handle_main($user) {
global $submit_urls;
$user_submit = BoincUserSubmit::lookup_userid($user->id);
if (!$user_submit) {
error_page("Ask the project admins for permission to submit jobs");
}
page_head("Job submission and control");
// show links to per-app job submission pages
//
echo "Submit jobs
";
foreach ($submit_urls as $appname=>$submit_url) {
$appname = BoincDb::escape_string($appname);
$app = BoincApp::lookup("name='$appname'");
if (!$app) error_page("bad submit_url name: $appname");
$usa = BoincUserSubmitApp::lookup("user_id=$user->id and app_id=$app->id");
if ($usa || $user_submit->submit_all) {
echo "- $app->user_friendly_name ";
}
}
echo "
\n";
// show links to admin pages if relevant
//
$usas = BoincUserSubmitApp::enum("user_id=$user->id");
$app_admin = false;
foreach ($usas as $usa) {
if ($usa->manage) {
$app_admin = true;
break;
}
}
if ($user_submit->manage_all || $app_admin) {
echo "Administrative functions
\n";
}
$batches = BoincBatch::enum("user_id = $user->id order by id desc");
show_batches($batches, PAGE_SIZE, $user, null);
page_tail();
}
function check_admin_access($user, $app_id) {
$user_submit = BoincUserSubmit::lookup_userid($user->id);
if (!$user_submit) error_page("no access");
if ($app_id) {
if (!$user_submit->manage_all) {
$usa = BoincUserSubmitApp::lookup("user_id = $user->id and app_id=$app_id");
if (!$usa) error_page("no access");
}
} else {
if (!$user_submit->manage_all) error_page("no access");
}
}
function handle_admin($user) {
$app_id = get_int("app_id");
check_admin_access($user, $app_id);
if ($app_id) {
$app = BoincApp::lookup_id($app_id);
if (!$app) error_page("no such app");
page_head("Administer batches for $app->user_friendly_name");
$batches = BoincBatch::enum("app_id = $app_id order by id desc");
show_batches($batches, PAGE_SIZE, null, $app);
} else {
page_head("Administer batches (all apps)");
$batches = BoincBatch::enum("true order by id desc");
show_batches($batches, PAGE_SIZE, null, null);
}
page_tail();
}
// show the details of an existing batch
//
function handle_query_batch($user) {
$batch_id = get_int('batch_id');
$batch = BoincBatch::lookup_id($batch_id);
$app = BoincApp::lookup_id($batch->app_id);
$wus = BoincWorkunit::enum("batch = $batch->id");
$batch = get_batch_params($batch, $wus);
page_head("Batch $batch_id");
start_table();
row2("name", $batch->name);
row2("application", $app->name);
row2("state", batch_state_string($batch->state));
row2("# jobs", $batch->njobs);
row2("# error jobs", $batch->nerror_jobs);
//row2("logical end time", time_str($batch->logical_end_time));
row2("expiration time", time_str($batch->expire_time));
row2("progress", sprintf("%.0f%%", $batch->fraction_done*100));
if ($batch->completion_time) {
row2("completed", local_time_str($batch->completion_time));
}
row2("GFLOP/hours, estimated", number_format(credit_to_gflop_hours($batch->credit_estimate), 2));
row2("GFLOP/hours, actual", number_format(credit_to_gflop_hours($batch->credit_canonical), 2));
row2("Output File Size (MB)", number_format(batch_output_file_size($batch->id)/1e6,2));
end_table();
if (batch_output_file_size($batch->id) <= 1e8) {
$url = boinc_get_output_files_url($user, $batch_id);
show_button($url, "Get zipped output files");
} else {
echo "
The output file size of this batch is too big, it will be uploaded by FTP
";
}
switch ($batch->state) {
case BATCH_STATE_IN_PROGRESS:
echo "
";
show_button(
"submit.php?action=abort_batch_confirm&batch_id=$batch_id",
"Abort batch"
);
break;
case BATCH_STATE_COMPLETE:
case BATCH_STATE_ABORTED:
echo "
";
show_button(
"submit.php?action=retire_batch_confirm&batch_id=$batch_id",
"Retire batch"
);
break;
}
echo "Jobs
\n";
start_table();
table_header(
"Job ID and name
click for details or to get output files",
"status",
"Canonical instance
click to see result page on BOINC server",
"Download Results"
);
foreach($wus as $wu) {
$resultid = $wu->canonical_resultid;
$durl = boinc_get_wu_output_files_url($user,$wu->id);
if ($resultid) {
$x = "$resultid";
$y = 'completed';
$text = " Download Result Files";
} else {
$x = "---";
$text = "---";
if ($batch->state == BATCH_STATE_COMPLETE) {
$y = 'failed';
} else {
$y = "in progress";
}
}
echo "
id>$wu->id · $wu->name |
$y |
$x |
$text |
";
}
end_table();
echo "Return to job control page\n";
page_tail();
}
// show the details of a job, including links to see the output files
//
function handle_query_job($user) {
$wuid = get_int('wuid');
$wu = BoincWorkunit::lookup_id($wuid);
if (!$wu) error_page("no such job");
page_head("Job $wuid");
echo "
Workunit details ·
batch>Batch $wu->batch
";
// show input files
//
echo "
Input files
\n";
$x = "".$wu->xml_doc."";
$x = simplexml_load_string($x);
start_table();
table_header("Logical name
(click to view)",
"Size (bytes)", "MD5"
);
$fanout = parse_config(get_config(), "");
foreach ($x->workunit->file_ref as $fr) {
$pname = (string)$fr->file_name;
$lname = (string)$fr->open_name;
$dir = filename_hash($pname, $fanout);
$path = "../../download/$dir/$pname";
$md5 = md5_file($path);
$s = stat($path);
$size = $s['size'];
table_row(
"$lname",
$size,
$md5
);
}
end_table();
echo "Instances
\n";
start_table();
table_header(
"Instance ID
click for result page",
"State", "Output files
click to view the file"
);
$results = BoincResult::enum("workunitid=$wuid");
foreach($results as $result) {
echo "
id>$result->id · $result->name |
".state_string($result)." |
";
$i = 0;
if ($result->server_state == 5) {
$names = get_outfile_names($result);
$fanout = parse_config(get_config(), "");
$i = 0;
foreach ($names as $name) {
$url = boinc_get_output_file_url($user, $result, $i++);
$upload_dir = parse_config(get_config(), "");
$path = dir_hier_path($name, $upload_dir, $fanout);
$s = stat($path);
$size = $s['size'];
echo "$name (".number_format($size)." bytes) ";
}
$i++;
}
echo " |
\n";
}
end_table();
echo "Return to job control page\n";
page_tail();
}
function handle_abort_batch_confirm() {
$batch_id = get_int('batch_id');
page_head("Confirm abort batch");
echo "
Aborting a batch will cancel all unstarted jobs.
Are you sure you want to do this?
";
show_button(
"submit.php?action=abort_batch&batch_id=$batch_id",
"Yes - abort batch"
);
echo "
Return to job control page\n";
page_tail();
}
function check_access($user, $batch) {
if ($user->id == $batch->user_id) return;
$user_submit = BoincUserSubmit::lookup_userid($user->id);
if ($user_submit->manage_all) return;
$usa = BoincUserSubmitApp::lookup("user_id=$user->id and app_id=$batch->app_id");
if ($usa->manage) return;
error_page("no access");
}
function handle_abort_batch($user) {
$batch_id = get_int('batch_id');
$batch = BoincBatch::lookup_id($batch_id);
if (!$batch) error_page("no such batch");
check_access($user, $batch);
abort_batch($batch);
page_head("Batch aborted");
echo "
Return to job control page\n";
page_tail();
}
function handle_retire_batch_confirm() {
$batch_id = get_int('batch_id');
page_head("Confirm retire batch");
echo "
Retiring a batch will remove all of its output files.
Are you sure you want to do this?
";
show_button(
"submit.php?action=retire_batch&batch_id=$batch_id",
"Yes - retire batch"
);
echo "
Return to job control page\n";
page_tail();
}
function handle_retire_batch($user) {
$batch_id = get_int('batch_id');
$batch = BoincBatch::lookup_id($batch_id);
if (!$batch) error_page("no such batch");
check_access($user, $batch);
retire_batch($batch);
page_head("Batch retired");
echo "
Return to job control page\n";
page_tail();
}
function show_batches_in_state($batches, $state) {
switch ($state) {
case BATCH_STATE_IN_PROGRESS:
page_head("Batches in progress");
show_in_progress($batches, 0, null, null);
break;
case BATCH_STATE_COMPLETE:
page_head("Completed batches");
show_complete($batches, 0, null, null);
break;
case BATCH_STATE_ABORTED:
page_head("Aborted batches");
show_aborted($batches, 0, null, null);
break;
}
page_tail();
}
function handle_show_all($user) {
$userid = get_int("userid");
$appid = get_int("appid");
$state = get_int("state");
if ($userid) {
// user looking at their own batches
//
if ($userid != $user->id) error_page("wrong user");
$batches = BoincBatch::enum("user_id = $user->id and state=$state order by id desc");
fill_in_app_and_user_names($batches);
show_batches_in_state($batches, $state);
} else {
// admin looking at batches
//
check_admin_access($user, $appid);
if ($appid) {
$app = BoincApp::lookup_id($appid);
if (!$app) error_page("no such app");
$batches = BoincBatch::enum("app_id = $appid and state=$state order by id desc");
} else {
$batches = BoincBatch::enum("state=$state order by id desc");
}
fill_in_app_and_user_names($batches);
show_batches_in_state($batches, $state);
}
}
$user = get_logged_in_user();
$action = get_str('action', true);
switch ($action) {
case '': handle_main($user); break;
case 'abort_batch': handle_abort_batch($user); break;
case 'abort_batch_confirm': handle_abort_batch_confirm(); break;
case 'admin': handle_admin($user); break;
case 'query_batch': handle_query_batch($user); break;
case 'query_job': handle_query_job($user); break;
case 'retire_batch': handle_retire_batch($user); break;
case 'retire_batch_confirm': handle_retire_batch_confirm(); break;
case 'show_all': handle_show_all($user); break;
default:
error_page("no such action $action");
}
?>