X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fnetconf%2Fnetconf-testtool%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fnetconf%2Ftest%2Ftool%2FNetconfDeviceSimulator.java;h=30b307658964c59706818fff40846a135387cb28;hp=d0939a288f3ee0bb85915675e64f441165af8731;hb=c2eb34d0fe909f382bc4d9201d955e471ae1d80a;hpb=0a16f37f1baa4a2616e9b7289a445649b4f3132d diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java index d0939a288f..30b3076589 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java @@ -10,7 +10,7 @@ package org.opendaylight.controller.netconf.test.tool; import com.google.common.base.Charsets; import com.google.common.base.Function; -import com.google.common.base.Objects; +import com.google.common.base.MoreObjects; import com.google.common.base.Optional; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; @@ -19,6 +19,7 @@ import com.google.common.collect.Sets; import com.google.common.io.CharStreams; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.local.LocalAddress; @@ -30,10 +31,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; +import java.net.BindException; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.URI; import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.AbstractMap; import java.util.Date; import java.util.HashMap; @@ -42,12 +46,19 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.apache.sshd.common.util.ThreadUtils; +import org.apache.sshd.server.PasswordAuthenticator; +import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider; +import org.apache.sshd.server.session.ServerSession; import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; -import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; +import org.opendaylight.controller.netconf.impl.NetconfServerDispatcherImpl; import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory; import org.opendaylight.controller.netconf.impl.SessionIdProvider; import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl; @@ -58,8 +69,17 @@ import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot; import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService; -import org.opendaylight.controller.netconf.ssh.NetconfSSHServer; -import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; +import org.opendaylight.controller.netconf.ssh.SshProxyServer; +import org.opendaylight.controller.netconf.ssh.SshProxyServerConfiguration; +import org.opendaylight.controller.netconf.ssh.SshProxyServerConfigurationBuilder; +import org.opendaylight.controller.netconf.test.tool.rpc.DataList; +import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedCommit; +import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedCreateSubscription; +import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedEditConfig; +import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedGet; +import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedGetConfig; +import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedLock; +import org.opendaylight.controller.netconf.test.tool.rpc.SimulatedUnLock; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; @@ -85,28 +105,43 @@ public class NetconfDeviceSimulator implements Closeable { private final NioEventLoopGroup nettyThreadgroup; private final HashedWheelTimer hashedWheelTimer; private final List devicesChannels = Lists.newArrayList(); + private final List sshWrappers = Lists.newArrayList(); + private final ScheduledExecutorService minaTimerExecutor; + private final ExecutorService nioExecutor; + + private boolean sendFakeSchema = false; public NetconfDeviceSimulator() { - this(new NioEventLoopGroup(), new HashedWheelTimer()); + // TODO make pool size configurable + this(new NioEventLoopGroup(), new HashedWheelTimer(), + Executors.newScheduledThreadPool(8, new ThreadFactoryBuilder().setNameFormat("netconf-ssh-server-mina-timers-%d").build()), + ThreadUtils.newFixedThreadPool("netconf-ssh-server-nio-group", 8)); } - public NetconfDeviceSimulator(final NioEventLoopGroup eventExecutors, final HashedWheelTimer hashedWheelTimer) { + private NetconfDeviceSimulator(final NioEventLoopGroup eventExecutors, final HashedWheelTimer hashedWheelTimer, final ScheduledExecutorService minaTimerExecutor, final ExecutorService nioExecutor) { this.nettyThreadgroup = eventExecutors; this.hashedWheelTimer = hashedWheelTimer; + this.minaTimerExecutor = minaTimerExecutor; + this.nioExecutor = nioExecutor; } - private NetconfServerDispatcher createDispatcher(final Map moduleBuilders, final boolean exi, final int generateConfigsTimeout) { + private NetconfServerDispatcherImpl createDispatcher(final Map moduleBuilders, final boolean exi, final int generateConfigsTimeout, final Optional notificationsFile) { final Set capabilities = Sets.newHashSet(Collections2.transform(moduleBuilders.keySet(), new Function() { @Override public Capability apply(final ModuleBuilder input) { - return new ModuleBuilderCapability(input, moduleBuilders.get(input)); + if (sendFakeSchema) { + sendFakeSchema = false; + return new FakeModuleBuilderCapability(input, moduleBuilders.get(input)); + } else { + return new ModuleBuilderCapability(input, moduleBuilders.get(input)); + } } })); final SessionIdProvider idProvider = new SessionIdProvider(); - final SimulatedOperationProvider simulatedOperationProvider = new SimulatedOperationProvider(idProvider, capabilities); + final SimulatedOperationProvider simulatedOperationProvider = new SimulatedOperationProvider(idProvider, capabilities, notificationsFile); final NetconfMonitoringOperationService monitoringService = new NetconfMonitoringOperationService(new NetconfMonitoringServiceImpl(simulatedOperationProvider)); simulatedOperationProvider.addService(monitoringService); @@ -119,9 +154,9 @@ public class NetconfDeviceSimulator implements Closeable { final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( hashedWheelTimer, simulatedOperationProvider, idProvider, generateConfigsTimeout, commitNotifier, new LoggingMonitoringService(), serverCapabilities); - final NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer( + final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer( serverNegotiatorFactory); - return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup); + return new NetconfServerDispatcherImpl(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup); } private Map toModuleBuilders(final Map> sources) { @@ -157,30 +192,43 @@ public class NetconfDeviceSimulator implements Closeable { final Map moduleBuilders = parseSchemasToModuleBuilders(params); - final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders, params.exi, params.generateConfigsTimeout); + final NetconfServerDispatcherImpl dispatcher = createDispatcher(moduleBuilders, params.exi, params.generateConfigsTimeout, Optional.fromNullable(params.notificationFile)); int currentPort = params.startingPort; final List openDevices = Lists.newArrayList(); + + // Generate key to temp folder + final PEMGeneratorHostKeyProvider keyPairProvider = getPemGeneratorHostKeyProvider(); + for (int i = 0; i < params.deviceCount; i++) { + if (currentPort > 65535) { + LOG.warn("Port cannot be greater than 65535, stopping further attempts."); + break; + } final InetSocketAddress address = getAddress(currentPort); final ChannelFuture server; if(params.ssh) { + final InetSocketAddress bindingAddress = InetSocketAddress.createUnresolved("0.0.0.0", currentPort); final LocalAddress tcpLocalAddress = new LocalAddress(address.toString()); server = dispatcher.createLocalServer(tcpLocalAddress); try { - final NetconfSSHServer sshServer = NetconfSSHServer.start(currentPort, tcpLocalAddress, nettyThreadgroup, getPemArray()); - sshServer.setAuthProvider(new AcceptingAuthProvider()); - } catch (final Exception e) { - LOG.warn("Cannot start simulated device on {}, skipping", address, e); + final SshProxyServer sshServer = new SshProxyServer(minaTimerExecutor, nettyThreadgroup, nioExecutor); + sshServer.bind(getSshConfiguration(bindingAddress, tcpLocalAddress, keyPairProvider)); + sshWrappers.add(sshServer); + } catch (final BindException e) { + LOG.warn("Cannot start simulated device on {}, port already in use. Skipping.", address); // Close local server and continue server.cancel(true); if(server.isDone()) { server.channel().close(); } continue; + } catch (final IOException e) { + LOG.warn("Cannot start simulated device on {} due to IOException.", address, e); + break; } finally { currentPort++; } @@ -218,6 +266,8 @@ public class NetconfDeviceSimulator implements Closeable { if(openDevices.size() == params.deviceCount) { LOG.info("All simulated devices started successfully from port {} to {}", params.startingPort, currentPort - 1); + } else if (openDevices.size() == 0) { + LOG.warn("No simulated devices started."); } else { LOG.warn("Not all simulated devices started successfully. Started devices ar on ports {}", openDevices); } @@ -225,10 +275,27 @@ public class NetconfDeviceSimulator implements Closeable { return openDevices; } - private char[] getPemArray() { + private SshProxyServerConfiguration getSshConfiguration(final InetSocketAddress bindingAddress, final LocalAddress tcpLocalAddress, final PEMGeneratorHostKeyProvider keyPairProvider) throws IOException { + return new SshProxyServerConfigurationBuilder() + .setBindingAddress(bindingAddress) + .setLocalAddress(tcpLocalAddress) + .setAuthenticator(new PasswordAuthenticator() { + @Override + public boolean authenticate(final String username, final String password, final ServerSession session) { + return true; + } + }) + .setKeyPairProvider(keyPairProvider) + .setIdleTimeout(Integer.MAX_VALUE) + .createSshProxyServerConfiguration(); + } + + private PEMGeneratorHostKeyProvider getPemGeneratorHostKeyProvider() { try { - return PEMGenerator.readOrGeneratePK(new File("PK")).toCharArray(); + final Path tempFile = Files.createTempFile("tempKeyNetconfTest", "suffix"); + return new PEMGeneratorHostKeyProvider(tempFile.toAbsolutePath().toString()); } catch (final IOException e) { + LOG.error("Unable to generate PEM key", e); throw new RuntimeException(e); } } @@ -263,15 +330,15 @@ public class NetconfDeviceSimulator implements Closeable { final Map> asts = Maps.newHashMap(); for (final SourceIdentifier loadedSource : loadedSources) { - try { - final CheckedFuture ast = consumer.getSchemaSource(loadedSource, ASTSchemaSource.class); - final CheckedFuture text = consumer.getSchemaSource(loadedSource, YangTextSchemaSource.class); - asts.put(loadedSource, new AbstractMap.SimpleEntry<>(ast.get(), text.get())); - } catch (final InterruptedException e) { - throw new RuntimeException(e); - } catch (final ExecutionException e) { - throw new RuntimeException("Cannot parse schema context", e); - } + try { + final CheckedFuture ast = consumer.getSchemaSource(loadedSource, ASTSchemaSource.class); + final CheckedFuture text = consumer.getSchemaSource(loadedSource, YangTextSchemaSource.class); + asts.put(loadedSource, new AbstractMap.SimpleEntry<>(ast.get(), text.get())); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } catch (final ExecutionException e) { + throw new RuntimeException("Cannot parse schema context", e); + } } return toModuleBuilders(asts); } @@ -293,7 +360,7 @@ public class NetconfDeviceSimulator implements Closeable { public CheckedFuture getSource(final SourceIdentifier sourceIdentifier) { return Futures.immediateCheckedFuture(new YangTextSchemaSource(sourceId) { @Override - protected Objects.ToStringHelper addToStringAttributes(final Objects.ToStringHelper toStringHelper) { + protected MoreObjects.ToStringHelper addToStringAttributes(final MoreObjects.ToStringHelper toStringHelper) { return toStringHelper; } @@ -317,10 +384,15 @@ public class NetconfDeviceSimulator implements Closeable { @Override public void close() { + for (final SshProxyServer sshWrapper : sshWrappers) { + sshWrapper.close(); + } for (final Channel deviceCh : devicesChannels) { deviceCh.close(); } nettyThreadgroup.shutdownGracefully(); + minaTimerExecutor.shutdownNow(); + nioExecutor.shutdownNow(); // close Everything } @@ -329,9 +401,9 @@ public class NetconfDeviceSimulator implements Closeable { private final Set netconfOperationServices; - public SimulatedOperationProvider(final SessionIdProvider idProvider, final Set caps) { + public SimulatedOperationProvider(final SessionIdProvider idProvider, final Set caps, final Optional notificationsFile) { this.idProvider = idProvider; - final SimulatedOperationService simulatedOperationService = new SimulatedOperationService(caps, idProvider.getCurrentSessionId()); + final SimulatedOperationService simulatedOperationService = new SimulatedOperationService(caps, idProvider.getCurrentSessionId(), notificationsFile); this.netconfOperationServices = Sets.newHashSet(simulatedOperationService); } @@ -370,10 +442,12 @@ public class NetconfDeviceSimulator implements Closeable { static class SimulatedOperationService implements NetconfOperationService { private final Set capabilities; private final long currentSessionId; + private final Optional notificationsFile; - public SimulatedOperationService(final Set capabilities, final long currentSessionId) { + public SimulatedOperationService(final Set capabilities, final long currentSessionId, final Optional notificationsFile) { this.capabilities = capabilities; this.currentSessionId = currentSessionId; + this.notificationsFile = notificationsFile; } @Override @@ -388,7 +462,10 @@ public class NetconfDeviceSimulator implements Closeable { final SimulatedEditConfig sEditConfig = new SimulatedEditConfig(String.valueOf(currentSessionId), storage); final SimulatedGetConfig sGetConfig = new SimulatedGetConfig(String.valueOf(currentSessionId), storage); final SimulatedCommit sCommit = new SimulatedCommit(String.valueOf(currentSessionId)); - return Sets.newHashSet(sGet, sGetConfig, sEditConfig, sCommit); + final SimulatedLock sLock = new SimulatedLock(String.valueOf(currentSessionId)); + final SimulatedUnLock sUnlock = new SimulatedUnLock(String.valueOf(currentSessionId)); + final SimulatedCreateSubscription sCreateSubs = new SimulatedCreateSubscription(String.valueOf(currentSessionId), notificationsFile); + return Sets.newHashSet(sGet, sGetConfig, sEditConfig, sCommit, sLock, sUnlock, sCreateSubs); } @Override