* 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.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.opendaylight.controller.cluster.datastore.ShardDataTreeMocking.immediatePreCommit;
import com.google.common.base.Ticker;
-import com.google.common.collect.Maps;
import com.google.common.primitives.UnsignedLong;
import com.google.common.util.concurrent.FutureCallback;
+import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mockito;
-import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats;
import org.opendaylight.controller.cluster.datastore.persisted.CommitTransactionPayload;
+import org.opendaylight.controller.cluster.datastore.persisted.MetadataShardDataTreeSnapshot;
+import org.opendaylight.controller.cluster.datastore.persisted.PayloadVersion;
+import org.opendaylight.controller.cluster.datastore.persisted.ShardSnapshotState;
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.mdsal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.yang.common.Uint64;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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.schema.ContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
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.DataValidationFailedException;
import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+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.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public class ShardDataTreeTest extends AbstractTest {
+ private static final DatastoreContext DATASTORE_CONTEXT = DatastoreContext.newBuilder().build();
private final Shard mockShard = Mockito.mock(Shard.class);
private ShardDataTree shardDataTree;
- private SchemaContext fullSchema;
+ private EffectiveModelContext fullSchema;
@Before
public void setUp() {
doReturn(Ticker.systemTicker()).when(mockShard).ticker();
- doReturn(Mockito.mock(ShardStats.class)).when(mockShard).getShardMBean();
+ doReturn(new ShardStats("shardName", "mxBeanType", mockShard)).when(mockShard).getShardMBean();
+ doReturn(DATASTORE_CONTEXT).when(mockShard).getDatastoreContext();
fullSchema = SchemaContextHelper.full();
DOMDataTreeChangeListener listener = mock(DOMDataTreeChangeListener.class);
shardDataTree.registerTreeChangeListener(CarsModel.CAR_LIST_PATH.node(CarsModel.CAR_QNAME), listener,
- com.google.common.base.Optional.absent(), noop -> { });
+ Optional.empty(), noop -> { });
addCar(shardDataTree, "optima");
shardDataTree.applySnapshot(newDataTree.takeStateSnapshot());
- Map<YangInstanceIdentifier, ModificationType> expChanges = Maps.newHashMap();
+ Map<YangInstanceIdentifier, ModificationType> expChanges = new HashMap<>();
expChanges.put(CarsModel.newCarPath("optima"), ModificationType.WRITE);
expChanges.put(CarsModel.newCarPath("murano"), ModificationType.WRITE);
expChanges.put(CarsModel.newCarPath("sportage"), ModificationType.DELETE);
snapshot.write(PeopleModel.BASE_PATH, peopleNode));
YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
- MapEntryNode carNode = CarsModel.newCarEntry("optima", new BigInteger("100"));
+ MapEntryNode carNode = CarsModel.newCarEntry("optima", Uint64.valueOf(100));
final ShardDataTreeCohort cohort4 = newShardDataTreeCohort(snapshot -> snapshot.write(carPath, carNode));
immediateCanCommit(cohort1);
snapshot.write(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode()));
YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
- MapEntryNode carNode = CarsModel.newCarEntry("optima", new BigInteger("100"));
+ MapEntryNode carNode = CarsModel.newCarEntry("optima", Uint64.valueOf(100));
final ShardDataTreeCohort cohort3 = newShardDataTreeCohort(snapshot -> snapshot.write(carPath, carNode));
final FutureCallback<UnsignedLong> commitCallback2 = immediate3PhaseCommit(cohort2);
snapshot.write(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode()));
YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
- MapEntryNode carNode = CarsModel.newCarEntry("optima", new BigInteger("100"));
+ MapEntryNode carNode = CarsModel.newCarEntry("optima", Uint64.valueOf(100));
final ShardDataTreeCohort cohort3 = newShardDataTreeCohort(snapshot -> snapshot.write(carPath, carNode));
final FutureCallback<UnsignedLong> commitCallback1 = immediate3PhaseCommit(cohort1);
snapshot.write(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode()));
YangInstanceIdentifier carPath = CarsModel.newCarPath("optima");
- MapEntryNode carNode = CarsModel.newCarEntry("optima", new BigInteger("100"));
+ MapEntryNode carNode = CarsModel.newCarEntry("optima", Uint64.valueOf(100));
final ShardDataTreeCohort cohort4 = newShardDataTreeCohort(snapshot -> snapshot.write(carPath, carNode));
coordinatedCanCommit(cohort2);
assertEquals("People node", peopleNode, optional.get());
}
+ @Test
+ public void testUintCommitPayload() throws IOException {
+ shardDataTree.applyRecoveryPayload(CommitTransactionPayload.create(nextTransactionId(),
+ DataTreeCandidates.fromNormalizedNode(YangInstanceIdentifier.empty(), bigIntegerRoot()),
+ PayloadVersion.SODIUM_SR1));
+
+ assertCarsUint64();
+ }
+
+ @Test
+ public void testUintSnapshot() throws IOException, DataValidationFailedException {
+ shardDataTree.applyRecoverySnapshot(new ShardSnapshotState(new MetadataShardDataTreeSnapshot(bigIntegerRoot()),
+ true));
+
+ assertCarsUint64();
+ }
+
+ @Test
+ public void testUintReplay() throws DataValidationFailedException, IOException {
+ // Commit two writes and one merge, saving the data tree candidate for each.
+ // write(foo=1)
+ // write(foo=2)
+ // merge(foo=3)
+ final DataTree dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
+ fullSchema);
+ DataTreeModification mod = dataTree.takeSnapshot().newModification();
+ mod.write(CarsModel.BASE_PATH, Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(CarsModel.BASE_QNAME))
+ .withChild(Builders.mapBuilder()
+ .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
+ .withChild(createCar("one", BigInteger.ONE))
+ .build())
+ .build());
+ mod.ready();
+ dataTree.validate(mod);
+ final DataTreeCandidate first = dataTree.prepare(mod);
+ dataTree.commit(first);
+
+ mod = dataTree.takeSnapshot().newModification();
+ mod.write(CarsModel.newCarPath("two"), createCar("two", BigInteger.TWO));
+ mod.ready();
+ dataTree.validate(mod);
+ final DataTreeCandidate second = dataTree.prepare(mod);
+ dataTree.commit(second);
+
+ mod = dataTree.takeSnapshot().newModification();
+ mod.merge(CarsModel.CAR_LIST_PATH, Builders.mapBuilder()
+ .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
+ .withChild(createCar("three", BigInteger.TEN))
+ .build());
+ mod.ready();
+ dataTree.validate(mod);
+ final DataTreeCandidate third = dataTree.prepare(mod);
+ dataTree.commit(third);
+
+ // Apply first candidate as a snapshot
+ shardDataTree.applyRecoverySnapshot(
+ new ShardSnapshotState(new MetadataShardDataTreeSnapshot(first.getRootNode().getDataAfter().get()), true));
+ // Apply the other two snapshots as transactions
+ shardDataTree.applyRecoveryPayload(CommitTransactionPayload.create(nextTransactionId(), second,
+ PayloadVersion.SODIUM_SR1));
+ shardDataTree.applyRecoveryPayload(CommitTransactionPayload.create(nextTransactionId(), third,
+ PayloadVersion.SODIUM_SR1));
+
+ // Verify uint translation
+ final DataTreeSnapshot snapshot = shardDataTree.newReadOnlyTransaction(nextTransactionId()).getSnapshot();
+ final NormalizedNode<?, ?> cars = snapshot.readNode(CarsModel.CAR_LIST_PATH).get();
+
+ assertEquals(Builders.mapBuilder()
+ .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
+ // Note: Uint64
+ .withChild(createCar("one", Uint64.ONE))
+ .withChild(createCar("two", Uint64.TWO))
+ .withChild(createCar("three", Uint64.TEN))
+ .build(), cars);
+ }
+
+ private void assertCarsUint64() {
+ final DataTreeSnapshot snapshot = shardDataTree.newReadOnlyTransaction(nextTransactionId()).getSnapshot();
+ final NormalizedNode<?, ?> cars = snapshot.readNode(CarsModel.CAR_LIST_PATH).get();
+
+ assertEquals(Builders.mapBuilder()
+ .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
+ // Note: Uint64
+ .withChild(createCar("foo", Uint64.ONE))
+ .build(), cars);
+ }
+
+ private static ContainerNode bigIntegerRoot() {
+ return Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(SchemaContext.NAME))
+ .withChild(Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(CarsModel.CARS_QNAME))
+ .withChild(Builders.mapBuilder()
+ .withNodeIdentifier(new NodeIdentifier(CarsModel.CAR_QNAME))
+ // Note: BigInteger
+ .withChild(createCar("foo", BigInteger.ONE))
+ .build())
+ .build())
+ .build();
+ }
+
+ private static MapEntryNode createCar(final String name, final Object value) {
+ return Builders.mapEntryBuilder()
+ .withNodeIdentifier(NodeIdentifierWithPredicates.of(CarsModel.CAR_QNAME,CarsModel.CAR_NAME_QNAME, name))
+ .withChild(ImmutableNodes.leafNode(CarsModel.CAR_NAME_QNAME, name))
+ // Note: old BigInteger
+ .withChild(ImmutableNodes.leafNode(CarsModel.CAR_PRICE_QNAME, value))
+ .build();
+ }
+
private ShardDataTreeCohort newShardDataTreeCohort(final DataTreeOperation operation) {
final ReadWriteShardDataTreeTransaction transaction =
shardDataTree.newReadWriteTransaction(nextTransactionId());
return doTransaction(shardDataTree, snapshot -> {
snapshot.merge(CarsModel.BASE_PATH, CarsModel.emptyContainer());
snapshot.merge(CarsModel.CAR_LIST_PATH, CarsModel.newCarMapNode());
- snapshot.write(CarsModel.newCarPath(name), CarsModel.newCarEntry(name, new BigInteger("100")));
+ snapshot.write(CarsModel.newCarPath(name), CarsModel.newCarEntry(name, Uint64.valueOf(100)));
});
}