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=c686bcbc66fcb849da3f357bdd3bea2df7f62f2e;hp=a26843fae17a97621b48221a932eb9b823a6ce83;hb=351a78c9840c5b98a478b91ffd50befad998eb0e;hpb=999c87c5aa34530297757b653ffb0dea80bf4ff2 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 a26843fae1..c686bcbc66 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,25 +9,33 @@ 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 io.netty.channel.local.LocalAddress; -import io.netty.channel.nio.NioEventLoopGroup; +import com.google.common.base.Preconditions; 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.auth.AuthConstants; +import org.opendaylight.controller.netconf.auth.AuthProvider; import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; -import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; 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; + +import io.netty.channel.EventLoopGroup; +import io.netty.channel.local.LocalAddress; +import io.netty.channel.nio.NioEventLoopGroup; + /** * 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 @@ -40,6 +48,7 @@ import org.slf4j.LoggerFactory; */ public class NetconfSSHActivator implements BundleActivator { private static final Logger logger = LoggerFactory.getLogger(NetconfSSHActivator.class); + private static AuthProviderTracker authProviderTracker; private NetconfSSHServer server; @@ -49,39 +58,115 @@ public class NetconfSSHActivator implements BundleActivator { } @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(); + } } - private static NetconfSSHServer startSSHServer(BundleContext bundleContext) throws IOException { - Optional maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, + 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; } - InetSocketAddress sshSocketAddress = maybeSshSocketAddress.get(); - logger.trace("Starting netconf SSH bridge at {}", sshSocketAddress); - LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress(); + final InetSocketAddress sshSocketAddress = maybeSshSocketAddress.get(); + logger.trace("Starting netconf SSH bridge at {}", sshSocketAddress); + + final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress(); - 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 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 AuthProvider authProvider = new AuthProvider(privateKeyPEMString, bundleContext); - EventLoopGroup bossGroup = new NioEventLoopGroup(); - NetconfSSHServer server = NetconfSSHServer.start(sshSocketAddress.getPort(), localAddress, authProvider, bossGroup); + 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 server; + 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(); + } + + @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; + } + + 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()); + } + + private boolean isBetter(final Integer newServicePreference) { + Preconditions.checkNotNull(newServicePreference); + if(maxPreference == null) { + return true; + } + return newServicePreference > maxPreference; + } + + @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); + } + } + + @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); + } + + public void stop() { + listenerTracker.close(); + // sshThread should finish normally since sshServer.close stops processing + } + + } }