Ejemplo n.º 1
0
def email_crash_report():
    """ Construct an email with the details of a tournament crash and send to the designated recipients """

    try:
        cfg = EmailConfig()

        # connect to smtp server
        smtp = SMTP(cfg.smtp_server(), cfg.port(), timeout=10)
        smtp.starttls()
        smtp.login(cfg.sender(), cfg.password())

        message = "Hi,\n\n"
        message += f"The swen-tourney code has raised an exception and has been stopped.\n" \
                   f"Please correct this error and restart the tournament. " \
                   f"Details on this crash can be found at {paths.TRACE_FILE} on {socket.gethostname()}."

        print_tourney_trace(f"\tSending crash report email to {cfg.crash_report_recipients()}")
        _send_email(smtp, cfg.sender(), cfg.crash_report_recipients(), "SWEN Tournament crash", message)
        smtp.close()

    except socket.timeout:
        print_tourney_error("Timeout while trying to connect to SMTP server")
    except (SMTPHeloError, SMTPConnectError):
        print_tourney_error("Cannot connect to SMTP server")
    except SMTPAuthenticationError:
        print_tourney_error("Login attempt failed")
    except OSError as os_error:
        print_tourney_error(f"Error raised while sending emails: {os_error}")
        print_tourney_error("Email sending has been aborted.")
Ejemplo n.º 2
0
 def __init__(self):
     if not os.path.exists(paths.SERVER_CONFIG):
         print_tourney_trace(
             f"No server configuration file found at {paths.SERVER_CONFIG}. "
             f"Using default configuration.")
         ServerConfig.write_default()
         self.server_config = self.default_server_config
     else:
         self.server_config = json.load(open(paths.SERVER_CONFIG, 'r'))
Ejemplo n.º 3
0
def shutdown(message: str) -> Result:
    """ Set the shutdown flag for TourneyDaemon """
    if not is_alive():
        return Result(False, "Tournament is already offline")
    else:
        set_flag(TourneyFlag.SHUTDOWN, True, contents=message)
        print_tourney_trace("Shutdown event received. Finishing processing")
        return Result(
            True, "Tournament is shutting down. "
            "This may take a while as current processing must be completed.\n"
            "Check the tournament traces to see when the tournament has successfully stopped."
        )
Ejemplo n.º 4
0
def queue_submission(submitter: Submitter,
                     submission_time: datetime) -> Result:
    """ Create a submission for a submitter in the paths.STAGED_DIR """

    pre_val_dir = paths.get_pre_validation_dir(submitter)

    staged_dir = f"{paths.STAGING_DIR}/{_create_submission_request_name(submitter, submission_time)}"
    _remove_previous_occurrences(submitter)
    subprocess.run(f"mv {pre_val_dir} {staged_dir}", shell=True, check=True)
    set_flag(SubmissionFlag.SUBMISSION_READY, True, staged_dir)

    trace = f"Submission successfully made by {submitter} at {submission_time.strftime(fmt.DATETIME_TRACE_STRING)}"
    print_tourney_trace(trace)
    return Result(True, trace)
