From df6b77df7c38bcf9eb98f19a5673fa9be6c2e416 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Wed, 29 Apr 2015 16:37:04 +0200 Subject: [PATCH] Multiple clients for stresstool Added new parameter(--thread-amount) for stresstool that splits edits between multiple threads. Change-Id: I87a81a5442e32833c0c49dfd98b6475f8e699a2b Signed-off-by: Tomas Cere Signed-off-by: Maros Marsalek --- .../client/stress/AsyncExecutionStrategy.java | 16 +- .../test/tool/client/stress/Parameters.java | 8 + .../test/tool/client/stress/StressClient.java | 185 +++++++++--------- .../client/stress/StressClientCallable.java | 99 ++++++++++ .../client/stress/SyncExecutionStrategy.java | 16 +- 5 files changed, 213 insertions(+), 111 deletions(-) create mode 100644 opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/StressClientCallable.java diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/AsyncExecutionStrategy.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/AsyncExecutionStrategy.java index 7b60a17827..af352b1c2e 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/AsyncExecutionStrategy.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/AsyncExecutionStrategy.java @@ -30,24 +30,26 @@ class AsyncExecutionStrategy implements ExecutionStrategy { private final List preparedMessages; private final NetconfDeviceCommunicator sessionListener; private final List editBatches; + private final int editAmount; public AsyncExecutionStrategy(final Parameters params, final List editConfigMsgs, final NetconfDeviceCommunicator sessionListener) { this.params = params; this.preparedMessages = editConfigMsgs; this.sessionListener = sessionListener; - this.editBatches = countEditBatchSizes(params); + this.editBatches = countEditBatchSizes(params, editConfigMsgs.size()); + editAmount = editConfigMsgs.size(); } - private static List countEditBatchSizes(final Parameters params) { + private static List countEditBatchSizes(final Parameters params, final int amount) { final List editBatches = Lists.newArrayList(); - if (params.editBatchSize != params.editCount) { - final int fullBatches = params.editCount / params.editBatchSize; + if (params.editBatchSize != amount) { + final int fullBatches = amount / params.editBatchSize; for (int i = 0; i < fullBatches; i++) { editBatches.add(params.editBatchSize); } - if (params.editCount % params.editBatchSize != 0) { - editBatches.add(params.editCount % params.editBatchSize); + if (amount % params.editBatchSize != 0) { + editBatches.add(amount % params.editBatchSize); } } else { editBatches.add(params.editBatchSize); @@ -96,6 +98,6 @@ class AsyncExecutionStrategy implements ExecutionStrategy { } } - Preconditions.checkState(responseCounter.get() == params.editCount + editBatches.size(), "Not all responses were received, only %s from %s", responseCounter.get(), params.editCount + editBatches.size()); + Preconditions.checkState(responseCounter.get() == editAmount + editBatches.size(), "Not all responses were received, only %s from %s", responseCounter.get(), params.editCount + editBatches.size()); } } diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/Parameters.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/Parameters.java index 7203107901..8a9a915a08 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/Parameters.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/Parameters.java @@ -55,6 +55,9 @@ public class Parameters { @Arg(dest = "tcp-header") public String tcpHeader; + @Arg(dest = "thread-amount") + public int threadAmount; + static ArgumentParser getParser() { final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf stress client"); @@ -130,6 +133,11 @@ public class Parameters { .required(false) .dest("tcp-header"); + parser.addArgument("--thread-amount") + .type(Integer.class) + .setDefault(1) + .dest("thread-amount"); + // TODO add get-config option instead of edit + commit // TODO different edit config content diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/StressClient.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/StressClient.java index 6bf50d2c5a..0e87604cc5 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/StressClient.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/StressClient.java @@ -20,28 +20,23 @@ import com.google.common.io.Files; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.HashedWheelTimer; import io.netty.util.Timer; -import io.netty.util.concurrent.GlobalEventExecutor; import java.io.IOException; -import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import net.sourceforge.argparse4j.inf.ArgumentParser; import net.sourceforge.argparse4j.inf.ArgumentParserException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; -import org.opendaylight.controller.netconf.client.NetconfClientSession; -import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; -import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; -import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.controller.sal.connect.api.RemoteDevice; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; -import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; -import org.opendaylight.protocol.framework.NeverReconnectStrategy; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.CommitInput; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.EditConfigInput; import org.opendaylight.yangtools.yang.common.QName; @@ -80,6 +75,7 @@ public final class StressClient { " \n" + " \n" + " \n" + + " none" + " \n" + " \n" + ""); @@ -95,19 +91,35 @@ public final class StressClient { final Parameters params = parseArgs(args, Parameters.getParser()); params.validate(); - // Wait 5 seconds to allow for debugging/profiling - try { - Thread.sleep(5000); - } catch (final InterruptedException e) { - throw new RuntimeException(e); - } + // TODO remove +// try { +// Thread.sleep(10000); +// } catch (final InterruptedException e) { +// e.printStackTrace(); +// } final ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); root.setLevel(params.debug ? Level.DEBUG : Level.INFO); + int threadAmount = params.threadAmount; + LOG.info("thread amount: " + threadAmount); + int requestsPerThread = params.editCount / params.threadAmount; + LOG.info("requestsPerThread: " + requestsPerThread); + int leftoverRequests = params.editCount % params.threadAmount; + LOG.info("leftoverRequests: " + leftoverRequests); + + LOG.info("Preparing messages"); // Prepare all msgs up front - final List preparedMessages = Lists.newArrayListWithCapacity(params.editCount); + final List> allPreparedMessages = new ArrayList<>(threadAmount); + for (int i = 0; i < threadAmount; i++) { + if (i != threadAmount - 1) { + allPreparedMessages.add(new ArrayList(requestsPerThread)); + } else { + allPreparedMessages.add(new ArrayList(requestsPerThread + leftoverRequests)); + } + } + final String editContentString; try { @@ -116,63 +128,51 @@ public final class StressClient { throw new IllegalArgumentException("Cannot read content of " + params.editContent); } - for (int i = 0; i < params.editCount; i++) { - final Document msg = XmlUtil.createDocumentCopy(editBlueprint); - msg.getDocumentElement().setAttribute("message-id", Integer.toString(i)); - final NetconfMessage netconfMessage = new NetconfMessage(msg); - - final Element editContentElement; - try { - // Insert message id where needed - String specificEditContent = - editContentString.replaceAll(MSG_ID_PLACEHOLDER_REGEX, Integer.toString(i)); - - // Insert physical address where needed - specificEditContent = - specificEditContent.replaceAll(PHYS_ADDR_PLACEHOLDER_REGEX, getMac(i)); - - editContentElement = XmlUtil.readXmlToElement(specificEditContent); - final Node config = ((Element) msg.getDocumentElement().getElementsByTagName("edit-config").item(0)). - getElementsByTagName("config").item(0); - config.appendChild(msg.importNode(editContentElement, true)); - } catch (final IOException | SAXException e) { - throw new IllegalArgumentException("Edit content file is unreadable", e); + for (int i = 0; i < threadAmount; i++) { + final List preparedMessages = allPreparedMessages.get(i); + int padding = 0; + if (i == threadAmount - 1) { + padding = leftoverRequests; + } + for (int j = 0; j < requestsPerThread + padding; j++) { + LOG.debug("id: " + (i * requestsPerThread + j)); + preparedMessages.add(prepareMessage(i * requestsPerThread + j, editContentString)); } - - preparedMessages.add(netconfMessage); - } - final NioEventLoopGroup nioGroup = new NioEventLoopGroup(); final Timer timer = new HashedWheelTimer(); final NetconfClientDispatcherImpl netconfClientDispatcher = configureClientDispatcher(params, nioGroup, timer); - final NetconfDeviceCommunicator sessionListener = getSessionListener(params.getInetAddress()); - - final NetconfClientConfiguration cfg = getNetconfClientConfiguration(params, sessionListener); - - LOG.info("Connecting to netconf server {}:{}", params.ip, params.port); - final NetconfClientSession netconfClientSession; - try { - netconfClientSession = netconfClientDispatcher.createClient(cfg).get(); - } catch (final InterruptedException e) { - throw new RuntimeException(e); - } catch (final ExecutionException e) { - throw new RuntimeException("Unable to connect", e); + final List callables = new ArrayList<>(threadAmount); + for (List messages : allPreparedMessages) { + callables.add(new StressClientCallable(params, netconfClientDispatcher, messages)); } + final ExecutorService executorService = Executors.newFixedThreadPool(threadAmount); + LOG.info("Starting stress test"); final Stopwatch started = Stopwatch.createStarted(); - getExecutionStrategy(params, preparedMessages, sessionListener).invoke(); + try { + final List> futures = executorService.invokeAll(callables); + for (Future future : futures) { + try { + future.get(4L, TimeUnit.MINUTES); + } catch (ExecutionException | TimeoutException e) { + throw new RuntimeException(e); + } + } + executorService.shutdownNow(); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to execute requests", e); + } started.stop(); LOG.info("FINISHED. Execution time: {}", started); LOG.info("Requests per second: {}", (params.editCount * 1000.0 / started.elapsed(TimeUnit.MILLISECONDS))); // Cleanup - netconfClientSession.close(); timer.stop(); try { nioGroup.shutdownGracefully().get(20L, TimeUnit.SECONDS); @@ -181,29 +181,28 @@ public final class StressClient { } } - private static String getMac(final int i) { - final String hex = Integer.toHexString(i); - final Iterable macGroups = Splitter.fixedLength(2).split(hex); + private static NetconfMessage prepareMessage(final int id, final String editContentString) { + final Document msg = XmlUtil.createDocumentCopy(editBlueprint); + msg.getDocumentElement().setAttribute("message-id", Integer.toString(id)); + final NetconfMessage netconfMessage = new NetconfMessage(msg); - final int additional = 6 - Iterables.size(macGroups); - final ArrayList additionalGroups = Lists.newArrayListWithCapacity(additional); - for (int j = 0; j < additional; j++) { - additionalGroups.add("00"); + final Element editContentElement; + try { + // Insert message id where needed + String specificEditContent = editContentString.replaceAll(MSG_ID_PLACEHOLDER_REGEX, Integer.toString(id)); + + specificEditContent = + specificEditContent.replaceAll(PHYS_ADDR_PLACEHOLDER_REGEX, getMac(id)); + + editContentElement = XmlUtil.readXmlToElement(specificEditContent); + final Node config = ((Element) msg.getDocumentElement().getElementsByTagName("edit-config").item(0)). + getElementsByTagName("config").item(0); + config.appendChild(msg.importNode(editContentElement, true)); + } catch (final IOException | SAXException e) { + throw new IllegalArgumentException("Edit content file is unreadable", e); } - return Joiner.on(':').join(Iterables.concat(Iterables.transform(macGroups, new Function() { - @Override - public String apply(final String input) { - return input.length() == 1 ? input + "0" : input; - } - }), additionalGroups)); - } - private static ExecutionStrategy getExecutionStrategy(final Parameters params, final List preparedMessages, final NetconfDeviceCommunicator sessionListener) { - if(params.async) { - return new AsyncExecutionStrategy(params, preparedMessages, sessionListener); - } else { - return new SyncExecutionStrategy(params, preparedMessages, sessionListener); - } + return netconfMessage; } private static NetconfClientDispatcherImpl configureClientDispatcher(final Parameters params, final NioEventLoopGroup nioGroup, final Timer timer) { @@ -224,29 +223,21 @@ public final class StressClient { return netconfClientDispatcher; } - private static NetconfClientConfiguration getNetconfClientConfiguration(final Parameters params, final NetconfDeviceCommunicator sessionListener) { - final NetconfClientConfigurationBuilder netconfClientConfigurationBuilder = NetconfClientConfigurationBuilder.create(); - netconfClientConfigurationBuilder.withSessionListener(sessionListener); - netconfClientConfigurationBuilder.withAddress(params.getInetAddress()); - if(params.tcpHeader != null) { - final String header = params.tcpHeader.replaceAll("\"", "").trim() + "\n"; - netconfClientConfigurationBuilder.withAdditionalHeader(new NetconfHelloMessageAdditionalHeader(null, null, null, null, null) { - @Override - public String toFormattedString() { - LOG.debug("Sending TCP header {}", header); - return header; - } - }); - } - netconfClientConfigurationBuilder.withProtocol(params.ssh ? NetconfClientConfiguration.NetconfClientProtocol.SSH : NetconfClientConfiguration.NetconfClientProtocol.TCP); - netconfClientConfigurationBuilder.withConnectionTimeoutMillis(20000L); - netconfClientConfigurationBuilder.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000)); - return netconfClientConfigurationBuilder.build(); - } + private static String getMac(final int i) { + final String hex = Integer.toHexString(i); + final Iterable macGroups = Splitter.fixedLength(2).split(hex); - static NetconfDeviceCommunicator getSessionListener(final InetSocketAddress inetAddress) { - final RemoteDevice loggingRemoteDevice = new LoggingRemoteDevice(); - return new NetconfDeviceCommunicator(new RemoteDeviceId("secure-test", inetAddress), loggingRemoteDevice); + final int additional = 6 - Iterables.size(macGroups); + final ArrayList additionalGroups = Lists.newArrayListWithCapacity(additional); + for (int j = 0; j < additional; j++) { + additionalGroups.add("00"); + } + return Joiner.on(':').join(Iterables.concat(Iterables.transform(macGroups, new Function() { + @Override + public String apply(final String input) { + return input.length() == 1 ? input + "0" : input; + } + }), additionalGroups)); } private static Parameters parseArgs(final String[] args, final ArgumentParser parser) { @@ -263,7 +254,7 @@ public final class StressClient { } - private static class LoggingRemoteDevice implements RemoteDevice { + static class LoggingRemoteDevice implements RemoteDevice { @Override public void onRemoteSessionUp(final NetconfSessionPreferences remoteSessionCapabilities, final NetconfDeviceCommunicator netconfDeviceCommunicator) { LOG.info("Session established"); diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/StressClientCallable.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/StressClientCallable.java new file mode 100644 index 0000000000..a4c5c5702d --- /dev/null +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/StressClientCallable.java @@ -0,0 +1,99 @@ +/* + * 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.controller.netconf.test.tool.client.stress; + +import io.netty.util.concurrent.GlobalEventExecutor; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl; +import org.opendaylight.controller.netconf.client.NetconfClientSession; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration; +import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder; +import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; +import org.opendaylight.controller.sal.connect.api.RemoteDevice; +import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator; +import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.protocol.framework.NeverReconnectStrategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StressClientCallable implements Callable{ + + private static final Logger LOG = LoggerFactory.getLogger(StressClientCallable.class); + + private Parameters params; + private final NetconfDeviceCommunicator sessionListener; + private final NetconfClientDispatcherImpl netconfClientDispatcher; + private final NetconfClientConfiguration cfg; + private final NetconfClientSession netconfClientSession; + private final ExecutionStrategy executionStrategy; + + public StressClientCallable(final Parameters params, + final NetconfClientDispatcherImpl netconfClientDispatcher, + final List preparedMessages) { + this.params = params; + this.sessionListener = getSessionListener(params.getInetAddress()); + this.netconfClientDispatcher = netconfClientDispatcher; + cfg = getNetconfClientConfiguration(this.params, this.sessionListener); + + LOG.info("Connecting to netconf server {}:{}", params.ip, params.port); + try { + netconfClientSession = netconfClientDispatcher.createClient(cfg).get(); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } catch (final ExecutionException e) { + throw new RuntimeException("Unable to connect", e); + } + executionStrategy = getExecutionStrategy(params, preparedMessages, sessionListener); + } + + @Override + public Boolean call() throws Exception { + executionStrategy.invoke(); + netconfClientSession.close(); + return true; + } + + private static ExecutionStrategy getExecutionStrategy(final Parameters params, final List preparedMessages, final NetconfDeviceCommunicator sessionListener) { + if(params.async) { + return new AsyncExecutionStrategy(params, preparedMessages, sessionListener); + } else { + return new SyncExecutionStrategy(params, preparedMessages, sessionListener); + } + } + + private static NetconfDeviceCommunicator getSessionListener(final InetSocketAddress inetAddress) { + final RemoteDevice loggingRemoteDevice = new StressClient.LoggingRemoteDevice(); + return new NetconfDeviceCommunicator(new RemoteDeviceId("secure-test", inetAddress), loggingRemoteDevice); + } + + private static NetconfClientConfiguration getNetconfClientConfiguration(final Parameters params, final NetconfDeviceCommunicator sessionListener) { + final NetconfClientConfigurationBuilder netconfClientConfigurationBuilder = NetconfClientConfigurationBuilder.create(); + netconfClientConfigurationBuilder.withSessionListener(sessionListener); + netconfClientConfigurationBuilder.withAddress(params.getInetAddress()); + if(params.tcpHeader != null) { + final String header = params.tcpHeader.replaceAll("\"", "").trim() + "\n"; + netconfClientConfigurationBuilder.withAdditionalHeader(new NetconfHelloMessageAdditionalHeader(null, null, null, null, null) { + @Override + public String toFormattedString() { + LOG.debug("Sending TCP header {}", header); + return header; + } + }); + } + netconfClientConfigurationBuilder.withProtocol(params.ssh ? NetconfClientConfiguration.NetconfClientProtocol.SSH : NetconfClientConfiguration.NetconfClientProtocol.TCP); + netconfClientConfigurationBuilder.withConnectionTimeoutMillis(20000L); + netconfClientConfigurationBuilder.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000)); + return netconfClientConfigurationBuilder.build(); + } +} diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/SyncExecutionStrategy.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/SyncExecutionStrategy.java index 34142a7f2a..40f39022e6 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/SyncExecutionStrategy.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/SyncExecutionStrategy.java @@ -31,24 +31,26 @@ class SyncExecutionStrategy implements ExecutionStrategy { private final List preparedMessages; private final NetconfDeviceCommunicator sessionListener; private final List editBatches; + private final int editAmount; public SyncExecutionStrategy(final Parameters params, final List preparedMessages, final NetconfDeviceCommunicator sessionListener) { this.params = params; this.preparedMessages = preparedMessages; this.sessionListener = sessionListener; - editBatches = countEditBatchSizes(params); + this.editBatches = countEditBatchSizes(params, preparedMessages.size()); + editAmount = preparedMessages.size(); } - private static List countEditBatchSizes(final Parameters params) { + private static List countEditBatchSizes(final Parameters params, final int amount) { final List editBatches = Lists.newArrayList(); - if (params.editBatchSize != params.editCount) { - final int fullBatches = params.editCount / params.editBatchSize; + if (params.editBatchSize != amount) { + final int fullBatches = amount / params.editBatchSize; for (int i = 0; i < fullBatches; i++) { editBatches.add(params.editBatchSize); } - if (params.editCount % params.editBatchSize != 0) { - editBatches.add(params.editCount % params.editBatchSize); + if (amount % params.editBatchSize != 0) { + editBatches.add(amount % params.editBatchSize); } } else { editBatches.add(params.editBatchSize); @@ -82,7 +84,7 @@ class SyncExecutionStrategy implements ExecutionStrategy { sessionListener.sendRequest(StressClient.COMMIT_MSG, StressClient.COMMIT_QNAME)); } - Preconditions.checkState(responseCounter.get() == params.editCount + editBatches.size(), "Not all responses were received, only %s from %s", responseCounter.get(), params.editCount + editBatches.size()); + Preconditions.checkState(responseCounter.get() == editAmount + editBatches.size(), "Not all responses were received, only %s from %s", responseCounter.get(), params.editCount + editBatches.size()); } private void waitForResponse(AtomicInteger responseCounter, final ListenableFuture> netconfMessageFuture) { -- 2.36.6