Merge "- netconf SSH bridge bundle - Implement bridge using socket - standard netconf...
authorEd Warnicke <eaw@cisco.com>
Thu, 28 Nov 2013 11:34:00 +0000 (11:34 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 28 Nov 2013 11:34:00 +0000 (11:34 +0000)
14 files changed:
opendaylight/distribution/opendaylight/pom.xml
opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java
opendaylight/netconf/netconf-ssh/pom.xml
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/osgi/NetconfSSHActivator.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/handler/SSHChannelInboundHandler.java [deleted file]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/IOThread.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/SocketThread.java [moved from opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SocketThread.java with 51% similarity]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/SSHServerTest.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java
third-party/ganymed/pom.xml

index 5b08af79f835e39b0d729424d31cf522ea78c256..5f2651659d94b5195e8dc3ef57aa770815ca4c31 100644 (file)
           <artifactId>netconf-mapping-api</artifactId>
           <version>${netconf.version}</version>
         </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-ssh</artifactId>
+          <version>${netconf.version}</version>
+        </dependency>
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>config-netconf-connector</artifactId>
           <groupId>org.opendaylight.yangtools</groupId>
           <artifactId>yang-model-api</artifactId>
          </dependency>
-
          <dependency>
           <groupId>org.opendaylight.yangtools.model</groupId>
           <artifactId>yang-ext</artifactId>
          </dependency>
-
         <dependency>
          <groupId>org.opendaylight.controller.thirdparty</groupId>
          <artifactId>ganymed</artifactId>
index bfc3962040de008313256355ae917948d0caf5e4..00c46d9529ff0daaa4788b01d9607fcd4611cdd3 100644 (file)
@@ -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
index 38ac12f5a27b01aedc82152d7215dae48d11971b..17330b7babde5ce12030568ffca5d12207edb946 100644 (file)
@@ -30,7 +30,6 @@ public abstract class NetconfSession extends AbstractProtocolSession<NetconfMess
     private final long sessionId;
     private boolean up = false;
     private static final Logger logger = LoggerFactory.getLogger(NetconfSession.class);
-    private static final int T = 0;
 
     protected NetconfSession(SessionListener sessionListener, Channel channel, long sessionId) {
         this.sessionListener = sessionListener;
index 3de70afc51c74169ca50074a7f46de8283f5e913..c2c8d38b9a8daa08804a686e75106f4a86a60a58 100644 (file)
@@ -31,9 +31,4 @@ public class NetconfClientSession extends NetconfSession {
     public Collection<String> getServerCapabilities() {
         return capabilities;
     }
-
-    public Channel getChannel(){
-        return channel;
-    }
-
 }
index 4526cafe26a281024aec21a8a2a818861d2581ad..e5b9fa3ffcd63404d6ec25264da45fcef7da581a 100644 (file)
@@ -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<InputStream> yangDependencies = new ArrayList<>();
+        List<String> 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();
     }
 
 
index 16100f0c2a043241ed5a13b06433df3f326b5f11..794bb16605fc1b0d572ac3b08b2f3a26ba607831 100644 (file)
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-util</artifactId>
         </dependency>
-        <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>netconf-client</artifactId>
-        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-api</artifactId>
             <groupId>org.opendaylight.controller.thirdparty</groupId>
             <artifactId>ganymed</artifactId>
         </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
                             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,
index b5e86e05c163f04ec9ba9b80951575b3762dd7f0..6626f47b0375e4a7a0e503f78cd862ab647f2f62 100644 (file)
@@ -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<InetSocketAddress> sshSocketAddressOptional = NetconfConfigUtil.extractSSHNetconfAddress(context);
+        Optional<InetSocketAddress> 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 ...");
+        }
     }
 }
index dad149fd60b762704fbf4c5f4d79882c51b8b676..72135cc7dcdd39acddfd8d69d826f2683ee9b921 100644 (file)
@@ -1,32 +1,63 @@
-/*\r
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+/*
+ * 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 (file)
index 7651f47..0000000
+++ /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 (file)
index 0000000..33ed88e
--- /dev/null
@@ -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());
+    }
+}
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 1ff963d69c7640cef2ffe7a679ed31ef80f2fa5c..95fdd48bfe31d6e83b1082eefedbfe7da06842a5 100644 (file)
@@ -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.<SSLContext>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)
index acad146b7737ff4c74478670b3439da07e12e551..54bc7bc4b686771279e5542bb7c29e4dabc0d8d2 100644 (file)
@@ -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("<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>".getBytes());
+            IOUtils.copy(sess.getStdout(), System.out);
         } catch (IOException e) {
             e.printStackTrace();
         }
index 76068399c17f470b249c0ba35027f89d723b5d81..8a0a9cd80e861510980c0fa6f77336c685e0911c 100644 (file)
@@ -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<InetSocketAddress> extractSSHNetconfAddress(BundleContext context) {
+        return extractSomeNetconfAddress(context, InfixProp.ssh);
+    }
+
+
     public static Optional<TLSConfiguration> extractTLSConfiguration(BundleContext context) {
         Optional<InetSocketAddress> address = extractSomeNetconfAddress(context, InfixProp.tls);
         if (address.isPresent()) {
index 98a6596e0e25af0eda1fbb037162d198859561bc..266b5a560a2edf01869ed66f79f709b79aca3be8 100644 (file)
@@ -41,7 +41,7 @@
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
-                        <Export-Package>ch.ethz.ssh2</Export-Package>
+                        <Export-Package>ch.ethz.ssh2.*</Export-Package>
                         <Embed-Dependency>ganymed-ssh2;scope=compile</Embed-Dependency>
                         <Embed-Transitive>true</Embed-Transitive>
                     </instructions>