Skip to content

Commit 2c44cd0

Browse files
committed
added lighty-yang-validator, added validation benchmarking
1 parent c97bd6c commit 2c44cd0

File tree

8 files changed

+152
-4
lines changed

8 files changed

+152
-4
lines changed

Dockerfile

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,23 @@ ARG YANGCATALOG_CONFIG_PATH
55
ARG CONFD_VERSION
66
ARG YANGLINT_VERSION
77
ARG XYM_VERSION
8+
ARG VALIDATOR_JDK_VERSION
9+
ARG MAVEN_VERSION
10+
ARG LYV_VERSION
811

912
ENV YANG_ID "$YANG_ID"
1013
ENV YANG_GID "$YANG_GID"
1114
ENV YANGCATALOG_CONFIG_PATH "$YANGCATALOG_CONFIG_PATH"
1215
ENV CONFD_VERSION "$CONFD_VERSION"
1316
ENV YANGLINT_VERSION "$YANGLINT_VERSION"
1417
ENV XYM_VERSION "$XYM_VERSION"
15-
18+
ENV VALIDATOR_JDK_VERSION "$VALIDATOR_JDK_VERSION"
19+
ENV MAVEN_VERSION "$MAVEN_VERSION"
20+
ENV LYV_VERSION "$LYV_VERSION"
1621
ENV VIRTUAL_ENV=/home/yangvalidator/yang-extractor-validator
1722

1823
RUN apt-get -y update
19-
RUN apt-get install -y clang cmake git gnupg2 gunicorn openssh-client rsyslog
24+
RUN apt-get install -y clang cmake git gnupg2 gunicorn openssh-client rsyslog unzip wget
2025

2126
# Create 'yang' user and group
2227
RUN groupadd -g ${YANG_GID} -r yang && useradd --no-log-init -r -g yang -u ${YANG_ID} -d /home yang
@@ -27,9 +32,33 @@ RUN mkdir -p /home/libyang/build
2732
WORKDIR /home/libyang/build
2833
RUN cmake -D CMAKE_BUILD_TYPE:String="Release" .. && make && make install
2934

