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.Builders;
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;
}
private static class NetconfOperationCollectionStrategy<N extends NormalizedNode<YangInstanceIdentifier.NodeIdentifier, ?>> implements ExtensibleParser.BuildingStrategy<YangInstanceIdentifier.NodeIdentifier, N> {
- private final DataTreeChangeTracker changeTracker;
+ private final DataTreeChangeTracker dataTreeChangeTracker;
public NetconfOperationCollectionStrategy(final DataTreeChangeTracker changeTracker) {
- this.changeTracker = changeTracker;
+ this.dataTreeChangeTracker = changeTracker;
}
@Nullable
@Override
public N build(final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ?, N> builder) {
- changeTracker.popPath();
- return builder.build();
+ final N node = builder.build();
+
+ // Try to auto create list-parent, mixin nodes to prevent exception from data tree
+
+ // Not under DELETE/REMOVE subtree
+ if (dataTreeChangeTracker.getDeleteOperationTracker() == 0 &&
+ dataTreeChangeTracker.getRemoveOperationTracker() == 0) {
+
+ // Artificial list-parent, mixin, empty node
+ final NormalizedNode<?, ?> mixinParent = getEmptyCollectionParent(node);
+
+ // Make sure to merge list-parent, mixin empty node to prevent missing parent node ex
+ dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(
+ mixinParent, ModifyAction.MERGE, new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
+ }
+
+ dataTreeChangeTracker.popPath();
+ return node;
+ }
+
+ private NormalizedNode<?, ?> getEmptyCollectionParent(final N node) {
+ if(node instanceof OrderedMapNode) {
+ return Builders.orderedMapBuilder().withNodeIdentifier(node.getIdentifier()).build();
+ } else if(node instanceof MapNode) {
+ return Builders.mapBuilder().withNodeIdentifier(node.getIdentifier()).build();
+ }
+
+ throw new IllegalArgumentException("Unexpected node type " + node.getClass() +
+ " while auto creating list like mixin node");
}
@Override
public void prepareAttributes(final Map<QName, String> attributes, final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ?, N> containerBuilder) {
- changeTracker.pushPath(containerBuilder.build().getIdentifier());
+ dataTreeChangeTracker.pushPath(containerBuilder.build().getIdentifier());
}
}
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
+import com.google.common.io.ByteSource;
import com.google.common.util.concurrent.Futures;
import java.io.IOException;
import java.io.InputStream;
import java.util.EnumMap;
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.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.w3c.dom.NodeList;
import org.xml.sax.SAXException;
-import com.google.common.io.ByteSource;
-
public class NetconfMDSalMappingTest {
private static final Logger LOG = LoggerFactory.getLogger(NetconfMDSalMappingTest.class);
}
+ @Test
+ public void testListParentAutoCreation() throws Exception {
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_n2.xml"), RPC_REPLY_OK);
+ verifyResponse(commit(), RPC_REPLY_OK);
+ getConfigRunning();
+ deleteDatastore();
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_n3.xml"), RPC_REPLY_OK);
+ verifyResponse(commit(), RPC_REPLY_OK);
+ getConfigRunning();
+ deleteDatastore();
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_n4.xml"), RPC_REPLY_OK);
+ verifyResponse(commit(), RPC_REPLY_OK);
+ getConfigRunning();
+ deleteDatastore();
+
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_n5.xml"), RPC_REPLY_OK);
+ verifyResponse(commit(), RPC_REPLY_OK);
+ getConfigRunning();
+ deleteDatastore();
+
+ // The last test should fail because the netconf edit tries to create nested list entry without touching the
+ // parent structure (indicated by NONE operation). So the edit should fail. However thanks to our auto-create
+ // list parent mechanics, parent structure will be created.
+ verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_n6.xml"), RPC_REPLY_OK);
+ verifyResponse(commit(), RPC_REPLY_OK);
+ getConfigRunning();
+ deleteDatastore();
+ }
+
@Test
public void testKeyOrder() throws Exception {
verifyResponse(edit("messages/mapping/editConfigs/editConfig_merge_multiple_keys_1.xml"), RPC_REPLY_OK);
</mapping-nodes>
<top xmlns="urn:opendaylight:mdsal:mapping:test" xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="remove">
</top>
+ <top-list xmlns="urn:opendaylight:mdsal:mapping:test" xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="remove">
+ <id>1</id>
+ </top-list>
</config>
</edit-config>
</rpc>
\ No newline at end of file
</test-option>
<default-operation>merge</default-operation>
<config>
- <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test" >
<mapping-node>
<id>node1-put</id>
<content>put 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>none</default-operation>
+ <config>
+ <top-list xmlns="urn:opendaylight:mdsal:mapping:test" xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0"
+ a:operation="replace">
+ <id>1</id>
+ </top-list>
+ </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-list xmlns="urn:opendaylight:mdsal:mapping:test" xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0"
+ a:operation="replace">
+ <id>1</id>
+ </top-list>
+ </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>
+ <config>
+ <top-list xmlns="urn:opendaylight:mdsal:mapping:test">
+ <id>1</id>
+ </top-list>
+ </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>
+ <config>
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test" >
+ <mapping-node xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0"
+ a:operation="replace">
+ <id>node1-put</id>
+ <content>put content</content>
+ </mapping-node>
+ </mapping-nodes>
+ </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>none</default-operation>
+ <config>
+ <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+ <mapping-node xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0"
+ a:operation="replace">
+ <id>node1-put</id>
+ <content>put content</content>
+ </mapping-node>
+ </mapping-nodes>
+ </config>
+ </edit-config>
+</rpc>
\ No newline at end of file
revision "2015-02-26";
+ list top-list {
+ key "id";
+
+ leaf id {
+ type string;
+ }
+ }
+
+
container mapping-nodes {
list multiple-keys {
type string;
}
}
-
+
list mapping-node{
key "id";
leaf id {