- server: added a mechanism for submitting jobs as:

> boinc_submit --infile foo --outfile blah program --args
    This will run "program" on a remote host,
    with the given input, output files, and command-line args

    It manages everything for you.
    You don't have to worry about apps, app_versions,
    template files, wrapper job files, etc.
    See: http://boinc.berkeley.edu/trac/wiki/SingleJob

    Initial checkin - not debugged yet.

svn path=/trunk/boinc/; revision=14842
This commit is contained in:
David Anderson 2008-03-04 23:50:38 +00:00
parent d6ec0b2b5b
commit b20f2d915c
11 changed files with 630 additions and 15 deletions

View File

@ -1837,3 +1837,30 @@ David Mar 4 2008
html/inc
friend.inc
David Mar 4 2008
- server: added a mechanism for submitting jobs as:
> boinc_submit --infile foo --outfile blah program --args
This will run "program" on a remote host,
with the given input, output files, and command-line args
It manages everything for you.
You don't have to worry about apps, app_versions,
template files, wrapper job files, etc.
See: http://boinc.berkeley.edu/trac/wiki/SingleJob
Initial checkin - not debugged yet.
html/
inc/
boinc_db.inc
ops/
single_job_setup (new)
sched/
Makefile.am
feeder.C
single_job_assimilator.C (new)
tools/
backend_lib.C
boinc_submit (new)
create_work.C

View File

