Runner Queue

This part of the documentation describes the runner package. It includes all details about the runner queue.

Celery Context Object

railgun.runner.context.app

The celery.Celery context object.

Celery is a robust and scalable job queue. You may shift a running instance from one machine to paralleled computing platform simply by changing the backend of Celery. To learn more about this framework, you may refer to Celery Project.

railgun.runner.context.logger

The default logger for this Celery application. It is used everywhere in this package. The interface is the same as logging.Logger, so you may refer to logging for more details.

Tasks of the Runner

This module defines all celery.Task objects.

A Task object is a proxy for the foreground process to queue their jobs into the Celery background runner. The communications are managed by the Celery framework, and the client may not care about these details.

For example, if you want to run a long-time job in the background runner, you may decorate the job method with app.task():

@app.task
def RestartService(name):
    os.system('sudo service %s restart' % name)

Then you can tell Celery to queue and run that method in your foreground process:

from tasks import RestartService

RestartService.delay('dnsmasq')

Task defined in this module are mainly used in railgun.website.codelang. You may also refer to Tasks to learn more about how to define a task, and Calling Tasks about how to call a task.

railgun.runner.tasks.run_python(handid, hwid, upload, options)[source]

Run the given Python submission.

Handler:

PythonHandin

Parameters:
  • handid (str) – The uuid of this submission.
  • hwid (str) – The uuid of the homework.
  • upload (str) – The uploaded archive file content encoded in base64.
  • options (dict) – {‘filename’: the uploaded filename}
railgun.runner.tasks.run_netapi(handid, hwid, remote_addr, options)[source]

Run the given NetAPI submission.

Handler:

NetApiHandin

Parameters:
  • handid (str) – The uuid of this submission.
  • hwid (str) – The uuid of the homework.
  • remote_addr (str) – The submitted url address.
  • options (dict) – {}
railgun.runner.tasks.run_input(handid, hwid, csvdata, options)[source]

Run the given CSV data submission.

Handler:

InputClassHandin

Parameters:
  • handid (str) – The uuid of this submission.
  • hwid (str) – The uuid of the homework.
  • csvdata (str) – The submitted csv file content.
  • options (dict) – {}
railgun.runner.tasks.run_handin(handler, handid, hwid)[source]

Common pattern to run a submission. Its main function is to glue BaseHandin, BaseHost and ApiClient together.

It is guaranteed that all errors are handled and logged correctly in this method.

Parameters:
  • handler – A factory to create a BaseHandin handler object.
  • handid (str) – The uuid of this submission.
  • hwid (str) – The uuid of the homework.

Handlers of Submissions

This module provides BaseHandin as the basic interface of a submission handler, as well as derived classes targeted to different submission types.

Submissions may be various in programming languages. Each programming language should have a corresponding handler, which decodes the user uploaded data (for example, extracting the archive files, or parsing the csv data), and prepares for the runner host.

class railgun.runner.handin.BaseHandin(lang, handid, hwid, upload, options)[source]

The basic interface of a submission handler.

Parameters:
  • lang (str) – The programming language name of this handler. Derived classes should provide this value in contructors.
  • handid (str) – The uuid of this submission.
  • hwid (str) – The uuid of the homework.
  • upload (str) – The uploaded data of this submission. It may be base64 encoded byte sequence to represent an archive file in a Python submission, or a url address in a NetAPI submission.
  • options (dict) – The extra options of this submission. Vary for different types of submissions.
execute()[source]

Run this submission and store the result. Derived classes should at least implement this.

Returns:A tuple of (exitcode, stdout, stderr).
handid = None

The uuid of this submission.

hw = None

Store the corresponding Homework to hwid.

lang = None

The programming language of this handler.

options = None

The extra options of this submission.

upload = None

The original uploaded data of this submission.

class railgun.runner.handin.InputClassHandin(handid, hwid, upload, options)[source]

CSV data submission handler, derived from BaseHandin.

Parameters:
  • handid (str) – The uuid of this submission.
  • hwid (str) – The uuid of the homework.
  • upload (str) – The submitted csv file data.
  • options (dict) – {}
class railgun.runner.handin.NetApiHandin(handid, hwid, upload, options)[source]

NetAPI submission handler, derived from BaseHandin.

