X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-distributed-datastore%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fdatastore%2Fentityownership%2FAbstractEntityOwnershipTest.java;h=05d37fd1b2a2dc74663c9f9f5b1bf486684dc0cb;hp=3134a6eedd4a540721cc8c03b88d93411585b092;hb=04ce3cfa566f7667800abb376d533f41246ff909;hpb=a4d9810d7211097f2803174d0c23a27b53dbc9d2 diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/AbstractEntityOwnershipTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/AbstractEntityOwnershipTest.java index 3134a6eedd..05d37fd1b2 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/AbstractEntityOwnershipTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/AbstractEntityOwnershipTest.java @@ -17,15 +17,33 @@ import static org.opendaylight.controller.cluster.datastore.entityownership.Enti import static org.opendaylight.controller.cluster.datastore.entityownership.EntityOwnersModel.ENTITY_OWNER_QNAME; import static org.opendaylight.controller.cluster.datastore.entityownership.EntityOwnersModel.ENTITY_QNAME; import static org.opendaylight.controller.cluster.datastore.entityownership.EntityOwnersModel.ENTITY_TYPE_QNAME; +import static org.opendaylight.controller.cluster.datastore.entityownership.EntityOwnersModel.candidatePath; import static org.opendaylight.controller.cluster.datastore.entityownership.EntityOwnersModel.entityPath; + +import akka.pattern.Patterns; +import akka.testkit.TestActorRef; +import akka.util.Timeout; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Stopwatch; import com.google.common.util.concurrent.Uninterruptibles; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import org.hamcrest.Description; import org.junit.Assert; +import org.mockito.ArgumentMatcher; +import org.mockito.Matchers; +import org.opendaylight.controller.cluster.access.concepts.MemberName; import org.opendaylight.controller.cluster.datastore.AbstractActorTest; +import org.opendaylight.controller.cluster.datastore.AbstractShardTest; import org.opendaylight.controller.cluster.datastore.ShardDataTree; +import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; +import org.opendaylight.controller.cluster.raft.client.messages.GetOnDemandRaftState; +import org.opendaylight.controller.cluster.raft.client.messages.OnDemandRaftState; +import org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState; +import org.opendaylight.mdsal.eos.dom.api.DOMEntity; +import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.clustering.entity.owners.rev150804.EntityOwners; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.clustering.entity.owners.rev150804.entity.owners.EntityType; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.clustering.entity.owners.rev150804.entity.owners.entity.type.entity.Candidate; @@ -40,9 +58,15 @@ import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; 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.tree.DataTreeCandidateTip; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import scala.concurrent.Await; +import scala.concurrent.Future; +import scala.concurrent.duration.Duration; +import scala.concurrent.duration.FiniteDuration; /** * Abstract base class providing utility methods. @@ -50,6 +74,10 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailed * @author Thomas Pantelis */ public class AbstractEntityOwnershipTest extends AbstractActorTest { + protected final Logger testLog = LoggerFactory.getLogger(getClass()); + + private static final AtomicInteger NEXT_SHARD_NUM = new AtomicInteger(); + protected void verifyEntityCandidate(NormalizedNode node, String entityType, YangInstanceIdentifier entityId, String candidateName, boolean expectPresent) { try { @@ -65,7 +93,7 @@ public class AbstractEntityOwnershipTest extends AbstractActorTest { entityId, true); getMapEntryNodeChild(entityEntry, Candidate.QNAME, CANDIDATE_NAME_QNAME, candidateName, expectPresent); - } catch(AssertionError e) { + } catch (AssertionError e) { throw new AssertionError("Verification of entity candidate failed - returned data was: " + node, e); } } @@ -74,7 +102,7 @@ public class AbstractEntityOwnershipTest extends AbstractActorTest { Function> reader, boolean expectPresent) { AssertionError lastError = null; Stopwatch sw = Stopwatch.createStarted(); - while(sw.elapsed(TimeUnit.MILLISECONDS) <= 5000) { + while (sw.elapsed(TimeUnit.MILLISECONDS) <= 5000) { NormalizedNode node = reader.apply(ENTITY_OWNERS_PATH); try { verifyEntityCandidate(node, entityType, entityId, candidateName, expectPresent); @@ -102,9 +130,9 @@ public class AbstractEntityOwnershipTest extends AbstractActorTest { MapNode entityTypeMapNode = (MapNode) childNode.get(); Optional entityTypeEntry = entityTypeMapNode.getChild(new NodeIdentifierWithPredicates( childMap, child, key)); - if(expectPresent && !entityTypeEntry.isPresent()) { + if (expectPresent && !entityTypeEntry.isPresent()) { fail("Missing " + childMap.toString() + " entry for " + key + ". Actual: " + entityTypeMapNode.getValue()); - } else if(!expectPresent && entityTypeEntry.isPresent()) { + } else if (!expectPresent && entityTypeEntry.isPresent()) { fail("Found unexpected " + childMap.toString() + " entry for " + key); } @@ -116,13 +144,13 @@ public class AbstractEntityOwnershipTest extends AbstractActorTest { AssertionError lastError = null; YangInstanceIdentifier entityPath = entityPath(entityType, entityId).node(ENTITY_OWNER_QNAME); Stopwatch sw = Stopwatch.createStarted(); - while(sw.elapsed(TimeUnit.MILLISECONDS) <= 5000) { + while (sw.elapsed(TimeUnit.MILLISECONDS) <= 5000) { try { NormalizedNode node = reader.apply(entityPath); Assert.assertNotNull("Owner was not set for entityId: " + entityId, node); Assert.assertEquals("Entity owner", expected, node.getValue().toString()); return; - } catch(AssertionError e) { + } catch (AssertionError e) { lastError = e; Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); } @@ -131,16 +159,28 @@ public class AbstractEntityOwnershipTest extends AbstractActorTest { throw lastError; } + @SuppressWarnings("checkstyle:IllegalCatch") + static void verifyOwner(final TestActorRef shard, String entityType, + YangInstanceIdentifier entityId, String localMemberName) { + verifyOwner(localMemberName, entityType, entityId, path -> { + try { + return AbstractShardTest.readStore(shard, path); + } catch (Exception e) { + return null; + } + }); + } + protected void verifyNodeRemoved(YangInstanceIdentifier path, Function> reader) { AssertionError lastError = null; Stopwatch sw = Stopwatch.createStarted(); - while(sw.elapsed(TimeUnit.MILLISECONDS) <= 5000) { + while (sw.elapsed(TimeUnit.MILLISECONDS) <= 5000) { try { NormalizedNode node = reader.apply(path); Assert.assertNull("Node was not removed at path: " + path, node); return; - } catch(AssertionError e) { + } catch (AssertionError e) { lastError = e; Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); } @@ -151,14 +191,14 @@ public class AbstractEntityOwnershipTest extends AbstractActorTest { static void writeNode(YangInstanceIdentifier path, NormalizedNode node, ShardDataTree shardDataTree) throws DataValidationFailedException { - DataTreeModification modification = shardDataTree.getDataTree().takeSnapshot().newModification(); + DataTreeModification modification = shardDataTree.newModification(); modification.merge(path, node); commit(shardDataTree, modification); } static void deleteNode(YangInstanceIdentifier path, ShardDataTree shardDataTree) throws DataValidationFailedException { - DataTreeModification modification = shardDataTree.getDataTree().takeSnapshot().newModification(); + DataTreeModification modification = shardDataTree.newModification(); modification.delete(path); commit(shardDataTree, modification); } @@ -166,10 +206,126 @@ public class AbstractEntityOwnershipTest extends AbstractActorTest { static void commit(ShardDataTree shardDataTree, DataTreeModification modification) throws DataValidationFailedException { modification.ready(); - shardDataTree.getDataTree().validate(modification); - DataTreeCandidateTip candidate = shardDataTree.getDataTree().prepare(modification); + final DataTreeCandidate candidate = shardDataTree.getDataTree().prepare(modification); shardDataTree.getDataTree().commit(candidate); shardDataTree.notifyListeners(candidate); } + + static DOMEntityOwnershipChange ownershipChange(final DOMEntity expEntity, final boolean expWasOwner, + final boolean expIsOwner, final boolean expHasOwner) { + return ownershipChange(expEntity, expWasOwner, expIsOwner, expHasOwner, false); + } + + static DOMEntityOwnershipChange ownershipChange(final DOMEntity expEntity, final boolean expWasOwner, + final boolean expIsOwner, final boolean expHasOwner, final boolean expInJeopardy) { + return Matchers.argThat(new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + DOMEntityOwnershipChange change = (DOMEntityOwnershipChange) argument; + return expEntity.equals(change.getEntity()) && expWasOwner == change.getState().wasOwner() + && expIsOwner == change.getState().isOwner() && expHasOwner == change.getState().hasOwner() + && expInJeopardy == change.inJeopardy(); + } + + @Override + public void describeTo(Description description) { + description.appendValue(new DOMEntityOwnershipChange(expEntity, EntityOwnershipChangeState.from( + expWasOwner, expIsOwner, expHasOwner), expInJeopardy)); + } + }); + } + + static DOMEntityOwnershipChange ownershipChange(final DOMEntity expEntity) { + return Matchers.argThat(new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + DOMEntityOwnershipChange change = (DOMEntityOwnershipChange) argument; + return expEntity.equals(change.getEntity()); + } + + @Override + public void describeTo(Description description) { + description.appendValue(new DOMEntityOwnershipChange(expEntity, EntityOwnershipChangeState.from( + false, false, false))); + } + }); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + static void verifyNoOwnerSet(TestActorRef shard, String entityType, + YangInstanceIdentifier entityId) { + YangInstanceIdentifier entityPath = entityPath(entityType, entityId).node(ENTITY_OWNER_QNAME); + try { + NormalizedNode node = AbstractShardTest.readStore(shard, entityPath); + if (node != null) { + Assert.fail("Owner " + node.getValue() + " was set for " + entityPath); + } + + } catch (Exception e) { + throw new AssertionError("read failed", e); + } + } + + static void verifyRaftState(final TestActorRef shard, + Consumer verifier) + throws Exception { + AssertionError lastError = null; + Stopwatch sw = Stopwatch.createStarted(); + while (sw.elapsed(TimeUnit.SECONDS) <= 5) { + FiniteDuration operationDuration = Duration.create(5, TimeUnit.SECONDS); + Future future = Patterns.ask(shard, GetOnDemandRaftState.INSTANCE, new Timeout(operationDuration)); + OnDemandRaftState raftState = (OnDemandRaftState)Await.result(future, operationDuration); + try { + verifier.accept(raftState); + return; + } catch (AssertionError e) { + lastError = e; + Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS); + } + } + + throw lastError; + } + + static ShardIdentifier newShardId(String memberName) { + return ShardIdentifier.create("entity-ownership", MemberName.forName(memberName), + "operational" + NEXT_SHARD_NUM.getAndIncrement()); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + void verifyEntityCandidateRemoved(final TestActorRef shard, String entityType, + YangInstanceIdentifier entityId, String candidateName) { + verifyNodeRemoved(candidatePath(entityType, entityId, candidateName), path -> { + try { + return AbstractShardTest.readStore(shard, path); + } catch (Exception e) { + throw new AssertionError("Failed to read " + path, e); + } + }); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + void verifyCommittedEntityCandidate(final TestActorRef shard, String entityType, + YangInstanceIdentifier entityId, String candidateName) { + verifyEntityCandidate(entityType, entityId, candidateName, path -> { + try { + return AbstractShardTest.readStore(shard, path); + } catch (Exception e) { + throw new AssertionError("Failed to read " + path, e); + } + }); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + void verifyNoEntityCandidate(final TestActorRef shard, String entityType, + YangInstanceIdentifier entityId, String candidateName) { + verifyEntityCandidate(entityType, entityId, candidateName, path -> { + try { + return AbstractShardTest.readStore(shard, path); + } catch (Exception e) { + throw new AssertionError("Failed to read " + path, e); + } + }, false); + } }