Remove commons-io dependency in netconf-ssh
[controller.git] / opendaylight / netconf / netconf-ssh / src / main / java / org / opendaylight / controller / netconf / ssh / osgi / NetconfSSHActivator.java
index a26843fae17a97621b48221a932eb9b823a6ce83..fa87b73fad2b10bc977f0d88134c1fe21f5fb676 100644 (file)
@@ -10,17 +10,19 @@ package org.opendaylight.controller.netconf.ssh.osgi;
 import static com.google.common.base.Preconditions.checkState;
 
 import com.google.common.base.Optional;
-import io.netty.channel.EventLoopGroup;
+import com.google.common.base.Strings;
 import io.netty.channel.local.LocalAddress;
 import io.netty.channel.nio.NioEventLoopGroup;
-import java.io.File;
 import java.io.IOException;
 import java.net.InetSocketAddress;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
-import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import org.apache.sshd.common.util.ThreadUtils;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
+import org.opendaylight.controller.netconf.ssh.SshProxyServerConfigurationBuilder;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.InfixProp;
 import org.osgi.framework.BundleActivator;
@@ -28,60 +30,87 @@ 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 listens for client connections. Each client connection creation is handled in separate
- * {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker} 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 static final Logger logger = LoggerFactory.getLogger(NetconfSSHActivator.class);
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfSSHActivator.class);
+
+    private static final java.lang.String ALGORITHM = "RSA";
+    private static final int KEY_SIZE = 4096;
+    public static final int POOL_SIZE = 8;
+    private static final int DEFAULT_IDLE_TIMEOUT = Integer.MAX_VALUE;
 
-    private NetconfSSHServer server;
+    private ScheduledExecutorService minaTimerExecutor;
+    private NioEventLoopGroup clientGroup;
+    private ExecutorService nioExecutor;
+    private AuthProviderTracker authProviderTracker;
+
+    private SshProxyServer server;
 
     @Override
     public void start(final BundleContext bundleContext) throws IOException {
+        minaTimerExecutor = Executors.newScheduledThreadPool(POOL_SIZE, new ThreadFactory() {
+            @Override
+            public Thread newThread(final Runnable r) {
+                return new Thread(r, "netconf-ssh-server-mina-timers");
+            }
+        });
+        clientGroup = new NioEventLoopGroup();
+        nioExecutor = ThreadUtils.newFixedThreadPool("netconf-ssh-server-nio-group", POOL_SIZE);
         server = startSSHServer(bundleContext);
     }
 
     @Override
-    public void stop(BundleContext context) throws IOException {
+    public void stop(final BundleContext context) throws IOException {
         if (server != null) {
             server.close();
         }
+
+        if(authProviderTracker != null) {
+            authProviderTracker.stop();
+        }
+
+        if(nioExecutor!=null) {
+            nioExecutor.shutdownNow();
+        }
+
+        if(clientGroup != null) {
+            clientGroup.shutdownGracefully();
+        }
+
+        if(minaTimerExecutor != null) {
+            minaTimerExecutor.shutdownNow();
+        }
     }
 
-    private static NetconfSSHServer startSSHServer(BundleContext bundleContext) throws IOException {
-        Optional<InetSocketAddress> maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext,
-                InfixProp.ssh);
+    private SshProxyServer startSSHServer(final BundleContext bundleContext) throws IOException {
+        final Optional<InetSocketAddress> maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, InfixProp.ssh);
 
-        if (maybeSshSocketAddress.isPresent() == false) {
-            logger.trace("SSH bridge not configured");
+        if (!maybeSshSocketAddress.isPresent()) {
+            LOG.trace("SSH bridge not configured");
             return null;
         }
-        InetSocketAddress sshSocketAddress = maybeSshSocketAddress.get();
-        logger.trace("Starting netconf SSH  bridge at {}", sshSocketAddress);
 
-        LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+        final InetSocketAddress sshSocketAddress = maybeSshSocketAddress.get();
+        LOG.trace("Starting netconf SSH bridge at {}", sshSocketAddress);
 
-        String path = FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(bundleContext));
-        checkState(StringUtils.isNotBlank(path), "Path to ssh private key is blank. Reconfigure %s", NetconfConfigUtil.getPrivateKeyKey());
-        String privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File(path));
+        final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
 
-        final AuthProvider authProvider = new AuthProvider(privateKeyPEMString, bundleContext);
-        EventLoopGroup bossGroup  = new NioEventLoopGroup();
-        NetconfSSHServer server = NetconfSSHServer.start(sshSocketAddress.getPort(), localAddress, authProvider, bossGroup);
+        authProviderTracker = new AuthProviderTracker(bundleContext);
 
-        final Thread serverThread = new Thread(server, "netconf SSH server thread");
-        serverThread.setDaemon(true);
-        serverThread.start();
-        logger.trace("Netconf SSH  bridge up and running.");
-        return server;
-    }
+        final String path = NetconfConfigUtil.getPrivateKeyPath(bundleContext);
+
+        checkState(!Strings.isNullOrEmpty(path), "Path to ssh private key is blank. Reconfigure %s",
+                NetconfConfigUtil.getPrivateKeyKey());
 
+        final SshProxyServer sshProxyServer = new SshProxyServer(minaTimerExecutor, clientGroup, nioExecutor);
+        sshProxyServer.bind(
+                new SshProxyServerConfigurationBuilder()
+                        .setBindingAddress(sshSocketAddress)
+                        .setLocalAddress(localAddress)
+                        .setAuthenticator(authProviderTracker)
+                        .setKeyPairProvider(new PEMGeneratorHostKeyProvider(path, ALGORITHM, KEY_SIZE))
+                        .setIdleTimeout(DEFAULT_IDLE_TIMEOUT)
+                        .createSshProxyServerConfiguration());
+        return sshProxyServer;
+    }
 
 }