</dependencyManagement>
<properties>
- <!-- FIXME: should be inherited from odlparent -->
- <akka.version>2.3.14</akka.version>
- <scala.major.version>2.11</scala.major.version>
- <scala.minor.version>7</scala.minor.version>
</properties>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
- <version>${scala.major.version}.${scala.minor.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
- <artifactId>akka-actor_${scala.major.version}</artifactId>
- <version>${akka.version}</version>
+ <artifactId>akka-actor_${scala.version}</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
- <artifactId>akka-remote_${scala.major.version}</artifactId>
- <version>${akka.version}</version>
+ <artifactId>akka-remote_${scala.version}</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
- <artifactId>akka-cluster_${scala.major.version}</artifactId>
- <version>${akka.version}</version>
+ <artifactId>akka-cluster_${scala.version}</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
- <artifactId>akka-osgi_${scala.major.version}</artifactId>
- <version>${akka.version}</version>
+ <artifactId>akka-osgi_${scala.version}</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe</groupId>
package org.opendaylight.netconf.topology.util;
+import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
private static final Logger LOG = LoggerFactory.getLogger(SalNodeWriter.class);
- private final DataBroker dataBroker;
private final String topologyId;
+ private final BindingTransactionChain transactionChain;
public SalNodeWriter(final DataBroker dataBroker, final String topologyId) {
- this.dataBroker = dataBroker;
this.topologyId = topologyId;
+ this.transactionChain = Preconditions.checkNotNull(dataBroker).createTransactionChain(new TransactionChainListener() {
+ @Override
+ public void onTransactionChainFailed(TransactionChain<?, ?> transactionChain, AsyncTransaction<?, ?> transaction, Throwable cause) {
+ LOG.error("{}: TransactionChain({}) {} FAILED!", transactionChain,
+ transaction.getIdentifier(), cause);
+ throw new IllegalStateException("Abstract topology writer TransactionChain(" + transactionChain + ") not committed correctly", cause);
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(TransactionChain<?, ?> transactionChain) {
+ LOG.trace("Abstract topology writer TransactionChain({}) SUCCESSFUL", transactionChain);
+ }
+ });
}
- //FIXME change to txChains
@Override public void init(@Nonnull final NodeId id, @Nonnull final Node operationalDataNode) {
// put into Datastore
- final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
+ final WriteTransaction wTx = transactionChain.newWriteOnlyTransaction();
wTx.put(LogicalDatastoreType.OPERATIONAL, TopologyUtil.createTopologyNodeListPath(new NodeKey(id), topologyId), operationalDataNode);
commitTransaction(wTx, id, "init");
}
@Override public void update(@Nonnull final NodeId id, @Nonnull final Node operationalDataNode) {
// merge
- final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
+ final WriteTransaction wTx = transactionChain.newWriteOnlyTransaction();
wTx.put(LogicalDatastoreType.OPERATIONAL, TopologyUtil.createTopologyNodeListPath(new NodeKey(id), topologyId), operationalDataNode);
commitTransaction(wTx, id, "update");
}
@Override public void delete(@Nonnull final NodeId id) {
// delete
- final WriteTransaction wTx = dataBroker.newWriteOnlyTransaction();
+ final WriteTransaction wTx = transactionChain.newWriteOnlyTransaction();
wTx.delete(LogicalDatastoreType.OPERATIONAL, TopologyUtil.createTopologyNodeListPath(new NodeKey(id), topologyId));
commitTransaction(wTx, id, "delete");
}
import org.opendaylight.controller.messagebus.app.util.TopicDOMNotification;
import org.opendaylight.controller.messagebus.app.util.Util;
import org.opendaylight.controller.messagebus.spi.EventSource;
-import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.netconf.util.NetconfUtil;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.NotificationPattern;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.TopicId;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.messagebus.eventaggregator.rev141202.TopicNotification;
final SchemaContext context = getDOMMountPoint().getSchemaContext();
final SchemaPath schemaPath = body.getType();
try {
- NetconfMessageTransformUtil.writeNormalizedNode(body.getBody(), result, schemaPath, context);
+ NetconfUtil.writeNormalizedNode(body.getBody(), result, schemaPath, context);
return Builders.anyXmlBuilder().withNodeIdentifier(PAYLOAD_ARG).withValue(new DOMSource(element)).build();
} catch (IOException | XMLStreamException e) {
LOG.error("Unable to encapsulate notification.", e);
* Severity: error Error-info: <bad-element> : name of the
* unexpected element Description: An unexpected element is present.
*/
- // TODO add message to error info
- throw new DocumentedException("Unknown tag " + rootNode.getNodeName(),
+ throw new DocumentedException("Unknown tag " + rootNode.getNodeName() + " in message:\n" + netconfMessage,
DocumentedException.ErrorType.protocol, DocumentedException.ErrorTag.unknown_element,
DocumentedException.ErrorSeverity.error, ImmutableMap.of("bad-element",
rootNode.getNodeName()));
import org.apache.sshd.ClientSession;
import org.apache.sshd.client.future.AuthFuture;
-//TODO checkstyle reports imports only used in javadoc as not used, readd link to AsyncSshHandler once this is no longer the case
/**
* Class Providing username/password authentication option to
- * AsyncSshHandler
+ * {@link org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandler}
*/
public class LoginPassword extends AuthenticationHandler {
private final String username;
import org.opendaylight.controller.config.util.xml.DocumentedException;
import org.opendaylight.controller.config.util.xml.XmlElement;
import org.opendaylight.controller.config.util.xml.XmlMappingConstants;
-import org.opendaylight.netconf.util.mapping.AbstractNetconfOperation;
import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.netconf.mapping.api.HandlingPriority;
import org.opendaylight.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.netconf.notifications.NetconfNotificationRegistry;
+import org.opendaylight.netconf.util.NetconfUtil;
+import org.opendaylight.netconf.util.mapping.AbstractNetconfOperation;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.Netconf;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.NetconfBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.Streams;
final DOMResult result = new DOMResult(getPlaceholder(partialResponse));
try {
- NotificationsTransformUtil.writeNormalizedNode(normalized, result, SchemaPath.ROOT);
+ NetconfUtil.writeNormalizedNode(normalized, result, SchemaPath.ROOT, NotificationsTransformUtil.NOTIFICATIONS_SCHEMA_CTX);
} catch (final XMLStreamException | IOException e) {
throw new IllegalStateException("Unable to serialize " + netconfSubtree, e);
}
import java.util.Collections;
import java.util.Date;
import javassist.ClassPool;
-import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMResult;
import org.opendaylight.controller.config.util.xml.XmlUtil;
import org.opendaylight.netconf.notifications.NetconfNotification;
+import org.opendaylight.netconf.util.NetconfUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.$YangModuleInfoImpl;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
static final SchemaContext NOTIFICATIONS_SCHEMA_CTX;
static final BindingNormalizedNodeCodecRegistry CODEC_REGISTRY;
- static final XMLOutputFactory XML_FACTORY;
static final RpcDefinition CREATE_SUBSCRIPTION_RPC;
static final SchemaPath CAPABILITY_CHANGE_SCHEMA_PATH = SchemaPath.create(true, NetconfCapabilityChange.QNAME);
static {
- XML_FACTORY = XMLOutputFactory.newFactory();
- XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
moduleInfoBackedContext.addModuleInfos(Collections.singletonList($YangModuleInfoImpl.getInstance()));
final ContainerNode containerNode = CODEC_REGISTRY.toNormalizedNodeNotification(capabilityChange);
final DOMResult result = new DOMResult(XmlUtil.newDocument());
try {
- writeNormalizedNode(containerNode, result, CAPABILITY_CHANGE_SCHEMA_PATH);
+ NetconfUtil.writeNormalizedNode(containerNode, result, CAPABILITY_CHANGE_SCHEMA_PATH, NOTIFICATIONS_SCHEMA_CTX);
} catch (final XMLStreamException| IOException e) {
throw new IllegalStateException("Unable to serialize " + capabilityChange, e);
}
new NetconfNotification(node);
}
- static void writeNormalizedNode(final NormalizedNode<?, ?> normalized, final DOMResult result, final SchemaPath schemaPath) throws IOException, XMLStreamException {
- NormalizedNodeWriter normalizedNodeWriter = null;
- NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
- XMLStreamWriter writer = null;
- try {
- writer = XML_FACTORY.createXMLStreamWriter(result);
- normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, NOTIFICATIONS_SCHEMA_CTX, schemaPath);
- normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
-
- normalizedNodeWriter.write(normalized);
-
- normalizedNodeWriter.flush();
- } finally {
- try {
- if(normalizedNodeWriter != null) {
- normalizedNodeWriter.close();
- }
- if(normalizedNodeStreamWriter != null) {
- normalizedNodeStreamWriter.close();
- }
- if(writer != null) {
- writer.close();
- }
- } catch (final Exception e) {
- LOG.warn("Unable to close resource properly", e);
- }
- }
- }
-
}
akka {
- version = "2.3.14"
-
actor {
provider = "akka.cluster.ClusterActorRefProvider"
hostname = "127.0.0.1"
}
}
-
}
package org.opendaylight.netconf.util;
import com.google.common.base.Preconditions;
+import java.io.IOException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMResult;
import org.opendaylight.controller.config.util.xml.DocumentedException;
import org.opendaylight.controller.config.util.xml.XmlElement;
import org.opendaylight.controller.config.util.xml.XmlMappingConstants;
import org.opendaylight.controller.config.util.xml.XmlUtil;
import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
public final class NetconfUtil {
private static final Logger LOG = LoggerFactory.getLogger(NetconfUtil.class);
+ public static final XMLOutputFactory XML_FACTORY;
+
+ static {
+ XML_FACTORY = XMLOutputFactory.newFactory();
+ XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
+ }
private NetconfUtil() {}
throw new IllegalStateException("Can not load last configuration. Operation failed: "
+ XmlUtil.toString(response));
}
+
+ public static void writeNormalizedNode(final NormalizedNode<?, ?> normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext context)
+ throws IOException, XMLStreamException {
+ final XMLStreamWriter writer = XML_FACTORY.createXMLStreamWriter(result);
+ try (
+ final NormalizedNodeStreamWriter normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath);
+ final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter)
+ ) {
+ normalizedNodeWriter.write(normalized);
+ normalizedNodeWriter.flush();
+ } finally {
+ try {
+ if(writer != null) {
+ writer.close();
+ }
+ } catch (final Exception e) {
+ LOG.warn("Unable to close resource properly", e);
+ }
+ }
+ }
}
*/
/**
- * General API for remote connectors e.g. netconf connector
- *
- * TODO extract into separate bundle when another connector is implemented e.g. restconf connector
+ * API for netconf connector
*/
package org.opendaylight.netconf.sal.connect.api;
listeners.put(type, listener);
}
- // FIXME this should invoke create-subscription rpc on the remote device for a given notification
-
return new ListenerRegistration<T>() {
@Override
public void close() {
import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
import org.opendaylight.netconf.sal.connect.util.MessageCounter;
+import org.opendaylight.netconf.util.NetconfUtil;
import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
import org.opendaylight.yangtools.yang.common.QName;
private static void writeNormalizedRpc(final ContainerNode normalized, final DOMResult result,
final SchemaPath schemaPath, final SchemaContext baseNetconfCtx) throws IOException, XMLStreamException {
- final XMLStreamWriter writer = NetconfMessageTransformUtil.XML_FACTORY.createXMLStreamWriter(result);
+ final XMLStreamWriter writer = NetconfUtil.XML_FACTORY.createXMLStreamWriter(result);
try {
try (final NormalizedNodeStreamWriter normalizedNodeStreamWriter =
XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath)) {
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
-import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import org.opendaylight.controller.config.util.xml.XmlUtil;
import org.opendaylight.netconf.api.NetconfDocumentedException;
import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.util.NetconfUtil;
import org.opendaylight.netconf.util.messages.NetconfMessageUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.edit.config.input.EditContent;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInput;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
private static final Logger LOG= LoggerFactory.getLogger(NetconfMessageTransformUtil.class);
public static final String MESSAGE_ID_ATTR = "message-id";
- public static final XMLOutputFactory XML_FACTORY;
-
- static {
- XML_FACTORY = XMLOutputFactory.newFactory();
- XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
- }
public static final QName CREATE_SUBSCRIPTION_RPC_QNAME = QName.cachedReference(QName.create(CreateSubscriptionInput.QNAME, "create-subscription"));
private static final String SUBTREE = "subtree";
element.setAttributeNS(NETCONF_FILTER_QNAME.getNamespace().toString(), NETCONF_TYPE_QNAME.getLocalName(), "subtree");
try {
- writeNormalizedNode(filterContent, new DOMResult(element), SchemaPath.ROOT, ctx);
+ NetconfUtil.writeNormalizedNode(filterContent, new DOMResult(element), SchemaPath.ROOT, ctx);
} catch (IOException | XMLStreamException e) {
throw new IllegalStateException("Unable to serialize filter element for path " + identifier, e);
}
final Element element = XmlUtil.createElement(BLANK_DOCUMENT, NETCONF_CONFIG_QNAME.getLocalName(), Optional.of(NETCONF_CONFIG_QNAME.getNamespace().toString()));
try {
- writeNormalizedNode(configContent, new DOMResult(element), SchemaPath.ROOT, ctx);
+ NetconfUtil.writeNormalizedNode(configContent, new DOMResult(element), SchemaPath.ROOT, ctx);
} catch (IOException | XMLStreamException e) {
throw new IllegalStateException("Unable to serialize edit config content element for path " + dataPath, e);
}
return SchemaPath.create(true, rpc);
}
- // FIXME similar code is in netconf-notifications-impl , DRY
- public static void writeNormalizedNode(final NormalizedNode<?, ?> normalized, final DOMResult result, final SchemaPath schemaPath, final SchemaContext context)
- throws IOException, XMLStreamException {
- NormalizedNodeWriter normalizedNodeWriter = null;
- NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
- XMLStreamWriter writer = null;
- try {
- writer = XML_FACTORY.createXMLStreamWriter(result);
- normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath);
- normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
-
- normalizedNodeWriter.write(normalized);
-
- normalizedNodeWriter.flush();
- } finally {
- try {
- if(normalizedNodeWriter != null) {
- normalizedNodeWriter.close();
- }
- if(normalizedNodeStreamWriter != null) {
- normalizedNodeStreamWriter.close();
- }
- if(writer != null) {
- writer.close();
- }
- } catch (final Exception e) {
- LOG.warn("Unable to close resource properly", e);
- }
- }
- }
}
*/
/**
- * Utility classes for remote connectors e.g. netconf connector
- *
- * TODO extract into separate bundle when another connector is implemented e.g. restconf connector
+ * Utility classes for netconf connector
*/
package org.opendaylight.netconf.sal.connect.util;
<shadedClassifierName>rest-perf-client</shadedClassifierName>
</configuration>
</execution>
+
+ <execution>
+ <id>scale-util</id>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <shadedArtifactId>scale-util</shadedArtifactId>
+ <filters>
+ <filter>
+ <artifact>*:*</artifact>
+ <excludes>
+ <exclude>META-INF/*.SF</exclude>
+ <exclude>META-INF/*.DSA</exclude>
+ <exclude>META-INF/*.RSA</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ <transformers>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <manifestEntries>
+ <Main-Class>org.opendaylight.netconf.test.tool.ScaleUtil</Main-Class>
+ <Class-Path>. lib lib/bcprov-jdk15on.jar lib/bcpkix-jdk15on.jar</Class-Path>
+ </manifestEntries>
+ </transformer>
+ </transformers>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <shadedClassifierName>scale-util</shadedClassifierName>
+ </configuration>
+ </execution>
</executions>
</plugin>
<plugin>
return null;
}
- private static class ConfigGenerator {
+ static class ConfigGenerator {
public static final String NETCONF_CONNECTOR_XML = "/99-netconf-connector-simulated.xml";
public static final String SIM_DEVICE_SUFFIX = "-sim-device";
--- /dev/null
+/*
+ * Copyright (c) 2016 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.netconf.test.tool;
+
+import ch.qos.logback.classic.Level;
+import com.google.common.base.Charsets;
+import com.google.common.base.Stopwatch;
+import com.google.common.io.CharStreams;
+import com.ning.http.client.AsyncHttpClient;
+import com.ning.http.client.AsyncHttpClientConfig.Builder;
+import com.ning.http.client.Request;
+import com.ning.http.client.Response;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ConnectException;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import net.sourceforge.argparse4j.inf.ArgumentParserException;
+import org.opendaylight.netconf.test.tool.Main.Params;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ScaleUtil {
+
+ private static final Logger RESULTS_LOG = LoggerFactory.getLogger("results");
+ private static final ScheduledExecutorService executor = new LoggingWrapperExecutor(4);
+
+ private static final int deviceStep = 1000;
+ private static final long retryDelay = 10l;
+ private static final long timeout = 20l;
+
+ private static final Stopwatch stopwatch = Stopwatch.createUnstarted();
+
+ private static ScheduledFuture timeoutGuardFuture;
+ private static ch.qos.logback.classic.Logger root;
+ private static final Semaphore semaphore = new Semaphore(0);
+
+ public static void main(final String[] args) {
+ final Params params = parseArgs(args, Params.getParser());
+
+ root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+ root.setLevel(params.debug ? Level.DEBUG : Level.INFO);
+
+ // cleanup at the start in case controller was already running
+ final Runtime runtime = Runtime.getRuntime();
+ cleanup(runtime, params);
+
+ while (true) {
+ root.warn("Starting scale test with {} devices", params.deviceCount);
+ timeoutGuardFuture = executor.schedule(new TimeoutGuard(), timeout, TimeUnit.MINUTES);
+ final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator();
+ try {
+ final List<Integer> openDevices = netconfDeviceSimulator.start(params);
+ if (openDevices.size() == 0) {
+ root.error("Failed to start any simulated devices, exiting...");
+ System.exit(1);
+ }
+ if (params.distroFolder != null) {
+ final Main.ConfigGenerator configGenerator = new Main.ConfigGenerator(params.distroFolder, openDevices);
+ final List<File> generated = configGenerator.generate(
+ params.ssh, params.generateConfigBatchSize,
+ params.generateConfigsTimeout, params.generateConfigsAddress,
+ params.devicesPerPort);
+ configGenerator.updateFeatureFile(generated);
+ configGenerator.changeLoadOrder();
+ }
+ } catch (final Exception e) {
+ root.error("Unhandled exception", e);
+ netconfDeviceSimulator.close();
+ System.exit(1);
+ }
+
+ root.warn(params.distroFolder.getAbsolutePath());
+ try {
+ runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/start");
+ String status;
+ do {
+ final Process exec = runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/status");
+ try {
+ Thread.sleep(2000l);
+ } catch (InterruptedException e) {
+ root.warn("Failed to sleep", e);
+ }
+ status = CharStreams.toString(new BufferedReader(new InputStreamReader(exec.getInputStream())));
+ root.warn("Current status: {}", status);
+ } while (!status.startsWith("Running ..."));
+ root.warn("Doing feature install {}", params.distroFolder.getAbsolutePath() + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
+ final Process featureInstall = runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
+ root.warn(CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getInputStream()))));
+ root.warn(CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getErrorStream()))));
+
+ } catch (IOException e) {
+ root.warn("Failed to start karaf", e);
+ System.exit(1);
+ }
+
+ root.warn("Karaf started, starting stopwatch");
+ stopwatch.start();
+
+ try {
+ executor.schedule(new ScaleVerifyCallable(netconfDeviceSimulator, params.deviceCount), retryDelay, TimeUnit.SECONDS);
+ root.warn("First callable scheduled");
+ semaphore.acquire();
+ root.warn("semaphore released");
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ timeoutGuardFuture.cancel(false);
+ params.deviceCount += deviceStep;
+ netconfDeviceSimulator.close();
+ stopwatch.reset();
+
+ cleanup(runtime, params);
+ }
+ }
+
+ private static void cleanup(final Runtime runtime, final Params params) {
+ try {
+ stopKaraf(runtime, params);
+ deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
+
+ } catch (IOException | InterruptedException e) {
+ root.warn("Failed to stop karaf", e);
+ System.exit(1);
+ }
+ }
+
+ private static void stopKaraf(final Runtime runtime, final Params params) throws IOException, InterruptedException {
+ root.info("Stopping karaf and sleeping for 10 sec..");
+ String controllerPid = "";
+ do {
+
+ final Process pgrep = runtime.exec("pgrep -f org.apache.karaf.main.Main");
+
+ controllerPid = CharStreams.toString(new BufferedReader(new InputStreamReader(pgrep.getInputStream())));
+ root.warn(controllerPid);
+ runtime.exec("kill -9 " + controllerPid);
+
+ Thread.sleep(10000l);
+ } while (!controllerPid.isEmpty());
+ deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
+ }
+
+ private static void deleteFolder(File folder) {
+ File[] files = folder.listFiles();
+ if(files!=null) { //some JVMs return null for empty dirs
+ for(File f: files) {
+ if(f.isDirectory()) {
+ deleteFolder(f);
+ } else {
+ f.delete();
+ }
+ }
+ }
+ folder.delete();
+ }
+
+ private static Params parseArgs(final String[] args, final ArgumentParser parser) {
+ final Params parameters = new Params();
+ try {
+ parser.parseArgs(args, parameters);
+ return parameters;
+ } catch (ArgumentParserException e) {
+ parser.handleError(e);
+ }
+
+ System.exit(1);
+ return null;
+ }
+
+ private static class ScaleVerifyCallable implements Callable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ScaleVerifyCallable.class);
+
+ private static final String RESTCONF_URL = "http://127.0.0.1:8181/restconf/operational/network-topology:network-topology/topology/topology-netconf/";
+ private static final Pattern PATTERN = Pattern.compile("connected");
+
+ private final AsyncHttpClient asyncHttpClient = new AsyncHttpClient(new Builder()
+ .setConnectTimeout(Integer.MAX_VALUE)
+ .setRequestTimeout(Integer.MAX_VALUE)
+ .setAllowPoolingConnections(true)
+ .build());
+ private final NetconfDeviceSimulator simulator;
+ private final int deviceCount;
+ private final Request request;
+
+ public ScaleVerifyCallable(final NetconfDeviceSimulator simulator, final int deviceCount) {
+ LOG.info("New callable created");
+ this.simulator = simulator;
+ this.deviceCount = deviceCount;
+ AsyncHttpClient.BoundRequestBuilder requestBuilder = asyncHttpClient.prepareGet(RESTCONF_URL)
+ .addHeader("content-type", "application/xml")
+ .addHeader("Accept", "application/xml")
+ .setRequestTimeout(Integer.MAX_VALUE);
+ request = requestBuilder.build();
+ }
+
+ @Override
+ public Object call() throws Exception {
+ try {
+ final Response response = asyncHttpClient.executeRequest(request).get();
+
+ if (response.getStatusCode() != 200 && response.getStatusCode() != 204) {
+ LOG.warn("Request failed, status code: {}", response.getStatusCode() + response.getStatusText());
+ executor.schedule(new ScaleVerifyCallable(simulator, deviceCount), retryDelay, TimeUnit.SECONDS);
+ } else {
+ final String body = response.getResponseBody();
+ final Matcher matcher = PATTERN.matcher(body);
+ int count = 0;
+ while (matcher.find()) {
+ count++;
+ }
+ RESULTS_LOG.info("Currently connected devices : {} out of {}, time elapsed: {}", count, deviceCount + 1, stopwatch);
+ if (count != deviceCount + 1) {
+ executor.schedule(new ScaleVerifyCallable(simulator, deviceCount), retryDelay, TimeUnit.SECONDS);
+ } else {
+ stopwatch.stop();
+ RESULTS_LOG.info("All devices connected in {}", stopwatch);
+ semaphore.release();
+ }
+ }
+ } catch (ConnectException | ExecutionException e) {
+ LOG.warn("Failed to connect to Restconf, is the controller running?", e);
+ executor.schedule(new ScaleVerifyCallable(simulator, deviceCount), retryDelay, TimeUnit.SECONDS);
+ }
+ return null;
+ }
+ }
+
+ private static class TimeoutGuard implements Callable {
+
+ @Override
+ public Object call() throws Exception {
+ RESULTS_LOG.warn("Timeout for scale test reached after: {} ..aborting", stopwatch);
+ root.warn("Timeout for scale test reached after: {} ..aborting", stopwatch);
+ System.exit(0);
+ return null;
+ }
+ }
+
+ public static class LoggingWrapperExecutor extends ScheduledThreadPoolExecutor {
+
+ public LoggingWrapperExecutor(int corePoolSize) {
+ super(corePoolSize);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return super.schedule(wrapCallable(callable), delay, unit);
+ }
+
+ private Callable wrapCallable(Callable callable) {
+ return new LogOnExceptionCallable(callable);
+ }
+
+ private class LogOnExceptionCallable implements Callable {
+ private Callable theCallable;
+
+ public LogOnExceptionCallable(Callable theCallable) {
+ super();
+ this.theCallable = theCallable;
+ }
+
+ @Override
+ public Object call() throws Exception {
+ try {
+ theCallable.call();
+ return null;
+ } catch (Exception e) {
+ // log
+ root.warn("error in executing: " + theCallable + ". It will no longer be run!", e);
+
+ // rethrow so that the executor can do it's thing
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+}
@Arg(dest = "auth")
public ArrayList<String> auth;
+ @Arg(dest = "timeout")
+ public long timeout;
+
static ArgumentParser getParser() {
final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf stress client");
.help("Username and password for HTTP basic authentication in order username password.")
.dest("auth");
+ parser.addArgument("--timeout")
+ .type(Long.class)
+ .setDefault(5)
+ .help("Maximum time in minutes to wait for finishing all requests.")
+ .dest("timeout");
+
return parser;
}
void validate() {
Preconditions.checkArgument(port > 0, "Port =< 0");
Preconditions.checkArgument(editCount > 0, "Edit count =< 0");
+ Preconditions.checkArgument(timeout > 0, "Timeout =< 0");
Preconditions.checkArgument(editContent.exists(), "Edit content file missing");
Preconditions.checkArgument(editContent.isDirectory() == false, "Edit content file is a dir");
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import net.sourceforge.argparse4j.inf.ArgumentParser;
import net.sourceforge.argparse4j.inf.ArgumentParserException;
import org.opendaylight.netconf.test.tool.TestToolUtils;
LOG.info("Starting performance test");
final Stopwatch started = Stopwatch.createStarted();
try {
- final List<Future<Void>> futures = executorService.invokeAll(callables, 5, TimeUnit.MINUTES);
- for (final Future<Void> future : futures) {
- try {
- future.get(4L, TimeUnit.MINUTES);
- } catch (ExecutionException | TimeoutException e) {
- throw new RuntimeException(e);
+ final List<Future<Void>> futures = executorService.invokeAll(callables, parameters.timeout, TimeUnit.MINUTES);
+ for (int i = 0; i < futures.size(); i++) {
+ Future<Void> future = futures.get(i);
+ if (future.isCancelled()) {
+ LOG.info("{}. thread timed out.", i + 1);
+ } else {
+ try {
+ future.get();
+ } catch (final ExecutionException e) {
+ LOG.info("{}. thread failed.", i + 1, e);
+ }
}
}
- executorService.shutdownNow();
} catch (final InterruptedException e) {
- throw new RuntimeException("Unable to execute requests", e);
+ LOG.warn("Unable to execute requests", e);
}
+ executorService.shutdownNow();
started.stop();
LOG.info("FINISHED. Execution time: {}", started);
--- /dev/null
+/*
+ * Copyright (c) 2016 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.netconf.test.tool.scale.util;
+
+import java.io.File;
+import net.sourceforge.argparse4j.ArgumentParsers;
+import net.sourceforge.argparse4j.annotation.Arg;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import net.sourceforge.argparse4j.inf.ArgumentParserException;
+
+public class ScaleUtilParameters {
+
+ @Arg(dest = "distro-folder")
+ public File distroFolder;
+
+ static ArgumentParser getParser() {
+ final ArgumentParser parser = ArgumentParsers.newArgumentParser("scale test helper");
+
+ parser.addArgument("--distribution-folder")
+ .type(File.class)
+ .help("Directory where the karaf distribution for controller is located")
+ .dest("distro-folder");
+
+ return parser;
+ }
+
+ static ScaleUtilParameters parseArgs(final String[] args, final ArgumentParser parser) {
+ final ScaleUtilParameters parameters = new ScaleUtilParameters();
+ try {
+ parser.parseArgs(args, parameters);
+ return parameters;
+ } catch (ArgumentParserException e) {
+ parser.handleError(e);
+ }
+
+ System.exit(1);
+ return null;
+ }
+
+}
<appender-ref ref="STDOUT"/>
</root>
+ <appender name="RESULTS-FILE" class="ch.qos.logback.core.FileAppender">
+ <file>scale-results.log</file>
+ <append>true</append>
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="results" level="DEBUG" additivity="false">
+ <appender-ref ref="RESULTS-FILE"/>
+ </logger>
+
<logger name="com.ning.http.client" level="WARN"/>
</configuration>
\ No newline at end of file