Add the concept of a netconf client name 68/97568/9
authorPeter Puškár <ppuskar@frinx.io>
Thu, 13 Oct 2022 13:25:41 +0000 (15:25 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 6 Nov 2022 18:31:21 +0000 (19:31 +0100)
Clients can be optionally named with a simple string, this is then
propagated to AsyncSshHandler and used for logging purposes.

JIRA: NETCONF-896
Change-Id: Iebb6c094c50ff4646b8ef3e9221cebd3dbebe5e3
Signed-off-by: Zdenko Filip <zfilip@frinx.io>
Signed-off-by: Jaroslav Tóth <jtoth@frinx.io>
Signed-off-by: Peter Puškár <ppuskar@frinx.io>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
netconf/netconf-client/src/main/java/org/opendaylight/netconf/client/NetconfClientDispatcherImpl.java
netconf/netconf-client/src/main/java/org/opendaylight/netconf/client/SshClientChannelInitializer.java
netconf/netconf-client/src/main/java/org/opendaylight/netconf/client/conf/NetconfClientConfiguration.java
netconf/netconf-client/src/main/java/org/opendaylight/netconf/client/conf/NetconfClientConfigurationBuilder.java
netconf/netconf-client/src/main/java/org/opendaylight/netconf/client/conf/NetconfReconnectingClientConfiguration.java
netconf/netconf-client/src/main/java/org/opendaylight/netconf/client/conf/NetconfReconnectingClientConfigurationBuilder.java
netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/client/AsyncSshHandler.java
netconf/netconf-topology-impl/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java

index ac7ecfe4387e7ee2021f52133cc3e143ee746674..ef8fb66d57d9057820661dbb1f0d12a7f0a66ab9 100644 (file)
@@ -91,7 +91,8 @@ public class NetconfClientDispatcherImpl
         return super.createClient(currentConfiguration.getAddress(), currentConfiguration.getReconnectStrategy(),
             (ch, sessionPromise) -> new SshClientChannelInitializer(currentConfiguration.getAuthHandler(),
                         getNegotiatorFactory(currentConfiguration), currentConfiguration.getSessionListener(),
-                        currentConfiguration.getSshClient()).initialize(ch, sessionPromise));
+                        currentConfiguration.getSshClient(), currentConfiguration.getName())
+                    .initialize(ch, sessionPromise));
     }
 
     private ReconnectFuture createReconnectingSshClient(
@@ -99,7 +100,7 @@ public class NetconfClientDispatcherImpl
         LOG.debug("Creating reconnecting SSH client with configuration: {}", currentConfiguration);
         final SshClientChannelInitializer init = new SshClientChannelInitializer(currentConfiguration.getAuthHandler(),
                 getNegotiatorFactory(currentConfiguration), currentConfiguration.getSessionListener(),
-                currentConfiguration.getSshClient());
+                currentConfiguration.getSshClient(), currentConfiguration.getName());
 
         return super.createReconnectingClient(currentConfiguration.getAddress(),
                 currentConfiguration.getConnectStrategyFactory(), init::initialize);
index 81f2d00b9724222973af2bea2ce6f39f788cdcc5..3f2cf220428d2f537ac00ddc1ba499571c9a56f9 100644 (file)
@@ -16,26 +16,30 @@ import org.opendaylight.netconf.nettyutil.handler.ssh.client.NetconfSshClient;
 
 final class SshClientChannelInitializer extends AbstractClientChannelInitializer {
     private final AuthenticationHandler authenticationHandler;
-    private final NetconfSshClient sshClient;
+    private final @Nullable NetconfSshClient sshClient;
+    private final @Nullable String name;
 
     SshClientChannelInitializer(final AuthenticationHandler authHandler,
             final NetconfClientSessionNegotiatorFactory negotiatorFactory,
-            final NetconfClientSessionListener sessionListener, @Nullable final NetconfSshClient sshClient) {
+            final NetconfClientSessionListener sessionListener, final @Nullable NetconfSshClient sshClient,
+            final @Nullable String name) {
         super(negotiatorFactory, sessionListener);
         authenticationHandler = authHandler;
         this.sshClient = sshClient;
+        this.name = name;
     }
 
     SshClientChannelInitializer(final AuthenticationHandler authHandler,
             final NetconfClientSessionNegotiatorFactory negotiatorFactory,
             final NetconfClientSessionListener sessionListener) {
-        this(authHandler, negotiatorFactory, sessionListener, null);
+        this(authHandler, negotiatorFactory, sessionListener, null, null);
     }
 
     @Override
     public void initialize(final Channel ch, final Promise<NetconfClientSession> promise) {
         // ssh handler has to be the first handler in pipeline
-        ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(authenticationHandler, promise, sshClient));
+        var asyncHandler = AsyncSshHandler.createForNetconfSubsystem(authenticationHandler, promise, sshClient, name);
+        ch.pipeline().addFirst(asyncHandler);
         super.initialize(ch, promise);
     }
 }
