From 561058be77594a026abbdbe82616fc65c7f58f48 Mon Sep 17 00:00:00 2001 From: Tom Pantelis Date: Tue, 8 Mar 2016 23:07:02 -0500 Subject: [PATCH] Bug 5485: Improve DataTreeModification pruning on recovery 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 --- .../NormalizedNodeBuilderWrapper.java | 11 +- .../transformer/NormalizedNodePruner.java | 131 +++---- .../transformer/NormalizedNodePrunerTest.java | 356 +++++++++--------- .../cluster/datastore/util/TestModel.java | 110 ++---- .../test/resources/odl-datastore-test.yang | 78 +++- .../test/resources/simplelogger.properties | 6 + .../datastore/ShardRecoveryCoordinator.java | 17 +- .../datastore/SimpleShardDataTreeCohort.java | 2 +- .../utils/PruningDataTreeModification.java | 139 +++++-- .../cluster/datastore/AbstractShardTest.java | 2 +- .../cluster/datastore/ShardTest.java | 26 +- .../PruningDataTreeModificationTest.java | 222 +++++++++-- .../datastore/model/CompositeModel.java | 20 +- .../resources/odl-datastore-augmentation.yang | 6 + 14 files changed, 697 insertions(+), 429 deletions(-) create mode 100644 opendaylight/md-sal/sal-clustering-commons/src/test/resources/simplelogger.properties diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodeBuilderWrapper.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodeBuilderWrapper.java index 5295c88153..259529eaa9 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodeBuilderWrapper.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodeBuilderWrapper.java @@ -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> schemaNode; - - NormalizedNodeBuilderWrapper(NormalizedNodeContainerBuilder builder, YangInstanceIdentifier.PathArgument identifier) { + NormalizedNodeBuilderWrapper(NormalizedNodeContainerBuilder builder, + YangInstanceIdentifier.PathArgument identifier, Optional> schemaNode) { this.builder = builder; this.identifier = identifier; + this.schemaNode = schemaNode; } public NormalizedNodeContainerBuilder builder(){ @@ -34,4 +38,7 @@ public class NormalizedNodeBuilderWrapper { return identifier; } + public Optional> getSchema() { + return schemaNode; + } } diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java index 27504f37f1..03fb6412b3 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java @@ -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 stack = new SimpleStack<>(); private NormalizedNode normalizedNode; - private final Set validNamespaces; + private final DataSchemaContextNode nodePathSchemaNode; private boolean sealed = false; - public NormalizedNodePruner(SchemaContext schemaContext) { - this(NormalizedNodePruner.namespaces(schemaContext)); - } - - public NormalizedNodePruner(Set 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 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 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> 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 { List stack = new LinkedList<>(); @@ -294,18 +309,4 @@ public class NormalizedNodePruner implements NormalizedNodeStreamWriter { return stack.size(); } } - - @VisibleForTesting - SimpleStack stack(){ - return stack; - } - - public static Set namespaces(SchemaContext schemaContext){ - Set 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; - } } diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePrunerTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePrunerTest.java index 57fbd99bcd..9d6ff5eb98 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePrunerTest.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePrunerTest.java @@ -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 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 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 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 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.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 diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/util/TestModel.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/util/TestModel.java index 99b8cf8ff5..70bae6fcb3 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/util/TestModel.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/util/TestModel.java @@ -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 createBaseTestContainerBuilder() { // Create a list of shoes // This is to test leaf list entry - final LeafSetEntryNode nike = - ImmutableLeafSetEntryNodeBuilder - .create() - .withNodeIdentifier( - new NodeWithValue<>(QName.create( - TEST_QNAME, "shoe"), "nike")).withValue("nike").build(); + final LeafSetEntryNode nike = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier( + new NodeWithValue<>(SHOE_QNAME, "nike")).withValue("nike").build(); - final LeafSetEntryNode puma = - ImmutableLeafSetEntryNodeBuilder - .create() - .withNodeIdentifier( - new NodeWithValue<>(QName.create( - TEST_QNAME, "shoe"), "puma")).withValue("puma").build(); - - final LeafSetNode shoes = - ImmutableLeafSetNodeBuilder - .create() - .withNodeIdentifier( - new NodeIdentifier(QName.create( - TEST_QNAME, "shoe"))).withChild(nike).withChild(puma) - .build(); + final LeafSetEntryNode puma = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier( + new NodeWithValue<>(SHOE_QNAME, "puma")).withValue("puma").build(); + final LeafSetNode 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 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.builder().add(augMapEntry).build()).build()) - // .withChild(augmentationNode) + withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ORDERED_LIST_QNAME)). + withValue(ImmutableList.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(); } diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/resources/odl-datastore-test.yang b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/odl-datastore-test.yang index c720d5e0cd..613288dadd 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/test/resources/odl-datastore-test.yang +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/odl-datastore-test.yang @@ -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 index 0000000000..1a03523b6e --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/simplelogger.properties @@ -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 diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardRecoveryCoordinator.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardRecoveryCoordinator.java index 634121f5e5..776ab276c6 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardRecoveryCoordinator.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardRecoveryCoordinator.java @@ -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 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) { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/SimpleShardDataTreeCohort.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/SimpleShardDataTreeCohort.java index 4860e38e8f..7f1c6a0cb2 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/SimpleShardDataTreeCohort.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/SimpleShardDataTreeCohort.java @@ -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; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModification.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModification.java index d59e564345..0e9726373a 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModification.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModification.java @@ -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 validNamespaces; + private DataTreeModification delegate; + private final SchemaContext schemaContext; + private final DataTree dataTree; - public PruningDataTreeModification(DataTreeModification delegate, Set 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 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 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> readNode(@Nonnull final PathArgument child) { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public void close() { + } + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractShardTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractShardTest.java index 8f2eb89dfc..163c15baf6 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractShardTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/AbstractShardTest.java @@ -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; } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java index c5cd4635c3..9fd33b217d 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java @@ -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); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModificationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModificationTest.java index 1946057668..5e7c1cadff 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModificationTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/PruningDataTreeModificationTest.java @@ -8,65 +8,105 @@ 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 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> 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> 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> 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> 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 diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CompositeModel.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CompositeModel.java index 9f1f50973c..0757c0c17f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CompositeModel.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CompositeModel.java @@ -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"); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/odl-datastore-augmentation.yang b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/odl-datastore-augmentation.yang index 77d74c47d3..d8333040a2 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/odl-datastore-augmentation.yang +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/odl-datastore-augmentation.yang @@ -16,4 +16,10 @@ module odl-datastore-augmentation { } } + augment "/test:test" { + container aug-container { + container aug-inner-container { + } + } + } } \ No newline at end of file -- 2.36.6