diff --git a/Tools/validate_yaml.py b/Tools/validate_yaml.py new file mode 100755 index 0000000000..3cd9615324 --- /dev/null +++ b/Tools/validate_yaml.py @@ -0,0 +1,65 @@ +#! /usr/bin/env python +""" Script to validate YAML file(s) against a YAML schema file """ + +from __future__ import print_function + +import argparse +import os +import sys + +try: + import yaml +except: + print("Failed to import yaml.") + print("You may need to install it with 'sudo pip install pyyaml'") + print("") + raise + +try: + import cerberus +except: + print("Failed to import cerberus.") + print("You may need to install it with 'sudo pip install cerberus'") + print("") + raise + + +parser = argparse.ArgumentParser(description='Validate YAML file(s) against a schema') + +parser.add_argument('yaml_file', nargs='+', help='YAML config file(s)') +parser.add_argument('--schema-file', type=str, action='store', + help='YAML schema file', required=True) +parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', + help='Verbose Output') + +args = parser.parse_args() +schema_file = args.schema_file +yaml_files = args.yaml_file +verbose = args.verbose + +def load_yaml_file(file_name): + with open(file_name, 'r') as stream: + try: + return yaml.load(stream) + except yaml.YAMLError as exc: + print(exc) + raise + +# load the schema +schema = load_yaml_file(schema_file) +validator = cerberus.Validator(schema) + +# validate yaml files +for yaml_file in yaml_files: + if verbose: print("Validating {:}".format(yaml_file)) + document = load_yaml_file(yaml_file) + # ignore top-level entries prefixed with __ + for key in document.keys(): + if key.startswith('__'): del document[key] + + if not validator.validate(document): + print("Validation Errors:") + print(validator.errors) + print("") + raise Exception("Validation of {:} failed".format(yaml_file)) + diff --git a/test/module_schema.yaml b/test/module_schema.yaml new file mode 100644 index 0000000000..3a78054981 --- /dev/null +++ b/test/module_schema.yaml @@ -0,0 +1,73 @@ +# Cerberus Validation Schema for module configuration files. +# See http://docs.python-cerberus.org/en/stable/validation-rules.html + + +module_name: + # human-readable module name (used for descriptions, can contain spaces) + type: string + required: true + +serial_config: + # UART configuration (optional) + # A module can register autostart command(s) that are associated with a + # configuration parameter, so that a user can select on which serial port to + # run the command. + # One or several commands can be defined. + type: list + minlength: 1 + schema: + type: dict + schema: + command: + # script command that is executed on autostart. + # These variables can be used: + # ${SERIAL_DEV} Serial device (e.g. /dev/ttyS1) + # ${BAUD_PARAM} param name for the baudrate + # ${i} instance in [0, N-1] (for multi-instance commands) + # It's possible to use multiple lines. + type: string + required: true + + port_config_param: + # Parameter definition to configure on which port to run the + # command + type: dict + required: true + schema: + name: + # Parameter name (e.g. TEL_FRSKY_CONFIG, MAV_${i}_CONFIG) + type: string + regex: '[0-9A-Z_]+(\$\{i\}[0-9A-Z_]*)?' + required: true + group: + # Associated parameter group (e.g. GPS) + type: string + required: true + default: + # Default value(s). This can be a string to specify the + # serial tag (e.g. GPS1, TEL1, ...) or a list of strings + # for multiple instances. + # If omitted, the command is disabled by default. + anyof: + - type: string + - type: list + minlength: 1 + schema: + type: string + label: + # Optional command label (e.g. used in the autostart script). + # If omitted, module_name is used. + type: string + num_instances: + # Allow to configure and run multiple instances of a command. + # For multiple instances, '${i}' can be used to refer to + # an instance, for example in the parameter name or script + # command. + # Default: 1 + type: integer + min: 1 + +parameters: + type: list + # TODO +