def ValidateTag(parameter_list, studentdir, goal_type, inputtag, allowed_special_answer, logger): # if allowed_special_answer is true, then allow 'answer=<string>' # UNLESS if the goal_type is matchacross returntag = "" if '=' in inputtag: if not allowed_special_answer: logger.error("goals.config only answertag is allowed answer=<string>, resulttag (%s) is not" % inputtag) sys.exit(1) if goal_type == "matchacross": logger.error("goals.config answer=<string> and goal_type==matchacross is not allowed") sys.exit(1) (target, finaltag) = inputtag.split('=') returntag = getTagValue(parameter_list, target, finaltag, logger) elif inputtag.startswith('(') and inputtag.endswith(')'): returntag = 'result.%s' % inputtag elif '.' in inputtag: logger.debug("tag %s contains '.'" % inputtag) (target, finaltag) = inputtag.split('.') if not target in answer_tokens: logger.error("goals.config tag=<string> then tag must be:(%s), got %s" % (','.join(answer_tokens), inputtag)) sys.exit(1) if not MyUtil.CheckAlphaDashUnder(finaltag): logger.error("Invalid characters in goals.config's tag (%s)" % inputtag) sys.exit(1) returntag = getTagValue(parameter_list, target, finaltag, logger) else: logger.debug("tag is %s" % inputtag) if not MyUtil.CheckAlphaDashUnder(inputtag): logger.error("Invalid characters in goals.config's tag (%s)" % inputtag) sys.exit(1) returntag = 'result.%s' % inputtag return returntag
def ValidateUniqueConfig(actual_parsing, studentlabdir, container_list, labidname, each_key, each_value, logger): valid_field_types = ['CHECKSUM'] if not MyUtil.CheckAlphaDashUnder(each_key): logger.error("Not allowed characters in unique.config's key (%s)" % each_key) sys.exit(1) values = [] # expecting: # . - [ filename ] : [<field_type>] # field_type = (a valid_field_type defined above) - currently only CHECKSUM is supported # NOTE: Split using ' : ' - i.e., "space colon space" values = [x.strip() for x in each_value.split(' : ')] #print values numvalues = len(values) logger.debug("each_value is %s -- numvalues is (%d)" % (each_value, numvalues)) if numvalues < 2: logger.error("found no ':' delimiter in %s" % each_value) sys.exit(1) if numvalues < 3 and values[1] not in line_types: logger.error( "Offending line: (%s).\n Perhaps there is a missing ':'?" % each_value) logger.error("unique.config expected %s to be one of these: %s." % (values[1], str(line_types))) sys.exit(1) line_at = findLineIndex(values) if line_at is None: logger.error('No line_type in %s' % each_value) sys.exit(1) num_splits = line_at + 1 #print "line_at is (%d) and num_splits is (%d)" % (line_at, num_splits) # NOTE: Split using ' : ' - i.e., "space colon space" values = [x.strip() for x in each_value.split(' : ', num_splits)] newfilename = values[0].strip() logger.debug('newfilename is %s' % newfilename) # <cfgcontainername>:<filename> if ':' in newfilename: cfgcontainername, filename = newfilename.split(':', 1) else: if len(container_list) > 1: logger.error( 'No container name found in multi container lab entry (%s = %s)' % (each_key, each_value)) sys.exit(1) cfgcontainername = "" filename = newfilename # Construct proper containername from cfgcontainername if cfgcontainername == "": containername = "" else: containername = labidname + "." + cfgcontainername + ".student" if filename not in logfilelist: logfilelist.append(filename) return newfilename
def ParseGoals(homedir, studentdir, logger_in): MYHOME = homedir logger = logger_in nametags = [] configfilename = os.path.join(MYHOME,'.local','instr_config', 'goals.config') configfile = open(configfilename) configfilelines = configfile.readlines() configfile.close() lab_instance_seed = GetLabInstanceSeed(studentdir, logger) container_user = "" param_filename = os.path.join(MYHOME, '.local', 'config', 'parameter.config') pp = ParameterParser.ParameterParser(None, container_user, lab_instance_seed, logger) parameter_list = pp.ParseParameterConfig(param_filename) for line in configfilelines: linestrip = line.rstrip() if linestrip: if not linestrip.startswith('#'): logger.debug("Current linestrip is (%s)" % linestrip) try: (each_key, each_value) = linestrip.split('=', 1) except: logger.error('goal lacks "=" character, %s' % linestrip) sys.exit(1) each_key = each_key.strip() if not MyUtil.CheckAlphaDashUnder(each_key): logger.error("Invalid characters in goals.config's key (%s)" % each_key) sys.exit(1) if len(each_key) > 15: logger.debug("goal (%s) is more than 15 characters long\n" % each_key) values = [] # expecting - either: # <type> : <operator> : <resulttag> : <answertag> # <type> : <goal1tag> : <goal2tag> # <type> : <string> values = each_value.split(" : ") numvalues = len(values) logger.debug('numvalues is %d values are: %s' % (numvalues, str(values))) if not (numvalues == 4 or numvalues == 3 or numvalues == 2): logger.error("goals.config contains unexpected value (%s) format" % each_value) sys.exit(1) if numvalues == 4: ''' <type> : <operator> : <resulttag> : <answertag> ''' goal_type = values[0].strip() goal_operator = values[1].strip() resulttag = values[2].strip() answertag = values[3].strip() # Allowed 'answer=<string>' for answertag only valid_answertag = ValidateTag(parameter_list, studentdir, goal_type, answertag, True, logger) valid_resulttag = ValidateTag(parameter_list, studentdir, goal_type, resulttag, False, logger) if not (goal_type == "matchany" or goal_type == "matchlast" or goal_type == "matchacross" or goal_type == "count" or goal_type == "value" or goal_type == "execute"): logger.error("Error found in line (%s)" % linestrip) logger.error("goals.config contains unrecognized type (1) (%s)" % goal_type) sys.exit(1) if not (goal_type == "execute"): # If goal_type is not 'execute' then check the goal_operator if not (goal_operator == "string_equal" or goal_operator == "hash_equal" or goal_operator == "string_diff" or goal_operator == "string_start" or goal_operator == "string_end" or goal_operator == "string_contains" or goal_operator == "integer_equal" or goal_operator == "integer_greater" or goal_operator == "integer_lessthan"): logger.error("Error found in line (%s)" % linestrip) logger.error("goals.config contains unrecognized operator (%s)" % (goal_operator)) sys.exit(1) else: # Make sure the file to be executed exist execfile = os.path.join(MYHOME, '.local', 'bin', goal_operator) if not (os.path.exists(execfile) and os.path.isfile(execfile)): logger.error("Error found in line (%s)" % linestrip) logger.error("goals.config contains execute goals with missing exec file (%s)" % (goal_operator)) sys.exit(1) nametags.append(MyGoal(each_key, goal_type, goaloperator=goal_operator, answertag=valid_answertag, resulttag=valid_resulttag)) #print "goal_type non-boolean" #print nametags[each_key].goal_dict() elif numvalues == 3: ''' <type> : <goal1tag> : <goal2tag> ''' goal_type = values[0].strip() if goal_type == 'time_before' or goal_type == 'time_during' or goal_type == 'time_not_during': goal1tag = values[1].strip() goal2tag = values[2].strip() nametags.append(MyGoal(each_key, goal_type, goal1tag=goal1tag, goal2tag=goal2tag)) elif goal_type == 'count_greater': answertag = values[1].strip() subgoal_list = values[2].strip() nametags.append(MyGoal(each_key, goal_type, answertag=answertag, boolean_string=subgoal_list)) else: logger.error('Could not parse goals.config line %s' % each_value) sys.exit(1) #print "goal_type non-boolean" #print nametags[each_key].goal_dict() else: ''' <type> : <string> ''' goal_type = values[0].strip() if goal_type == 'boolean': boolean_string = values[1].strip() nametags.append(MyGoal(each_key, goal_type, boolean_string=boolean_string)) elif goal_type == 'is_true' or goal_type == 'is_false': resulttag = values[1].strip() #print('parsegoals type is %s result %s' % (goal_type, resulttag)) nametags.append(MyGoal(each_key, goal_type, resulttag=resulttag)) elif goal_type == 'count' or goal_type == 'value': resulttag = values[1].strip() nametags.append(MyGoal(each_key, goal_type, resulttag=resulttag)) elif goal_type == 'count_greater': logger.error('missing count_greater value in %s ?' % linestrip) sys.exit(1) else: logger.error('Could not parse goals.config line %s' % linestrip) sys.exit(1) #print "goal_type boolean" #print nametags[each_key].goal_dict() #nametags[each_key].toJSON() #nametags[each_key].goal_type = goal_type #nametags[each_key].goal_operator = goal_operator #nametags[each_key].answertag = valid_answertag #nametags[each_key].resulttag = valid_resulttag #nametags[each_key].boolean_string = boolean_string #else: # print "Skipping empty linestrip is (%s)" % linestrip #print nametags["crash"].toJSON() #for (each_key, each_goal) in nametags.items(): # print nametags[each_key].toJSON() student_parent_dir = os.path.dirname(studentdir) resultsdir = os.path.join(student_parent_dir, '.local','result') try: os.makedirs(resultsdir) except: pass outputjsonfname = os.path.join(resultsdir,'goals.json') #print "GoalsParser: Outputjsonfname is (%s)" % outputjsonfname #print nametags jsonoutput = open(outputjsonfname, "w") jsondumpsoutput = json.dumps([x.goal_dict() for x in nametags], indent=4) jsonoutput.write(jsondumpsoutput) jsonoutput.write('\n') jsonoutput.close() return parameter_list
def ProcessConfigLine(actual_parsing, studentlabdir, container_list, labidname, result_key, result_value, logger): ''' This function populates a set of global structures used in processing the results ''' valid_field_types = [ 'TOKEN', 'GROUP', 'PARENS', 'QUOTES', 'SLASH', 'LINE_COUNT', 'CHECKSUM', 'CONTAINS', 'FILE_REGEX', 'FILE_REGEX_TS', 'SEARCH', 'PARAM', 'STRING_COUNT', 'COMMAND_COUNT' ] if not MyUtil.CheckAlphaDashUnder(result_key): logger.ERROR("Not allowed characters in results.config's key (%s)" % result_key) sys.exit(1) values = [] # See the Labtainer Lab Designer User guide for syntax # NOTE: Split using ' : ' - i.e., "space colon space" values = [x.strip() for x in result_value.split(' : ')] #print values numvalues = len(values) logger.DEBUG("result_value is %s -- numvalues is (%d)" % (result_value, numvalues)) if numvalues < 2: logger.ERROR("found no ':' delimiter in %s" % result_value) sys.exit(1) if numvalues < 3 and values[1] not in just_field_type: logger.ERROR( "Offending line: (%s).\n Perhaps there is a missing ':'?" % result_value) logger.ERROR("results.config expected %s to be one of these: %s." % (values[1], str(just_field_type))) sys.exit(1) line_at = findLineIndex(values) if line_at is None: logger.ERROR('No line_type in %s' % result_value) sys.exit(1) num_splits = line_at + 1 #print "line_at is (%d) and num_splits is (%d)" % (line_at, num_splits) # NOTE: Split using ' : ' - i.e., "space colon space" values = [x.strip() for x in result_value.split(' : ', num_splits)] # get optional container name and determine if it is 'stdin' or 'stdout' newprogname_type = values[0].strip() logger.DEBUG('newprogname_type is %s' % newprogname_type) cmd = values[1].strip() # <cfgcontainername>:<exec_program>.<type> if ':' in newprogname_type: ''' [container_name:]<prog>.[stdin | stdout] | [container_name:]file_path[:time_program] ''' cfgcontainername = '' parts = newprogname_type.split(':') if len(parts) == 2: if parts[0].startswith('/'): progname_type = parts[0] if len(container_list) > 1: logger.ERROR( 'No container name found in multi container lab entry (%s = %s)' % (result_key, result_value)) sys.exit(1) else: cfgcontainername = parts[0] progname_type = parts[1] elif len(parts) == 3: cfgcontainername = parts[0] progname_type = parts[1] else: if len(container_list) > 1: logger.ERROR( 'No container name found in multi container lab entry (%s = %s)' % (result_key, result_value)) sys.exit(1) if newprogname_type.endswith('stdin') or newprogname_type.endswith('stdout') \ or newprogname_type.endswith('prgout'): cfgcontainername = container_list[0].split('.')[1] #print('assigned to %s' % cfgcontainername) else: cfgcontainername = "" progname_type = newprogname_type # Construct proper containername from cfgcontainername if cfgcontainername == "": containername = "" else: containername = labidname + "." + cfgcontainername + ".student" logger.DEBUG('Start to populate exec_program_list, progname_type is %s' % progname_type) # No longer restricted to stdin/stdout filenames if ('stdin' not in progname_type) and ('stdout' not in progname_type) and ( 'prgout' not in progname_type): # Not stdin/stdout - add the full name logger.DEBUG('Not a STDIN or STDOUT: %s ' % progname_type) else: (exec_program, targetfile) = progname_type.rsplit('.', 1) exec_program_list = [] # Can only parse for wildcard if it is actual parsing - not validation if exec_program == "*" and actual_parsing: exec_program_list = GetExecProgramList(containername, studentlabdir, container_list, targetfile) logger.DEBUG("wildcard, exec_program_list is %s" % exec_program_list) else: exec_program_list.append(exec_program) logger.DEBUG('exec_program %s, append to list, container is %s' % (exec_program, containername)) if containername != "": #print('containername is %s' % containername) if containername not in container_exec_proglist: container_exec_proglist[containername] = [] for cur_exec_program in exec_program_list: if cur_exec_program not in container_exec_proglist[ containername]: container_exec_proglist[containername].append( cur_exec_program) logger.DEBUG('proglist is %s' % str(container_exec_proglist[containername])) else: if "CURRENT" not in container_exec_proglist: container_exec_proglist["CURRENT"] = [] for cur_exec_program in exec_program_list: if cur_exec_program not in container_exec_proglist["CURRENT"]: container_exec_proglist["CURRENT"].append(cur_exec_program) #print container_exec_proglist["CURRENT"] #print container_exec_proglist # Validate <field_type> - if exists (i.e., line_at == 3) # - because <field_type> is optional field_type = None if line_at == 3: field_type = values[1].strip() if field_type not in valid_field_types: logger.ERROR("results.config line (%s)\n" % result_value) logger.ERROR("results.config invalid field_type") sys.exit(1) # Sanity check for 'PARAM' type if values[line_at] == 'PARAM': logger.DEBUG("progname_type is (%s)" % progname_type) if not progname_type.endswith('stdin'): logger.ERROR("results.config line (%s)\n" % result_value) logger.ERROR("PARAM field_type on non stdin file") sys.exit(1) paramtoken_id = values[2].strip() try: paramindex = int(paramtoken_id) except: logger.ERROR("results.config line (%s)\n" % result_value) logger.ERROR('PARAM field_type could not parse int from %s' % paramtoken_id) sys.exit(1) # If line_type1 (line_at != 1) - verify token id if field_type != 'SEARCH' and line_at != 1: token_index = 1 if line_at == 3: token_index = 2 ValidateTokenId(result_value, values[token_index], logger) if values[line_at] == 'LINE': try: int(values[line_at + 1]) except: logger.ERROR('Expected integer following LINE type, got %s in %s' % (values[line_at + 1], result_value)) sys.exit(1) return newprogname_type, cmd