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 .help("Ip of controller if available it will be used for spawning netconf connectors via topology"
123 + " configuration as a part of"
124 + " URI(http://<controller-ip>:<controller-port>/rests/data/...)"
125 + " otherwise it will just start simulated devices and skip the execution of PATCH requests")
126 .dest("controller-ip");
128 parser.addArgument("--controller-port")
130 .help("Port of controller if available it will be used for spawning netconf connectors via topology "
131 + "configuration as a part of"
132 + " URI(http://<controller-ip>:<controller-port>/rests/data/...) "
133 + "otherwise it will just start simulated devices and skip the execution of PATCH requests")
134 .dest("controller-port");
136 parser.addArgument("--device-count")
139 .help("Number of simulated netconf devices to spin. This is the number of actual ports open for the devices.")
140 .dest("devices-count");
142 parser.addArgument("--devices-per-port")
145 .help("Amount of config files generated per port to spoof more devices than are actually running")
146 .dest("devices-per-port");
148 parser.addArgument("--schemas-dir")
150 .help("Directory containing yang schemas to describe simulated devices. Some schemas e.g. netconf monitoring and inet types are included by default")
151 .dest("schemas-dir");
153 parser.addArgument("--notification-file")
155 .help("Xml file containing notifications that should be sent to clients after create subscription is called")
156 .dest("notification-file");
158 parser.addArgument("--initial-config-xml-file")
160 .help("Xml file containing initial simulatted configuration to be returned via get-config rpc")
161 .dest("initial-config-xml-file");
163 parser.addArgument("--starting-port")
166 .help("First port for simulated device. Each other device will have previous+1 port number")
167 .dest("starting-port");
169 parser.addArgument("--generate-config-connection-timeout")
171 .setDefault((int) TimeUnit.MINUTES.toMillis(30))
172 .help("Timeout to be generated in initial config files")
173 .dest("generate-config-connection-timeout");
175 parser.addArgument("--generate-config-address")
177 .setDefault("127.0.0.1")
178 .help("Address to be placed in generated configs")
179 .dest("generate-config-address");
181 parser.addArgument("--generate-configs-batch-size")
184 .help("Number of connector configs per generated file")
185 .dest("generate-configs-batch-size");
187 parser.addArgument("--distribution-folder")
189 .help("Directory where the karaf distribution for controller is located")
190 .dest("distro-folder");
192 parser.addArgument("--ssh")
194 .setDefault(Boolean.TRUE)
195 .help("Whether to use ssh for transport or just pure tcp")
198 parser.addArgument("--exi")
200 .setDefault(Boolean.TRUE)
201 .help("Whether to use exi to transport xml content")
204 parser.addArgument("--debug")
206 .setDefault(Boolean.FALSE)
207 .help("Whether to use debug log level instead of INFO")
210 parser.addArgument("--md-sal")
212 .setDefault(Boolean.FALSE)
213 .help("Whether to use md-sal datastore instead of default simulated datastore.")
216 parser.addArgument("--time-out")
219 .help("the maximum time in seconds for executing each PATCH request")
222 parser.addArgument("--ip")
224 .setDefault("0.0.0.0")
225 .help("Ip address which will be used for creating a socket address."
226 + "It can either be a machine name, such as "
227 + "java.sun.com, or a textual representation of its IP address.")
230 parser.addArgument("--thread-pool-size")
233 .help("The number of threads to keep in the pool, when creating a device simulator. Even if they are idle.")
234 .dest("thread-pool-size");
235 parser.addArgument("--rpc-config")
237 .help("Rpc config file. It can be used to define custom rpc behavior, or override the default one."
238 + "Usable for testing buggy device behavior.")
244 static TesttoolParameters parseArgs(final String[] args, final ArgumentParser parser) {
245 final TesttoolParameters opt = new TesttoolParameters();
247 parser.parseArgs(args, opt);
249 } catch (final ArgumentParserException e) {
250 parser.handleError(e);
257 @SuppressWarnings("checkstyle:regexpSinglelineJava")
259 if (controllerIp != null) {
260 //FIXME Ip validation
261 checkArgument(controllerPort != null, "Controller port is missing");
262 //FIXME Is there specific bound
263 checkArgument(controllerPort >= 0, "Controller port should be non-negative integer");
264 checkArgument(controllerPort < 65354, "Controller port should be less than 65354");
266 checkArgument(controllerPort == null, "Controller ip is missing");
269 checkArgument(deviceCount > 0, "Device count has to be > 0");
270 checkArgument(startingPort > 1023, "Starting port has to be > 1023");
271 checkArgument(devicesPerPort > 0, "At least one device per port needed");
273 if (schemasDir != null) {
274 checkArgument(schemasDir.exists(), "Schemas dir has to exist");
275 checkArgument(schemasDir.isDirectory(), "Schemas dir has to be a directory");
276 checkArgument(schemasDir.canRead(), "Schemas dir has to be readable");
278 final File[] filesArray = schemasDir.listFiles();
279 final List<File> files = filesArray != null ? Arrays.asList(filesArray) : Collections.emptyList();
280 for (final File file : files) {
281 checkArgument(file.canRead(), "Files in schemas dir has to be readable");
282 checkArgument(file.getName().endsWith(YangConstants.RFC6020_YANG_FILE_EXTENSION),
283 "Files in schemas dir has to be YANG files");
286 if (rpcConfig != null) {
287 checkArgument(rpcConfig.exists(), "Rpc config file has to exist");
288 checkArgument(!rpcConfig.isDirectory(), "Rpc config file can't be a directory");
289 checkArgument(rpcConfig.canRead(), "Rpc config file to be readable");
294 public String toString() {
295 final List<Field> fields = Arrays.asList(this.getClass().getDeclaredFields());
296 final StringJoiner joiner = new StringJoiner(", \n", "TesttoolParameters{", "}\n");
298 .filter(field -> field.getAnnotation(Arg.class) != null)
299 .map(this::getFieldString)
300 .forEach(joiner::add);
301 return joiner.toString();
304 private String getFieldString(final Field field) {
306 return field.getName() + "='" + field.get(this) + "'";
307 } catch (final IllegalAccessException e) {
308 return field.getName() + "= UNKNOWN";