Bug 6256 - OpenDaylight RESTCONF XML selects wrong YANG model for southbound NETCONF 30/46530/2
authorIvan Hrasko <ivan.hrasko@pantheon.tech>
Tue, 27 Sep 2016 12:32:33 +0000 (14:32 +0200)
committerIvan Hrasko <ivan.hrasko@pantheon.tech>
Wed, 5 Oct 2016 08:25:20 +0000 (10:25 +0200)
- dataSchemaNode can contains multiple childs with the same name,
thus when looking for child also namespace has to be compared
- added unit tests

Change-Id: I922e96fd2aea686c27ad4683ef7366c6718486c3
Signed-off-by: Ivan Hrasko <ivan.hrasko@pantheon.tech>
opendaylight/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlNormalizedNodeBodyReader.java
opendaylight/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReader.java
opendaylight/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReaderMountPoint.java
opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/xmlDataFindBarContainer.xml [new file with mode: 0644]
opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/xmlDataFindFooContainer.xml [new file with mode: 0644]
opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/yang/bar-module.yang [new file with mode: 0644]
opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/yang/foo-module.yang [new file with mode: 0644]

index 215a29d7aca4b71a99ddc7b431377600ce165747..48fcdb0a8a9ab3ea530fd5eb1319b57b836674c9 100644 (file)
@@ -130,10 +130,11 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
         } else if (schemaNodeContext instanceof DataSchemaNode) {
             schemaNode = (DataSchemaNode) schemaNodeContext;
         } else {
-            throw new IllegalStateException("Unknow SchemaNode");
+            throw new IllegalStateException("Unknown SchemaNode");
         }
 
         final String docRootElm = doc.getDocumentElement().getLocalName();
+        final String docRootNamespace = doc.getDocumentElement().getNamespaceURI();
         final List<YangInstanceIdentifier.PathArgument> iiToDataList = new ArrayList<>();
         InstanceIdentifierContext<? extends SchemaNode> outIIContext;
 
