|
1 | 1 | import os |
2 | 2 | import re |
3 | 3 | import json |
4 | | -import boto3 |
5 | 4 | import base64 |
6 | 5 | import shutil |
7 | 6 | import hashlib |
8 | 7 | import zipfile |
9 | | -import paramiko |
10 | 8 | import tempfile |
11 | 9 | import pycparser |
12 | 10 | from os import environ |
13 | 11 | from glob import glob |
14 | | -from Crypto import Random |
15 | 12 | from subprocess import run |
16 | 13 | from subprocess import PIPE |
17 | 14 | from tabulate import tabulate |
18 | | -from Crypto.Cipher import AES |
19 | 15 | from distutils.dir_util import copy_tree |
20 | 16 |
|
21 | 17 |
|
22 | | -# encrypt a string |
23 | | -def encrypt(raw): |
24 | | - def pad(s): |
25 | | - return s + (16 - len(s) % 16) * chr(16 - len(s) % 16) |
26 | | - rawkey = environ['AUTOGRADERS_KEY'] |
27 | | - key = hashlib.sha256(rawkey.encode()).digest() |
28 | | - raw = pad(raw) |
29 | | - iv = Random.new().read(AES.block_size) |
30 | | - cipher = AES.new(key, AES.MODE_CBC, iv) |
31 | | - return base64.b64encode(iv + cipher.encrypt(raw)).decode() |
32 | | - |
33 | | - |
34 | | -# decrypt an encrypted string |
35 | | -def decrypt(enc): |
36 | | - def unpad(s): |
37 | | - if (type(s[-1]) == int): |
38 | | - return s[0: -s[-1]] |
39 | | - return s[0: -ord(s[-1])] |
40 | | - enc = base64.b64decode(enc) |
41 | | - iv = enc[:16] |
42 | | - rawkey = environ['AUTOGRADERS_KEY'] |
43 | | - key = hashlib.sha256(rawkey.encode()).digest() |
44 | | - cipher = AES.new(key, AES.MODE_CBC, iv) |
45 | | - return unpad(cipher.decrypt(enc[16:])).decode() |
| 18 | +# builds program file string |
| 19 | +def get_ex(ch, ex): |
| 20 | + return f"./ch{ch}/ex-{ch}.{ex}.c" |
46 | 21 |
|
47 | 22 |
|
| 23 | +# builds program test file string |
| 24 | +def get_in(ch, ex): |
| 25 | + if (ch == 1): |
| 26 | + if (ex == 9): |
| 27 | + return "./ch1/spaces.test" |
| 28 | + elif (ex == 13 or ex == 14): |
| 29 | + return "./ch1/words.test" |
| 30 | + elif (ex == 17): |
| 31 | + return "./ch1/long.test" |
| 32 | + elif (ex == 20): |
| 33 | + return "./ch1/tabs.test" |
| 34 | + else: |
| 35 | + return "./ch1/input.test" |
| 36 | + else: |
| 37 | + return "placeholder.test" |
| 38 | + |
| 39 | +# builds expected output file string |
| 40 | +def get_out(ch, ex): |
| 41 | + return f"./ch{ch}/expected-{ch}.{ex}" |
| 42 | + |
48 | 43 | # reads a file |
49 | 44 | def read(filename): |
50 | 45 | f = open(filename, 'r') |
@@ -154,12 +149,66 @@ def expected_files(files, dir='.'): |
154 | 149 | def execute(cmd=[], shell=False, dir='.', input=None, encoding='ascii', timeout=5): |
155 | 150 | return run(cmd, shell=shell, stdout=PIPE, stderr=PIPE, input=input, cwd=dir, timeout=timeout) |
156 | 151 |
|
157 | | - |
158 | 152 | # makes a target |
159 | 153 | def make(target=''): |
160 | 154 | return execute(cmd=['make', target]) |
161 | 155 |
|
162 | 156 |
|
| 157 | +# compile a target |
| 158 | +def compile(target=''): |
| 159 | + return execute(cmd=['gcc', '-std=c99', target]) |
| 160 | + |
| 161 | +# run a file |
| 162 | +def run_program(command): |
| 163 | + return run(command, shell=True, stdout=PIPE, stderr=PIPE, cwd='.', timeout=10) |
| 164 | + |
| 165 | + |
| 166 | +# passed message |
| 167 | +def passed(*args): |
| 168 | + if len(args) > 0: |
| 169 | + return 'passed: ' + args[0] |
| 170 | + return 'passed' |
| 171 | + |
| 172 | + |
| 173 | +# failed message |
| 174 | +def failed(*args): |
| 175 | + if len(args) > 0: |
| 176 | + return 'failed: ' + args[0] |
| 177 | + return 'failed' |
| 178 | + |
| 179 | + |
| 180 | +# incomplete message |
| 181 | +def incomplete(*args): |
| 182 | + if len(args) > 0: |
| 183 | + return 'incomplete: ' + args[0] |
| 184 | + return 'incomplete' |
| 185 | + |
| 186 | + |
| 187 | +# creates a compilation error msg |
| 188 | +def create_error(filename, msg): |
| 189 | + if msg != '': |
| 190 | + return '[%s]\n\n%s\n' % (filename, msg) |
| 191 | + return '' |
| 192 | + |
| 193 | + |
| 194 | +# creates a pretty result report |
| 195 | +def report(table): |
| 196 | + return tabulate(table, headers=['Exercise', 'Grade', 'Message']) |
| 197 | + |
| 198 | + |
| 199 | +# writes autograder result (lab) |
| 200 | +def write_result(grade, msg): |
| 201 | + write_json({'grade': grade, 'output': msg}, 'output.json') |
| 202 | + |
| 203 | + |
| 204 | +# finds a specific function in the ast |
| 205 | +def find_func(ast, name): |
| 206 | + for f in ast.ext: |
| 207 | + if type(f) == pycparser.c_ast.FuncDef and f.decl.name == name: |
| 208 | + return f |
| 209 | + return None |
| 210 | + |
| 211 | + |
163 | 212 | # parses a form |
164 | 213 | def parse_form(f): |
165 | 214 | f = open(f, 'r', encoding='latin1') |
@@ -212,93 +261,3 @@ def parse_c_raw(filename): |
212 | 261 | text = task.stdout.decode().strip() |
213 | 262 | parser = pycparser.c_parser.CParser() |
214 | 263 | return parser.parse(text) |
215 | | - |
216 | | - |
217 | | -# passed message |
218 | | -def passed(*args): |
219 | | - if len(args) > 0: |
220 | | - return 'passed: ' + args[0] |
221 | | - return 'passed' |
222 | | - |
223 | | - |
224 | | -# failed message |
225 | | -def failed(*args): |
226 | | - if len(args) > 0: |
227 | | - return 'failed: ' + args[0] |
228 | | - return 'failed' |
229 | | - |
230 | | - |
231 | | -# incomplete message |
232 | | -def incomplete(*args): |
233 | | - if len(args) > 0: |
234 | | - return 'incomplete: ' + args[0] |
235 | | - return 'incomplete' |
236 | | - |
237 | | - |
238 | | -# creates a compilation error msg |
239 | | -def create_error(filename, msg): |
240 | | - if msg != '': |
241 | | - return '[%s]\n\n%s\n' % (filename, msg) |
242 | | - return '' |
243 | | - |
244 | | - |
245 | | -# creates a pretty result report |
246 | | -def report(table): |
247 | | - return tabulate(table, headers=['Exercise', 'Grade', 'Message']) |
248 | | - |
249 | | - |
250 | | -# writes autograder result |
251 | | -def write_result(grade, msg): |
252 | | - write_json({'grade': grade, 'output': msg}, 'output.json') |
253 | | - |
254 | | - |
255 | | -# finds a specific function in the ast |
256 | | -def find_func(ast, name): |
257 | | - for f in ast.ext: |
258 | | - if type(f) == pycparser.c_ast.FuncDef and f.decl.name == name: |
259 | | - return f |
260 | | - return None |
261 | | - |
262 | | - |
263 | | -class AWSTask: |
264 | | - |
265 | | - def __init__(self, name, instance='c5.2xlarge', AMI='ami-0e262d4de9c0b73fd', key='cc3'): |
266 | | - ec2 = boto3.resource('ec2') |
267 | | - self.instance = ec2.create_instances( |
268 | | - ImageId=AMI, |
269 | | - MaxCount=1, |
270 | | - MinCount=1, |
271 | | - InstanceType=instance, |
272 | | - SecurityGroupIds=['sg-00b6ec171be0d43f7'], |
273 | | - KeyName=key, |
274 | | - TagSpecifications=[ |
275 | | - { |
276 | | - 'ResourceType': 'instance', |
277 | | - 'Tags': [ |
278 | | - { |
279 | | - 'Key': 'Name', |
280 | | - 'Value': name |
281 | | - }, |
282 | | - ] |
283 | | - }, |
284 | | - ], |
285 | | - )[0] |
286 | | - self.instance.wait_until_running() |
287 | | - self.instance.reload() |
288 | | - self.instance.wait_until_running() |
289 | | - self.key = key |
290 | | - self.ipv4 = self.instance.public_ip_address |
291 | | - |
292 | | - def connect(self): |
293 | | - key = paramiko.RSAKey.from_private_key_file(self.key + '.pem') |
294 | | - client = paramiko.SSHClient() |
295 | | - client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) |
296 | | - client.connect(hostname=self.ipv4, username='ubuntu', pkey=key) |
297 | | - self.client = client |
298 | | - |
299 | | - def run(self, cmd, timeout=30): |
300 | | - stdin, stdout, stderr = self.client.exec_command(cmd, timeout=timeout) |
301 | | - return (stdout.read().decode(), stderr.read().decode()) |
302 | | - |
303 | | - def terminate(self): |
304 | | - self.instance.terminate() |
0 commit comments