By default the device connection ratelimitter wll be disabled.
device-connection-rate-limit-per-min is the property available in
openflowplugin.cfg, which can be changed to control the rate at
which number of devices connecting the controller. This will be
useful in reducing load on the controller if there is more number
of switches connected at the time during cluster reboot.
Default value is set to 0, which disables device connection ratelimitter.
Change-Id: I02a53719114e63748ffaa340650927f7b11d6dc1
Signed-off-by: Somashekhar Javalagi <somashekhar.manohara.javalagi@ericsson.com>
/**
* Enable or disable equal role functionality.
*/
- ENABLE_EQUAL_ROLE;
+ ENABLE_EQUAL_ROLE,
+ /**
+ * Device connection rate limit property type.
+ */
+ DEVICE_CONNECTION_RATE_LIMIT_PER_MIN;
private static final Map<String, ConfigurationProperty> KEY_VALUE_MAP;
type boolean;
default "false";
}
+
+ leaf device-connection-rate-limit-per-min {
+ description "To limit the number of datapath nodes connecting to odl controller
+ instance per minute. Default value 0 indicates that the device connection rate
+ limiter will be disabled.";
+ type uint16;
+ default 0;
+ }
}
}
#
# use-single-layer-serialization=true
+#
+# To limit the number of datapath nodes to be connected to the controller instance
+# per minute. When the default value of zero is set, then the device connection rate
+# limitter will be disabled. If it is set to any value, then only those many
+# number of datapath nodes are allowed to connect to the controller in a minute
+#
+# device-connection-rate-limit-per-min=0
+
#############################################################################
# #
# Forwarding Rule Manager Application Configuration #
--- /dev/null
+/*
+ * Copyright (c) 2018 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.impl.common;
+
+import com.google.common.util.concurrent.RateLimiter;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.provider.config.rev160510.OpenflowProviderConfig;
+
+
+public class DeviceConnectionRateLimiter {
+ private final boolean doRateLimit;
+ private final AtomicReference<RateLimiter> rateLimiter;
+
+ public DeviceConnectionRateLimiter(final OpenflowProviderConfig config) {
+ int deviceConnectionRateLimitPerMin = config.getDeviceConnectionRateLimitPerMin();
+ double rateLimiterSize;
+ if (deviceConnectionRateLimitPerMin == 0) {
+ doRateLimit = false;
+ rateLimiterSize = 1;
+ } else {
+ doRateLimit = true;
+ rateLimiterSize = 1d / (60d / deviceConnectionRateLimitPerMin);
+ }
+ rateLimiter = new AtomicReference<>(RateLimiter.create(rateLimiterSize));
+
+ }
+
+ public boolean tryAquire() {
+ if (doRateLimit) {
+ return rateLimiter.get().tryAcquire(0, TimeUnit.SECONDS);
+ }
+ return true;
+ }
+}
\ No newline at end of file
providerConfig.getThreadPoolMaxThreads().getValue().toString())
.put(ConfigurationProperty.THREAD_POOL_TIMEOUT.toString(),
providerConfig.getThreadPoolTimeout().toString())
+ .put(ConfigurationProperty.DEVICE_CONNECTION_RATE_LIMIT_PER_MIN.toString(),
+ providerConfig.getDeviceConnectionRateLimitPerMin().toString())
.build());
LOG.info("Loading configuration from '{}' configuration file", OFConstants.CONFIG_FILE_ID);
return OpenflowProviderConfig.class;
}
+ @Override
+ public Integer getDeviceConnectionRateLimitPerMin() {
+ return service.getProperty(ConfigurationProperty.DEVICE_CONNECTION_RATE_LIMIT_PER_MIN.toString(),
+ Integer::valueOf);
+ }
+
}
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceDisconnectedHandler;
import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
+import org.opendaylight.openflowplugin.impl.common.DeviceConnectionRateLimiter;
import org.opendaylight.openflowplugin.impl.connection.listener.ConnectionReadyListenerImpl;
import org.opendaylight.openflowplugin.impl.connection.listener.HandshakeListenerImpl;
import org.opendaylight.openflowplugin.impl.connection.listener.OpenflowProtocolListenerInitialImpl;
private DeviceConnectedHandler deviceConnectedHandler;
private final OpenflowProviderConfig config;
private final ExecutorService executorService;
+ private final DeviceConnectionRateLimiter deviceConnectionRateLimiter;
private DeviceDisconnectedHandler deviceDisconnectedHandler;
public ConnectionManagerImpl(final OpenflowProviderConfig config, final ExecutorService executorService) {
this.config = config;
this.executorService = executorService;
+ this.deviceConnectionRateLimiter = new DeviceConnectionRateLimiter(config);
}
@Override
final HandshakeListener handshakeListener) {
HandshakeManagerImpl handshakeManager = new HandshakeManagerImpl(connectionAdapter,
OFConstants.VERSION_ORDER.get(0),
- OFConstants.VERSION_ORDER, new ErrorHandlerSimpleImpl(), handshakeListener, BITMAP_NEGOTIATION_ENABLED);
+ OFConstants.VERSION_ORDER, new ErrorHandlerSimpleImpl(), handshakeListener, BITMAP_NEGOTIATION_ENABLED,
+ deviceConnectionRateLimiter);
return handshakeManager;
}
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;
+import org.opendaylight.openflowplugin.impl.common.DeviceConnectionRateLimiter;
import org.opendaylight.openflowplugin.impl.util.MessageFactory;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
private boolean useVersionBitmap; // not final just for unit test
+ private final DeviceConnectionRateLimiter deviceConnectionRateLimiter;
+
/**
* Constructor.
*
* @param useVersionBitmap should use negotiation bit map
*/
public HandshakeManagerImpl(ConnectionAdapter connectionAdapter, Short highestVersion, List<Short> versionOrder,
- ErrorHandler errorHandler, HandshakeListener handshakeListener, boolean useVersionBitmap) {
+ ErrorHandler errorHandler, HandshakeListener handshakeListener,
+ boolean useVersionBitmap, DeviceConnectionRateLimiter deviceConnectionRateLimiter) {
this.highestVersion = highestVersion;
this.versionOrder = versionOrder;
this.connectionAdapter = connectionAdapter;
this.errorHandler = errorHandler;
this.handshakeListener = handshakeListener;
this.useVersionBitmap = useVersionBitmap;
+ this.deviceConnectionRateLimiter = deviceConnectionRateLimiter;
}
@Override
LOG.trace("features are back");
if (rpcFeatures.isSuccessful()) {
GetFeaturesOutput featureOutput = rpcFeatures.getResult();
+ if (!deviceConnectionRateLimiter.tryAquire()) {
+ LOG.warn("Openflowplugin hit the device connection rate limit threshold. Denying"
+ + " the connection from device {}", featureOutput.getDatapathId());
+ connectionAdapter.disconnect();
+ return;
+ }
LOG.debug("obtained features: datapathId={}", featureOutput.getDatapathId());
LOG.debug("obtained features: auxiliaryId={}", featureOutput.getAuxiliaryId());
private static final long THREAD_POOL_TIMEOUT = 60;
private static final long BASIC_TIMER_DELAY = 1L;
private static final boolean USE_SINGLE_LAYER_SERIALIZATION = false;
+ private static final int DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = 0;
@Before
public void setUp() throws Exception {
.thenReturn(GLOBAL_NOTIFICATION_QUOTA);
when(configurationService.getProperty(eq(ConfigurationProperty.BASIC_TIMER_DELAY.toString()), any()))
.thenReturn(BASIC_TIMER_DELAY);
+ when(configurationService.getProperty(eq(ConfigurationProperty.DEVICE_CONNECTION_RATE_LIMIT_PER_MIN.toString()),
+ any())).thenReturn(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN);
}
@Test
@RunWith(MockitoJUnitRunner.class)
public class ConfigurationServiceFactoryImplTest {
- private static final int CONFIG_PROP_COUNT = 22;
+ private static final int CONFIG_PROP_COUNT = 23;
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_MIN_THREADS_UPDATE = 4;
private static final int THREAD_POOL_MAX_THREADS = 1000;
private static final long THREAD_POOL_TIMEOUT = 60;
+ private static final int DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = 0;
@Mock
private OpenflowProviderConfig config;
when(config.getThreadPoolMinThreads()).thenReturn(THREAD_POOL_MIN_THREADS);
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);
final Dictionary<String, Object> properties = new Hashtable<>();
properties.put(ConfigurationProperty.IS_STATISTICS_POLLING_ON.toString(), IS_STATISTICS_POLLING_ON);
private static final Integer THREAD_POOL_MIN_THREADS = 3;
private static final Integer THREAD_POOL_MAX_THREADS = 1000;
private static final Long THREAD_POOL_TIMEOUT = 60L;
+ private static final Integer DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = 0;
@Mock
private ConfigurationService configurationService;
.thenReturn(THREAD_POOL_MAX_THREADS);
when(configurationService.getProperty(eq(ConfigurationProperty.THREAD_POOL_TIMEOUT.toString()), any()))
.thenReturn(THREAD_POOL_TIMEOUT);
+ when(configurationService.getProperty(eq(ConfigurationProperty.DEVICE_CONNECTION_RATE_LIMIT_PER_MIN.toString()),
+ any())).thenReturn(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN);
openflowProviderConfig = new OpenFlowProviderConfigImpl(configurationService);
}
assertEquals(USE_SINGLE_LAYER_SERIALIZATION, openflowProviderConfig.isUseSingleLayerSerialization());
}
+ @Test
+ public void getDeviceConnectionRateLimitPerMin() throws Exception {
+ assertEquals(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN, openflowProviderConfig.getDeviceConnectionRateLimitPerMin());
+ }
+
}
\ No newline at end of file
private ArgumentCaptor<OpenflowProtocolListener> ofpListenerAC;
private static final long ECHO_REPLY_TIMEOUT = 500;
+ private static final int DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = 0;
@Before
public void setUp() {
connectionManagerImpl = new ConnectionManagerImpl(new OpenflowProviderConfigBuilder()
.setEchoReplyTimeout(new NonZeroUint32Type(ECHO_REPLY_TIMEOUT))
+ .setDeviceConnectionRateLimitPerMin(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN)
.build(), threadPool);
connectionManagerImpl.setDeviceConnectedHandler(deviceConnectedHandler);
import org.opendaylight.openflowplugin.api.OFConstants;
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;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.HelloElementType;
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.HelloMessageBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.hello.Elements;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.hello.ElementsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.provider.config.rev160510.OpenflowProviderConfigBuilder;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
@Mock
private HandshakeListener handshakeListener;
+ private DeviceConnectionRateLimiter deviceConnectionRateLimiter;
+
private RpcResult<GetFeaturesOutput> resultFeatures;
private final long helloXid = 42L;
private int expectedErrors = 0;
+ private static final int DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = 0;
+
/**
* invoked before every test method.
*/
@Before
public void setUp() {
+ 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);
+ errorHandler, handshakeListener, false, deviceConnectionRateLimiter);
resultFeatures = RpcResultBuilder.success(new GetFeaturesOutputBuilder().build()).build();