ConnectionManager proposal 54/17054/6
authorMartin Bobak <mbobak@cisco.com>
Tue, 24 Mar 2015 13:14:07 +0000 (14:14 +0100)
committerMartin Bobak <mbobak@cisco.com>
Thu, 26 Mar 2015 11:58:05 +0000 (12:58 +0100)
 - created impl for new proposal
 - connection manager and friends
 - timeout in test

Change-Id: Ice0f4b4655be5c933d429cd9b74898a4fd326fe6
Signed-off-by: Jozef Gloncak <jgloncak@cisco.com>
Signed-off-by: Martin Bobak <mbobak@cisco.com>
Signed-off-by: Vaclav Demcak <vdemcak@cisco.com>
Signed-off-by: Martin Bobak <mbobak@cisco.com>
openflowplugin-impl/pom.xml [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionContextImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeContextImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/ConnectionReadyListenerImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/HandshakeListenerImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/OpenflowProtocolListenerImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/SystemNotificationsListenerImpl.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImplTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/resources/log4j.xml [new file with mode: 0644]
pom.xml

diff --git a/openflowplugin-impl/pom.xml b/openflowplugin-impl/pom.xml
new file mode 100644 (file)
index 0000000..841dd6e
--- /dev/null
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.openflowplugin</groupId>
+        <artifactId>openflowplugin-parent</artifactId>
+        <version>0.1.0-SNAPSHOT</version>
+        <relativePath>../</relativePath>
+    </parent>
+
+    <artifactId>openflowplugin-impl</artifactId>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <yangtools.version>0.7.0-SNAPSHOT</yangtools.version>
+        <yangtools.generator.version>0.7.0-SNAPSHOT</yangtools.generator.version>
+        <yangtools.binding.version>0.7.0-SNAPSHOT</yangtools.binding.version>
+        <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
+        <sal-binding-api.version>1.2.0-SNAPSHOT</sal-binding-api.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>${project.build.directory}/generated-sources/config</outputBaseDir>
+                                    <additionalConfiguration>
+                                        <namespaceToPackage1>
+                                            urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang
+                                        </namespaceToPackage1>
+                                    </additionalConfiguration>
+                                </generator>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>${project.build.directory}/generated-sources/sal</outputBaseDir>
+                                </generator>
+                                <generator>
+                                    <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+                                    <outputBaseDir>${project.build.directory}/site/models</outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                            <inspectDependencies>true</inspectDependencies>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.controller</groupId>
+                        <artifactId>yang-jmx-generator-plugin</artifactId>
+                        <version>${config.parent.version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>maven-sal-api-gen-plugin</artifactId>
+                        <version>${yangtools.version}</version>
+                        <type>jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.openflowplugin</groupId>
+            <artifactId>openflowplugin-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <!-- to be deprecated -->
+            <groupId>org.opendaylight.openflowplugin</groupId>
+            <artifactId>openflowplugin</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.openflowplugin</groupId>
+            <artifactId>openflowplugin-extension-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.openflowplugin.model</groupId>
+            <artifactId>model-flow-base</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.openflowplugin.model</groupId>
+            <artifactId>model-flow-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.openflowplugin.model</groupId>
+            <artifactId>model-flow-statistics</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller.model</groupId>
+            <artifactId>model-inventory</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-broker-impl</artifactId>
+            <version>${sal-binding-api.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.openflowjava</groupId>
+            <artifactId>openflow-protocol-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.openflowjava</groupId>
+            <artifactId>openflow-protocol-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-common-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.openflowjava</groupId>
+            <artifactId>util</artifactId>
+        </dependency>
+
+    </dependencies>
+</project>
+
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionContextImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionContextImpl.java
new file mode 100644 (file)
index 0000000..202ecc7
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.openflowplugin.impl.connection;
+
+import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
+
+/**
+ * 
+ */
+public class ConnectionContextImpl implements ConnectionContext {
+
+    private ConnectionAdapter connectionAdapter;
+    private CONNECTION_STATE connectionState;
+    private FeaturesReply featuresReply;
+
+    /**
+     * @param connectionAdapter
+     */
+    public ConnectionContextImpl(ConnectionAdapter connectionAdapter) {
+        this.connectionAdapter = connectionAdapter;
+    }
+
+    @Override
+    public ConnectionAdapter getConnectionAdapter() {
+        return connectionAdapter;
+    }
+
+    @Override
+    public CONNECTION_STATE getConnectionState() {
+        return connectionState;
+    }
+
+    @Override
+    public NodeId getNodeId() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public void setConnectionState(CONNECTION_STATE connectionState) {
+        this.connectionState = connectionState;
+    }
+
+    @Override
+    public FeaturesReply getFeatures() {
+        return featuresReply;
+    }
+
+    @Override
+    public void setFeatures(FeaturesReply featuresReply) {
+        this.featuresReply = featuresReply;
+        
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImpl.java
new file mode 100644 (file)
index 0000000..6355c16
--- /dev/null
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.openflowplugin.impl.connection;
+
+import java.net.InetAddress;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
+import org.opendaylight.openflowjava.protocol.api.connection.ConnectionReadyListener;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionManager;
+import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceConnectedHandler;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.MessageHandler;
+import org.opendaylight.openflowplugin.api.openflow.md.core.ConnectionConductor;
+import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
+import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
+import org.opendaylight.openflowplugin.impl.connection.listener.ConnectionReadyListenerImpl;
+import org.opendaylight.openflowplugin.impl.connection.listener.HandshakeListenerImpl;
+import org.opendaylight.openflowplugin.impl.connection.listener.OpenflowProtocolListenerImpl;
+import org.opendaylight.openflowplugin.impl.connection.listener.SystemNotificationsListenerImpl;
+import org.opendaylight.openflowplugin.openflow.md.core.HandshakeManagerImpl;
+import org.opendaylight.openflowplugin.openflow.md.core.ThreadPoolLoggingExecutor;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OpenflowProtocolListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SystemNotificationsListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class ConnectionManagerImpl implements ConnectionManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ConnectionManagerImpl.class);
+    private static final int HELLO_LIMIT = 20;
+    private boolean bitmapNegotiationEnabled = true;
+    private DeviceConnectedHandler deviceConnectedHandler;
+
+    @Override
+    public void onSwitchConnected(final ConnectionAdapter connectionAdapter) {
+        LOG.trace("preparing handshake");
+
+        final int handshakeThreadLimit = 1; //TODO: move to constants/parametrize
+        final ThreadPoolLoggingExecutor handshakePool = createHandshakePool(
+                connectionAdapter.getRemoteAddress().toString(), handshakeThreadLimit);
+
+        LOG.trace("prepare connection context");
+        final ConnectionContext connectionContext = new ConnectionContextImpl(connectionAdapter);
+
+        HandshakeListener handshakeListener = new HandshakeListenerImpl(connectionContext, deviceConnectedHandler);
+        final HandshakeManager handshakeManager = createHandshakeManager(connectionAdapter, handshakeListener);
+
+        LOG.trace("prepare handshake context");
+        HandshakeContext handshakeContext = new HandshakeContextImpl(handshakePool, handshakeManager);
+
+        LOG.trace("prepare connection listeners");
+        final ConnectionReadyListener connectionReadyListener = new ConnectionReadyListenerImpl(
+                connectionContext, handshakeContext);
+        connectionAdapter.setConnectionReadyListener(connectionReadyListener);
+
+        final OpenflowProtocolListener ofMessageListener =
+                new OpenflowProtocolListenerImpl(connectionContext, handshakeContext);
+        connectionAdapter.setMessageListener(ofMessageListener);
+
+        final SystemNotificationsListener systemListener = new SystemNotificationsListenerImpl(connectionContext);
+        connectionAdapter.setSystemListener(systemListener);
+
+        LOG.trace("connection balet finished");
+    }
+
+    /**
+     * @param connectionAdapter
+     * @param handshakeThreadLimit
+     * @return
+     */
+    private static ThreadPoolLoggingExecutor createHandshakePool(
+            final String connectionIdentifier, int handshakeThreadLimit) {
+        return new ThreadPoolLoggingExecutor(handshakeThreadLimit,
+                handshakeThreadLimit, 0L, TimeUnit.MILLISECONDS,
+                new ArrayBlockingQueue<Runnable>(HELLO_LIMIT), "OFHandshake-" + connectionIdentifier);
+    }
+
+    /**
+     * @param connectionAdapter
+     * @param handshakeListener
+     * @return
+     */
+    private HandshakeManager createHandshakeManager(final ConnectionAdapter connectionAdapter,
+            HandshakeListener handshakeListener) {
+        HandshakeManagerImpl handshakeManager = new HandshakeManagerImpl(connectionAdapter,
+                ConnectionConductor.versionOrder.get(0),
+                ConnectionConductor.versionOrder);
+        handshakeManager.setUseVersionBitmap(isBitmapNegotiationEnabled());
+        handshakeManager.setHandshakeListener(handshakeListener);
+        return handshakeManager;
+    }
+
+    /**
+     * @return parameter dedicated to hello message content
+     */
+    public boolean isBitmapNegotiationEnabled() {
+        return bitmapNegotiationEnabled ;
+    }
+
+    /**
+     * @param bitmapNegotiationEnabled the bitmapNegotiationEnabled to set
+     */
+    public void setBitmapNegotiationEnabled(boolean bitmapNegotiationEnabled) {
+        this.bitmapNegotiationEnabled = bitmapNegotiationEnabled;
+    }
+
+    @Override
+    public boolean accept(InetAddress switchAddress) {
+        // TODO add connection accept logic based on address
+        return true;
+    }
+
+    @Override
+    public void setDeviceConnectedHandler(DeviceConnectedHandler deviceConnectedHandler) {
+        this.deviceConnectedHandler = deviceConnectedHandler;
+    }
+
+    @Override
+    public void setMessageHandler(MessageHandler arg0) {
+        // TODO Auto-generated method stub
+
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeContextImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeContextImpl.java
new file mode 100644 (file)
index 0000000..adf3385
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.openflowplugin.impl.connection;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
+import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
+
+/**
+ * 
+ */
+public class HandshakeContextImpl implements HandshakeContext {
+
+    private ThreadPoolExecutor handshakePool;
+    private HandshakeManager handshakeManager;
+
+    /**
+     * @param handshakePool
+     * @param handshakeManager
+     */
+    public HandshakeContextImpl(ThreadPoolExecutor handshakePool, HandshakeManager handshakeManager) {
+        this.handshakePool = handshakePool;
+        this.handshakeManager = handshakeManager;
+    }
+
+    @Override
+    public HandshakeManager getHandshakeManager() {
+        return handshakeManager;
+    }
+
+    @Override
+    public ThreadPoolExecutor getHandshakePool() {
+        return handshakePool;
+    }
+
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/ConnectionReadyListenerImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/ConnectionReadyListenerImpl.java
new file mode 100644 (file)
index 0000000..d35d19d
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.openflowplugin.impl.connection.listener;
+
+import org.opendaylight.openflowjava.protocol.api.connection.ConnectionReadyListener;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
+import org.opendaylight.openflowplugin.openflow.md.core.HandshakeStepWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * oneshot listener - once connection is ready, initiate handshake (if not already started by device)
+ */
+public class ConnectionReadyListenerImpl implements ConnectionReadyListener {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(ConnectionReadyListenerImpl.class);
+    
+    private ConnectionContext connectionContext;
+    private HandshakeContext handshakeContext;
+
+    /**
+     * @param connectionContext
+     * @param handshakeContext
+     */
+    public ConnectionReadyListenerImpl(ConnectionContext connectionContext,
+            HandshakeContext handshakeContext) {
+                this.connectionContext = connectionContext;
+                this.handshakeContext = handshakeContext;
+    }
+
+    @Override
+    public void onConnectionReady() {
+        LOG.debug("device is connected and ready-to-use (pipeline prepared)");
+        if (connectionContext.getConnectionState() == null) {
+            HandshakeStepWrapper handshakeStepWrapper = new HandshakeStepWrapper(
+                    null, handshakeContext.getHandshakeManager(), connectionContext.getConnectionAdapter());
+            handshakeContext.getHandshakePool().execute(handshakeStepWrapper);
+            connectionContext.setConnectionState(ConnectionContext.CONNECTION_STATE.HANDSHAKING);
+        } else {
+            LOG.debug("already touched by hello message");
+        }
+    }
+
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/HandshakeListenerImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/HandshakeListenerImpl.java
new file mode 100644 (file)
index 0000000..7dbd5f8
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.openflowplugin.impl.connection.listener;
+
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceConnectedHandler;
+import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+ */
+public class HandshakeListenerImpl implements HandshakeListener {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(HandshakeListenerImpl.class);
+    
+    private ConnectionContext connectionContext;
+    private DeviceConnectedHandler deviceConnectedHandler;
+
+    /**
+     * @param connectionContext
+     * @param deviceConnectedHandler 
+     */
+    public HandshakeListenerImpl(ConnectionContext connectionContext, DeviceConnectedHandler deviceConnectedHandler) {
+        this.connectionContext = connectionContext;
+        this.deviceConnectedHandler = deviceConnectedHandler;
+    }
+
+    @Override
+    public void onHandshakeSuccessfull(GetFeaturesOutput featureOutput, Short version) {
+        connectionContext.setConnectionState(ConnectionContext.CONNECTION_STATE.WORKING);
+        connectionContext.setFeatures(featureOutput);
+        deviceConnectedHandler.deviceConnected(connectionContext); 
+    }
+
+    @Override
+    public void onHandshakeFailure() {
+        LOG.info("handshake failed: {}", connectionContext.getConnectionAdapter().getRemoteAddress());
+        connectionContext.setConnectionState(ConnectionContext.CONNECTION_STATE.RIP);
+        // TODO ensure that connection is closed
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/OpenflowProtocolListenerImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/OpenflowProtocolListenerImpl.java
new file mode 100644 (file)
index 0000000..bb95f57
--- /dev/null
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.openflowplugin.impl.connection.listener;
+
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
+import org.opendaylight.openflowplugin.openflow.md.core.HandshakeStepWrapper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoRequestMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.ErrorMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.ExperimenterMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemovedMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReplyMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OpenflowProtocolListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PacketInMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PortStatusMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+
+/**
+ * 
+ */
+public class OpenflowProtocolListenerImpl implements OpenflowProtocolListener {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(OpenflowProtocolListenerImpl.class);
+
+    private ConnectionContext connectionContext;
+    private HandshakeContext handshakeContext;
+
+    /**
+     * @param connectionContext
+     * @param handshakeContext 
+     */
+    public OpenflowProtocolListenerImpl(ConnectionContext connectionContext,
+            HandshakeContext handshakeContext) {
+        this.connectionContext = connectionContext;
+        this.handshakeContext = handshakeContext;
+    }
+
+    @Override
+    public void onEchoRequestMessage(EchoRequestMessage notification) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onErrorMessage(ErrorMessage notification) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onExperimenterMessage(ExperimenterMessage notification) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onFlowRemovedMessage(FlowRemovedMessage notification) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onHelloMessage(HelloMessage hello) {
+        LOG.debug("processing HELLO.xid: {}", hello.getXid());
+        if (connectionContext.getConnectionState() == null) {
+            connectionContext.setConnectionState(ConnectionContext.CONNECTION_STATE.HANDSHAKING);
+        }
+        
+        if (checkState(ConnectionContext.CONNECTION_STATE.HANDSHAKING)) {
+            HandshakeStepWrapper handshakeStepWrapper = new HandshakeStepWrapper(
+                    hello, handshakeContext.getHandshakeManager(), connectionContext.getConnectionAdapter());
+            handshakeContext.getHandshakePool().submit(handshakeStepWrapper);
+        } else {
+            //TODO: consider disconnecting of bad behaving device
+        }
+        
+    }
+
+    @Override
+    public void onMultipartReplyMessage(MultipartReplyMessage notification) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onPacketInMessage(PacketInMessage notification) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onPortStatusMessage(PortStatusMessage notification) {
+        // TODO Auto-generated method stub
+
+    }
+    
+    /**
+     * @param expectedState
+     */
+    protected boolean checkState(ConnectionContext.CONNECTION_STATE expectedState) {
+        boolean verdict = true;
+        if (! Objects.equal(connectionContext.getConnectionState(), expectedState)) {
+            verdict = false;
+            LOG.info("Expected state: {}, actual state: {}", expectedState, 
+                    connectionContext.getConnectionState());
+        }
+        
+        return verdict;
+    }
+
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/SystemNotificationsListenerImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/SystemNotificationsListenerImpl.java
new file mode 100644 (file)
index 0000000..b3c8718
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.openflowplugin.impl.connection.listener;
+
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.DisconnectEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SwitchIdleEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SystemNotificationsListener;
+
+/**
+ * 
+ */
+public class SystemNotificationsListenerImpl implements SystemNotificationsListener {
+    
+    private ConnectionContext connectionContext;
+
+
+    /**
+     * @param connectionContext
+     */
+    public SystemNotificationsListenerImpl(ConnectionContext connectionContext) {
+        this.connectionContext = connectionContext;
+    }
+
+    @Override
+    public void onDisconnectEvent(DisconnectEvent notification) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void onSwitchIdleEvent(SwitchIdleEvent notification) {
+        // TODO Auto-generated method stub
+
+    }
+
+}
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImplTest.java
new file mode 100644 (file)
index 0000000..e493a0e
--- /dev/null
@@ -0,0 +1,190 @@
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.openflowplugin.impl.connection;
+
+import static org.junit.Assert.fail;
+import com.google.common.util.concurrent.SettableFuture;
+import java.math.BigInteger;
+import java.net.InetSocketAddress;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
+import org.opendaylight.openflowjava.protocol.api.connection.ConnectionReadyListener;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceConnectedHandler;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessageBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OpenflowProtocolListener;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * test of {@link ConnectionManagerImpl} - lightweight version, using basic ways (TDD)
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ConnectionManagerImplTest {
+
+    /** timeout of final step [ms] */
+    private static final int FINAL_STEP_TIMEOUT = 500;
+    private ConnectionManagerImpl connectionManagerImpl;
+    @Mock
+    private ConnectionAdapter connection;
+    @Mock
+    private DeviceConnectedHandler deviceConnectedHandler;
+    @Captor
+    private ArgumentCaptor<ConnectionReadyListener> connectionReadyListenerAC;
+    @Captor
+    private ArgumentCaptor<OpenflowProtocolListener> ofpListenerAC;
+
+    /**
+     * before each test method
+     */
+    @Before
+    public void setUp() {
+        connectionManagerImpl = new ConnectionManagerImpl();
+        connectionManagerImpl.setDeviceConnectedHandler(deviceConnectedHandler);
+        final InetSocketAddress deviceAddress = InetSocketAddress.createUnresolved("yahoo", 42);
+        Mockito.when(connection.getRemoteAddress()).thenReturn(deviceAddress);
+        Mockito.when(connection.isAlive()).thenReturn(true);
+    }
+
+    /**
+     * after each test method
+     * @throws InterruptedException
+     */
+    @After
+    public void tearDown() throws InterruptedException {
+        Thread.sleep(200L);
+    }
+
+    /**
+     * Test method for {@link org.opendaylight.openflowplugin.impl.connection.ConnectionManagerImpl#onSwitchConnected(org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter)}.
+     * invoking onConnectionReady first, scenario:
+     * <ol>
+     *  <li>send hello to device (rpc with void output)</li>
+     *  <li>receive hello from device (notification)</li>
+     *  <li>send getFeature to device (rpc with getFeatureOutput)</li>
+     *  <li>wait for rpc to finish with getFeatureOutput</li>
+     * </ol>
+     * @throws InterruptedException
+     */
+    @Test
+    public void testOnSwitchConnected1() throws InterruptedException {
+        connectionManagerImpl.onSwitchConnected(connection);
+        Mockito.verify(connection).setConnectionReadyListener(connectionReadyListenerAC.capture());
+        Mockito.verify(connection).setMessageListener(ofpListenerAC.capture());
+
+        // prepare void reply (hello rpc output)
+        final SettableFuture<RpcResult<Void>> voidResponseFx = SettableFuture.<RpcResult<Void>>create();
+        Mockito.when(connection.hello(Matchers.any(HelloInput.class))).thenReturn(voidResponseFx);
+        // prepare getFeature reply (getFeture rpc output)
+        final SettableFuture<RpcResult<GetFeaturesOutput>> featureResponseFx = SettableFuture.<RpcResult<GetFeaturesOutput>>create();
+        Mockito.when(connection.getFeatures(Matchers.any(GetFeaturesInput.class))).thenReturn(featureResponseFx);
+
+
+        // fire handshake
+        connectionReadyListenerAC.getValue().onConnectionReady();
+
+        // deliver hello send output (void)
+        Thread.sleep(100L);
+        final RpcResult<Void> helloResponse = RpcResultBuilder.success((Void) null).build();
+        voidResponseFx.set(helloResponse);
+
+        // send hello reply
+        final HelloMessage hello = new HelloMessageBuilder().setVersion(OFConstants.OFP_VERSION_1_3).setXid(1L).build();
+        ofpListenerAC.getValue().onHelloMessage(hello);
+
+        // deliver getFeature output
+        Thread.sleep(100L);
+        final GetFeaturesOutput getFeatureOutput = new GetFeaturesOutputBuilder()
+        .setDatapathId(BigInteger.TEN)
+        .setVersion(OFConstants.OFP_VERSION_1_3)
+        .setXid(2L)
+        .setTables((short) 15)
+        .build();
+        final RpcResult<GetFeaturesOutput> rpcFeaturesOutput = RpcResultBuilder.success(getFeatureOutput).build();
+        featureResponseFx.set(rpcFeaturesOutput);
+
+        Mockito.verify(deviceConnectedHandler, Mockito.timeout(500)).deviceConnected(Matchers.any(ConnectionContext.class));
+    }
+
+    /**
+     * Test method for {@link org.opendaylight.openflowplugin.impl.connection.ConnectionManagerImpl#onSwitchConnected(org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter)}.
+     * invoking onHelloMessage, scenario:
+     * <ol>
+     *  <li>receive hello from device (notification)</li>
+     *  <li>send hello to device (rpc with void output)</li>
+     *  <li>send getFeature to device (rpc with getFeatureOutput)</li>
+     *  <li>wait for rpc to finish with getFeatureOutput</li>
+     * </ol>
+     * @throws InterruptedException
+     */
+    @Test
+    public void testOnSwitchConnected2() throws InterruptedException {
+        connectionManagerImpl.onSwitchConnected(connection);
+        Mockito.verify(connection).setConnectionReadyListener(connectionReadyListenerAC.capture());
+        Mockito.verify(connection).setMessageListener(ofpListenerAC.capture());
+
+        // prepare void reply (hello rpc output)
+        final SettableFuture<RpcResult<Void>> voidResponseFx = SettableFuture.<RpcResult<Void>>create();
+        Mockito.when(connection.hello(Matchers.any(HelloInput.class))).thenReturn(voidResponseFx);
+        // prepare getFeature reply (getFeture rpc output)
+        final SettableFuture<RpcResult<GetFeaturesOutput>> featureResponseFx = SettableFuture.<RpcResult<GetFeaturesOutput>>create();
+        Mockito.when(connection.getFeatures(Matchers.any(GetFeaturesInput.class))).thenReturn(featureResponseFx);
+
+
+        // fire handshake - send hello reply
+        final HelloMessage hello = new HelloMessageBuilder().setVersion(OFConstants.OFP_VERSION_1_3).setXid(1L).build();
+        ofpListenerAC.getValue().onHelloMessage(hello);
+
+        // notify about connection ready
+        connectionReadyListenerAC.getValue().onConnectionReady();
+
+        // deliver hello send output (void)
+        Thread.sleep(100L);
+        final RpcResult<Void> helloResponse = RpcResultBuilder.success((Void) null).build();
+        voidResponseFx.set(helloResponse);
+
+        // deliver getFeature output
+        Thread.sleep(100L);
+        final GetFeaturesOutput getFeatureOutput = new GetFeaturesOutputBuilder()
+        .setDatapathId(BigInteger.TEN)
+        .setVersion(OFConstants.OFP_VERSION_1_3)
+        .setXid(2L)
+        .setTables((short) 15)
+        .build();
+        final RpcResult<GetFeaturesOutput> rpcFeaturesOutput = RpcResultBuilder.success(getFeatureOutput).build();
+        featureResponseFx.set(rpcFeaturesOutput);
+
+        Mockito.verify(deviceConnectedHandler, Mockito.timeout(FINAL_STEP_TIMEOUT)).deviceConnected(Matchers.any(ConnectionContext.class));
+    }
+
+    /**
+     * Test method for {@link org.opendaylight.openflowplugin.impl.connection.ConnectionManagerImpl#setOpenflowProtocolListener(org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OpenflowProtocolListener)}.
+     */
+    @Test
+    @Ignore
+    public void testSetOpenflowProtocolListener() {
+        fail("Not yet implemented");
+    }
+
+}
diff --git a/openflowplugin-impl/src/test/resources/log4j.xml b/openflowplugin-impl/src/test/resources/log4j.xml
new file mode 100644 (file)
index 0000000..a2c3e9f
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">\r
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">\r
+\r
+    <appender name="console" class="org.apache.log4j.ConsoleAppender">\r
+        <layout class="org.apache.log4j.PatternLayout">\r
+            <param name="ConversionPattern" value="%-6p %d{HH:mm:ss.SSS} [%10.10t] %30.30c %x - %m%n" />\r
+        </layout>\r
+<!--         <param name="Threshold" value="DEBUG" /> -->\r
+    </appender>\r
+\r
+    <logger name="org.opendaylight.openflowplugin" additivity="false">\r
+        <level value="DEBUG" />\r
+        <appender-ref ref="console" />\r
+    </logger>\r
+    \r
+    <logger name="org.opendaylight.openflowplugin.impl" additivity="false">\r
+        <level value="DEBUG" />\r
+        <appender-ref ref="console" />\r
+    </logger>\r
+    \r
+    <logger name="org.opendaylight.openflowplugin.impl.connection" additivity="false">\r
+        <level value="TRACE" />\r
+        <appender-ref ref="console" />\r
+    </logger>\r
+    \r
+    <logger name="org.opendaylight.openflowplugin.openflow.md.core.HandshakeManagerImpl" additivity="false">\r
+        <level value="TRACE" />\r
+        <appender-ref ref="console" />\r
+    </logger>\r
+\r
+    <root>\r
+        <priority value="INFO" />\r
+        <appender-ref ref="console" />\r
+    </root>\r
+</log4j:configuration>\r
diff --git a/pom.xml b/pom.xml
index 1a8abca45925785e629c5c3f96fa2509962fe1ed..3dd522014c413076b99e072bb8ee13ba3d46c9e3 100644 (file)
--- a/pom.xml
+++ b/pom.xml
     <modules>
       <module>openflowplugin-api</module>
       <module>openflowplugin</module>
+      <module>openflowplugin-impl</module>
       <module>extension</module>
       <module>distribution/karaf</module>
       <module>openflowplugin-controller-config</module>