def add(self, defn): """Adds the given Command Definition to this Command Dictionary.""" if defn.name not in self: self[defn.name] = defn else: msg = "Duplicate Command name '%s'" % defn.name log.error(msg) raise util.YAMLError(msg) if defn._opcode not in self.opcodes: self.opcodes[defn._opcode] = defn else: msg = "Duplicate Command opcode '%s'" % defn._opcode log.error(msg) raise util.YAMLError(msg)
def add(self, defn): """Adds the given Packet Definition to this Telemetry Dictionary.""" if defn.name not in self: self[defn.name] = defn else: msg = f"Duplicate packet name {defn.name}" log.error(msg) raise util.YAMLError(msg)
def add(self, defn): if defn.name not in self and defn.code not in self.codes: self[defn.name] = defn self.codes[defn.code] = defn else: msg = "EVRDict: Duplicate EVR name/code {}".format(defn) log.error(msg) raise util.YAMLError(msg)
def schema_val(self, messages=None): "Perform validation with processed YAML and Schema" self._ymlproc = YAMLProcessor(self._ymlfile) self._schemaproc = SchemaProcessor(self._schemafile) valid = True log.debug( "BEGIN: Schema-based validation for YAML '%s' with schema '%s'", self._ymlfile, self._schemafile, ) # Make sure the yml and schema have been loaded if self._ymlproc.loaded and self._schemaproc.loaded: # Load all of the yaml documents. Could be more than one in the same YAML file. for docnum, data in enumerate( yaml.load_all(self._ymlproc.data, Loader=yaml.Loader)): # Since YAML allows integer keys but JSON does not, we need to first # dump the data as a JSON string to encode all of the potential integers # as strings, and then read it back out into the YAML format. Kind of # a clunky workaround but it works as expected. data = yaml.load(json.dumps(data), Loader=yaml.Loader) # Now we want to get a validator ready v = jsonschema.Draft4Validator(self._schemaproc.data) # Loop through the errors (if any) and set valid = False if any are found # Display the error message for error in v.iter_errors(data): msg = ("Schema-based validation failed for YAML file '" + self._ymlfile + "'") self.ehandler.process(docnum, self._ymlproc.doclines, error, messages) valid = False if not valid: log.error(msg) elif not self._ymlproc.loaded: raise util.YAMLError("YAML must be loaded in order to validate.") elif not self._schemaproc.loaded: raise jsonschema.SchemaError( "Schema must be loaded in order to validate.") log.debug("END: Schema-based validation complete for '%s'", self._ymlfile) return valid
def process(self, ymlfile): """Cleans out all document tags from the YAML file to make it JSON-friendly to work with the JSON Schema. """ output = "" try: # Need a list of line numbers where the documents resides # Used for finding/displaying errors self.doclines = [] linenum = None with open(ymlfile, "r") as txt: for linenum, line in enumerate(txt): # Pattern to match document start lines doc_pattern = re.compile(r"(---) (![a-z]+)(.*$)", flags=re.I) # Pattern to match sequence start lines seq_pattern = re.compile(r"(\s*)(-+) !([a-z]+)(.*$)", flags=re.I) # If we find a document, remove the tag if doc_pattern.match(line): line = doc_pattern.sub(r"---", line).lower() self.doclines.append(linenum) elif seq_pattern.match(line): # Replace the sequence start with key string line = seq_pattern.sub( r"\1\2 \3: line " + str(linenum), line).lower() output = output + line if linenum is None: msg = "Empty YAML file: " + ymlfile raise util.YAMLError(msg) else: # Append one more document to docline for the end self.doclines.append(linenum + 1) return output except IOError as e: msg = "Could not process YAML file '" + ymlfile + "': '" + str( e) + "'" raise IOError(msg)
def load(self, ymlfile=None): """Load and process the YAML file""" if ymlfile is not None: self.ymlfile = ymlfile try: # If yaml should be 'cleaned' of document references if self._clean: self.data = self.process(self.ymlfile) else: with open(self.ymlfile, "rb") as stream: for data in yaml.load_all(stream, Loader=yaml.Loader): self.data.append(data) self.loaded = True except ScannerError as e: msg = "YAML formattting error - '" + self.ymlfile + ": '" + str( e) + "'" raise util.YAMLError(msg)
def test_YAMLError_exception(log_mock): message = "foo" e = util.YAMLError(message) assert message == e.message log_mock.assert_called_with(message)
def content_val(self, ymldata=None, messages=None): """Validates the Command Dictionary to ensure the contents for each of the fields meets specific criteria regarding the expected types, byte ranges, etc.""" self._ymlproc = YAMLProcessor(self._ymlfile, False) # Turn off the YAML Processor log.debug("BEGIN: Content-based validation of Command dictionary") if ymldata is not None: cmddict = ymldata elif ymldata is None and self._ymlproc.loaded: cmddict = self._ymlproc.data elif not self._ymlproc.loaded: raise util.YAMLError("YAML failed to load.") try: # instantiate the document number. this will increment in order to # track the line numbers and section where validation fails docnum = 0 # boolean to hold argument validity argsvalid = True # list of rules to validate against rules = [] ### set the command rules # # set uniqueness rule for command names rules.append( UniquenessRule('name', "Duplicate command name: %s", messages)) # set uniqueness rule for opcodes rules.append( UniquenessRule('opcode', "Duplicate opcode: %s", messages)) # ### for cmdcnt, cmddefn in enumerate(cmddict[0]): # check the command rules for rule in rules: rule.check(cmddefn) # list of argument rules to validate against argrules = [] ### set rules for command arguments # # set uniqueness rule for opcodes argrules.append( UniquenessRule( 'name', "Duplicate argument name: " + cmddefn.name + ".%s", messages)) # set type rule for arg.type argrules.append( TypeRule( 'type', "Invalid argument type for argument: " + cmddefn.name + ".%s", messages)) # set argument size rule for arg.type.nbytes argrules.append( TypeSizeRule( 'nbytes', "Invalid argument size for argument: " + cmddefn.name + ".%s", messages)) # set argument enumerations rule to check no enumerations contain un-quoted YAML special variables argrules.append( EnumRule( 'enum', "Invalid enum value for argument: " + cmddefn.name + ".%s", messages)) # set byte order rule to ensure proper ordering of aruguments argrules.append( ByteOrderRule( 'bytes', "Invalid byte order for argument: " + cmddefn.name + ".%s", messages)) # ### argdefns = cmddefn.argdefns for arg in argdefns: # check argument rules for rule in argrules: rule.check(arg) # check if argument rule failed, if so set the validity to False if not all(r.valid is True for r in argrules): argsvalid = False log.debug("END: Content-based validation complete for '%s'", self._ymlfile) # check validity of all command rules and argument validity return all(rule.valid is True for rule in rules) and argsvalid except util.YAMLValidationError, e: # Display the error message if messages is not None: if len(e.message) < 128: msg = "Validation Failed for YAML file '" + self._ymlfile + "': '" + str( e.message) + "'" else: msg = "Validation Failed for YAML file '" + self._ymlfile + "'" log.error(msg) self.ehandler.process(docnum, self.ehandler.doclines, e, messages) return False