import org.opendaylight.netconf.client.NetconfClientDispatcher;
import org.opendaylight.netconf.client.NetconfClientSession;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemas;
import org.opendaylight.netconf.client.mdsal.api.CredentialProvider;
import org.opendaylight.netconf.client.mdsal.api.DeviceActionFactory;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
-import org.opendaylight.netconf.nettyutil.ReconnectFuture;
import org.opendaylight.netconf.topology.spi.NetconfNodeUtils;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import org.osgi.service.component.annotations.Activate;
return activateChannel(clientConfiguration);
}
- @Override
- public ReconnectFuture createReconnectingClient(final NetconfReconnectingClientConfiguration clientConfiguration) {
- return new SingleReconnectFuture(eventExecutor, activateChannel(clientConfiguration));
- }
-
private Future<NetconfClientSession> activateChannel(final NetconfClientConfiguration conf) {
final InetSocketAddress remoteAddr = conf.getAddress();
final CallHomeMountSessionContext context = sessionManager().getByAddress(remoteAddr);
+++ /dev/null
-/*
- * Copyright (c) 2021 PANTHEON.tech, s.r.o. 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.netconf.callhome.mount;
-
-import static java.util.Objects.requireNonNull;
-
-import io.netty.util.concurrent.DefaultPromise;
-import io.netty.util.concurrent.EventExecutor;
-import io.netty.util.concurrent.Future;
-import org.opendaylight.netconf.client.NetconfClientSession;
-import org.opendaylight.netconf.nettyutil.ReconnectFuture;
-import org.opendaylight.yangtools.yang.common.Empty;
-
-final class SingleReconnectFuture extends DefaultPromise<Empty> implements ReconnectFuture {
- private final Future<NetconfClientSession> sessionFuture;
-
- SingleReconnectFuture(final EventExecutor eventExecutor, final Future<NetconfClientSession> sessionFuture) {
- super(eventExecutor);
- this.sessionFuture = requireNonNull(sessionFuture);
- sessionFuture.addListener(future -> {
- if (!isDone()) {
- if (future.isCancelled()) {
- cancel(false);
- } else if (future.isSuccess()) {
- setSuccess(Empty.value());
- } else {
- setFailure(future.cause());
- }
- }
- });
- }
-
- @Override
- public boolean cancel(final boolean mayInterruptIfRunning) {
- if (super.cancel(mayInterruptIfRunning)) {
- if (!sessionFuture.isDone()) {
- sessionFuture.cancel(mayInterruptIfRunning);
- }
- return true;
- }
- return false;
- }
-
- @Override
- public Future<?> firstSessionFuture() {
- return sessionFuture;
- }
-}
import org.opendaylight.netconf.client.mdsal.api.CredentialProvider;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
NetconfClientConfiguration.NetconfClientProtocol.SSH;
final NetconfHelloMessageAdditionalHeader additionalHeader = mock(NetconfHelloMessageAdditionalHeader.class);
final NetconfClientSessionListener sessionListener = mock(NetconfClientSessionListener.class);
- final ReconnectStrategy reconnectStrategy = mock(ReconnectStrategy.class);
final AuthenticationHandler authHandler = mock(AuthenticationHandler.class);
return NetconfClientConfigurationBuilder.create().withProtocol(protocol).withAddress(address)
.withConnectionTimeoutMillis(0).withAdditionalHeader(additionalHeader)
- .withSessionListener(sessionListener).withReconnectStrategy(reconnectStrategy)
- .withAuthHandler(authHandler).build();
+ .withSessionListener(sessionListener).withAuthHandler(authHandler).build();
}
@Test
import org.opendaylight.netconf.client.NetconfClientDispatcher;
import org.opendaylight.netconf.client.NetconfClientSessionListener;
import org.opendaylight.netconf.client.SslHandlerFactory;
-import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
+import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemas;
import org.opendaylight.netconf.client.mdsal.api.CredentialProvider;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
.setSleepFactor(Decimal64.valueOf("1.5"))
.setConnectionTimeoutMillis(Uint32.valueOf(20000));
- final NetconfReconnectingClientConfiguration configuration =
- spyTopology.getClientConfig(sessionListener, nodeBuilder.setTcpOnly(true).build(), NODE_ID);
- assertEquals(NetconfClientConfiguration.NetconfClientProtocol.TCP, configuration.getProtocol());
+ final var configuration = spyTopology.getClientConfig(nodeBuilder.setTcpOnly(true).build(), NODE_ID)
+ .withSessionListener(sessionListener).build();
+ assertEquals(NetconfClientProtocol.TCP, configuration.getProtocol());
assertNotNull(configuration.getAuthHandler());
assertNull(configuration.getSslHandlerFactory());
- final NetconfReconnectingClientConfiguration configuration2 =
- spyTopology.getClientConfig(sessionListener, nodeBuilder.setTcpOnly(false).build(), NODE_ID);
- assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, configuration2.getProtocol());
+ final var configuration2 = spyTopology.getClientConfig(nodeBuilder.setTcpOnly(false).build(), NODE_ID)
+ .withSessionListener(sessionListener).build();
+ assertEquals(NetconfClientProtocol.SSH, configuration2.getProtocol());
assertNotNull(configuration2.getAuthHandler());
assertNull(configuration2.getSslHandlerFactory());
- final NetconfReconnectingClientConfiguration configuration3 =
- spyTopology.getClientConfig(sessionListener, nodeBuilder
- .setProtocol(new ProtocolBuilder().setName(Name.SSH).build()).build(), NODE_ID);
- assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, configuration3.getProtocol());
+ final var configuration3 = spyTopology.getClientConfig(
+ nodeBuilder.setProtocol(new ProtocolBuilder().setName(Name.SSH).build()).build(), NODE_ID)
+ .withSessionListener(sessionListener).build();
+ assertEquals(NetconfClientProtocol.SSH, configuration3.getProtocol());
assertNotNull(configuration3.getAuthHandler());
assertNull(configuration3.getSslHandlerFactory());
final var sslHandlerFactory = mock(SslHandlerFactory.class);
doReturn(sslHandlerFactory).when(sslHandlerFactoryProvider).getSslHandlerFactory(null);
- final NetconfReconnectingClientConfiguration configuration4 =
- spyTopology.getClientConfig(sessionListener, nodeBuilder
- .setProtocol(new ProtocolBuilder().setName(Name.TLS).build()).build(), NODE_ID);
- assertEquals(NetconfClientConfiguration.NetconfClientProtocol.TLS, configuration4.getProtocol());
+ final var configuration4 = spyTopology.getClientConfig(
+ nodeBuilder.setProtocol(new ProtocolBuilder().setName(Name.TLS).build()).build(), NODE_ID)
+ .withSessionListener(sessionListener).build();
+ assertEquals(NetconfClientProtocol.TLS, configuration4.getProtocol());
assertNull(configuration4.getAuthHandler());
assertSame(sslHandlerFactory, configuration4.getSslHandlerFactory());
}
import com.google.common.util.concurrent.SettableFuture;
import com.typesafe.config.ConfigFactory;
import io.netty.util.concurrent.EventExecutor;
+import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.io.File;
import java.util.Iterator;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
import org.opendaylight.netconf.client.mdsal.impl.DefaultSchemaResourceManager;
-import org.opendaylight.netconf.nettyutil.ReconnectFuture;
import org.opendaylight.netconf.topology.singleton.impl.utils.ClusteringRpcException;
import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
setupSlave();
yangNodeInstanceId = bindingToNormalized.toYangInstanceIdentifier(NODE_INSTANCE_ID);
- final var future = mock(ReconnectFuture.class);
- doReturn(future).when(mockClientDispatcher).createReconnectingClient(any());
- doReturn(future).when(future).firstSessionFuture();
+ doReturn(mock(Future.class)).when(mockClientDispatcher).createClient(any());
LOG.info("****** Setup complete");
}
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import io.netty.util.concurrent.EventExecutor;
-import java.net.URL;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.ExecutionException;
+import org.checkerframework.checker.lock.qual.Holding;
import org.opendaylight.aaa.encrypt.AAAEncryptionService;
import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
import org.opendaylight.controller.config.threadpool.ThreadPool;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMMountPointService;
import org.opendaylight.netconf.client.NetconfClientDispatcher;
-import org.opendaylight.netconf.client.NetconfClientSessionListener;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
+import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
import org.opendaylight.netconf.client.mdsal.DatastoreBackedPublicKeyAuth;
-import org.opendaylight.netconf.client.mdsal.LibraryModulesSchemas;
-import org.opendaylight.netconf.client.mdsal.LibrarySchemaSourceProvider;
-import org.opendaylight.netconf.client.mdsal.NetconfDevice.SchemaResourcesDTO;
-import org.opendaylight.netconf.client.mdsal.NetconfDeviceBuilder;
-import org.opendaylight.netconf.client.mdsal.NetconfDeviceCommunicator;
-import org.opendaylight.netconf.client.mdsal.SchemalessNetconfDevice;
import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemas;
import org.opendaylight.netconf.client.mdsal.api.CredentialProvider;
import org.opendaylight.netconf.client.mdsal.api.DeviceActionFactory;
-import org.opendaylight.netconf.client.mdsal.api.RemoteDevice;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
-import org.opendaylight.netconf.client.mdsal.spi.KeepaliveSalFacade;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategyFactory;
-import org.opendaylight.netconf.nettyutil.TimedReconnectStrategyFactory;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev230430.connection.parameters.Protocol.Name;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev230430.credentials.Credentials;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev230430.credentials.credentials.KeyAuth;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.Empty;
-import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
-import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractNetconfTopology {
private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
- private final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
+ private final HashMap<NodeId, NetconfNodeHandler> activeConnectors = new HashMap<>();
private final NetconfClientDispatcher clientDispatcher;
private final EventExecutor eventExecutor;
private final DeviceActionFactory deviceActionFactory;
}
protected final synchronized void deleteAllNodes() {
- activeConnectors.values().forEach(NetconfConnectorDTO::close);
+ activeConnectors.values().forEach(NetconfNodeHandler::close);
activeConnectors.clear();
}
+ @Holding("this")
protected final void setupConnection(final NodeId nodeId, final Node configNode) {
- final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
- final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
+ final var netconfNode = configNode.augmentation(NetconfNode.class);
+ final var nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
requireNonNull(netconfNode.getHost());
requireNonNull(netconfNode.getPort());
- final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode, nodeOptional);
- final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
- final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
- final NetconfReconnectingClientConfiguration clientConfig =
- getClientConfig(netconfClientSessionListener, netconfNode, nodeId);
- final ListenableFuture<Empty> future =
- deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
+ // Instantiate the handler ...
+ final var deviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, netconfNode);
+ final var deviceSalFacade = createSalFacade(deviceId, netconfNode.requireLockDatastore());
+ final var nodeHandler = new NetconfNodeHandler(clientDispatcher, eventExecutor, keepaliveExecutor.getExecutor(),
+ baseSchemas, schemaManager, processingExecutor, deviceActionFactory,
+ deviceSalFacade, deviceId, nodeId, netconfNode, nodeOptional, getClientConfig(netconfNode, nodeId));
- activeConnectors.put(nodeId, deviceCommunicatorDTO);
+ // ... record it ...
+ activeConnectors.put(nodeId, nodeHandler);
- Futures.addCallback(future, new FutureCallback<>() {
- @Override
- public void onSuccess(final Empty result) {
- LOG.debug("Connector for {} started succesfully", nodeId.getValue());
- }
-
- @Override
- public void onFailure(final Throwable throwable) {
- LOG.error("Connector for {} failed", nodeId.getValue(), throwable);
- // remove this node from active connectors?
- }
- }, MoreExecutors.directExecutor());
- }
-
- protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node,
- final NetconfNodeAugmentedOptional nodeOptional) {
- final var deviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, node);
- final long keepaliveDelay = node.requireKeepaliveDelay().toJava();
-
- final var deviceSalFacade = createSalFacade(deviceId, node.requireLockDatastore());
- // The facade we are going it present to NetconfDevice
- RemoteDeviceHandler salFacade;
- final KeepaliveSalFacade keepAliveFacade;
- if (keepaliveDelay > 0) {
- LOG.info("Adding keepalive facade, for device {}", nodeId);
- salFacade = keepAliveFacade = new KeepaliveSalFacade(deviceId, deviceSalFacade,
- keepaliveExecutor.getExecutor(), keepaliveDelay, node.requireDefaultRequestTimeoutMillis().toJava());
- } else {
- salFacade = deviceSalFacade;
- keepAliveFacade = null;
- }
-
- // Setup reconnection on empty context, if so configured
- if (nodeOptional != null && nodeOptional.getIgnoreMissingSchemaSources().getAllowed()) {
- LOG.warn("Ignoring missing schema sources is not currently implemented for {}", deviceId);
- }
-
- final RemoteDevice<NetconfDeviceCommunicator> device;
- final List<SchemaSourceRegistration<?>> yanglibRegistrations;
- if (node.requireSchemaless()) {
- device = new SchemalessNetconfDevice(baseSchemas, deviceId, salFacade);
- yanglibRegistrations = List.of();
- } else {
- final SchemaResourcesDTO resources = schemaManager.getSchemaResources(node.getSchemaCacheDirectory(),
- nodeId.getValue());
- device = new NetconfDeviceBuilder()
- .setReconnectOnSchemasChange(node.requireReconnectOnChangedSchema())
- .setSchemaResourcesDTO(resources)
- .setGlobalProcessingExecutor(processingExecutor)
- .setId(deviceId)
- .setSalFacade(salFacade)
- .setDeviceActionFactory(deviceActionFactory)
- .setBaseSchemas(baseSchemas)
- .build();
- yanglibRegistrations = registerDeviceSchemaSources(deviceId, node, resources);
- }
-
- final int rpcMessageLimit = node.requireConcurrentRpcLimit().toJava();
- if (rpcMessageLimit < 1) {
- LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", deviceId);
- }
-
- final var netconfDeviceCommunicator = new NetconfDeviceCommunicator(deviceId, device, rpcMessageLimit,
- NetconfNodeUtils.extractUserCapabilities(node));
-
- if (keepAliveFacade != null) {
- keepAliveFacade.setListener(netconfDeviceCommunicator);
- }
-
- return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade, yanglibRegistrations);
+ // ... and start it
+ nodeHandler.connect();
}
protected RemoteDeviceHandler createSalFacade(final RemoteDeviceId deviceId, final boolean lockDatastore) {
return new NetconfTopologyDeviceSalFacade(deviceId, mountPointService, lockDatastore, dataBroker);
}
- private static List<SchemaSourceRegistration<?>> registerDeviceSchemaSources(final RemoteDeviceId remoteDeviceId,
- final NetconfNode node, final SchemaResourcesDTO resources) {
- final var yangLibrary = node.getYangLibrary();
- if (yangLibrary != null) {
- final Uri uri = yangLibrary.getYangLibraryUrl();
- if (uri != null) {
- final List<SchemaSourceRegistration<?>> registrations = new ArrayList<>();
- final String yangLibURL = uri.getValue();
- final SchemaSourceRegistry schemaRegistry = resources.getSchemaRegistry();
-
- // pre register yang library sources as fallback schemas to schema registry
- final LibraryModulesSchemas schemas;
- final String yangLibUsername = yangLibrary.getUsername();
- final String yangLigPassword = yangLibrary.getPassword();
- if (yangLibUsername != null && yangLigPassword != null) {
- schemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
- } else {
- schemas = LibraryModulesSchemas.create(yangLibURL);
- }
-
- for (final Map.Entry<SourceIdentifier, URL> entry : schemas.getAvailableModels().entrySet()) {
- registrations.add(schemaRegistry.registerSchemaSource(new LibrarySchemaSourceProvider(
- remoteDeviceId, schemas.getAvailableModels()),
- PotentialSchemaSource.create(entry.getKey(), YangTextSchemaSource.class,
- PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
- }
- return List.copyOf(registrations);
- }
- }
-
- return List.of();
- }
+ @VisibleForTesting
+ public NetconfClientConfigurationBuilder getClientConfig(final NetconfNode node, final NodeId nodeId) {
+ final var builder = NetconfClientConfigurationBuilder.create();
- public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
- final NetconfNode node, final NodeId nodeId) {
- final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
- node.requireMaxConnectionAttempts().toJava(), node.requireBetweenAttemptsTimeoutMillis().toJava(),
- node.requireSleepFactor().decimalValue());
- final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder;
final var protocol = node.getProtocol();
if (node.requireTcpOnly()) {
- reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
- .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
- .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
+ builder.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
+ .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
} else if (protocol == null || protocol.getName() == Name.SSH) {
- reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
- .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
- .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
+ builder.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
+ .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
} else if (protocol.getName() == Name.TLS) {
- reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
- .withSslHandlerFactory(sslHandlerFactoryProvider.getSslHandlerFactory(protocol.getSpecification()))
- .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
+ builder.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS)
+ .withSslHandlerFactory(sslHandlerFactoryProvider.getSslHandlerFactory(protocol.getSpecification()));
} else {
throw new IllegalStateException("Unsupported protocol type: " + protocol.getName());
}
- if (node.getOdlHelloMessageCapabilities() != null) {
- reconnectingClientConfigurationBuilder.withOdlHelloCapabilities(
- Lists.newArrayList(node.getOdlHelloMessageCapabilities().getCapability()));
+ final var helloCapabilities = node.getOdlHelloMessageCapabilities();
+ if (helloCapabilities != null) {
+ builder.withOdlHelloCapabilities(List.copyOf(helloCapabilities.requireCapability()));
}
- return reconnectingClientConfigurationBuilder
- .withName(nodeId.getValue())
- .withAddress(NetconfNodeUtils.toInetSocketAddress(node))
- .withConnectionTimeoutMillis(node.requireConnectionTimeoutMillis().toJava())
- .withReconnectStrategy(sf.createReconnectStrategy())
- .withConnectStrategyFactory(sf)
- .withSessionListener(listener)
- .build();
+ return builder
+ .withName(nodeId.getValue())
+ .withAddress(NetconfNodeUtils.toInetSocketAddress(node))
+ .withConnectionTimeoutMillis(node.requireConnectionTimeoutMillis().toJava());
}
private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
+++ /dev/null
-/*
- * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.netconf.topology.spi;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.netconf.client.NetconfClientSessionListener;
-import org.opendaylight.netconf.client.mdsal.NetconfDeviceCommunicator;
-import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
-import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
-
-final class NetconfConnectorDTO implements AutoCloseable {
- private final @NonNull List<SchemaSourceRegistration<?>> yanglibRegistrations;
- private final @NonNull NetconfDeviceCommunicator communicator;
- private final @NonNull RemoteDeviceHandler facade;
-
- NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, final RemoteDeviceHandler facade,
- final List<SchemaSourceRegistration<?>> yanglibRegistrations) {
- this.communicator = requireNonNull(communicator);
- this.facade = requireNonNull(facade);
- this.yanglibRegistrations = List.copyOf(yanglibRegistrations);
- }
-
- NetconfDeviceCommunicator getCommunicator() {
- return communicator;
- }
-
- NetconfClientSessionListener getSessionListener() {
- return communicator;
- }
-
- @Override
- public void close() {
- communicator.close();
- facade.close();
- yanglibRegistrations.forEach(SchemaSourceRegistration::close);
- }
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.netconf.topology.spi;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import io.netty.util.concurrent.EventExecutor;
+import io.netty.util.concurrent.Future;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.checkerframework.checker.lock.qual.Holding;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.dom.api.DOMNotification;
+import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
+import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
+import org.opendaylight.netconf.client.mdsal.LibraryModulesSchemas;
+import org.opendaylight.netconf.client.mdsal.LibrarySchemaSourceProvider;
+import org.opendaylight.netconf.client.mdsal.NetconfDevice.SchemaResourcesDTO;
+import org.opendaylight.netconf.client.mdsal.NetconfDeviceBuilder;
+import org.opendaylight.netconf.client.mdsal.NetconfDeviceCommunicator;
+import org.opendaylight.netconf.client.mdsal.NetconfDeviceSchema;
+import org.opendaylight.netconf.client.mdsal.SchemalessNetconfDevice;
+import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemas;
+import org.opendaylight.netconf.client.mdsal.api.DeviceActionFactory;
+import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
+import org.opendaylight.netconf.client.mdsal.api.RemoteDevice;
+import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
+import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices;
+import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
+import org.opendaylight.netconf.client.mdsal.spi.KeepaliveSalFacade;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev221225.NetconfNodeAugmentedOptional;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yangtools.concepts.AbstractRegistration;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * All state associated with a NETCONF topology node. Each node handles its own reconnection.
+ */
+final class NetconfNodeHandler extends AbstractRegistration implements RemoteDeviceHandler {
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeHandler.class);
+
+ private final @NonNull List<SchemaSourceRegistration<?>> yanglibRegistrations;
+ private final @NonNull NetconfClientDispatcher clientDispatcher;
+ private final @NonNull NetconfClientConfiguration clientConfig;
+ private final @NonNull NetconfDeviceCommunicator communicator;
+ private final @NonNull RemoteDeviceHandler delegate;
+ private final @NonNull EventExecutor eventExecutor;
+ private final @NonNull RemoteDeviceId deviceId;
+
+ private final long maxAttempts;
+ private final int minSleep;
+ private final double sleepFactor;
+
+ @GuardedBy("this")
+ private long attempts;
+ @GuardedBy("this")
+ private long lastSleep;
+ @GuardedBy("this")
+ private Future<?> currentTask;
+
+ NetconfNodeHandler(final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor,
+ final ScheduledExecutorService keepaliveExecutor, final BaseNetconfSchemas baseSchemas,
+ final SchemaResourceManager schemaManager, final ListeningExecutorService processingExecutor,
+ final DeviceActionFactory deviceActionFactory, final RemoteDeviceHandler delegate,
+ final RemoteDeviceId deviceId, final NodeId nodeId, final NetconfNode node,
+ final NetconfNodeAugmentedOptional nodeOptional, final NetconfClientConfigurationBuilder configBuilder) {
+ this.clientDispatcher = requireNonNull(clientDispatcher);
+ this.eventExecutor = requireNonNull(eventExecutor);
+ this.delegate = requireNonNull(delegate);
+ this.deviceId = requireNonNull(deviceId);
+
+ maxAttempts = node.requireMaxConnectionAttempts().toJava();
+ minSleep = node.requireBetweenAttemptsTimeoutMillis().toJava();
+ sleepFactor = node.requireSleepFactor().doubleValue();
+
+ // Setup reconnection on empty context, if so configured
+ // FIXME: NETCONF-925: implement this
+ if (nodeOptional != null && nodeOptional.getIgnoreMissingSchemaSources().getAllowed()) {
+ LOG.warn("Ignoring missing schema sources is not currently implemented for {}", deviceId);
+ }
+
+ // The facade we are going it present to NetconfDevice
+ RemoteDeviceHandler salFacade;
+ final KeepaliveSalFacade keepAliveFacade;
+ final long keepaliveDelay = node.requireKeepaliveDelay().toJava();
+ if (keepaliveDelay > 0) {
+ LOG.info("Adding keepalive facade, for device {}", nodeId);
+ salFacade = keepAliveFacade = new KeepaliveSalFacade(deviceId, this, keepaliveExecutor, keepaliveDelay,
+ node.requireDefaultRequestTimeoutMillis().toJava());
+ } else {
+ salFacade = this;
+ keepAliveFacade = null;
+ }
+
+ final RemoteDevice<NetconfDeviceCommunicator> device;
+ if (node.requireSchemaless()) {
+ device = new SchemalessNetconfDevice(baseSchemas, deviceId, salFacade);
+ yanglibRegistrations = List.of();
+ } else {
+ final var resources = schemaManager.getSchemaResources(node.getSchemaCacheDirectory(), nodeId.getValue());
+ device = new NetconfDeviceBuilder()
+ .setReconnectOnSchemasChange(node.requireReconnectOnChangedSchema())
+ .setSchemaResourcesDTO(resources)
+ .setGlobalProcessingExecutor(processingExecutor)
+ .setId(deviceId)
+ .setSalFacade(salFacade)
+ .setDeviceActionFactory(deviceActionFactory)
+ .setBaseSchemas(baseSchemas)
+ .build();
+ yanglibRegistrations = registerDeviceSchemaSources(deviceId, node, resources);
+ }
+
+ final int rpcMessageLimit = node.requireConcurrentRpcLimit().toJava();
+ if (rpcMessageLimit < 1) {
+ LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", deviceId);
+ }
+
+ communicator = new NetconfDeviceCommunicator(deviceId, device, rpcMessageLimit,
+ NetconfNodeUtils.extractUserCapabilities(node));
+
+ if (keepAliveFacade != null) {
+ keepAliveFacade.setListener(communicator);
+ }
+
+ clientConfig = configBuilder.withSessionListener(communicator).build();
+ }
+
+ synchronized void connect() {
+ lockedConnect();
+ }
+
+ @Holding("this")
+ private void lockedConnect() {
+ currentTask = clientDispatcher.createClient(clientConfig);
+ currentTask.addListener(this::connectComplete);
+ }
+
+ private void connectComplete(final Future<?> future) {
+ final Throwable cause;
+
+ // Locked manipulation of internal state
+ synchronized (this) {
+ // A quick sanity check
+ if (currentTask != future) {
+ LOG.warn("Ignoring connection completion, expected {} actual {}", future, currentTask);
+ return;
+ }
+
+ currentTask = null;
+ cause = future.cause();
+ if (cause == null || cause instanceof CancellationException) {
+ // Success or cancellation, nothing else to do
+ return;
+ }
+
+ LOG.debug("Connection attempt {} to {} failed", attempts, deviceId, cause);
+ }
+
+ // We are invoking callbacks, do not hold locks
+ onDeviceFailed(cause);
+ }
+
+ @Override
+ protected synchronized void removeRegistration() {
+ if (currentTask != null) {
+ currentTask.cancel(false);
+ currentTask = null;
+ }
+
+ communicator.close();
+ delegate.close();
+ yanglibRegistrations.forEach(SchemaSourceRegistration::close);
+ }
+
+ @Override
+ public synchronized void onDeviceConnected(final NetconfDeviceSchema deviceSchema,
+ final NetconfSessionPreferences sessionPreferences, final RemoteDeviceServices services) {
+ attempts = 0;
+ }
+
+ @Override
+ public void onDeviceDisconnected() {
+ delegate.onDeviceDisconnected();
+ scheduleReconnect();
+ }
+
+ @Override
+ public void onDeviceFailed(final Throwable throwable) {
+ LOG.debug("Connection attempt failed", throwable);
+ delegate.onDeviceFailed(throwable);
+ scheduleReconnect();
+ }
+
+ @Override
+ public void onNotification(final DOMNotification domNotification) {
+ delegate.onNotification(domNotification);
+ }
+
+ private synchronized void scheduleReconnect() {
+ if (isClosed()) {
+ return;
+ }
+
+ final long delayMillis;
+
+ // We have exceeded the number of connection attempts
+ if (maxAttempts > 0 && attempts >= maxAttempts) {
+ LOG.info("Failed to connect {} after {} attempts, not attempting", deviceId, attempts);
+ return;
+ }
+
+ // First connection attempt gets initialized to minimum sleep, each subsequent is exponentially backed off
+ // by sleepFactor.
+ if (attempts != 0) {
+ final long nextSleep = (long) (lastSleep * sleepFactor);
+ // check for overflow
+ delayMillis = nextSleep >= 0 ? nextSleep : Long.MAX_VALUE;
+ } else {
+ delayMillis = minSleep;
+ }
+
+ attempts++;
+ lastSleep = delayMillis;
+ LOG.debug("Retrying {} connection attempt {} after {} milliseconds", deviceId, attempts, delayMillis);
+
+ // If we are not sleeping at all, return an already-succeeded future
+ if (delayMillis == 0) {
+ lockedConnect();
+ return;
+ }
+
+ // Schedule a task for the right time. It will also clear the flag.
+ currentTask = eventExecutor.schedule(this::reconnect, delayMillis, TimeUnit.MILLISECONDS);
+ }
+
+ private synchronized void reconnect() {
+ currentTask = null;
+ if (notClosed()) {
+ lockedConnect();
+ }
+ }
+
+ private static List<SchemaSourceRegistration<?>> registerDeviceSchemaSources(final RemoteDeviceId remoteDeviceId,
+ final NetconfNode node, final SchemaResourcesDTO resources) {
+ final var yangLibrary = node.getYangLibrary();
+ if (yangLibrary != null) {
+ final Uri uri = yangLibrary.getYangLibraryUrl();
+ if (uri != null) {
+ final var registrations = new ArrayList<SchemaSourceRegistration<?>>();
+ final var yangLibURL = uri.getValue();
+ final var schemaRegistry = resources.getSchemaRegistry();
+
+ // pre register yang library sources as fallback schemas to schema registry
+ final var yangLibUsername = yangLibrary.getUsername();
+ final var yangLigPassword = yangLibrary.getPassword();
+ final var schemas = yangLibUsername != null && yangLigPassword != null
+ ? LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword)
+ : LibraryModulesSchemas.create(yangLibURL);
+
+ for (var entry : schemas.getAvailableModels().entrySet()) {
+ registrations.add(schemaRegistry.registerSchemaSource(new LibrarySchemaSourceProvider(
+ remoteDeviceId, schemas.getAvailableModels()),
+ PotentialSchemaSource.create(entry.getKey(), YangTextSchemaSource.class,
+ PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
+ }
+ return List.copyOf(registrations);
+ }
+ }
+
+ return List.of();
+ }
+}
\ No newline at end of file
* Creates a client.
*
* @param address remote address
- * @param strategy Reconnection strategy to be used when initial connection fails
+ * @param initializer Channel initializer
*
* @return Future representing the connection process. Its result represents the combined success of TCP connection
* as well as session negotiation.
*/
- protected Future<S> createClient(final InetSocketAddress address, final ReconnectStrategy strategy,
- final PipelineInitializer<S> initializer) {
+ protected Future<S> createClient(final InetSocketAddress address, final PipelineInitializer<S> initializer) {
final Bootstrap b = new Bootstrap();
- final NetconfSessionPromise<S> p = new NetconfSessionPromise<>(executor, address, strategy, b);
+ final NetconfSessionPromise<S> p = new NetconfSessionPromise<>(executor, address, b);
b.option(ChannelOption.SO_KEEPALIVE, true).handler(
new ChannelInitializer<SocketChannel>() {
@Override
*
* @param address remote address
*/
- protected Future<S> createClient(final InetSocketAddress address, final ReconnectStrategy strategy,
- final Bootstrap bootstrap, final PipelineInitializer<S> initializer) {
- final NetconfSessionPromise<S> p = new NetconfSessionPromise<>(executor, address, strategy, bootstrap);
+ protected Future<S> createClient(final InetSocketAddress address, final Bootstrap bootstrap,
+ final PipelineInitializer<S> initializer) {
+ final NetconfSessionPromise<S> p = new NetconfSessionPromise<>(executor, address, bootstrap);
bootstrap.handler(
new ChannelInitializer<SocketChannel>() {
return p;
}
- /**
- * Creates a reconnecting client.
- *
- * @param address remote address
- * @param connectStrategyFactory Factory for creating reconnection strategy for every reconnect attempt
- * @return Future representing the reconnection task. It will report completion based on reestablishStrategy, e.g.
- * success is never reported, only failure when it runs out of reconnection attempts.
- */
- protected ReconnectFuture createReconnectingClient(final InetSocketAddress address,
- final ReconnectStrategyFactory connectStrategyFactory, final PipelineInitializer<S> initializer) {
- final Bootstrap b = new Bootstrap();
-
- final ReconnectPromise<S, L> p = new ReconnectPromise<>(GlobalEventExecutor.INSTANCE, this, address,
- connectStrategyFactory, b, initializer);
-
- b.option(ChannelOption.SO_KEEPALIVE, true);
-
- setWorkerGroup(b);
- setChannelFactory(b);
-
- p.connect();
- return p;
- }
-
private static void setChannelFactory(final Bootstrap bootstrap) {
// There is no way to detect if this was already set by
// customizeBootstrap()
*/
package org.opendaylight.netconf.nettyutil;
-import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFutureListener;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor;
-import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.net.InetSocketAddress;
import org.checkerframework.checker.lock.qual.GuardedBy;
@Deprecated
final class NetconfSessionPromise<S extends NetconfSession> extends DefaultPromise<S> {
private static final Logger LOG = LoggerFactory.getLogger(NetconfSessionPromise.class);
- private final ReconnectStrategy strategy;
- private InetSocketAddress address;
+
private final Bootstrap bootstrap;
+ private InetSocketAddress address;
@GuardedBy("this")
- private Future<?> pending;
+ private ChannelFuture pending;
- NetconfSessionPromise(final EventExecutor executor, final InetSocketAddress address,
- final ReconnectStrategy strategy, final Bootstrap bootstrap) {
+ NetconfSessionPromise(final EventExecutor executor, final InetSocketAddress address, final Bootstrap bootstrap) {
super(executor);
- this.strategy = requireNonNull(strategy);
this.address = requireNonNull(address);
this.bootstrap = requireNonNull(bootstrap);
}
@SuppressWarnings("checkstyle:illegalCatch")
synchronized void connect() {
- final int timeout;
- try {
- timeout = strategy.getConnectTimeout();
- } catch (Exception e) {
- LOG.info("Connection to {} aborted due to strategy decision", address, e);
- setFailure(e);
- return;
- }
-
- LOG.debug("Promise {} attempting connect for {}ms", this, timeout);
-
final ChannelFuture connectFuture;
try {
if (address.isUnresolved()) {
@Override
public synchronized Promise<S> setSuccess(final S result) {
LOG.debug("Promise {} completed", this);
- strategy.reconnectSuccessful();
return super.setSuccess(result);
}
// Triggered when a connection attempt is resolved.
private synchronized void channelConnectComplete(final ChannelFuture cf) {
LOG.debug("Promise {} connection resolved", this);
- verify(pending == cf, "Completed channel future %s while pending %s", cf, pending);
/*
* The promise we gave out could have been cancelled,
}
LOG.debug("Attempt to connect to {} failed", address, cf.cause());
-
- final Future<Void> rf = strategy.scheduleReconnect(cf.cause());
- pending = rf;
- rf.addListener(this::reconnectFutureComplete);
- }
-
- // Triggered when a connection attempt is to be made.
- private synchronized void reconnectFutureComplete(final Future<?> sf) {
- LOG.debug("Promise {} strategy triggered reconnect", this);
- verify(pending == sf, "Completed strategy future %s while pending %s", sf, pending);
-
- /*
- * The promise we gave out could have been cancelled,
- * which cascades to the reconnect attempt getting
- * cancelled, but there is a slight race window, where
- * the reconnect attempt is already enqueued, but the
- * listener has not yet been notified -- if cancellation
- * happens at that point, we need to catch it here.
- */
- if (!isCancelled()) {
- if (sf.isSuccess()) {
- connect();
- } else {
- setFailure(sf.cause());
- }
- }
+ setFailure(cf.cause());
}
}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
-
-import io.netty.util.concurrent.EventExecutor;
-import io.netty.util.concurrent.Future;
-
-/**
- * Utility ReconnectStrategy singleton, which will cause the reconnect process to always fail. This class is thred-safe.
- */
-@Deprecated
-public final class NeverReconnectStrategy implements ReconnectStrategy {
- private final EventExecutor executor;
- private final int timeout;
-
- public NeverReconnectStrategy(final EventExecutor executor, final int timeout) {
- checkArgument(timeout >= 0);
- this.executor = requireNonNull(executor);
- this.timeout = timeout;
- }
-
- @Override
- public Future<Void> scheduleReconnect(final Throwable cause) {
- return executor.newFailedFuture(new Throwable("Reconnect failed", cause));
- }
-
- @Override
- public void reconnectSuccessful() {
- // Nothing to do
- }
-
- @Override
- public int getConnectTimeout() {
- return timeout;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2021 PANTHEON.tech, s.r.o. 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.netconf.nettyutil;
-
-import com.google.common.annotations.Beta;
-import io.netty.util.concurrent.Future;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.common.Empty;
-
-/**
- * A future representing the task of reconnecting of a certain channel. This future never completes successfully, it
- * either fails when the underlying strategy gives up, or when it is cancelled. It additionally exposes an additional
- * future, which completes when the session is established for the first time.
- */
-@Beta
-public interface ReconnectFuture extends Future<Empty> {
- /**
- * Return a Future which completes when the first session is established.
- *
- * @return First session establishment future
- */
- @NonNull Future<?> firstSessionFuture();
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
-
-import io.netty.util.concurrent.EventExecutor;
-import io.netty.util.concurrent.Future;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Utility ReconnectStrategy singleton, which will cause the reconnect process to immediately schedule a reconnection
- * attempt. This class is thread-safe.
- */
-@Deprecated
-public final class ReconnectImmediatelyStrategy implements ReconnectStrategy {
- private static final Logger LOG = LoggerFactory.getLogger(ReconnectImmediatelyStrategy.class);
- private final EventExecutor executor;
- private final int timeout;
-
- public ReconnectImmediatelyStrategy(final EventExecutor executor, final int timeout) {
- checkArgument(timeout >= 0);
- this.executor = requireNonNull(executor);
- this.timeout = timeout;
- }
-
- @Override
- public Future<Void> scheduleReconnect(final Throwable cause) {
- LOG.debug("Connection attempt failed", cause);
- return executor.newSucceededFuture(null);
- }
-
- @Override
- public void reconnectSuccessful() {
- // Nothing to do
- }
-
- @Override
- public int getConnectTimeout() {
- return timeout;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil;
-
-import static java.util.Objects.requireNonNull;
-
-import io.netty.bootstrap.Bootstrap;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.util.concurrent.DefaultPromise;
-import io.netty.util.concurrent.EventExecutor;
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.Promise;
-import java.net.InetSocketAddress;
-import org.checkerframework.checker.lock.qual.GuardedBy;
-import org.checkerframework.checker.lock.qual.Holding;
-import org.opendaylight.netconf.api.NetconfSession;
-import org.opendaylight.netconf.api.NetconfSessionListener;
-import org.opendaylight.netconf.nettyutil.AbstractNetconfDispatcher.PipelineInitializer;
-import org.opendaylight.yangtools.yang.common.Empty;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Deprecated
-final class ReconnectPromise<S extends NetconfSession, L extends NetconfSessionListener<? super S>>
- extends DefaultPromise<Empty> implements ReconnectFuture {
- private static final Logger LOG = LoggerFactory.getLogger(ReconnectPromise.class);
-
- private final AbstractNetconfDispatcher<S, L> dispatcher;
- private final InetSocketAddress address;
- private final ReconnectStrategyFactory strategyFactory;
- private final Bootstrap bootstrap;
- private final PipelineInitializer<S> initializer;
- private final Promise<Empty> firstSessionFuture;
-
- @GuardedBy("this")
- private Future<?> pending;
-
- ReconnectPromise(final EventExecutor executor, final AbstractNetconfDispatcher<S, L> dispatcher,
- final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory,
- final Bootstrap bootstrap, final PipelineInitializer<S> initializer) {
- super(executor);
- this.firstSessionFuture = new DefaultPromise<>(executor);
- this.bootstrap = requireNonNull(bootstrap);
- this.initializer = requireNonNull(initializer);
- this.dispatcher = requireNonNull(dispatcher);
- this.address = requireNonNull(address);
- this.strategyFactory = requireNonNull(connectStrategyFactory);
- }
-
- @Override
- public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
- if (super.cancel(mayInterruptIfRunning)) {
- firstSessionFuture.cancel(mayInterruptIfRunning);
- pending.cancel(mayInterruptIfRunning);
- return true;
- }
- return false;
- }
-
- @Override
- public Future<?> firstSessionFuture() {
- return firstSessionFuture;
- }
-
- synchronized void connect() {
- lockedConnect();
- }
-
- @Holding("this")
- private void lockedConnect() {
- final ReconnectStrategy cs = strategyFactory.createReconnectStrategy();
-
- // Set up a client with pre-configured bootstrap, but add a closed channel handler into the pipeline to support
- // reconnect attempts
- pending = dispatcher.createClient(address, cs, bootstrap, (channel, promise) -> {
- initializer.initializeChannel(channel, promise);
- // add closed channel handler
- // This handler has to be added as last channel handler and the channel inactive event has to be caught by
- // it
- // Handlers in front of it can react to channelInactive event, but have to forward the event or the
- // reconnect will not work
- // This handler is last so all handlers in front of it can handle channel inactive (to e.g. resource
- // cleanup) before a new connection is started
- channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
- @Override
- public void channelInactive(final ChannelHandlerContext ctx) {
- onChannelInactive();
- }
- });
- });
-
- if (!firstSessionFuture.isDone()) {
- pending.addListener(future -> {
- if (!future.isSuccess() && !firstSessionFuture.isDone()) {
- firstSessionFuture.setFailure(future.cause());
- }
- });
- }
- }
-
- private void onChannelInactive() {
- // This is the ultimate channel inactive handler, not forwarding
- if (isCancelled()) {
- return;
- }
-
- synchronized (this) {
- final Future<?> attempt = pending;
- if (!attempt.isDone() || !attempt.isSuccess()) {
- // Connection refused, negotiation failed, or similar
- LOG.debug("Connection to {} was dropped during negotiation, reattempting", address);
- }
-
- LOG.debug("Reconnecting after connection to {} was dropped", address);
- lockedConnect();
- }
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil;
-
-import io.netty.util.concurrent.Future;
-
-/**
- * Interface exposed by a reconnection strategy provider. A reconnection
- * strategy decides whether to attempt reconnection and when to do that.
- *
- * <p>
- * The proper way of using this API is such that when a connection attempt
- * has failed, the user will call scheduleReconnect() to obtain a future,
- * which tracks schedule of the next connect attempt. The user should add its
- * own listener to be get notified when the future is done. Once the
- * the notification fires, user should examine the future to see whether
- * it is successful or not. If it is successful, the user should immediately
- * initiate a connection attempt. If it is unsuccessful, the user must
- * not attempt any more connection attempts and should abort the reconnection
- * process.
- */
-@Deprecated
-public interface ReconnectStrategy {
- /**
- * Query the strategy for the connect timeout.
- *
- * @return connect try timeout in milliseconds, or
- * 0 for infinite (or system-default) timeout
- * @throws Exception if the connection should not be attempted
- */
- int getConnectTimeout() throws Exception;
-
- /**
- * Schedule a connection attempt. The precise time when the connection
- * should be attempted is signaled by successful completion of returned
- * future.
- *
- * @param cause Cause of previous failure
- * @return a future tracking the schedule, may not be null
- * @throws IllegalStateException when a connection attempt is currently
- * scheduled.
- */
- Future<Void> scheduleReconnect(Throwable cause);
-
- /**
- * Reset the strategy state. Users call this method once the reconnection
- * process succeeds.
- */
- void reconnectSuccessful();
-}
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil;
-
-/**
- * Factory interface for creating new ReconnectStrategy instances. This is
- * primarily useful for allowing injection of a specific type of strategy for
- * on-demand use, pretty much like you would use a ThreadFactory.
- */
-@Deprecated
-public interface ReconnectStrategyFactory {
- /**
- * Create a new ReconnectStrategy.
- *
- * @return a new reconnecty strategy
- */
- ReconnectStrategy createReconnectStrategy();
-}
-
+++ /dev/null
-/*
- * Copyright (c) 2013 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.netconf.nettyutil;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static java.util.Objects.requireNonNull;
-
-import io.netty.util.concurrent.EventExecutor;
-import io.netty.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import org.checkerframework.checker.lock.qual.GuardedBy;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Swiss army knife equivalent for reconnect strategies. This class is thread-safe.
- *
- * <p>
- * This strategy continues to schedule reconnect attempts, each having to complete in a fixed time (connectTime).
- *
- * <p>
- * Initial sleep time is specified as minSleep. Each subsequent unsuccessful attempt multiplies this time by a constant
- * factor (sleepFactor) -- this allows for either constant reconnect times (sleepFactor = 1), or various degrees of
- * exponential back-off (sleepFactor > 1). Maximum sleep time between attempts can be capped to a specific value
- * (maxSleep).
- *
- * <p>
- * The strategy can optionally give up based on two criteria:
- *
- * <p>
- * A preset number of connection retries (maxAttempts) has been reached, or
- *
- * <p>
- * A preset absolute deadline is reached (deadline nanoseconds, as reported by System.nanoTime(). In this specific case,
- * both connectTime and maxSleep will be controlled such that the connection attempt is resolved as closely to the
- * deadline as possible.
- *
- * <p>
- * Both these caps can be combined, with the strategy giving up as soon as the first one is reached.
- */
-@Deprecated
-public final class TimedReconnectStrategy implements ReconnectStrategy {
- private static final Logger LOG = LoggerFactory.getLogger(TimedReconnectStrategy.class);
- private final EventExecutor executor;
- private final Long deadline;
- private final Long maxAttempts;
- private final Long maxSleep;
- private final double sleepFactor;
- private final int connectTime;
- private final long minSleep;
-
- @GuardedBy("this")
- private long attempts;
-
- @GuardedBy("this")
- private long lastSleep;
-
- @GuardedBy("this")
- private boolean scheduled;
-
- public TimedReconnectStrategy(final EventExecutor executor, final int connectTime, final long minSleep,
- final double sleepFactor, final Long maxSleep, final Long maxAttempts, final Long deadline) {
- checkArgument(maxSleep == null || minSleep <= maxSleep);
- checkArgument(sleepFactor >= 1);
- checkArgument(connectTime >= 0);
- this.executor = requireNonNull(executor);
- this.deadline = deadline;
- this.maxAttempts = maxAttempts;
- this.minSleep = minSleep;
- this.maxSleep = maxSleep;
- this.sleepFactor = sleepFactor;
- this.connectTime = connectTime;
- }
-
- @Override
- public synchronized Future<Void> scheduleReconnect(final Throwable cause) {
- LOG.debug("Connection attempt failed", cause);
-
- // Check if a reconnect attempt is scheduled
- checkState(!this.scheduled);
-
- // Get a stable 'now' time for deadline calculations
- final long now = System.nanoTime();
-
- // Obvious stop conditions
- if (this.maxAttempts != null && this.attempts >= this.maxAttempts) {
- return this.executor.newFailedFuture(new Throwable("Maximum reconnection attempts reached"));
- }
- if (this.deadline != null && this.deadline <= now) {
- return this.executor.newFailedFuture(new TimeoutException("Reconnect deadline reached"));
- }
-
- /*
- * First connection attempt gets initialized to minimum sleep,
- * each subsequent is exponentially backed off by sleepFactor.
- */
- if (this.attempts != 0) {
- this.lastSleep *= this.sleepFactor;
- } else {
- this.lastSleep = this.minSleep;
- }
-
- // Cap the sleep time to maxSleep
- if (this.maxSleep != null && this.lastSleep > this.maxSleep) {
- LOG.debug("Capped sleep time from {} to {}", this.lastSleep, this.maxSleep);
- this.lastSleep = this.maxSleep;
- }
-
- this.attempts++;
-
- // Check if the reconnect attempt is within the deadline
- if (this.deadline != null && this.deadline <= now + TimeUnit.MILLISECONDS.toNanos(this.lastSleep)) {
- return this.executor.newFailedFuture(new TimeoutException("Next reconnect would happen after deadline"));
- }
-
- LOG.debug("Connection attempt {} sleeping for {} milliseconds", this.attempts, this.lastSleep);
-
- // If we are not sleeping at all, return an already-succeeded future
- if (this.lastSleep == 0) {
- return this.executor.newSucceededFuture(null);
- }
-
- // Set the scheduled flag.
- this.scheduled = true;
-
- // Schedule a task for the right time. It will also clear the flag.
- return this.executor.schedule(() -> {
- synchronized (TimedReconnectStrategy.this) {
- checkState(TimedReconnectStrategy.this.scheduled);
- TimedReconnectStrategy.this.scheduled = false;
- }
-
- return null;
- }, this.lastSleep, TimeUnit.MILLISECONDS);
- }
-
- @Override
- public synchronized void reconnectSuccessful() {
- checkState(!this.scheduled);
- this.attempts = 0;
- }
-
- @Override
- public int getConnectTimeout() throws TimeoutException {
- int timeout = this.connectTime;
-
- if (this.deadline != null) {
-
- // If there is a deadline, we may need to cap the connect
- // timeout to meet the deadline.
- final long now = System.nanoTime();
- if (now >= this.deadline) {
- throw new TimeoutException("Reconnect deadline already passed");
- }
-
- final long left = TimeUnit.NANOSECONDS.toMillis(this.deadline - now);
- if (left < 1) {
- throw new TimeoutException("Connect timeout too close to deadline");
- }
-
- /*
- * A bit of magic:
- * - if time left is less than the timeout, set it directly
- * - if there is no timeout, and time left is:
- * - less than maximum integer, set timeout to time left
- * - more than maximum integer, set timeout Integer.MAX_VALUE
- */
- if (timeout > left) {
- timeout = (int) left;
- } else if (timeout == 0) {
- timeout = left <= Integer.MAX_VALUE ? (int) left : Integer.MAX_VALUE;
- }
- }
- return timeout;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2019 Pantheon Technologies, s.r.o. 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.netconf.nettyutil;
-
-import io.netty.util.concurrent.EventExecutor;
-import java.math.BigDecimal;
-
-@Deprecated
-public final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
- private final Long connectionAttempts;
- private final EventExecutor executor;
- private final double sleepFactor;
- private final int minSleep;
-
- public TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
- final int minSleep, final BigDecimal sleepFactor) {
- if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
- connectionAttempts = maxConnectionAttempts;
- } else {
- connectionAttempts = null;
- }
-
- this.sleepFactor = sleepFactor.doubleValue();
- this.executor = executor;
- this.minSleep = minSleep;
- }
-
- @Override
- public ReconnectStrategy createReconnectStrategy() {
- return new TimedReconnectStrategy(executor, minSleep,
- minSleep, sleepFactor, null /*maxSleep*/, connectionAttempts, null /*deadline*/);
- }
-}
\ No newline at end of file
*/
package org.opendaylight.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 org.opendaylight.netconf.client.mdsal.NetconfDeviceCommunicator;
import org.opendaylight.netconf.client.mdsal.api.RemoteDevice;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
-import org.opendaylight.netconf.nettyutil.NeverReconnectStrategy;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static NetconfClientConfiguration getNetconfClientConfiguration(final Parameters params,
final NetconfDeviceCommunicator sessionListener) {
- final NetconfClientConfigurationBuilder netconfClientConfigurationBuilder = NetconfClientConfigurationBuilder
- .create();
- netconfClientConfigurationBuilder.withSessionListener(sessionListener);
- netconfClientConfigurationBuilder.withAddress(params.getInetAddress());
+ final var netconfClientConfigurationBuilder = NetconfClientConfigurationBuilder.create()
+ .withSessionListener(sessionListener)
+ .withAddress(params.getInetAddress())
+ .withProtocol(params.ssh ? NetconfClientConfiguration.NetconfClientProtocol.SSH
+ : NetconfClientConfiguration.NetconfClientProtocol.TCP)
+ .withAuthHandler(new LoginPasswordHandler(params.username, params.password))
+ .withConnectionTimeoutMillis(20000L);
+
if (params.tcpHeader != null) {
final String header = params.tcpHeader.replace("\"", "").trim() + "\n";
netconfClientConfigurationBuilder.withAdditionalHeader(
}
});
}
- netconfClientConfigurationBuilder.withProtocol(params.ssh ? NetconfClientConfiguration.NetconfClientProtocol.SSH
- : NetconfClientConfiguration.NetconfClientProtocol.TCP);
- netconfClientConfigurationBuilder.withAuthHandler(new LoginPasswordHandler(params.username, params.password));
- netconfClientConfigurationBuilder.withConnectionTimeoutMillis(20000L);
- netconfClientConfigurationBuilder.withReconnectStrategy(
- new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000));
return netconfClientConfigurationBuilder.build();
}
}
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
import io.netty.util.concurrent.DefaultThreadFactory;
-import io.netty.util.concurrent.GlobalEventExecutor;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.Map;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.NeverReconnectStrategy;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
import org.opendaylight.netconf.test.tool.config.Configuration;
import org.opendaylight.netconf.test.tool.config.ConfigurationBuilder;
.build();
}
- @SuppressWarnings("deprecation")
private static NetconfClientConfiguration getClientConfig(final String host, final int port,
final Configuration simulatorConfig,
final NetconfClientSessionListener sessionListener) {
return NetconfClientConfigurationBuilder.create()
.withAddress(new InetSocketAddress(host, port))
.withSessionListener(sessionListener)
- .withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
- NetconfClientConfigurationBuilder.DEFAULT_CONNECTION_TIMEOUT_MILLIS))
.withProtocol(simulatorConfig.isSsh() ? NetconfClientProtocol.SSH : NetconfClientProtocol.TCP)
.withAuthHandler(new LoginPasswordHandler(user.username, user.password))
.build();
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
-import io.netty.util.concurrent.Future;
import java.io.EOFException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import org.opendaylight.netconf.api.xml.XmlElement;
import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.netconf.api.xml.XmlUtil;
-import org.opendaylight.netconf.client.NetconfClientDispatcher;
import org.opendaylight.netconf.client.NetconfClientSession;
import org.opendaylight.netconf.client.NetconfClientSessionListener;
import org.opendaylight.netconf.client.NetconfMessageUtil;
-import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
import org.opendaylight.netconf.client.mdsal.api.RemoteDevice;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceCommunicator;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
-import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.ErrorSeverity;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
private final Queue<Request> requests = new ArrayDeque<>();
private NetconfClientSession currentSession;
- private final SettableFuture<Empty> firstConnectionFuture = SettableFuture.create();
- private Future<?> taskFuture;
-
// isSessionClosing indicates a close operation on the session is issued and
// tearDown will surely be called later to finish the close.
// Used to allow only one thread to enter tearDown and other threads should
} finally {
sessionLock.unlock();
}
-
- // FIXME: right, except ... this does not include the device schema setup, so is it really useful?
- if (!firstConnectionFuture.set(Empty.value())) {
- LOG.trace("{}: First connection already completed", id);
- }
- }
-
- /**
- * Initialize remote connection.
- *
- * @param dispatcher {@code NetconfCLientDispatcher}
- * @param config {@code NetconfClientConfiguration}
- * @return a ListenableFuture that returns success on first successful connection and failure when the underlying
- * reconnecting strategy runs out of reconnection attempts
- */
- public ListenableFuture<Empty> initializeRemoteConnection(final NetconfClientDispatcher dispatcher,
- final NetconfClientConfiguration config) {
-
- final Future<?> connectFuture;
- if (config instanceof NetconfReconnectingClientConfiguration) {
- // FIXME: This is weird. If I understand it correctly we want to know about the first connection so as to
- // forward error state. Analyze the call graph to understand what is going on here. We really want
- // to move reconnection away from the socket layer, so that it can properly interface with sessions
- // and generally has some event-driven state (as all good network glue does). There is a second story
- // which is we want to avoid duplicate code, so it depends on other users as well.
- final var future = dispatcher.createReconnectingClient((NetconfReconnectingClientConfiguration) config);
- taskFuture = future;
- connectFuture = future.firstSessionFuture();
- } else {
- taskFuture = connectFuture = dispatcher.createClient(config);
- }
-
- connectFuture.addListener(future -> {
- if (!future.isSuccess() && !future.isCancelled()) {
- LOG.debug("{}: Connection failed", id, future.cause());
- remoteDevice.onRemoteSessionFailed(future.cause());
- if (!firstConnectionFuture.isDone()) {
- firstConnectionFuture.setException(future.cause());
- }
- }
- });
- return firstConnectionFuture;
}
public void disconnect() {
@Override
public void close() {
- // Cancel reconnect if in progress
- if (taskFuture != null) {
- taskFuture.cancel(false);
- }
// Disconnect from device
// tear down not necessary, called indirectly by the close in disconnect()
disconnect();
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.withSettings;
import com.google.common.util.concurrent.ListenableFuture;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.util.HashedWheelTimer;
-import io.netty.util.Timer;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
-import io.netty.util.concurrent.GlobalEventExecutor;
import java.io.ByteArrayInputStream;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import org.opendaylight.netconf.api.NetconfMessage;
import org.opendaylight.netconf.api.NetconfTerminationReason;
import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
-import org.opendaylight.netconf.client.NetconfClientDispatcherImpl;
import org.opendaylight.netconf.client.NetconfClientSession;
import org.opendaylight.netconf.client.NetconfClientSessionListener;
-import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
import org.opendaylight.netconf.client.mdsal.api.RemoteDevice;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
import org.opendaylight.netconf.client.mdsal.impl.NetconfMessageTransformUtil;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
-import org.opendaylight.netconf.nettyutil.TimedReconnectStrategy;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.SessionIdType;
import org.opendaylight.yangtools.util.xml.UntrustedXML;
import org.opendaylight.yangtools.yang.common.ErrorSeverity;
errorInfoMessages.contains(errMsg1) && errorInfoMessages.contains(errMsg2));
}
- /**
- * Test whether reconnect is scheduled properly.
- */
- @Test
- public void testNetconfDeviceReconnectInCommunicator() {
- final RemoteDevice<NetconfDeviceCommunicator> device = mock(RemoteDevice.class);
-
- final TimedReconnectStrategy timedReconnectStrategy =
- new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, 10000, 0, 1.0, null, 100L, null);
- final ReconnectStrategy reconnectStrategy = spy(new ReconnectStrategy() {
- @Override
- @Deprecated
- public int getConnectTimeout() throws Exception {
- return timedReconnectStrategy.getConnectTimeout();
- }
-
- @Override
- @Deprecated
- public Future<Void> scheduleReconnect(final Throwable cause) {
- return timedReconnectStrategy.scheduleReconnect(cause);
- }
-
- @Override
- @Deprecated
- public void reconnectSuccessful() {
- timedReconnectStrategy.reconnectSuccessful();
- }
- });
-
- final EventLoopGroup group = new NioEventLoopGroup();
- final Timer time = new HashedWheelTimer();
- try {
- final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(
- new RemoteDeviceId("test", InetSocketAddress.createUnresolved("localhost", 22)), device, 10);
- final NetconfReconnectingClientConfiguration cfg = NetconfReconnectingClientConfigurationBuilder.create()
- .withAddress(new InetSocketAddress("localhost", 65000))
- .withReconnectStrategy(reconnectStrategy)
- .withConnectStrategyFactory(() -> reconnectStrategy)
- .withAuthHandler(new LoginPasswordHandler("admin", "admin"))
- .withConnectionTimeoutMillis(10000)
- .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
- .withSessionListener(listener)
- .build();
-
- listener.initializeRemoteConnection(new NetconfClientDispatcherImpl(group, group, time), cfg);
-
- verify(reconnectStrategy,
- timeout(TimeUnit.MINUTES.toMillis(4)).times(101)).scheduleReconnect(any(Throwable.class));
- } finally {
- time.stop();
- group.shutdownGracefully();
- }
- }
-
@Test
public void testOnResponseMessageWithWrongMessageID() throws Exception {
setupSession();
private static NetconfMessage createMultiErrorResponseMessage(final String messageID) throws Exception {
// multiple rpc-errors which simulate actual response like in NETCONF-666
- String xmlStr = "<nc:rpc-reply xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" xmlns:junos=\"http://xml.juniper.net/junos/18.4R1/junos\""
- + " message-id=\"" + messageID + "\">"
+ String xmlStr = "<nc:rpc-reply xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" "
+ + "xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" "
+ + "xmlns:junos=\"http://xml.juniper.net/junos/18.4R1/junos\" message-id=\"" + messageID + "\">"
+ "<nc:rpc-error>\n"
+ "<nc:error-type>protocol</nc:error-type>\n"
+ "<nc:error-tag>operation-failed</nc:error-tag>\n"
import io.netty.util.concurrent.Future;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
-import org.opendaylight.netconf.nettyutil.ReconnectFuture;
public interface NetconfClientDispatcher {
-
/**
* Create netconf client. Network communication has to be set up based on network protocol specified in
* clientConfiguration
* @return netconf client based on provided configuration
*/
Future<NetconfClientSession> createClient(NetconfClientConfiguration clientConfiguration);
-
- ReconnectFuture createReconnectingClient(NetconfReconnectingClientConfiguration clientConfiguration);
}
import javax.inject.Inject;
import javax.inject.Singleton;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.netconf.nettyutil.AbstractNetconfDispatcher;
-import org.opendaylight.netconf.nettyutil.ReconnectFuture;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
};
}
- @Override
- public ReconnectFuture createReconnectingClient(final NetconfReconnectingClientConfiguration clientConfiguration) {
- return switch (clientConfiguration.getProtocol()) {
- case TCP -> createReconnectingTcpClient(clientConfiguration);
- case SSH -> createReconnectingSshClient(clientConfiguration);
- case TLS -> createReconnectingTlsClient(clientConfiguration);
- };
- }
-
private Future<NetconfClientSession> createTcpClient(final NetconfClientConfiguration currentConfiguration) {
LOG.debug("Creating TCP client with configuration: {}", currentConfiguration);
- return super.createClient(currentConfiguration.getAddress(), currentConfiguration.getReconnectStrategy(),
+ return super.createClient(currentConfiguration.getAddress(),
(ch, promise) -> new TcpClientChannelInitializer(getNegotiatorFactory(currentConfiguration),
currentConfiguration.getSessionListener()).initialize(ch, promise));
}
- private ReconnectFuture createReconnectingTcpClient(
- final NetconfReconnectingClientConfiguration currentConfiguration) {
- LOG.debug("Creating reconnecting TCP client with configuration: {}", currentConfiguration);
- final TcpClientChannelInitializer init =
- new TcpClientChannelInitializer(getNegotiatorFactory(currentConfiguration),
- currentConfiguration.getSessionListener());
-
- return super.createReconnectingClient(currentConfiguration.getAddress(),
- currentConfiguration.getConnectStrategyFactory(), init::initialize);
- }
-
private Future<NetconfClientSession> createSshClient(final NetconfClientConfiguration currentConfiguration) {
LOG.debug("Creating SSH client with configuration: {}", currentConfiguration);
- return super.createClient(currentConfiguration.getAddress(), currentConfiguration.getReconnectStrategy(),
+ return super.createClient(currentConfiguration.getAddress(),
(ch, sessionPromise) -> new SshClientChannelInitializer(currentConfiguration.getAuthHandler(),
getNegotiatorFactory(currentConfiguration), currentConfiguration.getSessionListener(),
currentConfiguration.getSshClient(), currentConfiguration.getName())
.initialize(ch, sessionPromise));
}
- private ReconnectFuture createReconnectingSshClient(
- final NetconfReconnectingClientConfiguration currentConfiguration) {
- LOG.debug("Creating reconnecting SSH client with configuration: {}", currentConfiguration);
- final SshClientChannelInitializer init = new SshClientChannelInitializer(currentConfiguration.getAuthHandler(),
- getNegotiatorFactory(currentConfiguration), currentConfiguration.getSessionListener(),
- currentConfiguration.getSshClient(), currentConfiguration.getName());
-
- return super.createReconnectingClient(currentConfiguration.getAddress(),
- currentConfiguration.getConnectStrategyFactory(), init::initialize);
- }
-
private Future<NetconfClientSession> createTlsClient(final NetconfClientConfiguration currentConfiguration) {
LOG.debug("Creating TLS client with configuration: {}", currentConfiguration);
- return super.createClient(currentConfiguration.getAddress(), currentConfiguration.getReconnectStrategy(),
+ return super.createClient(currentConfiguration.getAddress(),
(ch, sessionPromise) -> new TlsClientChannelInitializer(currentConfiguration.getSslHandlerFactory(),
getNegotiatorFactory(currentConfiguration), currentConfiguration.getSessionListener())
.initialize(ch, sessionPromise));
}
- private ReconnectFuture createReconnectingTlsClient(
- final NetconfReconnectingClientConfiguration currentConfiguration) {
- LOG.debug("Creating reconnecting TLS client with configuration: {}", currentConfiguration);
- final TlsClientChannelInitializer init = new TlsClientChannelInitializer(
- currentConfiguration.getSslHandlerFactory(), getNegotiatorFactory(currentConfiguration),
- currentConfiguration.getSessionListener());
-
- return super.createReconnectingClient(currentConfiguration.getAddress(),
- currentConfiguration.getConnectStrategyFactory(), init::initialize);
- }
-
protected NetconfClientSessionNegotiatorFactory getNegotiatorFactory(final NetconfClientConfiguration cfg) {
final List<Uri> odlHelloCapabilities = cfg.getOdlHelloCapabilities();
if (odlHelloCapabilities == null || odlHelloCapabilities.isEmpty()) {
import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
import org.opendaylight.netconf.client.NetconfClientSessionListener;
import org.opendaylight.netconf.client.SslHandlerFactory;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
import org.opendaylight.netconf.nettyutil.handler.ssh.client.NetconfSshClient;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
private final NetconfHelloMessageAdditionalHeader additionalHeader;
private final NetconfClientSessionListener sessionListener;
- private final ReconnectStrategy reconnectStrategy;
-
private final AuthenticationHandler authHandler;
private final SslHandlerFactory sslHandlerFactory;
private final NetconfSshClient sshClient;
final Long connectionTimeoutMillis,
final NetconfHelloMessageAdditionalHeader additionalHeader,
final NetconfClientSessionListener sessionListener,
- final ReconnectStrategy reconnectStrategy, final AuthenticationHandler authHandler,
+ final AuthenticationHandler authHandler,
final SslHandlerFactory sslHandlerFactory, final NetconfSshClient sshClient,
final List<Uri> odlHelloCapabilities, final @NonNegative int maximumIncomingChunkSize,
final String name) {
this.additionalHeader = additionalHeader;
this.sessionListener = sessionListener;
clientProtocol = protocol;
- this.reconnectStrategy = reconnectStrategy;
this.authHandler = authHandler;
this.sslHandlerFactory = sslHandlerFactory;
this.sshClient = sshClient;
return sessionListener;
}
- @Deprecated(forRemoval = true)
- public final ReconnectStrategy getReconnectStrategy() {
- return reconnectStrategy;
- }
-
public final AuthenticationHandler getAuthHandler() {
return authHandler;
}
requireNonNull(clientProtocol, "clientProtocol");
requireNonNull(connectionTimeoutMillis, "connectionTimeoutMillis");
requireNonNull(sessionListener, "sessionListener");
- requireNonNull(reconnectStrategy, "reconnectStrategy");
}
@Override
.add("connectionTimeoutMillis", connectionTimeoutMillis)
.add("additionalHeader", additionalHeader)
.add("sessionListener", sessionListener)
- .add("reconnectStrategy", reconnectStrategy)
.add("clientProtocol", clientProtocol)
.add("authHandler", authHandler)
.add("sslHandlerFactory", sslHandlerFactory);
import org.opendaylight.netconf.client.NetconfClientSessionListener;
import org.opendaylight.netconf.client.SslHandlerFactory;
import org.opendaylight.netconf.nettyutil.AbstractNetconfSessionNegotiator;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
import org.opendaylight.netconf.nettyutil.handler.ssh.client.NetconfSshClient;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
private long connectionTimeoutMillis = DEFAULT_CONNECTION_TIMEOUT_MILLIS;
private NetconfHelloMessageAdditionalHeader additionalHeader;
private NetconfClientSessionListener sessionListener;
- private ReconnectStrategy reconnectStrategy;
private AuthenticationHandler authHandler;
private NetconfClientConfiguration.NetconfClientProtocol clientProtocol = DEFAULT_CLIENT_PROTOCOL;
private SslHandlerFactory sslHandlerFactory;
return this;
}
- @SuppressWarnings("checkstyle:hiddenField")
- public NetconfClientConfigurationBuilder withReconnectStrategy(final ReconnectStrategy reconnectStrategy) {
- this.reconnectStrategy = reconnectStrategy;
- return this;
- }
-
@SuppressWarnings("checkstyle:hiddenField")
public NetconfClientConfigurationBuilder withAuthHandler(final AuthenticationHandler authHandler) {
this.authHandler = authHandler;
return sessionListener;
}
- final ReconnectStrategy getReconnectStrategy() {
- return reconnectStrategy;
- }
-
final AuthenticationHandler getAuthHandler() {
return authHandler;
}
public NetconfClientConfiguration build() {
return new NetconfClientConfiguration(clientProtocol, address, connectionTimeoutMillis, additionalHeader,
- sessionListener, reconnectStrategy, authHandler, sslHandlerFactory, sshClient, odlHelloCapabilities,
+ sessionListener, authHandler, sslHandlerFactory, sshClient, odlHelloCapabilities,
maximumIncomingChunkSize, name);
}
}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.netconf.client.conf;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import java.net.InetSocketAddress;
-import java.util.List;
-import org.checkerframework.checker.index.qual.NonNegative;
-import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
-import org.opendaylight.netconf.client.NetconfClientSessionListener;
-import org.opendaylight.netconf.client.SslHandlerFactory;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategyFactory;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.netconf.nettyutil.handler.ssh.client.NetconfSshClient;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
-
-public final class NetconfReconnectingClientConfiguration extends NetconfClientConfiguration {
- private final ReconnectStrategyFactory connectStrategyFactory;
-
- NetconfReconnectingClientConfiguration(final NetconfClientProtocol clientProtocol, final InetSocketAddress address,
- final Long connectionTimeoutMillis,
- final NetconfHelloMessageAdditionalHeader additionalHeader,
- final NetconfClientSessionListener sessionListener,
- final ReconnectStrategy reconnectStrategy,
- final ReconnectStrategyFactory connectStrategyFactory,
- final AuthenticationHandler authHandler,
- final SslHandlerFactory sslHandlerFactory,
- final NetconfSshClient sshClient,
- final List<Uri> odlHelloCapabilities,
- final @NonNegative int maximumIncomingChunkSize,
- final String name) {
- super(clientProtocol, address, connectionTimeoutMillis, additionalHeader, sessionListener, reconnectStrategy,
- authHandler, sslHandlerFactory, sshClient, odlHelloCapabilities, maximumIncomingChunkSize, name);
- this.connectStrategyFactory = connectStrategyFactory;
- validateReconnectConfiguration();
- }
-
- public ReconnectStrategyFactory getConnectStrategyFactory() {
- return connectStrategyFactory;
- }
-
- private void validateReconnectConfiguration() {
- requireNonNull(connectStrategyFactory);
- }
-
- @Override
- protected ToStringHelper buildToStringHelper() {
- return super.buildToStringHelper().add("connectStrategyFactory", connectStrategyFactory);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.netconf.client.conf;
-
-import java.net.InetSocketAddress;
-import java.util.List;
-import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
-import org.opendaylight.netconf.client.NetconfClientSessionListener;
-import org.opendaylight.netconf.client.SslHandlerFactory;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategyFactory;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.netconf.nettyutil.handler.ssh.client.NetconfSshClient;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
-
-public final class NetconfReconnectingClientConfigurationBuilder extends NetconfClientConfigurationBuilder {
-
- private ReconnectStrategyFactory connectStrategyFactory;
-
- private NetconfReconnectingClientConfigurationBuilder() {
- }
-
- public static NetconfReconnectingClientConfigurationBuilder create() {
- return new NetconfReconnectingClientConfigurationBuilder();
- }
-
- @SuppressWarnings("checkstyle:hiddenField")
- public NetconfReconnectingClientConfigurationBuilder withConnectStrategyFactory(
- final ReconnectStrategyFactory connectStrategyFactory) {
- this.connectStrategyFactory = connectStrategyFactory;
- return this;
- }
-
- @Override
- public NetconfReconnectingClientConfiguration build() {
- return new NetconfReconnectingClientConfiguration(getProtocol(), getAddress(), getConnectionTimeoutMillis(),
- getAdditionalHeader(), getSessionListener(), getReconnectStrategy(), connectStrategyFactory,
- getAuthHandler(), getSslHandlerFactory(), getSshClient(), getOdlHelloCapabilities(),
- getMaximumIncomingChunkSize(), getName());
- }
-
- // Override setter methods to return subtype
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withAddress(final InetSocketAddress address) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withAddress(address);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withConnectionTimeoutMillis(
- final long connectionTimeoutMillis) {
- return (NetconfReconnectingClientConfigurationBuilder)
- super.withConnectionTimeoutMillis(connectionTimeoutMillis);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withAdditionalHeader(
- final NetconfHelloMessageAdditionalHeader additionalHeader) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withAdditionalHeader(additionalHeader);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withSessionListener(
- final NetconfClientSessionListener sessionListener) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withSessionListener(sessionListener);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withReconnectStrategy(
- final ReconnectStrategy reconnectStrategy) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withReconnectStrategy(reconnectStrategy);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withAuthHandler(final AuthenticationHandler authHandler) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withAuthHandler(authHandler);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withProtocol(
- final NetconfClientConfiguration.NetconfClientProtocol clientProtocol) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withProtocol(clientProtocol);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withSslHandlerFactory(
- final SslHandlerFactory sslHandlerFactory) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withSslHandlerFactory(sslHandlerFactory);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withSshClient(
- final NetconfSshClient sshClient) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withSshClient(sshClient);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withName(final String name) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withName(name);
- }
-
- @Override
- public NetconfReconnectingClientConfigurationBuilder withOdlHelloCapabilities(
- final List<Uri> odlHelloCapabilities) {
- return (NetconfReconnectingClientConfigurationBuilder) super.withOdlHelloCapabilities(odlHelloCapabilities);
- }
-}
* 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.client;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
import java.net.InetSocketAddress;
import java.util.Optional;
-import org.junit.Assert;
import org.junit.Test;
-import org.mockito.Mockito;
import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
public class NetconfClientConfigurationTest {
new NetconfHelloMessageAdditionalHeader("a", "host", "port", "trans", "id");
NetconfClientSessionListener listener = new SimpleNetconfClientSessionListener();
InetSocketAddress address = InetSocketAddress.createUnresolved("host", 830);
- ReconnectStrategy strategy = Mockito.mock(ReconnectStrategy.class);
- AuthenticationHandler handler = Mockito.mock(AuthenticationHandler.class);
+ AuthenticationHandler handler = mock(AuthenticationHandler.class);
NetconfClientConfiguration cfg = NetconfClientConfigurationBuilder.create()
.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
.withAddress(address)
.withConnectionTimeoutMillis(timeout)
- .withReconnectStrategy(strategy)
.withAdditionalHeader(header)
.withSessionListener(listener)
.withAuthHandler(handler).build();
- Assert.assertEquals(timeout, cfg.getConnectionTimeoutMillis());
- Assert.assertEquals(Optional.of(header), cfg.getAdditionalHeader());
- Assert.assertEquals(listener, cfg.getSessionListener());
- Assert.assertEquals(handler, cfg.getAuthHandler());
- Assert.assertEquals(strategy, cfg.getReconnectStrategy());
- Assert.assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, cfg.getProtocol());
- Assert.assertEquals(address, cfg.getAddress());
+ assertEquals(timeout, cfg.getConnectionTimeoutMillis());
+ assertEquals(Optional.of(header), cfg.getAdditionalHeader());
+ assertEquals(listener, cfg.getSessionListener());
+ assertEquals(handler, cfg.getAuthHandler());
+ assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, cfg.getProtocol());
+ assertEquals(address, cfg.getAddress());
- SslHandlerFactory sslHandlerFactory = Mockito.mock(SslHandlerFactory.class);
+ SslHandlerFactory sslHandlerFactory = mock(SslHandlerFactory.class);
NetconfClientConfiguration cfg2 = NetconfClientConfigurationBuilder.create()
.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS)
.withAddress(address)
.withConnectionTimeoutMillis(timeout)
- .withReconnectStrategy(strategy)
.withAdditionalHeader(header)
.withSessionListener(listener)
.withSslHandlerFactory(sslHandlerFactory).build();
- Assert.assertEquals(timeout, cfg2.getConnectionTimeoutMillis());
- Assert.assertEquals(Optional.of(header), cfg2.getAdditionalHeader());
- Assert.assertEquals(listener, cfg2.getSessionListener());
- Assert.assertEquals(sslHandlerFactory, cfg2.getSslHandlerFactory());
- Assert.assertEquals(strategy, cfg2.getReconnectStrategy());
- Assert.assertEquals(NetconfClientConfiguration.NetconfClientProtocol.TLS, cfg2.getProtocol());
- Assert.assertEquals(address, cfg2.getAddress());
+ assertEquals(timeout, cfg2.getConnectionTimeoutMillis());
+ assertEquals(Optional.of(header), cfg2.getAdditionalHeader());
+ assertEquals(listener, cfg2.getSessionListener());
+ assertEquals(sslHandlerFactory, cfg2.getSslHandlerFactory());
+ assertEquals(NetconfClientConfiguration.NetconfClientProtocol.TLS, cfg2.getProtocol());
+ assertEquals(address, cfg2.getAddress());
}
}
import org.mockito.Mockito;
import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.ReconnectFuture;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategyFactory;
+import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
public class NetconfClientDispatcherImplTest {
new NetconfHelloMessageAdditionalHeader("a", "host", "port", "trans", "id");
NetconfClientSessionListener listener = new SimpleNetconfClientSessionListener();
InetSocketAddress address = InetSocketAddress.createUnresolved("host", 830);
- ReconnectStrategyFactory reconnectStrategyFactory = Mockito.mock(ReconnectStrategyFactory.class);
AuthenticationHandler handler = Mockito.mock(AuthenticationHandler.class);
- ReconnectStrategy reconnect = Mockito.mock(ReconnectStrategy.class);
- doReturn(5).when(reconnect).getConnectTimeout();
- doReturn("").when(reconnect).toString();
doReturn("").when(handler).toString();
- doReturn("").when(reconnectStrategyFactory).toString();
- doReturn(reconnect).when(reconnectStrategyFactory).createReconnectStrategy();
- NetconfReconnectingClientConfiguration cfg = NetconfReconnectingClientConfigurationBuilder.create()
+ var cfg = NetconfClientConfigurationBuilder.create()
.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
.withAddress(address)
.withConnectionTimeoutMillis(timeout)
- .withReconnectStrategy(reconnect)
.withAdditionalHeader(header)
.withSessionListener(listener)
- .withConnectStrategyFactory(reconnectStrategyFactory)
.withAuthHandler(handler).build();
- NetconfReconnectingClientConfiguration cfg2 = NetconfReconnectingClientConfigurationBuilder.create()
+ var cfg2 = NetconfClientConfigurationBuilder.create()
.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
.withAddress(address)
.withConnectionTimeoutMillis(timeout)
- .withReconnectStrategy(reconnect)
.withAdditionalHeader(header)
.withSessionListener(listener)
- .withConnectStrategyFactory(reconnectStrategyFactory)
.withAuthHandler(handler).build();
NetconfClientDispatcherImpl dispatcher = new NetconfClientDispatcherImpl(bossGroup, workerGroup, timer);
Future<NetconfClientSession> sshSession = dispatcher.createClient(cfg);
Future<NetconfClientSession> tcpSession = dispatcher.createClient(cfg2);
- ReconnectFuture sshReconn = dispatcher.createReconnectingClient(cfg);
- final ReconnectFuture tcpReconn = dispatcher.createReconnectingClient(cfg2);
+ var sshReconn = dispatcher.createClient(cfg);
+ final var tcpReconn = dispatcher.createClient(cfg2);
assertNotNull(sshSession);
assertNotNull(tcpSession);
assertNotNull(tcpReconn);
SslHandlerFactory sslHandlerFactory = Mockito.mock(SslHandlerFactory.class);
- NetconfReconnectingClientConfiguration cfg3 = NetconfReconnectingClientConfigurationBuilder.create()
+ var cfg3 = NetconfClientConfigurationBuilder.create()
.withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS)
.withAddress(address)
.withConnectionTimeoutMillis(timeout)
- .withReconnectStrategy(reconnect)
.withAdditionalHeader(header)
.withSessionListener(listener)
- .withConnectStrategyFactory(reconnectStrategyFactory)
.withSslHandlerFactory(sslHandlerFactory).build();
Future<NetconfClientSession> tlsSession = dispatcher.createClient(cfg3);
- ReconnectFuture tlsReconn = dispatcher.createReconnectingClient(cfg3);
+ var tlsReconn = dispatcher.createClient(cfg3);
assertNotNull(tlsSession);
assertNotNull(tlsReconn);
+++ /dev/null
-/*
- * Copyright (c) 2014 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.netconf.client;
-
-import java.net.InetSocketAddress;
-import java.util.Optional;
-import org.junit.Assert;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
-import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategy;
-import org.opendaylight.netconf.nettyutil.ReconnectStrategyFactory;
-import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-
-public class NetconfReconnectingClientConfigurationTest {
- @Test
- public void testNetconfReconnectingClientConfiguration() throws Exception {
- Long timeout = 200L;
- NetconfHelloMessageAdditionalHeader header =
- new NetconfHelloMessageAdditionalHeader("a", "host", "port", "trans", "id");
- NetconfClientSessionListener listener = new SimpleNetconfClientSessionListener();
- InetSocketAddress address = InetSocketAddress.createUnresolved("host", 830);
- ReconnectStrategyFactory strategy = Mockito.mock(ReconnectStrategyFactory.class);
- AuthenticationHandler handler = Mockito.mock(AuthenticationHandler.class);
- ReconnectStrategy reconnect = Mockito.mock(ReconnectStrategy.class);
-
- NetconfReconnectingClientConfiguration cfg = NetconfReconnectingClientConfigurationBuilder.create()
- .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
- .withAddress(address)
- .withConnectionTimeoutMillis(timeout)
- .withReconnectStrategy(reconnect)
- .withAdditionalHeader(header)
- .withSessionListener(listener)
- .withConnectStrategyFactory(strategy)
- .withAuthHandler(handler).build();
-
- Assert.assertEquals(timeout, cfg.getConnectionTimeoutMillis());
- Assert.assertEquals(Optional.of(header), cfg.getAdditionalHeader());
- Assert.assertEquals(listener, cfg.getSessionListener());
- Assert.assertEquals(handler, cfg.getAuthHandler());
- Assert.assertEquals(strategy, cfg.getConnectStrategyFactory());
- Assert.assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, cfg.getProtocol());
- Assert.assertEquals(address, cfg.getAddress());
- Assert.assertEquals(reconnect, cfg.getReconnectStrategy());
-
- SslHandlerFactory sslHandlerFactory = Mockito.mock(SslHandlerFactory.class);
- NetconfReconnectingClientConfiguration cfg2 = NetconfReconnectingClientConfigurationBuilder.create()
- .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS)
- .withAddress(address)
- .withConnectionTimeoutMillis(timeout)
- .withReconnectStrategy(reconnect)
- .withAdditionalHeader(header)
- .withSessionListener(listener)
- .withConnectStrategyFactory(strategy)
- .withSslHandlerFactory(sslHandlerFactory).build();
-
- Assert.assertEquals(timeout, cfg2.getConnectionTimeoutMillis());
- Assert.assertEquals(Optional.of(header), cfg2.getAdditionalHeader());
- Assert.assertEquals(listener, cfg2.getSessionListener());
- Assert.assertEquals(sslHandlerFactory, cfg2.getSslHandlerFactory());
- Assert.assertEquals(strategy, cfg2.getConnectStrategyFactory());
- Assert.assertEquals(NetconfClientConfiguration.NetconfClientProtocol.TLS, cfg2.getProtocol());
- Assert.assertEquals(address, cfg2.getAddress());
- Assert.assertEquals(reconnect, cfg2.getReconnectStrategy());
- }
-}
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.GlobalEventExecutor;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetAddress;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.NeverReconnectStrategy;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.SessionIdType;
private static NetconfClientConfiguration getClientConfig(final String host, final int port, final boolean ssh,
final Optional<? extends AuthenticationHandler> maybeAuthHandler) throws UnknownHostException {
InetSocketAddress netconfAddress = new InetSocketAddress(InetAddress.getByName(host), port);
- final NetconfClientConfigurationBuilder b = NetconfClientConfigurationBuilder.create();
- b.withAddress(netconfAddress);
- b.withSessionListener(new SimpleNetconfClientSessionListener());
- b.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
- NetconfClientConfigurationBuilder.DEFAULT_CONNECTION_TIMEOUT_MILLIS));
+ final NetconfClientConfigurationBuilder b = NetconfClientConfigurationBuilder.create()
+ .withAddress(netconfAddress)
+ .withSessionListener(new SimpleNetconfClientSessionListener());
if (ssh) {
- b.withProtocol(NetconfClientProtocol.SSH);
- b.withAuthHandler(maybeAuthHandler.orElseThrow());
+ b.withProtocol(NetconfClientProtocol.SSH).withAuthHandler(maybeAuthHandler.orElseThrow());
} else {
b.withProtocol(NetconfClientProtocol.TCP);
}
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
-import io.netty.util.concurrent.GlobalEventExecutor;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.opendaylight.netconf.client.TestingNetconfClient;
import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.netconf.nettyutil.NeverReconnectStrategy;
import org.opendaylight.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
import org.opendaylight.netconf.server.api.SessionIdProvider;
import org.opendaylight.netconf.server.api.monitoring.Capability;
}
private NetconfClientConfiguration getClientConfig() {
- final NetconfClientConfigurationBuilder b = NetconfClientConfigurationBuilder.create();
- b.withAddress(NETCONF_ADDRESS);
- b.withAdditionalHeader(new NetconfHelloMessageAdditionalHeader("uname", "10.10.10.1", "830", "tcp",
- "client"));
- b.withSessionListener(new SimpleNetconfClientSessionListener());
- b.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
- NetconfClientConfigurationBuilder.DEFAULT_CONNECTION_TIMEOUT_MILLIS));
- return b.build();
+ return NetconfClientConfigurationBuilder.create()
+ .withAddress(NETCONF_ADDRESS)
+ .withAdditionalHeader(
+ new NetconfHelloMessageAdditionalHeader("uname", "10.10.10.1", "830", "tcp", "client"))
+ .withSessionListener(new SimpleNetconfClientSessionListener())
+ .build();
}
}
}