Source code for railgun.runner.apiclient
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# @file: railgun/runner/apiclient.py
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This file is released under BSD 2-clause license.
import os
import json
import requests
from railgun.common.crypto import EncryptMessage
from railgun.common.hw import HwScore
from . import runconfig
[docs]def get_comm_key():
"""Load the secret key from ``keys/commKey.txt``.
:return: The secret key to encrypt and decrypt API post data.
"""
path = os.path.join(runconfig.RAILGUN_ROOT, 'keys/commKey.txt')
with open(path, 'rb') as f:
return f.read().strip()
[docs]class ApiClient(object):
"""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 :ref:`design_webapi` for more details.
:param baseurl: The base url of website api.
:type baseurl: :class:`str`
"""
def __init__(self, baseurl):
#: Store the base url of website api.
self.baseurl = baseurl.rstrip('/')
#: Store the secret communication key.
self.key = get_comm_key()
def _get_url(self, action):
return '%s%s' % (self.baseurl, action)
[docs] def post(self, action, payload):
"""Send `payload` to remote server and execute given `action`.
:param action: The url of the performing action.
:type action: :class:`str`
:param payload: The plain object to be sent.
:type payload: :class:`object`
:return: The :class:`requests.Response` object.
"""
payload = EncryptMessage(json.dumps(payload), self.key)
return requests.post(
self._get_url(action),
data=payload,
headers={'Content-Type': 'application/octet-stream'},
verify=False
)
[docs] def report(self, handid, hwscore):
"""Send the score of given submission.
:param handid: The uuid of the submission.
:type handid: :class:`str`
:param hwscore: The score object.
:type hwscore: :class:`~railgun.common.hw.HwScore`
"""
obj = hwscore.to_plain()
obj['uuid'] = handid
self.post('/handin/report/%s/' % handid, payload=obj)
[docs] def start(self, handid):
"""Change the status of submission to `Running`.
:param handid: The uuid of the submission.
:type handid: :class:`str`
"""
obj = {'uuid': handid}
self.post('/handin/start/%s/' % handid, payload=obj)
[docs] def proclog(self, handid, exitcode, stdout, stderr):
"""Store the process exitcode, standard output and standard error
output of the submission.
:param handid: The uuid of the submission.
:type handid: :class:`str`
:param exitcode: The exit code of the process.
:type exitcode: :class:`int`
:param stdout: The standard output of the process.
:type stdout: :class:`str`
:param stderr: The standard error output of the process.
:type stderr: :class:`str`
"""
obj = {'uuid': handid, 'exitcode': exitcode, 'stdout': stdout,
'stderr': stderr}
self.post('/handin/proclog/%s/' % handid, payload=obj)
[docs]def report_error(handid, err):
"""Shortcut to report the error of a submission.
:param handid: The uuid of the submission.
:type handid: :class:`str`
:param err: A runner error object holding the error message.
:type err: :class:`~railgun.runner.errors.RunnerError`
"""
api = ApiClient(runconfig.WEBSITE_API_BASEURL)
score = HwScore(False, result=err.message, compile_error=err.compile_error)
api.report(handid, score)
[docs]def report_start(handid):
"""Shortcut to report that a submission has been launched.
:param handid: The uuid of the submission.
:type handid: :class:`str`
"""
api = ApiClient(runconfig.WEBSITE_API_BASEURL)
api.start(handid)