<controller.mdsal.version>1.5.0-SNAPSHOT</controller.mdsal.version>
<mdsal.model.version>0.10.0-SNAPSHOT</mdsal.model.version>
<yangtools.version>1.1.0-SNAPSHOT</yangtools.version>
+ <argparse4j.version>0.7.0</argparse4j.version>
</properties>
<dependencyManagement>
<scope>import</scope>
<type>pom</type>
</dependency>
+ <dependency>
+ <groupId>net.sourceforge.argparse4j</groupId>
+ <artifactId>argparse4j</artifactId>
+ <version>${argparse4j.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
+ <dependency>
+ <groupId>net.sourceforge.argparse4j</groupId>
+ <artifactId>argparse4j</artifactId>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.openflowjava.protocol.impl.clients;
+
+import java.net.InetAddress;
+import java.util.concurrent.Callable;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.SettableFuture;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Callable client class, inspired by SimpleClient class
+ * Simulating device/switch connected to controller
+ * @author Jozef Bacigal
+ * Date: 4.3.2016.
+ */
+public class CallableClient implements Callable<Boolean>, OFClient {
+
+ private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(CallableClient.class);
+
+ private int port = 6653;
+ private boolean securedClient = false;
+ private InetAddress ipAddress = null;
+ private String name = "Empty name";
+
+ private EventLoopGroup workerGroup;
+ private SettableFuture<Boolean> isOnlineFuture;
+ private SettableFuture<Boolean> scenarioDone;
+ private ScenarioHandler scenarioHandler = null;
+ private Bootstrap bootstrap = null;
+
+ public CallableClient(
+ final int port,
+ final boolean securedClient,
+ final InetAddress ipAddress,
+ final String name,
+ final ScenarioHandler scenarioHandler,
+ final Bootstrap bootstrap,
+ final EventLoopGroup eventExecutors) {
+
+ Preconditions.checkNotNull(ipAddress, "IP address cannot be null");
+ Preconditions.checkNotNull(scenarioHandler, "Scenario handler cannot be null");
+ this.port = port;
+ this.securedClient = securedClient;
+ this.ipAddress = ipAddress;
+ this.workerGroup = eventExecutors;
+ this.bootstrap = bootstrap;
+ this.name = name;
+ this.scenarioHandler = scenarioHandler;
+ }
+
+ @Override
+ public SettableFuture<Boolean> getIsOnlineFuture() {
+ return isOnlineFuture;
+ }
+
+ @Override
+ public SettableFuture<Boolean> getScenarioDone() {
+ return scenarioDone;
+ }
+
+ @Override
+ public void setScenarioHandler(final ScenarioHandler scenario) {
+ this.scenarioHandler = scenario;
+ }
+
+ @Override
+ public void setSecuredClient(final boolean securedClient) {
+ this.securedClient = securedClient;
+ }
+
+
+ @Override
+ public Boolean call() throws Exception {
+ Preconditions.checkNotNull(bootstrap);
+ Preconditions.checkNotNull(workerGroup);
+ LOG.info("Switch {} trying connect to controller", this.name);
+ SimpleClientInitializer clientInitializer = new SimpleClientInitializer(isOnlineFuture, securedClient);
+ clientInitializer.setScenario(scenarioHandler);
+ try {
+ bootstrap.group(workerGroup)
+ .channel(NioSocketChannel.class)
+ .handler(clientInitializer);
+
+ bootstrap.connect(ipAddress, port).sync();
+ synchronized (scenarioHandler) {
+ LOG.debug("WAITING FOR SCENARIO");
+ while (!scenarioHandler.isScenarioFinished()) {
+ scenarioHandler.wait();
+ }
+ }
+ } catch (Exception ex) {
+ LOG.error(ex.getMessage(), ex);
+ return false;
+ }
+ if (scenarioHandler.isFinishedOK()) {
+ LOG.info("Device {} finished scenario OK", this.name);
+ } else {
+ LOG.error("Device {} finished scenario with error", this.name);
+ }
+ return scenarioHandler.isFinishedOK();
+
+ }
+
+ @Override
+ public void run() {
+ throw new UnsupportedOperationException();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.openflowjava.protocol.impl.clients;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import javax.annotation.Nullable;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import net.sourceforge.argparse4j.ArgumentParsers;
+import net.sourceforge.argparse4j.annotation.Arg;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import net.sourceforge.argparse4j.inf.ArgumentParserException;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ControllerConnectionTestTool class, utilities for testing device's connect
+ * @author Jozef Bacigal
+ * Date: 4.3.2016.
+ */
+public class ControllerConnectionTestTool {
+
+ private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ControllerConnectionTestTool.class);
+
+ public static class Params {
+
+ @Arg(dest = "controller-ip")
+ public String controllerIP;
+
+ @Arg(dest = "devices-count")
+ public int deviceCount;
+
+ @Arg(dest = "ssl")
+ public boolean ssl;
+
+ @Arg(dest = "threads")
+ public int threads;
+
+ @Arg(dest = "port")
+ public int port;
+
+ @Arg(dest = "timeout")
+ public int timeout;
+
+ @Arg(dest = "freeze")
+ public int freeze;
+
+ @Arg(dest = "sleep")
+ public long sleep;
+
+ static ArgumentParser getParser() throws UnknownHostException {
+ final ArgumentParser parser = ArgumentParsers.newArgumentParser("openflowjava test-tool");
+
+ parser.description("Openflowjava switch -> controller connector simulator");
+
+ parser.addArgument("--device-count")
+ .type(Integer.class)
+ .setDefault(1)
+ .help("Number of simulated switches. Has to be more than 0")
+ .dest("devices-count");
+
+ parser.addArgument("--controller-ip")
+ .type(String.class)
+ .setDefault("127.0.0.1")
+ .help("ODL controller ip address")
+ .dest("controller-ip");
+
+ parser.addArgument("--ssl")
+ .type(Boolean.class)
+ .setDefault(false)
+ .help("Use secured connection")
+ .dest("ssl");
+
+ parser.addArgument("--threads")
+ .type(Integer.class)
+ .setDefault(1)
+ .help("Number of threads: MAX 1024")
+ .dest("threads");
+
+ parser.addArgument("--port")
+ .type(Integer.class)
+ .setDefault(6653)
+ .help("Connection port")
+ .dest("port");
+
+ parser.addArgument("--timeout")
+ .type(Integer.class)
+ .setDefault(60)
+ .help("Timeout in seconds")
+ .dest("timeout");
+
+ parser.addArgument("--scenarioTries")
+ .type(Integer.class)
+ .setDefault(3)
+ .help("Number of tries in scenario, while waiting for response")
+ .dest("freeze");
+
+ parser.addArgument("--timeBetweenScenario")
+ .type(Long.class)
+ .setDefault(100)
+ .help("Waiting time in milliseconds between tries.")
+ .dest("sleep");
+
+ return parser;
+ }
+
+ void validate() {
+ checkArgument(deviceCount > 0, "Switch count has to be > 0");
+ checkArgument(threads > 0 && threads < 1024, "Switch count has to be > 0 and < 1024");
+ }
+ }
+
+ public static void main(final String[] args) {
+
+ List<Callable<Boolean>> callableList = new ArrayList<>();
+ final EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ try {
+ final Params params = parseArgs(args, Params.getParser());
+ params.validate();
+
+ for(int loop=0;loop < params.deviceCount; loop++){
+
+ CallableClient cc = new CallableClient(
+ params.port,
+ params.ssl,
+ InetAddress.getByName(params.controllerIP),
+ "Switch no." + String.valueOf(loop),
+ new ScenarioHandler(ScenarioFactory.createHandshakeScenarioWithBarrier(), params.freeze, params.sleep),
+ new Bootstrap(),
+ workerGroup);
+
+ callableList.add(cc);
+
+ }
+
+ ExecutorService executorService = Executors.newFixedThreadPool(params.threads);
+ final ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService);
+
+ final List<ListenableFuture<Boolean>> listenableFutures = new ArrayList<>();
+ for (Callable<Boolean> booleanCallable : callableList) {
+ listenableFutures.add(listeningExecutorService.submit(booleanCallable));
+ }
+ final ListenableFuture<List<Boolean>> summaryFuture = Futures.successfulAsList(listenableFutures);
+ List<Boolean> booleanList = summaryFuture.get(params.timeout, TimeUnit.SECONDS);
+ Futures.addCallback(summaryFuture, new FutureCallback<List<Boolean>>() {
+ @Override
+ public void onSuccess(@Nullable final List<Boolean> booleanList) {
+ LOG.info("Tests finished");
+ workerGroup.shutdownGracefully();
+ LOG.info("Summary:");
+ int testsOK = 0;
+ int testFailure = 0;
+ for (Boolean aBoolean : booleanList) {
+ if (aBoolean) {
+ testsOK++;
+ } else {
+ testFailure++;
+ }
+ }
+ LOG.info("Tests OK: {}", testsOK);
+ LOG.info("Tests failure: {}", testFailure);
+ System.exit(0);
+ }
+
+ @Override
+ public void onFailure(final Throwable throwable) {
+ LOG.warn("Tests call failure");
+ workerGroup.shutdownGracefully();
+ System.exit(1);
+ }
+ });
+ } catch (Exception e) {
+ LOG.warn("Exception has been thrown: {}", e);
+ System.exit(1);
+ }
+ }
+
+ private static Params parseArgs(final String[] args, final ArgumentParser parser) throws ArgumentParserException {
+ final Params opt = new Params();
+ parser.parseArgs(args, opt);
+ return opt;
+ }
+
+
+}
return stack;
}
+ /**
+ * Creates stack with handshake needed messages. XID of messages:
+ * <ol>
+ * <li> hello sent - 00000001
+ * <li> hello waiting - 00000021
+ * <li> featuresrequest waiting - 00000002
+ * <li> featuresreply sent - 00000002
+ * </ol>
+ * @return stack filled with Handshake messages
+ */
+ public static Deque<ClientEvent> createHandshakeScenarioWithBarrier() {
+ Deque<ClientEvent> stack = new ArrayDeque<>();
+ stack.addFirst(new SendEvent(ByteBufUtils.hexStringToBytes("04 00 00 08 00 00 00 01")));
+ stack.addFirst(new WaitForMessageEvent(ByteBufUtils.hexStringToBytes("04 00 00 10 00 00 00 15 00 01 00 08 00 00 00 12"))); //Hello message 21
+ stack.addFirst(new WaitForMessageEvent(ByteBufUtils.hexStringToBytes("04 05 00 08 00 00 00 02")));
+ stack.addFirst(new SendEvent(ByteBufUtils.hexStringToBytes("04 06 00 20 00 00 00 02 "
+ + "00 01 02 03 04 05 06 07 00 01 02 03 01 00 00 00 00 01 02 03 00 01 02 03")));
+ stack.addFirst(new WaitForMessageEvent(ByteBufUtils.hexStringToBytes("04 14 00 08 00 00 00 00"))); //Barrier request
+ stack.addFirst(new SendEvent(ByteBufUtils.hexStringToBytes("04 15 00 08 00 00 00 04"))); //Barrier reply
+ return stack;
+ }
+
/**
* Creates stack with handshake needed messages. XID of messages:
* <ol>
private ChannelHandlerContext ctx;
private int eventNumber;
private boolean scenarioFinished = false;
+ private int freeze = 2;
+ private long sleepBetweenTries = 100l;
+ private boolean finishedOK = true;
/**
*
ofMsg = new LinkedBlockingQueue<>();
}
+ public ScenarioHandler(Deque<ClientEvent> scenario, int freeze, long sleepBetweenTries){
+ this.scenario = scenario;
+ ofMsg = new LinkedBlockingQueue<>();
+ this.sleepBetweenTries = sleepBetweenTries;
+ this.freeze = freeze;
+ }
+
@Override
public void run() {
int freezeCounter = 0;
event.setCtx(ctx);
}
if (peek.eventExecuted()) {
+ LOG.info("Scenario step finished OK, moving to next step.");
scenario.removeLast();
eventNumber++;
freezeCounter = 0;
+ finishedOK = true;
} else {
freezeCounter++;
}
- if (freezeCounter > 2) {
+ if (freezeCounter > freeze) {
LOG.warn("Scenario frozen: {}", freezeCounter);
+ LOG.warn("Scenario step not finished NOT OK!", freezeCounter);
+ this.finishedOK = false;
break;
}
try {
- sleep(100);
+ sleep(sleepBetweenTries);
} catch (InterruptedException e) {
LOG.error(e.getMessage(), e);
}
public boolean isScenarioFinished() {
return scenarioFinished;
}
+
+ public boolean isFinishedOK() {
+ return finishedOK;
+ }
}
* @param headerReceived header (first 8 bytes) of expected message
*/
public void setHeaderReceived(byte[] headerReceived) {
- this.headerReceived = new byte[headerReceived.length];
- for (int i = 0; i < headerReceived.length; i++) {
- this.headerReceived[i] = headerReceived[i];
+ if (headerReceived != null) {
+ this.headerReceived = new byte[headerReceived.length];
+ for (int i = 0; i < headerReceived.length; i++) {
+ this.headerReceived[i] = headerReceived[i];
+ }
}
}
}