Parameters:
  • handid (str) – The uuid of this submission.
  • hwid (str) – The uuid of the homework.
  • upload (str) – The submitted url address.
  • options (dict) – {}
remote_addr = None

Store the user submitted url address.

class railgun.runner.handin.PythonHandin(handid, hwid, upload, options)[source]

Python submission handler, derived from BaseHandin.

Parameters:
  • handid (str) – The uuid of this submission.
  • hwid (str) – The uuid of the homework.
  • upload (str) – The base64 encoded archive file content.
  • options (dict) – {‘filename’: the original uploaded file name}
class railgun.runner.handin.TempDiskUploadFile(upload, fname, ignore_error=False)[source]

Decode base64 file content and save to disk as a temporary file.

The file will be placed under config.TEMPORARY_DIR. If you manage the TempDiskUploadFile via with statement, then the file will be removed automatically. For example:

with TempDiskUploadFile(upload, '%s.zip' % uuid) as tempFile:
    os.system('7z x "%s"' % tempeFile.path)
Parameters:
  • upload (str) – The base64 encoded file content.
  • fname (str) – The temporary file name. If the chosen file name exists, it will be overwritten.
  • ignore_error (bool) – A bool indicating whether we should ignore system exceptions when we are trying to delete the temporary file?
ignore_error = None

Whether we should ignore the system exceptions when we are trying to delete the temporary file?

path = None

Store the full path of this temporary file.

upload = None

The original base64 encoded file content.

Hosts of the Runners

This module provides BaseHost as the basic interface of a submission handler, as well as derived classes targeted to different programming languages.

Submissions may be various in programming languages, while different languages may have different context to run. So a runner host prepares the environment, compiles and launches the submission according to its programming language.

class railgun.runner.host.BaseHost(uuid, hw, lang)[source]

The base interface for a runner host.

A runner host will hold a working directory under config.TEMPORARY_DIR, whose name is uuid. This directory will be automatically removed if BaseHost is managed by with statement, for example:

with BaseHost(uuid, hw, 'python') as host:
    pass
Parameters:
  • uuid (str) – The uuid of the submission.
  • hw (Homework) – The corresponding homework object.
  • lang (str) – The programming language of this host.
compile()[source]

Call to compile the submission. Some programming language may skip this process.

extract_handin(archive)[source]

Extract the given archive file into tempdir.

The FileRules defined in hw and hwcode will be validated on each file, so this method is safe.

Parameters:archive (Extractor) – An extractor of the submission archive file.
prepare_hwcode()[source]

Prepare the runner context by copying files from hw/code into tempdir. This method should be called before extract_handin().

run()[source]

Run this submission. All derived classes must implement this.

Returns:A tuple of (exitcode, stdout, stderr).
set_user(uid, gid=None)[source]

Set the user and the group in host config.

The uid will be stored in config['user_id'] while the gid will be stored in config['group_id']. However, if uid is given None, all the defence based on user privileges will not take place.

Parameters:
  • uid (int or str) – The uid or name of system account.
  • gid (int or str) – The gid or group name in the system. If set to None, the group of given user will be selected.
Raises:

KeyError if uid or gid is a str, but does not exist in the system database.

spawn(cmdline, timeout=None)[source]

Spawn an external process to execute the given commands.

If the owner user of current process (runner queue) is root, and config['user_id'] != 0, the owner of tempdir will be changed to that user, and the file system mode will be changed to 0700.

Parameters:
  • cmdline (str) – The command line to be executed.
  • timeout (float) –

    Wait for timeout seconds before we kill the external process and claim a rejected submission.

    If this argument is not given, config.RUNNER_DEFAULT_TIMEOUT will be chosen as the timeout limit.

compiler_params = None

The xml node of compiler parameters. You may refer to HwCode.compiler_params for more details.

config = None

The HostConfig for the process.

hw = None

The Homework.

hwcode = None

The HwCode of corresponding language.

runner_params = None

The xml node of runner parameters. You may refer to HwCode.runner_params for more details.

runner_user = None

The acquired system account (name or uid).

Railgun can be configured to run multiple submissions in different system accounts simultaneously. This attribute stores the acquired system account, so that we can release it later.

If you intend to get the uid and gid of this user, access config['user_id'] and config['group_id'] instead.

