From: Ed Warnicke Date: Mon, 25 Aug 2014 11:15:10 +0000 (+0000) Subject: Merge "BUG-1568 Remove ganymed Ssh client adapter" X-Git-Tag: release/helium~216 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=540f686647f7aac4af1460853ad29eebfb1db812;hp=7f6d29dd5b34750ca1ec172500782e69a69398c0 Merge "BUG-1568 Remove ganymed Ssh client adapter" --- diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/Invoker.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/Invoker.java deleted file mode 100644 index eab2546d6e..0000000000 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/Invoker.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.controller.netconf.nettyutil.handler.ssh.client; - -import java.io.IOException; - -/** - * Abstract class providing mechanism of invoking various SSH level services. - * Class is not allowed to be extended, as it provides its own implementations via instance initiators. - */ -abstract class Invoker { - private boolean invoked = false; - - private Invoker() { - } - - protected boolean isInvoked() { - return invoked; - } - - public void setInvoked() { - this.invoked = true; - } - - abstract void invoke(SshSession session) throws IOException; - - public static Invoker netconfSubsystem(){ - return subsystem("netconf"); - } - - public static Invoker subsystem(final String subsystem) { - return new Invoker() { - @Override - synchronized void invoke(SshSession session) throws IOException { - if (isInvoked()) { - throw new IllegalStateException("Already invoked."); - } - try { - session.startSubSystem(subsystem); - } finally { - setInvoked(); - } - } - }; - } -} diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClient.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClient.java deleted file mode 100644 index 271b781b99..0000000000 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClient.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.controller.netconf.nettyutil.handler.ssh.client; - -import ch.ethz.ssh2.Connection; -import ch.ethz.ssh2.Session; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket; - -/** - * Wrapper class around GANYMED SSH java library. - */ -class SshClient { - private final VirtualSocket socket; - private final Map openSessions = new HashMap<>(); - private final AuthenticationHandler authenticationHandler; - private Connection connection; - - public SshClient(VirtualSocket socket, AuthenticationHandler authenticationHandler) throws IOException { - this.socket = socket; - this.authenticationHandler = authenticationHandler; - } - - public SshSession openSession() throws IOException { - if (connection == null) { - connect(); - } - - Session session = connection.openSession(); - SshSession sshSession = new SshSession(session); - openSessions.put(openSessions.size(), sshSession); - - return sshSession; - } - - private void connect() throws IOException { - connection = new Connection(socket); - - connection.connect(); - authenticationHandler.authenticate(connection); - } - - - public void close() { - for (SshSession session : openSessions.values()){ - session.close(); - } - - openSessions.clear(); - - if (connection != null) { - connection.close(); - } - } - - @Override - public String toString() { - return "SshClient{" + - "socket=" + socket + - '}'; - } -} diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java deleted file mode 100644 index 4ca7bdf958..0000000000 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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.controller.netconf.nettyutil.handler.ssh.client; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.LinkedList; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicBoolean; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Worker thread class. Handles all downstream and upstream events in SSH Netty - * pipeline. - */ -class SshClientAdapter implements Runnable { - private static final Logger logger = LoggerFactory.getLogger(SshClientAdapter.class); - - private static final int BUFFER_SIZE = 1024; - - private final SshClient sshClient; - private final Invoker invoker; - - private OutputStream stdIn; - - private final Queue postponed = new LinkedList<>(); - - private ChannelHandlerContext ctx; - private ChannelPromise disconnectPromise; - - private final AtomicBoolean stopRequested = new AtomicBoolean(false); - - private final Object lock = new Object(); - - public SshClientAdapter(final SshClient sshClient, final Invoker invoker) { - this.sshClient = sshClient; - this.invoker = invoker; - } - - // TODO ganymed spawns a Thread that receives the data from remote inside TransportManager - // Get rid of this thread and reuse Ganymed internal thread (not sure if its possible without modifications in ganymed) - public void run() { - try { - final SshSession session = sshClient.openSession(); - invoker.invoke(session); - final InputStream stdOut = session.getStdout(); - - synchronized (lock) { - stdIn = session.getStdin(); - while (postponed.peek() != null) { - writeImpl(postponed.poll()); - } - } - - while (!stopRequested.get()) { - final byte[] readBuff = new byte[BUFFER_SIZE]; - final int c = stdOut.read(readBuff); - if (c == -1) { - continue; - } - - ctx.fireChannelRead(Unpooled.copiedBuffer(readBuff, 0, c)); - } - } catch (final Exception e) { - logger.error("Unexpected exception", e); - } finally { - sshClient.close(); - - synchronized (lock) { - if (disconnectPromise != null) { - ctx.disconnect(disconnectPromise); - } - } - } - } - - // TODO: needs rework to match netconf framer API. - public void write(final ByteBuf message) throws IOException { - synchronized (lock) { - if (stdIn == null) { - postponed.add(message); - return; - } - writeImpl(message); - } - } - - private void writeImpl(final ByteBuf message) throws IOException { - message.getBytes(0, stdIn, message.readableBytes()); - message.release(); - stdIn.flush(); - } - - public void stop(final ChannelPromise promise) { - synchronized (lock) { - stopRequested.set(true); - disconnectPromise = promise; - } - } - - public Thread start(final ChannelHandlerContext ctx, final ChannelFuture channelFuture) { - checkArgument(channelFuture.isSuccess()); - checkNotNull(ctx.channel().remoteAddress()); - synchronized (this) { - checkState(this.ctx == null); - this.ctx = ctx; - } - final String threadName = toString(); - final Thread thread = new Thread(this, threadName); - thread.start(); - return thread; - } - - @Override - public String toString() { - return "SshClientAdapter{" + - "sshClient=" + sshClient + - '}'; - } -} diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshHandler.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshHandler.java deleted file mode 100644 index c710a010e2..0000000000 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshHandler.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.controller.netconf.nettyutil.handler.ssh.client; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.ChannelPromise; -import java.io.IOException; -import java.net.SocketAddress; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Netty SSH handler class. Acts as interface between Netty and SSH library. All standard Netty message handling - * stops at instance of this class. All downstream events are handed of to wrapped {@link org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshClientAdapter}; - */ -public class SshHandler extends ChannelOutboundHandlerAdapter { - private static final Logger logger = LoggerFactory.getLogger(SshHandler.class); - private static final String SOCKET = "socket"; - - private final VirtualSocket virtualSocket = new VirtualSocket(); - private final SshClientAdapter sshClientAdapter; - - - public static SshHandler createForNetconfSubsystem(AuthenticationHandler authenticationHandler) throws IOException { - return new SshHandler(authenticationHandler, Invoker.netconfSubsystem()); - } - - - public SshHandler(AuthenticationHandler authenticationHandler, Invoker invoker) throws IOException { - SshClient sshClient = new SshClient(virtualSocket, authenticationHandler); - this.sshClientAdapter = new SshClientAdapter(sshClient, invoker); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx){ - if (ctx.channel().pipeline().get(SOCKET) == null) { - ctx.channel().pipeline().addFirst(SOCKET, virtualSocket); - } - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) { - if (ctx.channel().pipeline().get(SOCKET) != null) { - ctx.channel().pipeline().remove(SOCKET); - } - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws IOException { - this.sshClientAdapter.write((ByteBuf) msg); - } - - @Override - public void connect(final ChannelHandlerContext ctx, - SocketAddress remoteAddress, - SocketAddress localAddress, - ChannelPromise promise) { - ctx.connect(remoteAddress, localAddress, promise); - - promise.addListener(new ChannelFutureListener() { - public void operationComplete(ChannelFuture channelFuture) { - if (channelFuture.isSuccess()) { - sshClientAdapter.start(ctx, channelFuture); - } else { - logger.debug("Failed to connect to remote host"); - } - }} - ); - } - - @Override - public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) { - sshClientAdapter.stop(promise); - } -} diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java deleted file mode 100644 index 9cdc5926f0..0000000000 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.controller.netconf.nettyutil.handler.ssh.client; - -import ch.ethz.ssh2.Session; -import ch.ethz.ssh2.channel.Channel; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Wrapper class for proprietary SSH sessions implementations - */ -class SshSession implements Closeable { - private final Session session; - - public SshSession(final Session session) { - this.session = session; - } - - public void startSubSystem(final String name) throws IOException { - session.startSubSystem(name); - } - - public InputStream getStdout() { - return session.getStdout(); - } - - // FIXME according to http://www.ganymed.ethz.ch/ssh2/FAQ.html#blocking you should read data from both stdout and stderr to prevent window filling up (since stdout and stderr share a window) - // FIXME stdErr is not used anywhere - public InputStream getStderr() { - return session.getStderr(); - } - - public OutputStream getStdin() { - return session.getStdin(); - } - - @Override - public void close() { - if (session.getState() == Channel.STATE_OPEN || session.getState() == Channel.STATE_OPENING) { - session.close(); - } - } -} diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java index 61297835a0..1b2201170a 100644 --- a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java @@ -29,7 +29,7 @@ import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.netconf.netty.EchoClientHandler.State; import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword; -import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler; +import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandler; import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; import org.opendaylight.controller.netconf.ssh.authentication.AuthProviderImpl; @@ -94,7 +94,7 @@ public class SSHTest { ChannelInitializer channelInitializer = new ChannelInitializer() { @Override public void initChannel(NioSocketChannel ch) throws Exception { - ch.pipeline().addFirst(SshHandler.createForNetconfSubsystem(new LoginPassword("a", "a"))); + ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(new LoginPassword("a", "a"))); ch.pipeline().addLast(echoClientHandler); } };