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.netconf.test.tool;
10 import static com.google.common.base.Preconditions.checkArgument;
12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.lang.reflect.Field;
15 import java.util.Arrays;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.StringJoiner;
19 import java.util.concurrent.TimeUnit;
20 import net.sourceforge.argparse4j.ArgumentParsers;
21 import net.sourceforge.argparse4j.annotation.Arg;
22 import net.sourceforge.argparse4j.inf.ArgumentParser;
23 import net.sourceforge.argparse4j.inf.ArgumentParserException;
24 import org.opendaylight.yangtools.yang.common.YangConstants;
26 @SuppressFBWarnings({"DM_EXIT"})
27 public final class TesttoolParameters {
31 @Arg(dest = "thread-amount")
32 public int threadAmount;
33 @Arg(dest = "throttle")
35 @Arg(dest = "controller-auth-username")
36 public String controllerAuthUsername;
37 @Arg(dest = "controller-auth-password")
38 public String controllerAuthPassword;
39 @Arg(dest = "controller-ip")
40 public String controllerIp;
41 @Arg(dest = "controller-port")
42 public Integer controllerPort;
43 @Arg(dest = "schemas-dir")
44 public File schemasDir;
45 @Arg(dest = "devices-count")
46 public int deviceCount;
47 @Arg(dest = "devices-per-port")
48 public int devicesPerPort;
49 @Arg(dest = "starting-port")
50 public int startingPort;
51 @Arg(dest = "generate-config-connection-timeout")
52 public int generateConfigsTimeout;
53 @Arg(dest = "generate-config-address")
54 public String generateConfigsAddress;
55 @Arg(dest = "distro-folder")
56 public File distroFolder;
57 @Arg(dest = "generate-configs-batch-size")
58 public int generateConfigBatchSize;
62 public boolean exi = true;
65 @Arg(dest = "notification-file")
66 public File notificationFile;
69 @Arg(dest = "initial-config-xml-file")
70 public File initialConfigXMLFile;
71 @Arg(dest = "time-out")
75 @Arg(dest = "thread-pool-size")
76 public int threadPoolSize;
77 @Arg(dest = "rpc-config")
78 public File rpcConfig;
80 @SuppressWarnings("checkstyle:lineLength")
81 static ArgumentParser getParser() {
82 final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf testtool");
84 parser.description("netconf testtool");
86 parser.addArgument("--edit-content")
88 .dest("edit-content");
90 parser.addArgument("--async-requests")
92 .setDefault(Boolean.FALSE)
95 parser.addArgument("--thread-amount")
98 .dest("thread-amount")
99 .help("The number of threads to use for configuring devices.");
101 parser.addArgument("--throttle")
104 .help("Maximum amount of async requests that can be open at a time, "
105 + "with mutltiple threads this gets divided among all threads")
108 parser.addArgument("--controller-auth-username")
111 .help("Username for HTTP basic authentication to destination controller.")
112 .dest("controller-auth-username");
114 parser.addArgument("--controller-auth-password")
117 .help("Password for HTTP basic authentication to destination controller.")
118 .dest("controller-auth-password");
120 parser.addArgument("--controller-ip")
122 .setDefault("127.0.0.1")
123 .help("Ip of controller if available it will be used for spawning netconf connectors via topology"
124 + " configuration as a part of"
125 + " URI(http://<controller-ip>:<controller-port>/rests/data/...)"
126 + " otherwise it will just start simulated devices and skip the execution of PATCH requests")
127 .dest("controller-ip");
129 parser.addArgument("--controller-port")
132 .help("Port of controller if available it will be used for spawning netconf connectors via topology "
133 + "configuration as a part of"
134 + " URI(http://<controller-ip>:<controller-port>/rests/data/...) "
135 + "otherwise it will just start simulated devices and skip the execution of PATCH requests")
136 .dest("controller-port");
138 parser.addArgument("--device-count")
141 .help("Number of simulated netconf devices to spin. This is the number of actual ports open for the devices.")
142 .dest("devices-count");
144 parser.addArgument("--devices-per-port")
147 .help("Amount of config files generated per port to spoof more devices than are actually running")
148 .dest("devices-per-port");
150 parser.addArgument("--schemas-dir")
152 .help("Directory containing yang schemas to describe simulated devices. Some schemas e.g. netconf monitoring and inet types are included by default")
153 .dest("schemas-dir");
155 parser.addArgument("--notification-file")
157 .help("Xml file containing notifications that should be sent to clients after create subscription is called")
158 .dest("notification-file");
160 parser.addArgument("--initial-config-xml-file")
162 .help("Xml file containing initial simulatted configuration to be returned via get-config rpc")
163 .dest("initial-config-xml-file");
165 parser.addArgument("--starting-port")
168 .help("First port for simulated device. Each other device will have previous+1 port number")
169 .dest("starting-port");
171 parser.addArgument("--generate-config-connection-timeout")
173 .setDefault((int) TimeUnit.MINUTES.toMillis(30))
174 .help("Timeout to be generated in initial config files")
175 .dest("generate-config-connection-timeout");
177 parser.addArgument("--generate-config-address")
179 .setDefault("127.0.0.1")
180 .help("Address to be placed in generated configs")
181 .dest("generate-config-address");
183 parser.addArgument("--generate-configs-batch-size")
186 .help("Number of connector configs per generated file")
187 .dest("generate-configs-batch-size");
189 parser.addArgument("--distribution-folder")
191 .help("Directory where the karaf distribution for controller is located")
192 .dest("distro-folder");
194 parser.addArgument("--ssh")
196 .setDefault(Boolean.TRUE)
197 .help("Whether to use ssh for transport or just pure tcp")
200 parser.addArgument("--exi")
202 .setDefault(Boolean.TRUE)
203 .help("Whether to use exi to transport xml content")
206 parser.addArgument("--debug")
208 .setDefault(Boolean.FALSE)
209 .help("Whether to use debug log level instead of INFO")
212 parser.addArgument("--md-sal")
214 .setDefault(Boolean.FALSE)
215 .help("Whether to use md-sal datastore instead of default simulated datastore.")
218 parser.addArgument("--time-out")
221 .help("the maximum time in seconds for executing each PATCH request")
224 parser.addArgument("--ip")
226 .setDefault("0.0.0.0")
227 .help("Ip address which will be used for creating a socket address."
228 + "It can either be a machine name, such as "
229 + "java.sun.com, or a textual representation of its IP address.")
232 parser.addArgument("--thread-pool-size")
235 .help("The number of threads to keep in the pool, when creating a device simulator. Even if they are idle.")
236 .dest("thread-pool-size");
237 parser.addArgument("--rpc-config")
239 .help("Rpc config file. It can be used to define custom rpc behavior, or override the default one."
240 + "Usable for testing buggy device behavior.")
246 static TesttoolParameters parseArgs(final String[] args, final ArgumentParser parser) {
247 final TesttoolParameters opt = new TesttoolParameters();
249 parser.parseArgs(args, opt);
251 } catch (final ArgumentParserException e) {
252 parser.handleError(e);
259 @SuppressWarnings("checkstyle:regexpSinglelineJava")
261 if (controllerIp != null) {
262 //FIXME Ip validation
263 checkArgument(controllerPort != null, "Controller port is missing");
264 //FIXME Is there specific bound
265 checkArgument(controllerPort >= 0, "Controller port should be non-negative integer");
266 checkArgument(controllerPort < 65354, "Controller port should be less than 65354");
268 checkArgument(controllerPort == null, "Controller ip is missing");
271 checkArgument(deviceCount > 0, "Device count has to be > 0");
272 checkArgument(startingPort > 1023, "Starting port has to be > 1023");
273 checkArgument(devicesPerPort > 0, "At least one device per port needed");
275 if (schemasDir != null) {
276 checkArgument(schemasDir.exists(), "Schemas dir has to exist");
277 checkArgument(schemasDir.isDirectory(), "Schemas dir has to be a directory");
278 checkArgument(schemasDir.canRead(), "Schemas dir has to be readable");
280 final File[] filesArray = schemasDir.listFiles();
281 final List<File> files = filesArray != null ? Arrays.asList(filesArray) : Collections.emptyList();
282 for (final File file : files) {
283 checkArgument(file.canRead(), "Files in schemas dir has to be readable");
284 checkArgument(file.getName().endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION),
285 "Files in schemas dir has to be YANG files");
288 if (rpcConfig != null) {
289 checkArgument(rpcConfig.exists(), "Rpc config file has to exist");
290 checkArgument(!rpcConfig.isDirectory(), "Rpc config file can't be a directory");
291 checkArgument(rpcConfig.canRead(), "Rpc config file to be readable");
296 public String toString() {
297 final List<Field> fields = Arrays.asList(this.getClass().getDeclaredFields());
298 final StringJoiner joiner = new StringJoiner(", \n", "TesttoolParameters{", "}\n");
300 .filter(field -> field.getAnnotation(Arg.class) != null)
301 .map(this::getFieldString)
302 .forEach(joiner::add);
303 return joiner.toString();
306 private String getFieldString(final Field field) {
308 return field.getName() + "='" + field.get(this) + "'";
309 } catch (final IllegalAccessException e) {
310 return field.getName() + "= UNKNOWN";