diff options
-rw-r--r-- | exam/xmlrpc_clients.py | 42 | ||||
-rwxr-xr-x | python_server.py | 39 | ||||
-rw-r--r-- | settings.py | 5 |
3 files changed, 74 insertions, 12 deletions
diff --git a/exam/xmlrpc_clients.py b/exam/xmlrpc_clients.py index 5dc51c6..115ee6e 100644 --- a/exam/xmlrpc_clients.py +++ b/exam/xmlrpc_clients.py @@ -1,5 +1,41 @@ from xmlrpclib import ServerProxy -from settings import SERVER_PORT +from settings import SERVER_PORTS +import random +import socket -# Connect to the python server. -python_server = ServerProxy('http://localhost:%d'%(SERVER_PORT)) + +class PythonServer(object): + """A class that manages accesing the farm of Python servers and making + calls to them such that no one XMLRPC server is overloaded. + """ + def __init__(self): + servers = [ServerProxy('http://localhost:%d'%(x)) for x in SERVER_PORTS] + self.servers = servers + self.indices = range(len(SERVER_PORTS)) + + def run_code(self, answer, test_code, user_dir): + """See the documentation of the method of the same name in + python_server.py. + """ + done = False + result = [False, 'Unable to connect to any Python servers!'] + # Try to connect a few times if not, quit. + count = 5 + while (not done) and (count > 0): + try: + server = self._get_server() + result = server.run_code(answer, test_code, user_dir) + except socket.error: + count -= 1 + else: + done = True + return result + + def _get_server(self): + # pick a suitable server at random from our pool of servers. + index = random.choice(self.indices) + return self.servers[index] + +# views.py calls this Python server which forwards the request to one +# of the running servers. +python_server = PythonServer() diff --git a/python_server.py b/python_server.py index 20d7440..d33ee47 100755 --- a/python_server.py +++ b/python_server.py @@ -1,7 +1,19 @@ #!/usr/bin/env python """This server runs an XMLRPC server that can be submitted code and tests and returns the output. It *should* be run as root and will run as the user -'nobody' so as to minimize any damange by errant code. +'nobody' so as to minimize any damange by errant code. This can be configured +by editing settings.py to run as many servers as desired. One can also +specify the ports on the command line. Here are examples:: + + $ sudo ./python_server.py + # Runs servers based on settings.py:SERVER_PORTS one server per port given. + +or:: + + $ sudo ./python_server.py 8001 8002 8003 8004 8005 + # Runs 5 servers on ports specified. + +All these servers should be running as nobody. """ import sys import traceback @@ -10,11 +22,15 @@ import pwd import os from os.path import isdir import signal -from settings import SERVER_PORT, SERVER_TIMEOUT +from multiprocessing import Process + +# Local imports. +from settings import SERVER_PORTS, SERVER_TIMEOUT def run_as_nobody(): - # Set the effective uid + """Runs the current process as nobody.""" + # Set the effective uid to that of nobody. nobody = pwd.getpwnam('nobody') os.setegid(nobody.pw_gid) os.seteuid(nobody.pw_uid) @@ -25,6 +41,7 @@ class TimeoutException(Exception): pass def timeout_handler(signum, frame): + """A handler for the ALARM signal.""" raise TimeoutException('Code took too long to run.') @@ -80,13 +97,21 @@ def run_code(answer, test_code, in_dir=None): return success, err +def run_server(port): + server = SimpleXMLRPCServer(("localhost", port)) + server.register_function(run_code) + server.serve_forever() def main(): run_as_nobody() - server = SimpleXMLRPCServer(("localhost", SERVER_PORT)) - server.register_function(run_code) - server.serve_forever() + if len(sys.argv) == 1: + ports = SERVER_PORTS + else: + ports = [int(x) for x in sys.argv[1:]] + + for port in ports: + p = Process(target=run_server, args=(port,)) + p.start() if __name__ == '__main__': main() -
\ No newline at end of file diff --git a/settings.py b/settings.py index 4e9b390..e5e3e69 100644 --- a/settings.py +++ b/settings.py @@ -5,8 +5,9 @@ from os.path import dirname, join, basename, abspath DEBUG = True TEMPLATE_DEBUG = DEBUG -# The port the Python server should run on. -SERVER_PORT = 8001 +# The ports the Python server should run on. This will run one separate +# server for each port listed in the following list. +SERVER_PORTS = [8001] # range(8001, 8026) # Timeout for the code to run in seconds. This is an integer! SERVER_TIMEOUT = 2 |