예제 #1
0
def main(dir):
    """Unpacks archive and invokes executor"""
    # reads assignment config
    with open(join(dir, 'config')) as handle:
        config = ConfigParser.RawConfigParser()
        config.readfp(handle)

    # reads vmchecker_storer.ini
    with open(join(dir, 'storer')) as handle:
        storer = ConfigParser.RawConfigParser()
        storer.readfp(handle)

    # copies files to where vmchecker expects them (wtf
    # XXX 'executor_jobs' path is hardcoded in executor

    ejobs = vmcheckerpaths.abspath('executor_jobs')
    # cleans up executor_jobs, if not already clean
    if isdir(ejobs):
        shutil.rmtree(ejobs)
    os.mkdir(ejobs)

    shutil.copy(        # copies assignment
        join(dir, 'archive.zip'),
        vmcheckerpaths.abspath('executor_jobs', 'file.zip'))
    shutil.copy(        # copies tests
        join(dir, 'tests.zip'),
        vmcheckerpaths.abspath('executor_jobs', 'tests.zip'))

    assignment = config.get('Assignment', 'Assignment')  # yet another hack
    section = assignments._SECTION_PREFIX + assignment

    machine = storer.get(section, 'Machine')
    timeout = storer.get(section, 'Timeout')
    kernel_messages = storer.get(section, 'KernelMessages')

    _run_executor(ejobs, machine, assignment, timeout, kernel_messages)

    try:
        _run_callback(dir, ejobs)
    except:
        _logger.exception('cannot run callback')

    # clears files
    shutil.rmtree(ejobs)

    _logger.info('all done')
예제 #2
0
def check_tester_setup_correctly():
    # check needed paths setup correctly
    for path in vmcheckerpaths.tester_paths():
        if not os.path.isdir(path):
            _logger.error('"%s" missing. Run `make tester-dist`!', path)
            exit(1)
    # check binaries build
    # TODO: XXX: Hardcoded
    # VMExecutor is expected to die soon :)
    if not os.path.isfile(os.path.join(vmcheckerpaths.abspath('VMExecutor'),
                                       'vm_executor')):
        _logger.error('VMExecutor/vm_executor missing. Run `make tester-dist`!')
        exit(1)
예제 #3
0
    def include(self, assignment):
        """An iterator over the files to include when submitting an assignment.

        The iterators yields pairs (destination, source) where
            destination is the name of the file in the archive
            source is the name of the file on the disk relative to vmchecker root

        The include options is useful to include other scripts
        and configuration files.

        """
        self._check_valid(assignment)
        for option in self.__assignments[assignment]:
            if option.startswith(_INCLUDE_PREFIX):
                yield (option[len(_INCLUDE_PREFIX):],
                       vmcheckerpaths.abspath(self.get(assignment, option)))
예제 #4
0
def create_storer_git_repo():
    """Creates the repo for the assignments on the storer."""
    # first make teh destination directory
    rel_repo_path = vmcheckerpaths.repository
    abs_repo_path = vmcheckerpaths.abspath(rel_repo_path)
    _mkdir_if_not_exist(abs_repo_path)

    # then, if missing, initialize a git repo in it.
    repo_path_git = os.path.join(abs_repo_path, '.git')
    if not(os.path.isdir(repo_path_git)):
        # no git repo found in the dir.
        try:
            env = os.environ
            env['GIT_DIR'] = repo_path_git
            check_call(['git', 'init'], env=env)
        except CalledProcessError:
            logging.error('cannot create git repo in %s' % repo_path_git)
