X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fnetconf%2Fnetconf-ssh%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fnetconf%2Fssh%2Fosgi%2FNetconfSSHActivator.java;h=23c92e86488b06d31ae9d15bd4af91eaea06c987;hp=c686bcbc66fcb849da3f357bdd3bea2df7f62f2e;hb=1e884647502a8d91f8a57bde8193c60b9bbcce0d;hpb=351a78c9840c5b98a478b91ffd50befad998eb0e diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java index c686bcbc66..23c92e8648 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java @@ -9,51 +9,53 @@ 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 java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; 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 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,107 +68,49 @@ public class NetconfSSHActivator implements BundleActivator { if(authProviderTracker != null) { authProviderTracker.stop(); } - } - - private static NetconfSSHServer startSSHServer(final BundleContext bundleContext) throws IOException { - final Optional maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, - InfixProp.ssh); - - if (maybeSshSocketAddress.isPresent() == false) { - logger.trace("SSH bridge not configured"); - return null; - } - - 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 { - private final BundleContext bundleContext; - private final NetconfSSHServer server; - - private Integer maxPreference; - private Thread sshThread; - private final ServiceTracker 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(nioExecutor!=null) { + nioExecutor.shutdownNow(); } - @Override - public AuthProvider addingService(final ServiceReference reference) { - logger.trace("Service {} added", reference); - final AuthProvider authService = bundleContext.getService(reference); - final Integer newServicePreference = getPreference(reference); - if(isBetter(newServicePreference)) { - server.setAuthProvider(authService); - if(sshThread == null) { - sshThread = runNetconfSshThread(server); - } - } - return authService; + if(clientGroup != null) { + clientGroup.shutdownGracefully(); } - private Integer getPreference(final ServiceReference reference) { - final Object preferenceProperty = reference.getProperty(AuthConstants.SERVICE_PREFERENCE_KEY); - return preferenceProperty == null ? Integer.MIN_VALUE : Integer.valueOf(preferenceProperty.toString()); + if(minaTimerExecutor != null) { + minaTimerExecutor.shutdownNow(); } + } - private boolean isBetter(final Integer newServicePreference) { - Preconditions.checkNotNull(newServicePreference); - if(maxPreference == null) { - return true; - } + private SshProxyServer startSSHServer(final BundleContext bundleContext) throws IOException { + final Optional maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, InfixProp.ssh); - return newServicePreference > maxPreference; + if (maybeSshSocketAddress.isPresent() == false) { + LOG.trace("SSH bridge not configured"); + return null; } - @Override - public void modifiedService(final ServiceReference 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); - } - } + final InetSocketAddress sshSocketAddress = maybeSshSocketAddress.get(); + LOG.trace("Starting netconf SSH bridge at {}", sshSocketAddress); - @Override - public void removedService(final ServiceReference 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 LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress(); - public void stop() { - listenerTracker.close(); - // sshThread should finish normally since sshServer.close stops processing - } + authProviderTracker = new AuthProviderTracker(bundleContext); + final String path = FilenameUtils.separatorsToSystem(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; } + }