35+
WORKDIR /home
36+
# Set up java and maven
37+
RUN wget https://download.oracle.com/java/${VALIDATOR_JDK_VERSION}/latest/jdk-${VALIDATOR_JDK_VERSION}_linux-x64_bin.deb
38+
RUN dpkg -i jdk-${VALIDATOR_JDK_VERSION}_linux-x64_bin.deb
39+
40+
RUN wget https://dlcdn.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.zip
41+
RUN unzip apache-maven-${MAVEN_VERSION}-bin.zip -d /usr/local/bin/
42+
RUN mv /usr/local/bin/apache-maven-${MAVEN_VERSION} /usr/local/bin/maven
43+
44+
# Set up Maven settings for building lighty-yang-validator
45+
RUN mkdir -p /root/.m2
46+
RUN wget -O /root/.m2/settings.xml https://raw.githubusercontent.com/opendaylight/odlparent/master/settings.xml
47+
48+
# Set up lighty-yang-validator
49+
RUN mkdir -p /home/lyv/src
50+
WORKDIR /home/lyv/src
51+
RUN git clone -b ${LYV_VERSION} --depth 1 https://github.com/PANTHEONtech/lighty-yang-validator.git
52+
WORKDIR /home/lyv/src/lighty-yang-validator
53+
RUN /usr/local/bin/maven/bin/mvn clean install
54+
RUN unzip ./target/lighty-yang-validator-*-bin.zip -d /home/lyv/
55+
RUN mv /home/lyv/lighty-yang-validator-*/* /home/lyv/
56+
RUN chmod +x /home/lyv/lyv
57+
3058
RUN sed -i "/imklog/s/^/#/" /etc/rsyslog.conf
3159
RUN rm -rf /var/lib/apt/lists/*
3260

61+
WORKDIR /home
3362
RUN pip3 install --upgrade pip
3463
COPY ./yang-validator-extractor/requirements.txt .
3564
RUN pip3 install -r requirements.txt

yangvalidator/v2/confdParser.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import logging
2121
import os
22+
from time import perf_counter
2223
import typing as t
2324
from datetime import datetime, timezone
2425
from subprocess import CalledProcessError, call, check_output
@@ -53,8 +54,10 @@ def parse_module(self):
5354
outfp = open(self.__confdc_outfile, 'w+')
5455
cresfp = open(self.__confdc_resfile, 'w+')
5556

56-
self.LOG.info(f'Starting to confd parse use command {" ".join(self.__confdc_command)}')
57+
self.LOG.info(f'Starting confd parse using command {" ".join(self.__confdc_command)}')
58+
t0 = perf_counter()
5759
status = call(self.__confdc_command, stdout=outfp, stderr=cresfp)
60+
td = perf_counter()-t0
5861

5962
confdc_output = confdc_stderr = ''
6063
if os.path.isfile(self.__confdc_outfile):
@@ -76,5 +79,6 @@ def parse_module(self):
7679
confdc_res['version'] = self.VERSION
7780
confdc_res['code'] = status
7881
confdc_res['command'] = ' '.join(self.__confdc_command)
82+
confdc_res['validation_time'] = td
7983

8084
return confdc_res

yangvalidator/v2/lyvParser.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
__email__ = '[email protected]'
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

yangvalidator/v2/pyangParser.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io
2222
import logging
2323
import os
24+
from time import perf_counter
2425
import typing as t
2526
from datetime import datetime, timezone
2627

@@ -77,7 +78,10 @@ def parse_module(self):
7778
self.LOG.info('no module provided')
7879
self.__ctx.add_module(self.__infile, module)
7980

81+
t0 = perf_counter()
8082
self.__ctx.validate()
83+
td = perf_counter()-t0
84+
8185
pyang_stderr, pyang_output = self.__print_pyang_output()
8286
dirname = os.path.dirname(self.__working_directory)
8387

@@ -87,6 +91,7 @@ def parse_module(self):
8791
pyang_res['version'] = self.VERSION
8892
pyang_res['code'] = 0 if not pyang_stderr else 1
8993
pyang_res['command'] = ' '.join(self.__pyang_command)
94+
pyang_res['validation_time'] = td
9095
restore_statements()
9196
del self.__ctx
9297
return pyang_res

yangvalidator/v2/views.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import random
2727
import shutil
2828
import string
29+
30+
from time import perf_counter
2931
import typing as t
3032
from zipfile import ZipFile
3133

@@ -41,6 +43,7 @@
4143
from yangvalidator.v2.xymParser import XymParser
4244
from yangvalidator.v2.yangdumpProParser import YangdumpProParser
4345
from yangvalidator.v2.yanglintParser import YanglintParser
46+
from yangvalidator.v2.lyvParser import LyvParser
4447

4548
logger = logging.getLogger(__name__)
4649

@@ -70,6 +73,7 @@ def validate(request: WSGIRequest, xym_result=None, json_body=None):
7073
Validate yang module using 4 different validators. Yanglint, Pyang, Confdc, Yumadump-pro.
7174
Check if they are valid modules according to these validators and if not return problems that
7275
occurred while validating by each parser.
76+
7377
:param json_body: json body sent from other function
7478
:param request: request sent from user
7579
:return: HTTP response with validated yang modules
@@ -159,8 +163,13 @@ def validate(request: WSGIRequest, xym_result=None, json_body=None):
159163
(ConfdParser, 'confd'),
160164
(YanglintParser, 'yanglint'),
161165
(YangdumpProParser, 'yangdump-pro'),
166+
(LyvParser, 'lighty-yang-validator'),
162167
):
168+
t0 = perf_counter()
163169
parser_results = parser([working_dir], module_to_validate, working_dir).parse_module()
170+
t1 = perf_counter()
171+
parser_results['total_time'] = round((t1-t0)*1000, 2)
172+
parser_results['validation_time'] = round(parser_results['validation_time']*1000, 2)
164173
results[module_to_validate][name] = parser_results
165174
except Exception as e:
166175
results['error'] = f'Failed to parse a document - {e}'
@@ -449,6 +458,7 @@ def versions(request: WSGIRequest):
449458
'xym-version': XymParser.VERSION,
450459
'yangdump-version': YangdumpProParser.VERSION,
451460
'yanglint-version': YanglintParser.VERSION,
461+
'lighty-yang-validator-version': LyvParser.VERSION,
452462
},
453463
)
454464

@@ -465,6 +475,7 @@ def swagger(request: WSGIRequest):
465475
def try_validate_and_load_data(request: WSGIRequest):
466476
"""
467477
Check if request is POST and try to parse byte string to json format
478+
468479
:param request: request sent from user
469480
:return: Parsed json string
470481
"""
@@ -476,6 +487,7 @@ def try_validate_and_load_data(request: WSGIRequest):
476487
def create_random_suffix() -> str:
477488
"""
478489
Create random suffix to create new temp directory
490+
479491
:return: suffix of random 8 letters
480492
"""
481493
letters = string.ascii_letters

yangvalidator/v2/xymParser.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import sys
2222
from datetime import datetime, timezone
2323
from io import StringIO
24+
from time import perf_counter
2425

2526
from xym import __version__ as xym_version
2627
from xym import xym
@@ -44,6 +45,7 @@ def __init__(self, source, working_directory):
4445
self.__source = source
4546

4647
def parse_and_extract(self):
48+
t0 = perf_counter()
4749
extracted_models = xym.xym(
4850
source_id=self.__source,
4951
dstdir=self.__working_directory,
@@ -53,6 +55,8 @@ def parse_and_extract(self):
5355
debug_level=0,
5456
force_revision_regexp=True,
5557
)
58+
td = perf_counter()-t0
59+
5660
xym_res = {'time': datetime.now(timezone.utc).isoformat()}
5761
sys.stderr = self.__stderr_
5862
sys.stdout = self.__stdout_
@@ -65,4 +69,5 @@ def parse_and_extract(self):
6569
f'xym.xym(source_id="{self.__source}", dstdir="{self.__working_directory}", srcdir="", '
6670
'strict=True, strict_examples=False, debug_level=0, force_revision_regexp=True)'
6771
)
72+
xym_res['validation_time'] = td
6873
return extracted_models, xym_res

yangvalidator/v2/yangdumpProParser.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import logging
2121
import os
22+
from time import perf_counter
2223
import typing as t
2324
from datetime import datetime, timezone
2425
from subprocess import CalledProcessError, call, check_output
@@ -60,8 +61,10 @@ def parse_module(self):
6061
yangdump_res: t.Dict[str, t.Union[str, int]] = {'time': datetime.now(timezone.utc).isoformat()}
6162
ypoutfp = open(self.__yangdump_outfile, 'w+')
6263
ypresfp = open(self.__yangdump_resfile, 'w+')
63-
64+
65+
t0 = perf_counter()
6466
status = call(self.__yangdump_command, stdout=ypoutfp, stderr=ypresfp)
67+
td = perf_counter()-t0
6568

6669
yangdump_output = yangdump_stderr = ''
6770
if os.path.isfile(self.__yangdump_outfile):
@@ -85,4 +88,5 @@ def parse_module(self):
8588
yangdump_res['version'] = self.VERSION
8689
yangdump_res['code'] = status
8790
yangdump_res['command'] = ' '.join(self.__yangdump_command)
91+
yangdump_res['validation_time'] = td
8892
return yangdump_res

yangvalidator/v2/yanglintParser.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import logging
2121
import os
22+
from time import perf_counter
2223
import typing as t
2324
from datetime import datetime, timezone
2425
from subprocess import CalledProcessError, call, check_output
@@ -51,7 +52,11 @@ def parse_module(self):
5152

5253
yresfp = open(self.__yanglint_resfile, 'w+')
5354
outfp = open(self.__yanglint_outfile, 'w+')
55+
56+
t0 = perf_counter()
5457
status = call(self.__yanglint_cmd, stdout=outfp, stderr=yresfp)
58+
td = perf_counter()-t0
59+
5560
self.LOG.info(f'Starting to yanglint parse use command {" ".join(self.__yanglint_cmd)}')
5661
yanglint_output = yanglint_stderr = ''
5762
if os.path.isfile(self.__yanglint_outfile):
@@ -74,5 +79,6 @@ def parse_module(self):
7479
yanglint_res['version'] = self.VERSION
7580
yanglint_res['code'] = status
7681
yanglint_res['command'] = ' '.join(self.__yanglint_cmd)
82+
yanglint_res['validation_time'] = td
7783

7884
return yanglint_res

0 commit comments

Comments
 (0)