. // example of a web interface to remote job submission // // Although the architecture is intended to support remote portals, // this example must run on the BOINC project server. // In particular: // - It assumes cookie-based authentication, // as is done by the BOINC code. // You'll need to adapt/extend this considerably to run this // on a server other than the BOINC project server. // - It uses some functions from BOINC (page_head() etc.). // When you adapt this to your own purposes, // you can strip out this stuff if the web site doesn't use BOINC require_once("../inc/submit.inc"); require_once("../inc/common_defs.inc"); require_once("../inc/submit_db.inc"); require_once("../inc/submit_util.inc"); // needed for access control stuff require_once("../inc/util.inc"); require_once("../project/project.inc"); error_reporting(E_ALL); ini_set('display_errors', true); ini_set('display_startup_errors', true); // hardwired app name for now define('APP_NAME', 'remote_test'); $project = $master_url; // from project.inc $user = get_logged_in_user(); $auth = $user->authenticator; // 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() { global $project, $auth; $req = new StdClass; $req->project = $project; $req->authenticator = $auth; list($batches, $errmsg) = boinc_query_batches($req); if ($errmsg) error_page(htmlentities($errmsg)); page_head("Job submission and control"); echo " This is an example of a web interface for remote submission of BOINC jobs. It lets you
To use this, you must be logged in as a user with permission to submit jobs.
"; show_button("submit_example.php?action=create_form", "Create new batch"); $first = true; foreach ($batches as $batch) { if ($batch->state != BATCH_STATE_IN_PROGRESS) continue; if ($first) { $first = false; echo "
You have no in-progress batches.\n"; } else { end_table(); } $first = true; foreach ($batches as $batch) { if ($batch->state != BATCH_STATE_COMPLETE) continue; if ($first) { $first = false; echo "
You have no completed batches.\n"; } else { end_table(); } $first = true; foreach ($batches as $batch) { if ($batch->state != BATCH_STATE_ABORTED) continue; if ($first) { $first = false; echo "
Return to job control page\n"; page_tail(); } // return an array of the apps this user is allowed to submit jobs for // function eligible_apps() { global $user; $apps = BoincApp::enum("deprecated = 0"); $user_submit = BoincUserSubmit::lookup_userid($user->id); if (!$user_submit) return null; $a = array(); foreach($apps as $app) { if ($user_submit->submit_all) { $a[] = $app; } else { if (BoincUserSubmitApp::lookup("user_id=$user->id and app_id=$app->id")) { $a[] = $app; } } } return $a; } // return HTML for a popup menu of apps // function app_select($apps) { $x = "\n"; return $x; } // show a form for creating a batch. // function handle_create_form() { global $project, $auth; $apps = eligible_apps(); if (!$apps) error_page("You are not allowed to submit jobs"); page_head("Create batch"); echo " This form lets you specify a batch of jobs, and either submit it or get and estimate of its completion time.
The job runs an application that
\n"; echo "
Return to job control page\n"; page_tail(); } // build a request object for boinc_*_batch() from form variables // function form_to_request() { global $project, $auth; $input_url = get_str('input_url'); if (!$input_url) error_page("missing input URL"); $param_lo = (double)get_str('param_lo'); if ($param_lo<0 || $param_lo>60) error_page("param lo must be in 0..60"); $param_hi = (double)get_str('param_hi'); if ($param_hi<0 || $param_hi>60 || $param_hi <= $param_lo) { error_page("param hi must be in 0..60 and > param lo"); } $param_inc = (double)get_str('param_inc'); if ($param_inc < 1) error_page("param inc must be >= 1"); $req = new StdClass; $req->project = $project; $req->authenticator = $auth; $req->app_name = APP_NAME; $req->batch_name = get_str('batch_name'); $req->jobs = array(); $f = new StdClass; $f->source = $input_url; $f->mode = 'semilocal'; for ($x=$param_lo; $x<$param_hi; $x += $param_inc) { $job = new StdClass; $job->rsc_fpops_est = $x*1e9; $job->command_line = "--t $x"; $job->input_files = array($f); $req->jobs[] = $job; } return $req; } // create and submit a batch // function handle_create_action() { global $project, $auth; $get_estimate = get_str('get_estimate', true); if ($get_estimate) { $req = form_to_request($project, $auth); list($e, $errmsg) = boinc_estimate_batch($req); if ($errmsg) error_page(htmlentities($errmsg)); page_head("Batch estimate"); echo sprintf("Estimate: %.0f seconds", $e); echo "
Return to job control page\n"; page_tail(); } else { $req = form_to_request($project, $auth); list($id, $errmsg) = boinc_submit_batch($req); if ($errmsg) error_page(htmlentities($errmsg)); page_head("Batch submitted"); echo "Batch created, ID: $id\n"; echo "
Return to job control page\n";
page_tail();
}
}
// show the details of an existing batch
//
function handle_query_batch() {
global $project, $auth;
$req = (object)array(
'project' => $project,
'authenticator' => $auth,
'batch_id' => get_int('batch_id'),
);
list($batch, $errmsg) = boinc_query_batch($req);
if ($errmsg) error_page(htmlentities($errmsg));
page_head("Batch $req->batch_id");
start_table();
row2("name", $batch->name);
row2("application", $batch->app_name);
row2("state", batch_state_string($batch->state));
row2("# jobs", $batch->njobs);
row2("# error jobs", $batch->nerror_jobs);
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));
end_table();
$url = boinc_get_output_files($req);
show_button($url, "Get zipped output files");
switch ($batch->state) {
case BATCH_STATE_IN_PROGRESS:
echo "
";
show_button(
"submit_example.php?action=abort_batch_confirm&batch_id=$req->batch_id",
"Abort batch"
);
break;
case BATCH_STATE_COMPLETE:
case BATCH_STATE_ABORTED:
echo "
";
show_button(
"submit_example.php?action=retire_batch_confirm&batch_id=$req->batch_id",
"Retire batch"
);
break;
}
echo "
click for details or to get output files
", "status", "Canonical instanceclick to see result page on BOINC server
" ); foreach($batch->jobs as $job) { $id = (int)$job->id; $resultid = (int)$job->canonical_instance_id; if ($resultid) { $x = "$resultid"; $y = "completed"; } else { $x = "---"; $y = "in progress"; } 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() { global $project, $auth; $req = new StdClass; $req->project = $project; $req->authenticator = $auth; $req->job_id = get_int('job_id'); list($reply, $errmsg) = boinc_query_job($req); if ($errmsg) error_page(htmlentities($errmsg)); page_head("Job $req->job_id"); echo "job_id>View workunit page on BOINC server\n"; echo "
click for result page on BOINC server
", "State", "Output files" ); foreach($reply->instances as $inst) { 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_example.php?action=abort_batch&batch_id=$batch_id", "Yes - abort batch" ); echo "
Return to job control page\n"; page_tail(); } function handle_abort_batch() { global $project, $auth; $req->project = $project; $req->authenticator = $auth; $req->batch_id = get_int('batch_id'); $errmsg = boinc_abort_batch($req); if ($errmsg) error_page(htmlentities($errmsg)); 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_example.php?action=retire_batch&batch_id=$batch_id", "Yes - retire batch" ); echo "
Return to job control page\n"; page_tail(); } function handle_retire_batch() { global $project, $auth; $req->project = $project; $req->authenticator = $auth; $req->batch_id = get_int('batch_id'); $errmsg = boinc_retire_batch($req); if ($errmsg) error_page(htmlentities($errmsg)); page_head("Batch retired"); echo "
Return to job control page\n"; page_tail(); } $action = get_str('action', true); switch ($action) { case '': handle_main(); break; case 'abort_batch': handle_abort_batch(); break; case 'abort_batch_confirm': handle_abort_batch_confirm(); break; case 'create_action': handle_create_action(); break; case 'create_form': handle_create_form(); break; case 'query_batch': handle_query_batch(); break; case 'query_job': handle_query_job(); break; case 'retire_batch': handle_retire_batch(); break; case 'retire_batch_confirm': handle_retire_batch_confirm(); break; default: error_page('no such action'); } ?>