@ -1,10 +1,12 @@
<?php
$x = $_SERVER['PHP_SELF'];
$path = "/tmp/php_pids/".getmypid();
$f = fopen($path, "w");
fwrite($f, $x);
fclose($f);
if (0) {
$x = $_SERVER['PHP_SELF'];
$path = "/tmp/php_pids/".getmypid();
$f = fopen($path, "w");
fwrite($f, $x);
fclose($f);
}
function search_form() {
echo "

View File

@ -47,7 +47,6 @@ function show_detail($v) {
$path = "dl/$file";
$url = version_url($v);
$dlink = "<a href=$url>$file</a>";
//$md = md5_file($path);
$s = number_format(filesize($path)/1000000, 2);
$date = $v["date"];
$type = type_text($v["type"]);
@ -56,7 +55,6 @@ function show_detail($v) {
dl_item("File (click to download)", "$dlink ($s MB)");
dl_item("Version number", $num);
dl_item("Release date", $date);
//dl_item("MD5 checksum of download file", $md);
list_end();
}
@ -70,7 +68,6 @@ function show_version_xml($v, $p) {
$path = "dl/$file";
$url = version_url($v);
$dlink = "<a href=$url>$file</a>";
//$md = md5_file($path);
$s = number_format(filesize($path)/1000000, 2);
$date = $v["date"];
$type = type_text($v["type"]);
@ -87,7 +84,6 @@ function show_version_xml($v, $p) {
<installer>$type</installer>
</version>
";
// <md5>$md</md5>
}
function show_version($pname, $i, $v) {

View File

@ -184,6 +184,12 @@ class BoincWorkunit {
$db = BoincDb::get();
return $db->lookup_id($id, 'workunit', 'BoincWorkunit');
}
static function insert($clause) {
$db = BoincDb::get();
$ret = $db->insert('workunit', $clause);
if (!$ret) return $ret;
return $db->insert_id();
}
}
class BoincApp {
@ -191,10 +197,18 @@ class BoincApp {
$db = BoincDb::get();
return $db->lookup_id($id, 'app', 'BoincApp');
}
static function lookup($clause) {
$db = BoincDb::get();
return $db->lookup('app', 'BoincApp', $clause);
}
static function enum($clause) {
$db = BoincDb::get();
return $db->enum('app', 'BoincApp', $clause);
}
static function insert($clause) {
$db = BoincDb::get();
return $db->insert('app', $clause);
}
}
class BoincAppVersion {

128
html/ops/single_job_setup Executable file
View File

@ -0,0 +1,128 @@
#! /usr/bin/env php
<?
// configure a BOINC server to run single jobs;
// see http://boinc.berkeley.edu/trac/wiki/SingleJob
//
// Run this from project home dir.
// usage: html/ops/single_job_setup path-to-boinc_samples
$platform = 'i686-pc-linux-gnu';
function error($x) {
echo "$x\n";
exit(1);
}
function check_dirs() {
if (!file_exists('config.xml')) {
error("Run this from project home dir");
}
}
function usage() {
error("Usage: single_job_setup path-to-boinc_samples");
}
function get_includes() {
$c = getcwd();
chdir('html/ops');
require_once('../inc/boinc_db.inc');
BoincDb::get();
chdir($c);
}
if ($argc != 2) usage();
$boinc_samples_dir = $argv[1];
// check for existence of wrapper, get its checksum
$wrapper_filename = "$boinc_samples_dir/wrapper/wrapper";
if (!file_exists($wrapper_filename)) {
echo "$wrapper_filename doesn't exist.\n";
error("Make sure you've built boinc_samples.");
}
$wrapper_md5 = md5_file($wrapper_filename);
if (!$wrapper_md5) {
error("Can't read wrapper");
}
echo "MD5 of $wrapper_filename is $wrapper_md5\n";
get_includes();
$app_name = "single_job_$platform";
$app = BoincApp::lookup("name='$app_name'");
if (!$app) {
$now = time();
$retval = BoincApp::insert("(create_time, name, user_friendly_name) values ($now, '$app_name','Jobs for $platform')");
if (!$retval) {
error("Couldn't add application");
}
}
// create apps/appname
$app_dir = "apps/$app_name";
if (!is_dir($app_dir)) {
if (!mkdir($app_dir)) {
error("Couldn't make app dir");
}
}
// check for apps/appname/appname_platform_N,
// find the largest such N; see if have new wrapper
// If needed, create new version, copy wrapper
$i = 0;
$latest_i = -1;
$have_latest_wrapper = false;
while (1) {
$app_dir = "apps/$app_name/".$app_name."_1.".$i."_$platform";
echo "checking $app_dir\n";
if (!file_exists($app_dir)) break;
$latest_i = $i;
$i++;
}
if ($latest_i >= 0) {
$i = $latest_i;
$app_dir = "apps/$app_name/".$app_name."_1.".$i."_$platform";
$file = "$app_dir/".$app_name."_1.".$i."_$platform";
$latest_md5 = md5_file($file);
if ($latest_md5 == $wrapper_md5) {
$have_latest_wrapper = true;
echo "App version is current.\n";
} else {
echo "$latest_md5 != $wrapper_md5\n";
}
}
if (!$have_latest_wrapper) {
$i = $latest_i + 1;
$app_dir = "apps/$app_name/".$app_name."_1.".$i."_$platform";
$file = "$app_dir/".$app_name."_1.".$i."_$platform";
if (!mkdir($app_dir)) {
error("Couldn't created dir: $app_dir");
}
if (!copy($wrapper_filename, $file)) {
error("Couldn't copy $wrapper_filename to $file");
}
chmod($file, 0750);
echo "- type 'update_versions', and answer 'y' to all questions.\n";
}
$config = file_get_contents('config.xml');
if (!strstr($config, "single_job_assimilator")) {
echo "- Add the following to the <daemons> section of config.xml:\n
<daemon>
<cmd>single_job_assimilator -app $app_name</cmd>
<output>single_job_assimilator_$platform.out</output>
<pid>single_job_assimilator_$platform.pid</pid>
</daemon>
Then restart your project by typing
bin/stop
bin/start
";
}
?>

View File

@ -20,6 +20,7 @@ noinst_PROGRAMS = \
sample_bitwise_validator \
sample_trivial_validator \
sample_work_generator \
single_job_assimilator \
sched_driver \
send_file \
show_shmem \
@ -130,6 +131,9 @@ sample_dummy_assimilator_DEPENDENCIES = $(LIB_SCHED)
sample_assimilator_SOURCES = assimilator.C sample_assimilator.C validate_util.C validate_util.h
sample_assimilator_DEPENDENCIES = $(LIB_SCHED)
single_job_assimilator_SOURCES = assimilator.C single_job_assimilator.C validate_util.C validate_util.h
single_job_assimilator_DEPENDENCIES = $(LIB_SCHED)
sample_work_generator_SOURCES = sample_work_generator.C
sample_work_generator_DEPENDENCIES = $(LIB_SCHED)

View File

@ -489,7 +489,7 @@ void hr_init() {
if (hrt != hr_type0) apps_differ = true;
}
if (config.homogeneous_redundancy) {
log_msgs.printf(MSG_NORMAL,
log_messages.printf(MSG_NORMAL,
"config HR is %d\n", config.homogeneous_redundancy
);
hrt = config.homogeneous_redundancy;

View File

@ -0,0 +1,94 @@
// Berkeley Open Infrastructure for Network Computing
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// This is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any later version.
//
// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// To view the GNU Lesser General Public License visit
// http://www.gnu.org/copyleft/lesser.html
// or write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// assimilator for single jobs.
// - if success, move the output file(s) to job directory
// - delete job description file
// - delete WU template file
#include <vector>
#include <string>
#include <cstdlib>
#include "boinc_db.h"
#include "error_numbers.h"
#include "filesys.h"
#include "sched_msgs.h"
#include "validate_util.h"
#include "sched_config.h"
#include "sched_util.h"
using std::vector;
using std::string;
int assimilate_handler(
WORKUNIT& wu, vector<RESULT>& /*results*/, RESULT& canonical_result
) {
int retval;
char buf[1024], filename[256], job_dir[256];
unsigned int i;
sprintf(filename, "sj_%d", wu.id);
dir_hier_path(filename, config.upload_dir, config.uldl_dir_fanout, buf);
FILE* f = fopen(buf, "r");
if (!f) {
log_messages.printf(MSG_CRITICAL, "Can't open job file %s\n", buf);
return 0;
}
fgets(buf, 1024, f);
char* p = strstr(buf, "<job_dir>");
if (!p) {
log_messages.printf(MSG_CRITICAL, "garbage in job file: %s\n", buf);
return 0;
}
strcpy(job_dir, buf+strlen("<job_dir>"));
p = strstr(job_dir, "</job_dir>");
if (!p) {
log_messages.printf(MSG_CRITICAL, "garbage in job file: %s\n", buf);
return 0;
}
if (wu.canonical_resultid) {
vector<string> output_file_paths;
char copy_path[256];
get_output_file_paths(canonical_result, output_file_paths);
unsigned int n = output_file_paths.size();
for (i=0; i<n; i++) {
string path = output_file_paths[i];
strcpy(buf, path.c_str());
p = strrchr(buf, '/');
strcpy(filename, p+1);
p = strrchr(filename, '_');
*p = 0;
sprintf(copy_path, "%s/%s", job_dir, filename);
retval = boinc_copy(path.c_str() , copy_path);
if (retval) {
log_messages.printf(MSG_CRITICAL,
"couldn't copy file %s to %s\n",
path.c_str(), copy_path
);
return retval;
}
}
} else {
sprintf(buf, "%s/error_msg", job_dir);
f = fopen(buf, "w");
fprintf(f, "Error: 0x%x\n", wu.error_mask);
}
return 0;
}

View File

@ -610,12 +610,20 @@ int create_work(
} else {
wu.transition_time = time(0);
}
retval = wu.insert();
if (retval) {
fprintf(stderr, "create_work: workunit.insert() %d\n", retval);
return retval;
if (wu.id) {
retval = wu.update();
if (retval) {
fprintf(stderr, "create_work: workunit.update() %d\n", retval);
return retval;
}
} else {
retval = wu.insert();
if (retval) {
fprintf(stderr, "create_work: workunit.insert() %d\n", retval);
return retval;
}
wu.id = boinc_db.insert_id();
}
wu.id = boinc_db.insert_id();
return 0;
}

339
tools/boinc_submit Executable file
View File

@ -0,0 +1,339 @@
#!/usr/bin/env php
<?php
// Submit a single job.
// Implementation notes:
// - The jobs use the app "single_job_PLATFORM".
// This app has a single app_version containing the wrapper for that platform
// - the executable is part of the WU, and has the sticky bit set,
// and has a signature
// - The logical and physical name of the executable
// (as stored in the download directory) is "program_platform_cksum"
// where cksum is the last 8 chars of the MD5
// - The physical name of the job file is job_WUID.xml
// - The physical names of the input/output files are name_WUID
// - a file containing the job directory is stored in
// sj_WUID in the upload hierarchy
// - a workunit template sj_WUID is created in templates/
// - the single_job_assimilator copies the output files to the job dir,
// and cleans up the sj_WUID and WU template files
ini_set('error_reporting', E_ALL);
// global vars
//
$project_dir = null;
$job_dir = getcwd();
$platform = 'i686-pc-linux-gnu';
$infiles = array();
$outfiles = array();
$stdin_file = null;
$stdout_file = null;
$program = null;
$cmdline_args = null;
$app_name = null;
$wu_template_filename = null;
// relative to project dir
$result_template_filename = null;
// relative to project dir
function get_project_dir() {
global $project_dir;
$project_dir = getenv('BOINC_PROJECT_DIR');
if (!$project_dir) {
echo "You must set the environment variable BOINC_PROJECT_DIR
to the path of a BOINC project, e.g.:
> setenv BOINC_PROJECT_DIR ~/projects/my_project
";
exit(1);
}
}
function usage() {
echo "Usage: boinc_job [options] program
--platform p
Run the program on platform p
--infile f
The program will use f as an input file
--outfile f
The program will use f as an output file
--stdin f
Direct f to the program's stdin
--stdout f
Direct the program's stdout to f
";
exit(1);
}
function error($msg) {
echo "$msg\n";
exit(1);
}
function download_path($filename) {
global $project_dir;
return dir_hier_path($filename, "$project_dir/download", 1024);
}
function upload_path($filename) {
global $project_dir;
return dir_hier_path($filename, "$project_dir/upload", 1024);
}
function do_includes() {
global $project_dir;
chdir("$project_dir/html/ops");
require_once("../inc/boinc_db.inc");
require_once("../inc/dir_hier.inc");
BoincDb::get();
}
function check_app_version() {
global $platform, $app_name;
$app_name = "single_job_$platform";
$app = BoincApp::lookup("name='$app_name'");
if (!$app) {
error("This project isn't configured to run single jobs.");
}
}
// make the job.xml file used by the wrapper
//
function make_wrapper_job_file() {
global $program, $stdin_file, $stdout_file, $cmdline_args, $wuid;
global $project_dir;
chdir($project_dir);
$filename = "sj_$wuid.xml";
$path = download_path($filename);
$f = fopen($path, "w");
if (!$f) {
error("Can't open $path");
}
fwrite($f,
"<job_desc>
<task>
<application>$program</application>
");
if ($stdin_file) {
fwrite($f, " <stdin_filename>$stdin_file</stdin_filename>\n");
}
if ($stdout_file) {
fwrite($f, " <stdout_filename>$stdout_file</stdout_filename>\n");
}
if ($cmdline_args) {
fwrite($f, " <command_line>$cmdline_args</command_line>\n");
}
fwrite($f, " </task>\n</job_desc>\n");
fclose($f);
}
function make_wu_template() {
global $wuid, $infiles, $stdin_file, $program, $wu_template_filename;
global $project_dir;
chdir($project_dir);
$wu_template_filename = "templates/sj_wu_template_$wuid";
$f = fopen($wu_template_filename, "w");
if (!$f) {
error("Can't open $wu_template_filename");
}
for ($i=0; $i<count($infiles)+2; $i++) {
fwrite($f,
"<file_info>
<number>$i</number>
</file_info>
");
}
fwrite($f, "<workunit>\n");
$i = 0;
foreach($infiles as $infile) {
fwrite($f,
" <file_ref>
<file_number>$i</file_number>
<open_name>$infile</open_name>
<copy_file/>
</file_ref>
");
$i++;
}
if ($stdin_file) {
fwrite($f,
" <file_ref>
<file_number>$i</file_number>
<open_name>stdin</open_name>
</file_ref>
");
$i++;
}
fwrite($f,
" <file_ref>
<file_number>$i</file_number>
<open_name>$program</open_name>
<copy_file/>
</file_ref>
");
fwrite($f,
" <rsc_fpops_bound>1e18</rsc_fpops_bound>
<rsc_fpops_est>1e15</rsc_fpops_est>
</workunit>
");
fclose($f);
}
function make_result_template() {
global $wuid, $outfiles, $stdout_file, $project_dir;
global $result_template_filename;
chdir($project_dir);
$result_template_filename = "templates/sj_result_template_$wuid";
$f = fopen($result_template_filename, "w");
if (!$f) {
error("Can't open $result_template_filename");
}
$i = 0;
foreach($outfiles as $outfile) {
fwrite($f,
"<file_info>
<name><OUTFILE_$i/></name>
<generated_locally/>
<upload_when_present/>
<max_nbytes>1e12</max_nbytes>
<url><UPLOAD_URL/></url>
</file_info>
");
}
fwrite($f, "<result>\n");
$i = 0;
foreach($outfiles as $outfile) {
fwrite($f,
" <file_ref>
<file_name><OUTFILE_$i/></file_name>
<open_name>$outfile</open_name>
<copy_file/>
</file_ref>
");
$i++;
}
if ($stdout_file) {
fwrite($f,
" <file_ref>
<file_name><OUTFILE_$i/></file_name>
<open_name>stdout</open_name>
</file_ref>
");
}
fwrite($f, "</result>\n");
fclose($f);
}
// make the sj_WUID file
//
function make_job_file() {
global $wuid, $job_dir, $project_dir;
chdir($project_dir);
$filename = "sj_$wuid";
$path = upload_path($filename);
$f = fopen($path, "w");
if (!$f) {
error("Can't open $path");
}
fwrite($f,
"<job_dir>$job_dir</job_dir>
");
fclose($f);
}
function create_wu() {
global $wuid;
$name = md5(uniqid(rand(), true));
$wuid = BoincWorkunit::insert("(name, transition_time) values ('$name', ".PHP_INT_MAX.")");
}
function create_job() {
global $wuid, $app_name, $infiles, $program, $project_dir;
global $result_template_filename, $wu_template_filename;
chdir($project_dir);
$cmd = "bin/create_work --appname $app_name --wu_name sj_$wuid --wu_id $wuid --wu_template $wu_template_filename --result_template $result_template_filename";
foreach ($infiles as $infile) {
$cmd .= " $infile";
}
$cmd .= " sj_$wuid.xml";
$cmd .= " $program";
echo "Executing command: $cmd\n";
system($cmd);
}
// copy input files and program file to the download hierarchy
//
function copy_files() {
global $infiles, $wuid, $job_dir, $program;
chdir($job_dir);
foreach ($infiles as $infile) {
$filename = "$infile_$wuid";
$path = download_path($filename);
echo "copying $infile to $path\n";
copy($infile, $path);
}
$path = download_path($program);
echo "copying $program to $path\n";
copy($program, $path);
}
function parse_args($argc, $argv) {
global $platform, $infiles, $outfiles, $stdin_file, $stdout_file;
global $program, $cmdline_args;
for ($i=1; $i<$argc; $i++) {
switch ($argv[$i]) {
case '--platform':
$platform = $argv[++$i];
break;
case '--infile':
$infiles[] = $argv[++$i];
break;
case '--outfile':
$outfiles[] = $argv[++$i];
break;
case '--stdin':
$stdin_file = $argv[++$i];
break;
case '--stdout':
$stdout_file = $argv[++$i];
break;
default:
if ($program) {
$cmdline_args .= ''.$argv[$i];
} else {
$program = $argv[$i];
}
break;
}
}
if (!$program) usage();
}
get_project_dir();
parse_args($argc, $argv);
do_includes();
check_app_version();
create_wu();
make_wrapper_job_file();
make_job_file();
make_wu_template();
make_result_template();
copy_files();
create_job();
?>

View File

@ -102,6 +102,7 @@ int main(int argc, const char** argv) {
// defaults (in case they're not in WU template)
wu.id = 0;
wu.min_quorum = 2;
wu.target_nresults = 5;
wu.max_error_results = 10;
@ -156,6 +157,8 @@ int main(int argc, const char** argv) {
command_line= argv[++i];
} else if (arg(argv, i, "additional_xml")) {
strcpy(additional_xml, argv[++i]);
} else if (arg(argv, i, "wu_id")) {
wu.id = atoi(argv[++i]);
} else if (arg(argv, i, "assign_all")) {
assign_multi = true;
assign_flag = true;