From 8044e5dd8347a5e18f21e12214705b7a01571d17 Mon Sep 17 00:00:00 2001 From: Martin Bobak Date: Tue, 24 Mar 2015 14:14:07 +0100 Subject: [PATCH] ConnectionManager proposal - created impl for new proposal - connection manager and friends - timeout in test Change-Id: Ice0f4b4655be5c933d429cd9b74898a4fd326fe6 Signed-off-by: Jozef Gloncak Signed-off-by: Martin Bobak Signed-off-by: Vaclav Demcak Signed-off-by: Martin Bobak --- openflowplugin-impl/pom.xml | 172 ++++++++++++++++ .../connection/ConnectionContextImpl.java | 62 ++++++ .../connection/ConnectionManagerImpl.java | 134 ++++++++++++ .../impl/connection/HandshakeContextImpl.java | 42 ++++ .../listener/ConnectionReadyListenerImpl.java | 50 +++++ .../listener/HandshakeListenerImpl.java | 49 +++++ .../OpenflowProtocolListenerImpl.java | 120 +++++++++++ .../SystemNotificationsListenerImpl.java | 42 ++++ .../connection/ConnectionManagerImplTest.java | 190 ++++++++++++++++++ .../src/test/resources/log4j.xml | 36 ++++ pom.xml | 1 + 11 files changed, 898 insertions(+) create mode 100644 openflowplugin-impl/pom.xml create mode 100644 openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionContextImpl.java create mode 100644 openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImpl.java create mode 100644 openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeContextImpl.java create mode 100644 openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/ConnectionReadyListenerImpl.java create mode 100644 openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/HandshakeListenerImpl.java create mode 100644 openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/OpenflowProtocolListenerImpl.java create mode 100644 openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/SystemNotificationsListenerImpl.java create mode 100644 openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImplTest.java create mode 100644 openflowplugin-impl/src/test/resources/log4j.xml diff --git a/openflowplugin-impl/pom.xml b/openflowplugin-impl/pom.xml new file mode 100644 index 0000000000..841dd6ee71 --- /dev/null +++ b/openflowplugin-impl/pom.xml @@ -0,0 +1,172 @@ + + + 4.0.0 + + org.opendaylight.openflowplugin + openflowplugin-parent + 0.1.0-SNAPSHOT + ../ + + + openflowplugin-impl + bundle + + + 0.7.0-SNAPSHOT + 0.7.0-SNAPSHOT + 0.7.0-SNAPSHOT + ${project.build.directory}/generated-sources/config + 1.2.0-SNAPSHOT + + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.yangtools + yang-maven-plugin + + + + generate-sources + + + + + + org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + + ${project.build.directory}/generated-sources/config + + + urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang + + + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + + ${project.build.directory}/generated-sources/sal + + + org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl + ${project.build.directory}/site/models + + + true + + + + + + org.opendaylight.controller + yang-jmx-generator-plugin + ${config.parent.version} + + + org.opendaylight.yangtools + maven-sal-api-gen-plugin + ${yangtools.version} + jar + + + + + + + + org.opendaylight.openflowplugin + openflowplugin-api + ${project.version} + + + + org.opendaylight.openflowplugin + openflowplugin + ${project.version} + + + org.opendaylight.openflowplugin + openflowplugin-extension-api + ${project.version} + + + org.opendaylight.openflowplugin.model + model-flow-base + + + org.opendaylight.openflowplugin.model + model-flow-service + + + org.opendaylight.openflowplugin.model + model-flow-statistics + + + org.opendaylight.controller.model + model-inventory + + + org.opendaylight.controller + sal-binding-api + + + org.opendaylight.controller + sal-binding-broker-impl + ${sal-binding-api.version} + + + + org.opendaylight.openflowjava + openflow-protocol-api + + + org.opendaylight.openflowjava + openflow-protocol-spi + + + org.opendaylight.controller + config-api + + + + com.google.guava + guava + + + org.apache.commons + commons-lang3 + + + + junit + junit + test + + + org.mockito + mockito-all + test + + + + org.slf4j + slf4j-log4j12 + test + + + org.opendaylight.controller + sal-common-util + + + org.opendaylight.openflowjava + util + + + + + 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 index 0000000000..202ecc7a76 --- /dev/null +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionContextImpl.java @@ -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 index 0000000000..6355c165b6 --- /dev/null +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImpl.java @@ -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(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 index 0000000000..adf3385b60 --- /dev/null +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeContextImpl.java @@ -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 index 0000000000..d35d19d59d --- /dev/null +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/ConnectionReadyListenerImpl.java @@ -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 index 0000000000..7dbd5f8a7b --- /dev/null +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/HandshakeListenerImpl.java @@ -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 index 0000000000..bb95f5710f --- /dev/null +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/OpenflowProtocolListenerImpl.java @@ -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 index 0000000000..b3c8718b9b --- /dev/null +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/SystemNotificationsListenerImpl.java @@ -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 index 0000000000..e493a0e5f1 --- /dev/null +++ b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImplTest.java @@ -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 connectionReadyListenerAC; + @Captor + private ArgumentCaptor 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: + *
    + *
  1. send hello to device (rpc with void output)
  2. + *
  3. receive hello from device (notification)
  4. + *
  5. send getFeature to device (rpc with getFeatureOutput)
  6. + *
  7. wait for rpc to finish with getFeatureOutput
  8. + *
+ * @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> voidResponseFx = SettableFuture.>create(); + Mockito.when(connection.hello(Matchers.any(HelloInput.class))).thenReturn(voidResponseFx); + // prepare getFeature reply (getFeture rpc output) + final SettableFuture> featureResponseFx = SettableFuture.>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 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 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: + *
    + *
  1. receive hello from device (notification)
  2. + *
  3. send hello to device (rpc with void output)
  4. + *
  5. send getFeature to device (rpc with getFeatureOutput)
  6. + *
  7. wait for rpc to finish with getFeatureOutput
  8. + *
+ * @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> voidResponseFx = SettableFuture.>create(); + Mockito.when(connection.hello(Matchers.any(HelloInput.class))).thenReturn(voidResponseFx); + // prepare getFeature reply (getFeture rpc output) + final SettableFuture> featureResponseFx = SettableFuture.>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 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 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 index 0000000000..a2c3e9f22d --- /dev/null +++ b/openflowplugin-impl/src/test/resources/log4j.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 1a8abca459..3dd522014c 100644 --- a/pom.xml +++ b/pom.xml @@ -629,6 +629,7 @@ openflowplugin-api openflowplugin + openflowplugin-impl extension distribution/karaf openflowplugin-controller-config -- 2.36.6