server: extend script_validator and script_assimilator to pass extra args

You can arrange to pass result IDs, runtime, etc.
to your validation and assimilation scripts.  See
http://boinc.berkeley.edu/trac/wiki/ValidationSimple#Usingscriptinglanguages
http://boinc.berkeley.edu/trac/wiki/AssimilateIntro#Usingscriptinglanguages
This commit is contained in:
David Anderson 2014-10-24 14:22:07 -07:00
parent 97b9d947b8
commit 6eabd34068
4 changed files with 146 additions and 50 deletions

View File

@ -26,6 +26,7 @@
#ifndef _WIN32
#include "config.h"
#include <sstream>
#include <string>
#include <cmath>
#include <string.h>
@ -45,6 +46,8 @@
#include "str_util.h"
using std::string;
using std::stringstream;
using std::vector;
// Use this instead of strncpy().
// Result will always be null-terminated, and it's faster.
@ -740,3 +743,13 @@ void parse_serialnum(char* in, char* boinc, char* vbox, char* coprocs) {
in = p;
}
}
vector<string> split(string s, char delim) {
vector<string> result;
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
result.push_back(item);
}
return result;
}

View File

@ -19,6 +19,7 @@
#define STR_UTIL_H
#include <string>
#include <vector>
#include <string.h>
extern void strcpy_overlap(char*, const char*);
@ -99,4 +100,5 @@ extern const char* batch_state_string(int state);
extern void strip_translation(char* p);
extern std::vector<std::string> split(std::string, char delim);
#endif

View File