tempdir = None

A TempDir, whose directory name is uuid.

uuid = None

The uuid of the submission.

class railgun.runner.host.HostConfig[source]

Config values passed to hosts by environmental variables.

make_environ()[source]

Prepare the environmental variables for the host process.

Values of None will be ignored, and other values will be converted into string type before being added to environ dictionary.

All the keys matching [A-Z][A-Z0-9_]* will be used directly in the new environ dictionary. All the keys in other formats will be translated into uppercase, and a prefix of RAILGUN_ will be add to the key.

For example:

>>> config = HostConfig(user_id=1, group_id=2, ignored=None,
                        PYTHONPATH='/usr/local/lib/python2.7')
>>> config.make_environ()
{'RAILGUN_USER_ID': '1', 'RAILGUN_GROUP_ID': '2',
 'PYTHONPATH': '/usr/local/lib/python2.7'}
Returns:The environmental variable dictionary.
Return type:dict
class railgun.runner.host.InputClassHost(uuid, hw)[source]

The CSV data runner host, derived from PythonHost.

Parameters:
  • uuid (str) – The uuid of this submission.
  • hw (Homework) – The corresponding homework.
class railgun.runner.host.NetApiHost(remote_addr, uuid, hw)[source]

The NetAPI runner host, derived from PythonHost.

Parameters:
  • remote_addr (str) – The user submitted url address.
  • uuid (str) – The uuid of this submission.
  • hw (Homework) – The corresponding homework.
compile()[source]

Validate the user submitted url address at compile stage.

The url address will be tested with the configured regex patterns loaded from BaseHost.compiler_params. Refer to NetAPI Judging for more details about the rules.

class railgun.runner.host.PythonHost(uuid, hw, lang='python', offline=True)[source]

The Python runner host, derived from BaseHost.

This class is special, in that it does not only acts as a derived class to serve Python submission, but also acts as a base class for all Python based hosts.

For example, the InputClassHost, taking a Python script to evaluate the submitted csv data, is merely the same as a PythonHost. So it just inherits this class to implement the common functions.

Parameters:
  • uuid (str) – The uuid of this submission.
  • hw (Homework) – The corresponding homework.
  • lang (str) – The programming language of this host. Can be set by derived classes.
  • offline (bool) – Whether this host uses offline system accounts? Offline system accounts will not be able to access the internet.
run()[source]

Run this Python submission.

entry = None

The main Python script file (from BaseHost.runner_params).

entry_path = None

The parent directory of entry file.

offline = None

Whether this host uses offline system accounts?

safe_runner = None

Store the path of Python safe runner (RAILGUN_ROOT/SafeRunner).

timeout = None

The timeout limit of this submission (from BaseHost.runner_params).

Request for a System Account

Railgun can be configured to run multiple submission simultaneously with different system accounts.

This module acquires the accounts from a credential server (if provided), or just uses the config.OFFLINE_USER_ID and config.ONLINE_USER_ID.

Note

Offline users will not be able to access the internet. You must use the correct type of users for different submission types.

railgun.runner.credential.acquire_offline_user(expires=10)[source]

Get a free offline system account.

Parameters:expires (int) – Seconds for this user to expire.
Returns:The acquired user account name.
Raises:Various Exception if the user cannot be acquired.
railgun.runner.credential.acquire_online_user(expires=10)[source]

Get a free online system account.

Parameters:expires (int) – Seconds for this user to expire.
Returns:The acquired user account name.
Raises:Various Exception if the user cannot be acquired.
railgun.runner.credential.release_offline_user(user)[source]

Release an offline system account. Will do nothing if on credential server is configured.

Parameters:user (str) – The name of acquired user.
Raises:Various Exception if the user cannot be released.
railgun.runner.credential.release_online_user(user)[source]

Release an online system account. Will do nothing if on credential server is configured.

Parameters:user (str) – The name of acquired user.
Raises:Various Exception if the user cannot be released.

Common Submission Errors

This module provides the pre-defined errors that holds the messages to describe what errors a submission produces.

exception railgun.runner.errors.ArchiveContainTooManyFileError(**kwargs)[source]

The submission archive contains too many files. You may refer to extract_handin() of BaseHost to see more details.

exception railgun.runner.errors.BadArchiveError(**kwargs)[source]

