From: Moiz Raja Date: Fri, 12 Jun 2015 01:29:07 +0000 (-0700) Subject: BUG 2970 : Recovery fails with SchemaValidationException when removing modules X-Git-Tag: release/beryllium~465 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=2b0ddf57e4d3d2e78637a6762ae6b5a05128a3a1 BUG 2970 : Recovery fails with SchemaValidationException when removing modules My prior fixes for bug 2970 did not address the recovery problem when a module is removed but it's data is still present in the persisted files. This patch attempts to address that. There seems to be no schema validation when you write a normalized node at / so for when writing or merging at / the pruning is done first before attempting a write on InMemoryDataTreeModification Also removed all unneccessary code. Change-Id: Id2793330441606c62dfd903cdd698c2f86b14c1b Signed-off-by: Moiz Raja (cherry picked from commit 9a06d5de97157fdd497946f7c467c928344e0e67) --- 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 2c5bdf62df..6877a54a8a 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 @@ -32,6 +32,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; */ public class NormalizedNodePruner implements NormalizedNodeStreamWriter { + 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; @@ -271,6 +272,7 @@ public class NormalizedNodePruner implements NormalizedNodeStreamWriter { 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()); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/PruningShardDataTreeSnapshot.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/PruningShardDataTreeSnapshot.java deleted file mode 100644 index 2a0b9762f9..0000000000 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/PruningShardDataTreeSnapshot.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ - -package org.opendaylight.controller.cluster.datastore; - -import com.google.common.base.Optional; -import java.net.URI; -import java.util.Set; -import org.opendaylight.controller.cluster.datastore.utils.PruningDataTreeModification; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; - -/** - * The PruningShardDataTreeSnapshot returns a PruningDataTreeModification when a newModification is created - */ -class PruningShardDataTreeSnapshot implements DataTreeSnapshot { - - private final DataTreeSnapshot dataTreeSnapshot; - private final Set validNamespaces; - - public PruningShardDataTreeSnapshot(DataTreeSnapshot dataTreeSnapshot, Set validNamespaces) { - this.dataTreeSnapshot = dataTreeSnapshot; - this.validNamespaces = validNamespaces; - } - - @Override - public Optional> readNode(YangInstanceIdentifier yangInstanceIdentifier) { - return this.dataTreeSnapshot.readNode(yangInstanceIdentifier); - } - - @Override - public DataTreeModification newModification() { - return new PruningDataTreeModification(this.dataTreeSnapshot.newModification(), validNamespaces); - } - - -} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java index 0b4abe98a1..7ca9ca9928 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java @@ -156,6 +156,8 @@ public class Shard extends RaftActor { Dispatchers.DispatcherType.Transaction), self(), getContext(), shardMBean); snapshotCohort = new ShardSnapshotCohort(transactionActorFactory, store, LOG, this.name); + + } private void setTransactionCommitTimeout() { @@ -598,12 +600,11 @@ public class Shard extends RaftActor { @Override @Nonnull protected RaftActorRecoveryCohort getRaftActorRecoveryCohort() { - return new ShardRecoveryCoordinator(store, persistenceId(), LOG); + return new ShardRecoveryCoordinator(store, store.getSchemaContext(), persistenceId(), LOG); } @Override protected void onRecoveryComplete() { - store.recoveryDone(); //notify shard manager getContext().parent().tell(new ActorInitialized(), getSelf()); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTree.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTree.java index e852cfe420..fd42740784 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTree.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardDataTree.java @@ -7,18 +7,14 @@ */ package org.opendaylight.controller.cluster.datastore; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Strings; -import java.net.URI; import java.util.AbstractMap.SimpleEntry; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; -import org.opendaylight.controller.cluster.datastore.node.utils.transformer.NormalizedNodePruner; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener; @@ -53,8 +49,7 @@ public class ShardDataTree extends ShardDataTreeTransactionParent { private final ShardDataTreeChangePublisher treeChangePublisher = new ShardDataTreeChangePublisher(); private final ListenerTree listenerTree = ListenerTree.create(); private final TipProducingDataTree dataTree; - private Set validNamespaces; - private ShardDataTreeTransactionFactory transactionFactory = new RecoveryShardDataTreeTransactionFactory(); + private SchemaContext schemaContext; ShardDataTree(final SchemaContext schemaContext) { dataTree = InMemoryDataTreeFactory.getInstance().create(); @@ -66,10 +61,14 @@ public class ShardDataTree extends ShardDataTreeTransactionParent { return dataTree; } + SchemaContext getSchemaContext() { + return schemaContext; + } + void updateSchemaContext(final SchemaContext schemaContext) { Preconditions.checkNotNull(schemaContext); + this.schemaContext = schemaContext; dataTree.setSchemaContext(schemaContext); - validNamespaces = NormalizedNodePruner.namespaces(schemaContext); } private ShardDataTreeTransactionChain ensureTransactionChain(final String chainId) { @@ -84,7 +83,7 @@ public class ShardDataTree extends ShardDataTreeTransactionParent { ReadOnlyShardDataTreeTransaction newReadOnlyTransaction(final String txId, final String chainId) { if (Strings.isNullOrEmpty(chainId)) { - return transactionFactory.newReadOnlyTransaction(txId, chainId); + return new ReadOnlyShardDataTreeTransaction(txId, dataTree.takeSnapshot()); } return ensureTransactionChain(chainId).newReadOnlyTransaction(txId); @@ -92,7 +91,8 @@ public class ShardDataTree extends ShardDataTreeTransactionParent { ReadWriteShardDataTreeTransaction newReadWriteTransaction(final String txId, final String chainId) { if (Strings.isNullOrEmpty(chainId)) { - return transactionFactory.newReadWriteTransaction(txId, chainId); + return new ReadWriteShardDataTreeTransaction(ShardDataTree.this, txId, dataTree.takeSnapshot() + .newModification()); } return ensureTransactionChain(chainId).newReadWriteTransaction(txId); @@ -182,52 +182,4 @@ public class ShardDataTree extends ShardDataTreeTransactionParent { snapshot.ready(); return new SimpleShardDataTreeCohort(this, snapshot, transaction.getId()); } - - void recoveryDone(){ - transactionFactory = new RegularShardDataTreeTransactionFactory(); - } - - @VisibleForTesting - ShardDataTreeTransactionFactory getTransactionFactory(){ - return transactionFactory; - } - - @VisibleForTesting - static interface ShardDataTreeTransactionFactory { - ReadOnlyShardDataTreeTransaction newReadOnlyTransaction(final String txId, final String chainId); - - ReadWriteShardDataTreeTransaction newReadWriteTransaction(final String txId, final String chainId); - } - - @VisibleForTesting - class RecoveryShardDataTreeTransactionFactory implements ShardDataTreeTransactionFactory { - - @Override - public ReadOnlyShardDataTreeTransaction newReadOnlyTransaction(String txId, String chainId) { - return new ReadOnlyShardDataTreeTransaction(txId, - new PruningShardDataTreeSnapshot(dataTree.takeSnapshot(), validNamespaces)); - } - - @Override - public ReadWriteShardDataTreeTransaction newReadWriteTransaction(String txId, String chainId) { - return new ReadWriteShardDataTreeTransaction(ShardDataTree.this, txId, - new PruningShardDataTreeSnapshot(dataTree.takeSnapshot(), validNamespaces).newModification()); - } - } - - @VisibleForTesting - class RegularShardDataTreeTransactionFactory implements ShardDataTreeTransactionFactory { - - @Override - public ReadOnlyShardDataTreeTransaction newReadOnlyTransaction(String txId, String chainId) { - return new ReadOnlyShardDataTreeTransaction(txId, dataTree.takeSnapshot()); - - } - - @Override - public ReadWriteShardDataTreeTransaction newReadWriteTransaction(String txId, String chainId) { - return new ReadWriteShardDataTreeTransaction(ShardDataTree.this, txId, dataTree.takeSnapshot() - .newModification()); - } - } } 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 797641978d..5c9f0d11c6 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 @@ -7,9 +7,14 @@ */ 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.ModificationPayload; 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; import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload; @@ -21,6 +26,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; /** @@ -37,24 +43,28 @@ class ShardRecoveryCoordinator implements RaftActorRecoveryCohort { private final DataTree store; private final String shardName; private final Logger log; - private DataTreeModification transaction; + private final Set validNamespaces; + private PruningDataTreeModification transaction; private int size; - ShardRecoveryCoordinator(ShardDataTree store, String shardName, Logger log) { + ShardRecoveryCoordinator(ShardDataTree store, SchemaContext schemaContext, String shardName, Logger log) { this.store = store.getDataTree(); this.shardName = shardName; this.log = log; + this.validNamespaces = NormalizedNodePruner.namespaces(schemaContext); } @Override public void startLogRecoveryBatch(int maxBatchSize) { log.debug("{}: starting log recovery batch with max size {}", shardName, maxBatchSize); - transaction = store.takeSnapshot().newModification(); + transaction = new PruningDataTreeModification(store.takeSnapshot().newModification(), validNamespaces); size = 0; } @Override public void appendRecoveredLogEntry(Payload payload) { + Preconditions.checkState(transaction != null, "call startLogRecovery before calling appendRecoveredLogEntry"); + try { if (payload instanceof DataTreeCandidatePayload) { DataTreeCandidates.applyToModification(transaction, ((DataTreeCandidatePayload)payload).getCandidate()); @@ -79,10 +89,11 @@ class ShardRecoveryCoordinator implements RaftActorRecoveryCohort { } } - private void commitTransaction(DataTreeModification tx) throws DataValidationFailedException { - tx.ready(); - store.validate(tx); - store.commit(store.prepare(tx)); + private void commitTransaction(PruningDataTreeModification tx) throws DataValidationFailedException { + DataTreeModification delegate = tx.getDelegate(); + delegate.ready(); + store.validate(delegate); + store.commit(store.prepare(delegate)); } /** @@ -90,6 +101,8 @@ class ShardRecoveryCoordinator implements RaftActorRecoveryCohort { */ @Override public void applyCurrentLogRecoveryBatch() { + Preconditions.checkState(transaction != null, "call startLogRecovery before calling applyCurrentLogRecoveryBatch"); + log.debug("{}: Applying current log recovery batch with size {}", shardName, size); try { commitTransaction(transaction); @@ -109,7 +122,7 @@ class ShardRecoveryCoordinator implements RaftActorRecoveryCohort { log.debug("{}: Applying recovered snapshot", shardName); final NormalizedNode node = SerializationUtils.deserializeNormalizedNode(snapshotBytes); - final DataTreeModification tx = store.takeSnapshot().newModification(); + final PruningDataTreeModification tx = new PruningDataTreeModification(store.takeSnapshot().newModification(), validNamespaces); tx.write(ROOT, node); try { commitTransaction(tx); 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 cc976b381f..d59e564345 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 @@ -50,41 +50,57 @@ public class PruningDataTreeModification implements DataTreeModification { @Override public void merge(YangInstanceIdentifier yangInstanceIdentifier, NormalizedNode normalizedNode) { try { - delegate.merge(yangInstanceIdentifier, normalizedNode); + if(YangInstanceIdentifier.EMPTY.equals(yangInstanceIdentifier)){ + pruneAndMergeNode(yangInstanceIdentifier, normalizedNode); + } else { + 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", yangInstanceIdentifier); + pruneAndMergeNode(yangInstanceIdentifier, normalizedNode); + } + + } - NormalizedNode pruned = pruneNormalizedNode(normalizedNode); + private void pruneAndMergeNode(YangInstanceIdentifier yangInstanceIdentifier, NormalizedNode normalizedNode) { + LOG.warn("Node at path : {} was pruned during merge", yangInstanceIdentifier); - if(pruned != null) { - delegate.merge(yangInstanceIdentifier, pruned); - } - } + NormalizedNode pruned = pruneNormalizedNode(normalizedNode); + if(pruned != null) { + delegate.merge(yangInstanceIdentifier, pruned); + } } @Override public void write(YangInstanceIdentifier yangInstanceIdentifier, NormalizedNode normalizedNode) { try { - delegate.write(yangInstanceIdentifier, normalizedNode); + if(YangInstanceIdentifier.EMPTY.equals(yangInstanceIdentifier)){ + pruneAndWriteNode(yangInstanceIdentifier, normalizedNode); + } else { + 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", yangInstanceIdentifier); + 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(normalizedNode); - if(pruned != null) { - delegate.write(yangInstanceIdentifier, pruned); - } + if(pruned != null) { + delegate.write(yangInstanceIdentifier, pruned); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/PruningShardDataTreeSnapshotTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/PruningShardDataTreeSnapshotTest.java deleted file mode 100644 index 2d48e7a316..0000000000 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/PruningShardDataTreeSnapshotTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.opendaylight.controller.cluster.datastore; - -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.verify; -import java.net.URI; -import java.util.Set; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.cluster.datastore.utils.PruningDataTreeModification; -import org.opendaylight.controller.md.cluster.datastore.model.CarsModel; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; - -public class PruningShardDataTreeSnapshotTest { - - @Mock - DataTreeSnapshot dataTreeSnapshot; - - @Mock - Set validNamespaces; - - @Before - public void setUp(){ - MockitoAnnotations.initMocks(this); - } - - @Test - public void testNewModification(){ - PruningShardDataTreeSnapshot snapshot1 - = new PruningShardDataTreeSnapshot(dataTreeSnapshot, validNamespaces); - - DataTreeModification dataTreeModification1 = snapshot1.newModification(); - - assertTrue(dataTreeModification1 instanceof PruningDataTreeModification); - } - - @Test - public void testReadNode(){ - PruningShardDataTreeSnapshot snapshot - = new PruningShardDataTreeSnapshot(dataTreeSnapshot, validNamespaces); - - snapshot.readNode(CarsModel.BASE_PATH); - - verify(dataTreeSnapshot).readNode(CarsModel.BASE_PATH); - } -} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeTest.java index ae3ae6f792..f001bc372e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardDataTreeTest.java @@ -2,9 +2,9 @@ package org.opendaylight.controller.cluster.datastore; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import com.google.common.base.Optional; import java.util.concurrent.ExecutionException; +import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.md.cluster.datastore.model.CarsModel; import org.opendaylight.controller.md.cluster.datastore.model.PeopleModel; @@ -16,39 +16,28 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; public class ShardDataTreeTest { - @Test - public void testWrite() throws ExecutionException, InterruptedException { + SchemaContext fullSchema; - SchemaContext schemaContext = SchemaContextHelper.full(); - - modify(new ShardDataTree(schemaContext), false, true, true); + @Before + public void setUp(){ + fullSchema = SchemaContextHelper.full(); } @Test - public void testWriteWithMissingSchema() throws ExecutionException, InterruptedException { - - SchemaContext schemaContext = SchemaContextHelper.select(SchemaContextHelper.ODL_DATASTORE_TEST_YANG, SchemaContextHelper.PEOPLE_YANG); - - modify(new ShardDataTree(schemaContext), false, false, true); + public void testWrite() throws ExecutionException, InterruptedException { + modify(new ShardDataTree(fullSchema), false, true, true); } @Test public void testMerge() throws ExecutionException, InterruptedException { - - SchemaContext schemaContext = SchemaContextHelper.full(); - - modify(new ShardDataTree(schemaContext), true, true, true); + modify(new ShardDataTree(fullSchema), true, true, true); } - @Test - public void testMergeWithMissingSchema() throws ExecutionException, InterruptedException { - SchemaContext schemaContext = SchemaContextHelper.select(SchemaContextHelper.ODL_DATASTORE_TEST_YANG, SchemaContextHelper.PEOPLE_YANG); + private void modify(ShardDataTree shardDataTree, boolean merge, boolean expectedCarsPresent, boolean expectedPeoplePresent) throws ExecutionException, InterruptedException { - modify(new ShardDataTree(schemaContext), true, false, true); - } + assertEquals(fullSchema, shardDataTree.getSchemaContext()); - private void modify(ShardDataTree shardDataTree, boolean merge, boolean expectedCarsPresent, boolean expectedPeoplePresent) throws ExecutionException, InterruptedException { ReadWriteShardDataTreeTransaction transaction = shardDataTree.newReadWriteTransaction("txn-1", null); DataTreeModification snapshot = transaction.getSnapshot(); @@ -83,18 +72,4 @@ public class ShardDataTreeTest { } - @Test(expected = IllegalArgumentException.class) - public void testAfterRecoveryDone() throws ExecutionException, InterruptedException { - SchemaContext schemaContext = SchemaContextHelper.select(SchemaContextHelper.ODL_DATASTORE_TEST_YANG, SchemaContextHelper.PEOPLE_YANG); - ShardDataTree shardDataTree = new ShardDataTree(schemaContext); - assertTrue("transaction factory must be the recovery transaction factory", - shardDataTree.getTransactionFactory() instanceof ShardDataTree.RecoveryShardDataTreeTransactionFactory); - shardDataTree.recoveryDone(); - assertTrue("transaction factory must be the regular transaction factory", - shardDataTree.getTransactionFactory() instanceof ShardDataTree.RegularShardDataTreeTransactionFactory); - - modify(shardDataTree, true, false, true); - - } - } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardRecoveryCoordinatorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardRecoveryCoordinatorTest.java new file mode 100644 index 0000000000..f14bb3de76 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardRecoveryCoordinatorTest.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.cluster.datastore; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import com.google.common.base.Optional; +import java.io.IOException; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.cluster.datastore.modification.ModificationPayload; +import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification; +import org.opendaylight.controller.cluster.datastore.modification.WriteModification; +import org.opendaylight.controller.cluster.datastore.utils.SerializationUtils; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationByteStringPayload; +import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload; +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.yangtools.yang.data.api.YangInstanceIdentifier; +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.DataTreeSnapshot; +import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree; +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; +import org.slf4j.LoggerFactory; + +public class ShardRecoveryCoordinatorTest { + + private ShardDataTree peopleDataTree; + private SchemaContext peopleSchemaContext; + private SchemaContext carsSchemaContext; + + @Before + public void setUp(){ + peopleSchemaContext = SchemaContextHelper.select(SchemaContextHelper.PEOPLE_YANG); + carsSchemaContext = SchemaContextHelper.select(SchemaContextHelper.CARS_YANG); + + peopleDataTree = new ShardDataTree(peopleSchemaContext); + } + + @Test + public void testAppendRecoveredLogEntryDataTreeCandidatePayload(){ + ShardRecoveryCoordinator coordinator = new ShardRecoveryCoordinator(peopleDataTree, peopleSchemaContext, "foobar", LoggerFactory.getLogger("foo")); + coordinator.startLogRecoveryBatch(10); + try { + coordinator.appendRecoveredLogEntry(DataTreeCandidatePayload.create(createCar())); + } catch(SchemaValidationFailedException e){ + fail("SchemaValidationFailedException should not happen if pruning is done"); + } + + coordinator.applyCurrentLogRecoveryBatch(); + } + + @Test + public void testAppendRecoveredLogEntryModificationPayload() throws IOException { + ShardRecoveryCoordinator coordinator = new ShardRecoveryCoordinator(peopleDataTree, peopleSchemaContext, "foobar", LoggerFactory.getLogger("foo")); + coordinator.startLogRecoveryBatch(10); + try { + MutableCompositeModification modification = new MutableCompositeModification((short) 1); + modification.addModification(new WriteModification(CarsModel.BASE_PATH, CarsModel.create())); + coordinator.appendRecoveredLogEntry(new ModificationPayload(modification)); + } catch(SchemaValidationFailedException e){ + fail("SchemaValidationFailedException should not happen if pruning is done"); + } + } + + @Test + public void testAppendRecoveredLogEntryCompositeModificationPayload() throws IOException { + ShardRecoveryCoordinator coordinator = new ShardRecoveryCoordinator(peopleDataTree, peopleSchemaContext, "foobar", LoggerFactory.getLogger("foo")); + coordinator.startLogRecoveryBatch(10); + try { + MutableCompositeModification modification = new MutableCompositeModification((short) 1); + modification.addModification(new WriteModification(CarsModel.BASE_PATH, CarsModel.create())); + coordinator.appendRecoveredLogEntry(new CompositeModificationPayload(modification.toSerializable())); + } catch(SchemaValidationFailedException e){ + fail("SchemaValidationFailedException should not happen if pruning is done"); + } + } + + @Test + public void testAppendRecoveredLogEntryCompositeModificationByteStringPayload() throws IOException { + ShardRecoveryCoordinator coordinator = new ShardRecoveryCoordinator(peopleDataTree, peopleSchemaContext, "foobar", LoggerFactory.getLogger("foo")); + coordinator.startLogRecoveryBatch(10); + try { + MutableCompositeModification modification = new MutableCompositeModification((short) 1); + modification.addModification(new WriteModification(CarsModel.BASE_PATH, CarsModel.create())); + coordinator.appendRecoveredLogEntry(new CompositeModificationByteStringPayload(modification.toSerializable())); + } catch(SchemaValidationFailedException e){ + fail("SchemaValidationFailedException should not happen if pruning is done"); + } + + assertEquals(false, readCars(peopleDataTree).isPresent()); + } + + @Test + public void testApplyRecoverySnapshot(){ + ShardRecoveryCoordinator coordinator = new ShardRecoveryCoordinator(peopleDataTree , peopleSchemaContext, "foobar", LoggerFactory.getLogger("foo")); + coordinator.startLogRecoveryBatch(10); + + coordinator.applyRecoverySnapshot(createSnapshot()); + + assertEquals(false, readCars(peopleDataTree).isPresent()); + assertEquals(true, readPeople(peopleDataTree).isPresent()); + } + + + @Test + public void testApplyCurrentLogRecoveryBatch(){ + ShardRecoveryCoordinator coordinator = new ShardRecoveryCoordinator(peopleDataTree, peopleSchemaContext, "foobar", LoggerFactory.getLogger("foo")); + coordinator.startLogRecoveryBatch(10); + + try { + coordinator.applyCurrentLogRecoveryBatch(); + } catch(IllegalArgumentException e){ + fail("IllegalArgumentException should not happen - if the pruning modification delegate is passed"); + } + } + + private DataTreeCandidateTip createCar(){ + TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(); + dataTree.setSchemaContext(carsSchemaContext); + + DataTreeSnapshot snapshot = dataTree.takeSnapshot(); + + DataTreeModification modification = snapshot.newModification(); + + modification.merge(CarsModel.BASE_PATH, CarsModel.create()); + + return dataTree.prepare(modification); + } + + private Optional> readCars(ShardDataTree shardDataTree){ + TipProducingDataTree dataTree = shardDataTree.getDataTree(); + dataTree.setSchemaContext(peopleSchemaContext); + + DataTreeSnapshot snapshot = dataTree.takeSnapshot(); + + DataTreeModification modification = snapshot.newModification(); + + return modification.readNode(CarsModel.BASE_PATH); + } + + private Optional> readPeople(ShardDataTree shardDataTree){ + TipProducingDataTree dataTree = shardDataTree.getDataTree(); + dataTree.setSchemaContext(peopleSchemaContext); + + DataTreeSnapshot snapshot = dataTree.takeSnapshot(); + + DataTreeModification modification = snapshot.newModification(); + + return modification.readNode(PeopleModel.BASE_PATH); + } + + + + private byte[] createSnapshot(){ + TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(); + dataTree.setSchemaContext(SchemaContextHelper.select(SchemaContextHelper.CARS_YANG, SchemaContextHelper.PEOPLE_YANG)); + + DataTreeSnapshot snapshot = dataTree.takeSnapshot(); + + DataTreeModification modification = snapshot.newModification(); + + modification.merge(CarsModel.BASE_PATH, CarsModel.create()); + modification.merge(PeopleModel.BASE_PATH, PeopleModel.create()); + + DataTreeCandidateTip prepare = dataTree.prepare(modification); + + dataTree.commit(prepare); + + snapshot = dataTree.takeSnapshot(); + + modification = snapshot.newModification(); + + Optional> optional = modification.readNode(YangInstanceIdentifier.EMPTY); + + byte[] bytes = SerializationUtils.serializeNormalizedNode(optional.get()); + + return bytes; + + + } +} \ No newline at end of file