diff --git a/api/boinc_api.C b/api/boinc_api.C index 7625d6047d..979587c9f0 100644 --- a/api/boinc_api.C +++ b/api/boinc_api.C @@ -774,6 +774,8 @@ struct GRAPHICS_APP { static GRAPHICS_APP ga_win(false), ga_full(true); static bool have_graphics_app; +// The following is for backwards compatibility with version 5 clients. +// static inline void handle_graphics_messages() { static char graphics_app_path[1024]; char buf[MSG_CHANNEL_SIZE]; @@ -785,6 +787,9 @@ static inline void handle_graphics_messages() { GRAPHICS_APP_FILENAME, graphics_app_path, sizeof(graphics_app_path) ); + // if the above returns "graphics_app", there was no link file, + // so there's no graphics app + // if (!strcmp(graphics_app_path, GRAPHICS_APP_FILENAME)) { have_graphics_app = false; } else { diff --git a/checkin_notes b/checkin_notes index fdbce29cfc..6a44903644 100644 --- a/checkin_notes +++ b/checkin_notes @@ -1476,3 +1476,40 @@ Charlie Feb 20 2008 client/ app_graphics.C + +David Feb 20 2008 + - Server: add "job assignment" feature. + Lets you assign a WU to a particular host, + to one or all hosts belonging to a user or team, or to all hosts. + See http://boinc.berkeley.edu/trac/wiki/AssignedWork + Disabled unless you include in config.xml + Uses a new DB table. + Tested but only a little. + - Server: code cleanup; moved result-handling to a new file, + and removed the PLATFORM_LIST arg to everything + (put it in SCHEDULER_REQUEST instead) + + api/ + boinc_api.C + db/ + boinc_db.C,h + schema.sql + html/ + inc/ + db_ops.inc + ops/ + assign.php (new) + db_update.php + sched/ + Makefile.am + feeder.C + file_upload_handler.C + handle_request.C + sched_assign.C,h (new) + sched_result.C,h (new) + sched_* + transitioner.C + tools/ + Makefile.am + backend_lib.C,h + create_work.C diff --git a/db/boinc_db.C b/db/boinc_db.C index 05c9826d5f..42f698a3c0 100644 --- a/db/boinc_db.C +++ b/db/boinc_db.C @@ -64,6 +64,7 @@ void WORKUNIT::clear() {memset(this, 0, sizeof(*this));} void CREDITED_JOB::clear() {memset(this, 0, sizeof(*this));} void MSG_FROM_HOST::clear() {memset(this, 0, sizeof(*this));} void MSG_TO_HOST::clear() {memset(this, 0, sizeof(*this));} +void ASSIGNMENT::clear() {memset(this, 0, sizeof(*this));} void TRANSITIONER_ITEM::clear() {memset(this, 0, sizeof(*this));} void VALIDATOR_ITEM::clear() {memset(this, 0, sizeof(*this));} void SCHED_RESULT_ITEM::clear() {memset(this, 0, sizeof(*this));} @@ -90,6 +91,8 @@ DB_MSG_FROM_HOST::DB_MSG_FROM_HOST(DB_CONN* dc) : DB_BASE("msg_from_host", dc?dc:&boinc_db){} DB_MSG_TO_HOST::DB_MSG_TO_HOST(DB_CONN* dc) : DB_BASE("msg_to_host", dc?dc:&boinc_db){} +DB_ASSIGNMENT::DB_ASSIGNMENT(DB_CONN* dc) : + DB_BASE("assignment", dc?dc:&boinc_db){} DB_TRANSITIONER_ITEM_SET::DB_TRANSITIONER_ITEM_SET(DB_CONN* dc) : DB_BASE_SPECIAL(dc?dc:&boinc_db){} DB_VALIDATOR_ITEM_SET::DB_VALIDATOR_ITEM_SET(DB_CONN* dc) : @@ -114,6 +117,7 @@ int DB_WORKUNIT::get_id() {return id;} int DB_RESULT::get_id() {return id;} int DB_MSG_FROM_HOST::get_id() {return id;} int DB_MSG_TO_HOST::get_id() {return id;} +int DB_ASSIGNMENT::get_id() {return id;} void DB_PLATFORM::db_print(char* buf){ sprintf(buf, @@ -873,6 +877,36 @@ void DB_MSG_TO_HOST::db_parse(MYSQL_ROW& r) { strcpy2(xml, r[i++]); } +void DB_ASSIGNMENT::db_print(char* buf) { + sprintf(buf, + "create_time=%d, " + "target_id=%d, " + "target_type=%d, " + "multi=%d, " + "workunitid=%d, " + "resultid=%d", + create_time, + target_id, + target_type, + multi, + workunitid, + resultid + ); +} + +void DB_ASSIGNMENT::db_parse(MYSQL_ROW& r) { + int i=0; + clear(); + id = atoi(r[i++]); + create_time = atoi(r[i++]); + target_id = atoi(r[i++]); + target_type = atoi(r[i++]); + multi = atoi(r[i++]); + workunitid = atoi(r[i++]); + resultid = atoi(r[i++]); +} + + void TRANSITIONER_ITEM::parse(MYSQL_ROW& r) { int i=0; clear(); @@ -1595,6 +1629,7 @@ int DB_SCHED_RESULT_ITEM_SET::update_workunits() { ); for (i=0; ienum('assignment', 'BoincAssignment', $where_clause); + } +} define("NVALIDATE_STATES", 6); @@ -1386,4 +1392,5 @@ function host_name_by_id($hostid) { } } +$cvs_version_tracker[]="\$Id$"; //Generated automatically - do not edit ?> diff --git a/html/ops/assign.php b/html/ops/assign.php new file mode 100644 index 0000000000..e4a7068c74 --- /dev/null +++ b/html/ops/assign.php @@ -0,0 +1,60 @@ +create_time); + switch ($asgn->target_type) { + case 0: + $x = "All hosts"; + break; + case 1: + $x = "id>Host $asgn->target_id"; + break; + case 2: + if ($asgn->multi) { + $y = "All hosts belonging to "; + } else { + $y = "One host belonging to "; + } + $x = "$ytarget_id>Host $asgn->target_id"; + break; + case 3: + if ($asgn->multi) { + $y = "All hosts belonging to "; + } else { + $y = "One host belonging to "; + } + $x = "$ytarget_id>Team $asgn->target_id"; + break; + } + echo " + $asgn->id (created $when) + $x + workunitid>$asgn->workunitid + resultid>$asgn->resultid + + "; +} + +function show_assigns() { + admin_page_head("Assignments"); + $asgns = BoincAssignment::enum(); + start_table(); + table_header("Assignment ID/time", "target", "workunit", "result"); + foreach ($asgns as $asgn) { + show_assign($asgn); + } + end_table(); + admin_page_tail(); +} + +$action = get_str('action', true); +switch ($action) { +default: + show_assigns(); +} + +?> diff --git a/html/ops/db_update.php b/html/ops/db_update.php index a8dd4fcea5..b5d3430372 100755 --- a/html/ops/db_update.php +++ b/html/ops/db_update.php @@ -541,11 +541,25 @@ function update_12_28_2007() { "); } +function update_2_18_2008() { + do_query("create table assignment ( + id integer not null auto_increment, + create_time integer not null, + target_id integer not null, + target_type integer not null, + multi tinyint not null, + workunitid integer not null, + resultid integer not null, + primary key (id) + ) engine = InnoDB + "); +} + // modify the following to call the function you want. // Make sure you do all needed functions, in order. // (Look at your DB structure using "explain" queries to see // which ones you need). -//update_11_14_2007(); +//update_2_18_2008(); ?> diff --git a/lib/util.C b/lib/util.C index 958db84493..9fe1e606a8 100644 --- a/lib/util.C +++ b/lib/util.C @@ -312,17 +312,13 @@ void get_sandbox_account_token() { password_str = r_base64_decode(encoded_password_str); if (string::npos != encoded_username_str.find('\\')) { - domainname_str = - encoded_username_str.substr( - 0, - encoded_username_str.find('\\') - ); - username_str = - encoded_username_str.substr( - encoded_username_str.rfind(_T('\\')) + 1, - encoded_username_str.length() - encoded_username_str.rfind(_T('\\')) - 1 - ); - + domainname_str = encoded_username_str.substr( + 0, encoded_username_str.find('\\') + ); + username_str = encoded_username_str.substr( + encoded_username_str.rfind(_T('\\')) + 1, + encoded_username_str.length() - encoded_username_str.rfind(_T('\\')) - 1 + ); retval = LogonUserEx( username_str.c_str(), domainname_str.c_str(), @@ -337,7 +333,6 @@ void get_sandbox_account_token() { ); } else { username_str = encoded_username_str; - retval = LogonUserEx( username_str.c_str(), NULL, diff --git a/sched/Makefile.am b/sched/Makefile.am index a0cfde3754..4b34dd806b 100644 --- a/sched/Makefile.am +++ b/sched/Makefile.am @@ -71,10 +71,12 @@ cgi_SOURCES = \ hr_info.C \ main.C \ sched_array.C \ + sched_assign.C \ sched_hr.C \ sched_resend.C \ - sched_send.C \ sched_locality.C \ + sched_result.C \ + sched_send.C \ sched_timezone.C \ server_types.C \ time_stats_log.C \ @@ -184,17 +186,19 @@ fcgi_SOURCES = \ hr.C \ hr_info.C \ main.C \ - sched_send.C \ - sched_resend.C \ sched_array.C \ - sched_hr.C \ - server_types.C \ - sched_shmem.C \ - sched_util.C \ + sched_assign.C \ sched_config.C \ - sched_msgs.C \ + sched_hr.C \ sched_locality.C \ + sched_msgs.C \ + sched_resend.C \ + sched_result.C \ + sched_send.C \ + sched_shmem.C \ sched_timezone.C \ + sched_util.C \ + server_types.C \ time_stats_log.C \ edf_sim.C \ ../db/boinc_db.C \ diff --git a/sched/feeder.C b/sched/feeder.C index 2247dfb4b9..b0ae61b574 100644 --- a/sched/feeder.C +++ b/sched/feeder.C @@ -659,10 +659,12 @@ int main(int argc, char** argv) { "read " "%d platforms, " "%d apps, " - "%d app_versions\n", + "%d app_versions, " + "%d assignments\n", ssp->nplatforms, ssp->napps, - ssp->napp_versions + ssp->napp_versions, + ssp->nassignments ); log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL, "Using %d job slots\n", ssp->max_wu_results diff --git a/sched/file_upload_handler.C b/sched/file_upload_handler.C index e87f004989..a41512f4fe 100644 --- a/sched/file_upload_handler.C +++ b/sched/file_upload_handler.C @@ -553,7 +553,7 @@ int handle_request(FILE* in, R_RSA_PUBLIC_KEY& key) { } if (!did_something) { log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL, "handle_request: no command\n"); - return return_error(ERR_PERMANENT, "no command"); + return return_error(ERR_TRANSIENT, "no command"); } log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG, "elapsed time %f seconds\n", elapsed_wallclock_time()); diff --git a/sched/handle_request.C b/sched/handle_request.C index 2a9c962e93..58257cc09c 100644 --- a/sched/handle_request.C +++ b/sched/handle_request.C @@ -52,6 +52,7 @@ using namespace std; #include "sched_send.h" #include "sched_config.h" #include "sched_locality.h" +#include "sched_result.h" #include "time_stats_log.h" #ifdef _USING_FCGI_ @@ -766,296 +767,6 @@ int handle_global_prefs(SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply) { return 0; } - -// handle completed results -// -int handle_results(SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply) { - DB_SCHED_RESULT_ITEM_SET result_handler; - SCHED_RESULT_ITEM* srip; - unsigned int i; - int retval; - RESULT* rp; - bool changed_host=false; - - if (sreq.results.size() == 0) return 0; - - // copy reported results to a separate vector, "result_handler", - // initially with only the "name" field present - // - for (i=0; iname, &srip); - if (retval) { - log_messages.printf( - SCHED_MSG_LOG::MSG_CRITICAL, - "[HOST#%d] [RESULT#? %s] can't find result\n", - reply.host.id, rp->name - ); - - reply.result_acks.push_back(std::string(rp->name)); - continue; - } - - log_messages.printf( - SCHED_MSG_LOG::MSG_NORMAL, "[HOST#%d] [RESULT#%d %s] got result (DB: server_state=%d outcome=%d client_state=%d validate_state=%d delete_state=%d)\n", - reply.host.id, srip->id, srip->name, srip->server_state, srip->outcome, srip->client_state, srip->validate_state, srip->file_delete_state - ); - - // Do various sanity checks. - // If one of them fails, set srip->id = 0, - // which suppresses the DB update later on - // - - // If result has server_state OVER - // if outcome NO_REPLY accept it (it's just late). - // else ignore it - // - if (srip->server_state == RESULT_SERVER_STATE_OVER) { - const char *dont_replace_result = NULL; - switch (srip->outcome) { - case RESULT_OUTCOME_INIT: - // should never happen! - dont_replace_result = "this result was never sent"; - break; - case RESULT_OUTCOME_SUCCESS: - // don't replace a successful result! - dont_replace_result = "result already reported as success"; - break; - case RESULT_OUTCOME_COULDNT_SEND: - // should never happen! - dont_replace_result = "this result couldn't be sent"; - break; - case RESULT_OUTCOME_CLIENT_ERROR: - // should never happen! - dont_replace_result = "result already reported as error"; - break; - case RESULT_OUTCOME_CLIENT_DETACHED: - case RESULT_OUTCOME_NO_REPLY: - // result is late in arriving, but keep it anyhow - break; - case RESULT_OUTCOME_DIDNT_NEED: - // should never happen - dont_replace_result = "this result wasn't sent (not needed)"; - break; - case RESULT_OUTCOME_VALIDATE_ERROR: - // we already passed through the validator, so - // don't keep the new result - dont_replace_result = "result already reported, validate error"; - break; - default: - dont_replace_result = "server logic bug; please alert BOINC developers"; - break; - } - if (dont_replace_result) { - char buf[256]; - log_messages.printf( - SCHED_MSG_LOG::MSG_CRITICAL, - "[HOST#%d] [RESULT#%d %s] result already over [outcome=%d validate_state=%d]: %s\n", - reply.host.id, srip->id, srip->name, srip->outcome, srip->validate_state, dont_replace_result - ); - sprintf(buf, "Completed result %s refused: %s", srip->name, dont_replace_result); - USER_MESSAGE um(buf, "high"); - reply.insert_message(um); - srip->id = 0; - reply.result_acks.push_back(std::string(rp->name)); - continue; - } - } - - if (srip->server_state == RESULT_SERVER_STATE_UNSENT) { - log_messages.printf( - SCHED_MSG_LOG::MSG_CRITICAL, - "[HOST#%d] [RESULT#%d %s] got unexpected result: server state is %d\n", - reply.host.id, srip->id, srip->name, srip->server_state - ); - srip->id = 0; - reply.result_acks.push_back(std::string(rp->name)); - continue; - } - - if (srip->received_time) { - log_messages.printf( - SCHED_MSG_LOG::MSG_CRITICAL, - "[HOST#%d] [RESULT#%d %s] got result twice\n", - reply.host.id, srip->id, srip->name - ); - srip->id = 0; - reply.result_acks.push_back(std::string(rp->name)); - continue; - } - - if (srip->hostid != reply.host.id) { - log_messages.printf( - SCHED_MSG_LOG::MSG_CRITICAL, - "[HOST#%d] [RESULT#%d %s] got result from wrong host; expected [HOST#%d]\n", - reply.host.id, srip->id, srip->name, srip->hostid - ); - DB_HOST result_host; - retval = result_host.lookup_id(srip->hostid); - - if (retval) { - log_messages.printf( - SCHED_MSG_LOG::MSG_CRITICAL, - "[RESULT#%d %s] Can't lookup [HOST#%d]\n", - srip->id, srip->name, srip->hostid - ); - srip->id = 0; - reply.result_acks.push_back(std::string(rp->name)); - continue; - } else if (result_host.userid != reply.host.userid) { - log_messages.printf( - SCHED_MSG_LOG::MSG_CRITICAL, - "[USER#%d] [HOST#%d] [RESULT#%d %s] Not even the same user; expected [USER#%d]\n", - reply.host.userid, reply.host.id, srip->id, srip->name, result_host.userid - ); - srip->id = 0; - reply.result_acks.push_back(std::string(rp->name)); - continue; - } else { - log_messages.printf( - SCHED_MSG_LOG::MSG_CRITICAL, - "[HOST#%d] [RESULT#%d %s] Allowing result because same USER#%d\n", - reply.host.id, srip->id, srip->name, reply.host.userid - ); - changed_host = true; - } - } // hostids do not match - - // Modify the in-memory copy obtained from the DB earlier. - // If we found a problem above, - // we have continued and skipped this modify - // - srip->hostid = reply.host.id; - srip->teamid = reply.user.teamid; - srip->received_time = time(0); - srip->client_state = rp->client_state; - srip->cpu_time = rp->cpu_time; - - // check for impossible CPU time - // - double elapsed_time = srip->received_time - srip->sent_time; - if (elapsed_time < 0) { - log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL, - "[HOST#%d] [RESULT#%d] inconsistent sent/received times\n", srip->hostid, srip->id - ); - } else { - if (srip->cpu_time > elapsed_time) { - log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL, - "[HOST#%d] [RESULT#%d] excessive CPU time: reported %f > elapsed %f%s\n", - srip->hostid, srip->id, srip->cpu_time, elapsed_time, changed_host?" [OK: HOST changed]":"" - ); - if (!changed_host) srip->cpu_time = elapsed_time; - } - } - - srip->exit_status = rp->exit_status; - srip->app_version_num = rp->app_version_num; - if (rp->fpops_cumulative || rp->intops_cumulative) { - srip->claimed_credit = fpops_to_credit(rp->fpops_cumulative, rp->intops_cumulative); - } else if (rp->fpops_per_cpu_sec || rp->intops_per_cpu_sec) { - srip->claimed_credit = fpops_to_credit( - rp->fpops_per_cpu_sec*srip->cpu_time, - rp->intops_per_cpu_sec*srip->cpu_time - ); - } else { - srip->claimed_credit = srip->cpu_time * reply.host.claimed_credit_per_cpu_sec; - } - log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG, - "cpu %f cpcs %f, cc %f\n", srip->cpu_time, reply.host.claimed_credit_per_cpu_sec, srip->claimed_credit - ); - srip->server_state = RESULT_SERVER_STATE_OVER; - - strlcpy(srip->stderr_out, rp->stderr_out, sizeof(srip->stderr_out)); - strlcpy(srip->xml_doc_out, rp->xml_doc_out, sizeof(srip->xml_doc_out)); - - // look for exit status and app version in stderr_out - // (historical - can be deleted at some point) - // - parse_int(srip->stderr_out, "", srip->exit_status); - parse_int(srip->stderr_out, "", srip->app_version_num); - - if ((srip->client_state == RESULT_FILES_UPLOADED) && (srip->exit_status == 0)) { - srip->outcome = RESULT_OUTCOME_SUCCESS; - log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG, - "[RESULT#%d %s]: setting outcome SUCCESS\n", - srip->id, srip->name - ); - reply.got_good_result(); - } else { - log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG, - "[RESULT#%d %s]: client_state %d exit_status %d; setting outcome ERROR\n", - srip->id, srip->name, srip->client_state, srip->exit_status - ); - srip->outcome = RESULT_OUTCOME_CLIENT_ERROR; - srip->validate_state = VALIDATE_STATE_INVALID; - reply.got_bad_result(); - } - } // loop over all incoming results - - // Update the result records - // (skip items that we previously marked to skip) - // - for (i=0; i 0) { @@ -1519,8 +1231,9 @@ void process_request( reply.set_delay(1.01*config.min_sendwork_interval); } } + fprintf(stderr, "FOO 2 %d\n", ok_to_send_work); if (ok_to_send_work) { - send_work(sreq, reply, platforms, ss); + send_work(sreq, reply, ss); } } @@ -1554,10 +1267,11 @@ void handle_request( if (sreq.parse(fin) == 0){ log_messages.printf( SCHED_MSG_LOG::MSG_NORMAL, - "Handling request: IP %s, auth %s, host %d, platform %s, version %d.%d.%d\n", + "Handling request: IP %s, auth %s, host %d, platform %s, version %d.%d.%d, work req %d sec\n", get_remote_addr(), sreq.authenticator, sreq.hostid, sreq.platform.name, sreq.core_client_major_version, sreq.core_client_minor_version, - sreq.core_client_release + sreq.core_client_release, + (int)sreq.work_req_seconds ); process_request(sreq, sreply, ss, code_sign_key); diff --git a/sched/sched_array.C b/sched/sched_array.C index 6e96622d0d..6eb48eee6e 100644 --- a/sched/sched_array.C +++ b/sched/sched_array.C @@ -44,8 +44,7 @@ // send only results that were previously infeasible for some host // void scan_work_array( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, - SCHED_SHMEM& ss + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss ) { int i, j, retval, n, rnd_off; WORKUNIT wu; @@ -125,7 +124,7 @@ void scan_work_array( // Find the app and app_version for the client's platform. // If none, treat the WU as infeasible // - if (anonymous(platforms.list[0])) { + if (anonymous(sreq.platforms.list[0])) { app = ss.lookup_app(wu.appid); found = sreq.has_version(*app); if (!found) { @@ -133,7 +132,7 @@ void scan_work_array( } avp = NULL; } else { - found = find_app_version(reply.wreq, wu, platforms, ss, app, avp); + found = find_app_version(sreq, reply.wreq, wu, ss, app, avp); if (!found) { continue; } @@ -264,7 +263,7 @@ void scan_work_array( } retval = add_result_to_reply( - result, wu, sreq, reply, platforms, app, avp + result, wu, sreq, reply, app, avp ); // add_result_to_reply() fails only in fairly pathological cases - diff --git a/sched/sched_array.h b/sched/sched_array.h index 4b23a6c358..d11b959e53 100644 --- a/sched/sched_array.h +++ b/sched/sched_array.h @@ -1,3 +1,3 @@ extern void scan_work_array( - SCHEDULER_REQUEST&, SCHEDULER_REPLY&, PLATFORM_LIST&, SCHED_SHMEM& + SCHEDULER_REQUEST&, SCHEDULER_REPLY&, SCHED_SHMEM& ); diff --git a/sched/sched_assign.C b/sched/sched_assign.C new file mode 100644 index 0000000000..b5fa383c4f --- /dev/null +++ b/sched/sched_assign.C @@ -0,0 +1,169 @@ +// 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 + +#include "boinc_db.h" +#include "crypt.h" +#include "backend_lib.h" +#include "error_numbers.h" + +#include "server_types.h" +#include "main.h" +#include "sched_msgs.h" +#include "sched_send.h" + +#include "sched_assign.h" + +static int send_assigned_job( + ASSIGNMENT& asg, SCHEDULER_REQUEST& request, SCHEDULER_REPLY& reply +) { + int retval; + DB_WORKUNIT wu; + char rtfpath[256], suffix[256], path[256], buf[256]; + static bool first=true; + static int seqno=0; + static R_RSA_PRIVATE_KEY key; + APP* app; + APP_VERSION* avp; + + if (first) { + first = false; + sprintf(path, "%s/upload_private", config.key_dir); + retval = read_key_file(path, key); + if (retval) { + log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL, "can't read key\n"); + return -1; + } + + } + retval = wu.lookup_id(asg.workunitid); + if (retval) { + log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL, "ERROR: WU NOT FOUND\n"); + return retval; + } + app = ssp->lookup_app(wu.appid); + if (!app) { + log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL, "ERROR: APP NOT FOUND\n"); + return ERR_NOT_FOUND; + } + bool found = find_app_version(request, reply.wreq, wu, *ssp, app, avp); + if (!found) { + log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL, "ERROR: APP VERSION NOT FOUND\n"); + return ERR_NOT_FOUND; + } + + sprintf(rtfpath, "../%s", wu.result_template_file); + sprintf(suffix, "%d_%d_%d", getpid(), time(0), seqno++); + retval = create_result(wu, rtfpath, suffix, key, config, 0, 0); + if (retval) { + log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL, + "[WU#%d %s] create_result() %d\n", wu.id, wu.name, retval + ); + return retval; + } + int result_id = boinc_db.insert_id(); + DB_RESULT result; + retval = result.lookup_id(result_id); + add_result_to_reply(result, wu, request, reply, app, avp); + + // if this is a one-job assignment, fill in assignment.resultid + // so that it doesn't get sent again + // + if (!asg.multi) { + DB_ASSIGNMENT db_asg; + db_asg.id = asg.id; + sprintf(buf, "resultid=%d", result_id); + retval = db_asg.update_field(buf); + if (retval) { + log_messages.printf(SCHED_MSG_LOG::MSG_CRITICAL, "ERROR: ASGN UPDATE\n"); + return retval; + } + asg.resultid = result_id; + } + log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG, + "[WU#%d] [RESULT#%d] [HOST#%d] send assignment %d\n", + wu.id, result_id, reply.host.id + ); + return 0; +} + +// Send this host any jobs assigned to it, or to its user/team +// Return true iff we sent anything +// +bool send_assigned_jobs(SCHEDULER_REQUEST& request, SCHEDULER_REPLY& reply) { + DB_RESULT result; + int retval; + char buf[256]; + bool sent_something = false; + + for (int i=0; inassignments; i++) { + ASSIGNMENT& asg = ssp->assignments[i]; + + // see if this assignment applies to this host + // + if (asg.resultid) continue; + switch (asg.target_type) { + case ASSIGN_NONE: + sprintf(buf, "hostid=%d and workunitid=%d", + reply.host.id, asg.workunitid + ); + retval = result.lookup(buf); + if (retval == ERR_NOT_FOUND) { + retval = send_assigned_job(asg, request, reply); + if (!retval) sent_something = true; + } + break; + case ASSIGN_HOST: + if (reply.host.id != asg.target_id) continue; + sprintf(buf, "workunitid=%d", asg.workunitid); + retval = result.lookup(buf); + if (retval == ERR_NOT_FOUND) { + retval = send_assigned_job(asg, request, reply); + if (!retval) sent_something = true; + } + break; + case ASSIGN_USER: + if (reply.user.id != asg.target_id) continue; + if (asg.multi) { + sprintf(buf, "workunitid=%d and hostid=%d", asg.workunitid, reply.host.id); + } else { + sprintf(buf, "workunitid=%d", asg.workunitid); + } + retval = result.lookup(buf); + if (retval == ERR_NOT_FOUND) { + retval = send_assigned_job(asg, request, reply); + if (!retval) sent_something = true; + } + break; + case ASSIGN_TEAM: + if (reply.team.id != asg.target_id) continue; + if (asg.multi) { + sprintf(buf, "workunitid=%d and hostid=%d", asg.workunitid, reply.host.id); + } else { + sprintf(buf, "workunitid=%d", asg.workunitid); + } + retval = result.lookup(buf); + if (retval == ERR_NOT_FOUND) { + retval = send_assigned_job(asg, request, reply); + if (!retval) sent_something = true; + } + break; + } + } + return sent_something; +} diff --git a/sched/sched_assign.h b/sched/sched_assign.h new file mode 100644 index 0000000000..5ba9f338e9 --- /dev/null +++ b/sched/sched_assign.h @@ -0,0 +1,20 @@ +// 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 + +extern bool send_assigned_jobs(SCHEDULER_REQUEST&, SCHEDULER_REPLY&); diff --git a/sched/sched_config.C b/sched/sched_config.C index d9c4ad1c8b..fd35e913d0 100644 --- a/sched/sched_config.C +++ b/sched/sched_config.C @@ -140,6 +140,7 @@ int SCHED_CONFIG::parse(FILE* f) { if (xp.parse_str(tag, "httpd_user", httpd_user, sizeof(httpd_user))) continue; if (xp.parse_int(tag, "file_deletion_strategy", file_deletion_strategy)) continue; if (xp.parse_bool(tag, "request_time_stats_log", request_time_stats_log)) continue; + if (xp.parse_bool(tag, "enable_assignment", enable_assignment)) continue; // don't complain about unparsed XML; // there are lots of tags the scheduler doesn't know about diff --git a/sched/sched_config.h b/sched/sched_config.h index 7a28e655bc..10dec81245 100644 --- a/sched/sched_config.h +++ b/sched/sched_config.h @@ -110,6 +110,7 @@ public: int file_deletion_strategy; // select method of automatically deleting files from host bool request_time_stats_log; + bool enable_assignment; int parse(FILE*); int parse_file(const char* dir="."); diff --git a/sched/sched_locality.C b/sched/sched_locality.C index cb8d7c1a1d..0c12e4a619 100644 --- a/sched/sched_locality.C +++ b/sched/sched_locality.C @@ -277,8 +277,7 @@ int decrement_disk_space_locality( // static int possibly_send_result( DB_RESULT& result, - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, - SCHED_SHMEM& ss + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss ) { DB_WORKUNIT wu; DB_RESULT result2; @@ -290,11 +289,9 @@ static int possibly_send_result( retval = wu.lookup_id(result.workunitid); if (retval) return ERR_DB_NOT_FOUND; - retval = get_app_version( - wu, app, avp, sreq, reply, platforms, ss - ); + retval = get_app_version(wu, app, avp, sreq, reply, ss); - if (retval==ERR_NO_APP_VERSION && anonymous(platforms.list[0])) { + if (retval==ERR_NO_APP_VERSION && anonymous(sreq.platforms.list[0])) { char help_msg_buf[512]; sprintf(help_msg_buf, "To get more %s work, finish current work, stop BOINC, remove app_info.xml file, and restart.", config.long_name); USER_MESSAGE um(help_msg_buf, "high"); @@ -319,7 +316,7 @@ static int possibly_send_result( if (count > 0) return ERR_WU_USER_RULE; } - return add_result_to_reply(result, wu, sreq, reply, platforms, app, avp); + return add_result_to_reply(result, wu, sreq, reply, app, avp); } // returns true if the work generator can not make more work for this @@ -497,7 +494,7 @@ static void flag_for_possible_removal(char* filename) { static int send_results_for_file( char* filename, int& nsent, - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss, bool /*in_working_set*/ ) { @@ -652,9 +649,7 @@ static int send_results_for_file( // we found an unsent result, so try sending it. // This *should* always work. // - retval_send = possibly_send_result( - result, sreq, reply, platforms, ss - ); + retval_send = possibly_send_result(result, sreq, reply, ss); boinc_db.commit_transaction(); // if no app version or not enough resources, give up completely @@ -717,7 +712,7 @@ static int send_results_for_file( // min_resultname = R.filename; // static int send_new_file_work_deterministic_seeded( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss, int& nsent, const char *start_f, const char *end_f ) { DB_RESULT result; @@ -758,7 +753,7 @@ static int send_new_file_work_deterministic_seeded( ); retval = send_results_for_file( - filename, nsent, sreq, reply, platforms, ss, false + filename, nsent, sreq, reply, ss, false ); if (retval==ERR_NO_APP_VERSION || retval==ERR_INSUFFICIENT_RESOURCE) return retval; @@ -795,8 +790,7 @@ static bool is_host_slow(SCHEDULER_REQUEST& sreq) { // if it has not sent any new work. // static int send_new_file_work_deterministic( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, - SCHED_SHMEM& ss + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss ) { char start_filename[256]; int getfile_retval, nsent=0; @@ -810,7 +804,7 @@ static int send_new_file_work_deterministic( // start deterministic search with randomly chosen filename, go to // lexical maximum - send_new_file_work_deterministic_seeded(sreq, reply, platforms, ss, nsent, start_filename, NULL); + send_new_file_work_deterministic_seeded(sreq, reply, ss, nsent, start_filename, NULL); if (nsent) { return 0; } @@ -819,7 +813,7 @@ static int send_new_file_work_deterministic( // filename, continue to randomly choosen one if (!getfile_retval && reply.work_needed(true)) { send_new_file_work_deterministic_seeded( - sreq, reply, platforms, ss, nsent, "", start_filename + sreq, reply, ss, nsent, "", start_filename ); if (nsent) { return 0; @@ -831,8 +825,7 @@ static int send_new_file_work_deterministic( static int send_new_file_work_working_set( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, - SCHED_SHMEM& ss + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss ) { char filename[256]; int retval, nsent; @@ -845,21 +838,21 @@ static int send_new_file_work_working_set( ); return send_results_for_file( - filename, nsent, sreq, reply, platforms, ss, true + filename, nsent, sreq, reply, ss, true ); } // prototype static int send_old_work( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, - SCHED_SHMEM& ss, int t_min, int t_max); + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, + SCHED_SHMEM& ss, int t_min, int t_max +); // The host doesn't have any files for which work is available. // Pick new file to send. Returns nonzero if no work is available. // static int send_new_file_work( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, - SCHED_SHMEM& ss + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss ) { while (reply.work_needed(true)) { @@ -880,7 +873,7 @@ static int send_new_file_work( "send_new_file_work(): try to send old work\n" ); - retval_sow=send_old_work(sreq, reply, platforms, ss, start, end); + retval_sow=send_old_work(sreq, reply, ss, start, end); if (retval_sow==ERR_NO_APP_VERSION || retval_sow==ERR_INSUFFICIENT_RESOURCE) return retval_sow; @@ -890,7 +883,7 @@ static int send_new_file_work( "send_new_file_work(%d): try to send from working set\n", retry ); retry++; - retval_snfwws=send_new_file_work_working_set(sreq, reply, platforms, ss); + retval_snfwws=send_new_file_work_working_set(sreq, reply, ss); if (retval_snfwws==ERR_NO_APP_VERSION || retval_snfwws==ERR_INSUFFICIENT_RESOURCE) return retval_snfwws; } @@ -899,7 +892,7 @@ static int send_new_file_work( log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG, "send_new_file_work(): try deterministic method\n" ); - if (send_new_file_work_deterministic(sreq, reply, platforms, ss)) { + if (send_new_file_work_deterministic(sreq, reply, ss)) { // if no work remains at all, // we learn it here and return nonzero. // @@ -919,7 +912,7 @@ static int send_new_file_work( // t_min=INT_MIN if you wish to leave off the left constraint. // static int send_old_work( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss, int t_min, int t_max ) { char buf[1024], filename[256]; @@ -947,7 +940,7 @@ static int send_old_work( retval = result.lookup(buf); if (!retval) { - retval = possibly_send_result(result, sreq, reply, platforms, ss); + retval = possibly_send_result(result, sreq, reply, ss); boinc_db.commit_transaction(); if (!retval) { double age=(now-result.create_time)/3600.0; @@ -957,7 +950,7 @@ static int send_old_work( extract_retval=extract_filename(result.name, filename); if (!extract_retval) { send_results_for_file( - filename, nsent, sreq, reply, platforms, ss, false + filename, nsent, sreq, reply, ss, false ); } else { // David, is this right? Is this the only place in @@ -1005,8 +998,7 @@ bool file_info_order(const FILE_INFO& fi1, const FILE_INFO& fi2) { } void send_work_locality( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, - SCHED_SHMEM& ss + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss ) { int i, nsent, nfiles, j; @@ -1092,7 +1084,7 @@ void send_work_locality( // if (config.locality_scheduling_send_timeout && sreq.host.n_bwdown>100000) { int until=time(0)-config.locality_scheduling_send_timeout; - int retval_sow=send_old_work(sreq, reply, platforms, ss, INT_MIN, until); + int retval_sow=send_old_work(sreq, reply, ss, INT_MIN, until); if (retval_sow==ERR_NO_APP_VERSION || retval_sow==ERR_INSUFFICIENT_RESOURCE) return; } @@ -1105,7 +1097,7 @@ void send_work_locality( if (!reply.work_needed(true)) break; FILE_INFO& fi = sreq.file_infos[k]; retval_srff=send_results_for_file( - fi.name, nsent, sreq, reply, platforms, ss, false + fi.name, nsent, sreq, reply, ss, false ); if (retval_srff==ERR_NO_APP_VERSION || retval_srff==ERR_INSUFFICIENT_RESOURCE) return; @@ -1142,7 +1134,7 @@ void send_work_locality( // send new files if needed // if (reply.work_needed(true)) { - send_new_file_work(sreq, reply, platforms, ss); + send_new_file_work(sreq, reply, ss); } } diff --git a/sched/sched_locality.h b/sched/sched_locality.h index 97fda760a1..f2775e274d 100644 --- a/sched/sched_locality.h +++ b/sched/sched_locality.h @@ -18,7 +18,7 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA extern void send_work_locality( - SCHEDULER_REQUEST&, SCHEDULER_REPLY&, PLATFORM_LIST&, SCHED_SHMEM& + SCHEDULER_REQUEST&, SCHEDULER_REPLY&, SCHED_SHMEM& ); extern int decrement_disk_space_locality( diff --git a/sched/sched_resend.C b/sched/sched_resend.C index 431af0a2f1..d00e0d5485 100644 --- a/sched/sched_resend.C +++ b/sched/sched_resend.C @@ -95,8 +95,7 @@ static int possibly_give_result_new_deadline( // Return true if there were any such jobs // bool resend_lost_work( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, - PLATFORM_LIST& platforms, SCHED_SHMEM& ss + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss ) { DB_RESULT result; std::vectorresults; @@ -144,9 +143,7 @@ bool resend_lost_work( reply.wreq.core_client_version = sreq.core_client_major_version*100 + sreq.core_client_minor_version; - retval = get_app_version( - wu, app, avp, sreq, reply, platforms, ss - ); + retval = get_app_version(wu, app, avp, sreq, reply, ss); if (retval) { log_messages.printf( SCHED_MSG_LOG::MSG_CRITICAL, "[HOST#%d] no app version [RESULT#%d]\n", @@ -196,7 +193,7 @@ bool resend_lost_work( reply.insert_message(um); } else { retval = add_result_to_reply( - result, wu, sreq, reply, platforms, app, avp + result, wu, sreq, reply, app, avp ); if (retval) { log_messages.printf( SCHED_MSG_LOG::MSG_CRITICAL, diff --git a/sched/sched_resend.h b/sched/sched_resend.h index f8a9318fed..6ec2f60614 100644 --- a/sched/sched_resend.h +++ b/sched/sched_resend.h @@ -1,4 +1,4 @@ extern bool resend_lost_work( - SCHEDULER_REQUEST&, SCHEDULER_REPLY&, PLATFORM_LIST&, SCHED_SHMEM& + SCHEDULER_REQUEST&, SCHEDULER_REPLY&, SCHED_SHMEM& ); diff --git a/sched/sched_result.C b/sched/sched_result.C new file mode 100644 index 0000000000..3409c4811a --- /dev/null +++ b/sched/sched_result.C @@ -0,0 +1,316 @@ +// 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 + +#include "boinc_db.h" +#include "str_util.h" +#include "parse.h" + +#include "server_types.h" +#include "sched_msgs.h" +#include "sched_util.h" + +#include "sched_result.h" + +// handle completed results +// +int handle_results(SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply) { + DB_SCHED_RESULT_ITEM_SET result_handler; + SCHED_RESULT_ITEM* srip; + unsigned int i; + int retval; + RESULT* rp; + bool changed_host=false; + + if (sreq.results.size() == 0) return 0; + + // copy reported results to a separate vector, "result_handler", + // initially with only the "name" field present + // + for (i=0; iname, &srip); + if (retval) { + log_messages.printf( + SCHED_MSG_LOG::MSG_CRITICAL, + "[HOST#%d] [RESULT#? %s] can't find result\n", + reply.host.id, rp->name + ); + + reply.result_acks.push_back(std::string(rp->name)); + continue; + } + + log_messages.printf( + SCHED_MSG_LOG::MSG_NORMAL, "[HOST#%d] [RESULT#%d %s] got result (DB: server_state=%d outcome=%d client_state=%d validate_state=%d delete_state=%d)\n", + reply.host.id, srip->id, srip->name, srip->server_state, srip->outcome, srip->client_state, srip->validate_state, srip->file_delete_state + ); + + // Do various sanity checks. + // If one of them fails, set srip->id = 0, + // which suppresses the DB update later on + // + + // If result has server_state OVER + // if outcome NO_REPLY accept it (it's just late). + // else ignore it + // + if (srip->server_state == RESULT_SERVER_STATE_OVER) { + const char *dont_replace_result = NULL; + switch (srip->outcome) { + case RESULT_OUTCOME_INIT: + // should never happen! + dont_replace_result = "this result was never sent"; + break; + case RESULT_OUTCOME_SUCCESS: + // don't replace a successful result! + dont_replace_result = "result already reported as success"; + break; + case RESULT_OUTCOME_COULDNT_SEND: + // should never happen! + dont_replace_result = "this result couldn't be sent"; + break; + case RESULT_OUTCOME_CLIENT_ERROR: + // should never happen! + dont_replace_result = "result already reported as error"; + break; + case RESULT_OUTCOME_CLIENT_DETACHED: + case RESULT_OUTCOME_NO_REPLY: + // result is late in arriving, but keep it anyhow + break; + case RESULT_OUTCOME_DIDNT_NEED: + // should never happen + dont_replace_result = "this result wasn't sent (not needed)"; + break; + case RESULT_OUTCOME_VALIDATE_ERROR: + // we already passed through the validator, so + // don't keep the new result + dont_replace_result = "result already reported, validate error"; + break; + default: + dont_replace_result = "server logic bug; please alert BOINC developers"; + break; + } + if (dont_replace_result) { + char buf[256]; + log_messages.printf( + SCHED_MSG_LOG::MSG_CRITICAL, + "[HOST#%d] [RESULT#%d %s] result already over [outcome=%d validate_state=%d]: %s\n", + reply.host.id, srip->id, srip->name, srip->outcome, srip->validate_state, dont_replace_result + ); + sprintf(buf, "Completed result %s refused: %s", srip->name, dont_replace_result); + USER_MESSAGE um(buf, "high"); + reply.insert_message(um); + srip->id = 0; + reply.result_acks.push_back(std::string(rp->name)); + continue; + } + } + + if (srip->server_state == RESULT_SERVER_STATE_UNSENT) { + log_messages.printf( + SCHED_MSG_LOG::MSG_CRITICAL, + "[HOST#%d] [RESULT#%d %s] got unexpected result: server state is %d\n", + reply.host.id, srip->id, srip->name, srip->server_state + ); + srip->id = 0; + reply.result_acks.push_back(std::string(rp->name)); + continue; + } + + if (srip->received_time) { + log_messages.printf( + SCHED_MSG_LOG::MSG_CRITICAL, + "[HOST#%d] [RESULT#%d %s] got result twice\n", + reply.host.id, srip->id, srip->name + ); + srip->id = 0; + reply.result_acks.push_back(std::string(rp->name)); + continue; + } + + if (srip->hostid != reply.host.id) { + log_messages.printf( + SCHED_MSG_LOG::MSG_CRITICAL, + "[HOST#%d] [RESULT#%d %s] got result from wrong host; expected [HOST#%d]\n", + reply.host.id, srip->id, srip->name, srip->hostid + ); + DB_HOST result_host; + retval = result_host.lookup_id(srip->hostid); + + if (retval) { + log_messages.printf( + SCHED_MSG_LOG::MSG_CRITICAL, + "[RESULT#%d %s] Can't lookup [HOST#%d]\n", + srip->id, srip->name, srip->hostid + ); + srip->id = 0; + reply.result_acks.push_back(std::string(rp->name)); + continue; + } else if (result_host.userid != reply.host.userid) { + log_messages.printf( + SCHED_MSG_LOG::MSG_CRITICAL, + "[USER#%d] [HOST#%d] [RESULT#%d %s] Not even the same user; expected [USER#%d]\n", + reply.host.userid, reply.host.id, srip->id, srip->name, result_host.userid + ); + srip->id = 0; + reply.result_acks.push_back(std::string(rp->name)); + continue; + } else { + log_messages.printf( + SCHED_MSG_LOG::MSG_CRITICAL, + "[HOST#%d] [RESULT#%d %s] Allowing result because same USER#%d\n", + reply.host.id, srip->id, srip->name, reply.host.userid + ); + changed_host = true; + } + } // hostids do not match + + // Modify the in-memory copy obtained from the DB earlier. + // If we found a problem above, + // we have continued and skipped this modify + // + srip->hostid = reply.host.id; + srip->teamid = reply.user.teamid; + srip->received_time = time(0); + srip->client_state = rp->client_state; + srip->cpu_time = rp->cpu_time; + + // check for impossible CPU time + // + double elapsed_time = srip->received_time - srip->sent_time; + if (elapsed_time < 0) { + log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL, + "[HOST#%d] [RESULT#%d] inconsistent sent/received times\n", srip->hostid, srip->id + ); + } else { + if (srip->cpu_time > elapsed_time) { + log_messages.printf(SCHED_MSG_LOG::MSG_NORMAL, + "[HOST#%d] [RESULT#%d] excessive CPU time: reported %f > elapsed %f%s\n", + srip->hostid, srip->id, srip->cpu_time, elapsed_time, changed_host?" [OK: HOST changed]":"" + ); + if (!changed_host) srip->cpu_time = elapsed_time; + } + } + + srip->exit_status = rp->exit_status; + srip->app_version_num = rp->app_version_num; + if (rp->fpops_cumulative || rp->intops_cumulative) { + srip->claimed_credit = fpops_to_credit(rp->fpops_cumulative, rp->intops_cumulative); + } else if (rp->fpops_per_cpu_sec || rp->intops_per_cpu_sec) { + srip->claimed_credit = fpops_to_credit( + rp->fpops_per_cpu_sec*srip->cpu_time, + rp->intops_per_cpu_sec*srip->cpu_time + ); + } else { + srip->claimed_credit = srip->cpu_time * reply.host.claimed_credit_per_cpu_sec; + } + log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG, + "cpu %f cpcs %f, cc %f\n", srip->cpu_time, reply.host.claimed_credit_per_cpu_sec, srip->claimed_credit + ); + srip->server_state = RESULT_SERVER_STATE_OVER; + + strlcpy(srip->stderr_out, rp->stderr_out, sizeof(srip->stderr_out)); + strlcpy(srip->xml_doc_out, rp->xml_doc_out, sizeof(srip->xml_doc_out)); + + // look for exit status and app version in stderr_out + // (historical - can be deleted at some point) + // + parse_int(srip->stderr_out, "", srip->exit_status); + parse_int(srip->stderr_out, "", srip->app_version_num); + + if ((srip->client_state == RESULT_FILES_UPLOADED) && (srip->exit_status == 0)) { + srip->outcome = RESULT_OUTCOME_SUCCESS; + log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG, + "[RESULT#%d %s]: setting outcome SUCCESS\n", + srip->id, srip->name + ); + reply.got_good_result(); + } else { + log_messages.printf(SCHED_MSG_LOG::MSG_DEBUG, + "[RESULT#%d %s]: client_state %d exit_status %d; setting outcome ERROR\n", + srip->id, srip->name, srip->client_state, srip->exit_status + ); + srip->outcome = RESULT_OUTCOME_CLIENT_ERROR; + srip->validate_state = VALIDATE_STATE_INVALID; + reply.got_bad_result(); + } + } // loop over all incoming results + + // Update the result records + // (skip items that we previously marked to skip) + // + for (i=0; iid, p->id, app->min_version); if (avp) return true; } log_messages.printf( SCHED_MSG_LOG::MSG_DEBUG, "no app version available: APP#%d PLATFORM#%d min_version %d\n", - app->id, platforms.list[0]->id, app->min_version + app->id, sreq.platforms.list[0]->id, app->min_version ); wreq.no_app_version = true; return false; @@ -593,12 +594,10 @@ bool app_core_compatible(WORK_REQ& wreq, APP_VERSION& av) { } // add the given workunit to a reply. -// look up its app, and make sure there's a version for this platform. // Add the app and app_version to the reply also. // int add_wu_to_reply( - WORKUNIT& wu, SCHEDULER_REPLY& reply, PLATFORM_LIST& , - APP* app, APP_VERSION* avp + WORKUNIT& wu, SCHEDULER_REPLY& reply, APP* app, APP_VERSION* avp ) { int retval; WORKUNIT wu2, wu3; @@ -772,14 +771,13 @@ void SCHEDULER_REPLY::got_bad_result() { int add_result_to_reply( DB_RESULT& result, WORKUNIT& wu, SCHEDULER_REQUEST& request, - SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, - APP* app, APP_VERSION* avp + SCHEDULER_REPLY& reply, APP* app, APP_VERSION* avp ) { int retval; double wu_seconds_filled; bool resent_result = false; - retval = add_wu_to_reply(wu, reply, platforms, app, avp); + retval = add_wu_to_reply(wu, reply, app, avp); if (retval) return retval; // in the scheduling locality case, @@ -927,8 +925,7 @@ int add_result_to_reply( } void send_work( - SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, PLATFORM_LIST& platforms, - SCHED_SHMEM& ss + SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, SCHED_SHMEM& ss ) { char helpful[512]; @@ -945,7 +942,7 @@ void send_work( reply.wreq.beta_only = false; log_messages.printf( - SCHED_MSG_LOG::MSG_NORMAL, + SCHED_MSG_LOG::MSG_DEBUG, "[HOST#%d] got request for %f seconds of work; available disk %f GB\n", reply.host.id, sreq.work_req_seconds, reply.wreq.disk_available/1e9 ); @@ -960,6 +957,12 @@ void send_work( reply.wreq.seconds_to_fill = MIN_SECONDS_TO_SEND; } + if (config.enable_assignment) { + if (send_assigned_jobs(sreq, reply)) { + return; + } + } + if (config.workload_sim && sreq.have_other_results_list) { init_ip_results( sreq.global_prefs.work_buf_min(), reply.host.p_ncpus, sreq.ip_results @@ -968,14 +971,14 @@ void send_work( if (config.locality_scheduling) { reply.wreq.infeasible_only = false; - send_work_locality(sreq, reply, platforms, ss); + send_work_locality(sreq, reply, ss); } else { // give top priority to results that require a 'reliable host' // if (reply.wreq.host_info.reliable) { reply.wreq.reliable_only = true; reply.wreq.infeasible_only = false; - scan_work_array(sreq, reply, platforms, ss); + scan_work_array(sreq, reply, ss); } reply.wreq.reliable_only = false; @@ -990,17 +993,17 @@ void send_work( "[HOST#%d] will accept beta work. Scanning for beta work.\n", reply.host.id ); - scan_work_array(sreq, reply, platforms, ss); + scan_work_array(sreq, reply, ss); } reply.wreq.beta_only = false; // give next priority to results that were infeasible for some other host // reply.wreq.infeasible_only = true; - scan_work_array(sreq, reply, platforms, ss); + scan_work_array(sreq, reply, ss); reply.wreq.infeasible_only = false; - scan_work_array(sreq, reply, platforms, ss); + scan_work_array(sreq, reply, ss); } log_messages.printf( diff --git a/sched/sched_send.h b/sched/sched_send.h index 57a8acb595..8949b59f0f 100644 --- a/sched/sched_send.h +++ b/sched/sched_send.h @@ -20,22 +20,22 @@ extern int get_app_version( WORKUNIT& wu, APP* &app, APP_VERSION* &avp, SCHEDULER_REQUEST& sreq, SCHEDULER_REPLY& reply, - PLATFORM_LIST& platforms, SCHED_SHMEM& ss + SCHED_SHMEM& ss ); extern void send_work( - SCHEDULER_REQUEST&, SCHEDULER_REPLY&, PLATFORM_LIST&, SCHED_SHMEM& + SCHEDULER_REQUEST&, SCHEDULER_REPLY&, SCHED_SHMEM& ); extern int add_result_to_reply( DB_RESULT& result, WORKUNIT& wu, SCHEDULER_REQUEST&, SCHEDULER_REPLY&, - PLATFORM_LIST&, APP* app, APP_VERSION* avp + APP* app, APP_VERSION* avp ); extern bool anonymous(PLATFORM*); extern bool find_app_version( - WORK_REQ& wreq, WORKUNIT& wu, PLATFORM_LIST& platform, SCHED_SHMEM& ss, + SCHEDULER_REQUEST&, WORK_REQ& wreq, WORKUNIT& wu, SCHED_SHMEM& ss, APP*& app, APP_VERSION*& avp ); diff --git a/sched/sched_shmem.C b/sched/sched_shmem.C index 4694755101..2b4a18680c 100644 --- a/sched/sched_shmem.C +++ b/sched/sched_shmem.C @@ -43,10 +43,12 @@ void SCHED_SHMEM::init(int nwu_results) { platform_size = sizeof(PLATFORM); app_size = sizeof(APP); app_version_size = sizeof(APP_VERSION); + assignment_size = sizeof(ASSIGNMENT); wu_result_size = sizeof(WU_RESULT); max_platforms = MAX_PLATFORMS; max_apps = MAX_APPS; max_app_versions = MAX_APP_VERSIONS; + max_assignments = MAX_ASSIGNMENTS; max_wu_results = nwu_results; } @@ -61,10 +63,12 @@ int SCHED_SHMEM::verify() { if (platform_size != sizeof(PLATFORM)) return error_return("platform"); if (app_size != sizeof(APP)) return error_return("app"); if (app_version_size != sizeof(APP_VERSION)) return error_return("app_version"); + if (assignment_size != sizeof(ASSIGNMENT)) return error_return("assignment"); if (wu_result_size != sizeof(WU_RESULT)) return error_return("wu_result"); if (max_platforms != MAX_PLATFORMS) return error_return("max platform"); if (max_apps != MAX_APPS) return error_return("max apps"); - if (max_app_versions != MAX_APP_VERSIONS) return error_return("max app version"); + if (max_app_versions != MAX_APP_VERSIONS) return error_return("max app versions"); + if (max_assignments != MAX_ASSIGNMENTS) return error_return("max assignments"); return 0; } @@ -84,6 +88,7 @@ int SCHED_SHMEM::scan_tables() { DB_PLATFORM platform; DB_APP app; DB_APP_VERSION app_version; + DB_ASSIGNMENT assignment; int n; n = 0; @@ -126,6 +131,15 @@ int SCHED_SHMEM::scan_tables() { } napp_versions = n; + n = 0; + while (!assignment.enumerate()) { + assignments[n++] = assignment; + if (n == MAX_ASSIGNMENTS) { + overflow("assignments", "MAX_ASSIGNMENTS"); + } + } + nassignments = n; + return 0; } diff --git a/sched/sched_shmem.h b/sched/sched_shmem.h index c82674db84..e27a0f9850 100644 --- a/sched/sched_shmem.h +++ b/sched/sched_shmem.h @@ -35,6 +35,7 @@ #define MAX_PLATFORMS 50 #define MAX_APPS 10 #define MAX_APP_VERSIONS 50 +#define MAX_ASSIGNMENTS 10 // Default number of work items in shared mem. // You can configure this in config.xml () @@ -71,20 +72,22 @@ struct SCHED_SHMEM { int platform_size; // sizeof(PLATFORM) int app_size; // sizeof(APP) int app_version_size; // sizeof(APP_VERSION) + int assignment_size; // sizeof(ASSIGNMENT)) int wu_result_size; // sizeof(WU_RESULT) int nplatforms; int napps; double app_weights; int napp_versions; - int ncore_versions; + int nassignments; int max_platforms; int max_apps; int max_app_versions; - int max_core_versions; + int max_assignments; int max_wu_results; PLATFORM platforms[MAX_PLATFORMS]; APP apps[MAX_APPS]; APP_VERSION app_versions[MAX_APP_VERSIONS]; + ASSIGNMENT assignments[MAX_ASSIGNMENTS]; WU_RESULT wu_results[0]; void init(int nwu_results); diff --git a/sched/server_types.h b/sched/server_types.h index 9a8d33211d..995b310a3b 100644 --- a/sched/server_types.h +++ b/sched/server_types.h @@ -182,6 +182,7 @@ struct SCHEDULER_REQUEST { char authenticator[256]; CLIENT_PLATFORM platform; std::vector alt_platforms; + PLATFORM_LIST platforms; char cross_project_id[256]; int hostid; // zero if first RPC int core_client_major_version; diff --git a/sched/transitioner.C b/sched/transitioner.C index 7ed73fd00d..f10e24a24a 100644 --- a/sched/transitioner.C +++ b/sched/transitioner.C @@ -99,6 +99,13 @@ int handle_wu( bool all_over_and_validated, have_new_result_to_validate, do_delete; unsigned int i; + TRANSITIONER_ITEM& wu_item = items[0]; + TRANSITIONER_ITEM wu_item_original = wu_item; + + if (config.enable_assignment && strstr(wu_item.name, "asgn")) { + return 0; + } + // count up the number of results in various states, // and check for timed-out results // @@ -114,9 +121,6 @@ int handle_wu( have_new_result_to_validate = false; int rs, max_result_suffix = -1; - TRANSITIONER_ITEM& wu_item = items[0]; - TRANSITIONER_ITEM wu_item_original = wu_item; - // Scan the WU's results, and find the canonical result if there is one // canonical_result_index = -1; @@ -356,18 +360,18 @@ int handle_wu( char rtfpath[256]; sprintf(rtfpath, "../%s", wu_item.result_template_file); int priority_increase = 0; - if ( nover && config.reliable_priority_on_over ) { - priority_increase = priority_increase + config.reliable_priority_on_over; + if (nover && config.reliable_priority_on_over) { + priority_increase += config.reliable_priority_on_over; } else if (nover && !nerrors && config.reliable_priority_on_over_except_error) { - priority_increase = priority_increase + config.reliable_priority_on_over_except_error; + priority_increase += config.reliable_priority_on_over_except_error; } - retval = create_result( + retval = create_result_ti( wu_item, rtfpath, suffix, key, config, value_buf, priority_increase ); if (retval) { log_messages.printf( SCHED_MSG_LOG::MSG_CRITICAL, - "[WU#%d %s] create_result() %d\n", + "[WU#%d %s] create_result_ti() %d\n", wu_item.id, wu_item.name, retval ); return retval; diff --git a/tools/Makefile.am b/tools/Makefile.am index b712c7df85..95699bdd42 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -2,7 +2,7 @@ include $(top_srcdir)/Makefile.incl bin_PROGRAMS = create_work sign_executable dir_hier_path dir_hier_move -EXTRA_DIST = make_project add xadd update_versions dbcheck_files_exist upgrade makelog.sh cleanlogs.sh vote_monitor +EXTRA_DIST = make_project xadd update_versions dbcheck_files_exist upgrade makelog.sh cleanlogs.sh vote_monitor # TODO: use libboinc for these: diff --git a/tools/backend_lib.C b/tools/backend_lib.C index 07900afae3..276eab1be6 100644 --- a/tools/backend_lib.C +++ b/tools/backend_lib.C @@ -158,7 +158,9 @@ static void write_md5_info( return; } -// process WU template +// fill in the workunit's XML document (wu.xml_doc) +// by scanning the WU template, macro-substituting the input files, +// and putting in the command line element and additional XML // static int process_wu_template( WORKUNIT& wu, @@ -176,6 +178,7 @@ static int process_wu_template( double nbytes; char open_name[256]; bool found=false; + int nfiles_parsed = 0; out = ""; for (p=strtok(tmplate, "\n"); p; p=strtok(0, "\n")) { @@ -202,6 +205,7 @@ static int process_wu_template( ); return ERR_XML_PARSE; } + nfiles_parsed++; if (generated_locally) { sprintf(buf, " %s\n" @@ -345,7 +349,14 @@ static int process_wu_template( } } if (!found) { - fprintf(stderr, "create_work: bad WU template - no \n"); + fprintf(stderr, "process_wu_template: bad WU template - no \n"); + return -1; + } + if (nfiles_parsed != ninfiles) { + fprintf(stderr, + "process_wu_template: %d input files listed, but template has %d\n", + ninfiles, nfiles_parsed + ); return -1; } if (out.size() > sizeof(wu.xml_doc)-1) { @@ -361,7 +372,7 @@ static int process_wu_template( // initialize an about-to-be-created result, given its WU // -static void initialize_result(DB_RESULT& result, TRANSITIONER_ITEM& wu) { +static void initialize_result(DB_RESULT& result, WORKUNIT& wu) { result.id = 0; result.create_time = time(0); result.workunitid = wu.id; @@ -384,18 +395,48 @@ static void initialize_result(DB_RESULT& result, TRANSITIONER_ITEM& wu) { result.batch = wu.batch; } -// Create a new result for the given WU. -// This is called ONLY from the transitioner -// -int create_result( - TRANSITIONER_ITEM& wu, +int create_result_ti( + TRANSITIONER_ITEM& ti, char* result_template_filename, char* result_name_suffix, R_RSA_PRIVATE_KEY& key, SCHED_CONFIG& config, char* query_string, - int priority_increase // if nonzero, write value list here; else do insert + int priority_increase +) { + WORKUNIT wu; + + // copy relevant fields from TRANSITIONER_ITEM to WORKUNIT + // + strcpy(wu.name, ti.name); + wu.id = ti.id; + wu.appid = ti.appid; + wu.priority = ti.priority; + wu.batch = ti.batch; + return create_result( + wu, + result_template_filename, + result_name_suffix, + key, + config, + query_string, + priority_increase + ); +} + +// Create a new result for the given WU. +// This is called ONLY from the transitioner +// +int create_result( + WORKUNIT& wu, + char* result_template_filename, + char* result_name_suffix, + R_RSA_PRIVATE_KEY& key, + SCHED_CONFIG& config, + char* query_string, + // if nonzero, write value list here; else do insert + int priority_increase ) { DB_RESULT result; char base_outfile_name[256]; @@ -562,7 +603,11 @@ int create_work( fprintf(stderr, "no max_success_results given; can't create job\n"); return ERR_NO_OPTION; } - wu.transition_time = time(0); + if (strstr(wu.name, "asgn")) { + wu.transition_time = INT_MAX; + } else { + wu.transition_time = time(0); + } retval = wu.insert(); if (retval) { fprintf(stderr, "create_work: workunit.insert() %d\n", retval); diff --git a/tools/backend_lib.h b/tools/backend_lib.h index f8f77eada6..2b7f333f78 100644 --- a/tools/backend_lib.h +++ b/tools/backend_lib.h @@ -40,6 +40,16 @@ extern int read_filename(const char* path, char* buf, int len); extern void initialize_result(DB_RESULT&, DB_WORKUNIT&); extern int create_result( + WORKUNIT&, + char* result_template_filename, + char* suffix, + R_RSA_PRIVATE_KEY& key, + SCHED_CONFIG& config, + char* query_string=0, + int priority_increase=0 +); + +extern int create_result_ti( TRANSITIONER_ITEM&, char* result_template_filename, char* suffix, diff --git a/tools/create_work.C b/tools/create_work.C index b9059d4780..fecebcb1a3 100644 --- a/tools/create_work.C +++ b/tools/create_work.C @@ -24,24 +24,30 @@ // and there must be a valid config.xml file there // // create_work -// -appname name -// -wu_name name -// -wu_template filename relative to project root; usually in templates/ -// -result_template filename relative to project root; usually in templates/ -// [ -config_dir path ] -// [ -batch n ] +// --appname name +// --wu_name name +// --wu_template filename relative to project root; usually in templates/ +// --result_template filename relative to project root; usually in templates/ +// [ --config_dir path ] +// [ --batch n ] // the following can be supplied in WU template; see defaults below -// [ -rsc_fpops_est n ] -// [ -rsc_fpops_bound n ] -// [ -rsc_memory_bound n ] -// [ -rsc_disk_bound n ] -// [ -delay_bound x ] -// [ -min_quorum x ] -// [ -target_nresults x ] -// [ -max_error_results x ] -// [ -max_total_results x ] -// [ -max_success_results x ] -// [ -additional_xml x ] +// [ --rsc_fpops_est n ] +// [ --rsc_fpops_bound n ] +// [ --rsc_memory_bound n ] +// [ --rsc_disk_bound n ] +// [ --delay_bound x ] +// [ --min_quorum x ] +// [ --target_nresults x ] +// [ --max_error_results x ] +// [ --max_total_results x ] +// [ --max_success_results x ] +// [ --additional_xml x ] +// [ --assign_all ] +// [ --assign_host ID ] +// [ --assign_user_one ID ] +// [ --assign_user_all ID ] +// [ --assign_team_one ID ] +// [ --assign_team_all ID ] // infile1 infile2 ... // @@ -56,6 +62,15 @@ #include "backend_lib.h" #include "sched_config.h" +bool arg(const char** argv, int i, const char* name) { + char buf[256]; + sprintf(buf, "-%s", name); + if (!strcmp(argv[i], buf)) return true; + sprintf(buf, "--%s", name); + if (!strcmp(argv[i], buf)) return true; + return false; +} + int main(int argc, const char** argv) { DB_APP app; DB_WORKUNIT wu; @@ -70,6 +85,10 @@ int main(int argc, const char** argv) { char buf[256]; char additional_xml[256]; SCHED_CONFIG config; + bool assign_flag = false; + bool assign_multi = false; + int assign_id = 0; + int assign_type; strcpy(result_template_file, ""); strcpy(app.name, ""); @@ -94,50 +113,75 @@ int main(int argc, const char** argv) { wu.delay_bound = 100000; while (i < argc) { - if (!strcmp(argv[i], "-appname")) { + if (arg(argv, i, "appname")) { strcpy(app.name, argv[++i]); - } else if (!strcmp(argv[i], "-wu_name")) { + } else if (arg(argv, i, "wu_name")) { strcpy(wu.name, argv[++i]); - } else if (!strcmp(argv[i], "-wu_template")) { + } else if (arg(argv, i, "wu_template")) { strcpy(wu_template_file, argv[++i]); - } else if (!strcmp(argv[i], "-result_template")) { + } else if (arg(argv, i, "result_template")) { strcpy(result_template_file, argv[++i]); - } else if (!strcmp(argv[i], "-batch")) { + } else if (arg(argv, i, "batch")) { wu.batch = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-config_dir")) { + } else if (arg(argv, i, "config_dir")) { config_dir = argv[++i]; - } else if (!strcmp(argv[i], "-batch")) { + } else if (arg(argv, i, "batch")) { wu.batch = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-priority")) { + } else if (arg(argv, i, "priority")) { wu.priority = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-rsc_fpops_est")) { + } else if (arg(argv, i, "rsc_fpops_est")) { wu.rsc_fpops_est = atof(argv[++i]); - } else if (!strcmp(argv[i], "-rsc_fpops_bound")) { + } else if (arg(argv, i, "rsc_fpops_bound")) { wu.rsc_fpops_bound = atof(argv[++i]); - } else if (!strcmp(argv[i], "-rsc_memory_bound")) { + } else if (arg(argv, i, "rsc_memory_bound")) { wu.rsc_memory_bound = atof(argv[++i]); - } else if (!strcmp(argv[i], "-rsc_disk_bound")) { + } else if (arg(argv, i, "rsc_disk_bound")) { wu.rsc_disk_bound = atof(argv[++i]); - } else if (!strcmp(argv[i], "-delay_bound")) { + } else if (arg(argv, i, "delay_bound")) { wu.delay_bound = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-min_quorum")) { + } else if (arg(argv, i, "min_quorum")) { wu.min_quorum = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-target_nresults")) { + } else if (arg(argv, i, "target_nresults")) { wu.target_nresults = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-max_error_results")) { + } else if (arg(argv, i, "max_error_results")) { wu.max_error_results = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-max_total_results")) { + } else if (arg(argv, i, "max_total_results")) { wu.max_total_results = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-max_success_results")) { + } else if (arg(argv, i, "max_success_results")) { wu.max_success_results = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-opaque")) { + } else if (arg(argv, i, "opaque")) { wu.opaque = atoi(argv[++i]); - } else if (!strcmp(argv[i], "-command_line")) { + } else if (arg(argv, i, "command_line")) { command_line= argv[++i]; - } else if (!strcmp(argv[i], "-additional_xml")) { + } else if (arg(argv, i, "additional_xml")) { strcpy(additional_xml, argv[++i]); + } else if (arg(argv, i, "assign_all")) { + assign_flag = true; + assign_type = ASSIGN_NONE; + } else if (arg(argv, i, "assign_host")) { + assign_flag = true; + assign_type = ASSIGN_HOST; + assign_id = atoi(argv[++i]); + } else if (arg(argv, i, "assign_user_one")) { + assign_flag = true; + assign_type = ASSIGN_USER; + assign_id = atoi(argv[++i]); + } else if (arg(argv, i, "assign_user_all")) { + assign_flag = true; + assign_type = ASSIGN_USER; + assign_multi = true; + assign_id = atoi(argv[++i]); + } else if (arg(argv, i, "assign_team_one")) { + assign_flag = true; + assign_type = ASSIGN_TEAM; + assign_id = atoi(argv[++i]); + } else if (arg(argv, i, "assign_team_all")) { + assign_flag = true; + assign_type = ASSIGN_TEAM; + assign_multi = true; + assign_id = atoi(argv[++i]); } else { - if (!strncmp("-",argv[i],1)) { + if (!strncmp("-", argv[i], 1)) { fprintf(stderr, "create_work: bad argument '%s'\n", argv[i]); exit(1); } @@ -158,6 +202,12 @@ int main(int argc, const char** argv) { #undef CHKARG #undef CHKARG_STR + if (assign_flag) { + if (!strstr(wu.name, "asgn")) { + fprintf(stderr, "Assigned WU names must contain 'asgn'\n"); + exit(1); + } + } retval = config.parse_file(config_dir); if (retval) { fprintf(stderr, "Can't parse config file: %d\n", retval); @@ -207,6 +257,20 @@ int main(int argc, const char** argv) { fprintf(stderr, "create_work: %d\n", retval); exit(1); } + if (assign_flag) { + DB_ASSIGNMENT assignment; + assignment.clear(); + assignment.create_time = time(0); + assignment.target_id = assign_id; + assignment.target_type = assign_type; + assignment.multi = assign_multi; + assignment.workunitid = wu.id; + retval = assignment.insert(); + if (retval) { + fprintf(stderr, "assignment.insert() failed: %d\n", retval); + exit(1); + } + } boinc_db.close(); }