@ -15,14 +15,28 @@
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// An assimilator that runs a script of your choosing to handle completed jobs.
// This script is invoked as
// An assimilator that runs a script to handle completed jobs,
// so that you can do assimilation in Python, PHP, Perl, bash, etc.
//
// scriptname --wu_name X f1 ... fn
// where X is the workunit name
// and f1 ... fn are the output files of the canonical result
// or
// scriptname --wu_name X --error N
// cmdline args to this program:
// --script "scriptname arg1 ... argn"
//
// The script assimilates a completed job.
//
// arg1 ... argn represent cmdline args to be passed to the script.
// the options are:
//
// files list of output files of the job's canonical result
// wu_id workunit ID
// result_id ID of the canonical result
// runtime runtime of the canonical result
//
// if no args are specified, the script is invoked as
// scriptname wu_id files
//
// If the workunit has no canonical result (i.e. it failed)
// the script is invoked as
// scriptname --error N wu_id
// where N is an integer encoding the reasons for the job's failure
// (see WU_ERROR_* in html/inc/common_defs.inc)
@ -41,16 +55,19 @@ using std::vector;
using std::string;
bool first = true;
char script[MAXPATHLEN];
vector<string> script;
void parse_cmdline() {
strcpy(script, "");
for (int i=1; i<g_argc; i++) {
if (!strcmp(g_argv[i], "--script")) {
sprintf(script, "../bin/%s", g_argv[++i]);
script = split(g_argv[++i], ' ');
if (script.size() == 1) {
script.push_back("wu_id");
script.push_back("files");
}
}
}
if (!strlen(script)) {
if (!script.size()) {
log_messages.printf(MSG_CRITICAL,
"script name missing from command line\n"
);
@ -62,7 +79,8 @@ int assimilate_handler(
WORKUNIT& wu, vector<RESULT>& /*results*/, RESULT& canonical_result
) {
int retval;
char cmd[4096];
char cmd[4096], buf[256];
unsigned int i, j;
if (first) {
parse_cmdline();
@ -70,17 +88,28 @@ int assimilate_handler(
}
if (wu.canonical_resultid) {
sprintf(cmd, "%s --wu_name %s", script, wu.name);
sprintf(cmd, "../bin/%s", script[0].c_str());
vector<string> paths;
retval = get_output_file_paths(canonical_result, paths);
if (retval) return retval;
for (unsigned int i=0; i<paths.size(); i++) {
strcat(cmd, " ");
strcat(cmd, paths[i].c_str());
for (i=1; i<script.size(); i++) {
string& s = script[i];
if (s == "files") {
for (j=0; j<paths.size(); j++) {
strcat(cmd, " ");
strcat(cmd, paths[j].c_str());
}
} else if (s == "wu_id") {
sprintf(buf, " %d", wu.id);
strcat(cmd, buf);
} else if (s == "runtime") {
sprintf(buf, " %f", canonical_result.elapsed_time);
strcat(cmd, buf);
}
}
} else {
sprintf(cmd, "%s --wu_name %s --error %d",
script, wu.name, wu.error_mask
sprintf(cmd, "../bin/%s --error %d %d",
script[0].c_str(), wu.error_mask, wu.id
);
}
retval = system(cmd);

View File

@ -16,22 +16,33 @@
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// A validator that runs scripts to check and compare results,
// so that you can do your validation on Python, Perl, bash, etc.
// so that you can do your validation in Python, PHP, Perl, bash, etc.
//
// cmdline args:
// --init_script scriptname
// --compare_script scriptname
// cmdline args to this program:
// --init_script "scriptname arg1 ... argn"
// --compare_script "scriptname arg1 ... argn"
//
// the init script is called as
// scriptname f1 ... fn
// where f1 ... fn are the output files of a job (there may be just one)
// The init script checks the validity of a result,
// e.g. that the output files have the proper format.
// It returns zero if the files are valid
//
// the compare script is called as
// scriptname f1 ... fn g1 ... gn
// where f1 ... fn are the output files of one job,
// and g1 ... gn are the output files are another job.
// It returns zero if the files are equivalent.
// The compare script compares two results.
// If returns zero if the output files are equivalent.
//
// arg1 ... argn represent cmdline args to be passed to the scripts.
// The options for init_script are:
//
// files list of paths of output files of the result
// result_id result ID
// runtime task runtime
//
// Additional options for compare_script, for the second result:
// files2 list of paths of output files
// result_id2 result ID
// runtime2 task runtime
//
// "arg1 ... argn" can be omitted,
// in which case only the output file paths are passed to the scripts.
#include <sys/param.h>
@ -46,19 +57,25 @@ using std::string;
using std::vector;
bool first = true;
char init_script[MAXPATHLEN], compare_script[MAXPATHLEN];
vector<string> init_script, compare_script;
// first element is script path, other elements are args
void parse_cmdline() {
strcpy(init_script, "");
strcpy(compare_script, "");
for (int i=1; i<g_argc; i++) {
if (!strcmp(g_argv[i], "--init_script")) {
sprintf(init_script, "../bin/%s", g_argv[++i]);
init_script = split(g_argv[++i], ' ');
if (init_script.size() == 1) {
init_script.push_back(string("files"));
}
} else if (!strcmp(g_argv[i], "--compare_script")) {
sprintf(compare_script, "../bin/%s", g_argv[++i]);
compare_script = split(g_argv[++i], ' ');
if (compare_script.size() == 1) {
compare_script.push_back("files");
compare_script.push_back("files2");
}
}
}
if (!strlen(init_script) && !strlen(compare_script)) {
if (!init_script.size() && !compare_script.size()) {
log_messages.printf(MSG_CRITICAL,
"script names missing from command line\n"
);
@ -67,11 +84,14 @@ void parse_cmdline() {
}
int init_result(RESULT& result, void*&) {
unsigned int i, j;
char buf[256];
if (first) {
parse_cmdline();
first = false;
}
if (!strlen(init_script)) return 0;
if (!init_script.size()) return 0;
vector<string> paths;
int retval;
retval = get_output_file_paths(result, paths);
@ -80,10 +100,21 @@ int init_result(RESULT& result, void*&) {
return retval;
}
char cmd[4096];
strcpy(cmd, init_script);
for (unsigned int i=0; i<paths.size(); i++) {
strcat(cmd, " ");
strcat(cmd, paths[i].c_str());
sprintf(cmd, "../bin/%s", init_script[0].c_str());
for (i=1; i<init_script.size(); i++) {
string& s = init_script[i];
if (s == "files") {
for (j=0; j<paths.size(); j++) {
strcat(cmd, " ");
strcat(cmd, paths[j].c_str());
}
} else if (s == "runtime") {
sprintf(buf, " %f", result.elapsed_time);
strcat(cmd, buf);
} else if (s == "result_id") {
sprintf(buf, " %d", result.id);
strcat(cmd, buf);
}
}
retval = system(cmd);
if (retval) {
@ -93,11 +124,14 @@ int init_result(RESULT& result, void*&) {
}
int compare_results(RESULT& r1, void*, RESULT const& r2, void*, bool& match) {
unsigned int i, j;
char buf[256];
if (first) {
parse_cmdline();
first = false;
}
if (!strlen(compare_script)) {
if (!compare_script.size()) {
match = true;
return 0;
}
@ -114,14 +148,32 @@ int compare_results(RESULT& r1, void*, RESULT const& r2, void*, bool& match) {
return retval;
}
char cmd[4096];
strcpy(cmd, compare_script);
for (unsigned int i=0; i<paths1.size(); i++) {
strcat(cmd, " ");
strcat(cmd, paths1[i].c_str());
}
for (unsigned int i=0; i<paths2.size(); i++) {
strcat(cmd, " ");
strcat(cmd, paths2[i].c_str());
sprintf(cmd, "../bin/%s", compare_script[0].c_str());
for (i=1; i<compare_script.size(); i++) {
string& s = compare_script[i];
if (s == "files") {
for (j=0; j<paths1.size(); j++) {
strcat(cmd, " ");
strcat(cmd, paths1[j].c_str());
}
} else if (s == "files2") {
for (j=0; j<paths2.size(); j++) {
strcat(cmd, " ");
strcat(cmd, paths2[j].c_str());
}
} else if (s == "runtime") {
sprintf(buf, " %f", r1.elapsed_time);
strcat(cmd, buf);
} else if (s == "result_id") {
sprintf(buf, " %d", r1.id);
strcat(cmd, buf);
} else if (s == "runtime2") {
sprintf(buf, " %f", r2.elapsed_time);
strcat(cmd, buf);
} else if (s == "result_id2") {
sprintf(buf, " %d", r2.id);
strcat(cmd, buf);
}
}
retval = system(cmd);
if (retval) {