2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.validator;
10 import ch.qos.logback.classic.Level;
11 import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
12 import ch.qos.logback.classic.spi.ILoggingEvent;
13 import ch.qos.logback.core.FileAppender;
14 import com.google.common.base.Stopwatch;
15 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.HashSet;
19 import java.util.List;
21 import net.sourceforge.argparse4j.ArgumentParsers;
22 import net.sourceforge.argparse4j.impl.Arguments;
23 import net.sourceforge.argparse4j.inf.ArgumentParser;
24 import net.sourceforge.argparse4j.inf.Namespace;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
31 * Main class of Yang parser system test.
34 * Type yang-system-test -h for usage.
36 @SuppressWarnings({"checkstyle:LoggerMustBeSlf4j", "checkstyle:LoggerFactoryClassParameter"})
37 public final class Main {
38 private static final Logger LOG = LoggerFactory.getLogger(Main.class);
39 private static final ch.qos.logback.classic.Logger LOG_ROOT =
40 (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
41 private static final int MB = 1024 * 1024;
43 private static final String YANG_MODEL = "yang-model";
44 private static final String PATH = "path";
45 private static final String RECURSIVE = "recursive";
46 private static final String LIST_WARNING_OFF = "no-warning-for-unkeyed-lists";
47 private static final String OUTPUT = "output";
48 private static final String MODULE_NAME = "module-name";
49 private static final String FEATURES = "features";
50 private static final String DEBUG = "debug";
51 private static final String QUIET = "quiet";
52 private static final String VERBOSE = "verbose";
58 public static void main(final String[] args) {
59 final ArgumentParser parser = getParser();
60 final Namespace arguments = parser.parseArgsOrFail(args);
62 final String outputValues = arguments.get(OUTPUT);
63 if (outputValues != null) {
64 setOutput(outputValues);
67 LOG_ROOT.setLevel(Level.WARN);
68 if (arguments.getBoolean(DEBUG)) {
69 LOG_ROOT.setLevel(Level.DEBUG);
70 } else if (arguments.getBoolean(VERBOSE)) {
71 LOG_ROOT.setLevel(Level.INFO);
72 } else if (arguments.getBoolean(QUIET)) {
73 LOG_ROOT.detachAndStopAllAppenders();
76 final List<String> yangLibDirs = initYangDirsPath(arguments);
77 final List<String> yangFiles = new ArrayList<>();
78 final List<String> moduleNameValues = arguments.get(MODULE_NAME);
79 if (moduleNameValues != null) {
80 yangFiles.addAll(moduleNameValues);
82 if (arguments.get(YANG_MODEL) != null) {
83 yangFiles.addAll(arguments.get(YANG_MODEL));
86 final Set<QName> supportedFeatures = initSupportedFeatures(arguments);
88 runSystemTest(yangLibDirs, yangFiles, supportedFeatures, arguments.getBoolean(RECURSIVE),
89 !arguments.getBoolean(LIST_WARNING_OFF));
91 LOG_ROOT.getLoggerContext().reset();
94 private static void setOutput(final String... paths) {
95 LOG_ROOT.getLoggerContext().reset();
97 final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
98 encoder.setPattern("%date %level [%thread] [%file:%line] %msg%n");
99 encoder.setContext(LOG_ROOT.getLoggerContext());
102 for (final String path : paths) {
103 // create FileAppender
104 final FileAppender<ILoggingEvent> logfileOut = new FileAppender<>();
105 logfileOut.setAppend(false);
106 logfileOut.setFile(path);
107 logfileOut.setContext(LOG_ROOT.getLoggerContext());
108 logfileOut.setEncoder(encoder);
111 // attach the rolling file appender to the root logger
112 LOG_ROOT.addAppender(logfileOut);
116 @SuppressFBWarnings({ "DM_EXIT", "DM_GC" })
117 @SuppressWarnings("checkstyle:illegalCatch")
118 private static void runSystemTest(final List<String> yangLibDirs, final List<String> yangFiles,
119 final Set<QName> supportedFeatures, final boolean recursiveSearch, final boolean warnForUnkeyedLists) {
120 LOG.info("Yang model dirs: {} ", yangLibDirs);
121 LOG.info("Yang model files: {} ", yangFiles);
122 LOG.info("Supported features: {} ", supportedFeatures);
124 EffectiveModelContext context = null;
126 printMemoryInfo("start");
127 final Stopwatch stopWatch = Stopwatch.createStarted();
130 context = SystemTestUtils.parseYangSources(yangLibDirs, yangFiles, supportedFeatures,
131 recursiveSearch, warnForUnkeyedLists);
132 } catch (final Exception e) {
133 LOG.error("Failed to create SchemaContext.", e);
138 LOG.info("Elapsed time: {}", stopWatch);
139 printMemoryInfo("end");
140 LOG.info("SchemaContext resolved Successfully. {}", context);
141 Runtime.getRuntime().gc();
142 printMemoryInfo("after gc");
145 private static List<String> initYangDirsPath(final Namespace arguments) {
146 final List<String> yangDirs = new ArrayList<>();
147 final List<String> path = arguments.get(PATH);
149 yangDirs.addAll(path);
154 private static Set<QName> initSupportedFeatures(final Namespace arguments) {
155 final Set<QName> supportedFeatures = new HashSet<>();
156 final List<String> features = arguments.get(FEATURES);
157 if (features != null) {
158 supportedFeatures.addAll(createQNames(features.toArray(new String[0])));
160 return supportedFeatures;
163 private static Collection<? extends QName> createQNames(final String[] featuresArg) {
164 final Set<QName> qnames = new HashSet<>();
165 for (final String featureStr : featuresArg) {
166 qnames.add(QName.create(featureStr));
171 private static ArgumentParser getParser() {
172 final var parser = ArgumentParsers.newFor("yang-system-test").addHelp(true).build()
173 .description("Main class of Yang parser system test.");
174 parser.addArgument(YANG_MODEL)
175 .help("yang file(s) to validate")
180 parser.addArgument("-p", "--path")
181 .help("path is a space separated list of directories to search for yang modules")
185 parser.addArgument("-r", "--recursive")
186 .help("recursive search of directories specified by -p option")
188 .action(Arguments.storeTrue());
189 parser.addArgument("-K", "--no-warning-for-unkeyed-lists")
190 .help("do not add warnings about unkeyed lists with config true")
191 .dest(LIST_WARNING_OFF)
192 .action(Arguments.storeTrue());
193 parser.addArgument("-o", "--output")
194 .help("path to output file for logs. Output file will be overwritten")
196 parser.addArgument("-m", "--module-name")
197 .help("validate yang model by module name.")
201 parser.addArgument("-f", "--features").help(
203 features is a space separated list strings in the form [($namespace?revision=$revision)$local_name].
204 This option is used to prune the data model by removing all nodes that are defined with a "if-feature".
209 final var group = parser.addMutuallyExclusiveGroup("logging")
210 .description("exclusive group for logging parameters");
211 group.addArgument("-d", "--debug")
212 .help("add debug output")
214 .action(Arguments.storeTrue());
215 group.addArgument("-q", "--quiet")
216 .help("completely suppress output")
218 .action(Arguments.storeTrue());
219 group.addArgument("-v", "--verbose")
220 .help("shows details about the results of test running")
222 .action(Arguments.storeTrue());
226 private static void printMemoryInfo(final String info) {
227 LOG.info("Memory INFO [{}]: free {}MB, used {}MB, total {}MB, max {}MB", info,
228 Runtime.getRuntime().freeMemory() / MB,
229 (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / MB,
230 Runtime.getRuntime().totalMemory() / MB, Runtime.getRuntime().maxMemory() / MB);