|
| 1 | +# Copyright The IETF Trust 2021, All Rights Reserved |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | + |
| 15 | +__author__ = 'Miroslav Kovac' |
| 16 | +__copyright__ = 'Copyright The IETF Trust 2021, All Rights Reserved' |
| 17 | +__license__ = 'Apache License, Version 2.0' |
| 18 | + |
| 19 | + |
| 20 | +import logging |
| 21 | +import os |
| 22 | +from pathlib import Path |
| 23 | +import typing as t |
| 24 | +from datetime import datetime, timezone |
| 25 | +from subprocess import CalledProcessError, run, check_output |
| 26 | +from time import perf_counter |
| 27 | + |
| 28 | + |
| 29 | +class LyvParser: |
| 30 | + """ |
| 31 | + Cover the parsing of the module with lighty-yang-validator parser and validator |
| 32 | + """ |
| 33 | + |
| 34 | + LYV_CMD = '/home/lyv/lyv' |
| 35 | + try: |
| 36 | + VERSION = ( |
| 37 | + check_output(f'{LYV_CMD} -v', shell=True).decode('utf-8').replace("Version: ", "").split("\n")[0].rstrip() |
| 38 | + ) |
| 39 | + except CalledProcessError: |
| 40 | + VERSION = 'undefined' |
| 41 | + LOG = logging.getLogger(__name__) |
| 42 | + |
| 43 | + def __init__(self, context_directories, file_name: str, working_directory: str): |
| 44 | + self._working_directory = working_directory |
| 45 | + |
| 46 | + # Build the command |
| 47 | + cmds = [self.LYV_CMD, os.path.join(working_directory, file_name)] |
| 48 | + if context_directories: |
| 49 | + cmds.extend(['-p', ":".join(context_directories)]) |
| 50 | + self._lyv_cmd = cmds |
| 51 | + |
| 52 | + def parse_module(self): |
| 53 | + lyv_res: t.Dict[str, t.Union[str, int]] = {'time': datetime.now(timezone.utc).isoformat()} |
| 54 | + |
| 55 | + self.LOG.info(f'Starting lyv parse using command {" ".join(self._lyv_cmd)}') |
| 56 | + t0 = perf_counter() |
| 57 | + result = run(self._lyv_cmd, capture_output=True, text=True) |
| 58 | + td = perf_counter()-t0 |
| 59 | + |
| 60 | + # LYV returns everything in stdout, even errors. The only thing other than an error it can |
| 61 | + # return is a single line with the html output name, which we don't care about (for now). |
| 62 | + _HTML_GEN_STRING = "html generated to " |
| 63 | + stdout = result.stdout |
| 64 | + for line in stdout.splitlines(): |
| 65 | + if line.startswith(_HTML_GEN_STRING): |
| 66 | + htmlpath = Path(line.replace(_HTML_GEN_STRING, "")) |
| 67 | + if htmlpath.is_file(): |
| 68 | + # Remove unnecessary HTML report, but maybe we could display it somehow instead? |
| 69 | + htmlpath.unlink() |
| 70 | + stdout = stdout.replace(line, "", 1) |
| 71 | + break |
| 72 | + |
| 73 | + # Post-process results |
| 74 | + dirname = os.path.dirname(self._working_directory) |
| 75 | + lyv_res['stdout'] = stdout.replace(f'{dirname}/', '').strip() |
| 76 | + lyv_res['stderr'] = result.stderr.replace(f'{dirname}/', '').strip() |
| 77 | + lyv_res['name'] = 'lighty-yang-validator' |
| 78 | + lyv_res['version'] = self.VERSION |
| 79 | + lyv_res['code'] = result.returncode |
| 80 | + lyv_res['command'] = ' '.join(self._lyv_cmd) |
| 81 | + lyv_res['validation_time'] = td |
| 82 | + |
| 83 | + return lyv_res |
0 commit comments