diff --git a/checkin_notes b/checkin_notes index cbc5b20630..27f650f468 100755 --- a/checkin_notes +++ b/checkin_notes @@ -5069,3 +5069,17 @@ Karl 2003/06/25 cronjob - complains if anything fails. ./testbase (new) + +Karl 2003/06/26 + - fixed bug that caused client state to transition to + RESULT_FILES_UPLOADING even when file info(s) had errors; now it stays + in RESULT_COMPUTE_DONE. + - test makefile should rebuild version.py and boinc_db.py if necessary + - test_sanity should make sure proxy setup works + - test_masterurl_failure + + test/ + Makefile.am + test_sanity.py + boinc.py + test_masterurl_failure.py diff --git a/client/client_state.C b/client/client_state.C index 0d861cb1fe..8e1fc3ae25 100644 --- a/client/client_state.C +++ b/client/client_state.C @@ -1344,18 +1344,20 @@ bool CLIENT_STATE::update_results() { action = true; } break; - case RESULT_FILES_DOWNLOADED: - // The transition to COMPUTE_DONE is performed - // in app_finished() - break; - case RESULT_COMPUTE_DONE: - // Once the computation has been done, check - // that the necessary files have been uploaded - // before moving on - rp->state = RESULT_FILES_UPLOADING; - action = true; - break; + + // app_finished() transitions to either RESULT_COMPUTE_DONE or + // RESULT_FILES_UPLOADING. RESULT_COMPUTE_DONE is a dead-end state + // indicating we had an error at the end of computation. + + // case RESULT_FILES_DOWNLOADED: + // break; + // case RESULT_COMPUTE_DONE: + // rp->state = RESULT_FILES_UPLOADING; + // action = true; + // break; case RESULT_FILES_UPLOADING: + // Once the computation has been done, check that the necessary + // files have been uploaded before moving on if (rp->is_upload_done()) { rp->ready_to_ack = true; rp->state = RESULT_FILES_UPLOADED; diff --git a/client/cs_apps.C b/client/cs_apps.C index 0dce156181..1c20edfcc9 100644 --- a/client/cs_apps.C +++ b/client/cs_apps.C @@ -1,19 +1,19 @@ // The contents of this file are subject to the Mozilla Public License // Version 1.0 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at -// http://www.mozilla.org/MPL/ -// +// http://www.mozilla.org/MPL/ +// // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations -// under the License. -// -// The Original Code is the Berkeley Open Infrastructure for Network Computing. -// +// under the License. +// +// The Original Code is the Berkeley Open Infrastructure for Network Computing. +// // The Initial Developer of the Original Code is the SETI@home project. -// Portions created by the SETI@home project are Copyright (C) 2002 -// University of California at Berkeley. All Rights Reserved. -// +// Portions created by the SETI@home project are Copyright (C) 2002, 2003 +// University of California at Berkeley. All Rights Reserved. +// // Contributor(s): // @@ -93,40 +93,53 @@ int CLIENT_STATE::app_finished(ACTIVE_TASK& at) { int retval; double size; + bool had_error = false; + for (i=0; ioutput_files.size(); i++) { fip = rp->output_files[i].file_info; get_pathname(fip, path); retval = file_size(path, size); if (retval) { // an output file is unexpectedly absent. - // + // fip->status = retval; - } else { - if (size > fip->max_nbytes) { - msg_printf(at.result->project, MSG_INFO, "Output file %s for result %s exceeds size limit.", - fip->name, at.result->name); + had_error = true; + } else if (size > fip->max_nbytes) { + // Note: this is only checked when the application finishes. there + // is also a check_max_disk_exceeded that is checked while the + // application is running. + msg_printf(rp->project, MSG_INFO, "Output file %s for result %s exceeds size limit.", + fip->name, rp->name); - fip->delete_file(); - fip->status = ERR_FILE_TOO_BIG; + fip->delete_file(); + fip->status = ERR_FILE_TOO_BIG; + had_error = true; + } else { + if (!fip->upload_when_present && !fip->sticky) { + fip->delete_file(); // sets status to NOT_PRESENT } else { - if (!fip->upload_when_present && !fip->sticky) { - fip->delete_file(); // sets status to NOT_PRESENT + retval = md5_file(path, fip->md5_cksum, fip->nbytes); + if (retval) { + fip->status = retval; + had_error = true; } else { - retval = md5_file(path, fip->md5_cksum, fip->nbytes); - if (retval) { - fip->status = retval; - } else { - fip->status = FILE_PRESENT; - } + fip->status = FILE_PRESENT; } } } } - at.result->is_active = false; - at.result->state = RESULT_COMPUTE_DONE; - update_avg_cpu(at.result->project); - at.result->project->exp_avg_cpu += at.result->final_cpu_time; + rp->is_active = false; + if (had_error) { + // dead-end state indicating we had an error at end of computation; + // do not move to RESULT_FILES_UPLOADING + rp->state = RESULT_COMPUTE_DONE; + } else { + // can now upload files. + rp->state = RESULT_FILES_UPLOADING; + } + update_avg_cpu(rp->project); + rp->project->exp_avg_cpu += rp->final_cpu_time; return 0; } diff --git a/test/Makefile.am b/test/Makefile.am index 0a2f0b0800..60e82a3235 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -20,6 +20,8 @@ noinst_SCRIPTS = \ BUILT_SOURCES = boinc_db.inc boinc_db.py +$(TESTS): version.py boinc_db.py + boinc_db.inc: ../db/boinc_db.h ./db_def_to_php < ../db/boinc_db.h > boinc_db.inc diff --git a/test/Makefile.in b/test/Makefile.in index 51ac1e51f3..c8a9a91774 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -415,6 +415,8 @@ uninstall-am: uninstall-info-am $(LIBRSA): cd $(top_builddir)/RSAEuro/source; ${MAKE} librsaeuro.a +$(TESTS): version.py boinc_db.py + boinc_db.inc: ../db/boinc_db.h ./db_def_to_php < ../db/boinc_db.h > boinc_db.inc diff --git a/test/boinc.py b/test/boinc.py index 15d8c15f2d..f6c56034c9 100644 --- a/test/boinc.py +++ b/test/boinc.py @@ -66,20 +66,24 @@ def verbose_sleep(msg, wait): verbose_echo(1, msg + ' [sleep ' + ('.'*i).ljust(wait) + ']') time.sleep(1) -def shell_call(cmd, failok=False): +def shell_call(cmd, doexec=False, failok=False): + if doexec: + os.execl('/bin/sh', 'sh', '-c', cmd) + error("Command failed: "+cmd) + os._exit(1) if os.system(cmd): error("Command failed: "+cmd, fatal=(not failok)) return 1 return 0 -def verbose_shell_call(cmd, failok=False): +def verbose_shell_call(cmd, doexec=False, failok=False): verbose_echo(2, " "+cmd) - return shell_call(cmd, failok) + return shell_call(cmd, doexec, failok) -def proxerize(url, t): +def proxerize(url, t=True): if t: r = re.compile('http://[^/]*/') - return r.sub('http://localhost:080/', url) + return r.sub('http://localhost:8080/', url) else: return url @@ -94,8 +98,10 @@ HTML_DIR = get_env_var("BOINC_HTML_DIR") HOSTS_DIR = get_env_var("BOINC_HOSTS_DIR") def use_cgi_proxy(): + global CGI_URL CGI_URL = proxerize(CGI_URL) def use_html_proxy(): + global HTML_URL HTML_URL = proxerize(HTML_URL) def check_exists(file): @@ -889,23 +895,32 @@ def run_check_all(): all_projects.check() # all_projects.stop() -proxy_pid = 0 -def start_proxy(code): - global proxy_pid - pid = os.fork() - if not pid: - os._exit(verbose_shell_call("./testproxy 8080 localhost:80 '$code' 2>testproxy.log")) - - verbose_sleep("Starting proxy server", 1) - proxy_pid = pid - # check if child process died - (pid,status) = os.waitpid(pid, os.WNOHANG) - if pid: - fatal_error("testproxy failed") -def stop_proxy(): - global proxy_pid - if proxy_pid: - os.kill(2, proxy_pid) +class Proxy: + def __init__(self, code, cgi=0, html=0, start=1): + self.pid = 0 + self.code = code + if cgi: use_cgi_proxy() + if html: use_html_proxy() + if start: self.start() + def start(self): + self.pid = os.fork() + if not self.pid: + verbose_shell_call("exec ./testproxy 8080 localhost:80 '%s' 2>testproxy.log"%self.code, + doexec=True) + verbose_sleep("Starting proxy server", 1) + # check if child process died + (pid,status) = os.waitpid(self.pid, os.WNOHANG) + if pid: + fatal_error("testproxy failed") + atexit.register(self.stop) + def stop(self): + verbose_echo(1, "Stopping proxy server") + if self.pid: + try: + os.kill(self.pid, 2) + except OSError: + verbose_echo(0, "Couldn't kill pid %d" % self.pid) + self.pid = 0 def test_msg(msg): print diff --git a/test/test_masterurl_failure.py b/test/test_masterurl_failure.py new file mode 100755 index 0000000000..ca58c05cd0 --- /dev/null +++ b/test/test_masterurl_failure.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +## $Id$ + +from test_uc import * + +if __name__ == '__main__': + test_msg("scheduler exponential backoff (master url failure)") + proxy = Proxy('exit 1 if $nconnections < 4; if_done_kill(); if_done_ping();', + html=1) + ProjectUC() + run_check_all() + + ## TODO: verify it took ??? seconds + ## TODO: time out after ??? seconds and fail this test diff --git a/test/test_sanity.py b/test/test_sanity.py index 93623c9d1f..659c67293a 100755 --- a/test/test_sanity.py +++ b/test/test_sanity.py @@ -7,12 +7,14 @@ from boinc import * # test makes sure that testing framework is sane -def read_url(url): +def read_url(url, quiet=False): '''return 1 line from url''' + verbose_echo(2, " reading url: "+url) try: return urllib.URLopener().open(url).readline().strip() except IOError, e: - error("couldn't access HTML_URL: %s %s" % (HTML_URL, e)) + if not quiet: + error("couldn't access url: %s %s" % (url, e)) return '' if __name__ == '__main__': @@ -35,6 +37,7 @@ if __name__ == '__main__': html_path = os.path.join(HTML_DIR, 'test_sanity.txt') html_url = os.path.join(HTML_URL, 'test_sanity.txt') + html_proxy_url = proxerize(html_url) cgi_path = os.path.join(CGI_DIR, 'test_sanity_cgi') cgi_url = os.path.join(CGI_URL, 'test_sanity_cgi') @@ -43,6 +46,23 @@ if __name__ == '__main__': if read_url(html_url) != magic: error("couldn't access a file I just wrote: "+html_path+"\n using url: "+html_url) + verbose_echo(1, "Checking proxy setup") + if read_url(html_proxy_url, quiet=True): + error("Another proxy already running") + else: + proxy = Proxy('') + if read_url(html_proxy_url) != magic: + error("couldn't access file using proxy url: "+html_proxy_url) + else: + proxy.stop() + + proxy = Proxy('exit 1 if $nconnections < 2; if_done_kill(); if_done_ping();') + if read_url(html_proxy_url, quiet=True): + error("Proxy should have closed connection #1") + if read_url(html_proxy_url) != magic: + error("Proxy should have allowed connection #2") + proxy.stop() + os.unlink(html_path) verbose_echo(1, "Checking webserver setup: cgi")