Bug 5485: Improve DataTreeModification pruning on recovery 13/36013/5
authorTom Pantelis <tpanteli@brocade.com>
Wed, 9 Mar 2016 04:07:02 +0000 (23:07 -0500)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 16 Mar 2016 17:42:36 +0000 (17:42 +0000)
Modified the PruningDataTreeModification and NormalizedNodePruner to
validate path and node QNames via the SchemaContext instead of just
namespaces. This allows migration support for any element to be removed
from a yang hierarchy.

Also handled SchemaValidationFailedException on ready which can happen
with writes which don't immediately validate the sctructure as merge
does. The modification tree is re-applied with pruning.

Change-Id: I8c4f84fbaa93563ce6741d7a3e15855f7fc4940f
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
14 files changed:
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodeBuilderWrapper.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java
opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePrunerTest.java
opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/util/TestModel.java
opendaylight/md-sal/sal-clustering-commons/src/test/resources/odl-datastore-test.yang
opendaylight/md-sal/sal-clustering-commons/src/test/resources/simplelogger.properties [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardRecoveryCoordinator.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/SimpleShardDataTreeCohort.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModification.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractShardTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModificationTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CompositeModel.java
opendaylight/md-sal/sal-distributed-datastore/src/test/resources/odl-datastore-augmentation.yang

index 5295c88..259529e 100644 (file)
@@ -8,18 +8,22 @@
 
 package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
 
+import com.google.common.base.Optional;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
 
 public class NormalizedNodeBuilderWrapper {
     private final NormalizedNodeContainerBuilder<?,?,?,?> builder;
     private final YangInstanceIdentifier.PathArgument identifier;
+    private final Optional<DataSchemaContextNode<?>> schemaNode;
 
-
-    NormalizedNodeBuilderWrapper(NormalizedNodeContainerBuilder<?,?,?,?> builder, YangInstanceIdentifier.PathArgument identifier) {
+    NormalizedNodeBuilderWrapper(NormalizedNodeContainerBuilder<?,?,?,?> builder,
+            YangInstanceIdentifier.PathArgument identifier, Optional<DataSchemaContextNode<?>> schemaNode) {
         this.builder = builder;
         this.identifier = identifier;
+        this.schemaNode = schemaNode;
     }
 
     public NormalizedNodeContainerBuilder builder(){
@@ -34,4 +38,7 @@ public class NormalizedNodeBuilderWrapper {
         return identifier;
     }
 
+    public Optional<DataSchemaContextNode<?>> getSchema() {
+        return schemaNode;
+    }
 }
index 27504f3..03fb641 100644 (file)
@@ -9,23 +9,27 @@
 package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import java.io.IOException;
 import java.net.URI;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
 import javax.xml.transform.dom.DOMSource;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 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.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * The NormalizedNodePruner removes all nodes from the input NormalizedNode that do not have a corresponding
@@ -33,19 +37,16 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  *
  */
 public class NormalizedNodePruner implements NormalizedNodeStreamWriter {
+    private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodePruner.class);
 
     public static final URI BASE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
     private final SimpleStack<NormalizedNodeBuilderWrapper> stack = new SimpleStack<>();
     private NormalizedNode<?,?> normalizedNode;
-    private final Set<URI> validNamespaces;
+    private final DataSchemaContextNode<?> nodePathSchemaNode;
     private boolean sealed = false;
 
-    public NormalizedNodePruner(SchemaContext schemaContext) {
-        this(NormalizedNodePruner.namespaces(schemaContext));
-    }
-
-    public NormalizedNodePruner(Set<URI> validNamespaces) {
-        this.validNamespaces = validNamespaces;
+    public NormalizedNodePruner(YangInstanceIdentifier nodePath, SchemaContext schemaContext) {
+        nodePathSchemaNode = findSchemaNodeForNodePath(nodePath, schemaContext);
     }
 
     @SuppressWarnings("unchecked")
@@ -54,17 +55,18 @@ public class NormalizedNodePruner implements NormalizedNodeStreamWriter {
 
         checkNotSealed();
 
-        if(!isValidNamespace(nodeIdentifier)){
-            return;
-        }
-
         NormalizedNodeBuilderWrapper parent = stack.peek();
         LeafNode<Object> leafNode = Builders.leafBuilder().withNodeIdentifier(nodeIdentifier).withValue(o).build();
         if(parent != null) {
-            parent.builder().addChild(leafNode);
+            if(hasValidSchema(nodeIdentifier.getNodeType(), parent)) {
+                parent.builder().addChild(leafNode);
+            }
         } else {
             // If there's no parent node then this is a stand alone LeafNode.
-            this.normalizedNode = leafNode;
+            if(nodePathSchemaNode != null) {
+                this.normalizedNode = leafNode;
+            }
+
             sealed = true;
         }
     }
@@ -90,18 +92,19 @@ public class NormalizedNodePruner implements NormalizedNodeStreamWriter {
     public void leafSetEntryNode(QName name, Object o) throws IOException, IllegalArgumentException {
         checkNotSealed();
 
-        if(!isValidNamespace(name)){
-            return;
-        }
-
         NormalizedNodeBuilderWrapper parent = stack.peek();
         if(parent != null) {
-            parent.builder().addChild(Builders.leafSetEntryBuilder().withValue(o).withNodeIdentifier(
-                    new YangInstanceIdentifier.NodeWithValue<>(parent.nodeType(), o)).build());
+            if(hasValidSchema(name, parent)) {
+                parent.builder().addChild(Builders.leafSetEntryBuilder().withValue(o).withNodeIdentifier(
+                        new YangInstanceIdentifier.NodeWithValue<>(parent.nodeType(), o)).build());
+            }
         } else {
             // If there's no parent LeafSetNode then this is a stand alone LeafSetEntryNode.
-            this.normalizedNode = Builders.leafSetEntryBuilder().withValue(o).withNodeIdentifier(
-                    new YangInstanceIdentifier.NodeWithValue<>(name, o)).build();
+            if(nodePathSchemaNode != null) {
+                this.normalizedNode = Builders.leafSetEntryBuilder().withValue(o).withNodeIdentifier(
+                        new YangInstanceIdentifier.NodeWithValue<>(name, o)).build();
+            }
+
             sealed = true;
         }
     }
@@ -180,18 +183,19 @@ public class NormalizedNodePruner implements NormalizedNodeStreamWriter {
     public void anyxmlNode(YangInstanceIdentifier.NodeIdentifier nodeIdentifier, Object o) throws IOException, IllegalArgumentException {
         checkNotSealed();
 
-        if(!isValidNamespace(nodeIdentifier)){
-            return;
-        }
-
         NormalizedNodeBuilderWrapper parent = stack.peek();
         AnyXmlNode anyXmlNode = Builders.anyXmlBuilder().withNodeIdentifier(nodeIdentifier).
                 withValue((DOMSource) o).build();
         if(parent != null) {
-            parent.builder().addChild(anyXmlNode);
+            if(hasValidSchema(nodeIdentifier.getNodeType(), parent)) {
+                parent.builder().addChild(anyXmlNode);
+            }
         } else {
             // If there's no parent node then this is a stand alone AnyXmlNode.
-            this.normalizedNode = anyXmlNode;
+            if(nodePathSchemaNode != null) {
+                this.normalizedNode = anyXmlNode;
+            }
+
             sealed = true;
         }
     }
@@ -205,12 +209,14 @@ public class NormalizedNodePruner implements NormalizedNodeStreamWriter {
 
         Preconditions.checkState(child != null, "endNode called on an empty stack");
 
-        if(!isValidNamespace(child.identifier())){
+        if(!child.getSchema().isPresent()) {
+            LOG.debug("Schema not found for {}", child.identifier());
             return;
         }
+
         NormalizedNode<?,?> normalizedNode = child.builder().build();
 
-        if(stack.size() > 0){
+        if(stack.size() > 0) {
             NormalizedNodeBuilderWrapper parent = stack.peek();
             parent.builder().addChild(normalizedNode);
         } else {
@@ -237,36 +243,45 @@ public class NormalizedNodePruner implements NormalizedNodeStreamWriter {
         Preconditions.checkState(!sealed, "Pruner can be used only once");
     }
 
-    private boolean isValidNamespace(QName qName){
-        return validNamespaces.contains(qName.getNamespace());
-    }
-
-    private boolean isValidNamespace(YangInstanceIdentifier.AugmentationIdentifier augmentationIdentifier){
-        Set<QName> possibleChildNames = augmentationIdentifier.getPossibleChildNames();
-
-        for(QName qName : possibleChildNames){
-            if(isValidNamespace(qName)){
-                return true;
-            }
+    private boolean hasValidSchema(QName name, NormalizedNodeBuilderWrapper parent) {
+        boolean valid = parent.getSchema().isPresent() && parent.getSchema().get().getChild(name) != null;
+        if(!valid) {
+            LOG.debug("Schema not found for {}", name);
         }
-        return false;
 
+        return valid;
     }
 
-    private boolean isValidNamespace(YangInstanceIdentifier.PathArgument identifier){
-        if(identifier instanceof YangInstanceIdentifier.AugmentationIdentifier){
-            return isValidNamespace((YangInstanceIdentifier.AugmentationIdentifier) identifier);
+    private NormalizedNodeBuilderWrapper addBuilder(NormalizedNodeContainerBuilder<?,?,?,?> builder,
+            PathArgument identifier){
+        final Optional<DataSchemaContextNode<?>> schemaNode;
+        NormalizedNodeBuilderWrapper parent = stack.peek();
+        if(parent == null) {
+            schemaNode = Optional.fromNullable(nodePathSchemaNode);
+        } else if(parent.getSchema().isPresent()) {
+            schemaNode = Optional.fromNullable(parent.getSchema().get().getChild(identifier));
+        } else {
+            schemaNode = Optional.absent();
         }
 
-        return isValidNamespace(identifier.getNodeType());
-    }
-
-    private NormalizedNodeBuilderWrapper addBuilder(NormalizedNodeContainerBuilder<?,?,?,?> builder, YangInstanceIdentifier.PathArgument identifier){
-        NormalizedNodeBuilderWrapper wrapper = new NormalizedNodeBuilderWrapper(builder, identifier);
+        NormalizedNodeBuilderWrapper wrapper = new NormalizedNodeBuilderWrapper(builder, identifier, schemaNode);
         stack.push(wrapper);
         return wrapper;
     }
 
+    private static DataSchemaContextNode<?> findSchemaNodeForNodePath(YangInstanceIdentifier nodePath,
+            SchemaContext schemaContext) {
+        DataSchemaContextNode<?> schemaNode = DataSchemaContextTree.from(schemaContext).getRoot();
+        for(PathArgument arg : nodePath.getPathArguments()) {
+            schemaNode = schemaNode.getChild(arg);
+            if(schemaNode == null) {
+                break;
+            }
+        }
+
+        return schemaNode;
+    }
+
     @VisibleForTesting
     static class SimpleStack<E> {
         List<E> stack = new LinkedList<>();
@@ -294,18 +309,4 @@ public class NormalizedNodePruner implements NormalizedNodeStreamWriter {
             return stack.size();
         }
     }
-
-    @VisibleForTesting
-    SimpleStack<NormalizedNodeBuilderWrapper> stack(){
-        return stack;
-    }
-
-    public static Set<URI> namespaces(SchemaContext schemaContext){
-        Set<URI> namespaces = new HashSet<>(schemaContext.getModules().size());
-        namespaces.add(BASE_NAMESPACE);
-        for(org.opendaylight.yangtools.yang.model.api.Module module : schemaContext.getModules()){
-            namespaces.add(module.getNamespace());
-        }
-        return namespaces;
-    }
 }
index 57fbd99..9d6ff5e 100644 (file)
@@ -9,77 +9,64 @@
 package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.util.concurrent.atomic.AtomicInteger;
 import javax.xml.transform.dom.DOMSource;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.cluster.datastore.node.utils.NormalizedNodeNavigator;
 import org.opendaylight.controller.cluster.datastore.node.utils.NormalizedNodeVisitor;
 import org.opendaylight.controller.cluster.datastore.util.TestModel;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 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.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
 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.CollectionNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class NormalizedNodePrunerTest {
-
-    private NormalizedNodePruner prunerFullSchema;
-
-    private NormalizedNodePruner prunerNoAugSchema;
-
-    @Mock
-    private NormalizedNodeBuilderWrapper normalizedNodeBuilderWrapper;
-
-    @Mock
-    private NormalizedNodeContainerBuilder normalizedNodeContainerBuilder;
-
-    @Mock
-    private NormalizedNode<?,?> normalizedNode;
+    private static final SchemaContext NO_TEST_SCHEMA = TestModel.createTestContextWithoutTestSchema();
+    private static final SchemaContext NO_AUG_SCHEMA = TestModel.createTestContextWithoutAugmentationSchema();
+    private static final SchemaContext FULL_SCHEMA = TestModel.createTestContext();
 
     @Before
     public void setUp(){
         MockitoAnnotations.initMocks(this);
-        prunerFullSchema = new NormalizedNodePruner(TestModel.createTestContext());
-        prunerNoAugSchema = new NormalizedNodePruner(TestModel.createTestContextWithoutAugmentationSchema());
-        doReturn(normalizedNodeContainerBuilder).when(normalizedNodeBuilderWrapper).builder();
-        doReturn(TestModel.BOOLEAN_LEAF_QNAME).when(normalizedNodeBuilderWrapper).nodeType();
-        doReturn(normalizedNode).when(normalizedNodeContainerBuilder).build();
-        doReturn(new YangInstanceIdentifier.NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME)).when(normalizedNodeBuilderWrapper).identifier();
+    }
+
+    private NormalizedNodePruner prunerFullSchema(YangInstanceIdentifier path) {
+        return new NormalizedNodePruner(path, FULL_SCHEMA);
+    }
+
+    private NormalizedNodePruner prunerNoAugSchema(YangInstanceIdentifier path) {
+        return new NormalizedNodePruner(path, NO_AUG_SCHEMA);
+    }
+
+    private NormalizedNodePruner prunerNoTestSchema(YangInstanceIdentifier path) {
+        return new NormalizedNodePruner(path, NO_TEST_SCHEMA);
     }
 
     @Test
     public void testNodesNotPrunedWhenSchemaPresent() throws IOException {
-        NormalizedNodePruner pruner = prunerFullSchema;
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH);
 
         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
 
@@ -95,7 +82,7 @@ public class NormalizedNodePrunerTest {
 
     @Test(expected = IllegalStateException.class)
     public void testReusePruner() throws IOException {
-        NormalizedNodePruner pruner = prunerFullSchema;
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH);
 
         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
 
@@ -113,8 +100,8 @@ public class NormalizedNodePrunerTest {
 
 
     @Test
-    public void testNodesPrunedWhenAugmentationSchemaNotPresent() throws IOException {
-        NormalizedNodePruner pruner = prunerNoAugSchema;
+    public void testNodesPrunedWhenAugmentationSchemaMissing() throws IOException {
+        NormalizedNodePruner pruner = prunerNoAugSchema(TestModel.TEST_PATH);
 
         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
 
@@ -134,8 +121,8 @@ public class NormalizedNodePrunerTest {
     }
 
     @Test
-    public void testNodesPrunedWhenTestSchemaNotPresent() throws IOException {
-        NormalizedNodePruner pruner = new NormalizedNodePruner(TestModel.createTestContextWithoutTestSchema());
+    public void testNodesPrunedWhenTestSchemaMissing() throws IOException {
+        NormalizedNodePruner pruner = prunerNoTestSchema(TestModel.TEST_PATH);
 
         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
 
@@ -153,7 +140,6 @@ public class NormalizedNodePrunerTest {
 
     }
 
-
     private static int countNodes(NormalizedNode<?,?> normalizedNode, final String namespaceFilter){
         if(normalizedNode == null){
             return 0;
@@ -175,213 +161,225 @@ public class NormalizedNodePrunerTest {
     }
 
     @Test
-    public void testLeafNodeHasNoParent() throws IOException {
+    public void testLeafNodeNotPrunedWhenHasNoParent() throws IOException {
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.DESC_QNAME));
         NormalizedNode<?, ?> input = Builders.leafBuilder().withNodeIdentifier(
                 new NodeIdentifier(TestModel.DESC_QNAME)).withValue("test").build();
-        NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
         assertEquals("normalizedNode", input, actual);
     }
 
     @Test
-    public void testLeafNodeHasParent() throws IOException {
+    public void testLeafNodePrunedWhenHasAugmentationParentAndSchemaMissing() throws IOException {
+        AugmentationIdentifier augId = new AugmentationIdentifier(Sets.newHashSet(TestModel.AUG_CONT_QNAME));
+        NormalizedNodePruner pruner = prunerFullSchema(YangInstanceIdentifier.builder().
+                node(TestModel.TEST_QNAME).node(TestModel.AUGMENTED_LIST_QNAME).
+                        node(TestModel.AUGMENTED_LIST_QNAME).node(augId).build());
         LeafNode<Object> child = Builders.leafBuilder().withNodeIdentifier(
-                new NodeIdentifier(TestModel.DESC_QNAME)).withValue("test").build();
-        NormalizedNode<?, ?> input = Builders.containerBuilder().withNodeIdentifier(
-                new NodeIdentifier(TestModel.AUG_CONT_QNAME)).withChild(child).build();
-        NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
+                new NodeIdentifier(TestModel.INVALID_QNAME)).withValue("test").build();
+        NormalizedNode<?, ?> input = Builders.augmentationBuilder().withNodeIdentifier(augId).withChild(child).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
-        assertEquals("normalizedNode", input, actual);
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertEquals("normalizedNode", Builders.augmentationBuilder().withNodeIdentifier(augId).build(), actual);
     }
 
     @Test
-    public void testLeafNodeSchemaMissing() throws IOException {
-        prunerNoAugSchema.stack().push(normalizedNodeBuilderWrapper);
-        prunerNoAugSchema.leafNode(new NodeIdentifier(TestModel.AUG_CONT_QNAME), mock(Object.class));
-        verify(normalizedNodeContainerBuilder, never()).addChild(any(NormalizedNode.class));
+    public void testLeafNodePrunedWhenHasNoParentAndSchemaMissing() throws IOException {
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.INVALID_QNAME));
+        NormalizedNode<?, ?> input = Builders.leafBuilder().withNodeIdentifier(
+                new NodeIdentifier(TestModel.INVALID_QNAME)).withValue("test").build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
+
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertNull(actual);
     }
 
+
     @Test
-    public void testLeafSetEntryNodeHasNoParent() throws IOException {
-        NormalizedNode<?, ?> input = Builders.leafSetEntryBuilder().withValue("test").withNodeIdentifier(
-                new NodeWithValue<>(TestModel.FAMILY_QNAME, "test")).build();
-        NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
+    public void testLeafSetEntryNodeNotPrunedWhenHasNoParent() throws IOException {
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.SHOE_QNAME));
+        NormalizedNode<?, ?> input = Builders.leafSetEntryBuilder().withValue("puma").withNodeIdentifier(
+                new NodeWithValue<>(TestModel.SHOE_QNAME, "puma")).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
         assertEquals("normalizedNode", input, actual);
     }
 
     @Test
-    public void testLeafSetEntryNodeHasParent() throws IOException {
-        LeafSetEntryNode<Object> child = Builders.leafSetEntryBuilder().withValue("test").withNodeIdentifier(
-                new NodeWithValue<>(TestModel.FAMILY_QNAME, "test")).build();
+    public void testLeafSetEntryNodeNotPrunedWhenHasParent() throws IOException {
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.SHOE_QNAME));
+        LeafSetEntryNode<Object> child = Builders.leafSetEntryBuilder().withValue("puma").withNodeIdentifier(
+                new NodeWithValue<>(TestModel.SHOE_QNAME, "puma")).build();
         NormalizedNode<?, ?> input = Builders.leafSetBuilder().withNodeIdentifier(
-                new NodeIdentifier(TestModel.FAMILY_QNAME)).withChild(child).build();
-        NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
+                new NodeIdentifier(TestModel.SHOE_QNAME)).withChild(child).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
         assertEquals("normalizedNode", input, actual);
     }
 
     @Test
-    public void testLeafSetEntryNodeSchemaMissing() throws IOException {
-        doReturn(new NodeIdentifier(TestModel.AUG_CONT_QNAME)).when(normalizedNodeBuilderWrapper).identifier();
-
-        prunerNoAugSchema.stack().push(normalizedNodeBuilderWrapper);
-        prunerNoAugSchema.leafSetEntryNode(TestModel.AUG_CONT_QNAME, "");
+    public void testLeafSetEntryNodePrunedWhenHasNoParentAndSchemaMissing() throws IOException {
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.INVALID_QNAME));
+        NormalizedNode<?, ?> input = Builders.leafSetEntryBuilder().withValue("test").withNodeIdentifier(
+                new NodeWithValue<>(TestModel.INVALID_QNAME, "test")).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        verify(normalizedNodeContainerBuilder, never()).addChild(any(NormalizedNode.class));
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertNull(actual);
     }
 
     @Test
-    public void testAnyXMLNodeHasNoParent() throws IOException {
-        NormalizedNode<?, ?> input = Builders.anyXmlBuilder().withNodeIdentifier(
-                new NodeIdentifier(TestModel.CHILD_NAME_QNAME)).
-                    withValue(mock(DOMSource.class)).build();
-        NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
+    public void testLeafSetEntryNodePrunedWhenHasParentAndSchemaMissing() throws IOException {
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.INVALID_QNAME));
+        LeafSetEntryNode<Object> child = Builders.leafSetEntryBuilder().withValue("test").withNodeIdentifier(
+                new NodeWithValue<>(TestModel.INVALID_QNAME, "test")).build();
+        NormalizedNode<?, ?> input = Builders.leafSetBuilder().withNodeIdentifier(
+                new NodeIdentifier(TestModel.INVALID_QNAME)).withChild(child).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
-        assertEquals("normalizedNode", input, actual);
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertNull(actual);
     }
 
     @Test
-    public void testAnyXMLNodeHasParent() throws IOException {
-        AnyXmlNode child = Builders.anyXmlBuilder().withNodeIdentifier(
-                new NodeIdentifier(TestModel.CHILD_NAME_QNAME)).
-                    withValue(mock(DOMSource.class)).build();
-        NormalizedNode<?, ?> input = Builders.containerBuilder().withNodeIdentifier(
-                new NodeIdentifier(TestModel.AUG_CONT_QNAME)).withChild(child).build();
-        NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
+    public void testAnyXMLNodeNotPrunedWhenHasNoParent() throws IOException {
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.ANY_XML_QNAME));
+        NormalizedNode<?, ?> input = Builders.anyXmlBuilder().withNodeIdentifier(
+                new NodeIdentifier(TestModel.ANY_XML_QNAME)).withValue(mock(DOMSource.class)).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
         assertEquals("normalizedNode", input, actual);
     }
 
-    @Test
-    public void testAnyXmlNodeSchemaMissing() throws IOException {
-        prunerNoAugSchema.stack().push(normalizedNodeBuilderWrapper);
-        prunerNoAugSchema.anyxmlNode(new NodeIdentifier(TestModel.AUG_CONT_QNAME), mock(DOMSource.class));
-
-        verify(normalizedNodeContainerBuilder, never()).addChild(any(NormalizedNode.class));
-    }
-
-
-    @Test
-    public void testLeafSetPushesBuilderToStack() throws IOException {
-        prunerFullSchema.startLeafSet(new NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
-
-        assertEquals(1, prunerFullSchema.stack().size());
-        assertNotNull(prunerFullSchema.stack().peek());
-        assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof ListNodeBuilder);
-    }
 
     @Test
-    public void testStartContainerNodePushesBuilderToStack() throws IOException {
-        prunerFullSchema.startContainerNode(new NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
+    public void testAnyXMLNodeNotPrunedWhenHasParent() throws IOException {
+        NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH);
+        AnyXmlNode child = Builders.anyXmlBuilder().withNodeIdentifier(
+                new NodeIdentifier(TestModel.ANY_XML_QNAME)).withValue(mock(DOMSource.class)).build();
+        NormalizedNode<?, ?> input = Builders.containerBuilder().withNodeIdentifier(
+                new NodeIdentifier(TestModel.TEST_QNAME)).withChild(child).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        assertEquals(1, prunerFullSchema.stack().size());
-        assertNotNull(prunerFullSchema.stack().peek());
-        assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeAttrBuilder);
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertEquals("normalizedNode", input, actual);
     }
 
     @Test
-    public void testStartUnkeyedListPushesBuilderToStack() throws IOException {
-        prunerFullSchema.startUnkeyedList(new NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
+    public void testAnyXmlNodePrunedWhenHasNoParentAndSchemaMissing() throws IOException {
+        NormalizedNodePruner pruner = prunerNoTestSchema(TestModel.TEST_PATH.node(TestModel.ANY_XML_QNAME));
+        NormalizedNode<?, ?> input = Builders.anyXmlBuilder().withNodeIdentifier(
+                new NodeIdentifier(TestModel.ANY_XML_QNAME)).withValue(mock(DOMSource.class)).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        assertEquals(1, prunerFullSchema.stack().size());
-        assertNotNull(prunerFullSchema.stack().peek());
-        assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof CollectionNodeBuilder);
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertNull(actual);
     }
 
     @Test
-    public void testStartUnkeyedListItemPushesBuilderToStack() throws IOException {
-        prunerFullSchema.startUnkeyedListItem(new NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
-
-        assertEquals(1, prunerFullSchema.stack().size());
-        assertNotNull(prunerFullSchema.stack().peek());
-        assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeAttrBuilder);
-    }
+    public void testInnerContainerNodeWithFullPathPathNotPruned() throws IOException {
+        YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
+                node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
+                    node(TestModel.INNER_LIST_QNAME).nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").
+                        node(TestModel.INNER_CONTAINER_QNAME).build();
+        NormalizedNodePruner pruner = prunerFullSchema(path);
 
-    @Test
-    public void testStartMapNodePushesBuilderToStack() throws IOException {
-        prunerFullSchema.startMapNode(new NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
+        NormalizedNode<?, ?> input = ImmutableNodes.containerNode(TestModel.INNER_CONTAINER_QNAME);
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        assertEquals(1, prunerFullSchema.stack().size());
-        assertNotNull(prunerFullSchema.stack().peek());
-        assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof CollectionNodeBuilder);
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertEquals("normalizedNode", input, actual);
     }
 
     @Test
-    public void testStartMapEntryNodePushesBuilderToStack() throws IOException {
-        prunerFullSchema.startMapEntryNode(
-                new NodeIdentifierWithPredicates(TestModel.BOOLEAN_LEAF_QNAME,
-                        ImmutableMap.<QName, Object>of(TestModel.BOOLEAN_LEAF_QNAME, "value")), 10);
-
-        assertEquals(1, prunerFullSchema.stack().size());
-        assertNotNull(prunerFullSchema.stack().peek());
-        assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeAttrBuilder);
-    }
+    public void testInnerContainerNodeWithFullPathPrunedWhenSchemaMissing() throws IOException {
+        YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
+                node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
+                    node(TestModel.INNER_LIST_QNAME).nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").
+                        node(TestModel.INVALID_QNAME).build();
+        NormalizedNodePruner pruner = prunerFullSchema(path);
 
-    @Test
-    public void testStartOrderedMapNodePushesBuilderToStack() throws IOException {
-        prunerFullSchema.startOrderedMapNode(new NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
+        NormalizedNode<?, ?> input = ImmutableNodes.containerNode(TestModel.INVALID_QNAME);
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        assertEquals(1, prunerFullSchema.stack().size());
-        assertNotNull(prunerFullSchema.stack().peek());
-        assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof CollectionNodeBuilder);
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertNull(actual);
     }
 
     @Test
-    public void testStartChoiceNodePushesBuilderToStack() throws IOException {
-        prunerFullSchema.startChoiceNode(new NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
-
-        assertEquals(1, prunerFullSchema.stack().size());
-        assertNotNull(prunerFullSchema.stack().peek());
-        assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeBuilder);
+    public void testInnerContainerNodeWithParentPathPrunedWhenSchemaMissing() throws IOException {
+        YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
+                node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build();
+        NormalizedNodePruner pruner = prunerFullSchema(path);
+
+        MapNode innerList = mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(mapEntryBuilder(
+                TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").withChild(
+                        ImmutableNodes.containerNode(TestModel.INVALID_QNAME)).build()).build();
+        NormalizedNode<?, ?> input = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
+                withChild(innerList).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
+
+        NormalizedNode<?, ?> expected = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
+                withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(mapEntryBuilder(
+                TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").build()).build()).build();
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertEquals("normalizedNode", expected, actual);
     }
 
     @Test
-    public void testStartAugmentationPushesBuilderToStack() throws IOException {
-        prunerFullSchema.startAugmentationNode(new AugmentationIdentifier(ImmutableSet.of(TestModel.AUG_CONT_QNAME)));
+    public void testInnerListNodeWithFullPathNotPruned() throws IOException {
+        YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
+                node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
+                    node(TestModel.INNER_LIST_QNAME).build();
+        NormalizedNodePruner pruner = prunerFullSchema(path);
 
-        assertEquals(1, prunerFullSchema.stack().size());
-        assertNotNull(prunerFullSchema.stack().peek());
-        assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeBuilder);
-    }
+        MapNode input = mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(mapEntryBuilder(
+                TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").withChild(
+                        ImmutableNodes.containerNode(TestModel.INNER_CONTAINER_QNAME)).build()).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-    @Test(expected = IllegalStateException.class)
-    public void testEndNodeWhenNoBuildersOnStack() throws IOException {
-        prunerFullSchema.endNode();
-    }
-
-    @Test
-    public void testEndNodeWhenOneBuildersOnStack() throws IOException {
-        prunerFullSchema.stack().push(normalizedNodeBuilderWrapper);
-        prunerFullSchema.endNode();
-        assertEquals(normalizedNode, prunerFullSchema.normalizedNode());
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertEquals("normalizedNode", input, actual);
     }
 
     @Test
-    public void testEndNodeSchemaMissing() throws IOException {
-        doReturn(new NodeIdentifier(TestModel.AUG_CONT_QNAME)).when(normalizedNodeBuilderWrapper).identifier();
+    public void testInnerListNodeWithFullPathPrunedWhenSchemaMissing() throws IOException {
+        YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
+                node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
+                    node(TestModel.INVALID_QNAME).build();
+        NormalizedNodePruner pruner = prunerFullSchema(path);
 
-        prunerNoAugSchema.stack().push(normalizedNodeBuilderWrapper);
-        prunerNoAugSchema.endNode();
+        MapNode input = mapNodeBuilder(TestModel.INVALID_QNAME).withChild(mapEntryBuilder(
+                TestModel.INVALID_QNAME, TestModel.NAME_QNAME, "one").withChild(
+                        ImmutableNodes.containerNode(TestModel.INNER_CONTAINER_QNAME)).build()).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
 
-        assertEquals(null, prunerNoAugSchema.normalizedNode());
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertNull(actual);
     }
 
     @Test
-    public void testEndNodeWhenMoreThanOneBuilderOnStack() throws IOException {
-        // A little lazy in adding the "parent" builder
-        prunerFullSchema.stack().push(normalizedNodeBuilderWrapper);
-        prunerFullSchema.stack().push(normalizedNodeBuilderWrapper);
-        prunerFullSchema.endNode();
-        assertEquals(null, prunerFullSchema.normalizedNode());
-
-        verify(normalizedNodeContainerBuilder).addChild(any(NormalizedNode.class));
+    public void testInnerListNodeWithParentPathPrunedWhenSchemaMissing() throws IOException {
+        YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
+                node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build();
+        NormalizedNodePruner pruner = prunerFullSchema(path);
+
+        MapNode innerList = mapNodeBuilder(TestModel.INVALID_QNAME).withChild(mapEntryBuilder(
+                TestModel.INVALID_QNAME, TestModel.NAME_QNAME, "one").withChild(
+                        ImmutableNodes.containerNode(TestModel.INNER_CONTAINER_QNAME)).build()).build();
+        NormalizedNode<?, ?> input = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
+                withChild(innerList).build();
+        NormalizedNodeWriter.forStreamWriter(pruner).write(input);
+
+        NormalizedNode<?, ?> expected = mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+        NormalizedNode<?, ?> actual = pruner.normalizedNode();
+        assertEquals("normalizedNode", expected, actual);
     }
 
     private static NormalizedNode<?, ?> createTestContainer() {
@@ -404,10 +402,6 @@ public class NormalizedNodePrunerTest {
                         new NodeIdentifier(TestModel.BINARY_LEAF_LIST_QNAME)).
                         withChild(entry1).withChild(entry2).withChild(entry3).build()).
                 withChild(ImmutableNodes.leafNode(TestModel.SOME_BINARY_DATA_QNAME, new byte[]{1, 2, 3, 4})).
-                withChild(Builders.orderedMapBuilder().
-                        withNodeIdentifier(new NodeIdentifier(TestModel.ORDERED_LIST_QNAME)).
-                        withChild(ImmutableNodes.mapEntry(TestModel.ORDERED_LIST_ENTRY_QNAME,
-                                TestModel.ID_QNAME, 11)).build()).
                 build();
     }
 }
\ No newline at end of file
index 99b8cf8..70bae6f 100644 (file)
@@ -11,7 +11,6 @@ package org.opendaylight.controller.cluster.datastore.util;
 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import java.io.InputStream;
@@ -71,22 +70,17 @@ public class TestModel {
     public static final QName POINTER_QNAME = QName.create(TEST_QNAME, "pointer");
     public static final QName SOME_BINARY_DATA_QNAME = QName.create(TEST_QNAME, "some-binary-data");
     public static final QName BINARY_LEAF_LIST_QNAME = QName.create(TEST_QNAME, "binary_leaf_list");
-    public static final QName SOME_REF_QNAME = QName.create(TEST_QNAME,
-            "some-ref");
-    public static final QName MYIDENTITY_QNAME = QName.create(TEST_QNAME,
-            "myidentity");
-    public static final QName SWITCH_FEATURES_QNAME = QName.create(TEST_QNAME,
-            "switch-features");
+    public static final QName SOME_REF_QNAME = QName.create(TEST_QNAME, "some-ref");
+    public static final QName MYIDENTITY_QNAME = QName.create(TEST_QNAME, "myidentity");
+    public static final QName SWITCH_FEATURES_QNAME = QName.create(TEST_QNAME, "switch-features");
 
     public static final QName AUGMENTED_LIST_QNAME = QName.create(TEST_QNAME, "augmented-list");
     public static final QName AUGMENTED_LIST_ENTRY_QNAME = QName.create(TEST_QNAME, "augmented-list-entry");
 
-    public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME,
-            "outer-list");
-    public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME,
-            "inner-list");
-    public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME,
-            "outer-choice");
+    public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
+    public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
+    public static final QName INNER_CONTAINER_QNAME = QName.create(TEST_QNAME, "inner-container");
+    public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
     public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
     public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
     public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
@@ -96,23 +90,24 @@ public class TestModel {
     public static final QName BIGINTEGER_LEAF_QNAME = QName.create(TEST_QNAME, "biginteger-leaf");
     public static final QName BIGDECIMAL_LEAF_QNAME = QName.create(TEST_QNAME, "bigdecimal-leaf");
     public static final QName ORDERED_LIST_QNAME = QName.create(TEST_QNAME, "ordered-list");
-    public static final QName ORDERED_LIST_ENTRY_QNAME = QName.create(TEST_QNAME, "ordered-list-entry");
+    public static final QName ORDERED_LIST_ENTRY_QNAME = QName.create(TEST_QNAME, "ordered-list-leaf");
     public static final QName UNKEYED_LIST_QNAME = QName.create(TEST_QNAME, "unkeyed-list");
     public static final QName UNKEYED_LIST_ENTRY_QNAME = QName.create(TEST_QNAME, "unkeyed-list-entry");
     public static final QName CHOICE_QNAME = QName.create(TEST_QNAME, "choice");
+    public static final QName SHOE_QNAME = QName.create(TEST_QNAME, "shoe");
+    public static final QName ANY_XML_QNAME = QName.create(TEST_QNAME, "any");
+    public static final QName INVALID_QNAME = QName.create(TEST_QNAME, "invalid");
     private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
-    private static final String DATASTORE_AUG_YANG =
-            "/odl-datastore-augmentation.yang";
-    private static final String DATASTORE_TEST_NOTIFICATION_YANG =
-            "/odl-datastore-test-notification.yang";
+    private static final String DATASTORE_AUG_YANG = "/odl-datastore-augmentation.yang";
+    private static final String DATASTORE_TEST_NOTIFICATION_YANG = "/odl-datastore-test-notification.yang";
 
 
-    public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier
-            .of(TEST_QNAME);
-    public static final YangInstanceIdentifier DESC_PATH = YangInstanceIdentifier
-            .builder(TEST_PATH).node(DESC_QNAME).build();
+    public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
+    public static final YangInstanceIdentifier DESC_PATH = YangInstanceIdentifier.
+            builder(TEST_PATH).node(DESC_QNAME).build();
     public static final YangInstanceIdentifier OUTER_LIST_PATH =
             YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build();
+    public static final QName TWO_THREE_QNAME = QName.create(TEST_QNAME, "two");
     public static final QName TWO_QNAME = QName.create(TEST_QNAME, "two");
     public static final QName THREE_QNAME = QName.create(TEST_QNAME, "three");
 
@@ -126,26 +121,16 @@ public class TestModel {
     private static final Short SHORT_ID = 1;
     private static final Byte BYTE_ID = 1;
     // Family specific constants
-    public static final QName FAMILY_QNAME =
-            QName
-                    .create(
-                            "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:notification-test",
-                            "2014-04-17", "family");
-    public static final QName CHILDREN_QNAME = QName.create(FAMILY_QNAME,
-            "children");
-    public static final QName GRAND_CHILDREN_QNAME = QName.create(FAMILY_QNAME,
-            "grand-children");
-    public static final QName CHILD_NUMBER_QNAME = QName.create(FAMILY_QNAME,
-            "child-number");
-    public static final QName CHILD_NAME_QNAME = QName.create(FAMILY_QNAME,
-            "child-name");
-    public static final QName GRAND_CHILD_NUMBER_QNAME = QName.create(
-            FAMILY_QNAME, "grand-child-number");
-    public static final QName GRAND_CHILD_NAME_QNAME = QName.create(FAMILY_QNAME,
-            "grand-child-name");
-
-    public static final YangInstanceIdentifier FAMILY_PATH =
-            YangInstanceIdentifier.of(FAMILY_QNAME);
+    public static final QName FAMILY_QNAME = QName.create(
+            "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:notification-test", "2014-04-17", "family");
+    public static final QName CHILDREN_QNAME = QName.create(FAMILY_QNAME, "children");
+    public static final QName GRAND_CHILDREN_QNAME = QName.create(FAMILY_QNAME, "grand-children");
+    public static final QName CHILD_NUMBER_QNAME = QName.create(FAMILY_QNAME, "child-number");
+    public static final QName CHILD_NAME_QNAME = QName.create(FAMILY_QNAME, "child-name");
+    public static final QName GRAND_CHILD_NUMBER_QNAME = QName.create(FAMILY_QNAME, "grand-child-number");
+    public static final QName GRAND_CHILD_NAME_QNAME = QName.create(FAMILY_QNAME,"grand-child-name");
+
+    public static final YangInstanceIdentifier FAMILY_PATH = YangInstanceIdentifier.of(FAMILY_QNAME);
     public static final YangInstanceIdentifier FAMILY_DESC_PATH =
             YangInstanceIdentifier.builder(FAMILY_PATH).node(DESC_QNAME).build();
     public static final YangInstanceIdentifier CHILDREN_PATH =
@@ -252,28 +237,14 @@ public class TestModel {
     public static DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> createBaseTestContainerBuilder() {
         // Create a list of shoes
         // This is to test leaf list entry
-        final LeafSetEntryNode<Object> nike =
-                ImmutableLeafSetEntryNodeBuilder
-                        .create()
-                        .withNodeIdentifier(
-                                new NodeWithValue<>(QName.create(
-                                        TEST_QNAME, "shoe"), "nike")).withValue("nike").build();
+        final LeafSetEntryNode<Object> nike = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
+                new NodeWithValue<>(SHOE_QNAME, "nike")).withValue("nike").build();
 
-        final LeafSetEntryNode<Object> puma =
-                ImmutableLeafSetEntryNodeBuilder
-                        .create()
-                        .withNodeIdentifier(
-                                new NodeWithValue<>(QName.create(
-                                        TEST_QNAME, "shoe"), "puma")).withValue("puma").build();
-
-        final LeafSetNode<Object> shoes =
-                ImmutableLeafSetNodeBuilder
-                        .create()
-                        .withNodeIdentifier(
-                                new NodeIdentifier(QName.create(
-                                        TEST_QNAME, "shoe"))).withChild(nike).withChild(puma)
-                        .build();
+        final LeafSetEntryNode<Object> puma = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
+                new NodeWithValue<>(SHOE_QNAME, "puma")).withValue("puma").build();
 
+        final LeafSetNode<Object> shoes = ImmutableLeafSetNodeBuilder.create().withNodeIdentifier(
+                new NodeIdentifier(SHOE_QNAME)).withChild(nike).withChild(puma).build();
 
         // Test a leaf-list where each entry contains an identity
         final LeafSetEntryNode<Object> cap1 =
@@ -331,7 +302,7 @@ public class TestModel {
 
         // Create unkeyed list entry
         UnkeyedListEntryNode unkeyedListEntry =
-                Builders.unkeyedListEntryBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(UNKEYED_LIST_ENTRY_QNAME)).
+                Builders.unkeyedListEntryBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(UNKEYED_LIST_QNAME)).
                         withChild(ImmutableNodes.leafNode(NAME_QNAME, "unkeyed-entry-name")).build();
 
         // Create YangInstanceIdentifier with all path arg types.
@@ -364,12 +335,13 @@ public class TestModel {
                         .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(UNKEYED_LIST_QNAME))
                         .withChild(unkeyedListEntry).build())
                 .withChild(Builders.choiceBuilder()
-                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_QNAME))
-                        .withChild(ImmutableNodes.leafNode(DESC_QNAME, LONG_ID)).build())
+                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TWO_THREE_QNAME))
+                        .withChild(ImmutableNodes.leafNode(TWO_QNAME, "two")).build())
                 .withChild(Builders.orderedMapBuilder().
-                        withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DESC_QNAME)).
-                        withValue(ImmutableList.<MapEntryNode>builder().add(augMapEntry).build()).build())
-                        // .withChild(augmentationNode)
+                        withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ORDERED_LIST_QNAME)).
+                        withValue(ImmutableList.<MapEntryNode>builder().add(
+                                mapEntryBuilder(ORDERED_LIST_QNAME, ORDERED_LIST_ENTRY_QNAME, "1").build(),
+                                mapEntryBuilder(ORDERED_LIST_QNAME, ORDERED_LIST_ENTRY_QNAME, "2").build()).build()).build())
                 .withChild(shoes)
                 .withChild(numbers)
                 .withChild(switchFeatures)
@@ -409,7 +381,7 @@ public class TestModel {
                 .create()
                 .withNodeIdentifier(
                         new YangInstanceIdentifier.NodeIdentifierWithPredicates(
-                                AUGMENTED_LIST_ENTRY_QNAME, ID_QNAME, id))
+                                AUGMENTED_LIST_QNAME, ID_QNAME, id))
                 .withChild(ImmutableNodes.leafNode(ID_QNAME, id))
                 .withChild(augmentationNode).build();
     }
index c720d5e..613288d 100644 (file)
@@ -35,27 +35,67 @@ module odl-datastore-test {
         leaf desc {
             type string;
         }
+
+        leaf short-leaf {
+            type uint16;
+        }
+
+        leaf biginteger-leaf {
+            type uint64;
+        }
+
+        leaf bigdecimal-leaf {
+            type decimal64 {
+                fraction-digits 2;
+            }
+        }
+
+        leaf boolean-leaf {
+            type boolean;
+        }
+
+        leaf byte-leaf {
+            type uint8;
+        }
+
+        list unkeyed-list {
+            leaf name {
+                type string;
+            }
+        }
+
+        choice outer-choice {
+             case one {
+                leaf one {
+                    type string;
+                }
+             }
+             case two-three {
+                 leaf two {
+                     type string;
+                 }
+                 leaf three {
+                     type string;
+                 }
+             }
+        }
+
+        list ordered-list {
+            key ordered-list-leaf;
+            ordered-by user;
+
+            leaf ordered-list-leaf {
+                type string;
+            }
+        }
+
         list outer-list {
             key id;
             leaf id {
                 type uint16;
             }
-            choice outer-choice {
-                case one {
-                    leaf one {
-                        type string;
-                    }
-                }
-                case two-three {
-                    leaf two {
-                        type string;
-                    }
-                    leaf three {
-                        type string;
-                    }
-               }
-           }
-           list inner-list {
+
+            list inner-list {
                 key name;
                 leaf name {
                     type string;
@@ -63,6 +103,9 @@ module odl-datastore-test {
                 leaf value {
                     type string;
                 }
+
+                container inner-container {
+                }
             }
         }
 
@@ -115,6 +158,7 @@ module odl-datastore-test {
             type binary;
         }
 
-
+        anyxml any {
+        }
     }
 }
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/resources/simplelogger.properties b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/simplelogger.properties
new file mode 100644 (file)
index 0000000..1a03523
--- /dev/null
@@ -0,0 +1,6 @@
+org.slf4j.simpleLogger.showDateTime=true
+org.slf4j.simpleLogger.dateTimeFormat=hh:mm:ss,S a
+org.slf4j.simpleLogger.logFile=System.out
+org.slf4j.simpleLogger.showShortLogName=true
+org.slf4j.simpleLogger.levelInBrackets=true
+org.slf4j.simpleLogger.log.org.opendaylight.controller.cluster=debug
index 634121f..776ab27 100644 (file)
@@ -9,10 +9,7 @@ package org.opendaylight.controller.cluster.datastore;
 
 import com.google.common.base.Preconditions;
 import java.io.IOException;
-import java.net.URI;
-import java.util.Set;
 import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
-import org.opendaylight.controller.cluster.datastore.node.utils.transformer.NormalizedNodePruner;
 import org.opendaylight.controller.cluster.datastore.utils.PruningDataTreeModification;
 import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils;
 import org.opendaylight.controller.cluster.raft.RaftActorRecoveryCohort;
@@ -36,11 +33,10 @@ import org.slf4j.Logger;
  * @author Thomas Pantelis
  */
 class ShardRecoveryCoordinator implements RaftActorRecoveryCohort {
-    private static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build();
     private final ShardDataTree store;
     private final String shardName;
     private final Logger log;
-    private final Set<URI> validNamespaces;
+    private final SchemaContext schemaContext;
     private PruningDataTreeModification transaction;
     private int size;
     private final byte[] restoreFromSnapshot;
@@ -51,13 +47,13 @@ class ShardRecoveryCoordinator implements RaftActorRecoveryCohort {
         this.restoreFromSnapshot = restoreFromSnapshot;
         this.shardName = shardName;
         this.log = log;
-        this.validNamespaces = NormalizedNodePruner.namespaces(schemaContext);
+        this.schemaContext = schemaContext;
     }
 
     @Override
     public void startLogRecoveryBatch(int maxBatchSize) {
         log.debug("{}: starting log recovery batch with max size {}", shardName, maxBatchSize);
-        transaction = new PruningDataTreeModification(store.newModification(), validNamespaces);
+        transaction = new PruningDataTreeModification(store.newModification(), store.getDataTree(), schemaContext);
         size = 0;
     }
 
@@ -86,7 +82,7 @@ class ShardRecoveryCoordinator implements RaftActorRecoveryCohort {
     }
 
     private void commitTransaction(PruningDataTreeModification tx) throws DataValidationFailedException {
-        store.commit(tx.getDelegate());
+        store.commit(tx.getResultingModification());
     }
 
     /**
@@ -115,8 +111,9 @@ class ShardRecoveryCoordinator implements RaftActorRecoveryCohort {
         log.debug("{}: Applying recovered snapshot", shardName);
 
         final NormalizedNode<?, ?> node = SerializationUtils.deserializeNormalizedNode(snapshotBytes);
-        final PruningDataTreeModification tx = new PruningDataTreeModification(store.newModification(), validNamespaces);
-        tx.write(ROOT, node);
+        final PruningDataTreeModification tx = new PruningDataTreeModification(store.newModification(),
+                store.getDataTree(), schemaContext);
+        tx.write(YangInstanceIdentifier.EMPTY, node);
         try {
             commitTransaction(tx);
         } catch (DataValidationFailedException e) {
index 4860e38..7f1c6a0 100644 (file)
@@ -85,7 +85,7 @@ final class SimpleShardDataTreeCohort extends ShardDataTreeCohort {
     private DataTreeModification dataTreeModification() {
         DataTreeModification dataTreeModification = transaction;
         if(transaction instanceof PruningDataTreeModification){
-            dataTreeModification = ((PruningDataTreeModification) transaction).getDelegate();
+            dataTreeModification = ((PruningDataTreeModification) transaction).getResultingModification();
         }
         return dataTreeModification;
     }
index d59e564..0e97263 100644 (file)
@@ -10,16 +10,21 @@ package org.opendaylight.controller.cluster.datastore.utils;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import java.io.IOException;
-import java.net.URI;
-import java.util.Set;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import javax.annotation.Nonnull;
 import org.opendaylight.controller.cluster.datastore.node.utils.transformer.NormalizedNodePruner;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor;
 import org.opendaylight.yangtools.yang.data.impl.schema.tree.SchemaValidationFailedException;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -30,12 +35,14 @@ import org.slf4j.LoggerFactory;
 public class PruningDataTreeModification implements DataTreeModification {
 
     private static final Logger LOG = LoggerFactory.getLogger(PruningDataTreeModification.class);
-    private final DataTreeModification delegate;
-    private final Set<URI> validNamespaces;
+    private DataTreeModification delegate;
+    private final SchemaContext schemaContext;
+    private final DataTree dataTree;
 
-    public PruningDataTreeModification(DataTreeModification delegate, Set<URI> validNamespaces) {
+    public PruningDataTreeModification(DataTreeModification delegate, DataTree dataTree, SchemaContext schemaContext) {
         this.delegate = delegate;
-        this.validNamespaces = validNamespaces;
+        this.dataTree = dataTree;
+        this.schemaContext = schemaContext;
     }
 
     @Override
@@ -56,10 +63,8 @@ public class PruningDataTreeModification implements DataTreeModification {
                 delegate.merge(yangInstanceIdentifier, normalizedNode);
             }
         } catch (SchemaValidationFailedException e){
-            if(!isValidYangInstanceIdentifier(yangInstanceIdentifier)){
-                LOG.warn("Invalid node identifier {} ignoring merge", yangInstanceIdentifier);
-                return;
-            }
+            LOG.warn("Node at path {} was pruned during merge due to validation error: {}",
+                    yangInstanceIdentifier, e.getMessage());
 
             pruneAndMergeNode(yangInstanceIdentifier, normalizedNode);
         }
@@ -67,9 +72,7 @@ public class PruningDataTreeModification implements DataTreeModification {
     }
 
     private void pruneAndMergeNode(YangInstanceIdentifier yangInstanceIdentifier, NormalizedNode<?, ?> normalizedNode) {
-        LOG.warn("Node at path : {} was pruned during merge", yangInstanceIdentifier);
-
-        NormalizedNode<?,?> pruned = pruneNormalizedNode(normalizedNode);
+        NormalizedNode<?,?> pruned = pruneNormalizedNode(yangInstanceIdentifier, normalizedNode);
 
         if(pruned != null) {
             delegate.merge(yangInstanceIdentifier, pruned);
@@ -85,19 +88,15 @@ public class PruningDataTreeModification implements DataTreeModification {
                 delegate.write(yangInstanceIdentifier, normalizedNode);
             }
         } catch (SchemaValidationFailedException e){
-            if(!isValidYangInstanceIdentifier(yangInstanceIdentifier)){
-                LOG.warn("Invalid node identifier {} ignoring write", yangInstanceIdentifier);
-                return;
-            }
+            LOG.warn("Node at path : {} was pruned during write due to validation error: {}",
+                    yangInstanceIdentifier, e.getMessage());
 
             pruneAndWriteNode(yangInstanceIdentifier, normalizedNode);
         }
     }
 
     private void pruneAndWriteNode(YangInstanceIdentifier yangInstanceIdentifier, NormalizedNode<?, ?> normalizedNode) {
-        LOG.warn("Node at path : {} was pruned during write", yangInstanceIdentifier);
-
-        NormalizedNode<?,?> pruned = pruneNormalizedNode(normalizedNode);
+        NormalizedNode<?,?> pruned = pruneNormalizedNode(yangInstanceIdentifier, normalizedNode);
 
         if(pruned != null) {
             delegate.write(yangInstanceIdentifier, pruned);
@@ -106,7 +105,15 @@ public class PruningDataTreeModification implements DataTreeModification {
 
     @Override
     public void ready() {
-        delegate.ready();
+        try {
+            delegate.ready();
+        } catch (SchemaValidationFailedException e) {
+            DataTreeModification newModification = dataTree.takeSnapshot().newModification();
+            delegate.applyToCursor(new PruningDataTreeModificationCursor(newModification, this));
+
+            delegate = newModification;
+            delegate.ready();
+        }
     }
 
     @Override
@@ -121,12 +128,12 @@ public class PruningDataTreeModification implements DataTreeModification {
 
     @Override
     public DataTreeModification newModification() {
-        return new PruningDataTreeModification(delegate.newModification(), validNamespaces);
+        return new PruningDataTreeModification(delegate.newModification(), dataTree, schemaContext);
     }
 
     @VisibleForTesting
-    NormalizedNode<?, ?> pruneNormalizedNode(NormalizedNode<?,?> input){
-        NormalizedNodePruner pruner = new NormalizedNodePruner(validNamespaces);
+    NormalizedNode<?, ?> pruneNormalizedNode(YangInstanceIdentifier path, NormalizedNode<?,?> input) {
+        NormalizedNodePruner pruner = new NormalizedNodePruner(path, schemaContext);
         try {
             NormalizedNodeWriter.forStreamWriter(pruner).write(input);
         } catch (IOException ioe) {
@@ -136,18 +143,88 @@ public class PruningDataTreeModification implements DataTreeModification {
         return pruner.normalizedNode();
     }
 
-    public DataTreeModification getDelegate(){
+    public DataTreeModification getResultingModification(){
         return delegate;
     }
 
-    private boolean isValidYangInstanceIdentifier(YangInstanceIdentifier instanceIdentifier){
-        for(YangInstanceIdentifier.PathArgument pathArgument : instanceIdentifier.getPathArguments()){
-            if(!validNamespaces.contains(pathArgument.getNodeType().getNamespace())){
-                return false;
+    private static class PruningDataTreeModificationCursor implements DataTreeModificationCursor {
+        private final Deque<YangInstanceIdentifier> stack = new ArrayDeque<>();
+        private final DataTreeModification toModification;
+        private final PruningDataTreeModification pruningModification;
+
+        PruningDataTreeModificationCursor(DataTreeModification toModification,
+                PruningDataTreeModification pruningModification) {
+            this.toModification = toModification;
+            this.pruningModification = pruningModification;
+            stack.push(YangInstanceIdentifier.EMPTY);
+        }
+
+        @Override
+        public void write(PathArgument child, NormalizedNode<?, ?> data) {
+            YangInstanceIdentifier path = stack.peek().node(child);
+            NormalizedNode<?, ?> prunedNode = pruningModification.pruneNormalizedNode(path, data);
+            if(prunedNode != null) {
+                toModification.write(path, prunedNode);
             }
         }
 
-        return true;
-    }
+        @Override
+        public void merge(PathArgument child, NormalizedNode<?, ?> data) {
+            YangInstanceIdentifier path = stack.peek().node(child);
+            NormalizedNode<?, ?> prunedNode = pruningModification.pruneNormalizedNode(path, data);
+            if(prunedNode != null) {
+                toModification.merge(path, prunedNode);
+            }
+        }
 
+        @Override
+        public void delete(PathArgument child) {
+            try {
+                toModification.delete(stack.peek().node(child));
+            } catch(SchemaValidationFailedException e) {
+                // Ignoring since we would've already logged this in the call to the original modification.
+            }
+        }
+
+        @Override
+        public void enter(@Nonnull final PathArgument child) {
+            stack.push(stack.peek().node(child));
+        }
+
+        @Override
+        public void enter(@Nonnull final PathArgument... path) {
+            for (PathArgument arg : path) {
+                enter(arg);
+            }
+        }
+
+        @Override
+        public void enter(@Nonnull final Iterable<PathArgument> path) {
+            for (PathArgument arg : path) {
+                enter(arg);
+            }
+        }
+
+        @Override
+        public void exit() {
+            stack.pop();
+        }
+
+        @Override
+        public void exit(final int depth) {
+            Preconditions.checkArgument(depth < stack.size(), "Stack holds only %s elements, cannot exit %s levels", stack.size(), depth);
+            for (int i = 0; i < depth; ++i) {
+                stack.pop();
+            }
+        }
+
+        @Override
+        public Optional<NormalizedNode<?, ?>> readNode(@Nonnull final PathArgument child) {
+            throw new UnsupportedOperationException("Not implemented");
+        }
+
+        @Override
+        public void close() {
+        }
+    }
 }
index 8f2eb89..163c15b 100644 (file)
@@ -447,7 +447,7 @@ public abstract class AbstractShardTest extends AbstractActorTest{
         final DataTreeCandidateNode mockCandidateNode = mock(DataTreeCandidateNode.class, name + "-node");
         doReturn(ModificationType.WRITE).when(mockCandidateNode).getModificationType();
         doReturn(Optional.of(ImmutableNodes.containerNode(CarsModel.CARS_QNAME))).when(mockCandidateNode).getDataAfter();
-        doReturn(YangInstanceIdentifier.builder().build()).when(mockCandidate).getRootPath();
+        doReturn(CarsModel.BASE_PATH).when(mockCandidate).getRootPath();
         doReturn(mockCandidateNode).when(mockCandidate).getRootNode();
         return mockCandidate;
     }
index c5cd463..9fd33b2 100644 (file)
@@ -977,8 +977,12 @@ public class ShardTest extends AbstractShardTest {
     }
 
     @Test
-    public void testReadyWithImmediateCommit() throws Exception{
+    public void testReadyWithReadWriteImmediateCommit() throws Exception{
         testReadyWithImmediateCommit(true);
+    }
+
+    @Test
+    public void testReadyWithWriteOnlyImmediateCommit() throws Exception{
         testReadyWithImmediateCommit(false);
     }
 
@@ -1089,9 +1093,13 @@ public class ShardTest extends AbstractShardTest {
     }
 
     @Test
-    public void testCommitWithPersistenceDisabled() throws Throwable {
+    public void testReadWriteCommitWithPersistenceDisabled() throws Throwable {
+        testCommitWithPersistenceDisabled(true);
+    }
+
+    @Test
+    public void testWriteOnlyCommitWithPersistenceDisabled() throws Throwable {
         testCommitWithPersistenceDisabled(true);
-        testCommitWithPersistenceDisabled(false);
     }
 
     private void testCommitWithPersistenceDisabled(final boolean readWrite) throws Throwable {
@@ -1141,8 +1149,12 @@ public class ShardTest extends AbstractShardTest {
     }
 
     @Test
-    public void testCommitWhenTransactionHasNoModifications() {
+    public void testReadWriteCommitWhenTransactionHasNoModifications() {
         testCommitWhenTransactionHasNoModifications(true);
+    }
+
+    @Test
+    public void testWriteOnlyCommitWhenTransactionHasNoModifications() {
         testCommitWhenTransactionHasNoModifications(false);
     }
 
@@ -1199,8 +1211,12 @@ public class ShardTest extends AbstractShardTest {
     }
 
     @Test
-    public void testCommitWhenTransactionHasModifications() {
+    public void testReadWriteCommitWhenTransactionHasModifications() {
         testCommitWhenTransactionHasModifications(true);
+    }
+
+    @Test
+    public void testWriteOnlyCommitWhenTransactionHasModifications() {
         testCommitWhenTransactionHasModifications(false);
     }
 
index 1946057..5e7c1ca 100644 (file)
 
 package org.opendaylight.controller.cluster.datastore.utils;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import java.net.URI;
-import java.util.Set;
+import static org.opendaylight.controller.md.cluster.datastore.model.CompositeModel.AUG_CONTAINER;
+import static org.opendaylight.controller.md.cluster.datastore.model.CompositeModel.AUG_INNER_CONTAINER;
+import static org.opendaylight.controller.md.cluster.datastore.model.CompositeModel.AUG_QNAME;
+import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.NAME_QNAME;
+import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.TEST_QNAME;
+import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.innerNode;
+import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.outerNode;
+import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.outerNodeEntry;
+import com.google.common.base.Optional;
+import com.google.common.reflect.Reflection;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.cluster.datastore.ShardDataTree;
 import org.opendaylight.controller.md.cluster.datastore.model.CarsModel;
+import org.opendaylight.controller.md.cluster.datastore.model.PeopleModel;
+import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.yangtools.yang.common.QName;
 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.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
 import org.opendaylight.yangtools.yang.data.impl.schema.tree.SchemaValidationFailedException;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class PruningDataTreeModificationTest {
+    static final SchemaContext SCHEMA_CONTEXT = SchemaContextHelper.select(SchemaContextHelper.CARS_YANG,
+            SchemaContextHelper.ODL_DATASTORE_TEST_YANG);
 
-    @Mock
-    DataTreeModification delegate;
-
-    @Mock
-    Set<URI> validNamespaces;
+    static final QName INVALID_TEST_QNAME = QName.create(TestModel.TEST_QNAME, "invalid");
+    static final YangInstanceIdentifier INVALID_TEST_PATH = YangInstanceIdentifier.of(INVALID_TEST_QNAME);
 
     @Mock
-    NormalizedNode<?,?> prunedNormalizedNode;
+    private DataTreeModification mockModification;
 
-    PruningDataTreeModification pruningDataTreeModification;
+    private TipProducingDataTree dataTree;
+    private DataTreeModification realModification;
+    private DataTreeModification proxyModification;
+    private PruningDataTreeModification pruningDataTreeModification;
 
     @Before
     public void setUp(){
         MockitoAnnotations.initMocks(this);
-        pruningDataTreeModification = new PruningDataTreeModification(delegate, validNamespaces) {
+
+        dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        dataTree.setSchemaContext(SCHEMA_CONTEXT);
+
+        realModification = dataTree.takeSnapshot().newModification();
+        proxyModification = Reflection.newProxy(DataTreeModification.class, new InvocationHandler() {
             @Override
-            NormalizedNode<?, ?> pruneNormalizedNode(NormalizedNode<?, ?> input) {
-                return prunedNormalizedNode;
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                try {
+                    method.invoke(mockModification, args);
+                    return method.invoke(realModification, args);
+                } catch (InvocationTargetException e) {
+                    throw e.getCause();
+                }
             }
-        };
+        });
+
+        pruningDataTreeModification = new PruningDataTreeModification(proxyModification, dataTree, SCHEMA_CONTEXT);
     }
 
     @Test
     public void testDelete(){
         pruningDataTreeModification.delete(CarsModel.BASE_PATH);
 
-        verify(delegate).delete(CarsModel.BASE_PATH);
+        verify(mockModification, times(1)).delete(CarsModel.BASE_PATH);
     }
 
     @Test
     public void testDeleteOnException(){
         YangInstanceIdentifier path = CarsModel.BASE_PATH;
-        doThrow(SchemaValidationFailedException.class).when(delegate).delete(path);
+        doThrow(SchemaValidationFailedException.class).when(mockModification).delete(path);
 
         pruningDataTreeModification.delete(path);
 
-        verify(delegate, times(1)).delete(path);
+        verify(mockModification, times(1)).delete(path);
     }
 
 
@@ -76,21 +116,58 @@ public class PruningDataTreeModificationTest {
         YangInstanceIdentifier path = CarsModel.BASE_PATH;
         pruningDataTreeModification.merge(path, normalizedNode);
 
-        verify(delegate, times(1)).merge(path, normalizedNode);
+        verify(mockModification, times(1)).merge(path, normalizedNode);
     }
 
     @Test
-    public void testMergeOnException(){
-        NormalizedNode<?, ?> normalizedNode = CarsModel.create();
-        YangInstanceIdentifier path = CarsModel.BASE_PATH;
+    public void testMergeWithInvalidNamespace() throws DataValidationFailedException{
+        NormalizedNode<?, ?> normalizedNode = PeopleModel.emptyContainer();
+        YangInstanceIdentifier path = PeopleModel.BASE_PATH;
+
+        pruningDataTreeModification.merge(path, normalizedNode);
+
+        verify(mockModification, times(1)).merge(path, normalizedNode);
 
-        doThrow(SchemaValidationFailedException.class).when(delegate).merge(path, normalizedNode);
-        doReturn(true).when(validNamespaces).contains(any(YangInstanceIdentifier.PathArgument.class));
+        DataTreeCandidateTip candidate = getCandidate();
+        assertEquals("getModificationType", ModificationType.UNMODIFIED, candidate.getRootNode().getModificationType());
+    }
+
+    @Test
+    public void testMergeWithInvalidChildNodeNames() throws DataValidationFailedException{
+        ContainerNode augContainer = ImmutableContainerNodeBuilder.create().withNodeIdentifier(
+                new YangInstanceIdentifier.NodeIdentifier(AUG_CONTAINER)).withChild(
+                        ImmutableNodes.containerNode(AUG_INNER_CONTAINER)).build();
+
+        DataContainerChild<?, ?> outerNode = outerNode(outerNodeEntry(1, innerNode("one", "two")));
+        ContainerNode normalizedNode = ImmutableContainerNodeBuilder.create().withNodeIdentifier(
+                new YangInstanceIdentifier.NodeIdentifier(TEST_QNAME)).withChild(outerNode).withChild(augContainer).
+                            withChild(ImmutableNodes.leafNode(AUG_QNAME, "aug")).build();
+
+        YangInstanceIdentifier path = TestModel.TEST_PATH;
 
         pruningDataTreeModification.merge(path, normalizedNode);
 
-        verify(delegate, times(1)).merge(path, normalizedNode);
-        verify(delegate, times(1)).merge(path, prunedNormalizedNode);
+        dataTree.commit(getCandidate());
+
+        ContainerNode prunedNode = ImmutableContainerNodeBuilder.create().withNodeIdentifier(
+                new YangInstanceIdentifier.NodeIdentifier(TEST_QNAME)).withChild(outerNode).build();
+
+        Optional<NormalizedNode<?, ?>> actual = dataTree.takeSnapshot().readNode(path);
+        assertEquals("After pruning present", true, actual.isPresent());
+        assertEquals("After pruning", prunedNode, actual.get());
+    }
+
+    @Test
+    public void testMergeWithValidNamespaceAndInvalidNodeName() throws DataValidationFailedException{
+        NormalizedNode<?, ?> normalizedNode = ImmutableNodes.containerNode(INVALID_TEST_QNAME);
+        YangInstanceIdentifier path = INVALID_TEST_PATH;
+
+        pruningDataTreeModification.merge(path, normalizedNode);
+
+        verify(mockModification, times(1)).merge(path, normalizedNode);
+
+        DataTreeCandidateTip candidate = getCandidate();
+        assertEquals("getModificationType", ModificationType.UNMODIFIED, candidate.getRootNode().getModificationType());
     }
 
     @Test
@@ -99,28 +176,89 @@ public class PruningDataTreeModificationTest {
         YangInstanceIdentifier path = CarsModel.BASE_PATH;
         pruningDataTreeModification.write(path, normalizedNode);
 
-        verify(delegate, times(1)).write(path, normalizedNode);
+        verify(mockModification, times(1)).write(path, normalizedNode);
     }
 
     @Test
-    public void testWriteOnException(){
-        NormalizedNode<?, ?> normalizedNode = CarsModel.create();
-        YangInstanceIdentifier path = CarsModel.BASE_PATH;
+    public void testWriteRootNode() throws Exception{
+        ShardDataTree shardDataTree = new ShardDataTree(SCHEMA_CONTEXT, TreeType.CONFIGURATION);
+        DataTreeModification mod = shardDataTree.newModification();
 
-        doThrow(SchemaValidationFailedException.class).when(delegate).write(path, normalizedNode);
-        doReturn(true).when(validNamespaces).contains(any(YangInstanceIdentifier.PathArgument.class));
+        mod.write(CarsModel.BASE_PATH, CarsModel.create());
+        shardDataTree.commit(mod);
+
+        NormalizedNode<?, ?> normalizedNode = shardDataTree.readNode(YangInstanceIdentifier.EMPTY).get();
+        pruningDataTreeModification.write(YangInstanceIdentifier.EMPTY, normalizedNode);
+        dataTree.commit(getCandidate());
+
+        Optional<NormalizedNode<?, ?>> actual = dataTree.takeSnapshot().readNode(YangInstanceIdentifier.EMPTY);
+        assertEquals("Root present", true, actual.isPresent());
+        assertEquals("Root node", normalizedNode, actual.get());
+
+    }
+
+    @Test
+    public void testWriteRootNodeWithInvalidChild() throws Exception{
+        ShardDataTree shardDataTree = new ShardDataTree(SCHEMA_CONTEXT, TreeType.CONFIGURATION);
+        NormalizedNode<?, ?> root = shardDataTree.readNode(YangInstanceIdentifier.EMPTY).get();
+
+        NormalizedNode<?, ?> normalizedNode = ImmutableContainerNodeBuilder.create().withNodeIdentifier(
+                new YangInstanceIdentifier.NodeIdentifier(root.getNodeType())).withChild(
+                        ImmutableNodes.containerNode(AUG_CONTAINER)).build();
+        pruningDataTreeModification.write(YangInstanceIdentifier.EMPTY, normalizedNode);
+        dataTree.commit(getCandidate());
+
+        Optional<NormalizedNode<?, ?>> actual = dataTree.takeSnapshot().readNode(YangInstanceIdentifier.EMPTY);
+        assertEquals("Root present", true, actual.isPresent());
+        assertEquals("Root node", root, actual.get());
+
+    }
+
+    @Test
+    public void testWriteWithInvalidNamespace() throws DataValidationFailedException{
+        NormalizedNode<?, ?> normalizedNode = PeopleModel.emptyContainer();
+        YangInstanceIdentifier path = PeopleModel.BASE_PATH;
+
+        pruningDataTreeModification.write(path, normalizedNode);
+
+        verify(mockModification, times(1)).write(path, normalizedNode);
+
+        DataTreeCandidateTip candidate = getCandidate();
+        assertEquals("getModificationType", ModificationType.UNMODIFIED, candidate.getRootNode().getModificationType());
+    }
+
+    @Test
+    public void testWriteWithInvalidChildNodeNames() throws DataValidationFailedException{
+        ContainerNode augContainer = ImmutableContainerNodeBuilder.create().withNodeIdentifier(
+                new YangInstanceIdentifier.NodeIdentifier(AUG_CONTAINER)).withChild(
+                        ImmutableNodes.containerNode(AUG_INNER_CONTAINER)).build();
+
+        DataContainerChild<?, ?> outerNode = outerNode(outerNodeEntry(1, innerNode("one", "two")));
+        ContainerNode normalizedNode = ImmutableContainerNodeBuilder.create().withNodeIdentifier(
+                new YangInstanceIdentifier.NodeIdentifier(TEST_QNAME)).withChild(outerNode).withChild(augContainer).
+                            withChild(ImmutableNodes.leafNode(AUG_QNAME, "aug")).
+                                withChild(ImmutableNodes.leafNode(NAME_QNAME, "name")).build();
+
+        YangInstanceIdentifier path = TestModel.TEST_PATH;
 
         pruningDataTreeModification.write(path, normalizedNode);
 
-        verify(delegate, times(1)).write(path, normalizedNode);
-        verify(delegate, times(1)).write(path, prunedNormalizedNode);
+        dataTree.commit(getCandidate());
+
+        ContainerNode prunedNode = ImmutableContainerNodeBuilder.create().withNodeIdentifier(
+                new YangInstanceIdentifier.NodeIdentifier(TEST_QNAME)).withChild(outerNode).
+                        withChild(ImmutableNodes.leafNode(NAME_QNAME, "name")).build();
+
+        Optional<NormalizedNode<?, ?>> actual = dataTree.takeSnapshot().readNode(path);
+        assertEquals("After pruning present", true, actual.isPresent());
+        assertEquals("After pruning", prunedNode, actual.get());
     }
 
     @Test
     public void testReady(){
         pruningDataTreeModification.ready();
 
-        verify(delegate).ready();
+        verify(mockModification).ready();
     }
 
     @Test
@@ -128,20 +266,30 @@ public class PruningDataTreeModificationTest {
         DataTreeModificationCursor dataTreeModificationCursor = mock(DataTreeModificationCursor.class);
         pruningDataTreeModification.applyToCursor(dataTreeModificationCursor);
 
-        verify(delegate).applyToCursor(dataTreeModificationCursor);
+        verify(mockModification).applyToCursor(dataTreeModificationCursor);
     }
 
     @Test
     public void testReadNode(){
         pruningDataTreeModification.readNode(CarsModel.BASE_PATH);
 
-        verify(delegate).readNode(CarsModel.BASE_PATH);
+        verify(mockModification).readNode(CarsModel.BASE_PATH);
     }
 
     @Test
     public void testNewModification(){
+        realModification.ready();
         DataTreeModification dataTreeModification = pruningDataTreeModification.newModification();
 
         assertTrue("new modification not of type PruningDataTreeModification", dataTreeModification instanceof PruningDataTreeModification);
     }
+
+    private DataTreeCandidateTip getCandidate() throws DataValidationFailedException {
+        pruningDataTreeModification.ready();
+        DataTreeModification mod = pruningDataTreeModification.getResultingModification();
+        mod = mod == proxyModification ? realModification : mod;
+        dataTree.validate(mod);
+        DataTreeCandidateTip candidate = dataTree.prepare(mod);
+        return candidate;
+    }
 }
\ No newline at end of file
index 9f1f509..0757c0c 100644 (file)
@@ -8,6 +8,14 @@
 
 package org.opendaylight.controller.md.cluster.datastore.model;
 
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
@@ -29,16 +37,6 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
-import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
-import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
-
 public class CompositeModel {
 
   public static final QName TEST_QNAME = QName.create(
@@ -49,6 +47,8 @@ public class CompositeModel {
       "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:aug",
       "2014-03-13", "name");
 
+  public static final QName AUG_CONTAINER = QName.create(AUG_QNAME, "aug-container");
+  public static final QName AUG_INNER_CONTAINER = QName.create(AUG_QNAME, "aug-inner-container");
   public static final QName DESC_QNAME = QName.create(TEST_QNAME, "desc");
   public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME,
       "outer-list");
index 77d74c4..d833304 100644 (file)
@@ -16,4 +16,10 @@ module odl-datastore-augmentation {
         }
     }
 
+    augment "/test:test" {
+        container aug-container {
+            container aug-inner-container {
+            }
+        }
+    }
 }
\ No newline at end of file

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.