From 32198feec954f869a760c69bf5a4ccf6f116c7bd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jakub=20T=C3=B3th?= Date: Wed, 20 Feb 2019 11:11:35 +0100 Subject: [PATCH] NETCONF-608 - Change Netconf keepalives to not send during large payload replies MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * stop to send the keepalive rpc to device while ODL is waiting/processing the response from the device * junit tests Change-Id: Ib5f4c9a8e2c4cac92887750b687ea8c50e58f3c0 Signed-off-by: Jakub Tóth Signed-off-by: Miroslav Macko --- .../netconf/sal/KeepaliveSalFacade.java | 82 +++++++++- ...KeepaliveSalFacadeResponseWaitingTest.java | 151 ++++++++++++++++++ 2 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacadeResponseWaitingTest.java diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacade.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacade.java index 59ce05edca..3b255ed009 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacade.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacade.java @@ -139,7 +139,8 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler schedule; + + public void initScheduler(final Runnable runnable) { + if (currentKeepalive != null) { + currentKeepalive.cancel(true); + } else { + LOG.trace("Keepalive does not exist."); + } + scheduleKeepalives(); + //Listening on the result should be done before the keepalive rpc will be send + final long delay = (keepaliveDelaySeconds * 1000) - 500; + schedule = executor.schedule(runnable, delay, TimeUnit.MILLISECONDS); + } + + public void stopScheduler() { + if (schedule != null) { + schedule.cancel(true); + } else { + LOG.trace("Scheduler does not exist."); + } + } + } + + private static final class ResponseWaiting implements Runnable { + + private final FluentFuture rpcResultFuture; + private final ResponseWaitingScheduler responseWaitingScheduler; + + ResponseWaiting(final ResponseWaitingScheduler responseWaitingScheduler, + final FluentFuture rpcResultFuture) { + this.responseWaitingScheduler = responseWaitingScheduler; + this.rpcResultFuture = rpcResultFuture; + } + + public void start() { + LOG.trace("Start to waiting for result."); + responseWaitingScheduler.initScheduler(this); + } + + public void stop() { + LOG.info("Stop to waiting for result."); + responseWaitingScheduler.stopScheduler(); + } + + @Override + public void run() { + if (!rpcResultFuture.isCancelled() && !rpcResultFuture.isDone()) { + LOG.trace("Waiting for result"); + responseWaitingScheduler.initScheduler(this); + } else { + LOG.trace("Result has been cancelled or done."); + } + } + } + /* * Request timeout task is called once the defaultRequestTimeoutMillis is * reached. At this moment, if the request is not yet finished, we cancel @@ -265,9 +323,11 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler rpcResultFuture; + private final ResponseWaiting responseWaiting; - RequestTimeoutTask(final FluentFuture rpcResultFuture) { + RequestTimeoutTask(final FluentFuture rpcResultFuture, final ResponseWaiting responseWaiting) { this.rpcResultFuture = rpcResultFuture; + this.responseWaiting = responseWaiting; } @Override @@ -275,6 +335,9 @@ public final class KeepaliveSalFacade implements RemoteDeviceHandler invokeRpc(@Nonnull final SchemaPath type, final NormalizedNode input) { final FluentFuture rpcResultFuture = deviceRpc.invokeRpc(type, input); - Futures.addCallback(rpcResultFuture, resetKeepaliveTask, MoreExecutors.directExecutor()); + final ResponseWaiting responseWaiting = new ResponseWaiting(responseWaitingScheduler, rpcResultFuture); + responseWaiting.start(); + rpcResultFuture.addCallback(resetKeepaliveTask, MoreExecutors.directExecutor()); - final RequestTimeoutTask timeoutTask = new RequestTimeoutTask(rpcResultFuture); + final RequestTimeoutTask timeoutTask = new RequestTimeoutTask(rpcResultFuture, responseWaiting); executor.schedule(timeoutTask, defaultRequestTimeoutMillis, TimeUnit.MILLISECONDS); return rpcResultFuture; diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacadeResponseWaitingTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacadeResponseWaitingTest.java new file mode 100644 index 0000000000..ea547490d8 --- /dev/null +++ b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/KeepaliveSalFacadeResponseWaitingTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2019 Lumina Networks, Inc. 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.netconf.sal.connect.netconf.sal; + +import static org.mockito.Mockito.after; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps.getSourceNode; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_NODEID; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_PATH; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME; + +import com.google.common.util.concurrent.SettableFuture; +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.dom.api.DOMActionService; +import org.opendaylight.mdsal.dom.api.DOMMountPointService; +import org.opendaylight.mdsal.dom.api.DOMNotification; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; +import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class KeepaliveSalFacadeResponseWaitingTest { + + private static final RemoteDeviceId REMOTE_DEVICE_ID = + new RemoteDeviceId("test", new InetSocketAddress("localhost", 22)); + private static final ContainerNode KEEPALIVE_PAYLOAD = NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, + getSourceNode(NETCONF_RUNNING_QNAME), NetconfMessageTransformUtil.EMPTY_FILTER); + + private KeepaliveSalFacade keepaliveSalFacade; + private ScheduledExecutorService executorService; + + private LocalNetconfSalFacade underlyingSalFacade; + + @Mock + private DOMRpcService deviceRpc; + + @Mock + private DOMMountPointService mountPointService; + + @Mock + private DataBroker dataBroker; + + @Mock + private NetconfDeviceCommunicator listener; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + executorService = Executors.newScheduledThreadPool(2); + + underlyingSalFacade = new LocalNetconfSalFacade(); + doNothing().when(listener).disconnect(); + keepaliveSalFacade = new KeepaliveSalFacade(REMOTE_DEVICE_ID, underlyingSalFacade, executorService, 2L, 10000L); + keepaliveSalFacade.setListener(listener); + } + + @After + public void tearDown() { + executorService.shutdown(); + } + + /** + * Not sending keepalive rpc test while the repsonse is processing. + */ + @Test + public void testKeepaliveSalResponseWaiting() { + //This settable future object will be never set to any value. The test wants to simulate waiting for the result + //of the future object. + final SettableFuture settableFuture = SettableFuture.create(); + when(deviceRpc.invokeRpc(null, null)).thenReturn(settableFuture); + + //This settable future will be used to check the invokation of keepalive RPC. Should be never invoked. + final SettableFuture keepaliveSettableFuture = SettableFuture.create(); + when(deviceRpc.invokeRpc(NETCONF_GET_CONFIG_PATH, KEEPALIVE_PAYLOAD)).thenReturn(keepaliveSettableFuture); + final DOMRpcResult keepaliveResult = new DefaultDOMRpcResult(Builders.containerBuilder().withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifier(NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME)).build()); + keepaliveSettableFuture.set(keepaliveResult); + + keepaliveSalFacade.onDeviceConnected(null, null, deviceRpc); + + //Invoke general RPC on simulated local facade without args (or with null args). Will be returned + //settableFuture variable without any set value. WaitingShaduler in keepalive sal facade should wait for any + //result from the RPC and reset keepalive scheduler. + underlyingSalFacade.invokeNullRpc(); + + //Invoking of general RPC. + verify(deviceRpc, after(2000).times(1)).invokeRpc(null, null); + + //verify the keepalive RPC invoke. Should be never happen. + verify(deviceRpc, after(2000).never()).invokeRpc(NETCONF_GET_CONFIG_PATH, KEEPALIVE_PAYLOAD); + } + + private final class LocalNetconfSalFacade implements RemoteDeviceHandler { + + private DOMRpcService localDeviceRpc; + + @Override + public void onDeviceConnected(final SchemaContext remoteSchemaContext, + final NetconfSessionPreferences netconfSessionPreferences, final DOMRpcService currentDeviceRpc, + final DOMActionService deviceAction) { + localDeviceRpc = currentDeviceRpc; + } + + @Override + public void onDeviceDisconnected() { + } + + @Override + public void onDeviceFailed(final Throwable throwable) { + } + + @Override + public void onNotification(final DOMNotification domNotification) { + } + + @Override + public void close() { + } + + public void invokeNullRpc() { + if (localDeviceRpc != null) { + localDeviceRpc.invokeRpc(null, null); + } + } + } +} + -- 2.36.6