예제 #5
0
def _run_executor(ejobs, machine, assignment, timeout, kernel_messages):
    """Starts a job.

    XXX lots of wtf per minute
    XXX parsing config should be executors' job

    """
    args = [
            # '/bin/echo',
            vmcheckerpaths.abspath('VMExecutor/vm_executor'),
            machine,
            kernel_messages,                       # enables kernel_messages
            config.get(machine, 'VMPath'),
            config.get('Global', 'LocalAddress'),  # did I review commander.cpp?
            config.get(machine, 'GuestUser'),
            config.get(machine, 'GuestPassword'),     # XXX keys?
            config.get(machine, 'GuestBasePath'),
            config.get(machine, 'GuestShellPath'),
            config.get(machine, 'GuestHomeInBash'),   # why is this needed?
            vmcheckerpaths.root,
            assignment,
            timeout,
            ]
    _logger.info('Begin homework evaluation')
    _logger.debug('calling %s', args)

    start = time.time()

    # first just try to open the process
    try:
        popen = Popen(args)
    except Exception:
        _logger.exception('Cannot invoke VMExecutor.')
        with open(join(ejobs, 'job_errors'), 'a') as handler:
            print >> handler, 'Cannot run VMExecutor.'
            print >> handler, 'Please contact the administrators.'
        # if we cannot open the process, there is nothing more to be done
        return

    # waits for the the process to finish
    try:
        counter = 0
        deadline = start + int(timeout) + _EXECUTOR_OVERHEAD

        while time.time() < deadline:
            counter += 1
            exit_code = popen.poll()

            if exit_code is None:
                # if process has not finished => continue to sleep
                _logger.debug('-- VMExecutor sleeping for 5 seconds, '
                              'exit_code is None: x=%d', counter)
                # polls every 5 seconds
                time.sleep(5)
            else:
                with open(join(ejobs, 'job_errors'), 'a') as handler:
                    print >> handler, 'VMExecutor returned %d (%s)' % (
                        exit_code, ['success', 'error'][exit_code < 0])

                # no reason to stay in the loop after process exit terminates
                popen = None
                return
        else:
            _logger.error("VMChecker timeouted on assignment `%s' "
                          "running on machine `%s'.", assignment, machine)

            with open(join(ejobs, 'job_errors'), 'a') as handler:
                print >> handler, """\
VMExecutor successfuly started, but it's taking too long.
Check your sources, makefiles, etc and resubmit.
If the problem persists please contact administrators."""
    except:
        _logger.exception('Exception after starting VMExecutor.')

        with open(join(ejobs, 'job_errors'), 'a') as handler:
            print >> handler, """\
Error after starting VMExecutor.
If the problem persists please contact administrators."""
    finally:
        # release any leftover resources
        try:
            if popen:
                popen.kill()
        except:
            pass
예제 #6
0
def submit_assignment(assignment_config):
    """Submits config file for evaluation.

    This function creates a zip archive, stores it in
    $VMCHECKER_ROOT/unchecked/ directory and calls submit
    script.

    The archive contains:
        config - assignment config (eg. name, time of submission etc)
        global - global assignments config (eg. deadlines)
        archive.zip - a zip containing the homework
        callback - a script executed by the tester to send results back

    """
    assignment_config = abspath(assignment_config)

    # reads user, assignment and course
    aconfig = ConfigParser.RawConfigParser()
    with open(assignment_config) as handler:
        aconfig.readfp(handler)

    user = aconfig.get('Assignment', 'User')
    assignment = aconfig.get('Assignment', 'Assignment')
    course = config.get(assignment, 'Course')

    # location of student's homework
    archive = join(dirname(assignment_config), 'archive.zip')
    assert isfile(archive), "Missing archive `%s'" % archive

    # location of tests
    tests = join(vmcheckerpaths.dir_tests(), assignment + '.zip')
    assert isfile(tests), "Missing tests `%s'" % tests

    # builds archive with configuration
    with _Locker(assignment):
        # creates the zip archive with an unique name
        fd = mkstemp(
                suffix='.zip',
                prefix='%s_%s_%s_' % (course, assignment, user),
                dir=vmcheckerpaths.dir_unchecked())
        _logger.info("Creating zip package at `%s'", fd[1])

        # populates the archive
        # Includes at least these files:
        #   config -> homework config (see above)
        #   archive.zip -> homework files (student's sources)
        #   tests.zip -> assignment tests
        try:
            with os.fdopen(fd[0], 'w+b') as handler:
                zip = ZipFile(handler, 'w')
                zip.write(assignment_config, 'config')   # assignment config
                zip.write(archive, 'archive.zip')        # assignment archive
                zip.write(tests, 'tests.zip')            # the archive containing tests

                # includes extra required files
                for f in config.get(assignment):
                    if not f.startswith('include '):
                        continue

                    dst, src = f[8:], config.get(assignment, f)
                    src = vmcheckerpaths.abspath(src)
                    assert isfile(src), "`%s' is missing" % src

                    zip.write(src, dst)
                    _logger.debug("Included `%s' as `%s'.", src, dst)

                zip.close()
        except:
            _logger.error("Failed to create archive `%s'", fd[1])
            os.unlink(fd[1])
            raise

        # sends homework to tester
        submit = vmcheckerpaths.abspath(config.get(assignment, 'Submit'))
        _logger.info('Calling submission script %s', submit)
        try:
            check_call((submit, fd[1]))
        except:
            _logger.fatal("Cannot submit homework. Archive `%s' not deleted.", fd[1])
            raise