index c1edb5abc89b0e313ab732b0287a1455c70887ea..1a7338ce8ca27392446b6a2bc3208a42a73be5b2 100644 (file)
@@ -43,6 +43,7 @@ public class NetconfClientConfiguration {
 
     private final List<Uri> odlHelloCapabilities;
     private final @NonNegative int maximumIncomingChunkSize;
+    private final String name;
 
     NetconfClientConfiguration(final NetconfClientProtocol protocol, final InetSocketAddress address,
                                final Long connectionTimeoutMillis,
@@ -50,7 +51,8 @@ public class NetconfClientConfiguration {
                                final NetconfClientSessionListener sessionListener,
                                final ReconnectStrategy reconnectStrategy, final AuthenticationHandler authHandler,
                                final SslHandlerFactory sslHandlerFactory, final NetconfSshClient sshClient,
-                               final List<Uri> odlHelloCapabilities, final @NonNegative int maximumIncomingChunkSize) {
+                               final List<Uri> odlHelloCapabilities, final @NonNegative int maximumIncomingChunkSize,
+                               final String name) {
         this.address = address;
         this.connectionTimeoutMillis = connectionTimeoutMillis;
         this.additionalHeader = additionalHeader;
@@ -62,9 +64,14 @@ public class NetconfClientConfiguration {
         this.sshClient = sshClient;
         this.odlHelloCapabilities = odlHelloCapabilities;
         this.maximumIncomingChunkSize = maximumIncomingChunkSize;
+        this.name = name;
         validateConfiguration();
     }
 
+    public final String getName() {
+        return name;
+    }
+
     public final InetSocketAddress getAddress() {
         return address;
     }
index 09002ab1e430c675c3d17174d0b6aed3e518e979..16aa5c467aab85999ca7b5a2b04806e5e7f977b0 100644 (file)
@@ -39,6 +39,7 @@ public class NetconfClientConfigurationBuilder {
     private List<Uri> odlHelloCapabilities;
     private @NonNegative int maximumIncomingChunkSize =
         AbstractNetconfSessionNegotiator.DEFAULT_MAXIMUM_INCOMING_CHUNK_SIZE;
+    private String name;
 
     protected NetconfClientConfigurationBuilder() {
     }
@@ -103,6 +104,12 @@ public class NetconfClientConfigurationBuilder {
         return this;
     }
 
+    @SuppressWarnings("checkstyle:hiddenField")
+    public NetconfClientConfigurationBuilder withName(final String name) {
+        this.name = name;
+        return this;
+    }
+
     @SuppressWarnings("checkstyle:hiddenField")
     public NetconfClientConfigurationBuilder withOdlHelloCapabilities(final List<Uri> odlHelloCapabilities) {
         this.odlHelloCapabilities = odlHelloCapabilities;
@@ -161,9 +168,13 @@ public class NetconfClientConfigurationBuilder {
         return maximumIncomingChunkSize;
     }
 
+    final String getName() {
+        return name;
+    }
+
     public NetconfClientConfiguration build() {
         return new NetconfClientConfiguration(clientProtocol, address, connectionTimeoutMillis, additionalHeader,
                 sessionListener, reconnectStrategy, authHandler, sslHandlerFactory, sshClient, odlHelloCapabilities,
-                maximumIncomingChunkSize);
+                maximumIncomingChunkSize, name);
     }
 }
index e9d4889d9bdbabf64dcee42711ac6ca68bdfd085..1612b50eb19ee410fd05a65cc97585b5a4c569fd 100644 (file)
@@ -23,7 +23,6 @@ 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,
@@ -36,9 +35,10 @@ public final class NetconfReconnectingClientConfiguration extends NetconfClientC
                                            final SslHandlerFactory sslHandlerFactory,
                                            final NetconfSshClient sshClient,
                                            final List<Uri> odlHelloCapabilities,
-                                           final @NonNegative int maximumIncomingChunkSize) {
+                                           final @NonNegative int maximumIncomingChunkSize,
+                                           final String name) {
         super(clientProtocol, address, connectionTimeoutMillis, additionalHeader, sessionListener, reconnectStrategy,
-                authHandler, sslHandlerFactory, sshClient, odlHelloCapabilities, maximumIncomingChunkSize);
+                authHandler, sslHandlerFactory, sshClient, odlHelloCapabilities, maximumIncomingChunkSize, name);
         this.connectStrategyFactory = connectStrategyFactory;
         validateReconnectConfiguration();
     }
index 177f452f3d17d04993a764beeeb99fefb0795cac..a42d5f2cdd0156a02d2e1f984e513364d7b85697 100644 (file)
@@ -41,7 +41,7 @@ public final class NetconfReconnectingClientConfigurationBuilder extends Netconf
         return new NetconfReconnectingClientConfiguration(getProtocol(), getAddress(), getConnectionTimeoutMillis(),
                 getAdditionalHeader(), getSessionListener(), getReconnectStrategy(), connectStrategyFactory,
                 getAuthHandler(), getSslHandlerFactory(), getSshClient(), getOdlHelloCapabilities(),
-                getMaximumIncomingChunkSize());
+                getMaximumIncomingChunkSize(), getName());
     }
 
     // Override setter methods to return subtype
