From 70410740a68dbaf9df2c5f97f1f83188491256f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Fri, 8 Oct 2021 14:38:37 +0200 Subject: [PATCH 1/3] parser.py: fix curses error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setafb must be bytes, as we will decode() it later on. Signed-off-by: Vincent Stehlé --- parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.py b/parser.py index 5601902..236aa2d 100755 --- a/parser.py +++ b/parser.py @@ -45,7 +45,7 @@ green = '' if os.isatty(sys.stdout.fileno()): curses.setupterm() - setafb = curses.tigetstr('setaf') or '' + setafb = curses.tigetstr('setaf') or bytes() setaf = setafb.decode() normal = curses.tigetstr('sgr0').decode() or '' red = curses.tparm(setafb, curses.COLOR_RED).decode() or '' -- GitLab From ccc4909ca779f4191ca47f1ac2167cc561c456a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Fri, 8 Oct 2021 16:21:05 +0200 Subject: [PATCH 2/3] parser.py: more robust colors probing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even tparm() can fail on some platforms; make the whole colors probing part more robust by catching exceptions. Reported-by: Ilias Apalodimas Signed-off-by: Vincent Stehlé --- parser.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/parser.py b/parser.py index 236aa2d..d8038c9 100755 --- a/parser.py +++ b/parser.py @@ -44,13 +44,16 @@ yellow = '' green = '' if os.isatty(sys.stdout.fileno()): - curses.setupterm() - setafb = curses.tigetstr('setaf') or bytes() - setaf = setafb.decode() - normal = curses.tigetstr('sgr0').decode() or '' - red = curses.tparm(setafb, curses.COLOR_RED).decode() or '' - yellow = curses.tparm(setafb, curses.COLOR_YELLOW).decode() or '' - green = curses.tparm(setafb, curses.COLOR_GREEN).decode() or '' + try: + curses.setupterm() + setafb = curses.tigetstr('setaf') or bytes() + setaf = setafb.decode() + normal = curses.tigetstr('sgr0').decode() or '' + red = curses.tparm(setafb, curses.COLOR_RED).decode() or '' + yellow = curses.tparm(setafb, curses.COLOR_YELLOW).decode() or '' + green = curses.tparm(setafb, curses.COLOR_GREEN).decode() or '' + except Exception: + pass # Compute the plural of a word. -- GitLab From 8c803d0627fc4914b5ff081c919dcc23bb75690d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Fri, 17 Dec 2021 17:08:24 +0100 Subject: [PATCH 3/3] Add a schema to validate configurations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add a jsonschema describing requirements on the configuration file. - Add a --validate-config and a --schema parser options to make use of the schema. - Add a check for the EBBR.yaml configuration in `make check'. - Document in the README. Signed-off-by: Vincent Stehlé --- Makefile | 4 ++- README.md | 19 +++++++++++- parser.py | 67 ++++++++++++++++++++++++++++++++++------- schema.yaml | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 schema.yaml diff --git a/Makefile b/Makefile index 128e1ed..3df61f8 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,8 @@ help: @echo 'Targets:' @echo ' all' @echo ' check Perform sanity checks' - @echo ' (currently yamllint, shellcheck and flake8)' + @echo ' (currently yamllint, shellcheck and flake8,' + @echo ' as well as configuration validation)' @echo ' clean' @echo ' doc Generate README.pdf' @echo ' help Print this help.' @@ -21,6 +22,7 @@ check: yamllint . shellcheck $$(find -name '*.sh') flake8 + ./parser.py --validate-config clean: -rm -f README.pdf diff --git a/README.md b/README.md index ecac247..6a38709 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ correctly. Depending on your Linux distribution, this might be available as the It is also recommended to install the [packaging] library for smooth version detection. Depending on your Linux distribution, this might be available as the `python3-packaging` package. +The [python-jsonschema] module is required for configuration validation. See [Configuration file]. If you want to generate the pdf version of this documentation or convert @@ -26,6 +27,7 @@ markdown results to HTML, you need to install [pandoc]. See [Usage] and [PyYAML]: https://github.com/yaml/pyyaml [packaging]: https://github.com/pypa/packaging [pandoc]: https://pandoc.org +[python-jsonschema]: https://python-jsonschema.readthedocs.io ## Quick Start @@ -217,6 +219,8 @@ The configuration file is in [YAML] format. It contains a list of rules: - rule... ``` +See also [Validating configurations]. + [YAML]: https://yaml.org ### Rule processing @@ -300,7 +304,19 @@ $ ./parser.py \ --fields 'count,result,name,comments' --uniq --print ... ``` -### Database of sequence files +### Validating configurations + +It is possible to validate the configuration using a schema with: + +``` {.sh} +$ ./parser.py --validate-config --schema ... +``` + +If no schema is specified, the default `schema.yaml` is be used. + +See also [Configuration file format]. + +## Database of sequence files The `seq.db` file contains a list of known sequence files, which allows to identify the input sequence file. @@ -341,6 +357,7 @@ reports errors: `shellcheck` Shell scripts. ------------------------------- +This will also perform validation of the `EBBR.yaml' configuration. See `make help`. ### db structure: diff --git a/parser.py b/parser.py index d8038c9..875d9b8 100755 --- a/parser.py +++ b/parser.py @@ -30,6 +30,13 @@ if 'yaml' in sys.modules: except ImportError: from yaml import Dumper + try: + import jsonschema + except ImportError: + print( + 'No jsonschema. You should install python3-jsonschema for' + ' configuration validation support...') + # Not all yaml versions have a Loader argument. if 'packaging.version' in sys.modules and \ version.parse(yaml.__version__) >= version.parse('5.1'): @@ -361,11 +368,9 @@ def apply_rules(cross_check, conf): f" after applying {r} {maybe_plural(r, 'rule')}") -# Use YAML configuration file and perform all the transformations described in -# there. -# See the README.md for details on the file format. -# We modify cross_check in-place -def use_config(cross_check, filename): +# Load YAML configuration file +# See the README.md for details on the configuration file format. +def load_config(filename): assert('yaml' in sys.modules) # Load configuration file @@ -376,7 +381,24 @@ def use_config(cross_check, filename): logging.debug('{} rule(s)'.format(len(conf))) sanitize_yaml(conf) - apply_rules(cross_check, conf) + return conf + + +# Validate configuration using a YAML schema file +def validate_config(conf, filename): + assert('yaml' in sys.modules and 'jsonschema' in sys.modules) + + # Load schema file + logging.debug(f'Read {filename}') + + with open(filename, 'r') as yamlfile: + schema = yaml.load(yamlfile, **yaml_load_args) + + fc = jsonschema.FormatChecker() + logging.debug(f'Format checkers: {fc.checkers.keys()}') + jsonschema.validate(instance=conf, schema=schema, format_checker=fc) + # If we arrive here, the configuration is valid. + logging.debug('Validated configuration') # Filter tests data @@ -841,7 +863,9 @@ if __name__ == '__main__': ' according to the first sort key, then the second, etc.' ' Sorting happens after update by the configuration rules.' ' Useful example: --sort' - ' "group,descr,set guid,test set,sub set,guid,name,log"', + ' "group,descr,set guid,test set,sub set,guid,name,log"' + ' When not validating a configuration file, an input .ekl and' + ' an input .seq files are required.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('--csv', help='Output .csv filename') parser.add_argument('--json', help='Output .json filename') @@ -862,8 +886,8 @@ if __name__ == '__main__': parser.add_argument( '--seq-db', help='Known sequence files database filename', default=f'{here}/seq.db') - parser.add_argument('log_file', help='Input .ekl filename') - parser.add_argument('seq_file', help='Input .seq filename') + parser.add_argument('log_file', nargs='?', help='Input .ekl filename') + parser.add_argument('seq_file', nargs='?', help='Input .seq filename') parser.add_argument('find_key', nargs='?', help='Search key') parser.add_argument('find_value', nargs='?', help='Search value') @@ -876,6 +900,12 @@ if __name__ == '__main__': parser.add_argument('--yaml', help='Output .yaml filename') parser.add_argument( '--template', help='Output .yaml config template filename') + parser.add_argument( + '--schema', help='Configuration schema', + default=f'{here}/schema.yaml') + parser.add_argument( + '--validate-config', action='store_true', + help='Validate config and exit') args = parser.parse_args() @@ -888,6 +918,22 @@ if __name__ == '__main__': ln = logging.getLevelName(logging.ERROR) logging.addLevelName(logging.ERROR, f"{red}{ln}{normal}") + # We must have a log file and a seq file, except when validating the config + if not args.validate_config: + if args.log_file is None: + logging.error("No input .ekl!") + sys.exit(1) + if args.seq_file is None: + logging.error("No input .seq!") + sys.exit(1) + + # Validate config and exit, if requested. + if args.validate_config: + assert('config' in args and args.config is not None) + conf = load_config(args.config) + validate_config(conf, args.schema) + sys.exit() + if args.input_md is not None: cross_check = read_md(args.input_md) else: @@ -905,7 +951,8 @@ if __name__ == '__main__': # Take configuration file into account. This can perform transformations on # the tests results. if 'config' in args and args.config is not None: - use_config(cross_check, args.config) + conf = load_config(args.config) + apply_rules(cross_check, conf) # Filter tests data, if requested if args.filter is not None: diff --git a/schema.yaml b/schema.yaml new file mode 100644 index 0000000..0e8188d --- /dev/null +++ b/schema.yaml @@ -0,0 +1,86 @@ +--- +$schema: http://json-schema.org/draft-07/schema# +title: SCT Parser configuration schema +description: | + The SCT Parser uses configurations files in YAML format to perform a number + of transformations on the tests results. + + This schema describes requirements on the configuration file, which can be + verified by the parser with the --validate-config option. See the README for + details. +definitions: + guid: + type: string + format: uuid + pattern: '[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}' +type: array +minItems: 1 +uniqueItems: true +items: + type: object + properties: + rule: + type: string + criteria: + type: object + properties: + descr: + type: string + group: + type: string + guid: + '$ref': '#/definitions/guid' + name: + type: string + log: + type: string + result: + enum: + - DROPPED + - FAILURE + - SKIPPED + - WARNING + revision: + type: string + set guid: + '$ref': '#/definitions/guid' + sub set: + type: string + test set: + type: string + update: + type: object + properties: + comments: + type: string + descr: + type: string + group: + type: string + guid: + '$ref': '#/definitions/guid' + name: + type: string + log: + type: string + result: + enum: + - DROPPED + - FAILURE + - IGNORED + - KNOWN ACS LIMITATION + - KNOWN U-BOOT LIMITATION + - SKIPPED + - WARNING + revision: + type: string + set guid: + '$ref': '#/definitions/guid' + sub set: + type: string + test set: + type: string + required: + - rule + - criteria + - update -- GitLab