summaryrefslogtreecommitdiff
path: root/kpov_judge
diff options
context:
space:
mode:
Diffstat (limited to 'kpov_judge')
-rw-r--r--kpov_judge/README23
-rwxr-xr-xkpov_judge/add_task.py48
-rwxr-xr-xkpov_judge/create_disk_images.py80
-rwxr-xr-xkpov_judge/kpov_random_helpers.py5
-rw-r--r--kpov_judge/tasks/entrance_exam/task.py2
5 files changed, 143 insertions, 15 deletions
diff --git a/kpov_judge/README b/kpov_judge/README
new file mode 100644
index 0000000..c9a1687
--- /dev/null
+++ b/kpov_judge/README
@@ -0,0 +1,23 @@
+kpov_judge is a system for the automated testing of learning tasks related to networking.
+
+To solve each task, a student must create a number of virtual machines, connect them to a network and set them up according to the task instructions.
+
+Each task is defined by a file called task.py.
+
+This file contains some short instructions, a description of all the computers and networks needed to complete the task, the functions for customizing the task to each student and testing their solution.
+
+Each task for each student is customized by choosing some parameters randomly. These parameters are also described in task.py. Some of the parameters may be chosen or altered by the student. Also, some parameters might be hidden from the student. The parameters which should be chosen by the system are set in a function called gen_params(). To make the writing of this function easier, a number of helper functions are available in kpov_random_helpers.
+
+Two functions are used to test a student's solution.
+The first of these functions, task(...), is run on on a virtual machine under the control of the student; the other, task_check(results, params), on a remote server which is controlled by the teachers.
+
+The function task(...) performs the testing of the student's solution.
+It returns a dictionary of strings called results. These results are sent to the
+teacher's server.
+
+The function task_check(results, params) verifies that the student has indeed solved the specified task. The correct results returned by task(...) should be
+more difficult to create manually than it is to solve the task.
+
+Some tasks also include a set of detailed step-by-step instructions on how to perform the task. These instructions are in the form of a single web page and usually include screen-shots.
+
+
diff --git a/kpov_judge/add_task.py b/kpov_judge/add_task.py
index ec65fd0..ea7aec6 100755
--- a/kpov_judge/add_task.py
+++ b/kpov_judge/add_task.py
@@ -3,14 +3,16 @@
import pymongo
from bson.son import SON
+from bson import Binary
import sys
import inspect
import kpov_random_helpers
import settings
import guestfs
import glob
+import os
-def uploading_task_check(results, params):
+def task_check(results, params):
data = urllib.urlencode({
'results': json.dumps(results),
'params': json.dumps(params)
@@ -19,20 +21,24 @@ def uploading_task_check(results, params):
response = urllib2.urlopen(req)
return response.read()
-uploading_task_check_source = inspect.get_source(uploading_task_check)
+uploading_task_check_source = inspect.getsource(task_check)
-def dummy_gen_params(user_id, meta):
+def gen_params(user_id, meta):
return dict()
-dummy_gen_params_source = inspect.get_source(dummy_gen_params)
+dummy_gen_params_source = inspect.getsource(gen_params)
if __name__ == '__main__':
- if len(sys.argv) != 3:
- print "Usage: {0} task_name task_source.py"
- print "The task_source should contain two functions - task(param1, param2, ...) and task_check(res, params)"
- task_id = sys.argv[1]
- fname = sys.argv[2]
+ if len(sys.argv) < 2:
+ print u"Usage: {0} <task_dir> [task_name]".format(sys.argv[0])
+ exit(1)
+ dirname = sys.argv[1]
+ fname = os.path.join(dirname, 'task.py')
+ try:
+ task_id = sys.argv[2]
+ except:
+ task_id = os.path.basename(os.path.normpath(dirname))
db = pymongo.Connection(settings.DB_HOST).kpov
try:
db.authenticate(settings.USERNAME, settings.PASSWORD)
@@ -45,12 +51,14 @@ if __name__ == '__main__':
# task, task_check, gen_params, prepare_disks, computers, params_meta.
exec(code)
public_meta = dict()
- for k, v in params_meta:
+ for k, v in params_meta.iteritems():
if v.get('public', False):
public_meta[k] = v
- task_source = inspect.getsource(task) + "\n\n" +
- uploading_task_check_source + "\n\n" +
- "params_meta = " + str(public_meta) + "\n\n" + dummy_gen_params_source
+ task_source = "\n\n".join([
+ inspect.getsource(task),
+ uploading_task_check_source,
+ "params_meta = {}".format(public_meta),
+ dummy_gen_params_source])
task_check_source = inspect.getsource(task_check)
gen_params_source = inspect.getsource(gen_params)
prepare_disks_source = inspect.getsource(prepare_disks)
@@ -74,4 +82,16 @@ if __name__ == '__main__':
db.prepare_disks.update({'task_id': task_id}, {'$set': {'source': prepare_disks_source}}, upsert=True)
db.gen_params.update({'task_id': task_id}, {'$set': {'source': gen_params_source}}, upsert=True)
db.task_params_meta.update({'task_id': task_id}, {'$set': {'params': params_meta}}, upsert=True)
-
+ db.task_instructions.update({'task_id': task_id}, {'$set':
+ instructions}, upsert=True)
+ for howto_dir in glob.glob(os.path.join(dirname, 'howtos/*')):
+ howto_lang = os.path.basename(os.path.normpath(howto_dir))
+ if howto_lang not in {'images'}:
+ with open(os.path.join(howto_dir, 'index.html')) as f:
+ db.howtos.update({'task_id': task_id, 'lang': howto_lang},
+ {'$set': {'text': f.read()}})
+ else:
+ for img in glob.glob(os.path.join(howto_dir, '*')):
+ with open(img) as f:
+ db.howto_images.update({'task_id': task_id, 'fname': fname},
+ {'$set': {'data': Binary(f.read())}})
diff --git a/kpov_judge/create_disk_images.py b/kpov_judge/create_disk_images.py
index 168aa5a..d36ff90 100755
--- a/kpov_judge/create_disk_images.py
+++ b/kpov_judge/create_disk_images.py
@@ -11,6 +11,86 @@ import os
import glob
import subprocess
import fcntl
+import pyssh
+import paramiko
+
+class SSHGuestFs():
+ def __init__(hostname, username, password):
+s = pxssh.pxssh()
+ hostname = raw_input('hostname: ')
+ username = raw_input('username: ')
+ password = getpass.getpass('password: ')
+ s.login (hostname, username, password)
+ s.sendline ('uptime')
+ self.conn = pxssh.open(hostname, username, password)
+ def chmod(self, mode, path)
+ self.conn.
+ def chown(self, owner, group, path)
+ pass
+ def command(self, arguments)
+ pass
+ def cp(self, src, dest)
+ pass
+ def cp_a(self, src, dest)
+ pass
+ def cp_r(self, src, dest)
+ pass
+ def dd(self, src, dest)
+ pass
+ def df(self)
+ pass
+ def download(self)
+ pass
+ def du(self, path)
+ pass
+ def equal(self, file1, file2)
+ pass
+ def file(self, path)
+ pass
+ def getxattrs(self, path)
+ pass
+ def mv (self, src, dest):
+ pass
+ def read_file (self, path):
+ pass
+ def readdir (self, dir):
+ pass
+ def readlink (self, path):
+ pass
+ def rename (self, oldpath, newpath):
+ pass
+ def rm (self, path):
+ pass
+ def rm_rf (self, path):
+ pass
+ def rmdir (self, path):
+ pass
+ def setxattr (self, xattr, val, vallen, path):
+ pass
+ def write (self, path, content):
+ """This call creates a file called "path". The content of
+ the file is the string "content" (which can contain any
+ 8 bit data).
+
+ See also "g.write_append".
+ """
+ self._check_not_closed ()
+ r = libguestfsmod.write (self._o, path, content)
+ return r
+
+ def write_append (self, path, content):
+ """This call appends "content" to the end of file "path".
+ If "path" does not exist, then a new file is created.
+
+ See also "g.write".
+ """
+ self._check_not_closed ()
+ r = libguestfsmod.write_append (self._o, path, content)
+ return r
+
+ def write_file (self, path, content, size):
+
+# The external cmp(1) program is used for the comparison.
def get_prepare_disks(db, task_id):
prepare_disks_source = db.prepare_disks.find_one({'task_id':task_id})['source']
diff --git a/kpov_judge/kpov_random_helpers.py b/kpov_judge/kpov_random_helpers.py
index ef6d68c..3ce5b11 100755
--- a/kpov_judge/kpov_random_helpers.py
+++ b/kpov_judge/kpov_random_helpers.py
@@ -72,6 +72,11 @@ def IPv4_addr_gen(r, network, n_generated=1):
hosts.append(socket.inet_ntoa(struct.pack('>I', net | i)))
return hosts
+def MAC_gen(r):
+ s = "0123456789ABCDEF"
+ return ":".join([r.choice(s) + r.choice("26AE")] + \
+ [r.choice(s) + r.choice(s) for i in xrange(5)])
+
common_file_extensions = ['jpg', 'png', 'txt', 'doc', 'cfg', 'pdf', 'odt', 'cpp', 'c', 'sh', 'java']
def fname_gen(r, extension = True):
s = alnum_gen(r, 8)
diff --git a/kpov_judge/tasks/entrance_exam/task.py b/kpov_judge/tasks/entrance_exam/task.py
index 272407c..6984c7c 100644
--- a/kpov_judge/tasks/entrance_exam/task.py
+++ b/kpov_judge/tasks/entrance_exam/task.py
@@ -1,2 +1,2 @@
instructions = {'si': u"""
-Reši poizkusni pristopni kolokvij z visoko oceno.""" }
+Reši pristopni kolokvij z visoko oceno.""" }