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(
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);
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);
}
}
private final List<Uri> odlHelloCapabilities;
private final @NonNegative int maximumIncomingChunkSize;
+ private final String name;
NetconfClientConfiguration(final NetconfClientProtocol protocol, final InetSocketAddress address,
final Long connectionTimeoutMillis,
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;
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;
}
private List<Uri> odlHelloCapabilities;
private @NonNegative int maximumIncomingChunkSize =
AbstractNetconfSessionNegotiator.DEFAULT_MAXIMUM_INCOMING_CHUNK_SIZE;
+ private String name;
protected 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;
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);
}
}
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 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();
}
return new NetconfReconnectingClientConfiguration(getProtocol(), getAddress(), getConnectionTimeoutMillis(),
getAdditionalHeader(), getSessionListener(), getReconnectStrategy(), connectStrategyFactory,
getAuthHandler(), getSslHandlerFactory(), getSshClient(), getOdlHelloCapabilities(),
- getMaximumIncomingChunkSize());
+ getMaximumIncomingChunkSize(), getName());
}
// Override setter methods to return subtype
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) {
private final AuthenticationHandler authenticationHandler;
private final Future<?> negotiationFuture;
private final NetconfSshClient sshClient;
+ private final String name;
// Initialized by connect()
@GuardedBy("this")
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);
}
/**
* @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
@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) {
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?
}
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;
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;
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();
}
@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()) {
// 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,
// 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) {
channel = null;
}
promise.setSuccess();
- LOG.debug("SSH session closed on channel: {}", ctx.channel());
+ LOG.debug("{}: SSH session closed on channel: {}", name, ctx.channel());
}
}
.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());
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);
}
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());
}
return reconnectingClientConfigurationBuilder
+ .withName(nodeId.getValue())
.withAddress(NetconfNodeUtils.toInetSocketAddress(node))
.withConnectionTimeoutMillis(node.requireConnectionTimeoutMillis().toJava())
.withReconnectStrategy(sf.createReconnectStrategy())