Ejemplo n.º 5
0
def run_submission(submitter: Submitter, submission_time: str,
                   new_tests: [Test], new_progs: [Prog], pool: Pool):
    """
    Run a submission against all other previously made submissions in the tournament.
    The submission has been compared against the submitters prior submission (if any). Only new tests and progs require
    retesting
    :param submitter: the submitter
    :param submission_time: the time of the new submission
    :param new_tests: the list of new tests that need to be run/rerun
    :param new_progs: the list of new programs that need to be run/rerun
    :param pool: the thread pool to use for testing in parallel
    """

    tourney_state = TourneyState()
    other_submitters = [
        sub for sub in tourney_state.get_valid_submitters() if sub != submitter
    ]

    print_tourney_trace(f"Processing submission for {submitter}.")
    print_tourney_trace(f"\tNew tests: {new_tests}")
    print_tourney_trace(f"\tNew progs: {new_progs}")

    tourney_state.set_time_of_submission(submitter, submission_time)
    num_tests = json.load(
        open(f"{paths.get_tourney_dir(submitter)}/{paths.NUM_TESTS_FILE}",
             'r'))
    tourney_state.set_number_of_tests(submitter, num_tests)

    # multiprocessing.Pool.map can only work on one argument, use functools.partial to curry
    # run_tests into functions with just one argument
    rt_new_tests = partial(run_tests,
                           tourney_state=tourney_state,
                           new_tests=new_tests,
                           new_progs=[])
    rt_new_progs = partial(run_tests,
                           tourney_state=tourney_state,
                           new_tests=[],
                           new_progs=new_progs)

    # run submitter tests against others progs
    tester_results = pool.map(rt_new_tests, [(submitter, other)
                                             for other in other_submitters])

    # run others tests against submitters progs
    testee_results = pool.map(rt_new_progs, [(other, submitter)
                                             for other in other_submitters])

    for (tester, testee, test_set) in tester_results + testee_results:
        tourney_state.set(tester, testee, test_set)

    print_tourney_trace(f"Submission from {submitter} tested")
    tourney_state.save_to_file()
Ejemplo n.º 6
0
def main():
    """ Run the results server thread """

    try:
        time.sleep(5)
        print_tourney_trace("Starting the results server")
        if not os.path.exists(paths.RESULTS_FILE):
            TourneySnapshot(report_time=datetime.now()).write_snapshot()
        server_config = ServerConfig()
        server_address = ('', server_config.port())
        httpd = ThreadedHTTPServer(server_address, TourneyResultsHandler)
        threading.Thread(target=_server_assassin, args=[httpd],
                         daemon=True).start()
        httpd.serve_forever()
        print_tourney_trace("Shutting down the results server")
    except Exception as exception:  # pylint: disable=broad-except
        print_tourney_error("Exception caught while running Results Server")
        print_tourney_error(str(exception))
        print_tourney_error(traceback.format_exc())
        # wait 5 minutes to ensure the port is freed
        time.sleep(300)
        main()
Ejemplo n.º 7
0
def main():
    """
    TourneyDaemon constantly polls the paths.STAGED_DIR for files. If present, the oldest file
    (i.e. the earliest submission) is popped and processed.
    """
    print_tourney_trace("TourneyDaemon started...")

    # Thread pool for parallel processing. initargs contains a concurrency safe counter, used by set_process_name
    pool = Pool(initializer=_set_process_name,
                initargs=(Value('i', 0, lock=True), ))

    try:
        set_flag(TourneyFlag.ALIVE, True)
        set_flag(TourneyFlag.SHUTDOWN, False)

        # Create a snapshot file on startup
        TourneySnapshot(report_time=datetime.now()).write_snapshot()

        while not get_flag(TourneyFlag.SHUTDOWN):

            if not get_flag(TourneyFlag.ALIVE):
                # In the event of an uncaught crash the ALIVE flag can be manually deleted to kill the tournament
                break

            next_submission_to_process = fs_queue.get_next_request()

            if next_submission_to_process:
                file_path = f"{paths.STAGING_DIR}/{next_submission_to_process}"
                if fs_queue.is_submission(file_path):
                    _process_submission_request(file_path, pool)
                else:
                    # submission is present in the staged folder, but has not finished being copied across
                    print_tourney_trace(
                        f"Request present but not valid: {next_submission_to_process}"
                    )
                    sleep(5)
            else:
                print_tourney_trace("Nothing to process")
                sleep(60)
    except Exception as exception:  # pylint: disable=broad-except
        print_tourney_error("Exception caught while running tournament")
        print_tourney_error(str(exception))
        print_tourney_error(traceback.format_exc())
        # emailer.email_crash_report()

    # shutdown hook
    print_tourney_trace("TourneyDaemon shutting down.")
    set_flag(TourneyFlag.ALIVE, False)
Ejemplo n.º 8
0
def create_results_csv():
    """ Output the results of the tournament to a csv file """
    print_tourney_trace(f"Exporting tournament results to {paths.CSV_FILE}")
    TourneySnapshot(report_time=datetime.now()).write_csv()
    return Result(True, f"Results exported to {paths.CSV_FILE}")