/**
* Device connection rate limit property type.
*/
- DEVICE_CONNECTION_RATE_LIMIT_PER_MIN;
+ DEVICE_CONNECTION_RATE_LIMIT_PER_MIN,
+ /**
+ * Device connection hold time property type.
+ */
+ DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS;
private static final Map<String, ConfigurationProperty> KEY_VALUE_MAP;
* It instantiates and registers {@link ConnectionContext}
* used for handling all communication with device when onSwitchConnected notification is processed.
*/
-public interface ConnectionManager extends SwitchConnectionHandler {
+public interface ConnectionManager extends SwitchConnectionHandler, AutoCloseable {
/**
* Method registers handler responsible handling operations related to connected device after
--- /dev/null
+/*
+ * Copyright (c) 2019 Ericsson India Global Services Pvt Ltd. 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.api.openflow.connection;
+
+import java.math.BigInteger;
+import java.time.LocalDateTime;
+
+public interface DeviceConnectionStatusProvider extends AutoCloseable {
+
+ /**
+ * Initialize the DeviceConnectionStatusProvider.
+ */
+ void init();
+
+ /**
+ * Get the last connection time of a device.
+ * @param datapathId datapathId of node
+ */
+ LocalDateTime getDeviceLastConnectionTime(BigInteger datapathId);
+
+ /**
+ * Update the last connection time of a device.
+ * @param datapathId datapathId of node
+ * @param time last connected time of datapathId
+ */
+ void addDeviceLastConnectionTime(BigInteger datapathId, LocalDateTime time);
+
+ /**
+ * Clear the last connection time of a device.
+ * @param datapathId datapathId of node
+ */
+ void removeDeviceLastConnectionTime(BigInteger datapathId);
+}
type uint16;
default 0;
}
+
+ leaf device-connection-hold-time-in-seconds {
+ description "device connection hold time is the least time delay in seconds a
+ device has to maintain between its consecutive connection attempts. If time delay
+ between the previous connection and the current connection is within device
+ connection hold time, the device will not be allowed to connect to the controller.
+ Default value of the device connection hold time is 0 second.";
+ type uint16;
+ default 0;
+ }
}
}
#
# device-connection-rate-limit-per-min=0
+#
+# Device connection hold time is the least time delay in seconds a device has
+# to maintain between its consecutive connection attempts. If time delay between
+# the previous connection and the current connection is within device connection
+# hold time, the device will not be allowed to connect to the controller.
+# Default value of the device connection hold time is 0 second
+#
+# device-connection-hold-time-in-seconds=0
+
#############################################################################
# #
# Forwarding Rule Manager Application Configuration #
contextChainHolder.addManager(rpcManager);
contextChainHolder.addManager(roleManager);
- connectionManager = new ConnectionManagerImpl(config, executorService);
+ connectionManager = new ConnectionManagerImpl(config, executorService, dataBroker);
connectionManager.setDeviceConnectedHandler(contextChainHolder);
connectionManager.setDeviceDisconnectedHandler(contextChainHolder);
@Override
@PreDestroy
+ @SuppressWarnings("checkstyle:IllegalCatch")
public void close() {
try {
shutdownSwitchConnections().get(10, TimeUnit.SECONDS);
gracefulShutdown(hashedWheelTimer);
unregisterMXBean(MESSAGE_INTELLIGENCE_AGENCY_MX_BEAN_NAME);
openflowDiagStatusProvider.reportStatus(ServiceState.UNREGISTERED);
+ try {
+ if (connectionManager != null) {
+ connectionManager.close();
+ connectionManager = null;
+ }
+ } catch (Exception e) {
+ LOG.error("Failed to close ConnectionManager", e);
+ }
}
@SuppressWarnings("checkstyle:IllegalCatch")
providerConfig.getThreadPoolTimeout().toString())
.put(ConfigurationProperty.DEVICE_CONNECTION_RATE_LIMIT_PER_MIN.toString(),
providerConfig.getDeviceConnectionRateLimitPerMin().toString())
+ .put(ConfigurationProperty.DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS.toString(),
+ providerConfig.getDeviceConnectionHoldTimeInSeconds().toString())
.build());
}
return service.getProperty(ConfigurationProperty.DEVICE_CONNECTION_RATE_LIMIT_PER_MIN.toString(),
Uint16::valueOf);
}
+
+ @Override
+ public Uint16 getDeviceConnectionHoldTimeInSeconds() {
+ return service.getProperty(ConfigurationProperty.DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS.toString(),
+ Uint16::valueOf);
+ }
}
import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue;
import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueueHandlerRegistration;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.connection.DeviceConnectionStatusProvider;
import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
import org.opendaylight.openflowplugin.api.openflow.connection.OutboundQueueProvider;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
private HandshakeContext handshakeContext;
private DeviceInfo deviceInfo;
private final List<PortStatusMessage> portStatusMessages = new ArrayList<>();
+ private final DeviceConnectionStatusProvider deviceConnectionStatusProvider;
/**
* Constructor.
*
* @param connectionAdapter - connection adapter
*/
- public ConnectionContextImpl(final ConnectionAdapter connectionAdapter) {
+ public ConnectionContextImpl(final ConnectionAdapter connectionAdapter,
+ final DeviceConnectionStatusProvider deviceConnectionStatusProvider) {
this.connectionAdapter = connectionAdapter;
+ this.deviceConnectionStatusProvider = deviceConnectionStatusProvider;
}
@Override
*/
package org.opendaylight.openflowplugin.impl.connection;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import java.math.BigInteger;
import java.net.InetAddress;
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
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.connection.ConnectionManager;
+import org.opendaylight.openflowplugin.api.openflow.connection.DeviceConnectionStatusProvider;
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.DeviceDisconnectedHandler;
import org.opendaylight.openflowplugin.impl.connection.listener.HandshakeListenerImpl;
import org.opendaylight.openflowplugin.impl.connection.listener.OpenflowProtocolListenerInitialImpl;
import org.opendaylight.openflowplugin.impl.connection.listener.SystemNotificationsListenerImpl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
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.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.provider.config.rev160510.OpenflowProviderConfig;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger LOG = LoggerFactory.getLogger(ConnectionManagerImpl.class);
private static final boolean BITMAP_NEGOTIATION_ENABLED = true;
- private DeviceConnectedHandler deviceConnectedHandler;
private final OpenflowProviderConfig config;
private final ExecutorService executorService;
private final DeviceConnectionRateLimiter deviceConnectionRateLimiter;
+ private final DataBroker dataBroker;
+ private final int deviceConnectionHoldTime;
+ private DeviceConnectedHandler deviceConnectedHandler;
private DeviceDisconnectedHandler deviceDisconnectedHandler;
+ private DeviceConnectionStatusProvider deviceConnectionStatusProvider;
- public ConnectionManagerImpl(final OpenflowProviderConfig config, final ExecutorService executorService) {
+ public ConnectionManagerImpl(final OpenflowProviderConfig config, final ExecutorService executorService,
+ final DataBroker dataBroker) {
this.config = config;
this.executorService = executorService;
this.deviceConnectionRateLimiter = new DeviceConnectionRateLimiter(config);
+ this.dataBroker = dataBroker;
+ this.deviceConnectionHoldTime = config.getDeviceConnectionHoldTimeInSeconds().toJava();
+ deviceConnectionStatusProvider = new DeviceConnectionStatusProviderImpl();
+ deviceConnectionStatusProvider.init();
}
@Override
public void onSwitchConnected(final ConnectionAdapter connectionAdapter) {
LOG.trace("prepare connection context");
- final ConnectionContext connectionContext = new ConnectionContextImpl(connectionAdapter);
+ final ConnectionContext connectionContext = new ConnectionContextImpl(connectionAdapter,
+ deviceConnectionStatusProvider);
connectionContext.setDeviceDisconnectedHandler(this.deviceDisconnectedHandler);
HandshakeListener handshakeListener = new HandshakeListenerImpl(connectionContext, deviceConnectedHandler);
HandshakeManagerImpl handshakeManager = new HandshakeManagerImpl(connectionAdapter,
OFConstants.VERSION_ORDER.get(0),
OFConstants.VERSION_ORDER, new ErrorHandlerSimpleImpl(), handshakeListener, BITMAP_NEGOTIATION_ENABLED,
- deviceConnectionRateLimiter);
+ deviceConnectionRateLimiter, deviceConnectionHoldTime, deviceConnectionStatusProvider);
return handshakeManager;
}
public void setDeviceDisconnectedHandler(final DeviceDisconnectedHandler deviceDisconnectedHandler) {
this.deviceDisconnectedHandler = deviceDisconnectedHandler;
}
+
+ @VisibleForTesting
+ DeviceConnectionStatusProvider getDeviceConnectionStatusProvider() {
+ return deviceConnectionStatusProvider;
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (deviceConnectionStatusProvider != null) {
+ deviceConnectionStatusProvider.close();
+ deviceConnectionStatusProvider = null;
+ }
+ }
+
+ class DeviceConnectionStatusProviderImpl implements DeviceConnectionStatusProvider,
+ ClusteredDataTreeChangeListener<Node> {
+ private final Map<BigInteger, LocalDateTime> deviceConnectionMap = new ConcurrentHashMap<>();
+
+ private ListenerRegistration<DeviceConnectionStatusProviderImpl> listenerRegistration;
+
+ @Override
+ @SuppressWarnings({"checkstyle:IllegalCatch"})
+ public void init() {
+ DataTreeIdentifier<Node> treeId = DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL,
+ getWildCardPath());
+ try {
+ listenerRegistration = dataBroker.registerDataTreeChangeListener(treeId, this);
+ } catch (Exception e) {
+ LOG.error("DeviceConnectionStatusProvider listener registration failed", e);
+ }
+ }
+
+ @Override
+ public LocalDateTime getDeviceLastConnectionTime(BigInteger nodeId) {
+ return deviceConnectionMap.get(nodeId);
+ }
+
+ @Override
+ public void addDeviceLastConnectionTime(BigInteger nodeId, LocalDateTime time) {
+ deviceConnectionMap.put(nodeId, time);
+ }
+
+ @Override
+ public void removeDeviceLastConnectionTime(BigInteger nodeId) {
+ deviceConnectionMap.remove(nodeId);
+ }
+
+ @Override
+ public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Node>> changes) {
+ Preconditions.checkNotNull(changes, "Changes must not be null!");
+ for (DataTreeModification<Node> change : changes) {
+ final DataObjectModification<Node> mod = change.getRootNode();
+ switch (mod.getModificationType()) {
+ case DELETE:
+ break;
+ case SUBTREE_MODIFIED:
+ break;
+ case WRITE:
+ processNodeModification(change);
+ break;
+ default:
+ throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
+ }
+ }
+ }
+
+ private InstanceIdentifier<Node> getWildCardPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class);
+ }
+
+ private void processNodeModification(DataTreeModification<Node> change) {
+ final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
+ final InstanceIdentifier<Node> nodeIdent = key.firstIdentifierOf(Node.class);
+ String[] nodeIdentity = nodeIdent.firstKeyOf(Node.class).getId().getValue().split(":");
+ String nodeId = nodeIdentity[1];
+ LOG.info("Clearing the device connection timer for the device {}", nodeId);
+ removeDeviceLastConnectionTime(new BigInteger(nodeId));
+ }
+
+ @Override
+ public void close() {
+ if (listenerRegistration != null) {
+ listenerRegistration.close();
+ listenerRegistration = null;
+ }
+ }
+ }
+
}
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.math.BigInteger;
+import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.connection.DeviceConnectionStatusProvider;
import org.opendaylight.openflowplugin.api.openflow.md.core.ErrorHandler;
import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
private boolean useVersionBitmap; // not final just for unit test
private final DeviceConnectionRateLimiter deviceConnectionRateLimiter;
+ private final int deviceConnectionHoldTime;
+ private final DeviceConnectionStatusProvider deviceConnectionStatusProvider;
/**
* Constructor.
* @param errorHandler the ErrorHandler
* @param handshakeListener the HandshakeListener
* @param useVersionBitmap should use negotiation bit map
+ * @param deviceConnectionRateLimiter device connection rate limiter utility
+ * @param deviceConnectionHoldTime deivce connection hold time in seconds
+ * @param deviceConnectionStatusProvider utility for maintaining device connection states
*/
public HandshakeManagerImpl(final ConnectionAdapter connectionAdapter, final Short highestVersion,
- final List<Short> versionOrder,
- final ErrorHandler errorHandler, final HandshakeListener handshakeListener,
- final boolean useVersionBitmap,
- final DeviceConnectionRateLimiter deviceConnectionRateLimiter) {
+ final List<Short> versionOrder, final ErrorHandler errorHandler,
+ final HandshakeListener handshakeListener, final boolean useVersionBitmap,
+ final DeviceConnectionRateLimiter deviceConnectionRateLimiter,
+ final int deviceConnectionHoldTime,
+ final DeviceConnectionStatusProvider deviceConnectionStatusProvider) {
this.highestVersion = highestVersion;
this.versionOrder = versionOrder;
this.connectionAdapter = connectionAdapter;
this.handshakeListener = handshakeListener;
this.useVersionBitmap = useVersionBitmap;
this.deviceConnectionRateLimiter = deviceConnectionRateLimiter;
+ this.deviceConnectionHoldTime = deviceConnectionHoldTime;
+ this.deviceConnectionStatusProvider = deviceConnectionStatusProvider;
}
@Override
GetFeaturesOutput featureOutput = rpcFeatures.getResult();
final Uint64 dpId = featureOutput.getDatapathId();
- connectionAdapter.setDatapathId(dpId == null ? null : dpId.toJava());
- if (!deviceConnectionRateLimiter.tryAquire()) {
- LOG.warn("Openflowplugin hit the device connection rate limit threshold. Denying"
- + " the connection from device {}", featureOutput.getDatapathId());
+ BigInteger datapathId = dpId == null ? null : dpId.toJava();
+ connectionAdapter.setDatapathId(datapathId);
+ if (datapathId == null || !isAllowedToConnect(datapathId)) {
connectionAdapter.disconnect();
return;
}
LOG.debug("future features [{}] hooked ..", xid);
}
+ public boolean isAllowedToConnect(BigInteger nodeId) {
+ // The device isn't allowed for connection till device connection hold time is over
+ if (deviceConnectionHoldTime > 0) {
+ LocalDateTime lastConnectionTime = deviceConnectionStatusProvider.getDeviceLastConnectionTime(nodeId);
+ if (lastConnectionTime == null) {
+ LOG.debug("Initial connection attempt by device {} to the controller node. Allowing to connect after {}"
+ + "seconds", nodeId, deviceConnectionHoldTime);
+ deviceConnectionStatusProvider.addDeviceLastConnectionTime(nodeId, LocalDateTime.now());
+ return false;
+ } else if (LocalDateTime.now().isBefore(lastConnectionTime.plusSeconds(deviceConnectionHoldTime))) {
+ LOG.trace("Device trying to connect before the connection delay {} seconds, disconnecting the device "
+ + "{}", deviceConnectionHoldTime, nodeId);
+ return false;
+ }
+ }
+
+ if (!deviceConnectionRateLimiter.tryAquire()) {
+ LOG.debug("Permit not acquired for device {}, disconnecting the device.", nodeId);
+ connectionAdapter.disconnect();
+ return false;
+ }
+ return true;
+ }
+
/**
* Method for unit testing, only.
* This method is not thread safe and can only safely be used from a test.
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.opendaylight.infrautils.ready.SystemReadyMonitor;
private static final long BASIC_TIMER_DELAY = 1L;
private static final boolean USE_SINGLE_LAYER_SERIALIZATION = false;
private static final Uint16 DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = Uint16.ZERO;
+ private static final Uint16 DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS = Uint16.valueOf(60);
@Before
public void setUp() {
.thenReturn(THREAD_POOL_TIMEOUT);
when(configurationService.getProperty(eq(ConfigurationProperty.DEVICE_CONNECTION_RATE_LIMIT_PER_MIN.toString()),
any())).thenReturn(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN);
+ when(configurationService.getProperty(
+ Matchers.eq(ConfigurationProperty.DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS.toString()),
+ Matchers.any())).thenReturn(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS);
}
@Test
@RunWith(MockitoJUnitRunner.class)
public class ConfigurationServiceFactoryImplTest {
- private static final int CONFIG_PROP_COUNT = 23;
+ private static final int CONFIG_PROP_COUNT = 24;
private static final boolean IS_STATISTICS_POLLING_ON = true;
private static final int BARRIER_COUNT_LIMIT = 2000;
private static final long BARRIER_INTERVAL_TIMEOUT_LIMIT = 3000;
private static final int THREAD_POOL_MAX_THREADS = 1000;
private static final Uint32 THREAD_POOL_TIMEOUT = Uint32.valueOf(60);
private static final Uint16 DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = Uint16.ZERO;
+ private static final Uint16 DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS = Uint16.valueOf(60);
@Mock
private OpenflowProviderConfig config;
when(config.getThreadPoolMaxThreads()).thenReturn(new NonZeroUint16Type(THREAD_POOL_MAX_THREADS));
when(config.getThreadPoolTimeout()).thenReturn(THREAD_POOL_TIMEOUT);
when(config.getDeviceConnectionRateLimitPerMin()).thenReturn(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN);
+ when(config.getDeviceConnectionHoldTimeInSeconds()).thenReturn(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS);
final Map<String, String> properties = new Hashtable<>();
properties.put(ConfigurationProperty.IS_STATISTICS_POLLING_ON.toString(),
private static final int THREAD_POOL_MAX_THREADS = 1000;
private static final Uint32 THREAD_POOL_TIMEOUT = Uint32.valueOf(60);
private static final Uint16 DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = Uint16.ZERO;
+ private static final Uint16 DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS = Uint16.valueOf(60);
@Mock
private ConfigurationService configurationService;
.thenReturn(THREAD_POOL_TIMEOUT);
when(configurationService.getProperty(eq(ConfigurationProperty.DEVICE_CONNECTION_RATE_LIMIT_PER_MIN.toString()),
any())).thenReturn(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN);
+ when(configurationService.getProperty(
+ eq(ConfigurationProperty.DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS.toString()),
+ any())).thenReturn(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS);
openflowProviderConfig = new OpenFlowProviderConfigImpl(configurationService);
}
assertEquals(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN, openflowProviderConfig.getDeviceConnectionRateLimitPerMin());
}
+ @Test
+ public void getDeviceConnectionHoldTimeInSeconds() {
+ assertEquals(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS,
+ openflowProviderConfig.getDeviceConnectionHoldTimeInSeconds());
+ }
+
}
\ No newline at end of file
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueueHandlerRegistration;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.connection.DeviceConnectionStatusProvider;
import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
import org.opendaylight.openflowplugin.api.openflow.connection.OutboundQueueProvider;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceDisconnectedHandler;
private DeviceDisconnectedHandler deviceDisconnectedHandler;
@Mock
private OutboundQueueProvider outboundQueueProvider;
+ @Mock
+ private DeviceConnectionStatusProvider deviceConnectionStatusProvider;
private ConnectionContextImpl connectionContext;
.thenReturn(InetSocketAddress.createUnresolved("ofp-ut.example.org", 4242));
Mockito.when(connetionAdapter.isAlive()).thenReturn(true);
- connectionContext = new ConnectionContextImpl(connetionAdapter);
+ connectionContext = new ConnectionContextImpl(connetionAdapter, deviceConnectionStatusProvider);
connectionContext.setHandshakeContext(handshakeContext);
connectionContext.setNodeId(new NodeId("ut-node:123"));
connectionContext.setOutboundQueueHandleRegistration(outboundQueueRegistration);
import com.google.common.util.concurrent.SettableFuture;
import java.math.BigInteger;
import java.net.InetSocketAddress;
+import java.time.LocalDateTime;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.mdsal.binding.api.DataBroker;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionReadyListener;
import org.opendaylight.openflowplugin.api.OFConstants;
private ArgumentCaptor<ConnectionReadyListener> connectionReadyListenerAC;
@Captor
private ArgumentCaptor<OpenflowProtocolListener> ofpListenerAC;
+ @Mock
+ DataBroker dataBroker;
private static final long ECHO_REPLY_TIMEOUT = 500;
private static final int DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = 0;
+ private static final int DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS = 60;
@Before
public void setUp() {
connectionManagerImpl = new ConnectionManagerImpl(new OpenflowProviderConfigBuilder()
.setEchoReplyTimeout(new NonZeroUint32Type(ECHO_REPLY_TIMEOUT))
.setDeviceConnectionRateLimitPerMin(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN)
- .build(), threadPool);
+ .setDeviceConnectionHoldTimeInSeconds(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS)
+ .build(), threadPool, dataBroker);
connectionManagerImpl.setDeviceConnectedHandler(deviceConnectedHandler);
final InetSocketAddress deviceAddress = InetSocketAddress.createUnresolved("yahoo", 42);
final RpcResult<HelloOutput> helloResponse = RpcResultBuilder.success((HelloOutput) null).build();
voidResponseFx.set(helloResponse);
+ //set dpn last connected time to be before dpn hold time seconds from now
+ connectionManagerImpl.getDeviceConnectionStatusProvider().addDeviceLastConnectionTime(BigInteger.TEN,
+ LocalDateTime.now().minusSeconds(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS));
+
// send hello reply
final HelloMessage hello = new HelloMessageBuilder().setVersion(OFConstants.OFP_VERSION_1_3).setXid(1L).build();
ofpListenerAC.getValue().onHelloMessage(hello);
SettableFuture.create();
Mockito.when(connection.getFeatures(any(GetFeaturesInput.class))).thenReturn(featureResponseFx);
+ //set dpn last connected time to be before dpn hold time seconds from now
+ connectionManagerImpl.getDeviceConnectionStatusProvider().addDeviceLastConnectionTime(BigInteger.TEN,
+ LocalDateTime.now().minusSeconds(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS));
// fire handshake - send hello reply
final HelloMessage hello = new HelloMessageBuilder().setVersion(OFConstants.OFP_VERSION_1_3).setXid(1L).build();
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
+import java.math.BigInteger;
+import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.mockito.runners.MockitoJUnitRunner;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.connection.DeviceConnectionStatusProvider;
import org.opendaylight.openflowplugin.api.openflow.md.core.ErrorHandler;
import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
import org.opendaylight.openflowplugin.impl.common.DeviceConnectionRateLimiter;
public class HandshakeManagerImplTest {
private static final Logger LOG = LoggerFactory.getLogger(HandshakeManagerImplTest.class);
+ private static final int DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = 0;
+ private static final int DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS = 60;
private HandshakeManagerImpl handshakeManager;
@Mock
private ErrorHandler errorHandler;
@Mock
private HandshakeListener handshakeListener;
+ @Mock
+ private DeviceConnectionStatusProvider deviceConnectionStatusProvider;
private DeviceConnectionRateLimiter deviceConnectionRateLimiter;
private int expectedErrors = 0;
- private static final int DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = 0;
-
/**
* invoked before every test method.
*/
deviceConnectionRateLimiter = new DeviceConnectionRateLimiter(new OpenflowProviderConfigBuilder()
.setDeviceConnectionRateLimitPerMin(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN).build());
handshakeManager = new HandshakeManagerImpl(adapter, OFConstants.OFP_VERSION_1_3, OFConstants.VERSION_ORDER,
- errorHandler, handshakeListener, false, deviceConnectionRateLimiter);
+ errorHandler, handshakeListener, false, deviceConnectionRateLimiter,
+ DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS, deviceConnectionStatusProvider);
- resultFeatures = RpcResultBuilder.success(new GetFeaturesOutputBuilder().build()).build();
+ resultFeatures = RpcResultBuilder.success(new GetFeaturesOutputBuilder().setDatapathId(BigInteger.ONE).build())
+ .build();
Mockito.when(adapter.hello(any(HelloInput.class)))
.thenReturn(Futures.immediateFuture(RpcResultBuilder.success((HelloOutput) null).build()));
+ Mockito.when(deviceConnectionStatusProvider.getDeviceLastConnectionTime(BigInteger.ONE))
+ .thenReturn(LocalDateTime.now().minusSeconds(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS));
}
/**
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowplugin.api.OFConstants;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.connection.DeviceConnectionStatusProvider;
import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceConnectedHandler;
import org.opendaylight.openflowplugin.impl.connection.ConnectionContextImpl;
private ConnectionAdapter connectionAdapter;
@Mock
private HandshakeContext handshakeContext;
+ @Mock
+ private DeviceConnectionStatusProvider deviceConnectionStatusProvider;
@Captor
private ArgumentCaptor<NodeId> nodeIdCaptor;
public void setUp() {
Mockito.when(connectionAdapter.barrier(ArgumentMatchers.any()))
.thenReturn(RpcResultBuilder.success(new BarrierOutputBuilder().build()).buildFuture());
- connectionContextSpy = Mockito.spy(new ConnectionContextImpl(connectionAdapter));
+ connectionContextSpy = Mockito.spy(new ConnectionContextImpl(connectionAdapter,
+ deviceConnectionStatusProvider));
Mockito.when(connectionContextSpy.getConnectionAdapter()).thenReturn(connectionAdapter);
Mockito.when(features.getDatapathId()).thenReturn(Uint64.valueOf(10));
handshakeListener = new HandshakeListenerImpl(connectionContextSpy, deviceConnectedHandler);
import org.mockito.junit.MockitoJUnitRunner;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.connection.DeviceConnectionStatusProvider;
import org.opendaylight.openflowplugin.impl.connection.ConnectionContextImpl;
import org.opendaylight.openflowplugin.impl.util.ThreadPoolLoggingExecutor;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
private ConnectionAdapter connectionAdapter;
@Mock
private FeaturesReply features;
+ @Mock
+ private DeviceConnectionStatusProvider deviceConnectionStatusProvider;
private ConnectionContext connectionContext;
private ConnectionContextImpl connectionContextGolem;
@Before
public void setUp() {
- connectionContextGolem = new ConnectionContextImpl(connectionAdapter);
+ connectionContextGolem = new ConnectionContextImpl(connectionAdapter, deviceConnectionStatusProvider);
connectionContextGolem.changeStateToWorking();
connectionContextGolem.setNodeId(NODE_ID);
connectionContextGolem.setFeatures(features);