@@ -99,6 +99,11 @@ public final class NetconfReconnectingClientConfigurationBuilder extends Netconf
         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) {
index b659187b8732dc37dd582318c6854005f491f850..d30ec087beadd7a71e32b7ed173156d95fc3c334 100644 (file)
@@ -73,6 +73,7 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
     private final AuthenticationHandler authenticationHandler;
     private final Future<?> negotiationFuture;
     private final NetconfSshClient sshClient;
+    private final String name;
 
     // Initialized by connect()
     @GuardedBy("this")
@@ -85,11 +86,17 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
 
     private volatile boolean disconnected;
 
-    public AsyncSshHandler(final AuthenticationHandler authenticationHandler, final NetconfSshClient sshClient,
-            final Future<?> negotiationFuture) {
+    private AsyncSshHandler(final AuthenticationHandler authenticationHandler, final NetconfSshClient sshClient,
+            final @Nullable Future<?> negotiationFuture, final @Nullable String name) {
         this.authenticationHandler = requireNonNull(authenticationHandler);
         this.sshClient = requireNonNull(sshClient);
         this.negotiationFuture = negotiationFuture;
+        this.name = name != null && !name.isBlank() ? name : "UNNAMED";
+    }
+
+    public AsyncSshHandler(final AuthenticationHandler authenticationHandler, final NetconfSshClient sshClient,
+            final @Nullable Future<?> negotiationFuture) {
+        this(authenticationHandler, sshClient, negotiationFuture, null);
     }
 
     /**
@@ -115,9 +122,10 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
      * @return                      {@code AsyncSshHandler}
      */
     public static AsyncSshHandler createForNetconfSubsystem(final AuthenticationHandler authenticationHandler,
-            final Future<?> negotiationFuture, final @Nullable NetconfSshClient sshClient) {
+            final Future<?> negotiationFuture, final @Nullable NetconfSshClient sshClient,
+            final @Nullable String name) {
         return new AsyncSshHandler(authenticationHandler, sshClient != null ? sshClient : DEFAULT_CLIENT,
-                negotiationFuture);
+                negotiationFuture, name);
     }
 
     @Override
@@ -128,7 +136,7 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
     @Override
     public synchronized void connect(final ChannelHandlerContext ctx, final SocketAddress remoteAddress,
             final SocketAddress localAddress, final ChannelPromise promise) throws IOException {
-        LOG.debug("SSH session connecting on channel {}. promise: {}", ctx.channel(), promise);
+        LOG.debug("{}: SSH session connecting on channel {}. promise: {}", name, ctx.channel(), promise);
         connectPromise = requireNonNull(promise);
 
         if (negotiationFuture != null) {
@@ -141,7 +149,7 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
             negotiationFuture.addListener(negotiationFutureListener);
         }
 
-        LOG.debug("Starting SSH to {} on channel: {}", remoteAddress, ctx.channel());
+        LOG.debug("{}: Starting SSH to {} on channel: {}", name, remoteAddress, ctx.channel());
         sshClient.connect(authenticationHandler.getUsername(), remoteAddress)
             // FIXME: this is a blocking call, we should handle this with a concurrently-scheduled timeout. We do not
             //        have a Timer ready, so perhaps we should be using the event loop?
@@ -157,7 +165,7 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
         }
 
         final var clientSession = connectFuture.getSession();
-        LOG.trace("SSH session {} created on channel: {}", clientSession, ctx.channel());
+        LOG.trace("{}: SSH session {} created on channel: {}", name, clientSession, ctx.channel());
         verify(clientSession instanceof NettyAwareClientSession, "Unexpected session %s", clientSession);
 
         final var localSession = (NettyAwareClientSession) clientSession;
@@ -182,11 +190,11 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
             return;
         }
         if (disconnected) {
-            LOG.debug("Skipping SSH subsystem allocation, channel: {}", ctx.channel());
+            LOG.debug("{}: Skipping SSH subsystem allocation, channel: {}", name, ctx.channel());
             return;
         }
 