The submitted archive file is in bad format. This error may also be raised if the user uploads an archive file with wrong extension in file name.

exception railgun.runner.errors.ExtractFileFailure(**kwargs)[source]

The system cannot extract submission archive into working directory. You may refer to extract_handin() of BaseHost to see more details.

exception railgun.runner.errors.FileDenyError(fname, **kwargs)[source]

There exist some entity that is denied by hw.xml or code.xml in the submitted archive file.

Parameters:fname (str) – The name of the denied entity.
exception railgun.runner.errors.InternalServerError(**kwargs)[source]

The server has come across some technique error.

Since run_handin() is wrapped with a large try-catch, all the un-expected errors can be catched. This error is then generated and reported to user.

exception railgun.runner.errors.LanguageNotSupportError(lang, **kwargs)[source]

The submission language doesn’t belong to corresponding homework.

Parameters:lang (str) – The provided programming language.
exception railgun.runner.errors.NetApiAddressRejected(**kwargs)[source]

The URL address submitted by user is rejected by regex patterns defined in code.xml. You may refer to compile() of NetApiHost to see more details.

exception railgun.runner.errors.NonUTF8OutputError(**kwargs)[source]

The runner host produces invalid UTF-8 sequence. You should tell the students to encode their source code in UTF-8.

exception railgun.runner.errors.RunnerError(message, compile_error=None)[source]

The base class for all submission errors.

Parameters:
  • message (GetTextString) – The translated error message. Usually set by derived classes in the contructor.
  • compiler_error (GetTextString) – The compiler error of this submission. Compiler error is not part of HwScore, not can it be reported to server via Website API.
exception railgun.runner.errors.RunnerPermissionError(**kwargs)[source]

The file permission of runner is not configured correctly.

The runner will check the file system permissions to guarantee that the execution environment is safe if runconfig.RUNNER_CHECK_PERM is set to True. If the validation does not pass, all the new submissions will be rejected with this error.

exception railgun.runner.errors.RunnerTimeout(**kwargs)[source]

The submission runs out of time.

exception railgun.runner.errors.RuntimeFileCopyFailure(**kwargs)[source]

The system cannot copy runtime files into working directory. You may refer to prepare_hwcode() of BaseHost to see more details.

exception railgun.runner.errors.SpawnProcessFailure(**kwargs)[source]

The external process cannot be launched to run the submission. This is often due to a false configuration of the runner hosts.

Client of the Website API

class railgun.runner.apiclient.ApiClient(baseurl)[source]

The API client for runner to communicate with website.

The runner queue and the runner host should report the results of submissions via website api. Refer to Website API for more details.

Parameters:baseurl (str) – The base url of website api.
post(action, payload)[source]

Send payload to remote server and execute given action.

Parameters:
  • action (str) – The url of the performing action.
  • payload (object) – The plain object to be sent.
Returns:

The requests.Response object.

proclog(handid, exitcode, stdout, stderr)[source]

Store the process exitcode, standard output and standard error output of the submission.

Parameters:
  • handid (str) – The uuid of the submission.
  • exitcode (int) – The exit code of the process.
  • stdout (str) – The standard output of the process.
  • stderr (str) – The standard error output of the process.
report(handid, hwscore)[source]

Send the score of given submission.

Parameters:
  • handid (str) – The uuid of the submission.
  • hwscore (HwScore) – The score object.
start(handid)[source]

Change the status of submission to Running.

Parameters:handid (str) – The uuid of the submission.
baseurl = None

Store the base url of website api.

key = None

Store the secret communication key.

railgun.runner.apiclient.get_comm_key()[source]

Load the secret key from keys/commKey.txt.

Returns:The secret key to encrypt and decrypt API post data.
railgun.runner.apiclient.report_error(handid, err)[source]

Shortcut to report the error of a submission.

Parameters:
  • handid (str) – The uuid of the submission.
  • err (RunnerError) – A runner error object holding the error message.
railgun.runner.apiclient.report_start(handid)[source]

Shortcut to report that a submission has been launched.

Parameters:handid (str) – The uuid of the submission.

Preloaded Homework Objects

railgun.runner.hw.homeworks

Load the homeworks under config.HOMEWORK_DIR at the startup of runner queue.