예제 #7
0
def submit_homework(location):
    """Submits homework at location for evaluation

    This function creates a zip archive in the ./unchecked/
    directory and calls the submit script.

    The archive contains:
        config - assignment config (eg. name, time of submission etc)
        archive.zip - a zip containing the homework
        tests.zip - a zip containing the tests
        callback - a script executed by the tester to send results back
        ... - assignment's extra files (see assignments.Assignments.include())

    """
    # reads user, assignment and course
    # hrc = homework resource configuration
    hrc = ConfigParser.RawConfigParser()
    with open(os.path.join(location, "config")) as handler:
        hrc.readfp(handler)

    assignment = hrc.get("Assignment", "Assignment")
    user = hrc.get("Assignment", "User")
    course = config.assignments.course(assignment)

    # location of student's homework
    # XXX should create a clean zip from the repository
    archive = os.path.join(location, "archive.zip")
    assert os.path.isfile(archive), "Missing archive %s" % archive

    # location of tests
    tests = config.assignments.tests(assignment)
    assert os.path.isfile(tests), "Missing tests %s" % tests

    # builds archive with configuration
    with config.assignments.lock(assignment):
        # creates the zip archive with an unique name
        fd = tempfile.mkstemp(
            suffix=".zip", prefix="%s_%s_%s_" % (course, assignment, user), dir=vmcheckerpaths.dir_unchecked()
        )  # FIXME not here
        _logger.info("Creating zip package %s", fd[1])

        # populates the archive (see the function's docstring)
        try:
            with os.fdopen(fd[0], "w+b") as handler:
                zip_ = zipfile.ZipFile(handler, "w")
                zip_.write(os.path.join(location, "config"), "config")
                zip_.write(archive, "archive.zip")
                zip_.write(tests, "tests.zip")

                # includes extra required files
                for dest, src in config.assignments.include(assignment):
                    src = vmcheckerpaths.abspath(src)

                    # XXX do not assert, but raise
                    assert os.path.isfile(src), "File %s is missing" % src

                    zip_.write(src, dest)
                    _logger.debug("Included %s as %s", src, dest)

                zip_.close()
        except:
            _logger.error("Failed to create zip archive %s", fd[1])
            os.unlink(fd[1])
            raise

    # package created, sends homework to tester by invoking submission script
    submit = config.assignments.get(assignment, "Submit")
    submit = vmcheckerpaths.abspath(submit)
    _logger.info("Invoking submission script %s", submit)
    try:
        subprocess.check_call((submit, fd[1]))
    except:
        _logger.fatal("Cannot submit homework %s, %s", assignment, user)
        os.unlink(fd[1])
        raise
예제 #8
0
def path(section, option):
    """Returns an absolute path derived from an option"""
    return vmcheckerpaths.abspath(config.get(section, option))