-        LOG.debug("SSH session authenticated on channel: {}, server version: {}", ctx.channel(),
+        LOG.debug("{}: SSH session authenticated on channel: {}, server version: {}", name, ctx.channel(),
             clientSession.getServerVersion());
 
         final OpenFuture openFuture;
@@ -212,11 +220,11 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
             return;
         }
         if (disconnected) {
-            LOG.trace("Skipping activation, channel: {}", ctx.channel());
+            LOG.trace("{}: Skipping activation, channel: {}", name, ctx.channel());
             return;
         }
 
-        LOG.trace("SSH subsystem channel opened successfully on channel: {}", ctx.channel());
+        LOG.trace("{}: SSH subsystem channel opened successfully on channel: {}", name, ctx.channel());
         if (negotiationFuture == null) {
             connectPromise.setSuccess();
         }
@@ -228,7 +236,7 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
 
     @Holding("this")
     private void onOpenFailure(final ChannelHandlerContext ctx, final Throwable cause) {
-        LOG.warn("Unable to setup SSH connection on channel: {}", ctx.channel(), cause);
+        LOG.warn("{}: Unable to setup SSH connection on channel: {}", name, ctx.channel(), cause);
 
         // If the promise is not yet done, we have failed with initial connect and set connectPromise to failure
         if (!connectPromise.isDone()) {
@@ -255,7 +263,7 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
     // the channel's executor.
     @SuppressWarnings("checkstyle:IllegalCatch")
     private synchronized void safelyDisconnect(final ChannelHandlerContext ctx, final ChannelPromise promise) {
-        LOG.trace("Closing SSH session on channel: {} with connect promise in state: {}", ctx.channel(),
+        LOG.trace("{}: Closing SSH session on channel: {} with connect promise in state: {}", name, ctx.channel(),
             connectPromise);
 
         // If we have already succeeded and the session was dropped after,
@@ -300,7 +308,7 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
             // Disconnect has to be closed after inactive channel event was fired, because it interferes with it
             super.disconnect(ctx, ctx.newPromise());
         } catch (final Exception e) {
-            LOG.warn("Unable to cleanup all resources for channel: {}. Ignoring.", ctx.channel(), e);
+            LOG.warn("{}: Unable to cleanup all resources for channel: {}. Ignoring.", name, ctx.channel(), e);
         }
 
         if (channel != null) {
@@ -310,6 +318,6 @@ public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
             channel = null;
         }
         promise.setSuccess();
-        LOG.debug("SSH session closed on channel: {}", ctx.channel());
+        LOG.debug("{}: SSH session closed on channel: {}", name, ctx.channel());
     }
 }
index b3ffa6ea71176e9b9dbd1761903c688d6a26b60d..e8c43a533eb175526c9a2e929479a5981a810a08 100644 (file)
@@ -211,27 +211,27 @@ public class NetconfTopologyImplTest {
                 .setConnectionTimeoutMillis(Uint32.valueOf(20000));
 
         final NetconfReconnectingClientConfiguration configuration =
-                spyTopology.getClientConfig(sessionListener, nodeBuilder.setTcpOnly(true).build());
+                spyTopology.getClientConfig(sessionListener, nodeBuilder.setTcpOnly(true).build(), NODE_ID);
         assertEquals(NetconfClientConfiguration.NetconfClientProtocol.TCP, configuration.getProtocol());
         assertNotNull(configuration.getAuthHandler());
         assertNull(configuration.getSslHandlerFactory());
 
         final NetconfReconnectingClientConfiguration configuration2 =
-                spyTopology.getClientConfig(sessionListener, nodeBuilder.setTcpOnly(false).build());
+                spyTopology.getClientConfig(sessionListener, nodeBuilder.setTcpOnly(false).build(), NODE_ID);
         assertEquals(NetconfClientConfiguration.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());
+                        .setProtocol(new ProtocolBuilder().setName(Name.SSH).build()).build(), NODE_ID);
         assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, configuration3.getProtocol());
         assertNotNull(configuration3.getAuthHandler());
         assertNull(configuration3.getSslHandlerFactory());
 
         final NetconfReconnectingClientConfiguration configuration4 =
                 spyTopology.getClientConfig(sessionListener, nodeBuilder
-                        .setProtocol(new ProtocolBuilder().setName(Name.TLS).build()).build());
+                        .setProtocol(new ProtocolBuilder().setName(Name.TLS).build()).build(), NODE_ID);
         assertEquals(NetconfClientConfiguration.NetconfClientProtocol.TLS, configuration4.getProtocol());
         assertNull(configuration4.getAuthHandler());
         assertNotNull(configuration4.getSslHandlerFactory());
index 740ad3a869867a9cd5e498ce1a2605e27fcb50ea..2a47a126f4c4fb53ac7eae9397c5322db113fa9f 100644 (file)
@@ -171,7 +171,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
         final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
         final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
         final NetconfReconnectingClientConfiguration clientConfig =
-                getClientConfig(netconfClientSessionListener, netconfNode);
+                getClientConfig(netconfClientSessionListener, netconfNode, nodeId);
         final ListenableFuture<NetconfDeviceCapabilities> future =
                 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
 
@@ -298,7 +298,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
     }
 
     public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
-                                                                  final NetconfNode node) {
+                                                                  final NetconfNode node, final NodeId nodeId) {
         final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
                 node.requireMaxConnectionAttempts().toJava(), node.requireBetweenAttemptsTimeoutMillis().toJava(),
                 node.requireSleepFactor().decimalValue());
@@ -326,6 +326,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology {
         }
 
         return reconnectingClientConfigurationBuilder
+                .withName(nodeId.getValue())
                 .withAddress(NetconfNodeUtils.toInetSocketAddress(node))
                 .withConnectionTimeoutMillis(node.requireConnectionTimeoutMillis().toJava())
                 .withReconnectStrategy(sf.createReconnectStrategy())