X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Ftools%2Fnetconf-testtool%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Ftest%2Ftool%2FNetconfDeviceSimulator.java;h=33c4d9c0fc6b6ffad41f15bec7964c491e1227ee;hb=3b4f0880195460f9d9a00cc6bf4bc319f81e5bb7;hp=bbf8af73dba4d8e134799644a732e4223345c33e;hpb=5503d46fd502913e25b978018a1fffa607b35d38;p=netconf.git diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java index bbf8af73db..33c4d9c0fc 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java @@ -5,107 +5,96 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.netconf.test.tool; -import com.google.common.base.MoreObjects; -import com.google.common.base.Optional; +import static java.util.Objects.requireNonNullElseGet; + import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.collect.ImmutableList; 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; -import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.HashedWheelTimer; import java.io.Closeable; import java.io.IOException; -import java.io.InputStream; -import java.net.BindException; -import java.net.Inet4Address; -import java.net.InetSocketAddress; +import java.net.InetAddress; +import java.net.ServerSocket; import java.net.UnknownHostException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import org.apache.sshd.common.util.ThreadUtils; -import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider; -import org.opendaylight.controller.config.util.capability.BasicCapability; -import org.opendaylight.controller.config.util.capability.Capability; -import org.opendaylight.controller.config.util.capability.YangModuleCapability; -import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService; -import org.opendaylight.netconf.impl.NetconfServerDispatcherImpl; -import org.opendaylight.netconf.impl.NetconfServerSessionNegotiatorFactory; -import org.opendaylight.netconf.impl.SessionIdProvider; -import org.opendaylight.netconf.impl.osgi.AggregatedNetconfOperationServiceFactory; -import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory; -import org.opendaylight.netconf.monitoring.osgi.NetconfMonitoringActivator; -import org.opendaylight.netconf.monitoring.osgi.NetconfMonitoringOperationService; -import org.opendaylight.netconf.ssh.SshProxyServer; -import org.opendaylight.netconf.ssh.SshProxyServerConfiguration; -import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder; +import java.util.stream.IntStream; +import org.opendaylight.netconf.api.CapabilityURN; +import org.opendaylight.netconf.api.TransportConstants; +import org.opendaylight.netconf.server.ServerTransportInitializer; +import org.opendaylight.netconf.server.api.SessionIdProvider; +import org.opendaylight.netconf.server.api.monitoring.BasicCapability; +import org.opendaylight.netconf.server.api.monitoring.Capability; +import org.opendaylight.netconf.server.api.monitoring.NetconfMonitoringService; +import org.opendaylight.netconf.server.api.monitoring.YangModuleCapability; +import org.opendaylight.netconf.server.api.operations.NetconfOperationServiceFactory; +import org.opendaylight.netconf.server.impl.DefaultSessionIdProvider; +import org.opendaylight.netconf.server.osgi.AggregatedNetconfOperationServiceFactory; +import org.opendaylight.netconf.shaded.sshd.server.auth.UserAuthFactory; +import org.opendaylight.netconf.shaded.sshd.server.auth.password.UserAuthPasswordFactory; +import org.opendaylight.netconf.shaded.sshd.server.auth.pubkey.UserAuthPublicKeyFactory; import org.opendaylight.netconf.test.tool.config.Configuration; import org.opendaylight.netconf.test.tool.customrpc.SettableOperationProvider; +import org.opendaylight.netconf.test.tool.monitoring.NetconfMonitoringOperationServiceFactory; import org.opendaylight.netconf.test.tool.operations.DefaultOperationsCreator; import org.opendaylight.netconf.test.tool.operations.OperationsProvider; import org.opendaylight.netconf.test.tool.rpchandler.SettableOperationRpcProvider; import org.opendaylight.netconf.test.tool.schemacache.SchemaSourceCache; -import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; +import org.opendaylight.netconf.transport.api.TransportStack; +import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException; +import org.opendaylight.netconf.transport.ssh.SSHTransportStackFactory; +import org.opendaylight.netconf.transport.ssh.ServerFactoryManagerConfigurator; +import org.opendaylight.netconf.transport.tcp.TCPServer; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.server.rev231228.netconf.server.listen.stack.grouping.transport.ssh.ssh.TcpServerParametersBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.server.rev231228.TcpServerGrouping; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.Uint16; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; +import org.opendaylight.yangtools.yang.model.api.ModuleLike; +import org.opendaylight.yangtools.yang.model.api.Submodule; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.fs.FilesystemSchemaSourceCache; import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; -import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; -import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer; +import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToIRTransformer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NetconfDeviceSimulator implements Closeable { - private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceSimulator.class); - 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 final HashedWheelTimer hashedWheelTimer = new HashedWheelTimer(); private final Configuration configuration; - private SchemaContext schemaContext; + private final List servers; + private final SSHTransportStackFactory sshStackFactory; + private EffectiveModelContext schemaContext; private boolean sendFakeSchema = false; - public NetconfDeviceSimulator(Configuration configuration) { + public NetconfDeviceSimulator(final Configuration configuration) { this.configuration = configuration; - this.nettyThreadgroup = new NioEventLoopGroup(); - this.hashedWheelTimer = new HashedWheelTimer(); - this.minaTimerExecutor = Executors.newScheduledThreadPool(configuration.getThreadPoolSize(), - new ThreadFactoryBuilder().setNameFormat("netconf-ssh-server-mina-timers-%d").build()); - this.nioExecutor = ThreadUtils - .newFixedThreadPool("netconf-ssh-server-nio-group", configuration.getThreadPoolSize()); + servers = new ArrayList<>(configuration.getDeviceCount()); + sshStackFactory = new SSHTransportStackFactory("netconf-device-simulator-threads", + configuration.getThreadPoolSize()); } - private NetconfServerDispatcherImpl createDispatcher(final Set capabilities, + private ServerTransportInitializer createTransportInitializer(final Set capabilities, final SchemaSourceProvider sourceProvider) { - - final Set transformedCapabilities = Sets.newHashSet(Collections2.transform(capabilities, input -> { + final var transformedCapabilities = new HashSet<>(Collections2.transform(capabilities, input -> { if (sendFakeSchema) { sendFakeSchema = false; return new FakeCapability((YangModuleCapability) input); @@ -113,23 +102,16 @@ public class NetconfDeviceSimulator implements Closeable { return input; } })); - transformedCapabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0")); - final NetconfMonitoringService monitoringService1 = new DummyMonitoringService(transformedCapabilities); - final SessionIdProvider idProvider = new SessionIdProvider(); + transformedCapabilities.add(new BasicCapability(CapabilityURN.CANDIDATE)); + final var monitoringService1 = new DummyMonitoringService(transformedCapabilities); + final var idProvider = new DefaultSessionIdProvider(); - final NetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = createOperationServiceFactory( + final var aggregatedNetconfOperationServiceFactory = createOperationServiceFactory( sourceProvider, transformedCapabilities, monitoringService1, idProvider); - final Set serverCapabilities = configuration.getCapabilities(); - - final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new TesttoolNegotiationFactory( - hashedWheelTimer, aggregatedNetconfOperationServiceFactory, idProvider, - configuration.getGenerateConfigsTimeout(), - monitoringService1, serverCapabilities); - - final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = - new NetconfServerDispatcherImpl.ServerChannelInitializer(serverNegotiatorFactory); - return new NetconfServerDispatcherImpl(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup); + return new ServerTransportInitializer(new TesttoolNegotiationFactory( + hashedWheelTimer, aggregatedNetconfOperationServiceFactory, idProvider, + configuration.getGenerateConfigsTimeout(), monitoringService1, configuration.getCapabilities())); } private NetconfOperationServiceFactory createOperationServiceFactory( @@ -146,20 +128,22 @@ public class NetconfDeviceSimulator implements Closeable { idProvider, transformedCapabilities, schemaContext, sourceProvider); } else if (configuration.isXmlConfigurationProvided()) { LOG.info("using SimulatedOperationProvider."); - operationProvider = new SimulatedOperationProvider(idProvider, transformedCapabilities, - Optional.fromNullable(configuration.getNotificationFile()), - Optional.fromNullable(configuration.getInitialConfigXMLFile())); + operationProvider = new SimulatedOperationProvider(transformedCapabilities, + Optional.ofNullable(configuration.getNotificationFile()), + Optional.ofNullable(configuration.getInitialConfigXMLFile())); + } else if (configuration.isNotificationsSupported()) { + LOG.info("using SimulatedOperationProvider."); + operationProvider = new SimulatedOperationProvider(transformedCapabilities, + Optional.ofNullable(configuration.getNotificationFile()), + Optional.empty()); } else { LOG.info("using OperationsProvider."); - operationProvider = new OperationsProvider(idProvider, transformedCapabilities, - configuration.getOperationsCreator() != null ? configuration.getOperationsCreator() - : DefaultOperationsCreator.getDefaultOperationServiceCreator(idProvider.getCurrentSessionId())); + operationProvider = new OperationsProvider(transformedCapabilities, + requireNonNullElseGet(configuration.getOperationsCreator(), DefaultOperationsCreator::new)); } - - final NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory monitoringService = - new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory( - new NetconfMonitoringOperationService(monitoringService1)); + final NetconfMonitoringOperationServiceFactory monitoringService = + new NetconfMonitoringOperationServiceFactory(monitoringService1); aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(operationProvider); aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(monitoringService); if (configuration.getRpcConfigFile() != null) { @@ -175,129 +159,82 @@ public class NetconfDeviceSimulator implements Closeable { } public List start() { + final var proto = configuration.isSsh() ? "SSH" : "TCP"; LOG.info("Starting {}, {} simulated devices starting on port {}", - configuration.getDeviceCount(), configuration.isSsh() ? "SSH" : "TCP", configuration.getStartingPort()); - - final SharedSchemaRepository schemaRepo = new SharedSchemaRepository("netconf-simulator"); - final Set capabilities = parseSchemasToModuleCapabilities(schemaRepo); + configuration.getDeviceCount(), proto, configuration.getStartingPort()); - final NetconfServerDispatcherImpl dispatcher = createDispatcher(capabilities, + final var schemaRepo = new SharedSchemaRepository("netconf-simulator"); + final var capabilities = parseSchemasToModuleCapabilities(schemaRepo); + final var transportInitializer = createTransportInitializer(capabilities, sourceIdentifier -> schemaRepo.getSchemaSource(sourceIdentifier, YangTextSchemaSource.class)); - int currentPort = configuration.getStartingPort(); - - final List openDevices = Lists.newArrayList(); - - // Generate key to temp folder - final PEMGeneratorHostKeyProvider keyPairProvider = getPemGeneratorHostKeyProvider(); - - for (int i = 0; i < configuration.getDeviceCount(); i++) { - if (currentPort > 65535) { - LOG.warn("Port cannot be greater than 65535, stopping further attempts."); + final var ipAddress = getIpAddress(configuration); + final var startingPort = getStartingPort(configuration); + final var deviceCount = configuration.getDeviceCount(); + final var ports = IntStream.range(startingPort, Math.min(startingPort + deviceCount, 65536)) + .mapToObj(Integer::new).toList(); + + final var openDevices = new ArrayList(ports.size()); + final var configurator = configuration.isSsh() ? createServerFactoryManagerConfigurator(configuration) : null; + + LOG.debug("Ports: {}", ports); + + for (final int port : ports) { + try { + final var connectParams = connectionParams(ipAddress, port); + final var serverFuture = configuration.isSsh() + ? sshStackFactory.listenServer(TransportConstants.SSH_SUBSYSTEM, transportInitializer, + connectParams, null, configurator) + : TCPServer.listen(transportInitializer, sshStackFactory.newServerBootstrap(), connectParams); + servers.add(serverFuture.get()); + openDevices.add(port); + } catch (UnsupportedConfigurationException | InterruptedException | ExecutionException e) { + LOG.error("Could not start {} simulated device on port {}", proto, port, e); break; } - final InetSocketAddress address = getAddress(configuration.getIp(), currentPort); - - final ChannelFuture server; - if (configuration.isSsh()) { - final InetSocketAddress bindingAddress = InetSocketAddress.createUnresolved("0.0.0.0", currentPort); - final LocalAddress tcpLocalAddress = new LocalAddress(address.toString()); - - server = dispatcher.createLocalServer(tcpLocalAddress); - try { - 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++; - } - - try { - server.get(); - } catch (final InterruptedException e) { - throw new RuntimeException(e); - } catch (final ExecutionException e) { - LOG.warn("Cannot start ssh simulated device on {}, skipping", address, e); - continue; - } - - LOG.debug("Simulated SSH device started on {}", address); - - } else { - server = dispatcher.createServer(address); - currentPort++; - - try { - server.get(); - } catch (final InterruptedException e) { - throw new RuntimeException(e); - } catch (final ExecutionException e) { - LOG.warn("Cannot start tcp simulated device on {}, skipping", address, e); - continue; - } - - LOG.debug("Simulated TCP device started on {}", address); - } - - devicesChannels.add(server.channel()); - openDevices.add(currentPort - 1); } + final var first = openDevices.get(0); + final var last = openDevices.isEmpty() ? null : openDevices.get(openDevices.size() - 1); if (openDevices.size() == configuration.getDeviceCount()) { - LOG.info("All simulated devices started successfully from port {} to {}", - configuration.getStartingPort(), currentPort - 1); - } else if (openDevices.size() == 0) { + LOG.info("All simulated devices started successfully from port {} to {}", first, last); + } else if (openDevices.isEmpty()) { LOG.warn("No simulated devices started."); } else { - LOG.warn("Not all simulated devices started successfully. Started devices ar on ports {}", openDevices); + LOG.warn("Not all simulated devices started successfully. Started devices are on ports {} to {}", + first, last); } - return openDevices; } - private SshProxyServerConfiguration getSshConfiguration(final InetSocketAddress bindingAddress, - final LocalAddress tcpLocalAddress, final PEMGeneratorHostKeyProvider keyPairProvider) throws IOException { - return new SshProxyServerConfigurationBuilder() - .setBindingAddress(bindingAddress) - .setLocalAddress(tcpLocalAddress) - .setAuthenticator((username, password) -> true) - .setPublickeyAuthenticator(((username, key, session) -> { - LOG.info("Auth with public key: {}", key); - return true; - })) - .setKeyPairProvider(keyPairProvider) - .setIdleTimeout(Integer.MAX_VALUE) - .createSshProxyServerConfiguration(); - } - - private PEMGeneratorHostKeyProvider getPemGeneratorHostKeyProvider() { - try { - final Path tempFile = Files.createTempFile("tempKeyNetconfTest", "suffix"); - return new PEMGeneratorHostKeyProvider(tempFile.toAbsolutePath().toString(), "RSA", 4096); - } catch (final IOException e) { - LOG.error("Unable to generate PEM key", e); - throw new RuntimeException(e); - } + private static ServerFactoryManagerConfigurator createServerFactoryManagerConfigurator( + final Configuration configuration) { + final var authProvider = configuration.getAuthProvider(); + final var publicKeyAuthenticator = configuration.getPublickeyAuthenticator(); + return factoryManager -> { + final var authFactoriesListBuilder = ImmutableList.builder(); + authFactoriesListBuilder.add(new UserAuthPasswordFactory()); + factoryManager.setPasswordAuthenticator( + (usr, pass, session) -> authProvider.authenticated(usr, pass)); + if (publicKeyAuthenticator != null) { + final var factory = new UserAuthPublicKeyFactory(); + factory.setSignatureFactories(factoryManager.getSignatureFactories()); + authFactoriesListBuilder.add(factory); + factoryManager.setPublickeyAuthenticator(publicKeyAuthenticator); + } + factoryManager.setUserAuthFactories(authFactoriesListBuilder.build()); + factoryManager.setKeyPairProvider(new VirtualKeyPairProvider()); + }; } private Set parseSchemasToModuleCapabilities(final SharedSchemaRepository consumer) { - final Set loadedSources = Sets.newHashSet(); - consumer.registerSchemaSourceListener(TextToASTTransformer.create(consumer, consumer)); + final Set loadedSources = new HashSet<>(); + consumer.registerSchemaSourceListener(TextToIRTransformer.create(consumer, consumer)); consumer.registerSchemaSourceListener(new SchemaSourceListener() { @Override - public void schemaSourceEncountered(final SchemaSourceRepresentation schemaSourceRepresentation) {} + public void schemaSourceEncountered(final SchemaSourceRepresentation schemaSourceRepresentation) { + + } @Override public void schemaSourceRegistered(final Iterable> potentialSchemaSources) { @@ -307,7 +244,9 @@ public class NetconfDeviceSimulator implements Closeable { } @Override - public void schemaSourceUnregistered(final PotentialSchemaSource potentialSchemaSource) {} + public void schemaSourceUnregistered(final PotentialSchemaSource potentialSchemaSource) { + + } }); if (configuration.getSchemasDir() != null) { @@ -324,21 +263,23 @@ public class NetconfDeviceSimulator implements Closeable { LOG.info("Custom module loading skipped."); } - addDefaultSchemas(consumer); + configuration.getDefaultYangResources().forEach(r -> { + registerSource(consumer, r.resourcePath(), new SourceIdentifier(r.moduleName(), r.revision())); + }); try { //necessary for creating mdsal data stores and operations - this.schemaContext = consumer.createSchemaContextFactory( - SchemaSourceFilter.ALWAYS_ACCEPT) - .createSchemaContext(loadedSources).checkedGet(); - } catch (final SchemaResolutionException e) { - throw new RuntimeException("Cannot parse schema context", e); + schemaContext = consumer.createEffectiveModelContextFactory() + .createEffectiveModelContext(loadedSources).get(); + } catch (final InterruptedException | ExecutionException e) { + throw new IllegalStateException( + "Cannot parse schema context. Please read stack trace and check YANG files in schema directory.", e); } - final Set capabilities = Sets.newHashSet(); + final Set capabilities = new HashSet<>(); for (final Module module : schemaContext.getModules()) { - for (final Module subModule : module.getSubmodules()) { + for (final Submodule subModule : module.getSubmodules()) { addModuleCapability(consumer, capabilities, subModule); } addModuleCapability(consumer, capabilities, module); @@ -346,78 +287,70 @@ public class NetconfDeviceSimulator implements Closeable { return capabilities; } - private void addModuleCapability(final SharedSchemaRepository consumer, final Set capabilities, - final Module module) { - final SourceIdentifier moduleSourceIdentifier = SourceIdentifier.create(module.getName(), - (SimpleDateFormatUtil.DEFAULT_DATE_REV == module.getRevision() ? Optional.absent() : - Optional.of(module.getQNameModule().getFormattedRevision()))); + private static void addModuleCapability(final SharedSchemaRepository consumer, final Set capabilities, + final ModuleLike module) { + final var moduleNamespace = module.getNamespace().toString(); + final var moduleName = module.getName(); + final var revision = module.getRevision().map(Revision::toString).orElse(null); + final var sourceId = new SourceIdentifier(moduleName, revision); + + final String moduleContent; try { - final String moduleContent = new String( - consumer.getSchemaSource(moduleSourceIdentifier, YangTextSchemaSource.class).checkedGet().read()); - capabilities.add(new YangModuleCapability(module, moduleContent)); - //IOException would be thrown in creating SchemaContext already - } catch (SchemaSourceException | IOException e) { - throw new RuntimeException("Cannot retrieve schema source for module " - + moduleSourceIdentifier.toString() + " from schema repository", e); + moduleContent = consumer.getSchemaSource(sourceId, YangTextSchemaSource.class).get().read(); + } catch (ExecutionException | InterruptedException | IOException e) { + throw new IllegalStateException( + "Cannot retrieve schema source for module " + sourceId + " from schema repository", e); } - } - - private void addDefaultSchemas(final SharedSchemaRepository consumer) { - SourceIdentifier srcId = RevisionSourceIdentifier.create("ietf-netconf-monitoring", "2010-10-04"); - registerSource(consumer, "/META-INF/yang/ietf-netconf-monitoring.yang", srcId); - - srcId = RevisionSourceIdentifier.create("ietf-netconf-monitoring-extension", "2013-12-10"); - registerSource(consumer, "/META-INF/yang/ietf-netconf-monitoring-extension.yang", srcId); - - srcId = RevisionSourceIdentifier.create("ietf-yang-types", "2013-07-15"); - registerSource(consumer, "/META-INF/yang/ietf-yang-types@2013-07-15.yang", srcId); - srcId = RevisionSourceIdentifier.create("ietf-inet-types", "2013-07-15"); - registerSource(consumer, "/META-INF/yang/ietf-inet-types@2013-07-15.yang", srcId); + capabilities.add(new YangModuleCapability(moduleNamespace, moduleName, revision, moduleContent)); } - private void registerSource(final SharedSchemaRepository consumer, final String resource, - final SourceIdentifier sourceId) { - consumer.registerSchemaSource(new SchemaSourceProvider() { - @Override - public CheckedFuture getSource( - final SourceIdentifier sourceIdentifier) { - return Futures.immediateCheckedFuture(new YangTextSchemaSource(sourceId) { - @Override - protected MoreObjects.ToStringHelper addToStringAttributes( - final MoreObjects.ToStringHelper toStringHelper) { - return toStringHelper; - } - - @Override - public InputStream openStream() throws IOException { - return getClass().getResourceAsStream(resource); - } - }); - } - }, PotentialSchemaSource.create( - sourceId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.IMMEDIATE.getValue())); + private static void registerSource(final SharedSchemaRepository consumer, final String resource, + final SourceIdentifier sourceId) { + consumer.registerSchemaSource(sourceIdentifier -> Futures.immediateFuture( + YangTextSchemaSource.forResource(NetconfDeviceSimulator.class, resource)), + PotentialSchemaSource.create(sourceId, YangTextSchemaSource.class, + PotentialSchemaSource.Costs.IMMEDIATE.getValue())); } - private static InetSocketAddress getAddress(final String ip, final int port) { + private static IpAddress getIpAddress(final Configuration configuration) { try { - return new InetSocketAddress(Inet4Address.getByName(ip), port); + return IetfInetUtil.ipAddressFor(InetAddress.getByName(configuration.getIp())); } catch (final UnknownHostException e) { - throw new RuntimeException(e); + throw new IllegalArgumentException("Cannot resolve address " + configuration.getIp(), e); } } - @Override - public void close() { - for (final SshProxyServer sshWrapper : sshWrappers) { - sshWrapper.close(); + private static int getStartingPort(final Configuration configuration) { + final int startingPort = configuration.getStartingPort(); + if (startingPort > 0 && startingPort < 65536) { + return startingPort; } - for (final Channel deviceCh : devicesChannels) { - deviceCh.close(); + // find available port + try { + final var socket = new ServerSocket(0); + final int port = socket.getLocalPort(); + socket.close(); + return port; + } catch (IOException e) { + throw new IllegalStateException("Cannot find available port", e); } - nettyThreadgroup.shutdownGracefully(); - minaTimerExecutor.shutdownNow(); - nioExecutor.shutdownNow(); } + private static TcpServerGrouping connectionParams(final IpAddress address, final int port) { + return new TcpServerParametersBuilder().setLocalAddress(address) + .setLocalPort(new PortNumber(Uint16.valueOf(port))).build(); + } + + @Override + public void close() { + for (final var server : servers) { + try { + server.shutdown().get(); + } catch (InterruptedException | ExecutionException e) { + LOG.debug("Exception on simulated device shutdown", e); + } + } + sshStackFactory.close(); + } }