import org.checkerframework.checker.lock.qual.Holding;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.netconf.shaded.sshd.client.channel.ChannelSubsystem;
import org.opendaylight.netconf.shaded.sshd.client.channel.ClientChannel;
import org.opendaylight.netconf.shaded.sshd.client.future.AuthFuture;
import org.opendaylight.netconf.shaded.sshd.client.future.ConnectFuture;
/**
* Netty SSH handler class. Acts as interface between Netty and SSH library.
*/
+@Deprecated(since = "7.0.0", forRemoval = true)
public final class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(AsyncSshHandler.class);
private static final VarHandle DISCONNECTED;
private final AuthenticationHandler authenticationHandler;
private final Future<?> negotiationFuture;
private final NetconfSshClient sshClient;
+ private final String name;
// Initialized by connect()
@GuardedBy("this")
private ChannelPromise connectPromise;
private AsyncSshHandlerWriter sshWriteAsyncHandler;
- private NettyAwareChannelSubsystem channel;
+ private ChannelSubsystem channel;
private ClientSession session;
private FutureListener<Object> negotiationFutureListener;
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) {
- //TODO: see if calling just close() is sufficient
- //channel.close(false);
- channel.close();
+ channel.close(false);
channel = null;
}
promise.setSuccess();
- LOG.debug("SSH session closed on channel: {}", ctx.channel());
+ LOG.debug("{}: SSH session closed on channel: {}", name, ctx.channel());
}
}