From 0b970c7a638654a815b67da82ae6163c36da9302 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 12 Aug 2014 15:31:27 +0200 Subject: [PATCH] BUG-1541 Netconf device simulating testtool Produces executable jar file. The jar can simulate arbitrary number of netconf devices that listen on configured ports Schemas are loaded from a provided folder Can also generate ODL initial config files for started simulated devices to support testing with ODL distribution Change-Id: I8fce73fa7c568a272c29073f26a4e8aafeebfd82 Signed-off-by: Maros Marsalek --- opendaylight/netconf/netconf-testtool/pom.xml | 143 +++++++ .../test/tool/AcceptingAuthProvider.java | 36 ++ .../controller/netconf/test/tool/Main.java | 224 +++++++++++ .../test/tool/ModuleBuilderCapability.java | 64 ++++ .../test/tool/NetconfDeviceSimulator.java | 356 ++++++++++++++++++ .../netconf/test/tool/SimulatedGet.java | 35 ++ opendaylight/netconf/pom.xml | 10 + 7 files changed, 868 insertions(+) create mode 100644 opendaylight/netconf/netconf-testtool/pom.xml create mode 100644 opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/AcceptingAuthProvider.java create mode 100644 opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java create mode 100644 opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/ModuleBuilderCapability.java create mode 100644 opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java create mode 100644 opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/SimulatedGet.java diff --git a/opendaylight/netconf/netconf-testtool/pom.xml b/opendaylight/netconf/netconf-testtool/pom.xml new file mode 100644 index 0000000000..ae0bb76832 --- /dev/null +++ b/opendaylight/netconf/netconf-testtool/pom.xml @@ -0,0 +1,143 @@ + + + + + 4.0.0 + + + org.opendaylight.controller + netconf-subsystem + 0.2.5-SNAPSHOT + + + netconf-testtool + ${project.artifactId} + + + + net.sourceforge.argparse4j + argparse4j + 0.4.3 + + + ch.qos.logback + logback-classic + compile + + + ${project.groupId} + netconf-netty-util + + + org.opendaylight.controller + commons.logback_settings + + + org.opendaylight.controller + config-netconf-connector + + + org.opendaylight.controller + netconf-connector-config + + + org.opendaylight.controller + logback-config + + + org.opendaylight.yangtools + mockito-configuration + + + org.slf4j + slf4j-api + + + xmlunit + xmlunit + + + + ${project.groupId} + config-util + + + ${project.groupId} + netconf-api + + + ${project.groupId} + netconf-client + + + ${project.groupId} + netconf-impl + + + ${project.groupId} + netconf-mapping-api + + + ${project.groupId} + netconf-monitoring + + + + ${project.groupId} + netconf-ssh + + + + ${project.groupId} + netty-config-api + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + + shade + + package + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + org.opendaylight.controller.netconf.test.tool.Main + + + true + executable + + + + + + + + diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/AcceptingAuthProvider.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/AcceptingAuthProvider.java new file mode 100644 index 0000000000..35f2345248 --- /dev/null +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/AcceptingAuthProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * 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.controller.netconf.test.tool; + +import java.io.File; +import java.io.IOException; +import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider; +import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator; + +class AcceptingAuthProvider implements AuthProvider { + private final String privateKeyPEMString; + + public AcceptingAuthProvider() { + try { + this.privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File("PK")); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public synchronized boolean authenticated(final String username, final String password) { + return true; + } + + @Override + public char[] getPEMAsCharArray() { + return privateKeyPEMString.toCharArray(); + } +} diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java new file mode 100644 index 0000000000..59e9f4c980 --- /dev/null +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * 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.controller.netconf.test.tool; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +import net.sourceforge.argparse4j.ArgumentParsers; +import net.sourceforge.argparse4j.annotation.Arg; +import net.sourceforge.argparse4j.inf.ArgumentParser; +import net.sourceforge.argparse4j.inf.ArgumentParserException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; + +public final class Main { + + // TODO add logback config + + // TODO make exi configurable + + private static final Logger LOG = LoggerFactory.getLogger(Main.class); + + static class Params { + + @Arg(dest = "schemas-dir") + public File schemasDir; + + @Arg(dest = "devices-count") + public int deviceCount; + + @Arg(dest = "starting-port") + public int startingPort; + + @Arg(dest = "generate-configs-dir") + public File generateConfigsDir; + + @Arg(dest = "generate-configs-batch-size") + public int generateConfigBatchSize; + + @Arg(dest = "ssh") + public boolean ssh; + + static ArgumentParser getParser() { + final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf testool"); + parser.addArgument("--devices-count") + .type(Integer.class) + .setDefault(1) + .type(Integer.class) + .help("Number of simulated netconf devices to spin") + .dest("devices-count"); + + parser.addArgument("--schemas-dir") + .type(File.class) + .required(true) + .help("Directory containing yang schemas to describe simulated devices") + .dest("schemas-dir"); + + parser.addArgument("--starting-port") + .type(Integer.class) + .setDefault(17830) + .help("First port for simulated device. Each other device will have previous+1 port number") + .dest("starting-port"); + + parser.addArgument("--generate-configs-batch-size") + .type(Integer.class) + .setDefault(100) + .help("Number of connector configs per generated file") + .dest("generate-configs-batch-size"); + + parser.addArgument("--generate-configs-dir") + .type(File.class) + .help("Directory where initial config files for ODL distribution should be generated") + .dest("generate-configs-dir"); + + parser.addArgument("--ssh") + .type(Boolean.class) + .setDefault(true) + .help("Whether to use ssh for transport or just pure tcp") + .dest("ssh"); + + return parser; + } + + void validate() { + checkArgument(deviceCount > 0, "Device count has to be > 0"); + checkArgument(startingPort > 1024, "Starting port has to be > 1024"); + + checkArgument(schemasDir.exists(), "Schemas dir has to exist"); + checkArgument(schemasDir.isDirectory(), "Schemas dir has to be a directory"); + checkArgument(schemasDir.canRead(), "Schemas dir has to be readable"); + } + } + + public static void main(final String[] args) { + final Params params = parseArgs(args, Params.getParser()); + params.validate(); + + final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator(); + try { + final List openDevices = netconfDeviceSimulator.start(params); + if(params.generateConfigsDir != null) { + new ConfigGenerator(params.generateConfigsDir, openDevices).generate(params.ssh, params.generateConfigBatchSize); + } + } catch (final Exception e) { + LOG.error("Unhandled exception", e); + netconfDeviceSimulator.close(); + System.exit(1); + } + + // Block main thread + synchronized (netconfDeviceSimulator) { + try { + netconfDeviceSimulator.wait(); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + + private static Params parseArgs(final String[] args, final ArgumentParser parser) { + final Params opt = new Params(); + try { + parser.parseArgs(args, opt); + return opt; + } catch (final ArgumentParserException e) { + parser.handleError(e); + } + + System.exit(1); + return null; + } + + private static class ConfigGenerator { + public static final String NETCONF_CONNECTOR_XML = "/initial/99-netconf-connector.xml"; + public static final String NETCONF_CONNECTOR_NAME = "controller-config"; + public static final String NETCONF_CONNECTOR_PORT = "1830"; + public static final String NETCONF_USE_SSH = "false"; + public static final String SIM_DEVICE_SUFFIX = "-sim-device"; + + private final File directory; + private final List openDevices; + + public ConfigGenerator(final File directory, final List openDevices) { + this.directory = directory; + this.openDevices = openDevices; + } + + public void generate(final boolean useSsh, final int batchSize) { + if(directory.exists() == false) { + checkState(directory.mkdirs(), "Unable to create folder %s" + directory); + } + + try(InputStream stream = Main.class.getResourceAsStream(NETCONF_CONNECTOR_XML)) { + checkNotNull(stream, "Cannot load %s", NETCONF_CONNECTOR_XML); + String configBlueprint = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8)); + + // TODO make address configurable + checkState(configBlueprint.contains(NETCONF_CONNECTOR_NAME)); + checkState(configBlueprint.contains(NETCONF_CONNECTOR_PORT)); + checkState(configBlueprint.contains(NETCONF_USE_SSH)); + configBlueprint = configBlueprint.replace(NETCONF_CONNECTOR_NAME, "%s"); + configBlueprint = configBlueprint.replace(NETCONF_CONNECTOR_PORT, "%s"); + configBlueprint = configBlueprint.replace(NETCONF_USE_SSH, "%s"); + + final String before = configBlueprint.substring(0, configBlueprint.indexOf("")); + final String middleBlueprint = configBlueprint.substring(configBlueprint.indexOf(""), configBlueprint.indexOf("") + "".length()); + final String after = configBlueprint.substring(configBlueprint.indexOf("") + "".length()); + + int connectorCount = 0; + Integer batchStart = null; + StringBuilder b = new StringBuilder(); + b.append(before); + + for (final Integer openDevice : openDevices) { + if(batchStart == null) { + batchStart = openDevice; + } + + final String name = String.valueOf(openDevice) + SIM_DEVICE_SUFFIX; + final String configContent = String.format(middleBlueprint, name, String.valueOf(openDevice), String.valueOf(!useSsh)); + b.append(configContent); + connectorCount++; + if(connectorCount == batchSize) { + b.append(after); + Files.write(b.toString(), new File(directory, String.format("simulated-devices_%d-%d.xml", batchStart, openDevice)), Charsets.UTF_8); + connectorCount = 0; + b = new StringBuilder(); + b.append(before); + batchStart = null; + } + } + + // Write remaining + if(connectorCount != 0) { + b.append(after); + Files.write(b.toString(), new File(directory, String.format("simulated-devices_%d-%d.xml", batchStart, openDevices.get(openDevices.size() - 1))), Charsets.UTF_8); + } + + LOG.info("Config files generated in {}", directory); + } catch (final IOException e) { + throw new RuntimeException("Unable to generate config files", e); + } + } + } +} diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/ModuleBuilderCapability.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/ModuleBuilderCapability.java new file mode 100644 index 0000000000..1a68f55e55 --- /dev/null +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/ModuleBuilderCapability.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * 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.controller.netconf.test.tool; + +import com.google.common.base.Optional; +import java.util.Date; +import java.util.List; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.opendaylight.controller.netconf.mapping.api.Capability; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder; + +final class ModuleBuilderCapability implements Capability { + private static final Date NO_REVISION = new Date(0); + private final ModuleBuilder input; + private final Optional content; + + public ModuleBuilderCapability(final ModuleBuilder input, final String inputStream) { + this.input = input; + this.content = Optional.of(inputStream); + } + + @Override + public String getCapabilityUri() { + // FIXME capabilities in Netconf-impl need to check for NO REVISION + final String withoutRevision = getModuleNamespace().get() + "?module=" + getModuleName().get(); + return hasRevision() ? withoutRevision + "&revision=" + Util.writeDate(input.getRevision()) : withoutRevision; + } + + @Override + public Optional getModuleNamespace() { + return Optional.of(input.getNamespace().toString()); + } + + @Override + public Optional getModuleName() { + return Optional.of(input.getName()); + } + + @Override + public Optional getRevision() { + return Optional.of(hasRevision() ? QName.formattedRevision(input.getRevision()) : ""); + } + + private boolean hasRevision() { + return !input.getRevision().equals(NO_REVISION); + } + + @Override + public Optional getCapabilitySchema() { + return content; + } + + @Override + public Optional> getLocation() { + return Optional.absent(); + } +} 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 new file mode 100644 index 0000000000..b21c02ac35 --- /dev/null +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * 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.controller.netconf.test.tool; + +import com.google.common.base.Charsets; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.io.CharStreams; +import com.google.common.util.concurrent.CheckedFuture; +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.io.InputStreamReader; +import java.lang.management.ManagementFactory; +import java.net.Inet4Address; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.util.AbstractMap; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ExecutionException; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; +import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; +import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; +import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory; +import org.opendaylight.controller.netconf.impl.SessionIdProvider; +import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl; +import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService; +import org.opendaylight.controller.netconf.mapping.api.Capability; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; +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.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; +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.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener; +import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; +import org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils; +import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder; +import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl; +import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; +import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource; +import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NetconfDeviceSimulator implements Closeable { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceSimulator.class); + + public static final int CONNECTION_TIMEOUT_MILLIS = 20000; + + private final NioEventLoopGroup nettyThreadgroup; + private final HashedWheelTimer hashedWheelTimer; + private final List devicesChannels = Lists.newArrayList(); + + public NetconfDeviceSimulator() { + this(new NioEventLoopGroup(), new HashedWheelTimer()); + } + + public NetconfDeviceSimulator(final NioEventLoopGroup eventExecutors, final HashedWheelTimer hashedWheelTimer) { + this.nettyThreadgroup = eventExecutors; + this.hashedWheelTimer = hashedWheelTimer; + } + + private NetconfServerDispatcher createDispatcher(final Map moduleBuilders) { + + 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)); + } + })); + + final SessionIdProvider idProvider = new SessionIdProvider(); + + final SimulatedOperationProvider simulatedOperationProvider = new SimulatedOperationProvider(idProvider, capabilities); + final NetconfMonitoringOperationService monitoringService = new NetconfMonitoringOperationService(new NetconfMonitoringServiceImpl(simulatedOperationProvider)); + simulatedOperationProvider.addService(monitoringService); + + final DefaultCommitNotificationProducer commitNotifier = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); + + final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( + hashedWheelTimer, simulatedOperationProvider, idProvider, CONNECTION_TIMEOUT_MILLIS, commitNotifier, new LoggingMonitoringService()); + + final NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer( + serverNegotiatorFactory); + return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup); + } + + private Map toModuleBuilders(final Map> sources) { + final Map asts = Maps.transformValues(sources, new Function, ParserRuleContext>() { + @Override + public ParserRuleContext apply(final Map.Entry input) { + return input.getKey().getAST(); + } + }); + final Map> namespaceContext = BuilderUtils.createYangNamespaceContext( + asts.values(), Optional.absent()); + + final ParseTreeWalker walker = new ParseTreeWalker(); + final Map sourceToBuilder = new HashMap<>(); + + for (final Map.Entry entry : asts.entrySet()) { + final ModuleBuilder moduleBuilder = YangParserListenerImpl.create(namespaceContext, entry.getKey().getName(), + walker, entry.getValue()).getModuleBuilder(); + + try(InputStreamReader stream = new InputStreamReader(sources.get(entry.getKey()).getValue().openStream(), Charsets.UTF_8)) { + sourceToBuilder.put(moduleBuilder, CharStreams.toString(stream)); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + return sourceToBuilder; + } + + + public List start(final Main.Params params) { + final Map moduleBuilders = parseSchemasToModuleBuilders(params); + + final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders); + + int currentPort = params.startingPort; + + final List openDevices = Lists.newArrayList(); + for (int i = 0; i < params.deviceCount; i++) { + final InetSocketAddress address = getAddress(currentPort); + + final ChannelFuture server; + if(params.ssh) { + final LocalAddress tcpLocalAddress = new LocalAddress(address.toString()); + + server = dispatcher.createLocalServer(tcpLocalAddress); + try { + NetconfSSHServer.start(currentPort, tcpLocalAddress, new AcceptingAuthProvider(), nettyThreadgroup); + } catch (final Exception e) { + LOG.warn("Cannot start simulated device on {}, skipping", address, e); + // Close local server and continue + server.cancel(true); + if(server.isDone()) { + server.channel().close(); + } + continue; + } 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); + + } + + if(openDevices.size() == params.deviceCount) { + LOG.info("All simulated devices started successfully from port {} to {}", params.startingPort, currentPort); + } else { + LOG.warn("Not all simulated devices started successfully. Started devices ar on ports {}", openDevices); + } + + return openDevices; + } + + private Map parseSchemasToModuleBuilders(final Main.Params params) { + final SharedSchemaRepository consumer = new SharedSchemaRepository("netconf-simulator"); + consumer.registerSchemaSourceListener(TextToASTTransformer.create(consumer, consumer)); + + final Set loadedSources = Sets.newHashSet(); + + consumer.registerSchemaSourceListener(new SchemaSourceListener() { + @Override + public void schemaSourceEncountered(final SchemaSourceRepresentation schemaSourceRepresentation) {} + + @Override + public void schemaSourceRegistered(final Iterable> potentialSchemaSources) { + for (final PotentialSchemaSource potentialSchemaSource : potentialSchemaSources) { + loadedSources.add(potentialSchemaSource.getSourceIdentifier()); + } + } + + @Override + public void schemaSourceUnregistered(final PotentialSchemaSource potentialSchemaSource) {} + }); + + final FilesystemSchemaSourceCache cache = new FilesystemSchemaSourceCache<>(consumer, YangTextSchemaSource.class, params.schemasDir); + consumer.registerSchemaSourceListener(cache); + + 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); + } + } + return toModuleBuilders(asts); + } + + private static InetSocketAddress getAddress(final int port) { + try { + // TODO make address configurable + return new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port); + } catch (final UnknownHostException e) { + throw new RuntimeException(e); + } + } + + @Override + public void close() { + for (final Channel deviceCh : devicesChannels) { + deviceCh.close(); + } + nettyThreadgroup.shutdownGracefully(); + // close Everything + } + + private static class SimulatedOperationProvider implements NetconfOperationProvider { + private final SessionIdProvider idProvider; + private final Set netconfOperationServices; + + + public SimulatedOperationProvider(final SessionIdProvider idProvider, final Set caps) { + this.idProvider = idProvider; + final SimulatedOperationService simulatedOperationService = new SimulatedOperationService(caps, idProvider.getCurrentSessionId()); + this.netconfOperationServices = Sets.newHashSet(simulatedOperationService); + } + + @Override + public NetconfOperationServiceSnapshot openSnapshot(final String sessionIdForReporting) { + return new SimulatedServiceSnapshot(idProvider, netconfOperationServices); + } + + public void addService(final NetconfOperationService monitoringService) { + netconfOperationServices.add(monitoringService); + } + + private static class SimulatedServiceSnapshot implements NetconfOperationServiceSnapshot { + private final SessionIdProvider idProvider; + private final Set netconfOperationServices; + + public SimulatedServiceSnapshot(final SessionIdProvider idProvider, final Set netconfOperationServices) { + this.idProvider = idProvider; + this.netconfOperationServices = netconfOperationServices; + } + + @Override + public String getNetconfSessionIdForReporting() { + return String.valueOf(idProvider.getCurrentSessionId()); + } + + @Override + public Set getServices() { + return netconfOperationServices; + } + + @Override + public void close() throws Exception {} + } + + static class SimulatedOperationService implements NetconfOperationService { + private final Set capabilities; + private static SimulatedGet sGet; + + public SimulatedOperationService(final Set capabilities, final long currentSessionId) { + this.capabilities = capabilities; + sGet = new SimulatedGet(String.valueOf(currentSessionId)); + } + + @Override + public Set getCapabilities() { + return capabilities; + } + + @Override + public Set getNetconfOperations() { + return Sets.newHashSet(sGet); + } + + @Override + public void close() { + } + + } + } + + private class LoggingMonitoringService implements SessionMonitoringService { + @Override + public void onSessionUp(final NetconfManagementSession session) { + LOG.debug("Session {} established", session); + } + + @Override + public void onSessionDown(final NetconfManagementSession session) { + LOG.debug("Session {} down", session); + } + } + +} diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/SimulatedGet.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/SimulatedGet.java new file mode 100644 index 0000000000..b1938c8332 --- /dev/null +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/SimulatedGet.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * 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.controller.netconf.test.tool; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +class SimulatedGet extends AbstractConfigNetconfOperation { + + SimulatedGet(final String netconfSessionIdForReporting) { + super(null, netconfSessionIdForReporting); + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + return XmlUtil.createElement(document, XmlNetconfConstants.DATA_KEY, Optional.absent()); + } + + @Override + protected String getOperationName() { + return XmlNetconfConstants.GET; + } +} diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml index e55ec697ba..b1b410a1fc 100644 --- a/opendaylight/netconf/pom.xml +++ b/opendaylight/netconf/pom.xml @@ -150,5 +150,15 @@ netconf-it + + + testtool + + false + + + netconf-testtool + + -- 2.36.6