From 52967bd65486e407c884fe392c14fe633bba285c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beat=20K=C3=BCng?= <beat-kueng@gmx.net>
Date: Mon, 27 Aug 2018 11:40:32 +0200
Subject: [PATCH] Tools/validate_yaml: add schema validation for module yaml
 config files

---
 Tools/validate_yaml.py  | 65 ++++++++++++++++++++++++++++++++++++
 test/module_schema.yaml | 73 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 138 insertions(+)
 create mode 100755 Tools/validate_yaml.py
 create mode 100644 test/module_schema.yaml

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
+
-- 
GitLab