@@ -143,7 +144,7 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
                 DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, pathContext.getSchemaContext());
 
         if (isPost() && !isRpc) {
-            final Deque<Object> foundSchemaNodes = findPathToSchemaNodeByName(schemaNode, docRootElm);
+            final Deque<Object> foundSchemaNodes = findPathToSchemaNodeByName(schemaNode, docRootElm, docRootNamespace);
             if (foundSchemaNodes.isEmpty()) {
                 throw new IllegalStateException(String.format("Child \"%s\" was not found in parent schema node \"%s\"",
                         docRootElm, schemaNode.getQName()));
@@ -162,7 +163,7 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
 
         NormalizedNode<?, ?> parsed = null;
 
-        if(schemaNode instanceof ContainerSchemaNode) {
+        if (schemaNode instanceof ContainerSchemaNode) {
             parsed = parserFactory.getContainerNodeParser().parse(Collections.singletonList(doc.getDocumentElement()), (ContainerSchemaNode) schemaNode);
         } else if(schemaNode instanceof ListSchemaNode) {
             final ListSchemaNode casted = (ListSchemaNode) schemaNode;
@@ -182,28 +183,35 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
         return new NormalizedNodeContext(outIIContext, parsed);
     }
 
-    private static Deque<Object> findPathToSchemaNodeByName(final DataSchemaNode schemaNode, final String elementName) {
+    private static Deque<Object> findPathToSchemaNodeByName(final DataSchemaNode schemaNode, final String elementName,
+                                                            final String namespace) {
         final Deque<Object> result = new ArrayDeque<>();
         final ArrayList<ChoiceSchemaNode> choiceSchemaNodes = new ArrayList<>();
         final Collection<DataSchemaNode> children = ((DataNodeContainer) schemaNode).getChildNodes();
         for (final DataSchemaNode child : children) {
             if (child instanceof ChoiceSchemaNode) {
                 choiceSchemaNodes.add((ChoiceSchemaNode) child);
-            } else if (child.getQName().getLocalName().equalsIgnoreCase(elementName)) {
+            } else if (child.getQName().getLocalName().equalsIgnoreCase(elementName)
+                    && child.getQName().getNamespace().toString().equalsIgnoreCase(namespace)) {
+                // add child to result
                 result.push(child);
+
+                // find augmentation
                 if (child.isAugmenting()) {
                     final AugmentationSchema augment = findCorrespondingAugment(schemaNode, child);
                     if (augment != null) {
                         result.push(augment);
                     }
                 }
+
+                // return result
                 return result;
             }
         }
 
         for (final ChoiceSchemaNode choiceNode : choiceSchemaNodes) {
             for (final ChoiceCaseNode caseNode : choiceNode.getCases()) {
-                final Deque<Object> resultFromRecursion = findPathToSchemaNodeByName(caseNode, elementName);
+                final Deque<Object> resultFromRecursion = findPathToSchemaNodeByName(caseNode, elementName, namespace);
                 if (!resultFromRecursion.isEmpty()) {
                     resultFromRecursion.push(choiceNode);
                     if (choiceNode.isAugmenting()) {
index 8eb1a1b76b1ed3f39e7c1fea9f761172baee5f4d..f5ac6a92aec19b4fc19292c80b244f7ce133bbb7 100644 (file)
@@ -194,4 +194,48 @@ public class TestXmlBodyReader extends AbstractBodyReaderTest {
         assertEquals(dataNodeIdent, nnContext.getInstanceIdentifierContext().getInstanceIdentifier());
         assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent));
     }
+
+    /**
+     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
+     * used to distinguish between them to find correct one. Check if container was found not only according to its name
+     * but also by correct namespace used in payload.
+     */
+    @Test
+    public void findFooContainerUsingNamespaceTest() throws Exception {
+        mockBodyReader("", xmlBodyReader, true);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml");
+        final NormalizedNodeContext returnValue = xmlBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+
+        // check return value
+        checkNormalizedNodeContext(returnValue);
+        // check if container was found both according to its name and namespace
+        assertEquals("Not correct container found, name was ignored",
+                "foo-bar-container", returnValue.getData().getNodeType().getLocalName());
+        assertEquals("Not correct container found, namespace was ignored",
+                "foo:module", returnValue.getData().getNodeType().getNamespace().toString());
+    }
+
+    /**
+     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
+     * used to distinguish between them to find correct one. Check if container was found not only according to its name
+     * but also by correct namespace used in payload.
+     */
+    @Test
+    public void findBarContainerUsingNamespaceTest() throws Exception {
+        mockBodyReader("", xmlBodyReader, true);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml");
+        final NormalizedNodeContext returnValue = xmlBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+
+        // check return value
+        checkNormalizedNodeContext(returnValue);
+        // check if container was found both according to its name and namespace
+        assertEquals("Not correct container found, name was ignored",
+                "foo-bar-container", returnValue.getData().getNodeType().getLocalName());
+        assertEquals("Not correct container found, namespace was ignored",
+                "bar:module", returnValue.getData().getNodeType().getNamespace().toString());
+    }
 }
index be13ea19b62a14b857248ec8687242a7d8ba50f3..249c642a9c16047e96ae16483edb8196cc8ecde1 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.controller.sal.rest.impl.test.providers;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
@@ -177,4 +178,48 @@ public class TestXmlBodyReaderMountPoint extends AbstractBodyReaderTest {
         assertNotNull(NormalizedNodes.findNode(nnContext.getData(),
                 dataNodeIdent));
     }
+
+    /**
+     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
+     * used to distinguish between them to find correct one. Check if container was found not only according to its name
+     * but also by correct namespace used in payload.
+     */
+    @Test
+    public void findFooContainerUsingNamespaceTest() throws Exception {
+        mockBodyReader("instance-identifier-module:cont/yang-ext:mount", xmlBodyReader, true);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml");
+        final NormalizedNodeContext returnValue = xmlBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+
+        // check return value
+        checkMountPointNormalizedNodeContext(returnValue);
+        // check if container was found both according to its name and namespace
+        assertEquals("Not correct container found, name was ignored",
+                "foo-bar-container", returnValue.getData().getNodeType().getLocalName());
+        assertEquals("Not correct container found, namespace was ignored",
+                "foo:module", returnValue.getData().getNodeType().getNamespace().toString());
+    }
+
+    /**
+     * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be
+     * used to distinguish between them to find correct one. Check if container was found not only according to its name
+     * but also by correct namespace used in payload.
+     */
+    @Test
+    public void findBarContainerUsingNamespaceTest() throws Exception {
+        mockBodyReader("instance-identifier-module:cont/yang-ext:mount", xmlBodyReader, true);
+        final InputStream inputStream = TestXmlBodyReader.class
+                .getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml");
+        final NormalizedNodeContext returnValue = xmlBodyReader
+                .readFrom(null, null, null, mediaType, null, inputStream);
+
+        // check return value
+        checkMountPointNormalizedNodeContext(returnValue);
+        // check if container was found both according to its name and namespace
+        assertEquals("Not correct container found, name was ignored",
+                "foo-bar-container", returnValue.getData().getNodeType().getLocalName());
+        assertEquals("Not correct container found, namespace was ignored",
+                "bar:module", returnValue.getData().getNodeType().getNamespace().toString());
+    }
 }
diff --git a/opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/xmlDataFindBarContainer.xml b/opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/xmlDataFindBarContainer.xml
new file mode 100644 (file)
index 0000000..6523345
--- /dev/null
@@ -0,0 +1,10 @@
+<!--
+  ~ 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
+  -->
+
+<foo-bar-container xmlns="bar:module">
+</foo-bar-container>
\ No newline at end of file
diff --git a/opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/xmlDataFindFooContainer.xml b/opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/xml/xmlDataFindFooContainer.xml
new file mode 100644 (file)
index 0000000..93aeff8
--- /dev/null
@@ -0,0 +1,10 @@
+<!--
+  ~ 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
+  -->
+
+<foo-bar-container xmlns="foo:module">
+</foo-bar-container>
\ No newline at end of file
diff --git a/opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/yang/bar-module.yang b/opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/yang/bar-module.yang
new file mode 100644 (file)
index 0000000..90de085
--- /dev/null
@@ -0,0 +1,11 @@
+module bar-module {
+  namespace "bar:module";
+
+  prefix "bar-module";
+    revision 2016-09-29 {
+  }
+
+  /* This container has the same name as container in foo-module */
+  container foo-bar-container {
+  }
+}
\ No newline at end of file
diff --git a/opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/yang/foo-module.yang b/opendaylight/restconf/sal-rest-connector/src/test/resources/instanceidentifier/yang/foo-module.yang
new file mode 100644 (file)
index 0000000..16b8e7f
--- /dev/null
@@ -0,0 +1,11 @@
+module foo-module {
+  namespace "foo:module";
+
+  prefix "foo-module";
+    revision 2016-09-29 {
+  }
+
+  /* This container has the same name as container in bar-module */
+  container foo-bar-container {
+  }
+}
\ No newline at end of file