- Code cleanup for remote job submission

- Add abort_jobs operation to BOINC GAHP
- Change batch-related RPCs so that you can identify batch
    either by database ID or name
This commit is contained in:
David Anderson 2013-02-20 16:20:00 -08:00 committed by Oliver Bock
parent fc712178b1
commit d6c92d870c
10 changed files with 173 additions and 50 deletions

29
html/inc/common_defs.inc Normal file
View File

@ -0,0 +1,29 @@
<?php
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2013 University of California
//
// BOINC 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 3 of the License, or (at your option) any later version.
//
// BOINC 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.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// defines needed by multiple pieces of code
// see db/boinc_db.h
//
define('BATCH_STATE_INIT', 0);
define('BATCH_STATE_IN_PROGRESS', 1);
define('BATCH_STATE_COMPLETE', 2);
define('BATCH_STATE_ABORTED', 3);
define('BATCH_STATE_RETIRED', 4);
?>

View File

@ -18,11 +18,17 @@
// Tables related to job submission
require_once("../inc/common_defs.inc");
class BoincBatch {
static function lookup_id($id) {
$db = BoincDb::get();
return $db->lookup_id($id, 'batch', 'BoincBatch');
}
static function lookup_name($name) {
$db = BoincDb::get();
return $db->lookup('batch', 'BoincBatch', "name='$name'");
}
static function enum($clause) {
$db = BoincDb::get();
return $db->enum('batch', 'BoincBatch', $clause);
@ -43,14 +49,6 @@ class BoincBatch {
}
}
// see db/boinc_db.h
//
define('BATCH_STATE_INIT', 0);
define('BATCH_STATE_IN_PROGRESS', 1);
define('BATCH_STATE_COMPLETE', 2);
define('BATCH_STATE_ABORTED', 3);
define('BATCH_STATE_RETIRED', 4);
class BoincUserSubmit {
static function enum($clause) {
$db = BoincDb::get();

View File

@ -55,12 +55,21 @@ function get_output_file($instance_name, $file_num, $auth_str) {
// get all the output files of a batch (canonical instances only)
// and make a zip of all of them
//
function get_batch_output_files($batch_id, $auth_str) {
$batch = BoincBatch::lookup_id($batch_id);
if (!$batch) die("no batch $batch_id");
function get_batch_output_files($auth_str) {
$batch_id = get_int('batch_id', true);
if ($batch_id) {
$batch = BoincBatch::lookup_id($batch_id);
if (!$batch) die("no batch $batch_id");
} else {
$batch_name = get_int('batch_name');
$batch_name = BoincDb::escape_string($batch_name);
$batch = BoincBatch::lookup("name='$batch_name'");
if (!$batch) die("no batch $batch_name");
}
$user = BoincUser::lookup_id($batch->user_id);
if (!$user) die("no user $batch->user_id");
$x = md5($user->authenticator.$batch_id);
$x = md5($user->authenticator.$batch->id);
if ($x != $auth_str) die("bad auth str");
$zip_basename = tempnam("/tmp", "boinc_batch_");
@ -68,7 +77,7 @@ function get_batch_output_files($batch_id, $auth_str) {
$fanout = parse_config(get_config(), "<uldl_dir_fanout>");
$upload_dir = parse_config(get_config(), "<upload_dir>");
$wus = BoincWorkunit::enum("batch=$batch_id");
$wus = BoincWorkunit::enum("batch=$batch->id");
foreach ($wus as $wu) {
if (!$wu->canonical_resultid) continue;
$result = BoincResult::lookup_id($wu->canonical_resultid);
@ -149,7 +158,6 @@ case 'result_file';
get_output_file($result_name, $file_num, $auth_str);
break;
case 'batch_files':
$batch_id = get_int('batch_id');
get_batch_output_files($batch_id, $auth_str);
break;
case 'workunit_file':

View File

@ -25,12 +25,12 @@
// This eliminates issues related to file immutability
//
// 2) how do we keep track of the files?
// In the MySQL database, in a table called job_files.
// In the MySQL database, in a table called "job_file".
// Each row describes a file currently on the server.
// In addition, we maintain a table batch_file_assoc to record
// In addition, we maintain a table "batch_file_assoc" to record
// that a file is used by a particular batch.
// (Note: the association could be at the job level instead.
// but this way is more efficient if all the jobs in a batch use
// but this way is more efficient if many jobs in a batch use
// a particular file.)
//
// 3) how do we clean up unused files?

View File

@ -31,8 +31,9 @@
// you can strip out this stuff if the web site doesn't use BOINC
require_once("../inc/submit.inc");
require_once("../inc/submit_util.inc");
require_once("../inc/common_defs.inc");
require_once("../inc/submit_db.inc");
// needed for access control stuff
require_once("../inc/util.inc");
require_once("../project/project.inc");
@ -74,7 +75,6 @@ function handle_main() {
with permission to submit jobs.
<p>
";
show_button("submit_example.php?action=manage_files", "Manage files");
show_button("submit_example.php?action=create_form", "Create new batch");
$first = true;
@ -455,19 +455,13 @@ function handle_retire_batch() {
page_tail();
}
function manage_files() {
$files = submit_get_file_list();
}
$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 'manage_files': manage_files(); break;
case 'query_batch': handle_query_batch(); break;
case 'query_job': handle_query_job(); break;
case 'retire_batch': handle_retire_batch(); break;

View File

@ -281,11 +281,26 @@ function n_outfiles($wu) {
return count($r->file_info);
}
// return a batch specified by the command, using either ID or name
//
function get_batch($r) {
if (!empty($r->batch_id)) {
$batch_id = (int)($r->batch_id);
$batch = BoincBatch::lookup_id($batch_id);
} else if (!empty($r->batch_name)) {
$batch_name = (string)($r->batch_name);
$batch_name = BoincDb::escape_string($batch_name);
$batch = BoincBatch::lookup("name='$batch_name'");
} else {
xml_error(-1, "batch not specified");
}
if (!$batch) xml_error(-1, "no such batch");
return $batch;
}
function query_batch($r) {
list($user, $user_submit) = authenticate_user($r, null);
$batch_id = (int)($r->batch_id);
$batch = BoincBatch::lookup_id($batch_id);
if (!$batch) xml_error(-1, "no such batch");
$batch = get_batch($r);
if ($batch->user_id != $user->id) xml_error(-1, "not owner");
$wus = BoincWorkunit::enum("batch = $batch_id");
@ -305,14 +320,13 @@ function query_batch($r) {
}
// variant for Condor, which doesn't care about job instances
// and refers to batches by name
//
function query_batch2($r) {
list($user, $user_submit) = authenticate_user($r, null);
$batch_id = (int)($r->batch_id);
$batch = BoincBatch::lookup_id($batch_id);
if (!$batch) xml_error(-1, "no such batch");
$batch = get_batch($r);
if ($batch->user_id != $user->id) xml_error(-1, "not owner");
$wus = BoincWorkunit::enum("batch = $batch_id");
$wus = BoincWorkunit::enum("batch = $batch->id");
echo "<batch>\n";
foreach ($wus as $wu) {
if ($wu->canonical_resultid) {
@ -368,9 +382,7 @@ function query_job($r) {
function handle_abort_batch($r) {
list($user, $user_submit) = authenticate_user($r, null);
$batch_id = (int)($r->batch_id);
$batch = BoincBatch::lookup_id($batch_id);
if (!$batch) xml_error(-1, "no such batch");
$batch = get_batch($r);
if ($batch->user_id != $user->id) {
xml_error(-1, "not owner");
}
@ -378,11 +390,29 @@ function handle_abort_batch($r) {
echo "<success>1</success>";
}
function handle_abort_jobs($r) {
list($user, $user_submit) = authenticate_user($r, null);
$batch = get_batch($r);
if ($batch->user_id != $user->id) {
xml_error(-1, "not owner");
}
foreach ($r->job_names as $job_name) {
$job_name = BoincDb::escape_string($job_name);
$wu = BoincWorkunit::lookup("name='$job_name'");
if (!$wu) {
xml_error(-1, "No job $job_name");
}
if ($wu->batch != $batch_id) {
xml_error(-1, "Not owner of job $job_name");
}
abort_workunit($wu);
}
echo "<success>1</success>";
}
function handle_retire_batch($r) {
list($user, $user_submit) = authenticate_user($r, null);
$batch_id = (int)($r->batch_id);
$batch = BoincBatch::lookup_id($batch_id);
if (!$batch) xml_error(-1, "no such batch");
$batch = get_batch($r);
if ($batch->user_id != $user->id) {
xml_error(-1, "not owner");
}
@ -442,6 +472,7 @@ if (!$r) {
switch ($r->getName()) {
case 'abort_batch': handle_abort_batch($r); break;
case 'abort_jobs': handle_abort_jobs($r); break;
case 'estimate_batch': estimate_batch($r); break;
case 'query_batch': query_batch($r); break;
case 'query_batch2': query_batch2($r); break;

View File

@ -23,7 +23,7 @@
#include "parse.h"
#include "job_rpc.h"
#include "remote_submit.h"
using std::vector;
using std::string;
@ -41,7 +41,7 @@ static int do_http_get(
return -1;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "BOINC Condor adapter");
curl_easy_setopt(curl, CURLOPT_USERAGENT, "BOINC remote job submission");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, reply);
CURLcode res = curl_easy_perform(curl);
@ -305,13 +305,13 @@ int submit_jobs(
int query_batch(
const char* project_url,
const char* authenticator,
int batch_id,
string batch_name,
QUERY_BATCH_REPLY& qb_reply
) {
string request;
char url[1024], buf[256];
request = "<query_batch2>\n";
sprintf(buf, "<batch_id>%d</batch_id>\n", batch_id);
sprintf(buf, "<batch_name>%s</batch_name>\n", batch_name.c_str());
request += string(buf);
sprintf(buf, "<authenticator>%s</authenticator>\n", authenticator);
request += string(buf);
@ -346,6 +346,43 @@ int query_batch(
return retval;
}
int abort_jobs(
const char* project_url,
const char* authenticator,
string batch_name,
vector<string> &job_names
) {
string request;
char url[1024], buf[256];
request = "<abort_jobs>\n";
sprintf(buf, "<authenticator>%s</authenticator>\n", authenticator);
request += string(buf);
sprintf(buf, "<batch_name>%s</batch_name>\n", batch_name.c_str());
request += string(buf);
for (unsigned int i=0; i<job_names.size(); i++) {
sprintf(buf, "<job_name>%s</job_name>\n", job_names[i].c_str());
request += string(buf);
}
request += "</abort_jobs>\n";
sprintf(url, "%ssubmit_rpc_handler.php", project_url);
FILE* reply = tmpfile();
vector<string> x;
int retval = do_http_post(url, request.c_str(), reply, x);
if (retval) {
fclose(reply);
return retval;
}
fseek(reply, 0, SEEK_SET);
retval = 0;
while (fgets(buf, 256, reply)) {
if (strstr(buf, "error")) {
retval = -1;
}
}
fclose(reply);
return retval;
}
int get_output_file(
const char* project_url,
const char* authenticator,

View File

@ -15,6 +15,8 @@
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// C++ interfaces to remote job submissions and file management RPCs
#include <stdio.h>
#include <string>
#include <vector>
@ -104,7 +106,7 @@ extern int submit_jobs(
extern int query_batch(
const char* project_url,
const char* authenticator,
int batch_id,
string batch_name,
QUERY_BATCH_REPLY& reply
);
@ -115,3 +117,10 @@ extern int get_output_file(
int file_num,
const char* dst_path
);
extern int abort_jobs(
const char* project_url,
const char* authenticator,
string batch_name,
vector<string> &job_names
);

View File

@ -1,11 +1,11 @@
all: boinc_gahp
boinc_gahp: boinc_gahp.cpp job_rpc.cpp job_rpc.h
boinc_gahp: boinc_gahp.cpp ../../lib/remote_submit.h ../../lib/remote_submit.cpp
g++ -g -O0 -I../../lib \
-o boinc_gahp boinc_gahp.cpp job_rpc.cpp \
-o boinc_gahp boinc_gahp.cpp ../../lib/remote_submit.cpp \
-L../../lib -lboinc -lpthread -lcurl
test: test.cpp job_rpc.cpp
test: test.cpp ../../lib/remote_submit.cpp ../../lib/remote_submit.h
g++ -g -o test -I../../lib \
test.cpp job_rpc.cpp \
test.cpp ../../lib/remote_submit.cpp \
-L../../lib -lboinc -lcurl

View File

@ -33,7 +33,7 @@
#include "md5_file.h"
#include "parse.h"
#include "job_rpc.h"
#include "remote_submit.h"
using std::map;
using std::pair;
@ -208,9 +208,9 @@ void handle_submit(COMMAND& c, char* p) {
}
void handle_query_batch(COMMAND&c, char* p) {
int batch_id = atoi(strtok_r(NULL, " ", &p));
char* batch_name = strtok_r(NULL, " ", &p);
QUERY_BATCH_REPLY reply;
query_batch(project_url, authenticator, batch_id, reply);
query_batch(project_url, authenticator, batch_name, reply);
for (unsigned int i=0; i<reply.jobs.size(); i++) {
QUERY_BATCH_JOB &j = reply.jobs[i];
printf("job %s: status %s\n", j.job_name.c_str(), j.status.c_str());
@ -244,6 +244,20 @@ void handle_fetch_output(COMMAND& c, char* p) {
}
}
void handle_abort_jobs(COMMAND&c, char* p) {
vector<string> job_names;
char* batch_name = strtok_r(NULL, " ", &p);
while (1) {
char* job_name = strtok_r(NULL, " ", &p);
if (!job_name) break;
job_names.push_back(string(job_name));
}
int retval = abort_jobs(project_url, authenticator, string(batch_name), job_names);
if (retval) {
printf("abort_jobs() returned %d\n", retval);
}
}
void* handle_command_aux(void* q) {
COMMAND &c = *((COMMAND*)q);
char *p;
@ -257,6 +271,8 @@ void* handle_command_aux(void* q) {
handle_query_batch(c, p);
} else if (!strcmp(cmd, "BOINC_FETCH_OUTPUT")) {
handle_fetch_output(c, p);
} else if (!strcmp(cmd, "BOINC_ABORT_JOBS")) {
handle_abort_jobs(c, p);
} else {
sleep(10);
char buf[256];
@ -384,5 +400,6 @@ int main() {
COMMAND c;
c.in = p;
handle_command(c);
fflush(stdout);
}
}