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.Arrays;
18 import java.util.Collection;
19 import java.util.HashSet;
20 import java.util.List;
22 import org.apache.commons.cli.CommandLine;
23 import org.apache.commons.cli.CommandLineParser;
24 import org.apache.commons.cli.DefaultParser;
25 import org.apache.commons.cli.HelpFormatter;
26 import org.apache.commons.cli.Option;
27 import org.apache.commons.cli.OptionGroup;
28 import org.apache.commons.cli.Options;
29 import org.apache.commons.cli.ParseException;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
36 * Main class of Yang parser system test.
39 * yang-system-test [-f features] [-h help] [-p path] [-v verbose] yangFiles...
40 * -f,--features <arg> features is a string in the form
41 * [feature(,feature)*] and feature is a string in the form
42 * [($namespace?revision=$revision)$local_name].
43 * This option is used to prune the data model by removing
44 * all nodes that are defined with a "if-feature".
45 * -h,--help print help message and exit.
46 * -p,--path <arg> path is a colon (:) separated list of directories
47 * to search for yang modules.
48 * -r, --recursive recursive search of directories specified by -p option
49 * -v, --verbose shows details about the results of test running.
50 * -o, --output path to output file for logs. Output file will be overwritten.
51 * -m, --module-name validate yang by module name.
52 * -K, --no-warning-for-unkeyed-lists
53 * do not add warnings about unkeyed lists with config true.
55 @SuppressWarnings({"checkstyle:LoggerMustBeSlf4j", "checkstyle:LoggerFactoryClassParameter"})
56 public final class Main {
57 private static final Logger LOG = LoggerFactory.getLogger(Main.class);
58 private static final ch.qos.logback.classic.Logger LOG_ROOT =
59 (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
60 private static final int MB = 1024 * 1024;
62 private static final Option FEATURE = new Option("f", "features", true,
63 "features is a string in the form [feature(,feature)*] and feature is a string in the form "
64 + "[($namespace?revision=$revision)$local_name]. This option is used to prune the data model "
65 + "by removing all nodes that are defined with a \"if-feature\".");
67 private static final Option HELP = new Option("h", "help", false, "print help message and exit.");
68 private static final Option MODULE_NAME = new Option("m", "module-name", true,
69 "validate yang model by module name.");
70 private static final Option OUTPUT = new Option("o", "output", true,
71 "path to output file for logs. Output file will be overwritten");
72 private static final Option PATH = new Option("p", "path", true,
73 "path is a colon (:) separated list of directories to search for yang modules.");
74 private static final Option RECURSIVE = new Option("r", "recursive", false,
75 "recursive search of directories specified by -p option.");
77 private static final Option DEBUG = new Option("d", "debug", false, "add debug output");
78 private static final Option QUIET = new Option("q", "quiet", false, "completely suppress output.");
79 private static final Option VERBOSE = new Option("v", "verbose", false,
80 "shows details about the results of test running.");
81 private static final Option LIST_WARNING_OFF = new Option("K", "no-warning-for-unkeyed-lists", false,
82 "do not add warnings about unkeyed lists with config true");
88 private static Options createOptions() {
93 .addOptionGroup(new OptionGroup().addOption(DEBUG).addOption(QUIET).addOption(VERBOSE))
94 .addOption(LIST_WARNING_OFF)
96 .addOption(MODULE_NAME)
100 public static void main(final String[] args) {
101 final HelpFormatter formatter = new HelpFormatter();
102 final Options options = createOptions();
103 final CommandLine arguments = parseArguments(args, options, formatter);
105 if (arguments.hasOption(HELP.getLongOpt())) {
106 printHelp(options, formatter);
110 final String[] outputValues = arguments.getOptionValues(OUTPUT.getLongOpt());
111 if (outputValues != null) {
112 setOutput(outputValues);
115 LOG_ROOT.setLevel(Level.WARN);
116 if (arguments.hasOption(DEBUG.getLongOpt())) {
117 LOG_ROOT.setLevel(Level.DEBUG);
118 } else if (arguments.hasOption(VERBOSE.getLongOpt())) {
119 LOG_ROOT.setLevel(Level.INFO);
120 } else if (arguments.hasOption(QUIET.getLongOpt())) {
121 LOG_ROOT.detachAndStopAllAppenders();
124 final boolean warnForUnkeyedLists = !arguments.hasOption(LIST_WARNING_OFF.getLongOpt());
126 final List<String> yangLibDirs = initYangDirsPath(arguments);
127 final List<String> yangFiles = new ArrayList<>();
128 final String[] moduleNameValues = arguments.getOptionValues(MODULE_NAME.getLongOpt());
129 if (moduleNameValues != null) {
130 yangFiles.addAll(Arrays.asList(moduleNameValues));
132 yangFiles.addAll(Arrays.asList(arguments.getArgs()));
134 final Set<QName> supportedFeatures = initSupportedFeatures(arguments);
136 runSystemTest(yangLibDirs, yangFiles, supportedFeatures, arguments.hasOption(RECURSIVE.getLongOpt()),
137 warnForUnkeyedLists);
139 LOG_ROOT.getLoggerContext().reset();
142 private static void setOutput(final String... paths) {
143 LOG_ROOT.getLoggerContext().reset();
145 final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
146 encoder.setPattern("%date %level [%thread] [%file:%line] %msg%n");
147 encoder.setContext(LOG_ROOT.getLoggerContext());
150 for (final String path : paths) {
151 // create FileAppender
152 final FileAppender<ILoggingEvent> logfileOut = new FileAppender<>();
153 logfileOut.setAppend(false);
154 logfileOut.setFile(path);
155 logfileOut.setContext(LOG_ROOT.getLoggerContext());
156 logfileOut.setEncoder(encoder);
159 // attach the rolling file appender to the root logger
160 LOG_ROOT.addAppender(logfileOut);
164 @SuppressFBWarnings({ "DM_EXIT", "DM_GC" })
165 @SuppressWarnings("checkstyle:illegalCatch")
166 private static void runSystemTest(final List<String> yangLibDirs, final List<String> yangFiles,
167 final Set<QName> supportedFeatures, final boolean recursiveSearch, final boolean warnForUnkeyedLists) {
168 LOG.info("Yang model dirs: {} ", yangLibDirs);
169 LOG.info("Yang model files: {} ", yangFiles);
170 LOG.info("Supported features: {} ", supportedFeatures);
172 EffectiveModelContext context = null;
174 printMemoryInfo("start");
175 final Stopwatch stopWatch = Stopwatch.createStarted();
178 context = SystemTestUtils.parseYangSources(yangLibDirs, yangFiles, supportedFeatures,
179 recursiveSearch, warnForUnkeyedLists);
180 } catch (final Exception e) {
181 LOG.error("Failed to create SchemaContext.", e);
186 LOG.info("Elapsed time: {}", stopWatch);
187 printMemoryInfo("end");
188 LOG.info("SchemaContext resolved Successfully. {}", context);
189 Runtime.getRuntime().gc();
190 printMemoryInfo("after gc");
193 private static List<String> initYangDirsPath(final CommandLine arguments) {
194 final List<String> yangDirs = new ArrayList<>();
195 if (arguments.hasOption("path")) {
196 for (final String pathArg : arguments.getOptionValues("path")) {
197 yangDirs.addAll(Arrays.asList(pathArg.split(":")));
203 private static Set<QName> initSupportedFeatures(final CommandLine arguments) {
204 Set<QName> supportedFeatures = null;
205 if (arguments.hasOption("features")) {
206 supportedFeatures = new HashSet<>();
207 for (final String pathArg : arguments.getOptionValues("features")) {
208 supportedFeatures.addAll(createQNames(pathArg.split(",")));
211 return supportedFeatures;
214 private static Collection<? extends QName> createQNames(final String[] featuresArg) {
215 final Set<QName> qnames = new HashSet<>();
216 for (final String featureStr : featuresArg) {
217 qnames.add(QName.create(featureStr));
223 @SuppressFBWarnings("DM_EXIT")
224 private static CommandLine parseArguments(final String[] args, final Options options,
225 final HelpFormatter formatter) {
226 final CommandLineParser parser = new DefaultParser();
228 CommandLine cmd = null;
230 cmd = parser.parse(options, args);
231 } catch (final ParseException e) {
232 LOG.error("Failed to parse command line options.", e);
233 printHelp(options, formatter);
240 private static void printHelp(final Options options, final HelpFormatter formatter) {
241 formatter.printHelp("yang-system-test [OPTION...] YANG-FILE...", options);
244 private static void printMemoryInfo(final String info) {
245 LOG.info("Memory INFO [{}]: free {}MB, used {}MB, total {}MB, max {}MB", info,
246 Runtime.getRuntime().freeMemory() / MB,
247 (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / MB,
248 Runtime.getRuntime().totalMemory() / MB, Runtime.getRuntime().maxMemory() / MB);