From: Tom Pantelis Date: Fri, 11 Sep 2015 04:13:51 +0000 (-0400) Subject: Bug 4105: Add hasOwner param to EntityOwnershipListener#ownershipChanged X-Git-Tag: release/beryllium~292 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=1f3f61ea49191bb0ada2d4de831a10f0a38a104d Bug 4105: Add hasOwner param to EntityOwnershipListener#ownershipChanged OF clustering needs to know when the last candidate is removed for an entity so it can clean up inventory. We decided to add a new param, hasOwner, passed to EntityOwnershipListener#ownershipChanged to indicate if there is at least one remaining candidate and current owner when a controller node loses ownership. So if wasOwner=true && isOwner=false && hasOwner=false, the OF code can remove the device node from inventory. To simplify the EntityOwnershipListener#ownershipChanged interface and to allow for possible future parameters w/o breaking the interface, the parameters are now encapsulated in an EntityOwnershipChanged DTO. There already was the same EntityOwnershipChanged class in sal-distributed-datastore - this class was removed. Change-Id: I07375f154ac55d34062380ad6d0b30d970bd28e7 Signed-off-by: Tom Pantelis (cherry picked from commit 061d4edabfb421ec79d1dc7afe4163aa52828450) --- diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/clustering/EntityOwnershipChange.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/clustering/EntityOwnershipChange.java new file mode 100644 index 0000000000..b9d67f1759 --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/clustering/EntityOwnershipChange.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015 Brocade Communications 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.md.sal.common.api.clustering; + +import com.google.common.base.Preconditions; +import javax.annotation.Nonnull; + +/** + * A DTO that encapsulates an ownership change for an entity. + * + * @author Thomas Pantelis + */ +public class EntityOwnershipChange { + private final Entity entity; + private final boolean wasOwner; + private final boolean isOwner; + private final boolean hasOwner; + + public EntityOwnershipChange(@Nonnull Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner) { + this.entity = Preconditions.checkNotNull(entity, "entity can't be null"); + this.wasOwner = wasOwner; + this.isOwner = isOwner; + this.hasOwner = hasOwner; + } + + /** + * Returns the entity whose ownership status changed. + * @return the entity + */ + @Nonnull public Entity getEntity() { + return entity; + } + + /** + * Returns the previous ownership status of the entity for this process instance. + * @return true if this process was the owner of the entity at the time this notification was generated + */ + public boolean wasOwner() { + return wasOwner; + } + + /** + * Returns the current ownership status of the entity for this process instance. + * @return true if this process is now the owner of the entity + */ + public boolean isOwner() { + return isOwner; + } + + /** + * Returns the current ownership status of the entity across all process instances. + * @return true if the entity has an owner which may or may not be this process. If false, then + * the entity has no candidates and thus no owner. + */ + public boolean hasOwner() { + return hasOwner; + } + + @Override + public String toString() { + return "EntityOwnershipChanged [entity=" + entity + ", wasOwner=" + wasOwner + ", isOwner=" + isOwner + + ", hasOwner=" + hasOwner + "]"; + } +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/clustering/EntityOwnershipListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/clustering/EntityOwnershipListener.java index 96b9ec2e5f..b4eb44569b 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/clustering/EntityOwnershipListener.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/clustering/EntityOwnershipListener.java @@ -14,12 +14,22 @@ package org.opendaylight.controller.md.sal.common.api.clustering; public interface EntityOwnershipListener { /** - * A notification that is generated when the ownership status for a given entity changes in the current process. + * A notification that is generated when the ownership status of an entity changes. * - * @param entity the entity whose ownership status has changed - * @param wasOwner true if this process was the owner of the given entity right before this notification - * was generated - * @param isOwner true if this process now owns the given entity + * The following outlines valid combinations of the ownership status flags in the EntityOwnershipChange + * parameter and their meanings: + * + * @param ownershipChange contains the entity and its ownership status flags */ - void ownershipChanged(Entity entity, boolean wasOwner, boolean isOwner); + void ownershipChanged(EntityOwnershipChange ownershipChange); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnerChangeListener.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnerChangeListener.java index cf9df1821a..cc0b8a318d 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnerChangeListener.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnerChangeListener.java @@ -67,14 +67,14 @@ class EntityOwnerChangeListener implements DOMDataTreeChangeListener { if(!Objects.equal(origOwner, newOwner)) { boolean isOwner = Objects.equal(localMemberName, newOwner); boolean wasOwner = Objects.equal(localMemberName, origOwner); - if(isOwner || wasOwner) { - Entity entity = createEntity(change.getRootPath()); + boolean hasOwner = newOwner != null && !newOwner.toString().isEmpty(); - LOG.debug("{}: Calling notifyEntityOwnershipListeners: entity: {}, wasOwner: {}, isOwner: {}", - logId(), entity, wasOwner, isOwner); + Entity entity = createEntity(change.getRootPath()); - listenerSupport.notifyEntityOwnershipListeners(entity, wasOwner, isOwner); - } + LOG.debug("{}: Calling notifyEntityOwnershipListeners: entity: {}, wasOwner: {}, isOwner: {}, hasOwner: {}", + logId(), entity, wasOwner, isOwner, hasOwner); + + listenerSupport.notifyEntityOwnershipListeners(entity, wasOwner, isOwner, hasOwner); } } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerActor.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerActor.java index f62f7492f6..8c6b04cbf3 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerActor.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerActor.java @@ -11,7 +11,7 @@ import akka.actor.Props; import akka.japi.Creator; import com.google.common.base.Preconditions; import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor; -import org.opendaylight.controller.cluster.datastore.entityownership.messages.EntityOwnershipChanged; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,16 +32,16 @@ class EntityOwnershipListenerActor extends AbstractUntypedActor { @Override protected void handleReceive(Object message) { - if(message instanceof EntityOwnershipChanged) { - onEntityOwnershipChanged((EntityOwnershipChanged)message); + if(message instanceof EntityOwnershipChange) { + onEntityOwnershipChanged((EntityOwnershipChange)message); } } - private void onEntityOwnershipChanged(EntityOwnershipChanged change) { + private void onEntityOwnershipChanged(EntityOwnershipChange change) { LOG.debug("Notifying EntityOwnershipListener {}: {}", listener, change); try { - listener.ownershipChanged(change.getEntity(), change.wasOwner(), change.isOwner()); + listener.ownershipChanged(change); } catch (Exception e) { LOG.error("Error notifying listener {}", listener, e); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java index ed4a004ed0..a09d224954 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupport.java @@ -16,9 +16,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.IdentityHashMap; import java.util.Map; -import org.opendaylight.controller.cluster.datastore.entityownership.messages.EntityOwnershipChanged; import org.opendaylight.controller.md.sal.common.api.clustering.Entity; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidate; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,27 +80,27 @@ class EntityOwnershipListenerSupport { removeListener(listener, entityType, entityTypeListenerMap); } - void notifyEntityOwnershipListeners(Entity entity, boolean wasOwner, boolean isOwner) { - notifyListeners(entity, entity, wasOwner, isOwner, entityListenerMap); - notifyListeners(entity, entity.getType(), wasOwner, isOwner, entityTypeListenerMap); + void notifyEntityOwnershipListeners(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner) { + notifyListeners(entity, entity, wasOwner, isOwner, hasOwner, entityListenerMap); + notifyListeners(entity, entity.getType(), wasOwner, isOwner, hasOwner, entityTypeListenerMap); } - void notifyEntityOwnershipListener(Entity entity, boolean wasOwner, boolean isOwner, + void notifyEntityOwnershipListener(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner, EntityOwnershipListener listener) { - notifyListeners(entity, wasOwner, isOwner, Arrays.asList(listener)); + notifyListeners(entity, wasOwner, isOwner, hasOwner, Arrays.asList(listener)); } - private void notifyListeners(Entity entity, T mapKey, boolean wasOwner, boolean isOwner, + private void notifyListeners(Entity entity, T mapKey, boolean wasOwner, boolean isOwner, boolean hasOwner, Multimap listenerMap) { Collection listeners = listenerMap.get(mapKey); if(!listeners.isEmpty()) { - notifyListeners(entity, wasOwner, isOwner, listeners); + notifyListeners(entity, wasOwner, isOwner, hasOwner, listeners); } } - private void notifyListeners(Entity entity, boolean wasOwner, boolean isOwner, + private void notifyListeners(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner, Collection listeners) { - EntityOwnershipChanged changed = new EntityOwnershipChanged(entity, wasOwner, isOwner); + EntityOwnershipChange changed = new EntityOwnershipChange(entity, wasOwner, isOwner, hasOwner); for(EntityOwnershipListener listener: listeners) { ActorRef listenerActor = listenerActorFor(listener); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipShard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipShard.java index 58a7a03675..d8b1b53244 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipShard.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipShard.java @@ -169,7 +169,7 @@ class EntityOwnershipShard extends Shard { if(registerListener.getEntityType().equals(entityType)) { Entity entity = new Entity(entityType, (YangInstanceIdentifier) entityNode.getChild(ENTITY_ID_NODE_ID).get().getValue()); - listenerSupport.notifyEntityOwnershipListener(entity, false, true, registerListener.getListener()); + listenerSupport.notifyEntityOwnershipListener(entity, false, true, true, registerListener.getListener()); } } }); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/messages/EntityOwnershipChanged.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/messages/EntityOwnershipChanged.java deleted file mode 100644 index 86fd9f70cd..0000000000 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/entityownership/messages/EntityOwnershipChanged.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2015 Brocade Communications 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.entityownership.messages; - -import com.google.common.base.Preconditions; -import org.opendaylight.controller.md.sal.common.api.clustering.Entity; - -/** - * Message encapsulating an entity ownership change. - * - * @author Thomas Pantelis - */ -public class EntityOwnershipChanged { - private final Entity entity; - private final boolean wasOwner; - private final boolean isOwner; - - public EntityOwnershipChanged(Entity entity, boolean wasOwner, boolean isOwner) { - this.entity = Preconditions.checkNotNull(entity, "entity can't be null"); - this.wasOwner = wasOwner; - this.isOwner = isOwner; - } - - public Entity getEntity() { - return entity; - } - - public boolean wasOwner() { - return wasOwner; - } - - public boolean isOwner() { - return isOwner; - } - - @Override - public String toString() { - return "EntityOwnershipChanged [entity=" + entity + ", wasOwner=" + wasOwner + ", isOwner=" + isOwner + "]"; - } -} 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..ea7648efb9 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 @@ -23,9 +23,14 @@ 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 org.hamcrest.Description; import org.junit.Assert; +import org.mockito.ArgumentMatcher; +import org.mockito.Matchers; import org.opendaylight.controller.cluster.datastore.AbstractActorTest; import org.opendaylight.controller.cluster.datastore.ShardDataTree; +import org.opendaylight.controller.md.sal.common.api.clustering.Entity; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange; 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; @@ -172,4 +177,36 @@ public class AbstractEntityOwnershipTest extends AbstractActorTest { shardDataTree.getDataTree().commit(candidate); shardDataTree.notifyListeners(candidate); } + + static EntityOwnershipChange ownershipChange(final Entity expEntity, final boolean expWasOwner, + final boolean expIsOwner, final boolean expHasOwner) { + return Matchers.argThat(new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + EntityOwnershipChange change = (EntityOwnershipChange) argument; + return expEntity.equals(change.getEntity()) && expWasOwner == change.wasOwner() && + expIsOwner == change.isOwner() && expHasOwner == change.hasOwner(); + } + + @Override + public void describeTo(Description description) { + description.appendValue(new EntityOwnershipChange(expEntity, expWasOwner, expIsOwner, expHasOwner)); + } + }); + } + + static EntityOwnershipChange ownershipChange(final Entity expEntity) { + return Matchers.argThat(new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + EntityOwnershipChange change = (EntityOwnershipChange) argument; + return expEntity.equals(change.getEntity()); + } + + @Override + public void describeTo(Description description) { + description.appendValue(new EntityOwnershipChange(expEntity, false, false, false)); + } + }); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/DistributedEntityOwnershipIntegrationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/DistributedEntityOwnershipIntegrationTest.java index fc978f8687..ed7ca2d33e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/DistributedEntityOwnershipIntegrationTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/DistributedEntityOwnershipIntegrationTest.java @@ -8,12 +8,10 @@ package org.opendaylight.controller.cluster.datastore.entityownership; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; +import static org.opendaylight.controller.cluster.datastore.entityownership.AbstractEntityOwnershipTest.ownershipChange; import static org.opendaylight.controller.cluster.datastore.entityownership.EntityOwnersModel.CANDIDATE_NAME_NODE_ID; import static org.opendaylight.controller.cluster.datastore.entityownership.EntityOwnersModel.entityPath; import akka.actor.ActorSystem; @@ -94,6 +92,18 @@ public class DistributedEntityOwnershipIntegrationTest { @Mock private EntityOwnershipCandidate follower2MockCandidate; + @Mock + private EntityOwnershipCandidate leaderMockListener; + + @Mock + private EntityOwnershipCandidate leaderMockListener2; + + @Mock + private EntityOwnershipCandidate follower1MockListener; + + @Mock + private EntityOwnershipCandidate follower2MockListener; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -149,54 +159,71 @@ public class DistributedEntityOwnershipIntegrationTest { public void test() throws Exception { initDatastores("test"); + leaderEntityOwnershipService.registerListener(ENTITY_TYPE1, leaderMockListener); + leaderEntityOwnershipService.registerListener(ENTITY_TYPE2, leaderMockListener2); + follower1EntityOwnershipService.registerListener(ENTITY_TYPE1, follower1MockListener); + // Register leader candidate for entity1 and verify it becomes owner leaderEntityOwnershipService.registerCandidate(ENTITY1, leaderMockCandidate); - verify(leaderMockCandidate, timeout(5000)).ownershipChanged(ENTITY1, false, true); - reset(leaderMockCandidate); + verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1, false, true, true)); + verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1, false, false, true)); + reset(leaderMockListener, follower1MockListener); // Register leader candidate for entity1_2 (same id, different type) and verify it becomes owner leaderEntityOwnershipService.registerCandidate(ENTITY1_2, leaderMockCandidate); - verify(leaderMockCandidate, timeout(5000)).ownershipChanged(ENTITY1_2, false, true); - reset(leaderMockCandidate); + verify(leaderMockListener2, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1_2, false, true, true)); + verify(leaderMockListener, timeout(300).never()).ownershipChanged(ownershipChange(ENTITY1_2)); + reset(leaderMockListener2); // Register follower1 candidate for entity1 and verify it gets added but doesn't become owner follower1EntityOwnershipService.registerCandidate(ENTITY1, follower1MockCandidate); verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-1", "member-2"); verifyOwner(leaderDistributedDataStore, ENTITY1, "member-1"); - verify(follower1MockCandidate, never()).ownershipChanged(any(Entity.class), anyBoolean(), anyBoolean()); + verify(leaderMockListener, timeout(300).never()).ownershipChanged(ownershipChange(ENTITY1)); + verify(follower1MockListener, timeout(300).never()).ownershipChanged(ownershipChange(ENTITY1)); // Register follower1 candidate for entity2 and verify it becomes owner follower1EntityOwnershipService.registerCandidate(ENTITY2, follower1MockCandidate); - verify(follower1MockCandidate, timeout(5000)).ownershipChanged(ENTITY2, false, true); - reset(follower1MockCandidate); + verify(follower1MockCandidate, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, true, true)); + verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, false, true)); + reset(leaderMockListener, follower1MockListener); // Register follower2 candidate for entity2 and verify it gets added but doesn't become owner + follower2EntityOwnershipService.registerListener(ENTITY_TYPE1, follower2MockListener); follower2EntityOwnershipService.registerCandidate(ENTITY2, follower2MockCandidate); verifyCandidates(leaderDistributedDataStore, ENTITY2, "member-2", "member-3"); verifyOwner(leaderDistributedDataStore, ENTITY2, "member-2"); - verify(follower2MockCandidate, never()).ownershipChanged(any(Entity.class), anyBoolean(), anyBoolean()); // Unregister follower1 candidate for entity2 and verify follower2 becomes owner follower1EntityOwnershipService.unregisterCandidate(ENTITY2, follower1MockCandidate); - verify(follower2MockCandidate, timeout(5000)).ownershipChanged(ENTITY2, false, true); + verifyOwner(leaderDistributedDataStore, ENTITY2, "member-3"); + verify(follower2MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, true, true)); + verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, true, false, true)); + verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, false, true)); + verifyCandidates(leaderDistributedDataStore, ENTITY2, "member-3"); // Register follower1 candidate for entity3 and verify it becomes owner follower1EntityOwnershipService.registerCandidate(ENTITY3, follower1MockCandidate); - verify(follower1MockCandidate, timeout(5000)).ownershipChanged(ENTITY3, false, true); verifyOwner(leaderDistributedDataStore, ENTITY3, "member-2"); + verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY3, false, true, true)); + verify(follower2MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY3, false, false, true)); + verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY3, false, false, true)); // Register follower2 candidate for entity4 and verify it becomes owner follower2EntityOwnershipService.registerCandidate(ENTITY4, follower2MockCandidate); - verify(follower2MockCandidate, timeout(5000)).ownershipChanged(ENTITY4, false, true); - reset(follower2MockCandidate); + verifyOwner(leaderDistributedDataStore, ENTITY4, "member-3"); + verify(follower2MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY4, false, true, true)); + verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY4, false, false, true)); + verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY4, false, false, true)); + reset(follower2MockListener); // Register follower1 candidate for entity4 and verify it gets added but doesn't become owner @@ -204,26 +231,29 @@ public class DistributedEntityOwnershipIntegrationTest { follower1EntityOwnershipService.registerCandidate(ENTITY4, follower1MockCandidate); verifyCandidates(leaderDistributedDataStore, ENTITY4, "member-3", "member-2"); verifyOwner(leaderDistributedDataStore, ENTITY4, "member-3"); - verify(follower1MockCandidate, never()).ownershipChanged(any(Entity.class), anyBoolean(), anyBoolean()); // Shutdown follower2 and verify it's owned entities (entity 2 & 4) get re-assigned - reset(follower1MockCandidate); + reset(leaderMockListener, follower1MockListener); JavaTestKit.shutdownActorSystem(follower2System); - verify(follower1MockCandidate, timeout(15000)).ownershipChanged(ENTITY4, false, true); + verify(follower1MockListener, timeout(15000)).ownershipChanged(ownershipChange(ENTITY4, false, true, true)); + verify(leaderMockListener, timeout(15000)).ownershipChanged(ownershipChange(ENTITY4, false, false, true)); + verify(leaderMockListener, timeout(15000)).ownershipChanged(ownershipChange(ENTITY2, false, false, false)); verifyOwner(leaderDistributedDataStore, ENTITY2, ""); // no other candidate // Register leader candidate for entity2 and verify it becomes owner leaderEntityOwnershipService.registerCandidate(ENTITY2, leaderMockCandidate); - verify(leaderMockCandidate, timeout(5000)).ownershipChanged(ENTITY2, false, true); + verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, true, true)); verifyOwner(leaderDistributedDataStore, ENTITY2, "member-1"); // Unregister leader candidate for entity2 and verify the owner is cleared leaderEntityOwnershipService.unregisterCandidate(ENTITY2, leaderMockCandidate); verifyOwner(leaderDistributedDataStore, ENTITY2, ""); + verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, true, false, false)); + verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, false, false)); } private void verifyCandidates(DistributedDataStore dataStore, Entity entity, String... expCandidates) throws Exception { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnerChangeListenerTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnerChangeListenerTest.java index 8b80d6b9e7..e13f45640c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnerChangeListenerTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnerChangeListenerTest.java @@ -59,48 +59,78 @@ public class EntityOwnerChangeListenerTest { public void testOnDataTreeChanged() throws Exception { writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME)); writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, ENTITY_ID2, LOCAL_MEMBER_NAME)); - verify(mockListenerSupport, never()).notifyEntityOwnershipListeners(any(Entity.class), anyBoolean(), anyBoolean()); + verify(mockListenerSupport, never()).notifyEntityOwnershipListeners(any(Entity.class), anyBoolean(), + anyBoolean(), anyBoolean()); + + // Write local member as owner for entity 1 writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, LOCAL_MEMBER_NAME)); - verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY1, false, true); + verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY1, false, true, true); + + // Add remote member 1 as candidate for entity 1 - listener support should not get notified reset(mockListenerSupport); writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, ENTITY_ID1, REMOTE_MEMBER_NAME1)); - verify(mockListenerSupport, never()).notifyEntityOwnershipListeners(any(Entity.class), anyBoolean(), anyBoolean()); + verify(mockListenerSupport, never()).notifyEntityOwnershipListeners(any(Entity.class), anyBoolean(), + anyBoolean(), anyBoolean()); + + // Change owner to remote member 1 for entity 1 reset(mockListenerSupport); writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, REMOTE_MEMBER_NAME1)); - verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY1, true, false); + verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY1, true, false, true); + + // Change owner to remote member 2 for entity 1 reset(mockListenerSupport); writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, REMOTE_MEMBER_NAME2)); - verify(mockListenerSupport, never()).notifyEntityOwnershipListeners(any(Entity.class), anyBoolean(), anyBoolean()); + verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY1, false, false, true); + + // Clear the owner for entity 1 + + reset(mockListenerSupport); + writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, "")); + verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY1, false, false, false); + + // Change owner to the local member for entity 1 writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, LOCAL_MEMBER_NAME)); - verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY1, false, true); + verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY1, false, true, true); + + // Change owner to remote member 2 for entity 2 reset(mockListenerSupport); writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, REMOTE_MEMBER_NAME1)); - verify(mockListenerSupport, never()).notifyEntityOwnershipListeners(any(Entity.class), anyBoolean(), anyBoolean()); + verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY2, false, false, true); + + // Change owner to the local member for entity 2 reset(mockListenerSupport); writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, LOCAL_MEMBER_NAME)); - verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY2, false, true); + verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY2, false, true, true); + + // Write local member owner for entity 2 again - expect no change reset(mockListenerSupport); writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, LOCAL_MEMBER_NAME)); - verify(mockListenerSupport, never()).notifyEntityOwnershipListeners(any(Entity.class), anyBoolean(), anyBoolean()); + verify(mockListenerSupport, never()).notifyEntityOwnershipListeners(any(Entity.class), anyBoolean(), + anyBoolean(), anyBoolean()); + + // Clear the owner for entity 2 reset(mockListenerSupport); writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, null)); - verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY2, true, false); + verify(mockListenerSupport).notifyEntityOwnershipListeners(ENTITY2, true, false, false); + + // Clear the owner for entity 2 again - expect no change + + reset(mockListenerSupport); + writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, null)); + verify(mockListenerSupport, never()).notifyEntityOwnershipListeners(any(Entity.class), anyBoolean(), + anyBoolean(), anyBoolean()); } private void writeNode(YangInstanceIdentifier path, NormalizedNode node) throws DataValidationFailedException { AbstractEntityOwnershipTest.writeNode(path, node, shardDataTree); } - - private void deleteNode(YangInstanceIdentifier path) throws DataValidationFailedException { - AbstractEntityOwnershipTest.deleteNode(path, shardDataTree); - } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerActorTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerActorTest.java index 50ed12f21b..ef8e951b18 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerActorTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerActorTest.java @@ -16,10 +16,9 @@ import akka.actor.ActorRef; import akka.testkit.TestActorRef; import org.junit.After; import org.junit.Test; -import org.opendaylight.controller.cluster.datastore.AbstractActorTest; -import org.opendaylight.controller.cluster.datastore.entityownership.messages.EntityOwnershipChanged; import org.opendaylight.controller.cluster.raft.TestActorFactory; import org.opendaylight.controller.md.sal.common.api.clustering.Entity; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -29,7 +28,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; * * @author Thomas Pantelis */ -public class EntityOwnershipListenerActorTest extends AbstractActorTest { +public class EntityOwnershipListenerActorTest extends AbstractEntityOwnershipTest { private final TestActorFactory actorFactory = new TestActorFactory(getSystem()); @After @@ -47,9 +46,10 @@ public class EntityOwnershipListenerActorTest extends AbstractActorTest { Entity entity = new Entity("test", YangInstanceIdentifier.of(QName.create("test", "id1"))); boolean wasOwner = false; boolean isOwner = true; - listenerActor.tell(new EntityOwnershipChanged(entity, wasOwner, isOwner), ActorRef.noSender()); + boolean hasOwner = true; + listenerActor.tell(new EntityOwnershipChange(entity, wasOwner, isOwner, hasOwner), ActorRef.noSender()); - verify(mockListener, timeout(5000)).ownershipChanged(entity, wasOwner, isOwner); + verify(mockListener, timeout(5000)).ownershipChanged(ownershipChange(entity, wasOwner, isOwner, hasOwner)); } @Test @@ -57,16 +57,16 @@ public class EntityOwnershipListenerActorTest extends AbstractActorTest { EntityOwnershipListener mockListener = mock(EntityOwnershipListener.class); Entity entity1 = new Entity("test", YangInstanceIdentifier.of(QName.create("test", "id1"))); - doThrow(new RuntimeException("mock")).when(mockListener).ownershipChanged(entity1, false, true); + doThrow(new RuntimeException("mock")).when(mockListener).ownershipChanged(ownershipChange(entity1, false, true, true)); Entity entity2 = new Entity("test", YangInstanceIdentifier.of(QName.create("test", "id2"))); - doNothing().when(mockListener).ownershipChanged(entity2, true, false); + doNothing().when(mockListener).ownershipChanged(ownershipChange(entity2, true, false, false)); TestActorRef listenerActor = actorFactory.createTestActor( EntityOwnershipListenerActor.props(mockListener), actorFactory.generateActorId("listener")); - listenerActor.tell(new EntityOwnershipChanged(entity1, false, true), ActorRef.noSender()); - listenerActor.tell(new EntityOwnershipChanged(entity2, true, false), ActorRef.noSender()); + listenerActor.tell(new EntityOwnershipChange(entity1, false, true, true), ActorRef.noSender()); + listenerActor.tell(new EntityOwnershipChange(entity2, true, false, false), ActorRef.noSender()); - verify(mockListener, timeout(5000)).ownershipChanged(entity2, true, false); + verify(mockListener, timeout(5000)).ownershipChanged(ownershipChange(entity2, true, false, false)); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupportTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupportTest.java index 00448751da..2394ee0bb1 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupportTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipListenerSupportTest.java @@ -8,8 +8,6 @@ package org.opendaylight.controller.cluster.datastore.entityownership; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -27,7 +25,6 @@ import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.opendaylight.controller.cluster.datastore.AbstractActorTest; import org.opendaylight.controller.cluster.raft.TestActorFactory; import org.opendaylight.controller.cluster.raft.utils.DoNothingActor; import org.opendaylight.controller.md.sal.common.api.clustering.Entity; @@ -43,7 +40,7 @@ import scala.collection.immutable.Iterable; * * @author Thomas Pantelis */ -public class EntityOwnershipListenerSupportTest extends AbstractActorTest { +public class EntityOwnershipListenerSupportTest extends AbstractEntityOwnershipTest { private final TestActorFactory actorFactory = new TestActorFactory(getSystem()); private ActorContext actorContext; @@ -83,73 +80,73 @@ public class EntityOwnershipListenerSupportTest extends AbstractActorTest { // Notify entity1 changed and verify listeners are notified. - support.notifyEntityOwnershipListeners(entity1, false, true); + support.notifyEntityOwnershipListeners(entity1, false, true, true); - verify(mockListener1, timeout(5000)).ownershipChanged(entity1, false, true); - verify(mockListener2, timeout(5000)).ownershipChanged(entity1, false, true); - verify(mockListener3, timeout(5000)).ownershipChanged(entity1, false, true); + verify(mockListener1, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, true, true)); + verify(mockListener2, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, true, true)); + verify(mockListener3, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, true, true)); assertEquals("# of listener actors", 3, actorContext.children().size()); // Notify entity2 changed and verify only mockListener1 and mockListener3 are notified. - support.notifyEntityOwnershipListeners(entity2, false, true); + support.notifyEntityOwnershipListeners(entity2, false, false, false); - verify(mockListener1, timeout(5000)).ownershipChanged(entity2, false, true); - verify(mockListener3, timeout(5000)).ownershipChanged(entity2, false, true); + verify(mockListener1, timeout(5000)).ownershipChanged(ownershipChange(entity2, false, false, false)); + verify(mockListener3, timeout(5000)).ownershipChanged(ownershipChange(entity2, false, false, false)); Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener2, never()).ownershipChanged(eq(entity2), anyBoolean(), anyBoolean()); + verify(mockListener2, never()).ownershipChanged(ownershipChange(entity2)); assertEquals("# of listener actors", 3, actorContext.children().size()); // Notify entity3 changed and verify only mockListener3 is notified. - support.notifyEntityOwnershipListeners(entity3, false, true); + support.notifyEntityOwnershipListeners(entity3, false, true, true); - verify(mockListener3, timeout(5000)).ownershipChanged(entity3, false, true); + verify(mockListener3, timeout(5000)).ownershipChanged(ownershipChange(entity3, false, true, true)); Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener1, never()).ownershipChanged(eq(entity3), anyBoolean(), anyBoolean()); - verify(mockListener2, never()).ownershipChanged(eq(entity3), anyBoolean(), anyBoolean()); + verify(mockListener1, never()).ownershipChanged(ownershipChange(entity3)); + verify(mockListener2, never()).ownershipChanged(ownershipChange(entity3)); // Notify entity4 changed and verify no listeners are notified. - support.notifyEntityOwnershipListeners(entity4, false, true); + support.notifyEntityOwnershipListeners(entity4, true, false, true); Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener1, never()).ownershipChanged(eq(entity4), anyBoolean(), anyBoolean()); - verify(mockListener2, never()).ownershipChanged(eq(entity4), anyBoolean(), anyBoolean()); - verify(mockListener3, never()).ownershipChanged(eq(entity4), anyBoolean(), anyBoolean()); + verify(mockListener1, never()).ownershipChanged(ownershipChange(entity4)); + verify(mockListener2, never()).ownershipChanged(ownershipChange(entity4)); + verify(mockListener3, never()).ownershipChanged(ownershipChange(entity4)); // Notify entity5 changed and verify no listener is notified. - support.notifyEntityOwnershipListeners(entity5, false, true); + support.notifyEntityOwnershipListeners(entity5, true, false, true); Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener1, never()).ownershipChanged(eq(entity4), anyBoolean(), anyBoolean()); - verify(mockListener2, never()).ownershipChanged(eq(entity4), anyBoolean(), anyBoolean()); - verify(mockListener3, never()).ownershipChanged(eq(entity4), anyBoolean(), anyBoolean()); + verify(mockListener1, never()).ownershipChanged(ownershipChange(entity4)); + verify(mockListener2, never()).ownershipChanged(ownershipChange(entity4)); + verify(mockListener3, never()).ownershipChanged(ownershipChange(entity4)); reset(mockListener1, mockListener2, mockListener3); // Unregister mockListener1 for entity1, issue a change and verify only mockListeners 2 & 3 are notified. support.removeEntityOwnershipListener(entity1, mockListener1); - support.notifyEntityOwnershipListeners(entity1, false, true); + support.notifyEntityOwnershipListeners(entity1, true, false, true); - verify(mockListener2, timeout(5000)).ownershipChanged(entity1, false, true); - verify(mockListener3, timeout(5000)).ownershipChanged(entity1, false, true); + verify(mockListener2, timeout(5000)).ownershipChanged(ownershipChange(entity1, true, false, true)); + verify(mockListener3, timeout(5000)).ownershipChanged(ownershipChange(entity1, true, false, true)); Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener1, never()).ownershipChanged(eq(entity1), anyBoolean(), anyBoolean()); + verify(mockListener1, never()).ownershipChanged(ownershipChange(entity1)); // Unregister mockListener3, issue a change for entity1 and verify only mockListeners2 is notified. reset(mockListener1, mockListener2, mockListener3); support.removeEntityOwnershipListener(entity1.getType(), mockListener3); - support.notifyEntityOwnershipListeners(entity1, false, true); + support.notifyEntityOwnershipListeners(entity1, false, false, false); - verify(mockListener2, timeout(5000)).ownershipChanged(entity1, false, true); + verify(mockListener2, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, false, false)); Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener1, never()).ownershipChanged(eq(entity1), anyBoolean(), anyBoolean()); - verify(mockListener3, never()).ownershipChanged(eq(entity1), anyBoolean(), anyBoolean()); + verify(mockListener1, never()).ownershipChanged(ownershipChange(entity1)); + verify(mockListener3, never()).ownershipChanged(ownershipChange(entity1)); // Completely unregister all listeners and verify their listener actors are destroyed. @@ -180,11 +177,11 @@ public class EntityOwnershipListenerSupportTest extends AbstractActorTest { support.addEntityOwnershipListener(entity1, mockListener1); - support.notifyEntityOwnershipListeners(entity1, false, true); + support.notifyEntityOwnershipListeners(entity1, false, false, true); - verify(mockListener1, timeout(5000)).ownershipChanged(entity1, false, true); - verify(mockListener2, never()).ownershipChanged(eq(entity1), anyBoolean(), anyBoolean()); - verify(mockListener3, never()).ownershipChanged(eq(entity1), anyBoolean(), anyBoolean()); + verify(mockListener1, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, false, true)); + verify(mockListener2, never()).ownershipChanged(ownershipChange(entity1)); + verify(mockListener3, never()).ownershipChanged(ownershipChange(entity1)); // Quickly register and unregister mockListener2 - expecting no exceptions. diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipShardTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipShardTest.java index 2dc754472e..c2fbfea98f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipShardTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/entityownership/EntityOwnershipShardTest.java @@ -7,13 +7,9 @@ */ package org.opendaylight.controller.cluster.datastore.entityownership; -import static org.hamcrest.CoreMatchers.either; -import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.mockito.AdditionalMatchers.or; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -40,7 +36,6 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import org.hamcrest.Matcher; import org.junit.After; import org.junit.Test; import org.opendaylight.controller.cluster.datastore.AbstractShardTest; @@ -72,6 +67,7 @@ import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply; import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper; import org.opendaylight.controller.md.sal.common.api.clustering.Entity; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidate; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -124,7 +120,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyCommittedEntityCandidate(shard, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); verifyOwner(shard, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - verify(candidate, timeout(5000)).ownershipChanged(entity, false, true); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, false, true, true)); } @Test @@ -155,7 +151,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyOwner(shard, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - verify(candidate, timeout(5000)).ownershipChanged(entity, false, true); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, false, true, true)); } @Test @@ -198,7 +194,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyOwner(shard, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - verify(candidate, timeout(5000)).ownershipChanged(entity, false, true); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, false, true, true)); } @Test @@ -237,7 +233,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyOwner(shard, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - verify(candidate, timeout(5000)).ownershipChanged(entity, false, true); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, false, true, true)); } @Test @@ -328,7 +324,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyCommittedEntityCandidate(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verify(candidate, timeout(5000)).ownershipChanged(entity, false, true); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, false, true, true)); // Unregister @@ -338,7 +334,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { kit.expectMsgClass(SuccessReply.class); verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, ""); - verify(candidate, never()).ownershipChanged(any(Entity.class), anyBoolean(), anyBoolean()); + //verify(candidate).ownershipChanged(entity, true, false, false); // Register again @@ -347,7 +343,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyCommittedEntityCandidate(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verify(candidate, timeout(5000)).ownershipChanged(entity, false, true); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, false, true, true)); } @Test @@ -375,7 +371,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyCommittedEntityCandidate(shard, ENTITY_TYPE, ENTITY_ID1, remoteMemberName1); verifyCommittedEntityCandidate(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, remoteMemberName1); - verify(candidate, never()).ownershipChanged(any(Entity.class), anyBoolean(), anyBoolean()); + verify(candidate, never()).ownershipChanged(any(EntityOwnershipChange.class)); // Add another remote candidate and verify ownership doesn't change @@ -386,7 +382,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyCommittedEntityCandidate(shard, ENTITY_TYPE, ENTITY_ID1, remoteMemberName2); Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, remoteMemberName1); - verify(candidate, never()).ownershipChanged(any(Entity.class), anyBoolean(), anyBoolean()); + verify(candidate, never()).ownershipChanged(any(EntityOwnershipChange.class)); // Remove the second remote candidate and verify ownership doesn't change @@ -396,7 +392,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyEntityCandidateRemoved(shard, ENTITY_TYPE, ENTITY_ID1, remoteMemberName2); Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, remoteMemberName1); - verify(candidate, never()).ownershipChanged(any(Entity.class), anyBoolean(), anyBoolean()); + verify(candidate, never()).ownershipChanged(any(EntityOwnershipChange.class)); // Remove the first remote candidate and verify the local candidate becomes owner @@ -405,7 +401,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyEntityCandidateRemoved(shard, ENTITY_TYPE, ENTITY_ID1, remoteMemberName1); verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verify(candidate, timeout(5000)).ownershipChanged(entity, false, true); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, false, true, true)); // Add the second remote candidate back and verify ownership doesn't change @@ -415,7 +411,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyCommittedEntityCandidate(shard, ENTITY_TYPE, ENTITY_ID1, remoteMemberName2); Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verify(candidate, never()).ownershipChanged(any(Entity.class), anyBoolean(), anyBoolean()); + verify(candidate, never()).ownershipChanged(any(EntityOwnershipChange.class)); // Unregister the local candidate and verify the second remote candidate becomes owner @@ -641,7 +637,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { shard.tell(new RegisterCandidateLocal(candidate, entity), kit.getRef()); kit.expectMsgClass(SuccessReply.class); verifyCommittedEntityCandidate(shard, entity.getType(), entity.getId(), LOCAL_MEMBER_NAME); - verify(candidate, timeout(5000)).ownershipChanged(entity, false, true); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, false, true, true)); reset(candidate); // Simulate a replicated commit from the leader to remove the local candidate that would occur after a @@ -649,12 +645,12 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { leader.tell(new PeerDown(LOCAL_MEMBER_NAME, localId.toString()), ActorRef.noSender()); - verify(candidate, timeout(5000)).ownershipChanged(entity, true, false); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, true, false, false)); // Since the the shard has a local candidate registered, it should re-add its candidate to the entity. verifyCommittedEntityCandidate(shard, entity.getType(), entity.getId(), LOCAL_MEMBER_NAME); - verify(candidate, timeout(5000)).ownershipChanged(entity, false, true); + verify(candidate, timeout(5000)).ownershipChanged(ownershipChange(entity, false, true, true)); // Unregister the local candidate and verify it's removed and no re-added. @@ -691,12 +687,12 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { shard.tell(new RegisterCandidateLocal(candidate, entity1), kit.getRef()); kit.expectMsgClass(SuccessReply.class); - verify(listener, timeout(5000)).ownershipChanged(entity1, false, true); + verify(listener, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, true, true)); shard.tell(new RegisterCandidateLocal(candidate, entity2), kit.getRef()); kit.expectMsgClass(SuccessReply.class); - verify(listener, timeout(5000)).ownershipChanged(entity2, false, true); + verify(listener, timeout(5000)).ownershipChanged(ownershipChange(entity2, false, true, true)); reset(listener); // Register another candidate for another entity type and verify listener is not notified. @@ -705,7 +701,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { kit.expectMsgClass(SuccessReply.class); Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verify(listener, never()).ownershipChanged(eq(entity4), anyBoolean(), anyBoolean()); + verify(listener, never()).ownershipChanged(ownershipChange(entity4)); // Register remote candidate for entity1 @@ -719,7 +715,7 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { shard.tell(new UnregisterCandidateLocal(candidate, entity1), kit.getRef()); kit.expectMsgClass(SuccessReply.class); - verify(listener, timeout(5000)).ownershipChanged(entity1, true, false); + verify(listener, timeout(5000)).ownershipChanged(ownershipChange(entity1, true, false, true)); reset(listener); // Unregister the listener, add a candidate for entity3 and verify listener isn't notified @@ -732,22 +728,22 @@ public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { verifyOwner(shard, ENTITY_TYPE, entity3.getId(), LOCAL_MEMBER_NAME); Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verify(listener, never()).ownershipChanged(any(Entity.class), anyBoolean(), anyBoolean()); + verify(listener, never()).ownershipChanged(any(EntityOwnershipChange.class)); - // Re-register the listener and verify it gets notified of current locally owned entities + // Re-register the listener and verify it gets notified of currently owned entities reset(listener, candidate); shard.tell(new RegisterListenerLocal(listener, ENTITY_TYPE), kit.getRef()); kit.expectMsgClass(SuccessReply.class); - Matcher entityMatcher = either(equalTo(entity2)).or(equalTo(entity3)); - verify(listener, timeout(5000).times(2)).ownershipChanged(argThat(entityMatcher), eq(false), eq(true)); + verify(listener, timeout(5000).times(2)).ownershipChanged(or(ownershipChange(entity2, false, true, true), + ownershipChange(entity3, false, true, true))); Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(listener, never()).ownershipChanged(eq(entity4), anyBoolean(), anyBoolean()); - verify(listener, never()).ownershipChanged(eq(entity1), anyBoolean(), anyBoolean()); - verify(candidate, never()).ownershipChanged(eq(entity2), anyBoolean(), anyBoolean()); - verify(candidate, never()).ownershipChanged(eq(entity3), anyBoolean(), anyBoolean()); + verify(listener, never()).ownershipChanged(ownershipChange(entity4)); + verify(listener, never()).ownershipChanged(ownershipChange(entity1)); + verify(candidate, never()).ownershipChanged(ownershipChange(entity2)); + verify(candidate, never()).ownershipChanged(ownershipChange(entity3)); } private void commitModification(TestActorRef shard, NormalizedNode node, diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarProvider.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarProvider.java index 87aa45c808..d0b47aebdc 100644 --- a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarProvider.java +++ b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarProvider.java @@ -18,6 +18,7 @@ import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException; import org.opendaylight.controller.md.sal.common.api.clustering.Entity; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidate; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; @@ -173,8 +174,8 @@ public class CarProvider implements CarService { private static class CarEntityOwnershipCandidate implements EntityOwnershipCandidate { @Override - public void ownershipChanged(Entity entity, boolean wasOwner, boolean isOwner) { - LOG.info("ownershipChanged: entity: {}, wasOwner: {}, isOwner: ()", entity, wasOwner, isOwner); + public void ownershipChanged(EntityOwnershipChange ownershipChange) { + LOG.info("ownershipChanged: {}", ownershipChange); } } }