26d5ffa3061a23612d22bfad3c9517f7b4f4daa4
[yangtools.git] / tools / yang-model-validator / src / main / java / org / opendaylight / yangtools / yang / validator / Main.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.validator;
9
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;
20 import java.util.Set;
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;
29
30 /**
31  * Main class of Yang parser system test.
32  *
33  * <p>
34  * Type yang-system-test -h for usage.
35  */
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;
42
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";
53
54     private Main() {
55         // Hidden on purpose
56     }
57
58     public static void main(final String[] args) {
59         final ArgumentParser parser = getParser();
60         final Namespace arguments = parser.parseArgsOrFail(args);
61
62         final String outputValues = arguments.get(OUTPUT);
63         if (outputValues != null) {
64             setOutput(outputValues);
65         }
66
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();
74         }
75
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);
81         }
82         if (arguments.get(YANG_MODEL) != null) {
83             yangFiles.addAll(arguments.get(YANG_MODEL));
84         }
85
86         final Set<QName> supportedFeatures = initSupportedFeatures(arguments);
87
88         runSystemTest(yangLibDirs, yangFiles, supportedFeatures, arguments.getBoolean(RECURSIVE),
89                 !arguments.getBoolean(LIST_WARNING_OFF));
90
91         LOG_ROOT.getLoggerContext().reset();
92     }
93
94     private static void setOutput(final String... paths) {
95         LOG_ROOT.getLoggerContext().reset();
96
97         final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
98         encoder.setPattern("%date %level [%thread] [%file:%line] %msg%n");
99         encoder.setContext(LOG_ROOT.getLoggerContext());
100         encoder.start();
101
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);
109             logfileOut.start();
110
111             // attach the rolling file appender to the root logger
112             LOG_ROOT.addAppender(logfileOut);
113         }
114     }
115
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);
123
124         EffectiveModelContext context = null;
125
126         printMemoryInfo("start");
127         final Stopwatch stopWatch = Stopwatch.createStarted();
128
129         try {
130             context = SystemTestUtils.parseYangSources(yangLibDirs, yangFiles, supportedFeatures,
131                     recursiveSearch, warnForUnkeyedLists);
132         } catch (final Exception e) {
133             LOG.error("Failed to create SchemaContext.", e);
134             System.exit(1);
135         }
136
137         stopWatch.stop();
138         LOG.info("Elapsed time: {}", stopWatch);
139         printMemoryInfo("end");
140         LOG.info("SchemaContext resolved Successfully. {}", context);
141         Runtime.getRuntime().gc();
142         printMemoryInfo("after gc");
143     }
144
145     private static List<String> initYangDirsPath(final Namespace arguments) {
146         final List<String> yangDirs = new ArrayList<>();
147         final List<String> path = arguments.get(PATH);
148         if (path != null) {
149             yangDirs.addAll(path);
150         }
151         return yangDirs;
152     }
153
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])));
159         }
160         return supportedFeatures;
161     }
162
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));
167         }
168         return qnames;
169     }
170
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")
176                 .dest(YANG_MODEL)
177                 .nargs("*")
178                 .metavar(YANG_MODEL)
179                 .type(String.class);
180         parser.addArgument("-p", "--path")
181                 .help("path is a space separated list of directories to search for yang modules")
182                 .dest(PATH)
183                 .nargs("+")
184                 .type(String.class);
185         parser.addArgument("-r", "--recursive")
186                 .help("recursive search of directories specified by -p option")
187                 .dest(RECURSIVE)
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")
195                 .dest(OUTPUT);
196         parser.addArgument("-m", "--module-name")
197                 .help("validate yang model by module name.")
198                 .dest(MODULE_NAME)
199                 .nargs("+")
200                 .type(String.class);
201         parser.addArgument("-f", "--features").help(
202                 """
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".
205                 """)
206                 .dest(FEATURES)
207                 .nargs("+")
208                 .type(String.class);
209         final var group = parser.addMutuallyExclusiveGroup("logging")
210                 .description("exclusive group for logging parameters");
211         group.addArgument("-d", "--debug")
212                 .help("add debug output")
213                 .dest(DEBUG)
214                 .action(Arguments.storeTrue());
215         group.addArgument("-q", "--quiet")
216                 .help("completely suppress output")
217                 .dest(QUIET)
218                 .action(Arguments.storeTrue());
219         group.addArgument("-v", "--verbose")
220                 .help("shows details about the results of test running")
221                 .dest(VERBOSE)
222                 .action(Arguments.storeTrue());
223         return parser;
224     }
225
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);
231     }
232 }