From: Somashekhar Javalagi Date: Wed, 28 Mar 2018 09:37:36 +0000 (+0530) Subject: OPNFLWPLUG-992 : Device connection rate limiter implemenation X-Git-Tag: release/fluorine~68 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=ce31b87f8529f9e607472effb6bbf84c4f2bbed2;hp=d2d32cbb3371be0d42e58136663f4d53b23dd7f4;p=openflowplugin.git OPNFLWPLUG-992 : Device connection rate limiter implemenation 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 --- diff --git a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/configuration/ConfigurationProperty.java b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/configuration/ConfigurationProperty.java index f212c104de..bcc48b4de2 100644 --- a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/configuration/ConfigurationProperty.java +++ b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/configuration/ConfigurationProperty.java @@ -111,7 +111,11 @@ public enum ConfigurationProperty { /** * 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 KEY_VALUE_MAP; diff --git a/openflowplugin-api/src/main/yang/openflow-provider-config.yang b/openflowplugin-api/src/main/yang/openflow-provider-config.yang index 213d0b356f..21bd712762 100644 --- a/openflowplugin-api/src/main/yang/openflow-provider-config.yang +++ b/openflowplugin-api/src/main/yang/openflow-provider-config.yang @@ -194,5 +194,13 @@ module openflow-provider-config { 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; + } } } diff --git a/openflowplugin-blueprint-config/src/main/resources/initial/openflowplugin.cfg b/openflowplugin-blueprint-config/src/main/resources/initial/openflowplugin.cfg index 1d04928e10..68f7e76d9e 100644 --- a/openflowplugin-blueprint-config/src/main/resources/initial/openflowplugin.cfg +++ b/openflowplugin-blueprint-config/src/main/resources/initial/openflowplugin.cfg @@ -122,6 +122,14 @@ # # 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 # diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/common/DeviceConnectionRateLimiter.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/common/DeviceConnectionRateLimiter.java new file mode 100644 index 0000000000..5d3862d557 --- /dev/null +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/common/DeviceConnectionRateLimiter.java @@ -0,0 +1,41 @@ +/* + * 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; + + 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 diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImpl.java index 15ef52d694..a1616dddee 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImpl.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImpl.java @@ -94,6 +94,8 @@ public class ConfigurationServiceFactoryImpl implements ConfigurationServiceFact 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); diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImpl.java index 81d6cf25d5..1adf381f77 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImpl.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImpl.java @@ -179,4 +179,10 @@ public class OpenFlowProviderConfigImpl implements OpenflowProviderConfig { return OpenflowProviderConfig.class; } + @Override + public Integer getDeviceConnectionRateLimitPerMin() { + return service.getProperty(ConfigurationProperty.DEVICE_CONNECTION_RATE_LIMIT_PER_MIN.toString(), + Integer::valueOf); + } + } 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 index 9f35df5ed1..61d2424a9c 100644 --- 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 @@ -20,6 +20,7 @@ import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceConnec 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; @@ -37,11 +38,13 @@ public class ConnectionManagerImpl implements ConnectionManager { 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 @@ -78,7 +81,8 @@ public class ConnectionManagerImpl implements ConnectionManager { 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; } diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeManagerImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeManagerImpl.java index 5d32cf6467..a4dbfc649e 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeManagerImpl.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeManagerImpl.java @@ -24,6 +24,7 @@ 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.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; @@ -57,6 +58,8 @@ public class HandshakeManagerImpl implements HandshakeManager { private boolean useVersionBitmap; // not final just for unit test + private final DeviceConnectionRateLimiter deviceConnectionRateLimiter; + /** * Constructor. * @@ -68,13 +71,15 @@ public class HandshakeManagerImpl implements HandshakeManager { * @param useVersionBitmap should use negotiation bit map */ public HandshakeManagerImpl(ConnectionAdapter connectionAdapter, Short highestVersion, List 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 @@ -385,6 +390,12 @@ public class HandshakeManagerImpl implements HandshakeManager { 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()); diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImplTest.java index f75bc45f41..c7f8e4052e 100644 --- a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImplTest.java +++ b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImplTest.java @@ -85,6 +85,7 @@ public class OpenFlowPluginProviderImplTest { 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 { @@ -109,6 +110,8 @@ public class OpenFlowPluginProviderImplTest { .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 diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImplTest.java index a18da729a0..e5010013ae 100644 --- a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImplTest.java +++ b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImplTest.java @@ -37,7 +37,7 @@ import org.osgi.service.cm.ConfigurationAdmin; @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; @@ -55,6 +55,7 @@ public class ConfigurationServiceFactoryImplTest { 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; @@ -101,6 +102,7 @@ public class ConfigurationServiceFactoryImplTest { 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 properties = new Hashtable<>(); properties.put(ConfigurationProperty.IS_STATISTICS_POLLING_ON.toString(), IS_STATISTICS_POLLING_ON); diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImplTest.java index 46f0da45b8..55af2a5f47 100644 --- a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImplTest.java +++ b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImplTest.java @@ -40,6 +40,7 @@ public class OpenFlowProviderConfigImplTest { 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; @@ -79,6 +80,8 @@ public class OpenFlowProviderConfigImplTest { .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); } @@ -168,4 +171,9 @@ public class OpenFlowProviderConfigImplTest { 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 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 index 1dfd70ea5d..cec8c64a36 100644 --- 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 @@ -61,6 +61,7 @@ public class ConnectionManagerImplTest { private ArgumentCaptor ofpListenerAC; private static final long ECHO_REPLY_TIMEOUT = 500; + private static final int DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = 0; @Before public void setUp() { @@ -70,6 +71,7 @@ public class ConnectionManagerImplTest { connectionManagerImpl = new ConnectionManagerImpl(new OpenflowProviderConfigBuilder() .setEchoReplyTimeout(new NonZeroUint32Type(ECHO_REPLY_TIMEOUT)) + .setDeviceConnectionRateLimitPerMin(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN) .build(), threadPool); connectionManagerImpl.setDeviceConnectedHandler(deviceConnectedHandler); diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/connection/HandshakeManagerImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/connection/HandshakeManagerImplTest.java index 83a4c5df04..650a7b5160 100644 --- a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/connection/HandshakeManagerImplTest.java +++ b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/connection/HandshakeManagerImplTest.java @@ -25,6 +25,7 @@ import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter; 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; @@ -33,6 +34,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731 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; @@ -54,19 +56,25 @@ public class HandshakeManagerImplTest { @Mock private HandshakeListener handshakeListener; + private DeviceConnectionRateLimiter deviceConnectionRateLimiter; + private RpcResult 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();