All of this code has been rendered unused, remove it.
JIRA: NETCONF-1108
Change-Id: I3543a42edd7f2464544af6a54036444ee923cf01
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
+++ /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.handler.ssh.authentication;
-
-import java.io.IOException;
-import org.opendaylight.netconf.shaded.sshd.client.future.AuthFuture;
-import org.opendaylight.netconf.shaded.sshd.client.session.ClientSession;
-
-/**
- * Class Providing username/password authentication option to
- * {@link org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandler}.
- */
-public final class LoginPasswordHandler extends AuthenticationHandler {
- private final String username;
- private final String password;
-
- public LoginPasswordHandler(final String username, final String password) {
- this.username = username;
- this.password = password;
- }
-
- @Override
- public String getUsername() {
- return username;
- }
-
- @Override
- public AuthFuture authenticate(final ClientSession session) throws IOException {
- session.addPasswordIdentity(password);
- return session.auth();
- }
-}
+++ /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.nettyutil.handler.ssh.client;
-
-import static com.google.common.base.Verify.verify;
-import static java.util.Objects.requireNonNull;
-
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelOutboundHandlerAdapter;
-import io.netty.channel.ChannelPromise;
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.FutureListener;
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
-import java.net.SocketAddress;
-import java.time.Duration;
-import java.util.concurrent.TimeUnit;
-import org.checkerframework.checker.lock.qual.GuardedBy;
-import org.checkerframework.checker.lock.qual.Holding;
-import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.netconf.api.TransportConstants;
-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;
-import org.opendaylight.netconf.shaded.sshd.client.future.OpenFuture;
-import org.opendaylight.netconf.shaded.sshd.client.session.ClientSession;
-import org.opendaylight.netconf.shaded.sshd.core.CoreModuleProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * 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;
-
- static {
- try {
- DISCONNECTED = MethodHandles.lookup().findVarHandle(AsyncSshHandler.class, "disconnected", boolean.class);
- } catch (NoSuchFieldException | IllegalAccessException e) {
- throw new ExceptionInInitializerError(e);
- }
- }
-
- public static final int SSH_DEFAULT_NIO_WORKERS = 8;
-
- public static final NetconfSshClient DEFAULT_CLIENT;
-
- static {
- final var c = new NetconfClientBuilder().build();
- // Disable default timeouts from mina sshd
- final var zero = Duration.ofMillis(0);
- CoreModuleProperties.AUTH_TIMEOUT.set(c, zero);
- CoreModuleProperties.IDLE_TIMEOUT.set(c, zero);
- CoreModuleProperties.NIO2_READ_TIMEOUT.set(c, zero);
- CoreModuleProperties.TCP_NODELAY.set(c, true);
-
- // TODO make configurable, or somehow reuse netty threadpool
- c.setNioWorkers(SSH_DEFAULT_NIO_WORKERS);
- c.start();
- DEFAULT_CLIENT = c;
- }
-
- 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 ChannelSubsystem channel;
- private ClientSession session;
- private FutureListener<Object> negotiationFutureListener;
-
- private volatile boolean disconnected;
-
- 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);
- }
-
- /**
- * Constructor of {@code AsyncSshHandler}.
- *
- * @param authenticationHandler authentication handler
- * @param sshClient started SshClient
- */
- public AsyncSshHandler(final AuthenticationHandler authenticationHandler, final NetconfSshClient sshClient) {
- this(authenticationHandler, sshClient, null);
- }
-
- public static AsyncSshHandler createForNetconfSubsystem(final AuthenticationHandler authenticationHandler) {
- return new AsyncSshHandler(authenticationHandler, DEFAULT_CLIENT);
- }
-
- /**
- * Create AsyncSshHandler for netconf subsystem. Negotiation future has to be set to success after successful
- * netconf negotiation.
- *
- * @param authenticationHandler authentication handler
- * @param negotiationFuture negotiation future
- * @return {@code AsyncSshHandler}
- */
- public static AsyncSshHandler createForNetconfSubsystem(final AuthenticationHandler authenticationHandler,
- final Future<?> negotiationFuture, final @Nullable NetconfSshClient sshClient,
- final @Nullable String name) {
- return new AsyncSshHandler(authenticationHandler, sshClient != null ? sshClient : DEFAULT_CLIENT,
- negotiationFuture, name);
- }
-
- @Override
- public synchronized void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) {
- sshWriteAsyncHandler.write(ctx, msg, promise);
- }
-
- @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: {}", name, ctx.channel(), promise);
- connectPromise = requireNonNull(promise);
-
- if (negotiationFuture != null) {
- negotiationFutureListener = future -> {
- if (future.isSuccess()) {
- promise.setSuccess();
- }
- };
- //complete connection promise with netconf negotiation future
- negotiationFuture.addListener(negotiationFutureListener);
- }
-
- 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?
- .verify(ctx.channel().config().getConnectTimeoutMillis(), TimeUnit.MILLISECONDS)
- .addListener(future -> onConnectComplete(future, ctx));
- }
-
- private synchronized void onConnectComplete(final ConnectFuture connectFuture, final ChannelHandlerContext ctx) {
- final var cause = connectFuture.getException();
- if (cause != null) {
- onOpenFailure(ctx, cause);
- return;
- }
-
- final var clientSession = connectFuture.getSession();
- LOG.trace("{}: SSH session {} created on channel: {}", name, clientSession, ctx.channel());
- verify(clientSession instanceof NettyAwareClientSession, "Unexpected session %s", clientSession);
-
- final var localSession = (NettyAwareClientSession) clientSession;
- session = localSession;
-
- final AuthFuture authFuture;
- try {
- authFuture = authenticationHandler.authenticate(localSession);
- } catch (final IOException e) {
- onOpenFailure(ctx, e);
- return;
- }
-
- authFuture.addListener(future -> onAuthComplete(future, localSession, ctx));
- }
-
- private synchronized void onAuthComplete(final AuthFuture authFuture, final NettyAwareClientSession clientSession,
- final ChannelHandlerContext ctx) {
- final var cause = authFuture.getException();
- if (cause != null) {
- onOpenFailure(ctx, new AuthenticationFailedException("Authentication failed", cause));
- return;
- }
- if (disconnected) {
- LOG.debug("{}: Skipping SSH subsystem allocation, channel: {}", name, ctx.channel());
- return;
- }
-
- LOG.debug("{}: SSH session authenticated on channel: {}, server version: {}", name, ctx.channel(),
- clientSession.getServerVersion());
-
- final OpenFuture openFuture;
- try {
- channel = clientSession.createSubsystemChannel(TransportConstants.SSH_SUBSYSTEM, ctx);
- channel.setStreaming(ClientChannel.Streaming.Async);
- openFuture = channel.open();
- } catch (final IOException e) {
- onOpenFailure(ctx, e);
- return;
- }
-
- openFuture.addListener(future -> ctx.executor().execute(() -> onOpenComplete(future, ctx)));
- }
-
- // This callback has to run on the channel's executor because it runs fireChannelActive(), which needs to be
- // delivered synchronously. If we were to execute on some other thread we would end up delaying the event,
- // potentially creating havoc in the pipeline.
- private synchronized void onOpenComplete(final OpenFuture openFuture, final ChannelHandlerContext ctx) {
- final var cause = openFuture.getException();
- if (cause != null) {
- onOpenFailure(ctx, cause);
- return;
- }
- if (disconnected) {
- LOG.trace("{}: Skipping activation, channel: {}", name, ctx.channel());
- return;
- }
-
- LOG.trace("{}: SSH subsystem channel opened successfully on channel: {}", name, ctx.channel());
- if (negotiationFuture == null) {
- connectPromise.setSuccess();
- }
-
- sshWriteAsyncHandler = new AsyncSshHandlerWriter(channel.getAsyncIn());
- ctx.fireChannelActive();
- channel.onClose(() -> disconnect(ctx, ctx.newPromise()));
- }
-
- @Holding("this")
- private void onOpenFailure(final ChannelHandlerContext ctx, final Throwable 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()) {
- connectPromise.setFailure(cause);
- }
-
- disconnect(ctx, ctx.newPromise());
- }
-
- @Override
- public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) {
- disconnect(ctx, promise);
- }
-
- @Override
- public void disconnect(final ChannelHandlerContext ctx, final ChannelPromise promise) {
- if (DISCONNECTED.compareAndSet(this, false, true)) {
- ctx.executor().execute(() -> safelyDisconnect(ctx, promise));
- }
- }
-
- // This method has the potential to interact with the channel pipeline, for example via fireChannelInactive(). These
- // callbacks need to complete during execution of this method and therefore this method needs to be executing on
- // 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: {}", name, ctx.channel(),
- connectPromise);
-
- // If we have already succeeded and the session was dropped after,
- // we need to fire inactive to notify reconnect logic
- if (connectPromise.isSuccess()) {
- ctx.fireChannelInactive();
- }
-
- if (sshWriteAsyncHandler != null) {
- sshWriteAsyncHandler.close();
- }
-
- //If connection promise is not already set, it means negotiation failed
- //we must set connection promise to failure
- if (!connectPromise.isDone()) {
- connectPromise.setFailure(new IllegalStateException("Negotiation failed"));
- }
-
- //Remove listener from negotiation future, we don't want notifications
- //from negotiation anymore
- if (negotiationFuture != null) {
- negotiationFuture.removeListener(negotiationFutureListener);
- }
-
- if (session != null && !session.isClosed() && !session.isClosing()) {
- session.close(false).addListener(future -> {
- synchronized (this) {
- if (!future.isClosed()) {
- session.close(true);
- }
- session = null;
- }
- });
- }
-
- // Super disconnect is necessary in this case since we are using NioSocketChannel and it needs
- // to cleanup its resources e.g. Socket that it tries to open in its constructor
- // (https://bugs.opendaylight.org/show_bug.cgi?id=2430)
- // TODO better solution would be to implement custom ChannelFactory + Channel
- // that will use mina SSH lib internally: port this to custom channel implementation
- try {
- // 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.", name, ctx.channel(), e);
- }
-
- if (channel != null) {
- channel.close(false);
- channel = null;
- }
- promise.setSuccess();
- LOG.debug("{}: SSH session closed on channel: {}", name, ctx.channel());
- }
-}
+++ /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.nettyutil.handler.ssh.client;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import org.opendaylight.netconf.shaded.sshd.common.future.SshFutureListener;
-import org.opendaylight.netconf.shaded.sshd.common.io.IoInputStream;
-import org.opendaylight.netconf.shaded.sshd.common.io.IoReadFuture;
-import org.opendaylight.netconf.shaded.sshd.common.util.buffer.Buffer;
-import org.opendaylight.netconf.shaded.sshd.common.util.buffer.ByteArrayBuffer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Listener on async input stream from SSH session.
- * This listeners schedules reads in a loop until the session is closed or read fails.
- */
-@Deprecated(since = "7.0.0", forRemoval = true)
-public final class AsyncSshHandlerReader implements SshFutureListener<IoReadFuture>, AutoCloseable {
- private static final Logger LOG = LoggerFactory.getLogger(AsyncSshHandlerReader.class);
- private static final int BUFFER_SIZE = 2048;
-
- private final AutoCloseable connectionClosedCallback;
- private final ReadMsgHandler readHandler;
-
- private final String channelId;
- private IoInputStream asyncOut;
- private Buffer buf;
- private IoReadFuture currentReadFuture;
-
- public AsyncSshHandlerReader(final AutoCloseable connectionClosedCallback, final ReadMsgHandler readHandler,
- final String channelId, final IoInputStream asyncOut) {
- this.connectionClosedCallback = connectionClosedCallback;
- this.readHandler = readHandler;
- this.channelId = channelId;
- this.asyncOut = asyncOut;
- buf = new ByteArrayBuffer(BUFFER_SIZE);
- asyncOut.read(buf).addListener(this);
- }
-
- @Override
- public void operationComplete(final IoReadFuture future) {
- if (checkDisconnect(future)) {
- invokeDisconnect();
- }
- }
-
- private synchronized boolean checkDisconnect(final IoReadFuture future) {
- if (future.getException() != null) {
- //if asyncout is already set to null by close method, do nothing
- if (asyncOut == null) {
- return false;
- }
-
- if (asyncOut.isClosed() || asyncOut.isClosing()) {
- // Ssh dropped
- LOG.debug("Ssh session dropped on channel: {}", channelId, future.getException());
- } else {
- LOG.warn("Exception while reading from SSH remote on channel {}", channelId, future.getException());
- }
- return true;
- } else if (future.getRead() > 0) {
- final ByteBuf msg = Unpooled.wrappedBuffer(buf.array(), 0, future.getRead());
- if (LOG.isTraceEnabled()) {
- LOG.trace("Reading message on channel: {}, message: {}",
- channelId, AsyncSshHandlerWriter.byteBufToString(msg));
- }
- readHandler.onMessageRead(msg);
-
- // Schedule next read
- buf = new ByteArrayBuffer(BUFFER_SIZE);
- currentReadFuture = asyncOut.read(buf);
- currentReadFuture.addListener(this);
- }
- return false;
- }
-
- /**
- * Closing of the {@link AsyncSshHandlerReader}. This method should never be called with any locks held since
- * call to {@link AutoCloseable#close()} can be a source of ABBA deadlock.
- */
- @SuppressWarnings("checkstyle:IllegalCatch")
- private void invokeDisconnect() {
- try {
- connectionClosedCallback.close();
- } catch (final Exception e) {
- // This should not happen
- throw new IllegalStateException(e);
- }
- }
-
- @Override
- public synchronized void close() {
- // Remove self as listener on close to prevent reading from closed input
- if (currentReadFuture != null) {
- currentReadFuture.removeListener(this);
- currentReadFuture = null;
- }
-
- asyncOut = null;
- }
-
- public interface ReadMsgHandler {
-
- void onMessageRead(ByteBuf msg);
- }
-}
+++ /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.nettyutil.handler.ssh.client;
-
-import static com.google.common.base.Preconditions.checkState;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPromise;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Deque;
-import java.util.LinkedList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.checkerframework.checker.lock.qual.GuardedBy;
-import org.opendaylight.netconf.shaded.sshd.common.io.IoOutputStream;
-import org.opendaylight.netconf.shaded.sshd.common.io.WritePendingException;
-import org.opendaylight.netconf.shaded.sshd.common.util.buffer.Buffer;
-import org.opendaylight.netconf.shaded.sshd.common.util.buffer.ByteArrayBuffer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Async Ssh writer. Takes messages(byte arrays) and sends them encrypted to remote server.
- * Also handles pending writes by caching requests until pending state is over.
- */
-@Deprecated(since = "7.0.0", forRemoval = true)
-public final class AsyncSshHandlerWriter implements AutoCloseable {
- private static final Logger LOG = LoggerFactory.getLogger(AsyncSshHandlerWriter.class);
- private static final Pattern NON_ASCII = Pattern.compile("([^\\x20-\\x7E\\x0D\\x0A])+");
-
- // public static final int MAX_PENDING_WRITES = 1000;
- // TODO implement Limiting mechanism for pending writes
- // But there is a possible issue with limiting:
- // 1. What to do when queue is full ? Immediate Fail for every request ?
- // 2. At this level we might be dealing with Chunks of messages(not whole messages)
- // and unexpected behavior might occur when we send/queue 1 chunk and fail the other chunks
-
- private final Object asyncInLock = new Object();
- private volatile IoOutputStream asyncIn;
-
- // Order has to be preserved for queued writes
- private final Deque<PendingWriteRequest> pending = new LinkedList<>();
-
- @GuardedBy("asyncInLock")
- private boolean isWriteExecuted = false;
-
- public AsyncSshHandlerWriter(final IoOutputStream asyncIn) {
- this.asyncIn = asyncIn;
- }
-
- public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) {
- if (asyncIn == null) {
- promise.setFailure(new IllegalStateException("Channel closed"));
- return;
- }
- // synchronized block due to deadlock that happens on ssh window resize
- // writes and pending writes would lock the underlyinch channel session
- // window resize write would try to write the message on an already locked channelSession
- // while the pending write was in progress from the write callback
- synchronized (asyncInLock) {
- // TODO check for isClosed, isClosing might be performed by mina SSH internally and is not required here
- // If we are closed/closing, set immediate fail
- if (asyncIn.isClosed() || asyncIn.isClosing()) {
- promise.setFailure(new IllegalStateException("Channel closed"));
- } else {
- final ByteBuf byteBufMsg = (ByteBuf) msg;
- if (isWriteExecuted) {
- queueRequest(ctx, byteBufMsg, promise);
- return;
- }
-
- writeWithPendingDetection(ctx, promise, byteBufMsg, false);
- }
- }
- }
-
- //sending message with pending
- //if resending message not succesfull, then attribute wasPending is true
- private void writeWithPendingDetection(final ChannelHandlerContext ctx, final ChannelPromise promise,
- final ByteBuf byteBufMsg, final boolean wasPending) {
- try {
-
- if (LOG.isTraceEnabled()) {
- LOG.trace("Writing request on channel: {}, message: {}", ctx.channel(), byteBufToString(byteBufMsg));
- }
-
- isWriteExecuted = true;
-
- asyncIn.writeBuffer(toBuffer(byteBufMsg)).addListener(future -> {
- // synchronized block due to deadlock that happens on ssh window resize
- // writes and pending writes would lock the underlying channel session
- // window resize write would try to write the message on an already locked channelSession,
- // while the pending write was in progress from the write callback
- synchronized (asyncInLock) {
- final var cause = future.getException();
- if (LOG.isTraceEnabled()) {
- LOG.trace("Ssh write request finished on channel: {} with ex: {}, message: {}", ctx.channel(),
- cause, byteBufToString(byteBufMsg));
- }
-
- // Notify success or failure
- if (cause != null) {
- LOG.warn("Ssh write request failed on channel: {} for message: {}", ctx.channel(),
- byteBufToString(byteBufMsg), cause);
- promise.setFailure(cause);
- } else {
- promise.setSuccess();
- }
-
- //rescheduling message from queue after successfully sent
- if (wasPending) {
- byteBufMsg.resetReaderIndex();
- pending.remove();
- }
-
- // Not needed anymore, release
- byteBufMsg.release();
- }
-
- // Check pending queue and schedule next
- // At this time we are guaranteed that we are not in pending state anymore
- // so the next request should succeed
- writePendingIfAny();
- });
-
- } catch (final IOException | WritePendingException e) {
- if (!wasPending) {
- queueRequest(ctx, byteBufMsg, promise);
- }
- }
- }
-
- private void writePendingIfAny() {
- synchronized (asyncInLock) {
- final PendingWriteRequest pendingWrite = pending.peek();
- if (pendingWrite == null) {
- isWriteExecuted = false;
- return;
- }
-
- final ByteBuf msg = pendingWrite.msg;
- if (LOG.isTraceEnabled()) {
- LOG.trace("Writing pending request on channel: {}, message: {}",
- pendingWrite.ctx.channel(), byteBufToString(msg));
- }
-
- writeWithPendingDetection(pendingWrite.ctx, pendingWrite.promise, msg, true);
- }
- }
-
- public static String byteBufToString(final ByteBuf msg) {
- final String message = msg.toString(StandardCharsets.UTF_8);
- msg.resetReaderIndex();
- Matcher matcher = NON_ASCII.matcher(message);
- return matcher.replaceAll(data -> {
- StringBuilder buf = new StringBuilder();
- buf.append("\"");
- for (byte b : data.group().getBytes(StandardCharsets.US_ASCII)) {
- buf.append(String.format("%02X", b));
- }
- buf.append("\"");
- return buf.toString();
- });
- }
-
- private void queueRequest(final ChannelHandlerContext ctx, final ByteBuf msg, final ChannelPromise promise) {
- LOG.debug("Write pending on channel: {}, queueing, current queue size: {}", ctx.channel(), pending.size());
- if (LOG.isTraceEnabled()) {
- LOG.trace("Queueing request due to pending: {}", byteBufToString(msg));
- }
-
-// try {
- final var req = new PendingWriteRequest(ctx, msg, promise);
- // Preconditions.checkState(pending.size() < MAX_PENDING_WRITES,
- // "Too much pending writes(%s) on channel: %s, remote window is not getting read or is too small",
- // pending.size(), ctx.channel());
- checkState(pending.offer(req), "Cannot pend another request write (pending count: %s) on channel: %s",
- pending.size(), ctx.channel());
-// } catch (final Exception ex) {
-// LOG.warn("Unable to queue write request on channel: {}. Setting fail for the request: {}",
-// ctx.channel(), ex, byteBufToString(msg));
-// msg.release();
-// promise.setFailure(ex);
-// }
- }
-
- @Override
- public void close() {
- asyncIn = null;
- }
-
- private static Buffer toBuffer(final ByteBuf msg) {
- // TODO Buffer vs ByteBuf translate, Can we handle that better ?
- msg.resetReaderIndex();
- final byte[] temp = new byte[msg.readableBytes()];
- msg.readBytes(temp, 0, msg.readableBytes());
- return new ByteArrayBuffer(temp);
- }
-
- private static final class PendingWriteRequest {
- private final ChannelHandlerContext ctx;
- private final ByteBuf msg;
- private final ChannelPromise promise;
-
- PendingWriteRequest(final ChannelHandlerContext ctx, final ByteBuf msg, final ChannelPromise promise) {
- this.ctx = ctx;
- // Reset reader index, last write (failed) attempt moved index to the end
- msg.resetReaderIndex();
- this.msg = msg;
- this.promise = promise;
- }
- }
-}
+++ /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.nettyutil.handler.ssh.client;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.annotations.Beta;
-import org.opendaylight.netconf.shaded.sshd.common.SshException;
-
-/**
- * Exception reported when endpoint authentication fails.
- */
-@Beta
-public class AuthenticationFailedException extends SshException {
- @java.io.Serial
- private static final long serialVersionUID = 1L;
-
- public AuthenticationFailedException(final String message) {
- super(requireNonNull(message));
- }
-
- public AuthenticationFailedException(final String message, final Throwable cause) {
- super(requireNonNull(message), cause);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2019 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.handler.ssh.client;
-
-import static com.google.common.base.Verify.verify;
-
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import java.util.stream.Stream;
-import org.opendaylight.netconf.shaded.sshd.client.ClientBuilder;
-import org.opendaylight.netconf.shaded.sshd.client.SshClient;
-import org.opendaylight.netconf.shaded.sshd.common.NamedFactory;
-import org.opendaylight.netconf.shaded.sshd.common.kex.BuiltinDHFactories;
-import org.opendaylight.netconf.shaded.sshd.common.kex.KeyExchangeFactory;
-import org.opendaylight.netconf.shaded.sshd.common.signature.BuiltinSignatures;
-import org.opendaylight.netconf.shaded.sshd.common.signature.Signature;
-
-/**
- * A {@link ClientBuilder} which builds {@link NetconfSshClient} instances.
- */
-@Deprecated(since = "7.0.0", forRemoval = true)
-public class NetconfClientBuilder extends ClientBuilder {
- // RFC8332 rsa-sha2-256/rsa-sha2-512 are not a part of Mina's default set of signatures for clients as of 2.5.1.
- // Add them to ensure interop with modern highly-secured devices.
- private static final ImmutableList<NamedFactory<Signature>> FULL_SIGNATURE_PREFERENCE =
- Stream.concat(DEFAULT_SIGNATURE_PREFERENCE.stream(), Stream.of(
- BuiltinSignatures.rsaSHA512, BuiltinSignatures.rsaSHA256))
- .filter(BuiltinSignatures::isSupported)
- .distinct()
- .collect(ImmutableList.toImmutableList());
-
- // The SHA1 algorithm is disabled by default in Mina SSHD since 2.6.0.
- // More details available here: https://issues.apache.org/jira/browse/SSHD-1004
- // This block adds diffie-hellman-group14-sha1 back to the list of supported algorithms.
- private static final ImmutableList<BuiltinDHFactories> FULL_DH_FACTORIES_LIST =
- Stream.concat(DEFAULT_KEX_PREFERENCE.stream(), Stream.of(BuiltinDHFactories.dhg14))
- .collect(ImmutableList.toImmutableList());
- private static final List<KeyExchangeFactory> FULL_KEX_PREFERENCE =
- NamedFactory.setUpTransformedFactories(true, FULL_DH_FACTORIES_LIST, DH2KEX);
-
- @Override
- public NetconfSshClient build() {
- final SshClient client = super.build();
- verify(client instanceof NetconfSshClient, "Unexpected client %s", client);
- return (NetconfSshClient) client;
- }
-
- @Override
- protected ClientBuilder fillWithDefaultValues() {
- if (factory == null) {
- factory = NetconfSshClient::new;
- }
- if (signatureFactories == null) {
- signatureFactories = FULL_SIGNATURE_PREFERENCE;
- }
- if (keyExchangeFactories == null) {
- keyExchangeFactories = FULL_KEX_PREFERENCE;
- }
- return super.fillWithDefaultValues();
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2019 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.handler.ssh.client;
-
-import static java.util.Objects.requireNonNull;
-
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPipeline;
-import java.io.IOException;
-import org.opendaylight.netconf.shaded.sshd.client.ClientFactoryManager;
-import org.opendaylight.netconf.shaded.sshd.client.channel.ChannelSubsystem;
-import org.opendaylight.netconf.shaded.sshd.client.session.ClientSessionImpl;
-import org.opendaylight.netconf.shaded.sshd.common.io.IoSession;
-
-/**
- * A {@link ClientSessionImpl} which additionally allows creation of NETCONF subsystem channel, which is routed to
- * a particular {@link ChannelHandlerContext}.
- */
-@Deprecated(since = "7.0.0", forRemoval = true)
-public final class NetconfClientSessionImpl extends ClientSessionImpl implements NettyAwareClientSession {
- public NetconfClientSessionImpl(final ClientFactoryManager client, final IoSession ioSession) throws Exception {
- super(client, ioSession);
- }
-
- @Override
- public ChannelSubsystem createSubsystemChannel(final String subsystem, final ChannelHandlerContext ctx)
- throws IOException {
- requireNonNull(ctx);
- return registerSubsystem(new NettyChannelSubsystem(subsystem) {
- @Override
- ChannelHandlerContext context() {
- return ctx;
- }
- });
- }
-
- @Override
- public ChannelSubsystem createSubsystemChannel(final String subsystem,
- final ChannelPipeline pipeline) throws IOException {
- requireNonNull(pipeline);
- return registerSubsystem(new NettyChannelSubsystem(subsystem) {
- @Override
- ChannelHandlerContext context() {
- return pipeline.firstContext();
- }
- });
- }
-
- private ChannelSubsystem registerSubsystem(final ChannelSubsystem subsystem) throws IOException {
- final var service = getConnectionService();
- final var id = service.registerChannel(subsystem);
- if (log.isDebugEnabled()) {
- log.debug("createSubsystemChannel({})[{}] created id={}", this, subsystem.getSubsystem(), id);
- }
- return subsystem;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2019 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.handler.ssh.client;
-
-import org.opendaylight.netconf.shaded.sshd.client.ClientFactoryManager;
-import org.opendaylight.netconf.shaded.sshd.client.session.SessionFactory;
-import org.opendaylight.netconf.shaded.sshd.common.io.IoSession;
-
-/**
- * A {@link SessionFactory} which creates {@link NetconfClientSessionImpl}s.
- */
-@Deprecated(since = "7.0.0", forRemoval = true)
-public class NetconfSessionFactory extends SessionFactory {
- public NetconfSessionFactory(final ClientFactoryManager client) {
- super(client);
- }
-
- @Override
- protected NetconfClientSessionImpl doCreateSession(final IoSession ioSession) throws Exception {
- return new NetconfClientSessionImpl(getClient(), ioSession);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2019 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.handler.ssh.client;
-
-import org.opendaylight.netconf.shaded.sshd.client.SshClient;
-
-/**
- * An extension to {@link SshClient} which uses {@link NetconfSessionFactory} to create sessions (leading towards
- * {@link NetconfClientSessionImpl}.
- */
-@Deprecated(since = "7.0.0", forRemoval = true)
-public class NetconfSshClient extends SshClient {
- @Override
- protected NetconfSessionFactory createSessionFactory() {
- return new NetconfSessionFactory(this);
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2019 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.handler.ssh.client;
-
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPipeline;
-import java.io.IOException;
-import org.opendaylight.netconf.shaded.sshd.client.channel.ChannelSubsystem;
-import org.opendaylight.netconf.shaded.sshd.client.session.ClientSession;
-
-/**
- * A {@link ClientSession} which additionally allows subsystem channels which are forwarded to a particular Netty
- * channel context.
- */
-@Deprecated(since = "7.0.0", forRemoval = true)
-public interface NettyAwareClientSession extends ClientSession {
- /**
- * Allocate a channel to the specified subsystem. Incoming data on the channel will be routed to the
- * specified {@link ChannelHandlerContext}.
- *
- * @param subsystem The subsystem name
- * @param ctx Context to which to route data to
- * @return The created {@link ChannelSubsystem}
- * @throws IOException If failed to create the requested channel
- */
- ChannelSubsystem createSubsystemChannel(String subsystem, ChannelHandlerContext ctx) throws IOException;
-
- /**
- * Allocate a channel to the specified subsystem. Incoming data on the channel will be routed to the
- * specified {@link ChannelPipeline}.
- *
- * @param subsystem The subsystem name
- * @param pipeline ChannelPipeline to which to route data to
- * @return The created {@link ChannelSubsystem}
- * @throws IOException If failed to create the requested channel
- */
- ChannelSubsystem createSubsystemChannel(String subsystem, ChannelPipeline pipeline) throws IOException;
-}
+++ /dev/null
-/*
- * Copyright (c) 2023 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.handler.ssh.client;
-
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandlerContext;
-import java.io.IOException;
-import org.opendaylight.netconf.shaded.sshd.client.channel.ChannelSubsystem;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Abstract base class for {@link ChannelSubsystem}s backed by a Netty {@link ChannelHandlerContext}.
- */
-@Deprecated(since = "7.0.0", forRemoval = true)
-abstract class NettyChannelSubsystem extends ChannelSubsystem {
- private static final Logger LOG = LoggerFactory.getLogger(NettyChannelSubsystem.class);
-
- NettyChannelSubsystem(final String subsystem) {
- super(subsystem);
- }
-
- @Override
- public final void close() {
- close(false);
- }
-
- @Override
- protected final void doWriteExtendedData(final byte[] data, final int off, final long len) throws IOException {
- // If we're already closing, ignore incoming data
- if (isClosing()) {
- return;
- }
-
- LOG.debug("Discarding {} bytes of extended data", len);
- if (len > 0) {
- getLocalWindow().release(len);
- }
- }
-
- @Override
- protected final void doWriteData(final byte[] data, final int off, final long len) throws IOException {
- // If we're already closing, ignore incoming data
- if (isClosing()) {
- return;
- }
-
- // TODO: consider using context's allocator for heap buffer here
- final int reqLen = (int) len;
- context().fireChannelRead(Unpooled.copiedBuffer(data, off, reqLen));
- if (reqLen > 0) {
- getLocalWindow().release(reqLen);
- }
- }
-
- abstract ChannelHandlerContext context();
-}
+++ /dev/null
-/*
- * Copyright (c) 2019 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
- */
-/**
- * Utilities for integration between Apache SSHD and Netty. Contains the wiring logic to extend SshClient to allow
- * efficient shuffling of data towards the Netty channel.
- */
-package org.opendaylight.netconf.nettyutil.handler.ssh.client;
\ No newline at end of file
+++ /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.nettyutil.handler.ssh.authentication;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Test;
-import org.opendaylight.netconf.shaded.sshd.client.future.AuthFuture;
-import org.opendaylight.netconf.shaded.sshd.client.session.ClientSession;
-
-public class LoginPasswordHandlerTest {
- @Test
- public void testLoginPassword() throws Exception {
- final LoginPasswordHandler loginPasswordHandler = new LoginPasswordHandler("user", "pwd");
- assertEquals("user", loginPasswordHandler.getUsername());
-
- final ClientSession session = mock(ClientSession.class);
- doNothing().when(session).addPasswordIdentity("pwd");
- doReturn(mock(AuthFuture.class)).when(session).auth();
- loginPasswordHandler.authenticate(session);
-
- verify(session).addPasswordIdentity("pwd");
- verify(session).auth();
- }
-}
+++ /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.nettyutil.handler.ssh.client;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-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.MoreExecutors;
-import com.google.common.util.concurrent.SettableFuture;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelConfig;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPromise;
-import io.netty.channel.DefaultChannelPromise;
-import io.netty.util.concurrent.EventExecutor;
-import java.io.IOException;
-import java.net.SocketAddress;
-import java.util.concurrent.TimeUnit;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-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;
-import org.opendaylight.netconf.shaded.sshd.client.future.OpenFuture;
-import org.opendaylight.netconf.shaded.sshd.client.session.ClientSession;
-import org.opendaylight.netconf.shaded.sshd.common.future.CloseFuture;
-import org.opendaylight.netconf.shaded.sshd.common.future.SshFuture;
-import org.opendaylight.netconf.shaded.sshd.common.future.SshFutureListener;
-import org.opendaylight.netconf.shaded.sshd.common.io.IoInputStream;
-import org.opendaylight.netconf.shaded.sshd.common.io.IoOutputStream;
-import org.opendaylight.netconf.shaded.sshd.common.io.IoReadFuture;
-import org.opendaylight.netconf.shaded.sshd.common.io.IoWriteFuture;
-import org.opendaylight.netconf.shaded.sshd.common.io.WritePendingException;
-import org.opendaylight.netconf.shaded.sshd.common.util.buffer.Buffer;
-import org.opendaylight.netconf.shaded.sshd.common.util.buffer.ByteArrayBuffer;
-
-@RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class AsyncSshHandlerTest {
-
- @Mock
- private NetconfSshClient sshClient;
- @Mock
- private AuthenticationHandler authHandler;
- @Mock
- private ChannelHandlerContext ctx;
- @Mock
- private Channel channel;
- @Mock
- private SocketAddress remoteAddress;
- @Mock
- private SocketAddress localAddress;
- @Mock
- private ChannelConfig channelConfig;
- @Mock
- private EventExecutor executor;
-
- private AsyncSshHandler asyncSshHandler;
-
- private SshFutureListener<ConnectFuture> sshConnectListener;
- private SshFutureListener<AuthFuture> sshAuthListener;
- private SshFutureListener<OpenFuture> sshChannelOpenListener;
- private ChannelPromise promise;
-
- @Before
- public void setUp() throws Exception {
- stubAuth();
- stubSshClient();
- stubChannel();
- stubCtx();
-
- promise = getMockedPromise();
-
- asyncSshHandler = new AsyncSshHandler(authHandler, sshClient);
- }
-
- @After
- public void tearDown() throws Exception {
- sshConnectListener = null;
- sshAuthListener = null;
- sshChannelOpenListener = null;
- promise = null;
- asyncSshHandler.close(ctx, getMockedPromise());
- }
-
- private void stubAuth() throws IOException {
- doReturn("usr").when(authHandler).getUsername();
-
- final AuthFuture authFuture = mock(AuthFuture.class);
- Futures.addCallback(stubAddListener(authFuture), new SuccessFutureListener<AuthFuture>() {
- @Override
- public void onSuccess(final SshFutureListener<AuthFuture> result) {
- sshAuthListener = result;
- }
- }, MoreExecutors.directExecutor());
- doReturn(authFuture).when(authHandler).authenticate(any(ClientSession.class));
- }
-
- @SuppressWarnings("unchecked")
- private static <T extends SshFuture<T>> ListenableFuture<SshFutureListener<T>> stubAddListener(final T future) {
- final SettableFuture<SshFutureListener<T>> listenerSettableFuture = SettableFuture.create();
-
- doAnswer(invocation -> {
- listenerSettableFuture.set((SshFutureListener<T>) invocation.getArguments()[0]);
- return null;
- }).when(future).addListener(any(SshFutureListener.class));
-
- return listenerSettableFuture;
- }
-
- private void stubCtx() {
- doReturn(channel).when(ctx).channel();
- doReturn(ctx).when(ctx).fireChannelActive();
- doReturn(ctx).when(ctx).fireChannelInactive();
- doReturn(mock(ChannelFuture.class)).when(ctx).disconnect(any(ChannelPromise.class));
- doReturn(getMockedPromise()).when(ctx).newPromise();
- doReturn(executor).when(ctx).executor();
- doAnswer(invocation -> {
- invocation.getArgument(0, Runnable.class).run();
- return null;
- }).when(executor).execute(any());
- }
-
- private void stubChannel() {
- doReturn("channel").when(channel).toString();
- }
-
- private void stubSshClient() throws IOException {
- final ConnectFuture connectFuture = mock(ConnectFuture.class);
- Futures.addCallback(stubAddListener(connectFuture), new SuccessFutureListener<ConnectFuture>() {
- @Override
- public void onSuccess(final SshFutureListener<ConnectFuture> result) {
- sshConnectListener = result;
- }
- }, MoreExecutors.directExecutor());
- doReturn(connectFuture).when(sshClient).connect("usr", remoteAddress);
- doReturn(channelConfig).when(channel).config();
- doReturn(1).when(channelConfig).getConnectTimeoutMillis();
- doReturn(connectFuture).when(connectFuture).verify(1,TimeUnit.MILLISECONDS);
- }
-
- @Test
- public void testConnectSuccess() throws Exception {
- asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
-
- final IoInputStream asyncOut = getMockedIoInputStream();
- final IoOutputStream asyncIn = getMockedIoOutputStream();
- final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
- final ClientSession sshSession = getMockedSshSession(subsystemChannel);
- final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
-
- sshConnectListener.operationComplete(connectFuture);
- sshAuthListener.operationComplete(getSuccessAuthFuture());
- sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
-
- verify(subsystemChannel).setStreaming(ClientChannel.Streaming.Async);
-
- verify(promise).setSuccess();
- verify(ctx).fireChannelActive();
- asyncSshHandler.close(ctx, getMockedPromise());
- verify(ctx).fireChannelInactive();
- }
-
- @Test
- public void testWrite() throws Exception {
- asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
-
- final IoInputStream asyncOut = getMockedIoInputStream();
- final IoOutputStream asyncIn = getMockedIoOutputStream();
- final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
- final ClientSession sshSession = getMockedSshSession(subsystemChannel);
- final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
-
- sshConnectListener.operationComplete(connectFuture);
- sshAuthListener.operationComplete(getSuccessAuthFuture());
- sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
-
- final ChannelPromise writePromise = getMockedPromise();
- asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0, 1, 2, 3, 4, 5}), writePromise);
-
- verify(writePromise).setSuccess();
- }
-
- @Test
- public void testWriteClosed() throws Exception {
- asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
-
- final IoInputStream asyncOut = getMockedIoInputStream();
- final IoOutputStream asyncIn = getMockedIoOutputStream();
-
- final IoWriteFuture ioWriteFuture = asyncIn.writeBuffer(new ByteArrayBuffer());
-
- Futures.addCallback(stubAddListener(ioWriteFuture), new SuccessFutureListener<IoWriteFuture>() {
- @Override
- public void onSuccess(final SshFutureListener<IoWriteFuture> result) {
- doReturn(new IllegalStateException()).when(ioWriteFuture).getException();
- result.operationComplete(ioWriteFuture);
- }
- }, MoreExecutors.directExecutor());
-
- final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
- final ClientSession sshSession = getMockedSshSession(subsystemChannel);
- final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
-
- sshConnectListener.operationComplete(connectFuture);
- sshAuthListener.operationComplete(getSuccessAuthFuture());
- sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
-
- final ChannelPromise writePromise = getMockedPromise();
- asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0,1,2,3,4,5}), writePromise);
-
- verify(writePromise).setFailure(any(Throwable.class));
- }
-
- @Test
- public void testWritePendingOne() throws Exception {
- asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
-
- final IoInputStream asyncOut = getMockedIoInputStream();
- final IoOutputStream asyncIn = getMockedIoOutputStream();
- final IoWriteFuture ioWriteFuture = asyncIn.writeBuffer(new ByteArrayBuffer());
-
- final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
- final ClientSession sshSession = getMockedSshSession(subsystemChannel);
- final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
-
- sshConnectListener.operationComplete(connectFuture);
- sshAuthListener.operationComplete(getSuccessAuthFuture());
- sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
-
- final ChannelPromise firstWritePromise = getMockedPromise();
-
- // intercept listener for first write,
- // so we can invoke successful write later thus simulate pending of the first write
- final ListenableFuture<SshFutureListener<IoWriteFuture>> firstWriteListenerFuture =
- stubAddListener(ioWriteFuture);
- asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0,1,2,3,4,5}), firstWritePromise);
- final SshFutureListener<IoWriteFuture> firstWriteListener = firstWriteListenerFuture.get();
- // intercept second listener,
- // this is the listener for pending write for the pending write to know when pending state ended
- final ListenableFuture<SshFutureListener<IoWriteFuture>> pendingListener = stubAddListener(ioWriteFuture);
-
- final ChannelPromise secondWritePromise = getMockedPromise();
- asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0, 1, 2, 3, 4, 5}), secondWritePromise);
-
- doReturn(ioWriteFuture).when(asyncIn).writeBuffer(any(Buffer.class));
-
- verifyNoMoreInteractions(firstWritePromise, secondWritePromise);
-
- // make first write stop pending
- firstWriteListener.operationComplete(ioWriteFuture);
-
- // notify listener for second write that pending has ended
- pendingListener.get().operationComplete(ioWriteFuture);
-
- // verify both write promises successful
- verify(firstWritePromise).setSuccess();
- verify(secondWritePromise).setSuccess();
- }
-
- @Ignore("Pending queue is not limited")
- @Test
- public void testWritePendingMax() throws Exception {
- asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
-
- final IoInputStream asyncOut = getMockedIoInputStream();
- final IoOutputStream asyncIn = getMockedIoOutputStream();
- final IoWriteFuture ioWriteFuture = asyncIn.writeBuffer(new ByteArrayBuffer());
-
- final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
- final ClientSession sshSession = getMockedSshSession(subsystemChannel);
- final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
-
- sshConnectListener.operationComplete(connectFuture);
- sshAuthListener.operationComplete(getSuccessAuthFuture());
- sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
-
- final ChannelPromise firstWritePromise = getMockedPromise();
-
- // intercept listener for first write,
- // so we can invoke successful write later thus simulate pending of the first write
- final ListenableFuture<SshFutureListener<IoWriteFuture>> firstWriteListenerFuture =
- stubAddListener(ioWriteFuture);
- asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0,1,2,3,4,5}), firstWritePromise);
-
- final ChannelPromise secondWritePromise = getMockedPromise();
- // now make write throw pending exception
- doThrow(WritePendingException.class).when(asyncIn).writeBuffer(any(Buffer.class));
- for (int i = 0; i < 1001; i++) {
- asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0, 1, 2, 3, 4, 5}), secondWritePromise);
- }
-
- verify(secondWritePromise, times(1)).setFailure(any(Throwable.class));
- }
-
- @Test
- public void testDisconnect() throws Exception {
- asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
-
- final IoInputStream asyncOut = getMockedIoInputStream();
- final IoOutputStream asyncIn = getMockedIoOutputStream();
- final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
- final ClientSession sshSession = getMockedSshSession(subsystemChannel);
- final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
-
- sshConnectListener.operationComplete(connectFuture);
- sshAuthListener.operationComplete(getSuccessAuthFuture());
- sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
-
- final ChannelPromise disconnectPromise = getMockedPromise();
- asyncSshHandler.disconnect(ctx, disconnectPromise);
-
- verify(sshSession).close(anyBoolean());
- verify(disconnectPromise).setSuccess();
- //verify(ctx).fireChannelInactive();
- }
-
- private static OpenFuture getSuccessOpenFuture() {
- final OpenFuture openFuture = mock(OpenFuture.class);
- doReturn(null).when(openFuture).getException();
- return openFuture;
- }
-
- private static AuthFuture getSuccessAuthFuture() {
- final AuthFuture authFuture = mock(AuthFuture.class);
- doReturn(null).when(authFuture).getException();
- return authFuture;
- }
-
- private static ConnectFuture getSuccessConnectFuture(final ClientSession sshSession) {
- final ConnectFuture connectFuture = mock(ConnectFuture.class);
- doReturn(null).when(connectFuture).getException();
-
- doReturn(sshSession).when(connectFuture).getSession();
- return connectFuture;
- }
-
- private static NettyAwareClientSession getMockedSshSession(final ChannelSubsystem subsystemChannel)
- throws IOException {
- final NettyAwareClientSession sshSession = mock(NettyAwareClientSession.class);
-
- doReturn("serverVersion").when(sshSession).getServerVersion();
- doReturn(false).when(sshSession).isClosed();
- doReturn(false).when(sshSession).isClosing();
- final CloseFuture closeFuture = mock(CloseFuture.class);
- Futures.addCallback(stubAddListener(closeFuture), new SuccessFutureListener<>() {
- @Override
- public void onSuccess(final SshFutureListener<CloseFuture> result) {
- doReturn(true).when(closeFuture).isClosed();
- result.operationComplete(closeFuture);
- }
- }, MoreExecutors.directExecutor());
- doReturn(closeFuture).when(sshSession).close(false);
-
- doReturn(subsystemChannel).when(sshSession).createSubsystemChannel(eq("netconf"),
- any(ChannelHandlerContext.class));
-
- return sshSession;
- }
-
- private ChannelSubsystem getMockedSubsystemChannel(final IoInputStream asyncOut,
- final IoOutputStream asyncIn) throws IOException {
- final ChannelSubsystem subsystemChannel = mock(ChannelSubsystem.class);
-
- doNothing().when(subsystemChannel).setStreaming(any(ClientChannel.Streaming.class));
- final OpenFuture openFuture = mock(OpenFuture.class);
-
- Futures.addCallback(stubAddListener(openFuture), new SuccessFutureListener<OpenFuture>() {
- @Override
- public void onSuccess(final SshFutureListener<OpenFuture> result) {
- sshChannelOpenListener = result;
- }
- }, MoreExecutors.directExecutor());
-
- doReturn(openFuture).when(subsystemChannel).open();
- doReturn(asyncIn).when(subsystemChannel).getAsyncIn();
- doNothing().when(subsystemChannel).onClose(any());
- doReturn(null).when(subsystemChannel).close(false);
- return subsystemChannel;
- }
-
- private static IoOutputStream getMockedIoOutputStream() throws IOException {
- final IoOutputStream mock = mock(IoOutputStream.class);
- final IoWriteFuture ioWriteFuture = mock(IoWriteFuture.class);
- doReturn(null).when(ioWriteFuture).getException();
-
- Futures.addCallback(stubAddListener(ioWriteFuture), new SuccessFutureListener<IoWriteFuture>() {
- @Override
- public void onSuccess(final SshFutureListener<IoWriteFuture> result) {
- result.operationComplete(ioWriteFuture);
- }
- }, MoreExecutors.directExecutor());
-
- doReturn(ioWriteFuture).when(mock).writeBuffer(any(Buffer.class));
- doReturn(false).when(mock).isClosed();
- doReturn(false).when(mock).isClosing();
- return mock;
- }
-
- private static IoInputStream getMockedIoInputStream() {
- final IoInputStream mock = mock(IoInputStream.class);
- final IoReadFuture ioReadFuture = mock(IoReadFuture.class);
- // Always success for read
- Futures.addCallback(stubAddListener(ioReadFuture), new SuccessFutureListener<IoReadFuture>() {
- @Override
- public void onSuccess(final SshFutureListener<IoReadFuture> result) {
- result.operationComplete(ioReadFuture);
- }
- }, MoreExecutors.directExecutor());
- return mock;
- }
-
- @Test
- public void testConnectFailOpenChannel() throws Exception {
- asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
-
- final IoInputStream asyncOut = getMockedIoInputStream();
- final IoOutputStream asyncIn = getMockedIoOutputStream();
- final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
- final ClientSession sshSession = getMockedSshSession(subsystemChannel);
- final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
-
- sshConnectListener.operationComplete(connectFuture);
-
- sshAuthListener.operationComplete(getSuccessAuthFuture());
-
- verify(subsystemChannel).setStreaming(ClientChannel.Streaming.Async);
-
- sshChannelOpenListener.operationComplete(getFailedOpenFuture());
- verify(promise).setFailure(any(Throwable.class));
- }
-
- @Test
- public void testConnectFailAuth() throws Exception {
- asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
-
- final NettyAwareClientSession sshSession = mock(NettyAwareClientSession.class);
- doReturn(true).when(sshSession).isClosed();
- final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
-
- sshConnectListener.operationComplete(connectFuture);
-
- final AuthFuture authFuture = getFailedAuthFuture();
-
- sshAuthListener.operationComplete(authFuture);
- verify(promise).setFailure(any(Throwable.class));
- asyncSshHandler.close(ctx, getMockedPromise());
- verify(ctx, times(0)).fireChannelInactive();
- }
-
- private static AuthFuture getFailedAuthFuture() {
- final AuthFuture authFuture = mock(AuthFuture.class);
- doReturn(new IllegalStateException()).when(authFuture).getException();
- return authFuture;
- }
-
- private static OpenFuture getFailedOpenFuture() {
- final OpenFuture openFuture = mock(OpenFuture.class);
- doReturn(new IllegalStateException()).when(openFuture).getException();
- return openFuture;
- }
-
- @Test
- public void testConnectFail() throws Exception {
- asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
-
- final ConnectFuture connectFuture = getFailedConnectFuture();
- sshConnectListener.operationComplete(connectFuture);
- verify(promise).setFailure(any(Throwable.class));
- }
-
- private static ConnectFuture getFailedConnectFuture() {
- final ConnectFuture connectFuture = mock(ConnectFuture.class);
- doReturn(new IllegalStateException()).when(connectFuture).getException();
- return connectFuture;
- }
-
- private ChannelPromise getMockedPromise() {
- return spy(new DefaultChannelPromise(channel));
- }
-
- private abstract static class SuccessFutureListener<T extends SshFuture<T>>
- implements FutureCallback<SshFutureListener<T>> {
-
- @Override
- public abstract void onSuccess(SshFutureListener<T> result);
-
- @Override
- public void onFailure(final Throwable throwable) {
- throw new RuntimeException(throwable);
- }
- }
-}
+++ /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.nettyutil.handler.ssh.client;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-
-import io.netty.buffer.ByteBuf;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mockito;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class AsyncSshHandlerWriterTest {
-
- private ByteBuf byteBuf;
-
- @Before
- public void setUp() throws Exception {
- byteBuf = mock(ByteBuf.class, Mockito.CALLS_REAL_METHODS);
- doReturn(byteBuf).when(byteBuf).resetReaderIndex();
- }
-
- @Test
- public void testByteBufToString() {
- String testText = "Lorem Ipsum 0123456780!@#$%^&*<>\\|/?[]()\n\r";
- doReturn(testText).when(byteBuf).toString(ArgumentMatchers.any());
- assertEquals(testText, AsyncSshHandlerWriter.byteBufToString(byteBuf));
-
- testText = "Lorem Ipsum" + (char) 0x8 + " 0123456780" + (char) 0x11 + (char) 0x7F + "9 !@#$%^&*<>\\|/?[]()\n\r";
- doReturn(testText).when(byteBuf).toString(ArgumentMatchers.any());
- assertEquals("Lorem Ipsum\"08\" 0123456780\"117F\"9 !@#$%^&*<>\\|/?[]()\n\r",
- AsyncSshHandlerWriter.byteBufToString(byteBuf));
- }
-}
\ No newline at end of file