From 25ba6b145406b98f8521bcf510bb85bf0167ef72 Mon Sep 17 00:00:00 2001 From: Martin Bobak Date: Mon, 25 Nov 2013 18:54:16 +0100 Subject: [PATCH 1/1] - netconf SSH bridge bundle - Implement bridge using socket - standard netconf configuration added to netconf-ssh - NetconfClietnSession no longer needs to propagate channel - introducet IOThread class for stream copy - serversession closed when -1 received - NetconfSSHActivator throws Exception where no valid configuration is found - ganymed bundle fixed to propagate whole package ch.ethz.ssh2.signature - netconf-ssh bundle added to distribution pom - netconf ssh server starting in separate thread in order to finis activator's start method Change-Id: I46eac8106a0f222e432d3d8e81c85239ed04ac42 Signed-off-by: Martin Bobak --- .../distribution/opendaylight/pom.xml | 7 +- .../main/resources/configuration/config.ini | 7 +- .../netconf/api/NetconfSession.java | 1 - .../netconf/client/NetconfClientSession.java | 5 - .../controller/netconf/it/NetconfITTest.java | 61 +++++---- opendaylight/netconf/netconf-ssh/pom.xml | 11 +- .../netconf/osgi/NetconfSSHActivator.java | 41 +++++- .../netconf/ssh/NetconfSSHServer.java | 61 ++++++--- .../ssh/handler/SSHChannelInboundHandler.java | 27 ---- .../netconf/ssh/threads/IOThread.java | 53 ++++++++ .../ssh/{ => threads}/SocketThread.java | 125 ++++++++++-------- .../controller/netconf/ssh/SSHServerTest.java | 23 ++-- .../netconf/util/osgi/NetconfConfigUtil.java | 7 +- third-party/ganymed/pom.xml | 2 +- 14 files changed, 273 insertions(+), 158 deletions(-) delete mode 100644 opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/handler/SSHChannelInboundHandler.java create mode 100644 opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java rename opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/{ => threads}/SocketThread.java (51%) diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 5b08af79f8..5f2651659d 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -244,6 +244,11 @@ netconf-mapping-api ${netconf.version} + + org.opendaylight.controller + netconf-ssh + ${netconf.version} + org.opendaylight.controller config-netconf-connector @@ -407,12 +412,10 @@ org.opendaylight.yangtools yang-model-api - org.opendaylight.yangtools.model yang-ext - org.opendaylight.controller.thirdparty ganymed diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini index bfc3962040..00c46d9529 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini @@ -17,10 +17,9 @@ osgi.bundles=\ netconf.tcp.address=0.0.0.0 netconf.tcp.port=8383 -#netconf.tls.address=127.0.0.1 -#netconf.tls.port=8384 -#netconf.tls.keystore= -#netconf.tls.keystore.password= + +netconf.ssh.address=0.0.0.0 +netconf.ssh.port=1830 netconf.config.persister.storageAdapterClass=org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter fileStorage=configuration/controller.config diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java index 38ac12f5a2..17330b7bab 100644 --- a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java @@ -30,7 +30,6 @@ public abstract class NetconfSession extends AbstractProtocolSession getServerCapabilities() { return capabilities; } - - public Channel getChannel(){ - return channel; - } - } diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java index 4526cafe26..e5b9fa3ffc 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java @@ -77,12 +77,12 @@ import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.SAXException; +import static java.util.Collections.emptyList; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.internal.util.Checks.checkNotNull; public class NetconfITTest extends AbstractConfigTest { @@ -90,7 +90,7 @@ public class NetconfITTest extends AbstractConfigTest { // private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023); - private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 830); + private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830); private static final String USERNAME = "netconf"; private static final String PASSWORD = "netconf"; @@ -166,10 +166,16 @@ public class NetconfITTest extends AbstractConfigTest { "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang", "/META-INF/yang/ietf-inet-types.yang"); final Collection yangDependencies = new ArrayList<>(); + List failedToFind = new ArrayList<>(); for (String path : paths) { - final InputStream is = checkNotNull(NetconfITTest.class.getResourceAsStream(path), path + " not found"); - yangDependencies.add(is); + InputStream resourceAsStream = NetconfITTest.class.getResourceAsStream(path); + if (resourceAsStream == null) { + failedToFind.add(path); + } else { + yangDependencies.add(resourceAsStream); + } } + assertEquals("Some yang files were not found",emptyList(), failedToFind); return yangDependencies; } @@ -453,18 +459,9 @@ public class NetconfITTest extends AbstractConfigTest { return netconfClient; } - private class TestSSHServer implements Runnable { - public void run() { - try { - NetconfSSHServer.start(); - } catch (Exception e) { - logger.info(e.getMessage()); - } - } - } private void startSSHServer() throws Exception{ logger.info("Creating SSH server"); - Thread sshServerThread = new Thread(new TestSSHServer()); + Thread sshServerThread = new Thread(NetconfSSHServer.start(10830,tcpAddress)); sshServerThread.setDaemon(true); sshServerThread.start(); logger.info("SSH server on"); @@ -473,18 +470,34 @@ public class NetconfITTest extends AbstractConfigTest { @Test public void sshTest() throws Exception { startSSHServer(); + logger.info("creating connection"); Connection conn = new Connection(sshAddress.getHostName(),sshAddress.getPort()); Assert.assertNotNull(conn); - try { - conn.connect(); - boolean isAuthenticated = conn.authenticateWithPassword(USERNAME,PASSWORD); - assertTrue(isAuthenticated); - Session sess = conn.openSession(); - sess.startSubSystem("netconf"); -// sess.requestPTY(""); - } catch (IOException e) { - e.printStackTrace(); - } + logger.info("connection created"); + conn.connect(); + boolean isAuthenticated = conn.authenticateWithPassword(USERNAME,PASSWORD); + assertTrue(isAuthenticated); + logger.info("user authenticated"); + final Session sess = conn.openSession(); + sess.startSubSystem("netconf"); + logger.info("user authenticated"); + sess.getStdin().write(XmlUtil.toString(this.getConfig.getDocument()).getBytes()); + + new Thread(){ + public void run(){ + while (true){ + byte[] bytes = new byte[1024]; + int c = 0; + try { + c = sess.getStdout().read(bytes); + } catch (IOException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + logger.info("got data:"+bytes); + if (c == 0) break; + } + } + }.join(); } diff --git a/opendaylight/netconf/netconf-ssh/pom.xml b/opendaylight/netconf/netconf-ssh/pom.xml index 16100f0c2a..794bb16605 100644 --- a/opendaylight/netconf/netconf-ssh/pom.xml +++ b/opendaylight/netconf/netconf-ssh/pom.xml @@ -17,10 +17,6 @@ ${project.groupId} netconf-util - - ${project.groupId} - netconf-client - ${project.groupId} netconf-api @@ -33,6 +29,10 @@ org.opendaylight.controller.thirdparty ganymed + + commons-io + commons-io + @@ -58,13 +58,16 @@ io.netty.util, io.netty.util.concurrent, javax.annotation, + java.net, javax.net.ssl, javax.xml.namespace, javax.xml.parsers, javax.xml.xpath, + org.apache.commons.io, org.opendaylight.controller.netconf.api, org.opendaylight.controller.netconf.client, org.opendaylight.controller.netconf.util, + org.opendaylight.controller.netconf.util.osgi, org.opendaylight.controller.netconf.util.xml, org.opendaylight.protocol.framework, org.osgi.framework, diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java index b5e86e05c1..6626f47b03 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java @@ -7,19 +7,56 @@ */ package org.opendaylight.controller.netconf.osgi; +import com.google.common.base.Optional; +import java.net.InetSocketAddress; import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +/** + * Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator + * starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket + * and listen for client connections. Each client connection creation is handled in separate + * {@link org.opendaylight.controller.netconf.ssh.threads.SocketThread} thread. + * This thread creates two additional threads {@link org.opendaylight.controller.netconf.ssh.threads.IOThread} + * forwarding data from/to client.IOThread closes servers session and server connection when it gets -1 on input stream. + * {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}'s run method waits for -1 on input stream to finish. + * All threads are daemons. + **/ public class NetconfSSHActivator implements BundleActivator{ + private NetconfSSHServer server; + private static final Logger logger = LoggerFactory.getLogger(NetconfSSHActivator.class); + @Override public void start(BundleContext context) throws Exception { - NetconfSSHServer.start(); + + logger.trace("Starting netconf SSH bridge."); + + Optional sshSocketAddressOptional = NetconfConfigUtil.extractSSHNetconfAddress(context); + Optional tcpSocketAddressOptional = NetconfConfigUtil.extractTCPNetconfAddress(context); + + if (sshSocketAddressOptional.isPresent() && tcpSocketAddressOptional.isPresent()){ + server = NetconfSSHServer.start(sshSocketAddressOptional.get().getPort(),tcpSocketAddressOptional.get()); + Thread serverThread = new Thread(server,"netconf SSH server thread"); + serverThread.setDaemon(true); + serverThread.start(); + logger.trace("Netconf SSH bridge up and running."); + } else { + logger.trace("No valid connection configuration for SSH bridge found."); + throw new Exception("No valid connection configuration for SSH bridge found."); + } } @Override public void stop(BundleContext context) throws Exception { - + if (server != null){ + logger.trace("Netconf SSH bridge going down ..."); + server.stop(); + logger.trace("Netconf SSH bridge is down ..."); + } } } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java index dad149fd60..72135cc7dc 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java @@ -1,32 +1,63 @@ -/* - * 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 +/* + * 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.ssh; +import java.io.IOException; +import java.net.InetSocketAddress; import java.net.ServerSocket; +import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.controller.netconf.ssh.threads.SocketThread; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class NetconfSSHServer { +@ThreadSafe +public class NetconfSSHServer implements Runnable { private static boolean acceptMore = true; - private static final int SERVER_PORT = 830; private ServerSocket ss = null; + private static final Logger logger = LoggerFactory.getLogger(NetconfSSHServer.class); + private static final AtomicLong sesssionId = new AtomicLong(); + private final InetSocketAddress clientAddress; - private NetconfSSHServer() throws Exception{ - this.ss = new ServerSocket(SERVER_PORT); - while (acceptMore) { - SocketThread.start(ss.accept()); + private NetconfSSHServer(int serverPort,InetSocketAddress clientAddress) throws Exception{ + + logger.trace("Creating SSH server socket on port {}",serverPort); + this.ss = new ServerSocket(serverPort); + if (!ss.isBound()){ + throw new Exception("Socket can't be bound to requested port :"+serverPort); } + logger.trace("Server socket created."); + this.clientAddress = clientAddress; + } - public static NetconfSSHServer start() throws Exception { - return new NetconfSSHServer(); + + + public static NetconfSSHServer start(int serverPort, InetSocketAddress clientAddress) throws Exception { + return new NetconfSSHServer(serverPort, clientAddress); } public void stop() throws Exception { - ss.close(); + acceptMore = false; + logger.trace("Closing SSH server socket."); + ss.close(); + logger.trace("SSH server socket closed."); } + @Override + public void run() { + while (acceptMore) { + logger.trace("Starting new socket thread."); + try { + SocketThread.start(ss.accept(), clientAddress, sesssionId.incrementAndGet()); + } catch (IOException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + } } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/handler/SSHChannelInboundHandler.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/handler/SSHChannelInboundHandler.java deleted file mode 100644 index 7651f4735f..0000000000 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/handler/SSHChannelInboundHandler.java +++ /dev/null @@ -1,27 +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.ssh.handler; - -import ch.ethz.ssh2.ServerSession; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; - -public class SSHChannelInboundHandler extends SimpleChannelInboundHandler { - - private ServerSession serverSession; - - public SSHChannelInboundHandler(ServerSession serverSession) { - this.serverSession = serverSession; - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { - this.serverSession.getStdin().write( ((ByteBuf)msg).readBytes(((ByteBuf)msg).readableBytes()).array()); - } -} diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java new file mode 100644 index 0000000000..33ed88edf8 --- /dev/null +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java @@ -0,0 +1,53 @@ +/* + * 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.ssh.threads; + +import ch.ethz.ssh2.ServerConnection; +import ch.ethz.ssh2.ServerSession; +import java.io.InputStream; +import java.io.OutputStream; +import javax.annotation.concurrent.ThreadSafe; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ThreadSafe +public class IOThread extends Thread { + + private static final Logger logger = LoggerFactory.getLogger(IOThread.class); + + private InputStream inputStream; + private OutputStream outputStream; + private String id; + private ServerSession servSession; + private ServerConnection servconnection; + + + public IOThread (InputStream is, OutputStream os, String id,ServerSession ss, ServerConnection conn){ + this.inputStream = is; + this.outputStream = os; + this.servSession = ss; + this.servconnection = conn; + super.setName(id); + logger.trace("IOThread {} created", super.getName()); + } + + @Override + public void run() { + logger.trace("thread {} started", super.getName()); + try { + IOUtils.copy(this.inputStream, this.outputStream); + } catch (Exception e) { + logger.error("inputstream -> outputstream copy error ",e); + } + logger.trace("closing server session"); + servSession.close(); + servconnection.close(); + logger.trace("thread {} is closing",super.getName()); + } +} diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SocketThread.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java similarity index 51% rename from opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SocketThread.java rename to opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java index 1ff963d69c..95fdd48bfe 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SocketThread.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java @@ -1,4 +1,4 @@ -package org.opendaylight.controller.netconf.ssh; +package org.opendaylight.controller.netconf.ssh.threads; import ch.ethz.ssh2.AuthenticationResult; @@ -9,75 +9,108 @@ import ch.ethz.ssh2.ServerConnectionCallback; import ch.ethz.ssh2.ServerSession; import ch.ethz.ssh2.ServerSessionCallback; import ch.ethz.ssh2.SimpleServerSessionCallback; -import com.google.common.base.Optional; -import io.netty.channel.nio.NioEventLoopGroup; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; -import java.nio.ByteBuffer; -import javax.net.ssl.SSLContext; -import org.opendaylight.controller.netconf.client.NetconfClient; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; -import org.opendaylight.controller.netconf.client.NetconfClientSession; +import javax.annotation.concurrent.ThreadSafe; import org.opendaylight.controller.netconf.ssh.authentication.RSAKey; -import org.opendaylight.controller.netconf.ssh.handler.SSHChannelInboundHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - +@ThreadSafe public class SocketThread implements Runnable, ServerAuthenticationCallback, ServerConnectionCallback { private Socket socket; private static final String USER = "netconf"; private static final String PASSWORD = "netconf"; - private NetconfClient netconfClient; - private static final InetSocketAddress clientAddress = new InetSocketAddress("127.0.0.1", 12023); + private InetSocketAddress clientAddress; private static final Logger logger = LoggerFactory.getLogger(SocketThread.class); + private ServerConnection conn = null; + private long sessionId; - private static ServerConnection conn = null; - - public static void start(Socket socket) throws IOException{ - new Thread(new SocketThread(socket)).start(); + public static void start(Socket socket, InetSocketAddress clientAddress, long sessionId) throws IOException{ + Thread netconf_ssh_socket_thread = new Thread(new SocketThread(socket,clientAddress,sessionId)); + netconf_ssh_socket_thread.setDaemon(true); + netconf_ssh_socket_thread.start(); } - private SocketThread(Socket socket) throws IOException { + private SocketThread(Socket socket, InetSocketAddress clientAddress, long sessionId) throws IOException { this.socket = socket; + this.clientAddress = clientAddress; + this.sessionId = sessionId; + + } + @Override + public void run() { conn = new ServerConnection(socket); RSAKey keyStore = new RSAKey(); conn.setRsaHostKey(keyStore.getPrivateKey()); conn.setAuthenticationCallback(this); conn.setServerConnectionCallback(this); - conn.connect(); - } - - @Override - public void run() { - //noop + try { + conn.connect(); + } catch (IOException e) { + logger.error("SocketThread error ",e); + } } public ServerSessionCallback acceptSession(final ServerSession session) { SimpleServerSessionCallback cb = new SimpleServerSessionCallback() { @Override - public Runnable requestSubsystem(ServerSession ss, final String subsystem) throws IOException + public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException { return new Runnable(){ public void run() { if (subsystem.equals("netconf")){ - logger.info("netconf subsystem received"); + IOThread netconf_ssh_input = null; + IOThread netconf_ssh_output = null; try { - NetconfClientDispatcher clientDispatcher = null; - NioEventLoopGroup nioGrup = new NioEventLoopGroup(1); - clientDispatcher = new NetconfClientDispatcher(Optional.absent(), nioGrup, nioGrup); - logger.info("dispatcher created"); - netconfClient = new NetconfClient("ssh_" + clientAddress.toString(),clientAddress,5000,clientDispatcher); - logger.info("netconf client created"); + String hostName = clientAddress.getHostName(); + int portNumber = clientAddress.getPort(); + final Socket echoSocket = new Socket(hostName, portNumber); + logger.trace("echo socket created"); + + logger.trace("starting netconf_ssh_input thread"); + netconf_ssh_input = new IOThread(echoSocket.getInputStream(),ss.getStdin(),"input_thread_"+sessionId,ss,conn); + netconf_ssh_input.setDaemon(false); + netconf_ssh_input.start(); + + logger.trace("starting netconf_ssh_output thread"); + netconf_ssh_output = new IOThread(ss.getStdout(),echoSocket.getOutputStream(),"output_thread_"+sessionId,ss,conn); + netconf_ssh_output.setDaemon(false); + netconf_ssh_output.start(); + } catch (Throwable t){ logger.error(t.getMessage(),t); + + try { + if (netconf_ssh_input!=null){ + netconf_ssh_input.join(); + } + } catch (InterruptedException e) { + logger.error("netconf_ssh_input join error ",e); + } + + try { + if (netconf_ssh_output!=null){ + netconf_ssh_output.join(); + } + } catch (InterruptedException e) { + logger.error("netconf_ssh_output join error ",e); + } + + } + } else { + try { + ss.getStdin().write("wrong subsystem requested - closing connection".getBytes()); + ss.close(); + } catch (IOException e) { + logger.debug("excpetion while sending bad subsystem response",e); } } } @@ -90,7 +123,7 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser { public void run() { - System.out.println("Client requested " + pty.term + " pty"); + //noop } }; } @@ -102,30 +135,7 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser { public void run() { - try - { - try (NetconfClientSession session = netconfClient.getClientSession()) - { - session.getChannel().pipeline().addLast(new SSHChannelInboundHandler(ss)); - byte[] bytes = new byte[1024]; - while (true) - { - int size = ss.getStdout().read(bytes); - if (size < 0) - { - System.err.println("SESSION EOF"); - return; - } - session.getChannel().write(ByteBuffer.wrap(bytes,0,size)); - } - } - - } - catch (IOException e) - { - System.err.println("SESSION DOWN"); - e.printStackTrace(); - } + //noop } }; } @@ -141,8 +151,7 @@ public class SocketThread implements Runnable, ServerAuthenticationCallback, Ser public String[] getRemainingAuthMethods(ServerConnection sc) { - return new String[] { ServerAuthenticationCallback.METHOD_PASSWORD, - ServerAuthenticationCallback.METHOD_PUBLICKEY }; + return new String[] { ServerAuthenticationCallback.METHOD_PASSWORD }; } public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) diff --git a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java index acad146b77..54bc7bc4b6 100644 --- a/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java +++ b/opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java @@ -10,8 +10,9 @@ package org.opendaylight.controller.netconf.ssh; import ch.ethz.ssh2.Connection; import ch.ethz.ssh2.Session; import java.io.IOException; +import java.net.InetSocketAddress; import junit.framework.Assert; -import org.junit.Before; +import org.apache.commons.io.IOUtils; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,22 +23,15 @@ public class SSHServerTest { private static final String USER = "netconf"; private static final String PASSWORD = "netconf"; private static final String HOST = "127.0.0.1"; - private static final int PORT = 830; + private static final int PORT = 1830; + private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383); private static final Logger logger = LoggerFactory.getLogger(SSHServerTest.class); - private class TestSSHServer implements Runnable { - public void run() { - try { - NetconfSSHServer.start(); - } catch (Exception e) { - logger.info(e.getMessage()); - } - } - } - @Before +// @Before public void startSSHServer() throws Exception{ logger.info("Creating SSH server"); - Thread sshServerThread = new Thread(new TestSSHServer()); + NetconfSSHServer server = NetconfSSHServer.start(PORT,tcpAddress); + Thread sshServerThread = new Thread(server); sshServerThread.setDaemon(true); sshServerThread.start(); logger.info("SSH server on"); @@ -57,7 +51,8 @@ public class SSHServerTest { Session sess = conn.openSession(); logger.info("subsystem netconf"); sess.startSubSystem("netconf"); -// sess.requestPTY(""); + sess.getStdin().write("urn:ietf:params:netconf:base:1.1]]>]]>".getBytes()); + IOUtils.copy(sess.getStdout(), System.out); } catch (IOException e) { e.printStackTrace(); } diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java index 76068399c1..8a0a9cd80e 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java @@ -26,7 +26,7 @@ public class NetconfConfigUtil { private static final String PREFIX_PROP = "netconf."; private enum InfixProp { - tcp, tls + tcp, tls, ssh } private static final String PORT_SUFFIX_PROP = ".port"; @@ -39,6 +39,11 @@ public class NetconfConfigUtil { return extractSomeNetconfAddress(context, InfixProp.tcp); } + public static Optional extractSSHNetconfAddress(BundleContext context) { + return extractSomeNetconfAddress(context, InfixProp.ssh); + } + + public static Optional extractTLSConfiguration(BundleContext context) { Optional address = extractSomeNetconfAddress(context, InfixProp.tls); if (address.isPresent()) { diff --git a/third-party/ganymed/pom.xml b/third-party/ganymed/pom.xml index 98a6596e0e..266b5a560a 100644 --- a/third-party/ganymed/pom.xml +++ b/third-party/ganymed/pom.xml @@ -41,7 +41,7 @@ true - ch.ethz.ssh2 + ch.ethz.ssh2.* ganymed-ssh2;scope=compile true -- 2.36.6