<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-operations</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>ietf-netconf</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-broker-impl</artifactId>
--- /dev/null
+/*
+ * Copyright (c) 2015 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.mdsal.connector.ops;
+
+import com.google.common.collect.Lists;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class DataTreeChangeTracker {
+
+ private final ModifyAction defaultAction;
+
+ private final Deque<ModifyAction> actions;
+ private final Deque<PathArgument> currentPath;
+ private final ArrayList<DataTreeChange> dataTreeChanges;
+ private int deleteOperationTracker = 0;
+ private int removeOperationTracker = 0;
+
+ public DataTreeChangeTracker(final ModifyAction defaultAction) {
+ this.defaultAction = defaultAction;
+ this.currentPath = new ArrayDeque<>();
+ this.actions = new ArrayDeque<>();
+ this.dataTreeChanges = new ArrayList<>();
+ }
+
+ public void pushAction(final ModifyAction action) {
+ if (ModifyAction.DELETE.equals(action)) {
+ deleteOperationTracker++;
+ }
+
+ if (ModifyAction.REMOVE.equals(action)) {
+ removeOperationTracker++;
+ }
+ this.actions.push(action);
+ }
+
+ public ModifyAction peekAction() {
+ return this.actions.peekFirst();
+ }
+
+ public ModifyAction popAction() {
+ final ModifyAction popResult = actions.pop();
+ if (ModifyAction.DELETE.equals(popResult)) {
+ deleteOperationTracker--;
+ }
+
+ if (ModifyAction.REMOVE.equals(popResult)) {
+ removeOperationTracker--;
+ }
+ return popResult;
+ }
+
+ public int getDeleteOperationTracker() {
+ return deleteOperationTracker;
+ }
+
+ public int getRemoveOperationTracker() {
+ return removeOperationTracker;
+ }
+
+ public void addDataTreeChange(final DataTreeChange change) {
+ dataTreeChanges.add(change);
+ }
+
+ public ArrayList<DataTreeChange> getDataTreeChanges() {
+ return dataTreeChanges;
+ }
+
+ public ModifyAction getDefaultAction() {
+ return defaultAction;
+ }
+
+ public void pushPath(final PathArgument pathArgument) {
+ currentPath.push(pathArgument);
+ }
+
+ public PathArgument popPath() {
+ return currentPath.pop();
+ }
+
+ public Deque<PathArgument> getCurrentPath() {
+ return currentPath;
+ }
+
+
+ public static final class DataTreeChange {
+
+ private final NormalizedNode<?, ?> changeRoot;
+ private final ModifyAction action;
+ private final List<PathArgument> path;
+
+ public DataTreeChange(final NormalizedNode<?, ?> changeRoot, final ModifyAction action, final ArrayList<PathArgument> path) {
+ this.changeRoot = changeRoot;
+ this.action = action;
+ this.path = Lists.reverse(path);
+ }
+
+ public NormalizedNode<?, ?> getChangeRoot() {
+ return changeRoot;
+ }
+
+ public ModifyAction getAction() {
+ return action;
+ }
+
+ public List<PathArgument> getPath() {
+ return path;
+ }
+ }
+}
package org.opendaylight.controller.netconf.mdsal.connector.ops;
import com.google.common.base.Optional;
-import com.google.common.util.concurrent.CheckedFuture;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.mdsal.connector.CurrentSchemaContext;
import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider;
+import org.opendaylight.controller.netconf.mdsal.connector.ops.DataTreeChangeTracker.DataTreeChange;
import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.yangtools.yang.data.api.ModifyAction;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
-import org.opendaylight.yangtools.yang.data.operations.DataModificationException;
-import org.opendaylight.yangtools.yang.data.operations.DataModificationException.DataExistsException;
-import org.opendaylight.yangtools.yang.data.operations.DataModificationException.DataMissingException;
-import org.opendaylight.yangtools.yang.data.operations.DataOperations;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
private static final String CONFIG_KEY = "config";
private static final String TARGET_KEY = "target";
private static final String DEFAULT_OPERATION_KEY = "default-operation";
-
-
private final CurrentSchemaContext schemaContext;
private final TransactionProvider transactionProvider;
for (XmlElement element : configElement.getChildElements()) {
final String ns = element.getNamespace();
final DataSchemaNode schemaNode = getSchemaNodeFromNamespace(ns, element).get();
- YangInstanceIdentifier ident = YangInstanceIdentifier.of(schemaNode.getQName());
- final NormalizedNode storedNode = readStoredNode(LogicalDatastoreType.CONFIGURATION, ident);
- try {
- final Optional<NormalizedNode<?, ?>> newNode = modifyNode(schemaNode, element, storedNode, defaultAction);
- final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction();
- if (newNode.isPresent()) {
- rwTx.put(LogicalDatastoreType.CONFIGURATION, ident, newNode.get());
- } else {
- rwTx.delete(LogicalDatastoreType.CONFIGURATION, ident);
- }
- } catch (final DataExistsException e) {
- throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.data_exists, ErrorSeverity.error);
- } catch (final DataMissingException e) {
- throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.data_missing, ErrorSeverity.error);
- } catch (final DataModificationException e) {
- throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.operation_failed, ErrorSeverity.error);
- }
+ final DataTreeChangeTracker changeTracker = new DataTreeChangeTracker(defaultAction);
+ final DomToNormalizedNodeParserFactory.BuildingStrategyProvider editOperationStrategyProvider = new EditOperationStrategyProvider(changeTracker);
+
+ parseIntoNormalizedNode(schemaNode, element, editOperationStrategyProvider);
+ executeOperations(changeTracker);
}
return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.<String>absent());
}
- private NormalizedNode readStoredNode(final LogicalDatastoreType logicalDatastoreType, final YangInstanceIdentifier path) throws NetconfDocumentedException{
- final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction();
- final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readFuture = rwTx.read(logicalDatastoreType, path);
- try {
- if (readFuture.checkedGet().isPresent()) {
- final NormalizedNode node = readFuture.checkedGet().get();
- return node;
- } else {
- LOG.debug("Unable to read node : {} from {} datastore", path, logicalDatastoreType);
+ private void executeOperations(final DataTreeChangeTracker changeTracker) throws NetconfDocumentedException {
+ final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction();
+ final List<DataTreeChange> aa = changeTracker.getDataTreeChanges();
+ final ListIterator<DataTreeChange> iterator = aa.listIterator(aa.size());
+
+ while (iterator.hasPrevious()) {
+ final DataTreeChange dtc = iterator.previous();
+ executeChange(rwTx, dtc);
+ }
+ }
+
+ private void executeChange(final DOMDataReadWriteTransaction rwtx, final DataTreeChange change) throws NetconfDocumentedException {
+ switch (change.getAction()) {
+ case NONE:
+ return;
+ case MERGE:
+ rwtx.merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()), change.getChangeRoot());
+ break;
+ case CREATE:
+ try {
+ final Optional<NormalizedNode<?, ?>> readResult = rwtx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath())).checkedGet();
+ if (readResult.isPresent()) {
+ throw new NetconfDocumentedException("Data already exists, cannot execute CREATE operation", ErrorType.protocol, ErrorTag.data_exists, ErrorSeverity.error);
+ }
+ rwtx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()), change.getChangeRoot());
+ } catch (ReadFailedException e) {
+ LOG.warn("Read from datastore failed when trying to read data for create operation", change, e);
+ }
+ break;
+ case REPLACE:
+ rwtx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()), change.getChangeRoot());
+ break;
+ case DELETE:
+ try {
+ final Optional<NormalizedNode<?, ?>> readResult = rwtx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath())).checkedGet();
+ if (!readResult.isPresent()) {
+ throw new NetconfDocumentedException("Data is missing, cannot execute DELETE operation", ErrorType.protocol, ErrorTag.data_missing, ErrorSeverity.error);
+ }
+ rwtx.delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()));
+ } catch (ReadFailedException e) {
+ LOG.warn("Read from datastore failed when trying to read data for delete operation", change, e);
}
- } catch (final ReadFailedException e) {
- //only log this since DataOperations.modify will handle throwing an exception or writing the node.
- LOG.debug("Unable to read stored data: {}", path, e);
+ break;
+ case REMOVE:
+ rwtx.delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()));
+ break;
+ default:
+ LOG.warn("Unknown/not implemented operation, not executing");
}
+ }
+
+ private NormalizedNode parseIntoNormalizedNode(final DataSchemaNode schemaNode, final XmlElement element,
+ final DomToNormalizedNodeParserFactory.BuildingStrategyProvider editOperationStrategyProvider) {
+
- //we can return null here since DataOperations.modify handles null as input
- return null;
+ if (schemaNode instanceof ContainerSchemaNode) {
+ return DomToNormalizedNodeParserFactory
+ .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext.getCurrentContext(), editOperationStrategyProvider)
+ .getContainerNodeParser()
+ .parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode);
+ } else if (schemaNode instanceof ListSchemaNode) {
+ return DomToNormalizedNodeParserFactory
+ .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext.getCurrentContext(), editOperationStrategyProvider)
+ .getMapNodeParser()
+ .parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode);
+ } else {
+ //this should never happen since edit-config on any other node type should not be possible nor makes sense
+ LOG.debug("DataNode from module is not ContainerSchemaNode nor ListSchemaNode, aborting..");
+ }
+ throw new UnsupportedOperationException("implement exception if parse fails");
}
private Optional<DataSchemaNode> getSchemaNodeFromNamespace(final String namespace, final XmlElement element) throws NetconfDocumentedException{
ErrorTag.unknown_namespace,
ErrorSeverity.error);
}
-
} catch (URISyntaxException e) {
LOG.debug("Unable to create URI for namespace : {}", namespace);
}
return dataSchemaNode;
}
- private Optional<NormalizedNode<?, ?>> modifyNode(final DataSchemaNode schemaNode, final XmlElement element, final NormalizedNode storedNode, final ModifyAction defaultAction) throws DataModificationException{
- if (schemaNode instanceof ContainerSchemaNode) {
- final ContainerNode modifiedNode =
- DomToNormalizedNodeParserFactory
- .getInstance(DomUtils.defaultValueCodecProvider())
- .getContainerNodeParser()
- .parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode);
-
- final Optional<ContainerNode> oNode = DataOperations.modify((ContainerSchemaNode) schemaNode, (ContainerNode) storedNode, modifiedNode, defaultAction);
- if (!oNode.isPresent()) {
- return Optional.absent();
- }
-
- final NormalizedNode<?,?> node = oNode.get();
- return Optional.<NormalizedNode<?,?>>of(node);
- } else if (schemaNode instanceof ListSchemaNode) {
- final MapNode modifiedNode =
- DomToNormalizedNodeParserFactory
- .getInstance(DomUtils.defaultValueCodecProvider())
- .getMapNodeParser()
- .parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode);
-
- final Optional<MapNode> oNode = DataOperations.modify((ListSchemaNode) schemaNode, (MapNode) storedNode, modifiedNode, defaultAction);
- if (!oNode.isPresent()) {
- return Optional.absent();
- }
-
- final NormalizedNode<?, ?> node = oNode.get();
- return Optional.<NormalizedNode<?,?>>of(node);
- } else {
- //this should never happen since edit-config on any other node type should not be possible nor makes sense
- LOG.debug("DataNode from module is not ContainerSchemaNode nor ListSchemaNode, aborting..");
- return Optional.absent();
- }
- }
-
private Datastore extractTargetParameter(final XmlElement operationElement) throws NetconfDocumentedException {
final NodeList elementsByTagName = operationElement.getDomElement().getElementsByTagName(TARGET_KEY);
// Direct lookup instead of using XmlElement class due to performance
--- /dev/null
+/*
+ * Copyright (c) 2015 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.mdsal.connector.ops;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.EditConfigInput;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.ExtensibleParser;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+
+class EditOperationStrategyProvider extends DomToNormalizedNodeParserFactory.BuildingStrategyProvider {
+
+ private static final QName OPERATION_ATTRIBUTE = QName.create(EditConfigInput.QNAME.getNamespace(), null, XmlNetconfConstants.OPERATION_ATTR_KEY);
+
+ private final DataTreeChangeTracker changeTracker;
+
+ public EditOperationStrategyProvider(final DataTreeChangeTracker changeTracker) {
+ this.changeTracker = changeTracker;
+ }
+
+ @Override
+ protected ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, LeafNode<?>> forLeaf() {
+ return new NetconfOperationLeafStrategy(changeTracker);
+ }
+
+ @Override
+ protected ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, ContainerNode> forContainer() {
+ return new NetconfOperationContainerStrategy<>(changeTracker);
+ }
+
+ @Override
+ protected ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, MapNode> forMap() {
+ return new NetconfOperationCollectionStrategy<>(changeTracker);
+ }
+
+ @Override
+ protected ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeWithValue, LeafSetEntryNode<?>> forLeafSetEntry() {
+ return new NetconfOperationLeafSetEntryStrategy(changeTracker);
+ }
+
+ @Override
+ protected ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> forMapEntry() {
+ return new NetconfOperationContainerStrategy<>(changeTracker);
+ }
+
+ @Override
+ protected ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, OrderedMapNode> forOrderedList() {
+ return new NetconfOperationCollectionStrategy<>(changeTracker);
+ }
+
+ @Override
+ protected ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, UnkeyedListEntryNode> forUnkeyedListEntry() {
+ return new NetconfOperationContainerStrategy<>(changeTracker);
+ }
+
+ @Override
+ protected ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, UnkeyedListNode> forUnkeyedList() {
+ return new NetconfOperationCollectionStrategy<>(changeTracker);
+ }
+
+ @Override
+ protected ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, ChoiceNode> forChoice() {
+ return new NetconfOperationContainerStrategy<>(changeTracker);
+ }
+
+ @Override
+ public ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.AugmentationIdentifier, AugmentationNode> forAugmentation() {
+ return new NetconfOperationContainerStrategy<>(changeTracker);
+ }
+
+ private static class NetconfOperationCollectionStrategy<N extends NormalizedNode<YangInstanceIdentifier.NodeIdentifier, ?>> implements ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, N> {
+ private final DataTreeChangeTracker changeTracker;
+
+ public NetconfOperationCollectionStrategy(final DataTreeChangeTracker changeTracker) {
+ this.changeTracker = changeTracker;
+ }
+
+ @Nullable
+ @Override
+ public N build(final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ?, N> builder) {
+ changeTracker.popPath();
+ return builder.build();
+ }
+
+ @Override
+ public void prepareAttributes(final Map<QName, String> attributes, final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ?, N> containerBuilder) {
+ changeTracker.pushPath(containerBuilder.build().getIdentifier());
+ }
+ }
+
+ public static final class NetconfOperationLeafStrategy implements ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, LeafNode<?>> {
+
+ private final DataTreeChangeTracker dataTreeChangeTracker;
+
+ public NetconfOperationLeafStrategy(final DataTreeChangeTracker dataTreeChangeTracker) {
+ this.dataTreeChangeTracker = dataTreeChangeTracker;
+ }
+
+ @Nullable
+ @Override
+ public LeafNode<?> build(final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ?, LeafNode<?>> builder) {
+ LeafNode<?> node = builder.build();
+ String operation = (String) node.getAttributeValue(OPERATION_ATTRIBUTE);
+ if (operation == null) {
+ return node;
+ }
+
+ if(builder instanceof AttributesBuilder<?>) {
+ ((AttributesBuilder<?>) builder).withAttributes(Collections.<QName, String>emptyMap());
+ }
+
+ node = builder.build();
+
+ ModifyAction action = ModifyAction.fromXmlValue(operation);
+ if (dataTreeChangeTracker.getDeleteOperationTracker() > 0 || dataTreeChangeTracker.getRemoveOperationTracker() > 0) {
+ return node;
+ } else {
+ if (!action.equals(dataTreeChangeTracker.peekAction())) {
+ dataTreeChangeTracker.pushPath(node.getIdentifier());
+ dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(node, action, new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
+ dataTreeChangeTracker.popPath();
+ return null;
+ } else {
+ return node;
+ }
+ }
+ }
+
+ @Override
+ public void prepareAttributes(final Map<QName, String> attributes, final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ?, LeafNode<?>> containerBuilder) {
+ // Noop
+ }
+ }
+
+ public static final class NetconfOperationLeafSetEntryStrategy implements ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeWithValue, LeafSetEntryNode<?>> {
+
+ private final DataTreeChangeTracker dataTreeChangeTracker;
+
+ public NetconfOperationLeafSetEntryStrategy(final DataTreeChangeTracker dataTreeChangeTracker) {
+ this.dataTreeChangeTracker = dataTreeChangeTracker;
+ }
+
+ @Nullable
+ @Override
+ public LeafSetEntryNode<?> build(final NormalizedNodeBuilder<YangInstanceIdentifier.NodeWithValue, ?, LeafSetEntryNode<?>> builder) {
+ LeafSetEntryNode<?> node = builder.build();
+ String operation = (String) node.getAttributeValue(OPERATION_ATTRIBUTE);
+ if (operation == null) {
+ return node;
+ }
+
+ if (builder instanceof AttributesBuilder<?>) {
+ ((AttributesBuilder<?>) builder).withAttributes(Collections.<QName, String>emptyMap());
+ }
+
+ node = builder.build();
+
+ ModifyAction action = ModifyAction.fromXmlValue(operation);
+ if (dataTreeChangeTracker.getDeleteOperationTracker() > 0 || dataTreeChangeTracker.getRemoveOperationTracker() > 0) {
+ return node;
+ } else {
+ if (!action.equals(dataTreeChangeTracker.peekAction())) {
+ dataTreeChangeTracker.pushPath(node.getIdentifier());
+ dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(node, action, new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
+ dataTreeChangeTracker.popPath();
+ return null;
+ } else {
+ return node;
+ }
+ }
+ }
+
+ @Override
+ public void prepareAttributes(final Map<QName, String> attributes, final NormalizedNodeBuilder<YangInstanceIdentifier.NodeWithValue, ?, LeafSetEntryNode<?>> containerBuilder) {
+
+ }
+ }
+
+ public static final class NetconfOperationContainerStrategy<P extends YangInstanceIdentifier.PathArgument, N extends DataContainerNode<P>> implements ExtensibleParser.BuildingStrategy<P, N> {
+
+ private final DataTreeChangeTracker dataTreeChangeTracker;
+
+ public NetconfOperationContainerStrategy(final DataTreeChangeTracker dataTreeChangeTracker) {
+ this.dataTreeChangeTracker = dataTreeChangeTracker;
+ }
+
+ @Nullable
+ @Override
+ public N build(final NormalizedNodeBuilder<P, ?, N> builder) {
+ if (builder instanceof AttributesBuilder<?>) {
+ ((AttributesBuilder<?>) builder).withAttributes(Collections.<QName, String>emptyMap());
+ }
+
+ final N node = builder.build();
+ final ModifyAction currentAction = dataTreeChangeTracker.popAction();
+
+ //if we know that we are going to delete a parent node just complete the entire subtree
+ if (dataTreeChangeTracker.getDeleteOperationTracker() > 0 || dataTreeChangeTracker.getRemoveOperationTracker() > 0) {
+ dataTreeChangeTracker.popPath();
+ return node;
+ } else {
+ //if parent and current actions dont match create a DataTreeChange and add it to the change list
+ //dont add a new child to the parent node
+ if (!currentAction.equals(dataTreeChangeTracker.peekAction())) {
+ dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(node, currentAction, new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
+ dataTreeChangeTracker.popPath();
+ return null;
+ } else {
+ dataTreeChangeTracker.popPath();
+ return node;
+ }
+ }
+ }
+
+ @Override
+ public void prepareAttributes(final Map<QName, String> attributes, final NormalizedNodeBuilder<P, ?, N> containerBuilder) {
+ dataTreeChangeTracker.pushPath(containerBuilder.build().getIdentifier());
+ final String operation = attributes.get(OPERATION_ATTRIBUTE);
+ if (operation != null) {
+ dataTreeChangeTracker.pushAction(ModifyAction.fromXmlValue(operation));
+ } else {
+ dataTreeChangeTracker.pushAction(dataTreeChangeTracker.peekAction() != null
+ ? dataTreeChangeTracker.peekAction() : dataTreeChangeTracker.getDefaultAction());
+ }
+ }
+ }
+}
import com.google.common.io.ByteSource;
import java.io.IOException;
import java.io.InputStream;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
-import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.cluster.datastore.ConcurrentDOMDataBroker;
import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider;
import org.opendaylight.controller.netconf.mdsal.connector.ops.get.Get;
import org.opendaylight.controller.netconf.mdsal.connector.ops.get.GetConfig;
+import org.opendaylight.controller.netconf.util.test.NetconfXmlUnitRecursiveQualifier;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
assertEmptyDatastore(getConfigRunning());
try {
- edit("messages/mapping/editConfigs/editConfig_delete-root.xml");
+ edit("messages/mapping/editConfigs/editConfig_delete-top.xml");
fail("Delete should have failed - data is missing");
} catch (NetconfDocumentedException e) {
assertTrue(e.getErrorSeverity() == ErrorSeverity.error);
deleteDatastore();
}
+ public static void printDocument(Document doc) throws IOException, TransformerException {
+ TransformerFactory tf = TransformerFactory.newInstance();
+ Transformer transformer = tf.newTransformer();
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+ transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+ StringWriter writer = new StringWriter();
+ transformer.transform(new DOMSource(doc),
+ new StreamResult(writer));
+ LOG.warn(writer.getBuffer().toString());
+ }
+
+ @Test
+ public void testEditConfigWithMultipleOperations() throws Exception {
+ deleteDatastore();
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_setup.xml"), RPC_REPLY_OK);
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_1.xml"), RPC_REPLY_OK);
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_2.xml"), RPC_REPLY_OK);
+ verifyResponse(getConfigCandidate(), XmlFileLoader.xmlFileToDocument("messages/mapping/editConfigs/editConfig_merge_multiple_operations_2_control.xml"));
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_3_leaf_operations.xml"), RPC_REPLY_OK);
+ verifyResponse(getConfigCandidate(), XmlFileLoader.xmlFileToDocument("messages/mapping/editConfigs/editConfig_merge_multiple_operations_3_control.xml"));
+
+ deleteDatastore();
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_4_setup.xml"), RPC_REPLY_OK);
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_4_default-replace.xml"), RPC_REPLY_OK);
+
+ try {
+ edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_4_create_existing.xml");
+ fail();
+ } catch (NetconfDocumentedException e) {
+ assertTrue(e.getErrorSeverity() == ErrorSeverity.error);
+ assertTrue(e.getErrorTag() == ErrorTag.data_exists);
+ assertTrue(e.getErrorType() == ErrorType.protocol);
+ }
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_4_delete_children_operations.xml"), RPC_REPLY_OK);
+ verifyResponse(getConfigCandidate(), XmlFileLoader.xmlFileToDocument("messages/mapping/editConfigs/editConfig_merge_multiple_operations_4_delete_children_operations_control.xml"));
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_4_remove-non-existing.xml"), RPC_REPLY_OK);
+
+ try {
+ edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_4_delete-non-existing.xml");
+ fail();
+ } catch (NetconfDocumentedException e) {
+ assertTrue(e.getErrorSeverity() == ErrorSeverity.error);
+ assertTrue(e.getErrorTag() == ErrorTag.data_missing);
+ assertTrue(e.getErrorType() == ErrorType.protocol);
+ }
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_5_choice_setup.xml"), RPC_REPLY_OK);
+ verifyResponse(getConfigCandidate(), XmlFileLoader.xmlFileToDocument("messages/mapping/editConfigs/editConfig_merge_multiple_operations_5_choice_setup-control.xml"));
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_5_choice_setup2.xml"), RPC_REPLY_OK);
+ verifyResponse(getConfigCandidate(), XmlFileLoader.xmlFileToDocument("messages/mapping/editConfigs/editConfig_merge_multiple_operations_5_choice_setup2-control.xml"));
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_operations_5_choice_delete.xml"), RPC_REPLY_OK);
+ verifyResponse(getConfigCandidate(), XmlFileLoader.xmlFileToDocument("messages/mapping/editConfigs/editConfig_merge_multiple_operations_4_delete_children_operations_control.xml"));
+
+ deleteDatastore();
+ }
+
@Test
public void testFiltering() throws Exception {
verifyResponse(edit("messages/mapping/editConfigs/editConfig-filtering-setup.xml"), RPC_REPLY_OK);
verifyResponse(commit(), RPC_REPLY_OK);
- verifyFilterIdentifier("messages/mapping/filters/get-filter-alluser.xml",
- YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
+ //TODO uncomment these tests once we can parse KeyedListNode as a selection node, currently you cannot use a KeyedList as a selection node in filter
+// verifyFilterIdentifier("messages/mapping/filters/get-filter-alluser.xml",
+// YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
verifyFilterIdentifier("messages/mapping/filters/get-filter-company-info.xml",
YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
verifyFilterIdentifier("messages/mapping/filters/get-filter-modules-and-admin.xml",
YangInstanceIdentifier.builder().node(TOP).build());
verifyFilterIdentifier("messages/mapping/filters/get-filter-only-names-types.xml",
YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
- verifyFilterIdentifier("messages/mapping/filters/get-filter-specific-module-type-and-user.xml",
- YangInstanceIdentifier.builder().node(TOP).build());
- verifyFilterIdentifier("messages/mapping/filters/get-filter-superuser.xml",
- YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
+// verifyFilterIdentifier("messages/mapping/filters/get-filter-specific-module-type-and-user.xml",
+// YangInstanceIdentifier.builder().node(TOP).build());
+// verifyFilterIdentifier("messages/mapping/filters/get-filter-superuser.xml",
+// YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
verifyFilterIdentifier("messages/mapping/filters/get-filter-users.xml",
YangInstanceIdentifier.builder().node(TOP).node(USERS).build());
assertEmptyDatastore(getConfigRunning());
}
- private void verifyResponse(Document response, Document template){
+ private void verifyResponse(Document response, Document template) throws IOException, TransformerException {
DetailedDiff dd = new DetailedDiff(new Diff(response, template));
- dd.overrideElementQualifier(new RecursiveElementNameAndTextQualifier());
- assertTrue(dd.similar());
+ dd.overrideElementQualifier(new NetconfXmlUnitRecursiveQualifier());
+
+ printDocument(response);
+ printDocument(template);
+
+ assertTrue(dd.toString(), dd.similar());
}
private void assertEmptyDatastore(Document response) {
<rpc-reply a="64" id="a" message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlnx="a:b:c:d">
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <mapping-nodes xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="create" xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
<mapping-node>
<id>node1-put</id>
<content>put content</content>
</test-option>
<default-operation>none</default-operation>
<config>
- <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test" xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test" xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="remove">
</mapping-nodes>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test" xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="remove">
+ </top>
</config>
</edit-config>
</rpc>
\ No newline at end of file
<rpc-reply a="64" id="a" message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlnx="a:b:c:d">
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <mapping-nodes xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="replace" xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
<mapping-node>
<id>new-node7</id>
<content>new node content</content>
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <users>
+ <user xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>rooty root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>admin</name>
+ <type>superuser</type>
+ <full-name>johny admin updated</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ </users>
+ <modules>
+ <module xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">
+ <id>module1</id>
+ <type>type1</type>
+ <desc>module1-desc</desc>
+ </module>
+ <module xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">
+ <id>module2</id>
+ <type>type1</type>
+ <desc>module2-desc</desc>
+ </module>
+ <module>
+ <id>new module</id>
+ <type>merged module</type>
+ <desc>merged module desc</desc>
+ </module>
+ </modules>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc-reply message-id="101" a="64" id="a" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlnx="a:b:c:d">
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-node>
+ <id>node1-merge</id>
+ <content>overwritten old content node1</content>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node5</id>
+ <content>new node5 content</content>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node6</id>
+ <content>new node6 content</content>
+ </mapping-node>
+ <mapping-node>
+ <id>node2-merge</id>
+ <content>overwritten old content node2</content>
+ </mapping-node>
+ </mapping-nodes>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <users>
+ <user>
+ <name>admin</name>
+ <full-name>johny admin updated</full-name>
+ <type>superuser</type>
+ <company-info>
+ <id>2</id>
+ <dept>2</dept>
+ </company-info>
+ </user>
+ <user>
+ <name>regular</name>
+ <full-name>burt regular</full-name>
+ <type>user</type>
+ <company-info>
+ <id>3</id>
+ <dept>3</dept>
+ </company-info>
+ </user>
+ </users>
+ <modules>
+ <augmented-container>
+ <identifier>augmented container</identifier>
+ </augmented-container>
+ <module>
+ <id>module3</id>
+ <type>unknown</type>
+ <desc>module3-desc</desc>
+ </module>
+ <module>
+ <id>new module</id>
+ <type>merged module</type>
+ <desc>merged module desc</desc>
+ </module>
+ </modules>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <users xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="replace">
+ <user>
+ <name>single user</name>
+ <type>superuser</type>
+ <full-name>i replaced everything</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ </users>
+ <modules>
+ <augmented-container xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">
+ <identifier>augmented container</identifier>
+ </augmented-container>
+ <module xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="replace">
+ <id>module1</id>
+ <type>type1</type>
+ <desc>module1-desc</desc>
+ </module>
+ <module xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="create">
+ <id>module2</id>
+ <type>type1</type>
+ <desc>module2-desc</desc>
+ </module>
+ </modules>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc-reply message-id="101" a="64" id="a" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlnx="a:b:c:d">
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-node>
+ <id>node1-merge</id>
+ <content>overwritten old content node1</content>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node5</id>
+ <content>new node5 content</content>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node6</id>
+ <content>new node6 content</content>
+ </mapping-node>
+ <mapping-node>
+ <id>node2-merge</id>
+ <content>overwritten old content node2</content>
+ </mapping-node>
+ </mapping-nodes>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <modules>
+ <module>
+ <id>module1</id>
+ <type>type1</type>
+ <desc>module1-desc</desc>
+ </module>
+ <module>
+ <id>module2</id>
+ <type>type1</type>
+ <desc>module2-desc</desc>
+ </module>
+ <module>
+ <id>module3</id>
+ <type>unknown</type>
+ <desc>module3-desc</desc>
+ </module>
+ <module>
+ <id>new module</id>
+ <type>merged module</type>
+ <desc>merged module desc</desc>
+ </module>
+ </modules>
+ <users>
+ <user>
+ <name>single user</name>
+ <type>superuser</type>
+ <full-name>i replaced everything</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ </users>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc-reply message-id="101" a="64" id="a" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlnx="a:b:c:d">
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-node>
+ <id>node1-merge</id>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node5</id>
+ <content>new-node5 replaced content</content>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node6</id>
+ </mapping-node>
+ </mapping-nodes>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <users>
+ <user>
+ <name>single user</name>
+ <type>superuser</type>
+ <full-name>i replaced everything</full-name>
+ </user>
+ </users>
+ <modules>
+ <module>
+ <id>module1</id>
+ <type>type1</type>
+ <desc>module1-desc</desc>
+ </module>
+ <module>
+ <id>module2</id>
+ <type>type1</type>
+ <desc>module2-desc</desc>
+ </module>
+ <module>
+ <id>module3</id>
+ <type>unknown</type>
+ <desc>module3-desc</desc>
+ </module>
+ <module>
+ <id>new module</id>
+ <type>merged module</type>
+ <desc>merged module desc</desc>
+ </module>
+ </modules>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-node>
+ <id>node1-merge</id>
+ <content xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">overwritten old content node1</content>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node5</id>
+ <content xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="replace">new-node5 replaced content</content>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node6</id>
+ <content xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">new node6 content</content>
+ </mapping-node>
+ <mapping-node xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">
+ <id>node2-merge</id>
+ <content>overwritten old content node2</content>
+ </mapping-node>
+ </mapping-nodes>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <users>
+ <user>
+ <name>single user</name>
+ <type>superuser</type>
+ <full-name>i replaced everything</full-name>
+ <company-info xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ </users>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mid-level xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="create">
+ <low-level/>
+ </mid-level>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>replace</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mid-level>
+ <low-level>
+ <lowest-level>
+ <note>note1</note>
+ <note>note2</note>
+ <note>note3</note>
+ <note>note4</note>
+ </lowest-level>
+ </low-level>
+ <low-level2>
+ <note>note1</note>
+ <note>note2</note>
+ <note>note3</note>
+ <note>note4</note>
+ </low-level2>
+ </mid-level>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mid-level xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete"/>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mid-level xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete">
+ <low-level>
+ <lowest-level xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="create">
+ <note>note1</note>
+ <note>note2</note>
+ <note>note3</note>
+ <note>note4</note>
+ <note>note5</note>
+ </lowest-level>
+ </low-level>
+ <low-level2 xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="create">
+ <note>note1</note>
+ <note>note2</note>
+ <note>note3</note>
+ <note>note4</note>
+ <note>note5</note>
+ </low-level2>
+ </mid-level>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" a="64" id="a" message-id="101" xmlnx="a:b:c:d">
+ <data>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test"/>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mid-level xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="remove"/>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mid-level>
+ <low-level>
+ <lowest-level>
+ <note>note1</note>
+ <note>note2</note>
+ <note>note3</note>
+ <note>note4</note>
+ <note>note5</note>
+ </lowest-level>
+ </low-level>
+ <low-level2>
+ <note>note1</note>
+ <note>note2</note>
+ <note>note3</note>
+ <note>note4</note>
+ <note>note5</note>
+ </low-level2>
+ </mid-level>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <choice-wrapper xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="delete"/>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" a="64" id="a" message-id="101" xmlnx="a:b:c:d">
+ <data>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <choice-wrapper>
+ <text>choice created</text>
+ </choice-wrapper>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <choice-wrapper>
+ <text xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="create">choice created</text>
+ </choice-wrapper>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" a="64" id="a" message-id="101" xmlnx="a:b:c:d">
+ <data>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <choice-wrapper>
+ <text>choice updated</text>
+ <text2>choice text2 created</text2>
+ </choice-wrapper>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <choice-wrapper>
+ <text>choice updated</text>
+ <text2 xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="create">choice text2 created</text2>
+ </choice-wrapper>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2015 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
+ -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <edit-config>
+ <target>
+ <candidate/>
+ </target>
+ <test-option>
+ set
+ </test-option>
+ <default-operation>merge</default-operation>
+ <config>
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-node>
+ <id>node1-merge</id>
+ <content>overwritten old content node1</content>
+ </mapping-node>
+ <mapping-node>
+ <id>node2-merge</id>
+ <content>overwritten old content node2</content>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node5</id>
+ <content>new node5 content</content>
+ </mapping-node>
+ <mapping-node>
+ <id>new-node6</id>
+ <content>new node6 content</content>
+ </mapping-node>
+ </mapping-nodes>
+ <top xmlns="urn:opendaylight:mdsal:mapping:test">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>rooty root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>admin</name>
+ <type>superuser</type>
+ <full-name>johny admin</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>regular</name>
+ <type>user</type>
+ <full-name>burt regular</full-name>
+ <company-info>
+ <dept>3</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <modules>
+ <augmented-container>
+ <identifier>augmented container</identifier>
+ </augmented-container>
+ <module>
+ <id>module1</id>
+ <type>type1</type>
+ <desc>module1-desc</desc>
+ </module>
+ <module>
+ <id>module2</id>
+ <type>type1</type>
+ <desc>module2-desc</desc>
+ </module>
+ <module>
+ <id>module3</id>
+ <type>unknown</type>
+ <desc>module3-desc</desc>
+ </module>
+ </modules>
+ </top>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
list user {
+ key "name";
+
leaf name {
type string;
}
list module {
+ key "id";
+
leaf id {
type string;
}
}
}
}
+
+ augment "/map:top" {
+ container mid-level {
+ container low-level {
+ container lowest-level {
+ leaf-list note {
+ type string;
+ }
+ }
+ }
+ container low-level2 {
+ leaf-list note {
+ type string;
+ }
+ }
+ }
+ }
}
\ No newline at end of file