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 0d0f95c3cb4d0856ba56c470f0890eb51371caa1..fa87b73fad2b10bc977f0d88134c1fe21f5fb676 100644 (file)
@@ -9,51 +9,52 @@ package org.opendaylight.controller.netconf.ssh.osgi;
 
 import static com.google.common.base.Preconditions.checkState;
 
-import com.google.common.base.Preconditions;
-import java.io.File;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.nio.NioEventLoopGroup;
 import java.io.IOException;
 import java.net.InetSocketAddress;
-
-import org.apache.commons.io.FilenameUtils;
-import org.opendaylight.controller.netconf.auth.AuthConstants;
-import org.opendaylight.controller.netconf.auth.AuthProvider;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-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;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Strings;
+public class NetconfSSHActivator implements BundleActivator {
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfSSHActivator.class);
 
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.local.LocalAddress;
-import io.netty.channel.nio.NioEventLoopGroup;
+    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;
 
-/**
- * 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 AuthProviderTracker authProviderTracker;
+    private ScheduledExecutorService minaTimerExecutor;
+    private NioEventLoopGroup clientGroup;
+    private ExecutorService nioExecutor;
+    private AuthProviderTracker authProviderTracker;
 
-    private NetconfSSHServer server;
+    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);
     }
 
@@ -66,108 +67,50 @@ public class NetconfSSHActivator implements BundleActivator {
         if(authProviderTracker != null) {
             authProviderTracker.stop();
         }
-    }
 
-    private static NetconfSSHServer 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");
-            return null;
+        if(nioExecutor!=null) {
+            nioExecutor.shutdownNow();
         }
 
-        final InetSocketAddress sshSocketAddress = maybeSshSocketAddress.get();
-        logger.trace("Starting netconf SSH bridge at {}", sshSocketAddress);
-
-        final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
-
-        final String path = FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(bundleContext));
-        checkState(!Strings.isNullOrEmpty(path), "Path to ssh private key is blank. Reconfigure %s", NetconfConfigUtil.getPrivateKeyKey());
-        final String privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File(path));
-
-        final EventLoopGroup bossGroup  = new NioEventLoopGroup();
-        final NetconfSSHServer server = NetconfSSHServer.start(sshSocketAddress.getPort(), localAddress, bossGroup, privateKeyPEMString.toCharArray());
-
-        authProviderTracker = new AuthProviderTracker(bundleContext, server);
-
-        return server;
-    }
-
-    private static Thread runNetconfSshThread(final NetconfSSHServer server) {
-        final Thread serverThread = new Thread(server, "netconf SSH server thread");
-        serverThread.setDaemon(true);
-        serverThread.start();
-        logger.trace("Netconf SSH  bridge up and running.");
-        return serverThread;
-    }
-
-    private static class AuthProviderTracker implements ServiceTrackerCustomizer<AuthProvider, AuthProvider> {
-        private final BundleContext bundleContext;
-        private final NetconfSSHServer server;
-
-        private Integer maxPreference;
-        private Thread sshThread;
-        private final ServiceTracker<AuthProvider, AuthProvider> listenerTracker;
-
-        public AuthProviderTracker(final BundleContext bundleContext, final NetconfSSHServer server) {
-            this.bundleContext = bundleContext;
-            this.server = server;
-            listenerTracker = new ServiceTracker<>(bundleContext, AuthProvider.class, this);
-            listenerTracker.open();
+        if(clientGroup != null) {
+            clientGroup.shutdownGracefully();
         }
 
-        @Override
-        public AuthProvider addingService(final ServiceReference<AuthProvider> reference) {
-            logger.trace("Service {} added", reference);
-            final AuthProvider authService = bundleContext.getService(reference);
-            final Integer newServicePreference = getPreference(reference);
-            if(isBetter(newServicePreference)) {
-                maxPreference = newServicePreference;
-                server.setAuthProvider(authService);
-                if(sshThread == null) {
-                    sshThread = runNetconfSshThread(server);
-                }
-            }
-            return authService;
+        if(minaTimerExecutor != null) {
+            minaTimerExecutor.shutdownNow();
         }
+    }
+
+    private SshProxyServer startSSHServer(final BundleContext bundleContext) throws IOException {
+        final Optional<InetSocketAddress> maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, InfixProp.ssh);
 
-        private Integer getPreference(final ServiceReference<AuthProvider> reference) {
-            final Object preferenceProperty = reference.getProperty(AuthConstants.SERVICE_PREFERENCE_KEY);
-            return preferenceProperty == null ? Integer.MIN_VALUE : Integer.valueOf(preferenceProperty.toString());
+        if (!maybeSshSocketAddress.isPresent()) {
+            LOG.trace("SSH bridge not configured");
+            return null;
         }
 
-        private boolean isBetter(final Integer newServicePreference) {
-            Preconditions.checkNotNull(newServicePreference);
-            if(maxPreference == null) {
-                return true;
-            }
+        final InetSocketAddress sshSocketAddress = maybeSshSocketAddress.get();
+        LOG.trace("Starting netconf SSH bridge at {}", sshSocketAddress);
 
-            return newServicePreference > maxPreference;
-        }
+        final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
 
-        @Override
-        public void modifiedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
-            final AuthProvider authService = bundleContext.getService(reference);
-            final Integer newServicePreference = getPreference(reference);
-            if(isBetter(newServicePreference)) {
-                logger.trace("Replacing modified service {} in netconf SSH.", reference);
-                server.setAuthProvider(authService);
-            }
-        }
+        authProviderTracker = new AuthProviderTracker(bundleContext);
 
-        @Override
-        public void removedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
-            logger.trace("Removing service {} from netconf SSH. " +
-                    "SSH won't authenticate users until AuthProvider service will be started.", reference);
-            maxPreference = null;
-            server.setAuthProvider(null);
-        }
+        final String path = NetconfConfigUtil.getPrivateKeyPath(bundleContext);
 
-        public void stop() {
-            listenerTracker.close();
-            // sshThread should finish normally since sshServer.close stops processing
-        }
+        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;
     }
+
 }