From: Robert Varga Date: Wed, 30 Jun 2021 20:16:56 +0000 (+0200) Subject: Remove sal-distrubited-eos X-Git-Tag: v4.0.0~17 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=118c8b39d7bece6c3dc4adb8e27401a325b69692 Remove sal-distrubited-eos There are no more packaging references to datastore-based entity ownership. Remove the implementation. JIRA: CONTROLLER-1982 Change-Id: Ia6a96a1853e0f173d7af559c76a06e9dfadc540e Signed-off-by: Robert Varga --- diff --git a/artifacts/pom.xml b/artifacts/pom.xml index 6e83e398fe..c02b567ecd 100644 --- a/artifacts/pom.xml +++ b/artifacts/pom.xml @@ -82,11 +82,6 @@ sal-distributed-datastore ${project.version} - - ${project.groupId} - sal-distributed-eos - ${project.version} - ${project.groupId} sal-remoterpc-connector diff --git a/opendaylight/md-sal/pom.xml b/opendaylight/md-sal/pom.xml index 6b63828472..b430b527ef 100644 --- a/opendaylight/md-sal/pom.xml +++ b/opendaylight/md-sal/pom.xml @@ -46,7 +46,8 @@ sal-dummy-distributed-datastore sal-cluster-admin-api sal-cluster-admin-impl - sal-distributed-eos + + eos-dom-akka diff --git a/opendaylight/md-sal/sal-distributed-eos/pom.xml b/opendaylight/md-sal/sal-distributed-eos/pom.xml deleted file mode 100644 index ac855a60d6..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - 4.0.0 - - org.opendaylight.controller - mdsal-parent - 4.0.0-SNAPSHOT - ../parent - - - sal-distributed-eos - bundle - - - - org.opendaylight.mdsal - mdsal-eos-dom-api - - - - org.osgi - osgi.cmpn - - - - org.opendaylight.controller - sal-distributed-datastore - - - - commons-lang - commons-lang - test - - - com.typesafe.akka - akka-testkit_2.13 - - - org.opendaylight.controller - sal-akka-raft-example - test - - - org.opendaylight.controller - sal-akka-raft - test-jar - test - - - org.opendaylight.controller - sal-distributed-datastore - ${project.version} - test-jar - test - - - org.slf4j - slf4j-simple - test - - - org.opendaylight.yangtools - yang-test-util - - - - - scm:git:http://git.opendaylight.org/gerrit/controller.git - scm:git:ssh://git.opendaylight.org:29418/controller.git - HEAD - https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Architecture:Clustering - - diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/AbstractEntityOwnerChangeListener.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/AbstractEntityOwnerChangeListener.java deleted file mode 100644 index eb81e992d0..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/AbstractEntityOwnerChangeListener.java +++ /dev/null @@ -1,33 +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.entityownership; - -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNERS_PATH; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNER_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_QNAME; - -import java.util.Optional; -import org.opendaylight.controller.cluster.datastore.ShardDataTree; -import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener; -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.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; - -public abstract class AbstractEntityOwnerChangeListener implements DOMDataTreeChangeListener { - private static final YangInstanceIdentifier EOS_PATH = YangInstanceIdentifier.builder(ENTITY_OWNERS_PATH) - .node(EntityType.QNAME).node(EntityType.QNAME).node(ENTITY_QNAME).node(ENTITY_QNAME) - .node(ENTITY_OWNER_QNAME).build(); - - void init(final ShardDataTree shardDataTree) { - shardDataTree.registerTreeChangeListener(EOS_PATH, this, Optional.empty(), noop -> { /* NOOP */ }); - } - - protected static String extractOwner(final LeafNode ownerLeaf) { - return ownerLeaf.body().toString(); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/CandidateListChangeListener.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/CandidateListChangeListener.java deleted file mode 100644 index d8df830d00..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/CandidateListChangeListener.java +++ /dev/null @@ -1,128 +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.entityownership; - -import static java.util.Objects.requireNonNull; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.CANDIDATE_NAME_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_ID_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNERS_PATH; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_QNAME; - -import akka.actor.ActorRef; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import org.opendaylight.controller.cluster.datastore.ShardDataTree; -import org.opendaylight.controller.cluster.entityownership.messages.CandidateAdded; -import org.opendaylight.controller.cluster.entityownership.messages.CandidateRemoved; -import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener; -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; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Listens for candidate entries added/removed and notifies the EntityOwnershipShard appropriately. - * - * @author Moiz Raja - * @author Thomas Pantelis - */ -class CandidateListChangeListener implements DOMDataTreeChangeListener { - private static final Logger LOG = LoggerFactory.getLogger(CandidateListChangeListener.class); - - private final String logId; - private final ActorRef shard; - private final Map> currentCandidates = new HashMap<>(); - - CandidateListChangeListener(final ActorRef shard, final String logId) { - this.shard = requireNonNull(shard, "shard should not be null"); - this.logId = logId; - } - - void init(final ShardDataTree shardDataTree) { - shardDataTree.registerTreeChangeListener(YangInstanceIdentifier.builder(ENTITY_OWNERS_PATH) - .node(EntityType.QNAME).node(EntityType.QNAME).node(ENTITY_QNAME).node(ENTITY_QNAME) - .node(Candidate.QNAME).node(Candidate.QNAME).build(), this, Optional.empty(), noop -> { /* NOOP */ }); - } - - @Override - public void onInitialData() { - // No-op - } - - @Override - public void onDataTreeChanged(final Collection changes) { - for (DataTreeCandidate change: changes) { - DataTreeCandidateNode changeRoot = change.getRootNode(); - ModificationType type = changeRoot.getModificationType(); - - LOG.debug("{}: Candidate node changed: {}, {}", logId, type, change.getRootPath()); - - NodeIdentifierWithPredicates candidateKey = - (NodeIdentifierWithPredicates) change.getRootPath().getLastPathArgument(); - String candidate = candidateKey.getValue(CANDIDATE_NAME_QNAME).toString(); - - YangInstanceIdentifier entityId = extractEntityPath(change.getRootPath()); - - if (type == ModificationType.WRITE || type == ModificationType.APPEARED) { - LOG.debug("{}: Candidate {} was added for entity {}", logId, candidate, entityId); - - Collection newCandidates = addToCurrentCandidates(entityId, candidate); - shard.tell(new CandidateAdded(entityId, candidate, new ArrayList<>(newCandidates)), shard); - } else if (type == ModificationType.DELETE || type == ModificationType.DISAPPEARED) { - LOG.debug("{}: Candidate {} was removed for entity {}", logId, candidate, entityId); - - Collection newCandidates = removeFromCurrentCandidates(entityId, candidate); - shard.tell(new CandidateRemoved(entityId, candidate, new ArrayList<>(newCandidates)), shard); - } - } - } - - private Collection addToCurrentCandidates(final YangInstanceIdentifier entityId, - final String newCandidate) { - Collection candidates = currentCandidates.computeIfAbsent(entityId, k -> new LinkedHashSet<>()); - candidates.add(newCandidate); - return candidates; - } - - private Collection removeFromCurrentCandidates(final YangInstanceIdentifier entityId, - final String candidateToRemove) { - Collection candidates = currentCandidates.get(entityId); - if (candidates != null) { - candidates.remove(candidateToRemove); - return candidates; - } - - // Shouldn't happen - return Collections.emptyList(); - } - - private static YangInstanceIdentifier extractEntityPath(final YangInstanceIdentifier candidatePath) { - List newPathArgs = new ArrayList<>(); - for (PathArgument pathArg: candidatePath.getPathArguments()) { - newPathArgs.add(pathArg); - if (pathArg instanceof NodeIdentifierWithPredicates - && ENTITY_ID_QNAME.equals(((NodeIdentifierWithPredicates) pathArg).keySet().iterator().next())) { - break; - } - } - - return YangInstanceIdentifier.create(newPathArgs); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipCandidateRegistration.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipCandidateRegistration.java deleted file mode 100644 index 4888f6ed76..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipCandidateRegistration.java +++ /dev/null @@ -1,33 +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.entityownership; - -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration; -import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; - -/** - * Implementation of EntityOwnershipCandidateRegistration. - * - * @author Thomas Pantelis - */ -class DistributedEntityOwnershipCandidateRegistration extends AbstractObjectRegistration - implements DOMEntityOwnershipCandidateRegistration { - private final DistributedEntityOwnershipService service; - - DistributedEntityOwnershipCandidateRegistration(final DOMEntity entity, - final DistributedEntityOwnershipService service) { - super(entity); - this.service = service; - } - - @Override - protected void removeRegistration() { - service.unregisterCandidate(getInstance()); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipListenerRegistration.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipListenerRegistration.java deleted file mode 100644 index bb4e31c7c8..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipListenerRegistration.java +++ /dev/null @@ -1,48 +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.entityownership; - -import static java.util.Objects.requireNonNull; - -import com.google.common.base.MoreObjects.ToStringHelper; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration; -import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; - -/** - * Implementation of EntityOwnershipListenerRegistration. - * - * @author Thomas Pantelis - */ -class DistributedEntityOwnershipListenerRegistration extends AbstractObjectRegistration - implements DOMEntityOwnershipListenerRegistration { - private final DistributedEntityOwnershipService service; - private final String entityType; - - DistributedEntityOwnershipListenerRegistration(final DOMEntityOwnershipListener listener, final String entityType, - final DistributedEntityOwnershipService service) { - super(listener); - this.entityType = requireNonNull(entityType, "entityType cannot be null"); - this.service = requireNonNull(service, "DOMEntityOwnershipListener cannot be null"); - } - - @Override - protected void removeRegistration() { - service.unregisterListener(getEntityType(), getInstance()); - } - - @Override - public String getEntityType() { - return entityType; - } - - @Override - protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) { - return toStringHelper.add("entityType", entityType); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipService.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipService.java deleted file mode 100644 index 5a519d4289..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipService.java +++ /dev/null @@ -1,264 +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.entityownership; - -import static java.util.Objects.requireNonNull; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.CANDIDATE_NODE_ID; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNER_NODE_ID; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityPath; - -import akka.actor.ActorRef; -import akka.dispatch.OnComplete; -import akka.pattern.Patterns; -import akka.util.Timeout; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Strings; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import java.util.Collection; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import org.opendaylight.controller.cluster.access.concepts.MemberName; -import org.opendaylight.controller.cluster.datastore.config.Configuration; -import org.opendaylight.controller.cluster.datastore.config.ModuleShardConfiguration; -import org.opendaylight.controller.cluster.datastore.messages.CreateShard; -import org.opendaylight.controller.cluster.datastore.messages.GetShardDataTree; -import org.opendaylight.controller.cluster.datastore.shardstrategy.ModuleShardStrategy; -import org.opendaylight.controller.cluster.datastore.utils.ActorUtils; -import org.opendaylight.controller.cluster.entityownership.messages.RegisterCandidateLocal; -import org.opendaylight.controller.cluster.entityownership.messages.RegisterListenerLocal; -import org.opendaylight.controller.cluster.entityownership.messages.UnregisterCandidateLocal; -import org.opendaylight.controller.cluster.entityownership.messages.UnregisterListenerLocal; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.EntityOwnerSelectionStrategyConfig; -import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException; -import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.clustering.entity.owners.rev150804.EntityOwners; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -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.DataTree; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import scala.concurrent.Await; -import scala.concurrent.Future; -import scala.concurrent.duration.Duration; - -/** - * The distributed implementation of the EntityOwnershipService. - * - * @author Thomas Pantelis - */ -public class DistributedEntityOwnershipService implements DOMEntityOwnershipService, AutoCloseable { - @VisibleForTesting - static final String ENTITY_OWNERSHIP_SHARD_NAME = "entity-ownership"; - - private static final Logger LOG = LoggerFactory.getLogger(DistributedEntityOwnershipService.class); - private static final Timeout MESSAGE_TIMEOUT = new Timeout(1, TimeUnit.MINUTES); - - private final ConcurrentMap registeredEntities = new ConcurrentHashMap<>(); - private final ActorUtils context; - - private volatile ActorRef localEntityOwnershipShard; - private volatile DataTree localEntityOwnershipShardDataTree; - - DistributedEntityOwnershipService(final ActorUtils context) { - this.context = requireNonNull(context); - } - - public static DistributedEntityOwnershipService start(final ActorUtils context, - final EntityOwnerSelectionStrategyConfig strategyConfig) { - ActorRef shardManagerActor = context.getShardManager(); - - Configuration configuration = context.getConfiguration(); - Collection entityOwnersMemberNames = configuration.getUniqueMemberNamesForAllShards(); - CreateShard createShard = new CreateShard(new ModuleShardConfiguration(EntityOwners.QNAME.getNamespace(), - "entity-owners", ENTITY_OWNERSHIP_SHARD_NAME, ModuleShardStrategy.NAME, entityOwnersMemberNames), - newShardBuilder(context, strategyConfig), null); - - Future createFuture = context.executeOperationAsync(shardManagerActor, createShard, MESSAGE_TIMEOUT); - createFuture.onComplete(new OnComplete<>() { - @Override - public void onComplete(final Throwable failure, final Object response) { - if (failure != null) { - LOG.error("Failed to create {} shard", ENTITY_OWNERSHIP_SHARD_NAME, failure); - } else { - LOG.info("Successfully created {} shard", ENTITY_OWNERSHIP_SHARD_NAME); - } - } - }, context.getClientDispatcher()); - - return new DistributedEntityOwnershipService(context); - } - - private void executeEntityOwnershipShardOperation(final ActorRef shardActor, final Object message) { - Future future = context.executeOperationAsync(shardActor, message, MESSAGE_TIMEOUT); - future.onComplete(new OnComplete<>() { - @Override - public void onComplete(final Throwable failure, final Object response) { - if (failure != null) { - // FIXME: CONTROLLER-1904: reduce the severity to info once we have a retry mechanism - LOG.error("Error sending message {} to {}", message, shardActor, failure); - } else { - LOG.debug("{} message to {} succeeded", message, shardActor); - } - } - }, context.getClientDispatcher()); - } - - @VisibleForTesting - void executeLocalEntityOwnershipShardOperation(final Object message) { - if (localEntityOwnershipShard == null) { - Future future = context.findLocalShardAsync(ENTITY_OWNERSHIP_SHARD_NAME); - future.onComplete(new OnComplete() { - @Override - public void onComplete(final Throwable failure, final ActorRef shardActor) { - if (failure != null) { - // FIXME: CONTROLLER-1904: reduce the severity to info once we have a retry mechanism - LOG.error("Failed to find local {} shard", ENTITY_OWNERSHIP_SHARD_NAME, failure); - } else { - localEntityOwnershipShard = shardActor; - executeEntityOwnershipShardOperation(localEntityOwnershipShard, message); - } - } - }, context.getClientDispatcher()); - - } else { - executeEntityOwnershipShardOperation(localEntityOwnershipShard, message); - } - } - - @Override - public DOMEntityOwnershipCandidateRegistration registerCandidate(final DOMEntity entity) - throws CandidateAlreadyRegisteredException { - requireNonNull(entity, "entity cannot be null"); - - if (registeredEntities.putIfAbsent(entity, entity) != null) { - throw new CandidateAlreadyRegisteredException(entity); - } - - RegisterCandidateLocal registerCandidate = new RegisterCandidateLocal(entity); - - LOG.debug("Registering candidate with message: {}", registerCandidate); - - executeLocalEntityOwnershipShardOperation(registerCandidate); - return new DistributedEntityOwnershipCandidateRegistration(entity, this); - } - - void unregisterCandidate(final DOMEntity entity) { - LOG.debug("Unregistering candidate for {}", entity); - - executeLocalEntityOwnershipShardOperation(new UnregisterCandidateLocal(entity)); - registeredEntities.remove(entity); - } - - @Override - public DOMEntityOwnershipListenerRegistration registerListener(final String entityType, - final DOMEntityOwnershipListener listener) { - RegisterListenerLocal registerListener = new RegisterListenerLocal(listener, entityType); - - LOG.debug("Registering listener with message: {}", registerListener); - - executeLocalEntityOwnershipShardOperation(registerListener); - return new DistributedEntityOwnershipListenerRegistration(listener, entityType, this); - } - - @Override - public Optional getOwnershipState(final DOMEntity forEntity) { - requireNonNull(forEntity, "forEntity cannot be null"); - - DataTree dataTree = getLocalEntityOwnershipShardDataTree(); - if (dataTree == null) { - return Optional.empty(); - } - - Optional entityNode = dataTree.takeSnapshot().readNode( - entityPath(forEntity.getType(), forEntity.getIdentifier())); - if (!entityNode.isPresent()) { - return Optional.empty(); - } - - // Check if there are any candidates, if there are none we do not really have ownership state - final MapEntryNode entity = (MapEntryNode) entityNode.get(); - final Optional optionalCandidates = entity.findChildByArg(CANDIDATE_NODE_ID); - final boolean hasCandidates = optionalCandidates.isPresent() - && ((MapNode) optionalCandidates.get()).body().size() > 0; - if (!hasCandidates) { - return Optional.empty(); - } - - MemberName localMemberName = context.getCurrentMemberName(); - Optional ownerLeaf = entity.findChildByArg(ENTITY_OWNER_NODE_ID); - String owner = ownerLeaf.isPresent() ? ownerLeaf.get().body().toString() : null; - boolean hasOwner = !Strings.isNullOrEmpty(owner); - boolean isOwner = hasOwner && localMemberName.getName().equals(owner); - - return Optional.of(EntityOwnershipState.from(isOwner, hasOwner)); - } - - @Override - public boolean isCandidateRegistered(final DOMEntity entity) { - return registeredEntities.get(entity) != null; - } - - @VisibleForTesting - @SuppressWarnings("checkstyle:IllegalCatch") - @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Akka's Await.result() API contract") - DataTree getLocalEntityOwnershipShardDataTree() { - final DataTree local = localEntityOwnershipShardDataTree; - if (local != null) { - return local; - } - - if (localEntityOwnershipShard == null) { - try { - localEntityOwnershipShard = Await.result(context.findLocalShardAsync( - ENTITY_OWNERSHIP_SHARD_NAME), Duration.Inf()); - } catch (TimeoutException | InterruptedException e) { - LOG.error("Failed to find local {} shard", ENTITY_OWNERSHIP_SHARD_NAME, e); - return null; - } - } - - try { - return localEntityOwnershipShardDataTree = (DataTree) Await.result(Patterns.ask(localEntityOwnershipShard, - GetShardDataTree.INSTANCE, MESSAGE_TIMEOUT), Duration.Inf()); - } catch (TimeoutException | InterruptedException e) { - LOG.error("Failed to find local {} shard", ENTITY_OWNERSHIP_SHARD_NAME, e); - return null; - } - } - - void unregisterListener(final String entityType, final DOMEntityOwnershipListener listener) { - LOG.debug("Unregistering listener {} for entity type {}", listener, entityType); - - executeLocalEntityOwnershipShardOperation(new UnregisterListenerLocal(listener, entityType)); - } - - @Override - public void close() { - } - - private static EntityOwnershipShard.Builder newShardBuilder(final ActorUtils context, - final EntityOwnerSelectionStrategyConfig strategyConfig) { - return EntityOwnershipShard.newBuilder().localMemberName(context.getCurrentMemberName()) - .ownerSelectionStrategyConfig(strategyConfig); - } - - @VisibleForTesting - ActorRef getLocalEntityOwnershipShard() { - return localEntityOwnershipShard; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnerChangeListener.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnerChangeListener.java deleted file mode 100644 index 974b5a150d..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnerChangeListener.java +++ /dev/null @@ -1,86 +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.entityownership; - -import static com.google.common.base.Verify.verifyNotNull; -import static java.util.Objects.requireNonNull; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.createEntity; - -import com.google.common.base.Strings; -import java.util.Collection; -import java.util.Objects; -import java.util.Optional; -import org.opendaylight.controller.cluster.access.concepts.MemberName; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Listens for entity owner changes and notifies the EntityOwnershipListenerSupport appropriately. - * - * @author Thomas Pantelis - */ -class EntityOwnerChangeListener extends AbstractEntityOwnerChangeListener { - private static final Logger LOG = LoggerFactory.getLogger(EntityOwnerChangeListener.class); - - private final String localMemberName; - private final EntityOwnershipChangePublisher publisher; - - EntityOwnerChangeListener(final MemberName localMemberName, final EntityOwnershipChangePublisher publisher) { - this.localMemberName = verifyNotNull(localMemberName.getName()); - this.publisher = requireNonNull(publisher); - } - - @Override - public void onInitialData() { - // No-op - } - - @Override - public void onDataTreeChanged(final Collection changes) { - for (DataTreeCandidate change: changes) { - DataTreeCandidateNode changeRoot = change.getRootNode(); - LeafNode ownerLeaf = (LeafNode) changeRoot.getDataAfter().get(); - - LOG.debug("{}: Entity node changed: {}, {}", logId(), changeRoot.getModificationType(), - change.getRootPath()); - - String newOwner = extractOwner(ownerLeaf); - - String origOwner = null; - Optional dataBefore = changeRoot.getDataBefore(); - if (dataBefore.isPresent()) { - origOwner = extractOwner((LeafNode) changeRoot.getDataBefore().get()); - } - - LOG.debug("{}: New owner: {}, Original owner: {}", logId(), newOwner, origOwner); - - if (!Objects.equals(origOwner, newOwner)) { - boolean isOwner = localMemberName.equals(newOwner); - boolean wasOwner = localMemberName.equals(origOwner); - boolean hasOwner = !Strings.isNullOrEmpty(newOwner); - - DOMEntity entity = createEntity(change.getRootPath()); - - LOG.debug( - "{}: Calling notifyEntityOwnershipListeners: entity: {}, wasOwner: {}, isOwner: {}, hasOwner: {}", - logId(), entity, wasOwner, isOwner, hasOwner); - - publisher.notifyEntityOwnershipListeners(entity, wasOwner, isOwner, hasOwner); - } - } - } - - private String logId() { - return publisher.getLogId(); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnersModel.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnersModel.java deleted file mode 100644 index 14da8aed87..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnersModel.java +++ /dev/null @@ -1,147 +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.entityownership; - -import java.util.Map.Entry; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -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; -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; -import org.opendaylight.yangtools.yang.common.QName; -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.YangInstanceIdentifier.PathArgument; -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.MapNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUserMapNodeBuilder; - -/** - * Utility methods for entity-owners yang model. - * - * @author Thomas Pantelis - */ -public final class EntityOwnersModel { - static final QName ENTITY_QNAME = Entity.QNAME; - static final QName CANDIDATE_NAME_QNAME = QName.create(Candidate.QNAME, "name").intern(); - static final QName ENTITY_ID_QNAME = QName.create(ENTITY_QNAME, "id").intern(); - static final QName ENTITY_OWNER_QNAME = QName.create(ENTITY_QNAME, "owner").intern(); - static final QName ENTITY_TYPE_QNAME = QName.create(EntityType.QNAME, "type").intern(); - - static final NodeIdentifier ENTITY_OWNERS_NODE_ID = NodeIdentifier.create(EntityOwners.QNAME); - static final NodeIdentifier ENTITY_OWNER_NODE_ID = NodeIdentifier.create(ENTITY_OWNER_QNAME); - static final NodeIdentifier ENTITY_NODE_ID = NodeIdentifier.create(ENTITY_QNAME); - static final NodeIdentifier ENTITY_ID_NODE_ID = NodeIdentifier.create(ENTITY_ID_QNAME); - static final NodeIdentifier ENTITY_TYPE_NODE_ID = NodeIdentifier.create(ENTITY_TYPE_QNAME); - static final NodeIdentifier CANDIDATE_NODE_ID = NodeIdentifier.create(Candidate.QNAME); - static final NodeIdentifier CANDIDATE_NAME_NODE_ID = NodeIdentifier.create(CANDIDATE_NAME_QNAME); - static final YangInstanceIdentifier ENTITY_OWNERS_PATH = YangInstanceIdentifier.create(ENTITY_OWNERS_NODE_ID); - static final YangInstanceIdentifier ENTITY_TYPES_PATH = ENTITY_OWNERS_PATH.node(EntityType.QNAME).toOptimized(); - - private EntityOwnersModel() { - } - - static YangInstanceIdentifier entityPath(final String entityType, final YangInstanceIdentifier entityId) { - return YangInstanceIdentifier.builder(ENTITY_OWNERS_PATH).node(EntityType.QNAME) - .nodeWithKey(EntityType.QNAME, ENTITY_TYPE_QNAME, entityType).node(ENTITY_QNAME) - .nodeWithKey(ENTITY_QNAME, ENTITY_ID_QNAME, entityId).build(); - - } - - static YangInstanceIdentifier candidatePath(final String entityType, final YangInstanceIdentifier entityId, - final String candidateName) { - return YangInstanceIdentifier.builder(ENTITY_OWNERS_PATH).node(EntityType.QNAME) - .nodeWithKey(EntityType.QNAME, ENTITY_TYPE_QNAME, entityType).node(ENTITY_QNAME) - .nodeWithKey(ENTITY_QNAME, ENTITY_ID_QNAME, entityId).node(Candidate.QNAME) - .nodeWithKey(Candidate.QNAME, CANDIDATE_NAME_QNAME, candidateName).build(); - } - - static YangInstanceIdentifier candidatePath(final YangInstanceIdentifier entityPath, final String candidateName) { - return YangInstanceIdentifier.builder(entityPath).node(Candidate.QNAME).nodeWithKey( - Candidate.QNAME, CANDIDATE_NAME_QNAME, candidateName).build(); - } - - static NodeIdentifierWithPredicates candidateNodeKey(final String candidateName) { - return NodeIdentifierWithPredicates.of(Candidate.QNAME, CANDIDATE_NAME_QNAME, candidateName); - } - - static NormalizedNode entityOwnersWithCandidate(final String entityType, - final YangInstanceIdentifier entityId, final String candidateName) { - return entityOwnersWithEntityTypeEntry(entityTypeEntryWithEntityEntry(entityType, - entityEntryWithCandidateEntry(entityId, candidateName))); - } - - static ContainerNode entityOwnersWithEntityTypeEntry(final MapEntryNode entityTypeNode) { - return ImmutableContainerNodeBuilder.create().withNodeIdentifier( - ENTITY_OWNERS_NODE_ID).addChild(ImmutableNodes.mapNodeBuilder(EntityType.QNAME) - .addChild(entityTypeNode).build()).build(); - } - - static MapEntryNode entityTypeEntryWithEntityEntry(final String entityType, final MapEntryNode entityNode) { - return ImmutableNodes.mapEntryBuilder(EntityType.QNAME, - ENTITY_TYPE_QNAME, entityType).addChild(ImmutableNodes.mapNodeBuilder( - ENTITY_QNAME).addChild(entityNode).build()).build(); - } - - static MapEntryNode entityEntryWithCandidateEntry(final YangInstanceIdentifier entityId, - final String candidateName) { - return ImmutableNodes.mapEntryBuilder(ENTITY_QNAME, ENTITY_ID_QNAME, entityId).addChild( - candidateEntry(candidateName)).build(); - } - - static MapNode candidateEntry(final String candidateName) { - return ImmutableUserMapNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(Candidate.QNAME)) - .addChild(candidateMapEntry(candidateName)).build(); - } - - static MapEntryNode candidateMapEntry(final String candidateName) { - return ImmutableNodes.mapEntry(Candidate.QNAME, CANDIDATE_NAME_QNAME, candidateName); - } - - static MapEntryNode entityEntryWithOwner(final YangInstanceIdentifier entityId, final String owner) { - return ImmutableNodes.mapEntryBuilder(ENTITY_QNAME, ENTITY_ID_QNAME, entityId) - .addChild(ImmutableNodes.leafNode(ENTITY_OWNER_QNAME, owner != null ? owner : "")) - .build(); - } - - public static String entityTypeFromEntityPath(final YangInstanceIdentifier entityPath) { - YangInstanceIdentifier parent = entityPath; - while (!parent.isEmpty()) { - if (EntityType.QNAME.equals(parent.getLastPathArgument().getNodeType())) { - YangInstanceIdentifier.NodeIdentifierWithPredicates entityTypeLastPathArgument = - (YangInstanceIdentifier.NodeIdentifierWithPredicates) parent.getLastPathArgument(); - return (String) entityTypeLastPathArgument.getValue(ENTITY_TYPE_QNAME); - } - parent = parent.getParent(); - } - return null; - } - - static DOMEntity createEntity(final YangInstanceIdentifier entityPath) { - String entityType = null; - YangInstanceIdentifier entityId = null; - for (PathArgument pathArg: entityPath.getPathArguments()) { - if (pathArg instanceof NodeIdentifierWithPredicates) { - NodeIdentifierWithPredicates nodeKey = (NodeIdentifierWithPredicates) pathArg; - Entry key = nodeKey.entrySet().iterator().next(); - if (ENTITY_TYPE_QNAME.equals(key.getKey())) { - entityType = key.getValue().toString(); - } else if (ENTITY_ID_QNAME.equals(key.getKey())) { - entityId = (YangInstanceIdentifier) key.getValue(); - } - } - } - - return new DOMEntity(entityType, entityId); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipChangePublisher.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipChangePublisher.java deleted file mode 100644 index 18f7ba152e..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipChangePublisher.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2017 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.entityownership; - -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; - -/** - * Abstract base for notifying EntityOwnershipListeners. - * - * @author Thomas Pantelis - */ -abstract class EntityOwnershipChangePublisher { - abstract void notifyEntityOwnershipListeners(DOMEntity entity, boolean wasOwner, boolean isOwner, boolean hasOwner); - - abstract String getLogId(); -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerActor.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerActor.java deleted file mode 100644 index 40e278abc6..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerActor.java +++ /dev/null @@ -1,71 +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.entityownership; - -import static java.util.Objects.requireNonNull; - -import akka.actor.Props; -import akka.japi.Creator; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; - -/** - * An actor which is responsible for notifying an EntityOwnershipListener of changes. - * - * @author Thomas Pantelis - */ -final class EntityOwnershipListenerActor extends AbstractUntypedActor { - private final DOMEntityOwnershipListener listener; - - private EntityOwnershipListenerActor(final DOMEntityOwnershipListener listener) { - this.listener = listener; - } - - @Override - protected void handleReceive(final Object message) { - if (message instanceof DOMEntityOwnershipChange) { - onEntityOwnershipChanged((DOMEntityOwnershipChange)message); - } else { - unknownMessage(message); - } - } - - @SuppressWarnings("checkstyle:IllegalCatch") - private void onEntityOwnershipChanged(final DOMEntityOwnershipChange change) { - LOG.debug("Notifying EntityOwnershipListener {}: {}", listener, change); - - try { - listener.ownershipChanged(change); - } catch (Exception e) { - LOG.error("Error notifying listener {}", listener, e); - } - } - - static Props props(final DOMEntityOwnershipListener listener) { - return Props.create(EntityOwnershipListenerActor.class, new EntityOwnershipListenerCreator(listener)); - } - - private static final class EntityOwnershipListenerCreator implements Creator { - private static final long serialVersionUID = 1L; - - @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "This field is not Serializable but we don't " - + "create remote instances of this actor and thus don't need it to be Serializable.") - private final DOMEntityOwnershipListener listener; - - EntityOwnershipListenerCreator(final DOMEntityOwnershipListener listener) { - this.listener = requireNonNull(listener); - } - - @Override - public EntityOwnershipListenerActor create() { - return new EntityOwnershipListenerActor(listener); - } - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerSupport.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerSupport.java deleted file mode 100644 index c6a710f991..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerSupport.java +++ /dev/null @@ -1,185 +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.entityownership; - -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.actor.PoisonPill; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Multimap; -import java.util.Collection; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.stream.Collectors; -import org.checkerframework.checker.lock.qual.GuardedBy; -import org.checkerframework.checker.lock.qual.Holding; -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.mdsal.eos.dom.api.DOMEntityOwnershipListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Manages EntityOwnershipListener registrations and notifications for the EntityOwnershipShard. This class is - * thread-safe. - * - * @author Thomas Pantelis - */ -class EntityOwnershipListenerSupport extends EntityOwnershipChangePublisher { - private static final Logger LOG = LoggerFactory.getLogger(EntityOwnershipListenerSupport.class); - - private final String logId; - private final ActorContext actorContext; - private final ReadWriteLock listenerLock = new ReentrantReadWriteLock(); - - @GuardedBy("listenerLock") - private final Map listenerActorMap = new IdentityHashMap<>(); - - @GuardedBy("listenerLock") - private final Multimap entityTypeListenerMap = HashMultimap.create(); - - private volatile boolean inJeopardy = false; - - EntityOwnershipListenerSupport(final ActorContext actorContext, final String logId) { - this.actorContext = actorContext; - this.logId = logId; - } - - @Override - String getLogId() { - return logId; - } - - /** - * Set the in-jeopardy flag and indicate its previous state. - * - * @param inJeopardy new value of the in-jeopardy flag - * @return Previous value of the flag. - */ - @SuppressWarnings("checkstyle:hiddenField") - boolean setInJeopardy(final boolean inJeopardy) { - final boolean wasInJeopardy = this.inJeopardy; - this.inJeopardy = inJeopardy; - return wasInJeopardy; - } - - void addEntityOwnershipListener(final String entityType, final DOMEntityOwnershipListener listener) { - LOG.debug("{}: Adding EntityOwnershipListener {} for entity type {}", logId, listener, entityType); - - listenerLock.writeLock().lock(); - try { - if (entityTypeListenerMap.put(entityType, listener)) { - ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); - if (listenerEntry == null) { - listenerActorMap.put(listener, new ListenerActorRefEntry(listener)); - } else { - listenerEntry.referenceCount++; - } - } - } finally { - listenerLock.writeLock().unlock(); - } - } - - void removeEntityOwnershipListener(final String entityType, final DOMEntityOwnershipListener listener) { - LOG.debug("{}: Removing EntityOwnershipListener {} for entity type {}", logId, listener, entityType); - - listenerLock.writeLock().lock(); - try { - if (entityTypeListenerMap.remove(entityType, listener)) { - ListenerActorRefEntry listenerEntry = listenerActorMap.get(listener); - - LOG.debug("{}: Found {}", logId, listenerEntry); - - listenerEntry.referenceCount--; - if (listenerEntry.referenceCount <= 0) { - listenerActorMap.remove(listener); - - if (listenerEntry.actorRef != null) { - LOG.debug("Killing EntityOwnershipListenerActor {}", listenerEntry.actorRef); - listenerEntry.actorRef.tell(PoisonPill.getInstance(), ActorRef.noSender()); - } - } - } - } finally { - listenerLock.writeLock().unlock(); - } - } - - @Override - void notifyEntityOwnershipListeners(final DOMEntity entity, final boolean wasOwner, final boolean isOwner, - final boolean hasOwner) { - listenerLock.readLock().lock(); - try { - Collection listeners = entityTypeListenerMap.get(entity.getType()); - if (!listeners.isEmpty()) { - notifyListeners(entity, wasOwner, isOwner, hasOwner, - listeners.stream().map(listenerActorMap::get).collect(Collectors.toList())); - } - } finally { - listenerLock.readLock().unlock(); - } - } - - void notifyEntityOwnershipListener(final DOMEntity entity, final boolean wasOwner, final boolean isOwner, - final boolean hasOwner, final DOMEntityOwnershipListener listener) { - listenerLock.readLock().lock(); - try { - notifyListeners(entity, wasOwner, isOwner, hasOwner, ImmutableList.of(listenerActorMap.get(listener))); - } finally { - listenerLock.readLock().unlock(); - } - } - - @Holding("listenerLock") - private void notifyListeners(final DOMEntity entity, final boolean wasOwner, final boolean isOwner, - final boolean hasOwner, final Collection listenerEntries) { - DOMEntityOwnershipChange changed = new DOMEntityOwnershipChange(entity, - EntityOwnershipChangeState.from(wasOwner, isOwner, hasOwner), inJeopardy); - for (ListenerActorRefEntry entry: listenerEntries) { - ActorRef listenerActor = entry.actorFor(); - - LOG.debug("{}: Notifying EntityOwnershipListenerActor {} with {}", logId, listenerActor, changed); - - listenerActor.tell(changed, ActorRef.noSender()); - } - } - - private class ListenerActorRefEntry { - final DOMEntityOwnershipListener listener; - - @GuardedBy("listenerLock") - ActorRef actorRef; - - @GuardedBy("listenerLock") - int referenceCount = 1; - - ListenerActorRefEntry(final DOMEntityOwnershipListener listener) { - this.listener = listener; - } - - ActorRef actorFor() { - if (actorRef == null) { - actorRef = actorContext.actorOf(EntityOwnershipListenerActor.props(listener)); - - LOG.debug("{}: Created EntityOwnershipListenerActor {} for listener {}", logId, actorRef, listener); - } - - return actorRef; - } - - @Override - public String toString() { - return "ListenerActorRefEntry [actorRef=" + actorRef + ", referenceCount=" + referenceCount + "]"; - } - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipShard.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipShard.java deleted file mode 100644 index 64d497b62f..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipShard.java +++ /dev/null @@ -1,712 +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.entityownership; - -import static java.util.Objects.requireNonNull; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.CANDIDATE_NAME_NODE_ID; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.CANDIDATE_NODE_ID; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_ID_NODE_ID; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_ID_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_NODE_ID; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNERS_PATH; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNER_NODE_ID; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNER_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_TYPES_PATH; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_TYPE_NODE_ID; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_TYPE_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.candidateNodeKey; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.candidatePath; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityOwnersWithCandidate; - -import akka.actor.ActorRef; -import akka.actor.ActorSelection; -import akka.actor.Cancellable; -import akka.cluster.Cluster; -import akka.cluster.ClusterEvent.CurrentClusterState; -import akka.cluster.Member; -import akka.cluster.MemberStatus; -import akka.pattern.Patterns; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import org.opendaylight.controller.cluster.access.concepts.MemberName; -import org.opendaylight.controller.cluster.datastore.DatastoreContext; -import org.opendaylight.controller.cluster.datastore.Shard; -import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; -import org.opendaylight.controller.cluster.datastore.messages.BatchedModifications; -import org.opendaylight.controller.cluster.datastore.messages.PeerDown; -import org.opendaylight.controller.cluster.datastore.messages.PeerUp; -import org.opendaylight.controller.cluster.datastore.messages.SuccessReply; -import org.opendaylight.controller.cluster.datastore.modification.DeleteModification; -import org.opendaylight.controller.cluster.datastore.modification.MergeModification; -import org.opendaylight.controller.cluster.datastore.modification.Modification; -import org.opendaylight.controller.cluster.datastore.modification.WriteModification; -import org.opendaylight.controller.cluster.entityownership.messages.CandidateAdded; -import org.opendaylight.controller.cluster.entityownership.messages.CandidateRemoved; -import org.opendaylight.controller.cluster.entityownership.messages.RegisterCandidateLocal; -import org.opendaylight.controller.cluster.entityownership.messages.RegisterListenerLocal; -import org.opendaylight.controller.cluster.entityownership.messages.RemoveAllCandidates; -import org.opendaylight.controller.cluster.entityownership.messages.SelectOwner; -import org.opendaylight.controller.cluster.entityownership.messages.UnregisterCandidateLocal; -import org.opendaylight.controller.cluster.entityownership.messages.UnregisterListenerLocal; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.EntityOwnerSelectionStrategy; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.EntityOwnerSelectionStrategyConfig; -import org.opendaylight.controller.cluster.raft.RaftState; -import org.opendaylight.controller.cluster.raft.VotingState; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -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.impl.schema.ImmutableNodes; -import scala.concurrent.Future; -import scala.concurrent.duration.FiniteDuration; - -/** - * Special Shard for EntityOwnership. - * - * @author Thomas Pantelis - */ -class EntityOwnershipShard extends Shard { - private final MemberName localMemberName; - private final EntityOwnershipShardCommitCoordinator commitCoordinator; - private final EntityOwnershipListenerSupport listenerSupport; - private final Set downPeerMemberNames = new HashSet<>(); - private final EntityOwnerSelectionStrategyConfig strategyConfig; - private final Map entityToScheduledOwnershipTask = new HashMap<>(); - private final EntityOwnershipStatistics entityOwnershipStatistics; - private boolean removeAllInitialCandidates = true; - - protected EntityOwnershipShard(final Builder builder) { - super(builder); - this.localMemberName = builder.localMemberName; - this.commitCoordinator = new EntityOwnershipShardCommitCoordinator(builder.localMemberName, LOG); - this.listenerSupport = new EntityOwnershipListenerSupport(getContext(), persistenceId()); - this.strategyConfig = builder.ownerSelectionStrategyConfig; - this.entityOwnershipStatistics = new EntityOwnershipStatistics(); - this.entityOwnershipStatistics.init(getDataStore()); - } - - private static DatastoreContext noPersistenceDatastoreContext(final DatastoreContext datastoreContext) { - return DatastoreContext.newBuilderFrom(datastoreContext).persistent(false).build(); - } - - @Override - protected void onDatastoreContext(final DatastoreContext context) { - super.onDatastoreContext(noPersistenceDatastoreContext(context)); - } - - @Override - protected void onRecoveryComplete() { - super.onRecoveryComplete(); - - new CandidateListChangeListener(getSelf(), persistenceId()).init(getDataStore()); - new EntityOwnerChangeListener(localMemberName, listenerSupport).init(getDataStore()); - } - - @Override - public void handleNonRaftCommand(final Object message) { - if (message instanceof RegisterCandidateLocal) { - onRegisterCandidateLocal((RegisterCandidateLocal) message); - } else if (message instanceof UnregisterCandidateLocal) { - onUnregisterCandidateLocal((UnregisterCandidateLocal) message); - } else if (message instanceof CandidateAdded) { - onCandidateAdded((CandidateAdded) message); - } else if (message instanceof CandidateRemoved) { - onCandidateRemoved((CandidateRemoved) message); - } else if (message instanceof PeerDown) { - onPeerDown((PeerDown) message); - } else if (message instanceof PeerUp) { - onPeerUp((PeerUp) message); - } else if (message instanceof RegisterListenerLocal) { - onRegisterListenerLocal((RegisterListenerLocal) message); - } else if (message instanceof UnregisterListenerLocal) { - onUnregisterListenerLocal((UnregisterListenerLocal) message); - } else if (message instanceof SelectOwner) { - onSelectOwner((SelectOwner) message); - } else if (message instanceof RemoveAllCandidates) { - onRemoveAllCandidates((RemoveAllCandidates) message); - } else if (!commitCoordinator.handleMessage(message, this)) { - super.handleNonRaftCommand(message); - } - } - - private void onRemoveAllCandidates(final RemoveAllCandidates message) { - LOG.debug("{}: onRemoveAllCandidates: {}", persistenceId(), message); - - removeCandidateFromEntities(message.getMemberName()); - } - - private void onSelectOwner(final SelectOwner selectOwner) { - LOG.debug("{}: onSelectOwner: {}", persistenceId(), selectOwner); - - String currentOwner = getCurrentOwner(selectOwner.getEntityPath()); - if (Strings.isNullOrEmpty(currentOwner)) { - writeNewOwner(selectOwner.getEntityPath(), newOwner(currentOwner, selectOwner.getAllCandidates(), - selectOwner.getOwnerSelectionStrategy())); - - Cancellable cancellable = entityToScheduledOwnershipTask.get(selectOwner.getEntityPath()); - if (cancellable != null) { - if (!cancellable.isCancelled()) { - cancellable.cancel(); - } - entityToScheduledOwnershipTask.remove(selectOwner.getEntityPath()); - } - } - } - - private void onRegisterCandidateLocal(final RegisterCandidateLocal registerCandidate) { - LOG.debug("{}: onRegisterCandidateLocal: {}", persistenceId(), registerCandidate); - - NormalizedNode entityOwners = entityOwnersWithCandidate(registerCandidate.getEntity().getType(), - registerCandidate.getEntity().getIdentifier(), localMemberName.getName()); - commitCoordinator.commitModification(new MergeModification(ENTITY_OWNERS_PATH, entityOwners), this); - - getSender().tell(SuccessReply.INSTANCE, getSelf()); - } - - private void onUnregisterCandidateLocal(final UnregisterCandidateLocal unregisterCandidate) { - LOG.debug("{}: onUnregisterCandidateLocal: {}", persistenceId(), unregisterCandidate); - - DOMEntity entity = unregisterCandidate.getEntity(); - YangInstanceIdentifier candidatePath = candidatePath(entity.getType(), entity.getIdentifier(), - localMemberName.getName()); - commitCoordinator.commitModification(new DeleteModification(candidatePath), this); - - getSender().tell(SuccessReply.INSTANCE, getSelf()); - } - - private void onRegisterListenerLocal(final RegisterListenerLocal registerListener) { - LOG.debug("{}: onRegisterListenerLocal: {}", persistenceId(), registerListener); - - listenerSupport.addEntityOwnershipListener(registerListener.getEntityType(), registerListener.getListener()); - - getSender().tell(SuccessReply.INSTANCE, getSelf()); - - searchForEntities((entityTypeNode, entityNode) -> { - String entityType = entityTypeNode.findChildByArg(ENTITY_TYPE_NODE_ID) - .map(child -> child.body().toString()) - .orElse(null); - if (registerListener.getEntityType().equals(entityType)) { - final boolean hasOwner; - final boolean isOwner; - - Optional possibleOwner = entityNode.findChildByArg(ENTITY_OWNER_NODE_ID); - if (possibleOwner.isPresent()) { - isOwner = localMemberName.getName().equals(possibleOwner.get().body().toString()); - hasOwner = true; - } else { - isOwner = false; - hasOwner = false; - } - - DOMEntity entity = new DOMEntity(entityType, - (YangInstanceIdentifier) entityNode.findChildByArg(ENTITY_ID_NODE_ID).get().body()); - - listenerSupport.notifyEntityOwnershipListener(entity, false, isOwner, hasOwner, - registerListener.getListener()); - } - }); - } - - private void onUnregisterListenerLocal(final UnregisterListenerLocal unregisterListener) { - LOG.debug("{}: onUnregisterListenerLocal: {}", persistenceId(), unregisterListener); - - listenerSupport.removeEntityOwnershipListener(unregisterListener.getEntityType(), - unregisterListener.getListener()); - - getSender().tell(SuccessReply.INSTANCE, getSelf()); - } - - void tryCommitModifications(final BatchedModifications modifications) { - if (isLeader()) { - LOG.debug("{}: Committing BatchedModifications {} locally", persistenceId(), - modifications.getTransactionId()); - - // Note that it's possible the commit won't get consensus and will timeout and not be applied - // to the state. However we don't need to retry it in that case b/c it will be committed to - // the journal first and, once a majority of followers come back on line and it is replicated, - // it will be applied at that point. - handleBatchedModificationsLocal(modifications, self()); - } else { - final ActorSelection leader = getLeader(); - if (leader != null) { - possiblyRemoveAllInitialCandidates(leader); - - LOG.debug("{}: Sending BatchedModifications {} to leader {}", persistenceId(), - modifications.getTransactionId(), leader); - - Future future = Patterns.ask(leader, modifications, TimeUnit.SECONDS.toMillis( - getDatastoreContext().getShardTransactionCommitTimeoutInSeconds())); - - Patterns.pipe(future, getContext().dispatcher()).pipeTo(getSelf(), ActorRef.noSender()); - } - } - } - - void possiblyRemoveAllInitialCandidates(final ActorSelection leader) { - // The following handles removing all candidates on startup when re-joining with a remote leader. When a - // follower is detected as down, the leader will re-assign new owners to entities that were owned by the - // down member but doesn't remove the down member as a candidate, as the down node may actually be isolated - // and still running. Therefore on startup we send an initial message to the remote leader to remove any - // potential stale candidates we had previously registered, as it's possible a candidate may not be - // registered by a client in the new incarnation. We have to send the RemoveAllCandidates message prior to any - // pending registrations. - if (removeAllInitialCandidates && leader != null) { - removeAllInitialCandidates = false; - if (!isLeader()) { - LOG.debug("{} - got new leader {} on startup - sending RemoveAllCandidates", persistenceId(), leader); - - leader.tell(new RemoveAllCandidates(localMemberName), ActorRef.noSender()); - } - } - } - - boolean hasLeader() { - return getLeader() != null && (!isLeader() || isLeaderActive()); - } - - /** - * Determine if we are in jeopardy based on observed RAFT state. - */ - private static boolean inJeopardy(final RaftState state) { - switch (state) { - case Candidate: - case Follower: - case Leader: - case PreLeader: - return false; - case IsolatedLeader: - return true; - default: - throw new IllegalStateException("Unsupported RAFT state " + state); - } - } - - private void notifyAllListeners() { - searchForEntities((entityTypeNode, entityNode) -> { - Optional possibleType = entityTypeNode.findChildByArg(ENTITY_TYPE_NODE_ID); - if (possibleType.isPresent()) { - final boolean hasOwner; - final boolean isOwner; - - Optional possibleOwner = entityNode.findChildByArg(ENTITY_OWNER_NODE_ID); - if (possibleOwner.isPresent()) { - isOwner = localMemberName.getName().equals(possibleOwner.get().body().toString()); - hasOwner = true; - } else { - isOwner = false; - hasOwner = false; - } - - DOMEntity entity = new DOMEntity(possibleType.get().body().toString(), - (YangInstanceIdentifier) entityNode.findChildByArg(ENTITY_ID_NODE_ID).get().body()); - - listenerSupport.notifyEntityOwnershipListeners(entity, isOwner, isOwner, hasOwner); - } - }); - } - - @Override - protected void onStateChanged() { - boolean isLeader = isLeader(); - LOG.debug("{}: onStateChanged: isLeader: {}, hasLeader: {}", persistenceId(), isLeader, hasLeader()); - - // Examine current RAFT state to see if we are in jeopardy, potentially notifying all listeners - final boolean inJeopardy = inJeopardy(getRaftState()); - final boolean wasInJeopardy = listenerSupport.setInJeopardy(inJeopardy); - if (inJeopardy != wasInJeopardy) { - LOG.debug("{}: {} jeopardy state, notifying all listeners", persistenceId(), - inJeopardy ? "entered" : "left"); - notifyAllListeners(); - } - - commitCoordinator.onStateChanged(this, isLeader); - - super.onStateChanged(); - } - - @Override - protected void onLeaderChanged(final String oldLeader, final String newLeader) { - boolean isLeader = isLeader(); - LOG.debug("{}: onLeaderChanged: oldLeader: {}, newLeader: {}, isLeader: {}", persistenceId(), oldLeader, - newLeader, isLeader); - - if (isLeader) { - - // Re-initialize the downPeerMemberNames from the current akka Cluster state. The previous leader, if any, - // is most likely down however it's possible we haven't received the PeerDown message yet. - initializeDownPeerMemberNamesFromClusterState(); - - // Clear all existing strategies so that they get re-created when we call createStrategy again - // This allows the strategies to be re-initialized with existing statistics maintained by - // EntityOwnershipStatistics - strategyConfig.clearStrategies(); - - // Re-assign owners for all members that are known to be down. In a cluster which has greater than - // 3 nodes it is possible for some node beside the leader being down when the leadership transitions - // it makes sense to use this event to re-assign owners for those downed nodes. - Set ownedBy = new HashSet<>(downPeerMemberNames.size() + 1); - for (MemberName downPeerName : downPeerMemberNames) { - ownedBy.add(downPeerName.getName()); - } - - // Also try to assign owners for entities that have no current owner. See explanation in onPeerUp. - ownedBy.add(""); - selectNewOwnerForEntitiesOwnedBy(ownedBy); - } else { - // The leader changed - notify the coordinator to check if pending modifications need to be sent. - // While onStateChanged also does this, this method handles the case where the shard hears from a - // leader and stays in the follower state. In that case no behavior state change occurs. - commitCoordinator.onStateChanged(this, isLeader); - } - - super.onLeaderChanged(oldLeader, newLeader); - } - - @Override - protected void onVotingStateChangeComplete() { - // Re-evaluate ownership for all entities - if a member changed from voting to non-voting it should lose - // ownership and vice versa it now is a candidate to become owner. - final List modifications = new ArrayList<>(); - searchForEntities((entityTypeNode, entityNode) -> { - YangInstanceIdentifier entityPath = YangInstanceIdentifier.builder(ENTITY_TYPES_PATH) - .node(entityTypeNode.getIdentifier()).node(ENTITY_NODE_ID).node(entityNode.getIdentifier()) - .node(ENTITY_OWNER_NODE_ID).build(); - - Optional possibleOwner = - entityNode.findChildByArg(ENTITY_OWNER_NODE_ID).map(node -> node.body().toString()); - String newOwner = newOwner(possibleOwner.orElse(null), getCandidateNames(entityNode), - getEntityOwnerElectionStrategy(entityPath)); - - if (!newOwner.equals(possibleOwner.orElse(""))) { - modifications.add(new WriteModification(entityPath, - ImmutableNodes.leafNode(ENTITY_OWNER_NODE_ID, newOwner))); - } - }); - - commitCoordinator.commitModifications(modifications, this); - } - - private void initializeDownPeerMemberNamesFromClusterState() { - Optional cluster = getRaftActorContext().getCluster(); - if (!cluster.isPresent()) { - return; - } - - CurrentClusterState state = cluster.get().state(); - Set unreachable = state.getUnreachable(); - - LOG.debug( - "{}: initializeDownPeerMemberNamesFromClusterState - current downPeerMemberNames: {}, unreachable: {}", - persistenceId(), downPeerMemberNames, unreachable); - - downPeerMemberNames.clear(); - for (Member m: unreachable) { - downPeerMemberNames.add(MemberName.forName(m.getRoles().iterator().next())); - } - - for (Member m: state.getMembers()) { - if (m.status() != MemberStatus.up() && m.status() != MemberStatus.weaklyUp()) { - LOG.debug("{}: Adding down member with status {}", persistenceId(), m.status()); - downPeerMemberNames.add(MemberName.forName(m.getRoles().iterator().next())); - } - } - - LOG.debug("{}: new downPeerMemberNames: {}", persistenceId(), downPeerMemberNames); - } - - private void onCandidateRemoved(final CandidateRemoved message) { - LOG.debug("{}: onCandidateRemoved: {}", persistenceId(), message); - - if (isLeader()) { - String currentOwner = getCurrentOwner(message.getEntityPath()); - writeNewOwner(message.getEntityPath(), - newOwner(currentOwner, message.getRemainingCandidates(), - getEntityOwnerElectionStrategy(message.getEntityPath()))); - } - } - - private EntityOwnerSelectionStrategy getEntityOwnerElectionStrategy(final YangInstanceIdentifier entityPath) { - final String entityType = EntityOwnersModel.entityTypeFromEntityPath(entityPath); - return strategyConfig.createStrategy(entityType, entityOwnershipStatistics.byEntityType(entityType)); - } - - private void onCandidateAdded(final CandidateAdded message) { - if (!isLeader()) { - return; - } - - LOG.debug("{}: onCandidateAdded: {}", persistenceId(), message); - - // Since a node's candidate member is only added by the node itself, we can assume the node is up so - // remove it from the downPeerMemberNames. - downPeerMemberNames.remove(MemberName.forName(message.getNewCandidate())); - - final String currentOwner = getCurrentOwner(message.getEntityPath()); - final EntityOwnerSelectionStrategy strategy = getEntityOwnerElectionStrategy(message.getEntityPath()); - - // Available members is all the known peers - the number of peers that are down + self - // So if there are 2 peers and 1 is down then availableMembers will be 2 - final int availableMembers = getRaftActorContext().getPeerIds().size() - downPeerMemberNames.size() + 1; - - LOG.debug("{}: Using strategy {} to select owner, currentOwner = {}", persistenceId(), strategy, currentOwner); - - if (strategy.getSelectionDelayInMillis() == 0L) { - writeNewOwner(message.getEntityPath(), newOwner(currentOwner, message.getAllCandidates(), - strategy)); - } else if (message.getAllCandidates().size() == availableMembers) { - LOG.debug("{}: Received the maximum candidates requests : {} writing new owner", - persistenceId(), availableMembers); - cancelOwnerSelectionTask(message.getEntityPath()); - writeNewOwner(message.getEntityPath(), newOwner(currentOwner, message.getAllCandidates(), - strategy)); - } else { - scheduleOwnerSelection(message.getEntityPath(), message.getAllCandidates(), strategy); - } - } - - private void onPeerDown(final PeerDown peerDown) { - LOG.info("{}: onPeerDown: {}", persistenceId(), peerDown); - - MemberName downMemberName = peerDown.getMemberName(); - if (downPeerMemberNames.add(downMemberName) && isLeader()) { - // Select new owners for entities owned by the down peer and which have other candidates. For an entity for - // which the down peer is the only candidate, we leave it as the owner and don't clear it. This is done to - // handle the case where the peer member process is actually still running but the node is partitioned. - // When the partition is healed, the peer just remains as the owner. If the peer process actually restarted, - // it will first remove all its candidates on startup. If another candidate is registered during the time - // the peer is down, the new candidate will be selected as the new owner. - - selectNewOwnerForEntitiesOwnedBy(ImmutableSet.of(downMemberName.getName())); - } - } - - private void selectNewOwnerForEntitiesOwnedBy(final Set ownedBy) { - final List modifications = new ArrayList<>(); - searchForEntitiesOwnedBy(ownedBy, (entityTypeNode, entityNode) -> { - YangInstanceIdentifier entityPath = YangInstanceIdentifier.builder(ENTITY_TYPES_PATH) - .node(entityTypeNode.getIdentifier()).node(ENTITY_NODE_ID).node(entityNode.getIdentifier()) - .node(ENTITY_OWNER_NODE_ID).build(); - String newOwner = newOwner(getCurrentOwner(entityPath), getCandidateNames(entityNode), - getEntityOwnerElectionStrategy(entityPath)); - - if (!newOwner.isEmpty()) { - LOG.debug("{}: Found entity {}, writing new owner {}", persistenceId(), entityPath, newOwner); - - modifications.add(new WriteModification(entityPath, - ImmutableNodes.leafNode(ENTITY_OWNER_NODE_ID, newOwner))); - - } else { - LOG.debug("{}: Found entity {} but no other candidates - not clearing owner", persistenceId(), - entityPath); - } - }); - - commitCoordinator.commitModifications(modifications, this); - } - - private void onPeerUp(final PeerUp peerUp) { - LOG.debug("{}: onPeerUp: {}", persistenceId(), peerUp); - - downPeerMemberNames.remove(peerUp.getMemberName()); - - // Notify the coordinator to check if pending modifications need to be sent. We do this here - // to handle the case where the leader's peer address isn't known yet when a prior state or - // leader change occurred. - commitCoordinator.onStateChanged(this, isLeader()); - - if (isLeader()) { - // Try to assign owners for entities that have no current owner. It's possible the peer that is now up - // had previously registered as a candidate and was the only candidate but the owner write tx couldn't be - // committed due to a leader change. Eg, the leader is able to successfully commit the candidate add tx but - // becomes isolated before it can commit the owner change and switches to follower. The majority partition - // with a new leader has the candidate but the entity has no owner. When the partition is healed and the - // previously isolated leader reconnects, we'll receive onPeerUp and, if there's still no owner, the - // previous leader will gain ownership. - selectNewOwnerForEntitiesOwnedBy(ImmutableSet.of("")); - } - } - - private static Collection getCandidateNames(final MapEntryNode entity) { - return entity.findChildByArg(CANDIDATE_NODE_ID).map(child -> { - Collection candidates = ((MapNode) child).body(); - Collection candidateNames = new ArrayList<>(candidates.size()); - for (MapEntryNode candidate: candidates) { - candidateNames.add(candidate.findChildByArg(CANDIDATE_NAME_NODE_ID).get().body().toString()); - } - return candidateNames; - }).orElse(ImmutableList.of()); - } - - private void searchForEntitiesOwnedBy(final Set ownedBy, final EntityWalker walker) { - LOG.debug("{}: Searching for entities owned by {}", persistenceId(), ownedBy); - - searchForEntities((entityTypeNode, entityNode) -> { - String currentOwner = entityNode.findChildByArg(ENTITY_OWNER_NODE_ID) - .map(child -> child.body().toString()) - .orElse(""); - if (ownedBy.contains(currentOwner)) { - walker.onEntity(entityTypeNode, entityNode); - } - }); - } - - private void removeCandidateFromEntities(final MemberName member) { - final List modifications = new ArrayList<>(); - searchForEntities((entityTypeNode, entityNode) -> { - if (hasCandidate(entityNode, member)) { - YangInstanceIdentifier entityId = (YangInstanceIdentifier) entityNode.getIdentifier() - .getValue(ENTITY_ID_QNAME); - YangInstanceIdentifier candidatePath = candidatePath(entityTypeNode.getIdentifier() - .getValue(ENTITY_TYPE_QNAME).toString(), entityId, member.getName()); - - LOG.info("{}: Found entity {}, removing candidate {}, path {}", persistenceId(), entityId, - member, candidatePath); - - modifications.add(new DeleteModification(candidatePath)); - } - }); - - commitCoordinator.commitModifications(modifications, this); - } - - private static boolean hasCandidate(final MapEntryNode entity, final MemberName candidateName) { - return entity.findChildByArg(CANDIDATE_NODE_ID) - .flatMap(child -> ((MapNode)child).findChildByArg(candidateNodeKey(candidateName.getName()))) - .isPresent(); - } - - private void searchForEntities(final EntityWalker walker) { - Optional possibleEntityTypes = getDataStore().readNode(ENTITY_TYPES_PATH); - if (!possibleEntityTypes.isPresent()) { - return; - } - - for (MapEntryNode entityType : ((MapNode) possibleEntityTypes.get()).body()) { - Optional possibleEntities = entityType.findChildByArg(ENTITY_NODE_ID); - if (!possibleEntities.isPresent()) { - // shouldn't happen but handle anyway - continue; - } - - for (MapEntryNode entity: ((MapNode) possibleEntities.get()).body()) { - walker.onEntity(entityType, entity); - } - } - } - - private void writeNewOwner(final YangInstanceIdentifier entityPath, final String newOwner) { - LOG.debug("{}: Writing new owner {} for entity {}", persistenceId(), newOwner, entityPath); - - commitCoordinator.commitModification(new WriteModification(entityPath.node(ENTITY_OWNER_QNAME), - ImmutableNodes.leafNode(ENTITY_OWNER_NODE_ID, newOwner)), this); - } - - /** - * Schedule a new owner selection job. Cancelling any outstanding job if it has not been cancelled. - */ - private void scheduleOwnerSelection(final YangInstanceIdentifier entityPath, final Collection allCandidates, - final EntityOwnerSelectionStrategy strategy) { - cancelOwnerSelectionTask(entityPath); - - LOG.debug("{}: Scheduling owner selection after {} ms", persistenceId(), strategy.getSelectionDelayInMillis()); - - final Cancellable lastScheduledTask = context().system().scheduler().scheduleOnce( - FiniteDuration.apply(strategy.getSelectionDelayInMillis(), TimeUnit.MILLISECONDS), self(), - new SelectOwner(entityPath, allCandidates, strategy), context().system().dispatcher(), self()); - - entityToScheduledOwnershipTask.put(entityPath, lastScheduledTask); - } - - private void cancelOwnerSelectionTask(final YangInstanceIdentifier entityPath) { - final Cancellable lastScheduledTask = entityToScheduledOwnershipTask.get(entityPath); - if (lastScheduledTask != null && !lastScheduledTask.isCancelled()) { - lastScheduledTask.cancel(); - } - } - - private String newOwner(final String currentOwner, final Collection candidates, - final EntityOwnerSelectionStrategy ownerSelectionStrategy) { - Collection viableCandidates = getViableCandidates(candidates); - if (viableCandidates.isEmpty()) { - return ""; - } - return ownerSelectionStrategy.newOwner(currentOwner, viableCandidates); - } - - private Collection getViableCandidates(final Collection candidates) { - Map memberToVotingState = new HashMap<>(); - getRaftActorContext().getPeers().forEach(peerInfo -> memberToVotingState.put( - ShardIdentifier.fromShardIdString(peerInfo.getId()).getMemberName(), peerInfo.getVotingState())); - - Collection viableCandidates = new ArrayList<>(); - - for (String candidate : candidates) { - MemberName memberName = MemberName.forName(candidate); - if (memberToVotingState.get(memberName) != VotingState.NON_VOTING - && !downPeerMemberNames.contains(memberName)) { - viableCandidates.add(candidate); - } - } - return viableCandidates; - } - - private String getCurrentOwner(final YangInstanceIdentifier entityId) { - return getDataStore().readNode(entityId.node(ENTITY_OWNER_QNAME)) - .map(owner -> owner.body().toString()) - .orElse(null); - } - - @FunctionalInterface - private interface EntityWalker { - void onEntity(MapEntryNode entityTypeNode, MapEntryNode entityNode); - } - - public static Builder newBuilder() { - return new Builder(); - } - - static class Builder extends Shard.AbstractBuilder { - private MemberName localMemberName; - private EntityOwnerSelectionStrategyConfig ownerSelectionStrategyConfig; - - protected Builder() { - super(EntityOwnershipShard.class); - } - - Builder localMemberName(final MemberName newLocalMemberName) { - checkSealed(); - this.localMemberName = newLocalMemberName; - return this; - } - - Builder ownerSelectionStrategyConfig(final EntityOwnerSelectionStrategyConfig newOwnerSelectionStrategyConfig) { - checkSealed(); - this.ownerSelectionStrategyConfig = newOwnerSelectionStrategyConfig; - return this; - } - - @Override - protected void verify() { - super.verify(); - requireNonNull(localMemberName, "localMemberName should not be null"); - requireNonNull(ownerSelectionStrategyConfig, "ownerSelectionStrategyConfig should not be null"); - } - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipShardCommitCoordinator.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipShardCommitCoordinator.java deleted file mode 100644 index 3402b23e72..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipShardCommitCoordinator.java +++ /dev/null @@ -1,277 +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.entityownership; - -import static java.util.Objects.requireNonNull; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNER_QNAME; - -import akka.actor.ActorRef; -import akka.actor.Cancellable; -import akka.actor.Status.Failure; -import com.google.common.collect.ImmutableList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; -import org.eclipse.jdt.annotation.Nullable; -import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier; -import org.opendaylight.controller.cluster.access.concepts.FrontendIdentifier; -import org.opendaylight.controller.cluster.access.concepts.FrontendType; -import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier; -import org.opendaylight.controller.cluster.access.concepts.MemberName; -import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier; -import org.opendaylight.controller.cluster.datastore.DataStoreVersions; -import org.opendaylight.controller.cluster.datastore.exceptions.NoShardLeaderException; -import org.opendaylight.controller.cluster.datastore.messages.BatchedModifications; -import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionReply; -import org.opendaylight.controller.cluster.datastore.modification.Modification; -import org.opendaylight.controller.cluster.datastore.modification.WriteModification; -import org.slf4j.Logger; -import scala.concurrent.duration.FiniteDuration; - -/** - * Handles commits and retries for the EntityOwnershipShard. - * - * @author Thomas Pantelis - */ -class EntityOwnershipShardCommitCoordinator { - private static final Object COMMIT_RETRY_MESSAGE = new Object() { - @Override - public String toString() { - return "entityCommitRetry"; - } - }; - private static final FrontendType FRONTEND_TYPE = FrontendType.forName("entity-ownership-internal"); - - private final Queue pendingModifications = new LinkedList<>(); - private final LocalHistoryIdentifier historyId; - private final Logger log; - - private BatchedModifications inflightCommit; - private Cancellable retryCommitSchedule; - private long transactionIDCounter = 0; - - EntityOwnershipShardCommitCoordinator(final MemberName localMemberName, final Logger log) { - this.log = requireNonNull(log); - historyId = new LocalHistoryIdentifier( - ClientIdentifier.create(FrontendIdentifier.create(localMemberName, FRONTEND_TYPE), 0), 0); - } - - boolean handleMessage(final Object message, final EntityOwnershipShard shard) { - boolean handled = true; - if (CommitTransactionReply.isSerializedType(message)) { - // Successful reply from a local commit. - inflightCommitSucceeded(shard); - } else if (message instanceof akka.actor.Status.Failure) { - // Failure reply from a local commit. - inflightCommitFailure(((Failure) message).cause(), shard); - } else if (COMMIT_RETRY_MESSAGE.equals(message)) { - retryInflightCommit(shard); - } else { - handled = false; - } - - return handled; - } - - private void retryInflightCommit(final EntityOwnershipShard shard) { - // Shouldn't be null happen but verify anyway - if (inflightCommit == null) { - return; - } - - if (shard.hasLeader()) { - log.debug("Retrying commit for BatchedModifications {}", inflightCommit.getTransactionId()); - - shard.tryCommitModifications(inflightCommit); - } else { - scheduleInflightCommitRetry(shard); - } - } - - void inflightCommitFailure(final Throwable cause, final EntityOwnershipShard shard) { - // This should've originated from a failed inflight commit but verify anyway - if (inflightCommit == null) { - return; - } - - log.debug("Inflight BatchedModifications {} commit failed", inflightCommit.getTransactionId(), cause); - - if (!(cause instanceof NoShardLeaderException)) { - // If the failure is other than NoShardLeaderException the commit may have been partially - // processed so retry with a new transaction ID to be safe. - newInflightCommitWithDifferentTransactionID(); - } - - scheduleInflightCommitRetry(shard); - } - - private void scheduleInflightCommitRetry(final EntityOwnershipShard shard) { - FiniteDuration duration = shard.getDatastoreContext().getShardRaftConfig().getElectionTimeOutInterval(); - - log.debug("Scheduling retry for BatchedModifications commit {} in {}", - inflightCommit.getTransactionId(), duration); - - retryCommitSchedule = shard.getContext().system().scheduler().scheduleOnce(duration, shard.getSelf(), - COMMIT_RETRY_MESSAGE, shard.getContext().dispatcher(), ActorRef.noSender()); - } - - void inflightCommitSucceeded(final EntityOwnershipShard shard) { - // Shouldn't be null but verify anyway - if (inflightCommit == null) { - return; - } - - if (retryCommitSchedule != null) { - retryCommitSchedule.cancel(); - } - - log.debug("BatchedModifications commit {} succeeded", inflightCommit.getTransactionId()); - - inflightCommit = null; - commitNextBatch(shard); - } - - void commitNextBatch(final EntityOwnershipShard shard) { - if (inflightCommit != null || pendingModifications.isEmpty() || !shard.hasLeader()) { - return; - } - - inflightCommit = newBatchedModifications(); - Iterator iter = pendingModifications.iterator(); - while (iter.hasNext()) { - inflightCommit.addModification(iter.next()); - iter.remove(); - if (inflightCommit.getModifications().size() - >= shard.getDatastoreContext().getShardBatchedModificationCount()) { - break; - } - } - - log.debug("Committing next BatchedModifications {}, size {}", inflightCommit.getTransactionId(), - inflightCommit.getModifications().size()); - - shard.tryCommitModifications(inflightCommit); - } - - void commitModification(final Modification modification, final EntityOwnershipShard shard) { - commitModifications(ImmutableList.of(modification), shard); - } - - void commitModifications(final List modifications, final EntityOwnershipShard shard) { - if (modifications.isEmpty()) { - return; - } - - boolean hasLeader = shard.hasLeader(); - if (inflightCommit != null || !hasLeader) { - if (log.isDebugEnabled()) { - log.debug("{} - adding modifications to pending", - inflightCommit != null ? "A commit is inflight" : "No shard leader"); - } - - pendingModifications.addAll(modifications); - } else { - inflightCommit = newBatchedModifications(); - inflightCommit.addModifications(modifications); - shard.tryCommitModifications(inflightCommit); - } - } - - void onStateChanged(final EntityOwnershipShard shard, final boolean isLeader) { - shard.possiblyRemoveAllInitialCandidates(shard.getLeader()); - - possiblyPrunePendingCommits(shard, isLeader); - - if (!isLeader && inflightCommit != null) { - // We're no longer the leader but we have an inflight local commit. This likely means we didn't get - // consensus for the commit and switched to follower due to another node with a higher term. We - // can't be sure if the commit was replicated to any node so we retry it here with a new - // transaction ID. - if (retryCommitSchedule != null) { - retryCommitSchedule.cancel(); - } - - newInflightCommitWithDifferentTransactionID(); - retryInflightCommit(shard); - } else { - commitNextBatch(shard); - } - } - - private void possiblyPrunePendingCommits(final EntityOwnershipShard shard, final boolean isLeader) { - // If we were the leader and transitioned to follower, we'll try to forward pending commits to the new leader. - // However certain commits, e.g. entity owner changes, should only be committed by a valid leader as the - // criteria used to determine the commit may be stale. Since we're no longer a valid leader, we should not - // forward such commits thus we prune the pending modifications. We still should forward local candidate change - // commits. - if (shard.hasLeader() && !isLeader) { - // We may have already submitted a transaction for replication and commit. We don't need the base Shard to - // forward it since we also have it stored in the inflightCommit and handle retries. So we just clear - // pending transactions and drop them. - shard.convertPendingTransactionsToMessages(); - - // Prune the inflightCommit. - if (inflightCommit != null) { - inflightCommit = pruneModifications(inflightCommit); - } - - // Prune the subsequent pending modifications. - pendingModifications.removeIf(mod -> !canForwardModificationToNewLeader(mod)); - } - } - - private @Nullable BatchedModifications pruneModifications(final BatchedModifications toPrune) { - BatchedModifications prunedModifications = new BatchedModifications(toPrune.getTransactionId(), - toPrune.getVersion()); - prunedModifications.setDoCommitOnReady(toPrune.isDoCommitOnReady()); - if (toPrune.isReady()) { - prunedModifications.setReady(toPrune.getParticipatingShardNames()); - } - prunedModifications.setTotalMessagesSent(toPrune.getTotalMessagesSent()); - for (Modification mod: toPrune.getModifications()) { - if (canForwardModificationToNewLeader(mod)) { - prunedModifications.addModification(mod); - } - } - - return !prunedModifications.getModifications().isEmpty() ? prunedModifications : null; - } - - private boolean canForwardModificationToNewLeader(final Modification mod) { - // If this is a WRITE of entity owner we don't want to forward it to a new leader since the criteria used - // to determine the new owner might be stale. - if (mod instanceof WriteModification) { - WriteModification writeMod = (WriteModification)mod; - boolean canForward = !writeMod.getPath().getLastPathArgument().getNodeType().equals(ENTITY_OWNER_QNAME); - - if (!canForward) { - log.debug("Not forwarding WRITE modification for {} to new leader", writeMod.getPath()); - } - - return canForward; - } - - return true; - } - - private void newInflightCommitWithDifferentTransactionID() { - BatchedModifications newBatchedModifications = newBatchedModifications(); - newBatchedModifications.addModifications(inflightCommit.getModifications()); - inflightCommit = newBatchedModifications; - } - - private BatchedModifications newBatchedModifications() { - BatchedModifications modifications = new BatchedModifications( - new TransactionIdentifier(historyId, ++transactionIDCounter), DataStoreVersions.CURRENT_VERSION); - modifications.setDoCommitOnReady(true); - modifications.setReady(); - modifications.setTotalMessagesSent(1); - return modifications; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipStatistics.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipStatistics.java deleted file mode 100644 index 59cc7550c6..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipStatistics.java +++ /dev/null @@ -1,88 +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.entityownership; - -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityTypeFromEntityPath; - -import com.google.common.base.Strings; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; -import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; -import tech.pantheon.triemap.TrieMap; - -/** - * EntityOwnershipStatistics is a utility class that keeps track of ownership statistics for the candidates and - * caches it for quick count queries. - *

- * While the entity ownership model does maintain the information about which entity is owned by which candidate - * finding out how many entities of a given type are owned by a given candidate is not an efficient query. - */ -class EntityOwnershipStatistics extends AbstractEntityOwnerChangeListener { - private final TrieMap> statistics = TrieMap.create(); - - EntityOwnershipStatistics() { - // Hidden on purpose - } - - @Override - public void onInitialData() { - // No-op - } - - @Override - public void onDataTreeChanged(final Collection changes) { - for (DataTreeCandidate change : changes) { - DataTreeCandidateNode changeRoot = change.getRootNode(); - LeafNode ownerLeaf = (LeafNode) changeRoot.getDataAfter().get(); - String entityType = entityTypeFromEntityPath(change.getRootPath()); - String newOwner = extractOwner(ownerLeaf); - if (!Strings.isNullOrEmpty(newOwner)) { - updateStatistics(entityType, newOwner, 1); - } - - Optional dataBefore = changeRoot.getDataBefore(); - if (dataBefore.isPresent()) { - String origOwner = extractOwner((LeafNode) changeRoot.getDataBefore().get()); - if (!Strings.isNullOrEmpty(origOwner)) { - updateStatistics(entityType, origOwner, -1); - } - } - } - } - - Map> all() { - Map> snapshot = new HashMap<>(); - for (String entityType : statistics.immutableSnapshot().keySet()) { - snapshot.put(entityType, byEntityType(entityType)); - } - return snapshot; - } - - Map byEntityType(final String entityType) { - if (statistics.get(entityType) != null) { - return statistics.get(entityType).immutableSnapshot(); - } - return new HashMap<>(); - } - - private void updateStatistics(final String entityType, final String candidateName, final long count) { - TrieMap map = statistics.get(entityType); - if (map == null) { - map = TrieMap.create(); - map.put(candidateName, count); - statistics.put(entityType, map); - } else { - map.merge(candidateName, count, (ownedEntities, addedEntities) -> ownedEntities + addedEntities); - } - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/OSGiDistributedEntityOwnershipService.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/OSGiDistributedEntityOwnershipService.java deleted file mode 100644 index cceae6c06c..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/OSGiDistributedEntityOwnershipService.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.entityownership; - -import com.google.common.annotations.Beta; -import java.util.Map; -import java.util.Optional; -import org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.EntityOwnerSelectionStrategyConfigReader; -import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException; -import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.Reference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@Beta -@Component(immediate = true, configurationPid = "org.opendaylight.controller.cluster.entity.owner.selection.strategies", - property = "type=default") -public final class OSGiDistributedEntityOwnershipService implements DOMEntityOwnershipService { - private static final Logger LOG = LoggerFactory.getLogger(OSGiDistributedEntityOwnershipService.class); - - @Reference(target = "(type=distributed-operational)") - DistributedDataStoreInterface operDatastore = null; - - private DistributedEntityOwnershipService delegate; - - @Override - public DOMEntityOwnershipCandidateRegistration registerCandidate(final DOMEntity entity) - throws CandidateAlreadyRegisteredException { - return delegate.registerCandidate(entity); - } - - @Override - public DOMEntityOwnershipListenerRegistration registerListener(final String entityType, - final DOMEntityOwnershipListener listener) { - return delegate.registerListener(entityType, listener); - } - - @Override - public Optional getOwnershipState(final DOMEntity forEntity) { - return delegate.getOwnershipState(forEntity); - } - - @Override - public boolean isCandidateRegistered(final DOMEntity forEntity) { - return delegate.isCandidateRegistered(forEntity); - } - - @Activate - // FIXME: 3.0.0: properties are keyed by String, this should be Map - void activate(final Map properties) { - LOG.info("Distributed Entity Ownership Service starting"); - delegate = DistributedEntityOwnershipService.start(operDatastore.getActorUtils(), - EntityOwnerSelectionStrategyConfigReader.loadStrategyWithConfig(properties)); - LOG.info("Distributed Entity Ownership Service started"); - } - - @Deactivate - void deactivate() { - LOG.info("Distributed Entity Ownership Service stopping"); - delegate.close(); - LOG.info("Distributed Entity Ownership Service stopped"); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/CandidateAdded.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/CandidateAdded.java deleted file mode 100644 index 2fb6bda68c..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/CandidateAdded.java +++ /dev/null @@ -1,48 +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.entityownership.messages; - -import java.util.Collection; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; - -/** - * Message sent when a new candidate is added for an entity. - * - * @author Moiz Raja - * @author Thomas Pantelis - */ -public class CandidateAdded { - private final YangInstanceIdentifier entityPath; - private final Collection allCandidates; - private final String newCandidate; - - public CandidateAdded(final YangInstanceIdentifier entityPath, final String newCandidate, - final Collection allCandidates) { - this.entityPath = entityPath; - this.newCandidate = newCandidate; - this.allCandidates = allCandidates; - } - - public YangInstanceIdentifier getEntityPath() { - return entityPath; - } - - public Collection getAllCandidates() { - return allCandidates; - } - - public String getNewCandidate() { - return newCandidate; - } - - @Override - public String toString() { - return "CandidateAdded [entityPath=" + entityPath + ", newCandidate=" + newCandidate + ", allCandidates=" - + allCandidates + "]"; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/CandidateRemoved.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/CandidateRemoved.java deleted file mode 100644 index d3e659c421..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/CandidateRemoved.java +++ /dev/null @@ -1,48 +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.entityownership.messages; - -import java.util.Collection; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; - -/** - * Message sent when a candidate is removed for an entity. - * - * @author Moiz Raja - * @author Thomas Pantelis - */ -public class CandidateRemoved { - private final YangInstanceIdentifier entityPath; - private final String removedCandidate; - private final Collection remainingCandidates; - - public CandidateRemoved(final YangInstanceIdentifier entityPath, final String removedCandidate, - final Collection remainingCandidates) { - this.entityPath = entityPath; - this.removedCandidate = removedCandidate; - this.remainingCandidates = remainingCandidates; - } - - public YangInstanceIdentifier getEntityPath() { - return entityPath; - } - - public String getRemovedCandidate() { - return removedCandidate; - } - - public Collection getRemainingCandidates() { - return remainingCandidates; - } - - @Override - public String toString() { - return "CandidateRemoved [entityPath=" + entityPath + ", removedCandidate=" + removedCandidate - + ", remainingCandidates=" + remainingCandidates + "]"; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/RegisterCandidateLocal.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/RegisterCandidateLocal.java deleted file mode 100644 index 3e70d2b273..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/RegisterCandidateLocal.java +++ /dev/null @@ -1,32 +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.entityownership.messages; - -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; - -/** - * Message sent to the local EntityOwnershipShard to register a candidate. - * - * @author Thomas Pantelis - */ -public class RegisterCandidateLocal { - private final DOMEntity entity; - - public RegisterCandidateLocal(final DOMEntity entity) { - this.entity = entity; - } - - public DOMEntity getEntity() { - return entity; - } - - @Override - public String toString() { - return "RegisterCandidateLocal [entity=" + entity + "]"; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/RegisterListenerLocal.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/RegisterListenerLocal.java deleted file mode 100644 index 2c9ebabaa4..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/RegisterListenerLocal.java +++ /dev/null @@ -1,42 +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.entityownership.messages; - -import static java.util.Objects.requireNonNull; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; - -/** - * Message sent to the local EntityOwnershipShard to register an EntityOwnershipListener. - * - * @author Thomas Pantelis - */ -@NonNullByDefault -public class RegisterListenerLocal { - private final DOMEntityOwnershipListener listener; - private final String entityType; - - public RegisterListenerLocal(final DOMEntityOwnershipListener listener, final String entityType) { - this.listener = requireNonNull(listener, "listener cannot be null"); - this.entityType = requireNonNull(entityType, "entityType cannot be null"); - } - - public DOMEntityOwnershipListener getListener() { - return listener; - } - - public String getEntityType() { - return entityType; - } - - @Override - public String toString() { - return "RegisterListenerLocal [entityType=" + entityType + ", listener=" + listener + "]"; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/RemoveAllCandidates.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/RemoveAllCandidates.java deleted file mode 100644 index 843a3dc54f..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/RemoveAllCandidates.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016 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.entityownership.messages; - -import java.io.Serializable; -import org.opendaylight.controller.cluster.access.concepts.MemberName; - -/** - * Message sent by an EntityOwnershipShard to its leader on startup to remove all its candidates. - * - * @author Thomas Pantelis - */ -public class RemoveAllCandidates implements Serializable { - private static final long serialVersionUID = 1L; - - private final MemberName memberName; - - public RemoveAllCandidates(final MemberName memberName) { - this.memberName = memberName; - } - - public MemberName getMemberName() { - return memberName; - } - - @Override - public String toString() { - return "RemoveAllCandidates [memberName=" + memberName + "]"; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/SelectOwner.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/SelectOwner.java deleted file mode 100644 index e7307bfe47..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/SelectOwner.java +++ /dev/null @@ -1,49 +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.entityownership.messages; - -import static java.util.Objects.requireNonNull; - -import java.util.Collection; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.EntityOwnerSelectionStrategy; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; - -/** - * Message sent when a new owner needs to be selected. - */ -public class SelectOwner { - private final YangInstanceIdentifier entityPath; - private final Collection allCandidates; - private final EntityOwnerSelectionStrategy ownerSelectionStrategy; - - public SelectOwner(final YangInstanceIdentifier entityPath, final Collection allCandidates, - final EntityOwnerSelectionStrategy ownerSelectionStrategy) { - this.entityPath = requireNonNull(entityPath, "entityPath should not be null"); - this.allCandidates = requireNonNull(allCandidates, "allCandidates should not be null"); - this.ownerSelectionStrategy = requireNonNull(ownerSelectionStrategy, - "ownerSelectionStrategy should not be null"); - } - - public YangInstanceIdentifier getEntityPath() { - return entityPath; - } - - public Collection getAllCandidates() { - return allCandidates; - } - - public EntityOwnerSelectionStrategy getOwnerSelectionStrategy() { - return ownerSelectionStrategy; - } - - @Override - public String toString() { - return "SelectOwner [entityPath=" + entityPath + ", allCandidates=" + allCandidates - + ", ownerSelectionStrategy=" + ownerSelectionStrategy + "]"; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/UnregisterCandidateLocal.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/UnregisterCandidateLocal.java deleted file mode 100644 index 7c4cabb665..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/UnregisterCandidateLocal.java +++ /dev/null @@ -1,32 +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.entityownership.messages; - -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; - -/** - * Message sent to the local EntityOwnershipShard to unregister a candidate. - * - * @author Thomas Pantelis - */ -public class UnregisterCandidateLocal { - private final DOMEntity entity; - - public UnregisterCandidateLocal(final DOMEntity entity) { - this.entity = entity; - } - - public DOMEntity getEntity() { - return entity; - } - - @Override - public String toString() { - return "UnregisterCandidateLocal [entity=" + entity + "]"; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/UnregisterListenerLocal.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/UnregisterListenerLocal.java deleted file mode 100644 index 1652c33086..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/messages/UnregisterListenerLocal.java +++ /dev/null @@ -1,38 +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.entityownership.messages; - -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; - -/** - * Message sent to the local EntityOwnershipShard to unregister an EntityOwnershipListener. - * - * @author Thomas Pantelis - */ -public class UnregisterListenerLocal { - private final DOMEntityOwnershipListener listener; - private final String entityType; - - public UnregisterListenerLocal(final DOMEntityOwnershipListener listener, final String entityType) { - this.listener = listener; - this.entityType = entityType; - } - - public DOMEntityOwnershipListener getListener() { - return listener; - } - - public String getEntityType() { - return entityType; - } - - @Override - public String toString() { - return "UnregisterListenerLocal [entityType=" + entityType + ", listener=" + listener + "]"; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/AbstractEntityOwnerSelectionStrategy.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/AbstractEntityOwnerSelectionStrategy.java deleted file mode 100644 index 23cd5be3c2..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/AbstractEntityOwnerSelectionStrategy.java +++ /dev/null @@ -1,31 +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.entityownership.selectionstrategy; - -import java.util.Map; - -public abstract class AbstractEntityOwnerSelectionStrategy implements EntityOwnerSelectionStrategy { - - private final long selectionDelayInMillis; - private final Map initialStatistics; - - protected AbstractEntityOwnerSelectionStrategy(final long selectionDelayInMillis, - final Map initialStatistics) { - this.selectionDelayInMillis = selectionDelayInMillis; - this.initialStatistics = initialStatistics; - } - - @Override - public long getSelectionDelayInMillis() { - return selectionDelayInMillis; - } - - public Map getInitialStatistics() { - return initialStatistics; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategy.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategy.java deleted file mode 100644 index df19a6541a..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategy.java +++ /dev/null @@ -1,33 +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.entityownership.selectionstrategy; - -import java.util.Collection; -import org.eclipse.jdt.annotation.Nullable; - -/** - * An EntityOwnerSelectionStrategy is to be used by the EntityOwnershipShard to select a new owner from a collection - * of candidates. - */ -public interface EntityOwnerSelectionStrategy { - /** - * Returns the time in millis owner selection should be delayed. - * - * @return the time in millis owner selection should be delayed - */ - long getSelectionDelayInMillis(); - - /** - * Selects a new owner from the list of viable candidates. - * - * @param currentOwner the current owner of the entity if any, null otherwise - * @param viableCandidates the available candidates from which to choose the new owner - * @return the new owner - */ - String newOwner(@Nullable String currentOwner, Collection viableCandidates); -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategyConfig.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategyConfig.java deleted file mode 100644 index fb6b2783b1..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategyConfig.java +++ /dev/null @@ -1,106 +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.entityownership.selectionstrategy; - -import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * FIXME: this is simple registry service, except it also loads classes. - */ -public final class EntityOwnerSelectionStrategyConfig { - private static final Logger LOG = LoggerFactory.getLogger(EntityOwnerSelectionStrategyConfig.class); - private final Map entityTypeToStrategyInfo = new HashMap<>(); - private final Map entityTypeToOwnerSelectionStrategy = new HashMap<>(); - - private EntityOwnerSelectionStrategyConfig() { - - } - - public boolean isStrategyConfigured(final String entityType) { - return entityTypeToStrategyInfo.get(entityType) != null; - } - - public EntityOwnerSelectionStrategy createStrategy(final String entityType, - final Map initialStatistics) { - final EntityOwnerSelectionStrategy strategy; - final EntityOwnerSelectionStrategy existingStrategy = entityTypeToOwnerSelectionStrategy.get(entityType); - if (existingStrategy != null) { - strategy = existingStrategy; - } else { - EntityOwnerSelectionStrategyConfig.StrategyInfo strategyInfo = entityTypeToStrategyInfo.get(entityType); - if (strategyInfo == null) { - strategy = FirstCandidateSelectionStrategy.INSTANCE; - } else { - strategy = strategyInfo.createStrategy(initialStatistics); - } - entityTypeToOwnerSelectionStrategy.put(entityType, strategy); - } - return strategy; - } - - /** - * This class should not exist. It contains a single long, which is passed to the constructor (via reflection). - * We are getting that information from a BundleContext. We are running in OSGi environment, hence this class - * needs to be deployed in its own bundle, with its own configuration. - * If this is used internally, it needs to be relocated into a separate package along with the implementation - * using it. - * - * @deprecated FIXME: THIS IS CONFIGURATION FOR A CUSTOM-LOADED CLASS CONSTRUCTOR - */ - @Deprecated - public void clearStrategies() { - entityTypeToOwnerSelectionStrategy.clear(); - } - - private static final class StrategyInfo { - private final Class strategyClass; - private final long delay; - - private StrategyInfo(final Class strategyClass, final long delay) { - this.strategyClass = strategyClass; - this.delay = delay; - } - - public EntityOwnerSelectionStrategy createStrategy(final Map initialStatistics) { - try { - return strategyClass.getDeclaredConstructor(long.class, Map.class) - .newInstance(delay, initialStatistics); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException - | NoSuchMethodException e) { - LOG.warn("could not create custom strategy", e); - } - return FirstCandidateSelectionStrategy.INSTANCE; - } - } - - public static Builder newBuilder() { - return new Builder(new EntityOwnerSelectionStrategyConfig()); - } - - public static final class Builder { - private final EntityOwnerSelectionStrategyConfig config; - - Builder(final EntityOwnerSelectionStrategyConfig config) { - this.config = config; - } - - public Builder addStrategy(final String entityType, - final Class strategy, final long delay) { - config.entityTypeToStrategyInfo.put(entityType, new StrategyInfo(strategy, delay)); - return this; - } - - public EntityOwnerSelectionStrategyConfig build() { - return this.config; - } - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategyConfigReader.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategyConfigReader.java deleted file mode 100644 index 8bac3dd301..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategyConfigReader.java +++ /dev/null @@ -1,88 +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.entityownership.selectionstrategy; - -import com.google.common.base.Preconditions; -import java.util.Map; -import java.util.Map.Entry; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.EntityOwnerSelectionStrategyConfig.Builder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Reads the entity owner selection strategy config. - * - */ -public final class EntityOwnerSelectionStrategyConfigReader { - - private static final Logger LOG = LoggerFactory.getLogger(EntityOwnerSelectionStrategyConfigReader.class); - private static final String ENTITY_TYPE_PREFIX = "entity.type."; - - private EntityOwnerSelectionStrategyConfigReader() { - // Hidden on purpose - } - - @SuppressWarnings("checkstyle:IllegalCatch") - public static EntityOwnerSelectionStrategyConfig loadStrategyWithConfig(final Map props) { - final EntityOwnerSelectionStrategyConfig.Builder builder = EntityOwnerSelectionStrategyConfig.newBuilder(); - - if (props != null && !props.isEmpty()) { - parseConfiguration(builder, props); - } else { - if (props == null) { - LOG.debug("Could not read strategy configuration file, will use default configuration."); - } else { - LOG.debug("Configuration file is empty, will use default configuration."); - } - } - return builder.build(); - } - - private static EntityOwnerSelectionStrategyConfig parseConfiguration(final Builder builder, - final Map properties) { - - for (final Entry entry : properties.entrySet()) { - final String key = (String) entry.getKey(); - if (!key.startsWith(ENTITY_TYPE_PREFIX)) { - LOG.debug("Ignoring non-conforming property key : {}", key); - continue; - } - - final String[] strategyClassAndDelay = ((String) properties.get(key)).split(","); - final Class aClass = loadClass(strategyClassAndDelay[0]); - - final long delay; - if (strategyClassAndDelay.length > 1) { - delay = Long.parseLong(strategyClassAndDelay[1]); - } else { - delay = 0; - } - - final String entityType = key.substring(key.lastIndexOf(".") + 1); - builder.addStrategy(entityType, aClass, delay); - LOG.debug("Entity Type '{}' using strategy {} delay {}", entityType, aClass, delay); - } - - return builder.build(); - } - - @SuppressWarnings("unchecked") - private static Class loadClass(final String strategyClassAndDelay) { - final Class clazz; - try { - clazz = EntityOwnerSelectionStrategyConfigReader.class.getClassLoader().loadClass(strategyClassAndDelay); - } catch (final ClassNotFoundException e) { - throw new IllegalArgumentException("Failed to load strategy " + strategyClassAndDelay, e); - } - - Preconditions.checkArgument(EntityOwnerSelectionStrategy.class.isAssignableFrom(clazz), - "Selected implementation %s must implement EntityOwnerSelectionStrategy, clazz"); - - return (Class) clazz; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/FirstCandidateSelectionStrategy.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/FirstCandidateSelectionStrategy.java deleted file mode 100644 index 9a18f72e76..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/FirstCandidateSelectionStrategy.java +++ /dev/null @@ -1,33 +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.entityownership.selectionstrategy; - -import com.google.common.base.Preconditions; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; - -/** - * The FirstCandidateSelectionStrategy always selects the first viable candidate from the list of candidates. - */ -public class FirstCandidateSelectionStrategy extends AbstractEntityOwnerSelectionStrategy { - - public static final FirstCandidateSelectionStrategy INSTANCE = - new FirstCandidateSelectionStrategy(0L, Collections.emptyMap()); - - public FirstCandidateSelectionStrategy(final long selectionDelayInMillis, - final Map initialStatistics) { - super(selectionDelayInMillis, initialStatistics); - } - - @Override - public String newOwner(final String currentOwner, final Collection viableCandidates) { - Preconditions.checkArgument(viableCandidates.size() > 0, "No viable candidates provided"); - return viableCandidates.iterator().next(); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/LeastLoadedCandidateSelectionStrategy.java b/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/LeastLoadedCandidateSelectionStrategy.java deleted file mode 100644 index fc53f9545e..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/LeastLoadedCandidateSelectionStrategy.java +++ /dev/null @@ -1,64 +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.entityownership.selectionstrategy; - -import static com.google.common.base.Preconditions.checkArgument; -import static java.util.Objects.requireNonNullElse; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Strings; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * The LeastLoadedCandidateSelectionStrategy assigns ownership for an entity to the candidate which owns the least - * number of entities. - */ -public class LeastLoadedCandidateSelectionStrategy extends AbstractEntityOwnerSelectionStrategy { - private final Map localStatistics = new HashMap<>(); - - protected LeastLoadedCandidateSelectionStrategy(final long selectionDelayInMillis, - final Map initialStatistics) { - super(selectionDelayInMillis, initialStatistics); - - localStatistics.putAll(initialStatistics); - } - - @Override - public String newOwner(final String currentOwner, final Collection viableCandidates) { - checkArgument(viableCandidates.size() > 0); - String leastLoadedCandidate = null; - long leastLoadedCount = Long.MAX_VALUE; - - if (!Strings.isNullOrEmpty(currentOwner)) { - long localVal = requireNonNullElse(localStatistics.get(currentOwner), 0L); - localStatistics.put(currentOwner, localVal - 1); - } - - for (String candidateName : viableCandidates) { - long val = requireNonNullElse(localStatistics.get(candidateName), 0L); - if (val < leastLoadedCount) { - leastLoadedCount = val; - leastLoadedCandidate = candidateName; - } - } - - if (leastLoadedCandidate == null) { - leastLoadedCandidate = viableCandidates.iterator().next(); - } - - localStatistics.put(leastLoadedCandidate, leastLoadedCount + 1); - return leastLoadedCandidate; - } - - @VisibleForTesting - Map getLocalStatistics() { - return localStatistics; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/main/yang/entity-owners.yang b/opendaylight/md-sal/sal-distributed-eos/src/main/yang/entity-owners.yang deleted file mode 100644 index 0f37e135a7..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/main/yang/entity-owners.yang +++ /dev/null @@ -1,47 +0,0 @@ -module entity-owners { - yang-version 1; - namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:clustering:entity-owners"; - prefix "entity-owners"; - - description - "This module contains the base YANG definitions for - an implementation of the EntityOwnershipService which stores - entity ownership information in the data store"; - - revision "2015-08-04" { - description "Initial revision."; - } - - container entity-owners { - - // A list of all entities grouped by type - list entity-type { - key type; - leaf type { - type string; - } - - list entity { - key id; - - leaf id { - type instance-identifier; - } - - leaf owner { - type string; - } - - // A list of all the candidates that would like to own the entity - list candidate { - key name; - ordered-by user; - - leaf name { - type string; - } - } - } - } - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/AbstractClusterRefEntityOwnershipTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/AbstractClusterRefEntityOwnershipTest.java deleted file mode 100644 index 6d127c366c..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/AbstractClusterRefEntityOwnershipTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016 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.entityownership; - -import akka.actor.ActorSystem; -import akka.testkit.javadsl.TestKit; -import com.typesafe.config.ConfigFactory; -import org.junit.AfterClass; -import org.junit.BeforeClass; - -public class AbstractClusterRefEntityOwnershipTest extends AbstractEntityOwnershipTest { - - private static ActorSystem system; - - @BeforeClass - public static void setUpClass() { - system = ActorSystem.create("test", ConfigFactory.load().getConfig("test-config")); - } - - @AfterClass - public static void tearDownClass() { - TestKit.shutdownActorSystem(system); - system = null; - } - - protected static ActorSystem getSystem() { - return system; - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/AbstractEntityOwnershipTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/AbstractEntityOwnershipTest.java deleted file mode 100644 index 7fbe4d5d73..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/AbstractEntityOwnershipTest.java +++ /dev/null @@ -1,307 +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.entityownership; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.argThat; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.CANDIDATE_NAME_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_ID_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNERS_PATH; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNER_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_TYPE_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.candidatePath; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityPath; - -import akka.pattern.Patterns; -import akka.testkit.TestActorRef; -import akka.util.Timeout; -import com.google.common.base.Stopwatch; -import com.google.common.util.concurrent.Uninterruptibles; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; -import java.util.function.Function; -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.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; -import org.opendaylight.yangtools.yang.common.QName; -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.DataContainerChild; -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.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.FiniteDuration; - -/** - * Abstract base class providing utility methods. - * - * @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(final NormalizedNode node, final String entityType, - final YangInstanceIdentifier entityId, final String candidateName, final boolean expectPresent) { - try { - assertNotNull("Missing " + EntityOwners.QNAME.toString(), node); - assertTrue(node instanceof ContainerNode); - - ContainerNode entityOwnersNode = (ContainerNode) node; - - MapEntryNode entityTypeEntry = getMapEntryNodeChild(entityOwnersNode, EntityType.QNAME, - ENTITY_TYPE_QNAME, entityType, true); - - MapEntryNode entityEntry = getMapEntryNodeChild(entityTypeEntry, ENTITY_QNAME, ENTITY_ID_QNAME, - entityId, true); - - getMapEntryNodeChild(entityEntry, Candidate.QNAME, CANDIDATE_NAME_QNAME, candidateName, expectPresent); - } catch (AssertionError e) { - throw new AssertionError("Verification of entity candidate failed - returned data was: " + node, e); - } - } - - protected void verifyEntityCandidate(final String entityType, final YangInstanceIdentifier entityId, - final String candidateName, final Function reader, - final boolean expectPresent) { - AssertionError lastError = null; - Stopwatch sw = Stopwatch.createStarted(); - while (sw.elapsed(TimeUnit.MILLISECONDS) <= 5000) { - NormalizedNode node = reader.apply(ENTITY_OWNERS_PATH); - try { - verifyEntityCandidate(node, entityType, entityId, candidateName, expectPresent); - return; - } catch (AssertionError e) { - lastError = e; - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - } - } - - throw lastError; - } - - protected void verifyEntityCandidate(final String entityType, final YangInstanceIdentifier entityId, - final String candidateName, final Function reader) { - verifyEntityCandidate(entityType, entityId, candidateName, reader, true); - } - - protected MapEntryNode getMapEntryNodeChild(final DataContainerNode parent, - final QName childMap, final QName child, final Object key, final boolean expectPresent) { - Optional childNode = parent.findChildByArg(new NodeIdentifier(childMap)); - // We have to account for empty maps disappearing. If we expect the entry to be non-present, tolerate a missing - // map. - if (!expectPresent && !childNode.isPresent()) { - return null; - } - - assertTrue("Missing " + childMap.toString(), childNode.isPresent()); - - MapNode entityTypeMapNode = (MapNode) childNode.get(); - Optional entityTypeEntry = entityTypeMapNode.findChildByArg(NodeIdentifierWithPredicates.of( - childMap, child, key)); - if (expectPresent && !entityTypeEntry.isPresent()) { - fail("Missing " + childMap.toString() + " entry for " + key + ". Actual: " + entityTypeMapNode.body()); - } else if (!expectPresent && entityTypeEntry.isPresent()) { - fail("Found unexpected " + childMap.toString() + " entry for " + key); - } - - return entityTypeEntry.isPresent() ? entityTypeEntry.get() : null; - } - - static void verifyOwner(final String expected, final String entityType, final YangInstanceIdentifier entityId, - final Function reader) { - AssertionError lastError = null; - YangInstanceIdentifier entityPath = entityPath(entityType, entityId).node(ENTITY_OWNER_QNAME); - Stopwatch sw = Stopwatch.createStarted(); - while (sw.elapsed(TimeUnit.MILLISECONDS) <= 5000) { - try { - NormalizedNode node = reader.apply(entityPath); - assertNotNull("Owner was not set for entityId: " + entityId, node); - assertEquals("Entity owner", expected, node.body().toString()); - return; - } catch (AssertionError e) { - lastError = e; - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - } - } - - throw lastError; - } - - @SuppressWarnings("checkstyle:IllegalCatch") - static void verifyOwner(final TestActorRef shard, final String entityType, - final YangInstanceIdentifier entityId, final String localMemberName) { - verifyOwner(localMemberName, entityType, entityId, path -> { - try { - return AbstractShardTest.readStore(shard, path); - } catch (Exception e) { - return null; - } - }); - } - - protected void verifyNodeRemoved(final YangInstanceIdentifier path, - final Function reader) { - AssertionError lastError = null; - Stopwatch sw = Stopwatch.createStarted(); - while (sw.elapsed(TimeUnit.MILLISECONDS) <= 5000) { - try { - assertNull("Node was not removed at path: " + path, reader.apply(path)); - return; - } catch (AssertionError e) { - lastError = e; - Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); - } - } - - throw lastError; - } - - static void writeNode(final YangInstanceIdentifier path, final NormalizedNode node, - final ShardDataTree shardDataTree) throws DataValidationFailedException { - DataTreeModification modification = shardDataTree.newModification(); - modification.merge(path, node); - commit(shardDataTree, modification); - } - - static void deleteNode(final YangInstanceIdentifier path, final ShardDataTree shardDataTree) - throws DataValidationFailedException { - DataTreeModification modification = shardDataTree.newModification(); - modification.delete(path); - commit(shardDataTree, modification); - } - - static void commit(final ShardDataTree shardDataTree, final DataTreeModification modification) - throws DataValidationFailedException { - modification.ready(); - shardDataTree.getDataTree().validate(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 argThat(change -> expEntity.equals(change.getEntity()) && expWasOwner == change.getState().wasOwner() - && expIsOwner == change.getState().isOwner() && expHasOwner == change.getState().hasOwner() - && expInJeopardy == change.inJeopardy()); - } - - static DOMEntityOwnershipChange ownershipChange(final DOMEntity expEntity) { - return argThat(change -> expEntity.equals(change.getEntity())); - } - - @SuppressWarnings("checkstyle:IllegalCatch") - static void verifyNoOwnerSet(final TestActorRef shard, final String entityType, - final YangInstanceIdentifier entityId) { - YangInstanceIdentifier entityPath = entityPath(entityType, entityId).node(ENTITY_OWNER_QNAME); - try { - NormalizedNode node = AbstractShardTest.readStore(shard, entityPath); - if (node != null) { - fail("Owner " + node.body() + " was set for " + entityPath); - } - - } catch (Exception e) { - throw new AssertionError("read failed", e); - } - } - - static void verifyRaftState(final TestActorRef shard, - final Consumer verifier) - throws Exception { - AssertionError lastError = null; - Stopwatch sw = Stopwatch.createStarted(); - while (sw.elapsed(TimeUnit.SECONDS) <= 5) { - FiniteDuration operationDuration = FiniteDuration.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(final String memberName) { - return ShardIdentifier.create("entity-ownership", MemberName.forName(memberName), - "operational" + NEXT_SHARD_NUM.getAndIncrement()); - } - - @SuppressWarnings("checkstyle:IllegalCatch") - void verifyEntityCandidateRemoved(final TestActorRef shard, final String entityType, - final YangInstanceIdentifier entityId, final 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, - final String entityType, final YangInstanceIdentifier entityId, final 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, final String entityType, - final YangInstanceIdentifier entityId, final 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); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/CandidateListChangeListenerTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/CandidateListChangeListenerTest.java deleted file mode 100644 index 24cfca69a9..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/CandidateListChangeListenerTest.java +++ /dev/null @@ -1,117 +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.entityownership; - -import static org.junit.Assert.assertEquals; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNERS_PATH; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.candidatePath; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityOwnersWithCandidate; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityPath; - -import akka.testkit.javadsl.TestKit; -import com.google.common.collect.ImmutableSet; -import java.time.Duration; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.cluster.datastore.AbstractActorTest; -import org.opendaylight.controller.cluster.datastore.Shard; -import org.opendaylight.controller.cluster.datastore.ShardDataTree; -import org.opendaylight.controller.cluster.entityownership.messages.CandidateAdded; -import org.opendaylight.controller.cluster.entityownership.messages.CandidateRemoved; -import org.opendaylight.yangtools.yang.common.QName; -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.DataValidationFailedException; -import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; - -/** - * Unit tests for CandidateListChangeListener. - * - * @author Thomas Pantelis - */ -public class CandidateListChangeListenerTest extends AbstractActorTest { - private static final String ENTITY_TYPE = "test"; - private static final YangInstanceIdentifier ENTITY_ID1 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity1")); - private static final YangInstanceIdentifier ENTITY_ID2 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity2")); - - private ShardDataTree shardDataTree; - - @Mock - private Shard mockShard; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - shardDataTree = new ShardDataTree(mockShard, EOSTestUtils.SCHEMA_CONTEXT, TreeType.OPERATIONAL); - } - - @Test - public void testOnDataTreeChanged() throws Exception { - TestKit kit = new TestKit(getSystem()); - - new CandidateListChangeListener(kit.getRef(), "test").init(shardDataTree); - - String memberName1 = "member-1"; - writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, ENTITY_ID1, memberName1)); - - CandidateAdded candidateAdded = kit.expectMsgClass(CandidateAdded.class); - assertEquals("getEntityId", entityPath(ENTITY_TYPE, ENTITY_ID1), candidateAdded.getEntityPath()); - assertEquals("getNewCandidate", memberName1, candidateAdded.getNewCandidate()); - assertEquals("getAllCandidates", ImmutableSet.of(memberName1), - ImmutableSet.copyOf(candidateAdded.getAllCandidates())); - - writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, ENTITY_ID1, memberName1)); - kit.expectNoMessage(Duration.ofMillis(500)); - - String memberName2 = "member-2"; - writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, ENTITY_ID1, memberName2)); - - candidateAdded = kit.expectMsgClass(CandidateAdded.class); - assertEquals("getEntityId", entityPath(ENTITY_TYPE, ENTITY_ID1), candidateAdded.getEntityPath()); - assertEquals("getNewCandidate", memberName2, candidateAdded.getNewCandidate()); - assertEquals("getAllCandidates", ImmutableSet.of(memberName1, memberName2), - ImmutableSet.copyOf(candidateAdded.getAllCandidates())); - - writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, ENTITY_ID2, memberName1)); - - candidateAdded = kit.expectMsgClass(CandidateAdded.class); - assertEquals("getEntityId", entityPath(ENTITY_TYPE, ENTITY_ID2), candidateAdded.getEntityPath()); - assertEquals("getNewCandidate", memberName1, candidateAdded.getNewCandidate()); - assertEquals("getAllCandidates", ImmutableSet.of(memberName1), - ImmutableSet.copyOf(candidateAdded.getAllCandidates())); - - deleteNode(candidatePath(ENTITY_TYPE, ENTITY_ID1, memberName1)); - - CandidateRemoved candidateRemoved = kit.expectMsgClass(CandidateRemoved.class); - assertEquals("getEntityId", entityPath(ENTITY_TYPE, ENTITY_ID1), candidateRemoved.getEntityPath()); - assertEquals("getRemovedCandidate", memberName1, candidateRemoved.getRemovedCandidate()); - assertEquals("getRemainingCandidates", ImmutableSet.of(memberName2), - ImmutableSet.copyOf(candidateRemoved.getRemainingCandidates())); - - deleteNode(candidatePath(ENTITY_TYPE, ENTITY_ID1, memberName2)); - - candidateRemoved = kit.expectMsgClass(CandidateRemoved.class); - assertEquals("getEntityId", entityPath(ENTITY_TYPE, ENTITY_ID1), candidateRemoved.getEntityPath()); - assertEquals("getRemovedCandidate", memberName2, candidateRemoved.getRemovedCandidate()); - assertEquals("getRemainingCandidates", ImmutableSet.of(), - ImmutableSet.copyOf(candidateRemoved.getRemainingCandidates())); - } - - private void writeNode(final YangInstanceIdentifier path, final NormalizedNode node) - throws DataValidationFailedException { - AbstractEntityOwnershipTest.writeNode(path, node, shardDataTree); - } - - private void deleteNode(final YangInstanceIdentifier path) throws DataValidationFailedException { - AbstractEntityOwnershipTest.deleteNode(path, shardDataTree); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipIntegrationTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipIntegrationTest.java deleted file mode 100644 index bcef25ee33..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipIntegrationTest.java +++ /dev/null @@ -1,883 +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.entityownership; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.AdditionalMatchers.or; -import static org.mockito.Mockito.doNothing; -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.MemberNode.verifyRaftState; -import static org.opendaylight.controller.cluster.entityownership.AbstractEntityOwnershipTest.ownershipChange; -import static org.opendaylight.controller.cluster.entityownership.DistributedEntityOwnershipService.ENTITY_OWNERSHIP_SHARD_NAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.CANDIDATE_NAME_NODE_ID; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityPath; - -import akka.actor.ActorRef; -import akka.actor.Status.Failure; -import akka.actor.Status.Success; -import akka.cluster.Cluster; -import akka.pattern.Patterns; -import akka.util.Timeout; -import com.google.common.base.Stopwatch; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Uninterruptibles; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.mockito.exceptions.base.MockitoException; -import org.opendaylight.controller.cluster.datastore.AbstractDataStore; -import org.opendaylight.controller.cluster.datastore.DatastoreContext; -import org.opendaylight.controller.cluster.datastore.IntegrationTestKit; -import org.opendaylight.controller.cluster.datastore.MemberNode; -import org.opendaylight.controller.cluster.datastore.messages.AddShardReplica; -import org.opendaylight.controller.cluster.datastore.messages.ChangeShardMembersVotingStatus; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.EntityOwnerSelectionStrategyConfig; -import org.opendaylight.controller.cluster.raft.policy.DisableElectionsRaftPolicy; -import org.opendaylight.controller.cluster.raft.utils.InMemoryJournal; -import org.opendaylight.controller.cluster.raft.utils.InMemorySnapshotStore; -import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService; -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; -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 scala.concurrent.Await; -import scala.concurrent.Future; -import scala.concurrent.duration.FiniteDuration; - -/** - * End-to-end integration tests for the entity ownership functionality. - * - * @author Thomas Pantelis - */ -public class DistributedEntityOwnershipIntegrationTest { - private static final String MODULE_SHARDS_CONFIG = "module-shards-default.conf"; - private static final String MODULE_SHARDS_5_NODE_CONFIG = "module-shards-default-5-node.conf"; - private static final String MODULE_SHARDS_MEMBER_1_CONFIG = "module-shards-default-member-1.conf"; - private static final String ENTITY_TYPE1 = "entityType1"; - private static final String ENTITY_TYPE2 = "entityType2"; - private static final DOMEntity ENTITY1 = new DOMEntity(ENTITY_TYPE1, "entity1"); - private static final DOMEntity ENTITY1_2 = new DOMEntity(ENTITY_TYPE2, "entity1"); - private static final DOMEntity ENTITY2 = new DOMEntity(ENTITY_TYPE1, "entity2"); - private static final DOMEntity ENTITY3 = new DOMEntity(ENTITY_TYPE1, "entity3"); - private static final DOMEntity ENTITY4 = new DOMEntity(ENTITY_TYPE1, "entity4"); - private final DatastoreContext.Builder leaderDatastoreContextBuilder = - DatastoreContext.newBuilder().shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(5) - .shardIsolatedLeaderCheckIntervalInMillis(1000000); - - private final DatastoreContext.Builder followerDatastoreContextBuilder = - DatastoreContext.newBuilder().shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(10000); - - private final List memberNodes = new ArrayList<>(); - - @Mock - private DOMEntityOwnershipListener leaderMockListener; - - @Mock - private DOMEntityOwnershipListener leaderMockListener2; - - @Mock - private DOMEntityOwnershipListener follower1MockListener; - - @Mock - private DOMEntityOwnershipListener follower2MockListener; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - InMemoryJournal.clear(); - InMemorySnapshotStore.clear(); - } - - @After - public void tearDown() { - for (MemberNode m : Lists.reverse(memberNodes)) { - m.cleanup(); - } - memberNodes.clear(); - } - - private static DistributedEntityOwnershipService newOwnershipService(final AbstractDataStore datastore) { - return DistributedEntityOwnershipService.start(datastore.getActorUtils(), - EntityOwnerSelectionStrategyConfig.newBuilder().build()); - } - - @Test - public void testFunctionalityWithThreeNodes() throws Exception { - String name = "testFunctionalityWithThreeNodes"; - MemberNode leaderNode = MemberNode.builder(memberNodes).akkaConfig("Member1").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(leaderDatastoreContextBuilder).build(); - - MemberNode follower1Node = MemberNode.builder(memberNodes).akkaConfig("Member2").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - MemberNode follower2Node = MemberNode.builder(memberNodes).akkaConfig("Member3").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - AbstractDataStore leaderDistributedDataStore = leaderNode.configDataStore(); - - leaderDistributedDataStore.waitTillReady(); - follower1Node.configDataStore().waitTillReady(); - follower2Node.configDataStore().waitTillReady(); - - final DOMEntityOwnershipService leaderEntityOwnershipService = newOwnershipService(leaderDistributedDataStore); - final DOMEntityOwnershipService follower1EntityOwnershipService = - newOwnershipService(follower1Node.configDataStore()); - final DOMEntityOwnershipService follower2EntityOwnershipService = - newOwnershipService(follower2Node.configDataStore()); - - leaderNode.kit().waitUntilLeader(leaderNode.configDataStore().getActorUtils(), ENTITY_OWNERSHIP_SHARD_NAME); - - 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); - verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1, false, true, true)); - verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1, false, false, true)); - reset(leaderMockListener, follower1MockListener); - - verifyGetOwnershipState(leaderEntityOwnershipService, ENTITY1, EntityOwnershipState.IS_OWNER); - verifyGetOwnershipState(follower1EntityOwnershipService, ENTITY1, EntityOwnershipState.OWNED_BY_OTHER); - - // Register leader candidate for entity1_2 (same id, different type) and verify it becomes owner - - leaderEntityOwnershipService.registerCandidate(ENTITY1_2); - verify(leaderMockListener2, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1_2, false, true, true)); - Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(leaderMockListener, 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); - verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-1", "member-2"); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-1"); - verifyOwner(follower2Node.configDataStore(), ENTITY1, "member-1"); - Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(leaderMockListener, never()).ownershipChanged(ownershipChange(ENTITY1)); - verify(follower1MockListener, never()).ownershipChanged(ownershipChange(ENTITY1)); - - // Register follower1 candidate for entity2 and verify it becomes owner - - final DOMEntityOwnershipCandidateRegistration follower1Entity2Reg = - follower1EntityOwnershipService.registerCandidate(ENTITY2); - verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, true, true)); - verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, false, true)); - verifyOwner(follower2Node.configDataStore(), ENTITY2, "member-2"); - reset(leaderMockListener, follower1MockListener); - - // Register follower2 candidate for entity2 and verify it gets added but doesn't become owner - - follower2EntityOwnershipService.registerListener(ENTITY_TYPE1, follower2MockListener); - verify(follower2MockListener, timeout(5000).times(2)).ownershipChanged(or( - ownershipChange(ENTITY1, false, false, true), ownershipChange(ENTITY2, false, false, true))); - - follower2EntityOwnershipService.registerCandidate(ENTITY2); - verifyCandidates(leaderDistributedDataStore, ENTITY2, "member-2", "member-3"); - verifyOwner(leaderDistributedDataStore, ENTITY2, "member-2"); - - // Unregister follower1 candidate for entity2 and verify follower2 becomes owner - - follower1Entity2Reg.close(); - verifyCandidates(leaderDistributedDataStore, ENTITY2, "member-3"); - verifyOwner(leaderDistributedDataStore, ENTITY2, "member-3"); - verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, true, false, true)); - verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, false, true)); - - // Depending on timing, follower2MockListener could get ownershipChanged with "false, false, true" if - // if the original ownership change with "member-2 is replicated to follower2 after the listener is - // registered. - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verify(follower2MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, true, true)); - - // Register follower1 candidate for entity3 and verify it becomes owner - - follower1EntityOwnershipService.registerCandidate(ENTITY3); - 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); - 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(follower1MockListener, follower2MockListener); - - // Register follower1 candidate for entity4 and verify it gets added but doesn't become owner - - follower1EntityOwnershipService.registerCandidate(ENTITY4); - verifyCandidates(leaderDistributedDataStore, ENTITY4, "member-3", "member-2"); - verifyOwner(leaderDistributedDataStore, ENTITY4, "member-3"); - - // Shutdown follower2 and verify it's owned entities (entity 4) get re-assigned - - reset(leaderMockListener, follower1MockListener); - follower2Node.cleanup(); - - verify(follower1MockListener, timeout(15000)).ownershipChanged(ownershipChange(ENTITY4, false, true, true)); - verify(leaderMockListener, timeout(15000)).ownershipChanged(ownershipChange(ENTITY4, false, false, true)); - - // Register leader candidate for entity2 and verify it becomes owner - - DOMEntityOwnershipCandidateRegistration leaderEntity2Reg = - leaderEntityOwnershipService.registerCandidate(ENTITY2); - verifyOwner(leaderDistributedDataStore, ENTITY2, "member-1"); - verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, true, true)); - - // Unregister leader candidate for entity2 and verify the owner is cleared - - leaderEntity2Reg.close(); - verifyOwner(leaderDistributedDataStore, ENTITY2, ""); - verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, true, false, false)); - verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY2, false, false, false)); - } - - @Test - public void testLeaderEntityOwnersReassignedAfterShutdown() throws Exception { - followerDatastoreContextBuilder.shardElectionTimeoutFactor(5) - .customRaftPolicyImplementation(DisableElectionsRaftPolicy.class.getName()); - - String name = "testLeaderEntityOwnersReassignedAfterShutdown"; - MemberNode leaderNode = MemberNode.builder(memberNodes).akkaConfig("Member1").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(leaderDatastoreContextBuilder).build(); - - MemberNode follower1Node = MemberNode.builder(memberNodes).akkaConfig("Member2").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - MemberNode follower2Node = MemberNode.builder(memberNodes).akkaConfig("Member3").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - AbstractDataStore leaderDistributedDataStore = leaderNode.configDataStore(); - - leaderDistributedDataStore.waitTillReady(); - follower1Node.configDataStore().waitTillReady(); - follower2Node.configDataStore().waitTillReady(); - - follower1Node.waitForMembersUp("member-1", "member-3"); - - final DOMEntityOwnershipService leaderEntityOwnershipService = newOwnershipService(leaderDistributedDataStore); - final DOMEntityOwnershipService follower1EntityOwnershipService = - newOwnershipService(follower1Node.configDataStore()); - final DOMEntityOwnershipService follower2EntityOwnershipService = - newOwnershipService(follower2Node.configDataStore()); - - leaderNode.kit().waitUntilLeader(leaderNode.configDataStore().getActorUtils(), ENTITY_OWNERSHIP_SHARD_NAME); - - // Register follower1 candidate for entity1 and verify it becomes owner - - follower1EntityOwnershipService.registerCandidate(ENTITY1); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-2"); - - // Register leader candidate for entity1 - - leaderEntityOwnershipService.registerCandidate(ENTITY1); - verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-2", "member-1"); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-2"); - - // Register leader candidate for entity2 and verify it becomes owner - - leaderEntityOwnershipService.registerCandidate(ENTITY2); - verifyOwner(leaderDistributedDataStore, ENTITY2, "member-1"); - - // Register follower2 candidate for entity2 - - follower2EntityOwnershipService.registerCandidate(ENTITY2); - verifyCandidates(leaderDistributedDataStore, ENTITY2, "member-1", "member-3"); - verifyOwner(leaderDistributedDataStore, ENTITY2, "member-1"); - - // Re-enable elections on all remaining followers so one becomes the new leader - - ActorRef follower1Shard = IntegrationTestKit.findLocalShard(follower1Node.configDataStore().getActorUtils(), - ENTITY_OWNERSHIP_SHARD_NAME); - follower1Shard.tell(DatastoreContext.newBuilderFrom(followerDatastoreContextBuilder.build()) - .customRaftPolicyImplementation(null).build(), ActorRef.noSender()); - - ActorRef follower2Shard = IntegrationTestKit.findLocalShard(follower2Node.configDataStore().getActorUtils(), - ENTITY_OWNERSHIP_SHARD_NAME); - follower2Shard.tell(DatastoreContext.newBuilderFrom(followerDatastoreContextBuilder.build()) - .customRaftPolicyImplementation(null).build(), ActorRef.noSender()); - - // Shutdown the leader and verify its removed from the candidate list - - leaderNode.cleanup(); - follower1Node.waitForMemberDown("member-1"); - follower2Node.waitForMemberDown("member-1"); - - // Verify the prior leader's entity owners are re-assigned. - - verifyCandidates(follower1Node.configDataStore(), ENTITY1, "member-2", "member-1"); - verifyCandidates(follower1Node.configDataStore(), ENTITY2, "member-1", "member-3"); - verifyOwner(follower1Node.configDataStore(), ENTITY1, "member-2"); - verifyOwner(follower1Node.configDataStore(), ENTITY2, "member-3"); - } - - @Test - public void testLeaderAndFollowerEntityOwnersReassignedAfterShutdown() throws Exception { - followerDatastoreContextBuilder.shardElectionTimeoutFactor(5) - .customRaftPolicyImplementation(DisableElectionsRaftPolicy.class.getName()); - - String name = "testLeaderAndFollowerEntityOwnersReassignedAfterShutdown"; - final MemberNode leaderNode = MemberNode.builder(memberNodes).akkaConfig("Member1") - .useAkkaArtery(false).testName(name) - .moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG) - .schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(leaderDatastoreContextBuilder).build(); - - final MemberNode follower1Node = MemberNode.builder(memberNodes).akkaConfig("Member2") - .testName(name).moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG) - .useAkkaArtery(false).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - final MemberNode follower2Node = MemberNode.builder(memberNodes).akkaConfig("Member3") - .testName(name).moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG) - .useAkkaArtery(false).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - final MemberNode follower3Node = MemberNode.builder(memberNodes).akkaConfig("Member4") - .testName(name).moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG) - .useAkkaArtery(false).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - final MemberNode follower4Node = MemberNode.builder(memberNodes).akkaConfig("Member5") - .testName(name).moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG) - .useAkkaArtery(false).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - AbstractDataStore leaderDistributedDataStore = leaderNode.configDataStore(); - - leaderDistributedDataStore.waitTillReady(); - follower1Node.configDataStore().waitTillReady(); - follower2Node.configDataStore().waitTillReady(); - follower3Node.configDataStore().waitTillReady(); - follower4Node.configDataStore().waitTillReady(); - - leaderNode.waitForMembersUp("member-2", "member-3", "member-4", "member-5"); - follower1Node.waitForMembersUp("member-1", "member-3", "member-4", "member-5"); - - final DOMEntityOwnershipService leaderEntityOwnershipService = newOwnershipService(leaderDistributedDataStore); - final DOMEntityOwnershipService follower1EntityOwnershipService = - newOwnershipService(follower1Node.configDataStore()); - final DOMEntityOwnershipService follower2EntityOwnershipService = - newOwnershipService(follower2Node.configDataStore()); - final DOMEntityOwnershipService follower3EntityOwnershipService = - newOwnershipService(follower3Node.configDataStore()); - newOwnershipService(follower4Node.configDataStore()); - - leaderNode.kit().waitUntilLeader(leaderNode.configDataStore().getActorUtils(), ENTITY_OWNERSHIP_SHARD_NAME); - - // Register follower1 candidate for entity1 and verify it becomes owner - - follower1EntityOwnershipService.registerCandidate(ENTITY1); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-2"); - - // Register leader candidate for entity1 - - leaderEntityOwnershipService.registerCandidate(ENTITY1); - verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-2", "member-1"); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-2"); - - // Register leader candidate for entity2 and verify it becomes owner - - leaderEntityOwnershipService.registerCandidate(ENTITY2); - verifyOwner(leaderDistributedDataStore, ENTITY2, "member-1"); - - // Register follower2 candidate for entity2 - - follower2EntityOwnershipService.registerCandidate(ENTITY2); - verifyCandidates(leaderDistributedDataStore, ENTITY2, "member-1", "member-3"); - verifyOwner(leaderDistributedDataStore, ENTITY2, "member-1"); - - // Register follower3 as a candidate for entity2 as well - - follower3EntityOwnershipService.registerCandidate(ENTITY2); - verifyCandidates(leaderDistributedDataStore, ENTITY2, "member-1", "member-3", "member-4"); - verifyOwner(leaderDistributedDataStore, ENTITY2, "member-1"); - - // Re-enable elections on all remaining followers so one becomes the new leader - - ActorRef follower1Shard = IntegrationTestKit.findLocalShard(follower1Node.configDataStore().getActorUtils(), - ENTITY_OWNERSHIP_SHARD_NAME); - follower1Shard.tell(DatastoreContext.newBuilderFrom(followerDatastoreContextBuilder.build()) - .customRaftPolicyImplementation(null).build(), ActorRef.noSender()); - - ActorRef follower2Shard = IntegrationTestKit.findLocalShard(follower2Node.configDataStore().getActorUtils(), - ENTITY_OWNERSHIP_SHARD_NAME); - follower2Shard.tell(DatastoreContext.newBuilderFrom(followerDatastoreContextBuilder.build()) - .customRaftPolicyImplementation(null).build(), ActorRef.noSender()); - - ActorRef follower4Shard = IntegrationTestKit.findLocalShard(follower4Node.configDataStore().getActorUtils(), - ENTITY_OWNERSHIP_SHARD_NAME); - follower4Shard.tell(DatastoreContext.newBuilderFrom(followerDatastoreContextBuilder.build()) - .customRaftPolicyImplementation(null).build(), ActorRef.noSender()); - - // Shutdown the leader and follower3 - - leaderNode.cleanup(); - - follower1Node.waitForMemberDown("member-1"); - follower2Node.waitForMemberDown("member-1"); - follower4Node.waitForMemberDown("member-1"); - - follower3Node.cleanup(); - - follower1Node.waitForMemberDown("member-4"); - follower2Node.waitForMemberDown("member-4"); - follower4Node.waitForMemberDown("member-4"); - - // Verify the prior leader's and follower3 entity owners are re-assigned. - - verifyCandidates(follower1Node.configDataStore(), ENTITY1, "member-2", "member-1"); - verifyCandidates(follower1Node.configDataStore(), ENTITY2, "member-1", "member-3", "member-4"); - verifyOwner(follower1Node.configDataStore(), ENTITY1, "member-2"); - verifyOwner(follower1Node.configDataStore(), ENTITY2, "member-3"); - } - - /** - * Reproduces bug 4554. - */ - @Test - public void testCloseCandidateRegistrationInQuickSuccession() throws Exception { - String name = "testCloseCandidateRegistrationInQuickSuccession"; - MemberNode leaderNode = MemberNode.builder(memberNodes).akkaConfig("Member1").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(leaderDatastoreContextBuilder).build(); - - MemberNode follower1Node = MemberNode.builder(memberNodes).akkaConfig("Member2").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - MemberNode follower2Node = MemberNode.builder(memberNodes).akkaConfig("Member3").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - AbstractDataStore leaderDistributedDataStore = leaderNode.configDataStore(); - - leaderDistributedDataStore.waitTillReady(); - follower1Node.configDataStore().waitTillReady(); - follower2Node.configDataStore().waitTillReady(); - - final DOMEntityOwnershipService leaderEntityOwnershipService = newOwnershipService(leaderDistributedDataStore); - final DOMEntityOwnershipService follower1EntityOwnershipService = - newOwnershipService(follower1Node.configDataStore()); - final DOMEntityOwnershipService follower2EntityOwnershipService = - newOwnershipService(follower2Node.configDataStore()); - - leaderNode.kit().waitUntilLeader(leaderNode.configDataStore().getActorUtils(), ENTITY_OWNERSHIP_SHARD_NAME); - - leaderEntityOwnershipService.registerListener(ENTITY_TYPE1, leaderMockListener); - follower1EntityOwnershipService.registerListener(ENTITY_TYPE1, follower1MockListener); - follower2EntityOwnershipService.registerListener(ENTITY_TYPE1, follower2MockListener); - - final DOMEntityOwnershipCandidateRegistration candidate1 = - leaderEntityOwnershipService.registerCandidate(ENTITY1); - verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1, false, true, true)); - - final DOMEntityOwnershipCandidateRegistration candidate2 = - follower1EntityOwnershipService.registerCandidate(ENTITY1); - verify(follower1MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1, false, false, true)); - - final DOMEntityOwnershipCandidateRegistration candidate3 = - follower2EntityOwnershipService.registerCandidate(ENTITY1); - verify(follower2MockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1, false, false, true)); - - Mockito.reset(leaderMockListener, follower1MockListener, follower2MockListener); - - ArgumentCaptor leaderChangeCaptor = - ArgumentCaptor.forClass(DOMEntityOwnershipChange.class); - ArgumentCaptor follower1ChangeCaptor = - ArgumentCaptor.forClass(DOMEntityOwnershipChange.class); - ArgumentCaptor follower2ChangeCaptor = - ArgumentCaptor.forClass(DOMEntityOwnershipChange.class); - doNothing().when(leaderMockListener).ownershipChanged(leaderChangeCaptor.capture()); - doNothing().when(follower1MockListener).ownershipChanged(follower1ChangeCaptor.capture()); - doNothing().when(follower2MockListener).ownershipChanged(follower2ChangeCaptor.capture()); - - candidate1.close(); - candidate2.close(); - candidate3.close(); - - boolean passed = false; - for (int i = 0; i < 100; i++) { - Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS); - final Optional leaderState = leaderEntityOwnershipService.getOwnershipState(ENTITY1); - final Optional follower1State = - follower1EntityOwnershipService.getOwnershipState(ENTITY1); - final Optional follower2State = - follower2EntityOwnershipService.getOwnershipState(ENTITY1); - final Optional leaderChange = getValueSafely(leaderChangeCaptor); - final Optional follower1Change = getValueSafely(follower1ChangeCaptor); - final Optional follower2Change = getValueSafely(follower2ChangeCaptor); - if (!leaderState.isPresent() || leaderState.get() == EntityOwnershipState.NO_OWNER - && follower1State.isPresent() && follower1State.get() == EntityOwnershipState.NO_OWNER - && follower2State.isPresent() && follower2State.get() == EntityOwnershipState.NO_OWNER - && leaderChange.isPresent() && !leaderChange.get().getState().hasOwner() - && follower1Change.isPresent() && !follower1Change.get().getState().hasOwner() - && follower2Change.isPresent() && !follower2Change.get().getState().hasOwner()) { - passed = true; - break; - } - } - - assertTrue("No ownership change message was sent with hasOwner=false", passed); - } - - private static Optional getValueSafely( - final ArgumentCaptor captor) { - try { - return Optional.ofNullable(captor.getValue()); - } catch (MockitoException e) { - // No value was captured - return Optional.empty(); - } - } - - /** - * Tests bootstrapping the entity-ownership shard when there's no shards initially configured for local - * member. The entity-ownership shard is initially created as inactive (ie remains a follower), requiring - * an AddShardReplica request to join it to an existing leader. - */ - @Test - public void testEntityOwnershipShardBootstrapping() throws Exception { - String name = "testEntityOwnershipShardBootstrapping"; - String moduleShardsConfig = MODULE_SHARDS_MEMBER_1_CONFIG; - MemberNode leaderNode = MemberNode.builder(memberNodes).akkaConfig("Member1").testName(name) - .moduleShardsConfig(moduleShardsConfig).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(leaderDatastoreContextBuilder).build(); - - AbstractDataStore leaderDistributedDataStore = leaderNode.configDataStore(); - final DOMEntityOwnershipService leaderEntityOwnershipService = newOwnershipService(leaderDistributedDataStore); - - leaderNode.kit().waitUntilLeader(leaderNode.configDataStore().getActorUtils(), ENTITY_OWNERSHIP_SHARD_NAME); - - MemberNode follower1Node = MemberNode.builder(memberNodes).akkaConfig("Member2").testName(name) - .moduleShardsConfig(moduleShardsConfig).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - AbstractDataStore follower1DistributedDataStore = follower1Node.configDataStore(); - follower1DistributedDataStore.waitTillReady(); - - leaderNode.waitForMembersUp("member-2"); - follower1Node.waitForMembersUp("member-1"); - - DOMEntityOwnershipService follower1EntityOwnershipService = newOwnershipService(follower1DistributedDataStore); - - leaderEntityOwnershipService.registerListener(ENTITY_TYPE1, leaderMockListener); - - // Register a candidate for follower1 - should get queued since follower1 has no leader - final DOMEntityOwnershipCandidateRegistration candidateReg = - follower1EntityOwnershipService.registerCandidate(ENTITY1); - Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(leaderMockListener, never()).ownershipChanged(ownershipChange(ENTITY1)); - - // Add replica in follower1 - AddShardReplica addReplica = new AddShardReplica(ENTITY_OWNERSHIP_SHARD_NAME); - follower1DistributedDataStore.getActorUtils().getShardManager().tell(addReplica, - follower1Node.kit().getRef()); - Object reply = follower1Node.kit().expectMsgAnyClassOf(follower1Node.kit().duration("5 sec"), - Success.class, Failure.class); - if (reply instanceof Failure) { - throw new AssertionError("AddShardReplica failed", ((Failure)reply).cause()); - } - - // The queued candidate registration should proceed - verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1, false, false, true)); - reset(leaderMockListener); - - candidateReg.close(); - verify(leaderMockListener, timeout(5000)).ownershipChanged(ownershipChange(ENTITY1, false, false, false)); - reset(leaderMockListener); - - // Restart follower1 and verify the entity ownership shard is re-instated by registering. - Cluster.get(leaderNode.kit().getSystem()).down(Cluster.get(follower1Node.kit().getSystem()).selfAddress()); - follower1Node.cleanup(); - - follower1Node = MemberNode.builder(memberNodes).akkaConfig("Member2").testName(name) - .moduleShardsConfig(moduleShardsConfig).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - follower1EntityOwnershipService = newOwnershipService(follower1Node.configDataStore()); - - follower1EntityOwnershipService.registerCandidate(ENTITY1); - verify(leaderMockListener, timeout(20000)).ownershipChanged(ownershipChange(ENTITY1, false, false, true)); - - verifyRaftState(follower1Node.configDataStore(), ENTITY_OWNERSHIP_SHARD_NAME, raftState -> { - assertNull("Custom RaftPolicy class name", raftState.getCustomRaftPolicyClassName()); - assertEquals("Peer count", 1, raftState.getPeerAddresses().keySet().size()); - assertThat("Peer Id", Iterables.getLast(raftState.getPeerAddresses().keySet()), - org.hamcrest.CoreMatchers.containsString("member-1")); - }); - } - - @Test - public void testOwnerSelectedOnRapidUnregisteringAndRegisteringOfCandidates() throws Exception { - String name = "testOwnerSelectedOnRapidUnregisteringAndRegisteringOfCandidates"; - MemberNode leaderNode = MemberNode.builder(memberNodes).akkaConfig("Member1").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(leaderDatastoreContextBuilder).build(); - - MemberNode follower1Node = MemberNode.builder(memberNodes).akkaConfig("Member2").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - MemberNode follower2Node = MemberNode.builder(memberNodes).akkaConfig("Member3").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - AbstractDataStore leaderDistributedDataStore = leaderNode.configDataStore(); - - leaderDistributedDataStore.waitTillReady(); - follower1Node.configDataStore().waitTillReady(); - follower2Node.configDataStore().waitTillReady(); - - final DOMEntityOwnershipService leaderEntityOwnershipService = newOwnershipService(leaderDistributedDataStore); - final DOMEntityOwnershipService follower1EntityOwnershipService = - newOwnershipService(follower1Node.configDataStore()); - newOwnershipService(follower2Node.configDataStore()); - - leaderNode.kit().waitUntilLeader(leaderNode.configDataStore().getActorUtils(), ENTITY_OWNERSHIP_SHARD_NAME); - - // Register leader candidate for entity1 and verify it becomes owner - - DOMEntityOwnershipCandidateRegistration leaderEntity1Reg = - leaderEntityOwnershipService.registerCandidate(ENTITY1); - - verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-1"); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-1"); - - leaderEntity1Reg.close(); - follower1EntityOwnershipService.registerCandidate(ENTITY1); - - verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-2"); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-2"); - } - - @Test - public void testOwnerSelectedOnRapidRegisteringAndUnregisteringOfCandidates() throws Exception { - String name = "testOwnerSelectedOnRapidRegisteringAndUnregisteringOfCandidates"; - MemberNode leaderNode = MemberNode.builder(memberNodes).akkaConfig("Member1").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(leaderDatastoreContextBuilder).build(); - - MemberNode follower1Node = MemberNode.builder(memberNodes).akkaConfig("Member2").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - MemberNode follower2Node = MemberNode.builder(memberNodes).akkaConfig("Member3").testName(name) - .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - AbstractDataStore leaderDistributedDataStore = leaderNode.configDataStore(); - - leaderDistributedDataStore.waitTillReady(); - follower1Node.configDataStore().waitTillReady(); - follower2Node.configDataStore().waitTillReady(); - - final DOMEntityOwnershipService leaderEntityOwnershipService = newOwnershipService(leaderDistributedDataStore); - final DOMEntityOwnershipService follower1EntityOwnershipService = - newOwnershipService(follower1Node.configDataStore()); - newOwnershipService(follower2Node.configDataStore()); - - leaderNode.kit().waitUntilLeader(leaderNode.configDataStore().getActorUtils(), ENTITY_OWNERSHIP_SHARD_NAME); - - // Register leader candidate for entity1 and verify it becomes owner - - final DOMEntityOwnershipCandidateRegistration leaderEntity1Reg = - leaderEntityOwnershipService.registerCandidate(ENTITY1); - - verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-1"); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-1"); - - follower1EntityOwnershipService.registerCandidate(ENTITY1); - leaderEntity1Reg.close(); - - verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-2"); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-2"); - } - - @Test - public void testEntityOwnershipWithNonVotingMembers() throws Exception { - followerDatastoreContextBuilder.shardElectionTimeoutFactor(5) - .customRaftPolicyImplementation(DisableElectionsRaftPolicy.class.getName()); - - String name = "testEntityOwnershipWithNonVotingMembers"; - final MemberNode member1LeaderNode = MemberNode.builder(memberNodes).akkaConfig("Member1") - .useAkkaArtery(false).testName(name) - .moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(leaderDatastoreContextBuilder).build(); - - final MemberNode member2FollowerNode = MemberNode.builder(memberNodes).akkaConfig("Member2") - .useAkkaArtery(false).testName(name) - .moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - final MemberNode member3FollowerNode = MemberNode.builder(memberNodes).akkaConfig("Member3") - .useAkkaArtery(false).testName(name) - .moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - final MemberNode member4FollowerNode = MemberNode.builder(memberNodes).akkaConfig("Member4") - .useAkkaArtery(false).testName(name) - .moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - final MemberNode member5FollowerNode = MemberNode.builder(memberNodes).akkaConfig("Member5") - .useAkkaArtery(false).testName(name) - .moduleShardsConfig(MODULE_SHARDS_5_NODE_CONFIG).schemaContext(EOSTestUtils.SCHEMA_CONTEXT) - .createOperDatastore(false).datastoreContextBuilder(followerDatastoreContextBuilder).build(); - - AbstractDataStore leaderDistributedDataStore = member1LeaderNode.configDataStore(); - - leaderDistributedDataStore.waitTillReady(); - member2FollowerNode.configDataStore().waitTillReady(); - member3FollowerNode.configDataStore().waitTillReady(); - member4FollowerNode.configDataStore().waitTillReady(); - member5FollowerNode.configDataStore().waitTillReady(); - - member1LeaderNode.waitForMembersUp("member-2", "member-3", "member-4", "member-5"); - - final DOMEntityOwnershipService member3EntityOwnershipService = - newOwnershipService(member3FollowerNode.configDataStore()); - final DOMEntityOwnershipService member4EntityOwnershipService = - newOwnershipService(member4FollowerNode.configDataStore()); - final DOMEntityOwnershipService member5EntityOwnershipService = - newOwnershipService(member5FollowerNode.configDataStore()); - - newOwnershipService(member1LeaderNode.configDataStore()); - member1LeaderNode.kit().waitUntilLeader(member1LeaderNode.configDataStore().getActorUtils(), - ENTITY_OWNERSHIP_SHARD_NAME); - - // Make member4 and member5 non-voting - - Future future = Patterns.ask(leaderDistributedDataStore.getActorUtils().getShardManager(), - new ChangeShardMembersVotingStatus(ENTITY_OWNERSHIP_SHARD_NAME, - ImmutableMap.of("member-4", Boolean.FALSE, "member-5", Boolean.FALSE)), - new Timeout(10, TimeUnit.SECONDS)); - Object response = Await.result(future, FiniteDuration.apply(10, TimeUnit.SECONDS)); - if (response instanceof Throwable) { - throw new AssertionError("ChangeShardMembersVotingStatus failed", (Throwable)response); - } - - assertNull("Expected null Success response. Actual " + response, response); - - // Register member4 candidate for entity1 - it should not become owner since it's non-voting - - member4EntityOwnershipService.registerCandidate(ENTITY1); - verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-4"); - - // Register member5 candidate for entity2 - it should not become owner since it's non-voting - - member5EntityOwnershipService.registerCandidate(ENTITY2); - verifyCandidates(leaderDistributedDataStore, ENTITY2, "member-5"); - - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verifyOwner(leaderDistributedDataStore, ENTITY1, ""); - verifyOwner(leaderDistributedDataStore, ENTITY2, ""); - - // Register member3 candidate for entity1 - it should become owner since it's voting - - member3EntityOwnershipService.registerCandidate(ENTITY1); - verifyCandidates(leaderDistributedDataStore, ENTITY1, "member-4", "member-3"); - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-3"); - - // Switch member4 and member5 back to voting and member3 non-voting. This should result in member4 and member5 - // to become entity owners. - - future = Patterns.ask(leaderDistributedDataStore.getActorUtils().getShardManager(), - new ChangeShardMembersVotingStatus(ENTITY_OWNERSHIP_SHARD_NAME, - ImmutableMap.of("member-3", Boolean.FALSE, "member-4", Boolean.TRUE, "member-5", Boolean.TRUE)), - new Timeout(10, TimeUnit.SECONDS)); - response = Await.result(future, FiniteDuration.apply(10, TimeUnit.SECONDS)); - if (response instanceof Throwable) { - throw new AssertionError("ChangeShardMembersVotingStatus failed", (Throwable)response); - } - - assertNull("Expected null Success response. Actual " + response, response); - - verifyOwner(leaderDistributedDataStore, ENTITY1, "member-4"); - verifyOwner(leaderDistributedDataStore, ENTITY2, "member-5"); - } - - private static void verifyGetOwnershipState(final DOMEntityOwnershipService service, final DOMEntity entity, - final EntityOwnershipState expState) { - Optional state = service.getOwnershipState(entity); - assertTrue("getOwnershipState present", state.isPresent()); - assertEquals("EntityOwnershipState", expState, state.get()); - } - - private static void verifyCandidates(final AbstractDataStore dataStore, final DOMEntity entity, - final String... expCandidates) throws Exception { - AssertionError lastError = null; - Stopwatch sw = Stopwatch.createStarted(); - while (sw.elapsed(TimeUnit.MILLISECONDS) <= 10000) { - Optional possible = dataStore.newReadOnlyTransaction() - .read(entityPath(entity.getType(), entity.getIdentifier()).node(Candidate.QNAME)) - .get(5, TimeUnit.SECONDS); - try { - assertTrue("Candidates not found for " + entity, possible.isPresent()); - Collection actual = new ArrayList<>(); - for (MapEntryNode candidate: ((MapNode)possible.get()).body()) { - actual.add(candidate.findChildByArg(CANDIDATE_NAME_NODE_ID).get().body().toString()); - } - - assertEquals("Candidates for " + entity, Arrays.asList(expCandidates), actual); - return; - } catch (AssertionError e) { - lastError = e; - Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - } - } - - throw lastError; - } - - @SuppressWarnings("checkstyle:IllegalCatch") - private static void verifyOwner(final AbstractDataStore dataStore, final DOMEntity entity, - final String expOwner) { - AbstractEntityOwnershipTest.verifyOwner(expOwner, entity.getType(), entity.getIdentifier(), path -> { - try { - return dataStore.newReadOnlyTransaction().read(path).get(5, TimeUnit.SECONDS).get(); - } catch (Exception e) { - return null; - } - }); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipServiceTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipServiceTest.java deleted file mode 100644 index 758ce9d3e1..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/DistributedEntityOwnershipServiceTest.java +++ /dev/null @@ -1,310 +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.entityownership; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_ID_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNERS_PATH; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_QNAME; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.candidatePath; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityEntryWithOwner; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityOwnersWithCandidate; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityOwnersWithEntityTypeEntry; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityPath; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityTypeEntryWithEntityEntry; - -import akka.actor.ActorRef; -import com.google.common.collect.Sets; -import java.util.Collection; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; -import org.opendaylight.controller.cluster.access.concepts.MemberName; -import org.opendaylight.controller.cluster.datastore.AbstractDataStore; -import org.opendaylight.controller.cluster.datastore.DatastoreContext; -import org.opendaylight.controller.cluster.datastore.DatastoreContextFactory; -import org.opendaylight.controller.cluster.datastore.DistributedDataStore; -import org.opendaylight.controller.cluster.datastore.Shard; -import org.opendaylight.controller.cluster.datastore.ShardDataTree; -import org.opendaylight.controller.cluster.datastore.config.Configuration; -import org.opendaylight.controller.cluster.datastore.config.ConfigurationImpl; -import org.opendaylight.controller.cluster.datastore.config.EmptyModuleShardConfigProvider; -import org.opendaylight.controller.cluster.datastore.utils.MockClusterWrapper; -import org.opendaylight.controller.cluster.entityownership.messages.RegisterCandidateLocal; -import org.opendaylight.controller.cluster.entityownership.messages.RegisterListenerLocal; -import org.opendaylight.controller.cluster.entityownership.messages.UnregisterCandidateLocal; -import org.opendaylight.controller.cluster.entityownership.messages.UnregisterListenerLocal; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.EntityOwnerSelectionStrategyConfig; -import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException; -import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; -import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; -import scala.concurrent.Await; -import scala.concurrent.Future; -import scala.concurrent.duration.FiniteDuration; - -/** - * Unit tests for DistributedEntityOwnershipService. - * - * @author Thomas Pantelis - */ -public class DistributedEntityOwnershipServiceTest extends AbstractClusterRefEntityOwnershipTest { - static final String ENTITY_TYPE = "test"; - static final String ENTITY_TYPE2 = "test2"; - static final QName QNAME = QName.create("test", "2015-08-11", "foo"); - static int ID_COUNTER = 1; - - private final String dataStoreName = "config" + ID_COUNTER++; - private AbstractDataStore dataStore; - - @Before - public void setUp() { - DatastoreContext datastoreContext = DatastoreContext.newBuilder().dataStoreName(dataStoreName) - .shardInitializationTimeout(10, TimeUnit.SECONDS).build(); - - Configuration configuration = new ConfigurationImpl(new EmptyModuleShardConfigProvider()) { - @Override - public Collection getUniqueMemberNamesForAllShards() { - return Sets.newHashSet(MemberName.forName("member-1")); - } - }; - - DatastoreContextFactory mockContextFactory = mock(DatastoreContextFactory.class); - Mockito.doReturn(datastoreContext).when(mockContextFactory).getBaseDatastoreContext(); - Mockito.doReturn(datastoreContext).when(mockContextFactory).getShardDatastoreContext(Mockito.anyString()); - - dataStore = new DistributedDataStore(getSystem(), new MockClusterWrapper(), configuration, - mockContextFactory, null); - - dataStore.onModelContextUpdated(EOSTestUtils.SCHEMA_CONTEXT); - } - - @After - public void tearDown() { - dataStore.close(); - } - - private static T verifyMessage(final DistributedEntityOwnershipService mock, final Class type) { - final ArgumentCaptor message = ArgumentCaptor.forClass(type); - verify(mock).executeLocalEntityOwnershipShardOperation(message.capture()); - return message.getValue(); - } - - @Test - public void testEntityOwnershipShardCreated() throws Exception { - DistributedEntityOwnershipService service = DistributedEntityOwnershipService.start(dataStore.getActorUtils(), - EntityOwnerSelectionStrategyConfig.newBuilder().build()); - - Future future = dataStore.getActorUtils().findLocalShardAsync( - DistributedEntityOwnershipService.ENTITY_OWNERSHIP_SHARD_NAME); - ActorRef shardActor = Await.result(future, FiniteDuration.create(10, TimeUnit.SECONDS)); - assertNotNull(DistributedEntityOwnershipService.ENTITY_OWNERSHIP_SHARD_NAME + " not found", shardActor); - - service.close(); - } - - @Test - public void testRegisterCandidate() throws Exception { - DistributedEntityOwnershipService service = spy(DistributedEntityOwnershipService.start( - dataStore.getActorUtils(), EntityOwnerSelectionStrategyConfig.newBuilder().build())); - - YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME); - DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId); - - DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(entity); - verifyRegisterCandidateLocal(service, entity); - verifyEntityOwnershipCandidateRegistration(entity, reg); - verifyEntityCandidate(service.getLocalEntityOwnershipShard(), ENTITY_TYPE, entityId, - dataStore.getActorUtils().getCurrentMemberName().getName()); - - // Register the same entity - should throw exception - - try { - service.registerCandidate(entity); - fail("Expected CandidateAlreadyRegisteredException"); - } catch (CandidateAlreadyRegisteredException e) { - // expected - assertEquals("getEntity", entity, e.getEntity()); - } - - // Register a different entity - should succeed - reset(service); - - DOMEntity entity2 = new DOMEntity(ENTITY_TYPE2, entityId); - DOMEntityOwnershipCandidateRegistration reg2 = service.registerCandidate(entity2); - - verifyEntityOwnershipCandidateRegistration(entity2, reg2); - verifyEntityCandidate(service.getLocalEntityOwnershipShard(), ENTITY_TYPE2, entityId, - dataStore.getActorUtils().getCurrentMemberName().getName()); - - service.close(); - } - - @Test - public void testCloseCandidateRegistration() throws Exception { - DistributedEntityOwnershipService service = spy(DistributedEntityOwnershipService.start( - dataStore.getActorUtils(), EntityOwnerSelectionStrategyConfig.newBuilder().build())); - - DOMEntity entity = new DOMEntity(ENTITY_TYPE, YangInstanceIdentifier.of(QNAME)); - - DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(entity); - - verifyEntityOwnershipCandidateRegistration(entity, reg); - verifyRegisterCandidateLocal(service, entity); - - reset(service); - reg.close(); - UnregisterCandidateLocal unregCandidate = verifyMessage(service, UnregisterCandidateLocal.class); - assertEquals("getEntity", entity, unregCandidate.getEntity()); - - // Re-register - should succeed. - reset(service); - service.registerCandidate(entity); - verifyRegisterCandidateLocal(service, entity); - - service.close(); - } - - @Test - public void testListenerRegistration() { - DistributedEntityOwnershipService service = spy(DistributedEntityOwnershipService.start( - dataStore.getActorUtils(), EntityOwnerSelectionStrategyConfig.newBuilder().build())); - - YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME); - DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId); - DOMEntityOwnershipListener listener = mock(DOMEntityOwnershipListener.class); - - DOMEntityOwnershipListenerRegistration reg = service.registerListener(entity.getType(), listener); - - assertNotNull("EntityOwnershipListenerRegistration null", reg); - assertEquals("getEntityType", entity.getType(), reg.getEntityType()); - assertEquals("getInstance", listener, reg.getInstance()); - - RegisterListenerLocal regListener = verifyMessage(service, RegisterListenerLocal.class); - assertSame("getListener", listener, regListener.getListener()); - assertEquals("getEntityType", entity.getType(), regListener.getEntityType()); - - reset(service); - reg.close(); - UnregisterListenerLocal unregListener = verifyMessage(service, UnregisterListenerLocal.class); - assertEquals("getEntityType", entity.getType(), unregListener.getEntityType()); - assertSame("getListener", listener, unregListener.getListener()); - - service.close(); - } - - @Test - public void testGetOwnershipState() throws Exception { - DistributedEntityOwnershipService service = spy(DistributedEntityOwnershipService.start( - dataStore.getActorUtils(), EntityOwnerSelectionStrategyConfig.newBuilder().build())); - - final Shard mockShard = Mockito.mock(Shard.class); - ShardDataTree shardDataTree = new ShardDataTree(mockShard, EOSTestUtils.SCHEMA_CONTEXT, TreeType.OPERATIONAL); - - when(service.getLocalEntityOwnershipShardDataTree()).thenReturn(shardDataTree.getDataTree()); - - DOMEntity entity1 = new DOMEntity(ENTITY_TYPE, "one"); - writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, entity1.getIdentifier(), "member-1"), - shardDataTree); - writeNode(ENTITY_OWNERS_PATH, entityOwnersWithEntityTypeEntry(entityTypeEntryWithEntityEntry(entity1.getType(), - entityEntryWithOwner(entity1.getIdentifier(), "member-1"))), shardDataTree); - verifyGetOwnershipState(service, entity1, EntityOwnershipState.IS_OWNER); - - writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, - entity1.getIdentifier(), "member-2"), shardDataTree); - writeNode(entityPath(entity1.getType(), entity1.getIdentifier()), - entityEntryWithOwner(entity1.getIdentifier(), "member-2"), shardDataTree); - verifyGetOwnershipState(service, entity1, EntityOwnershipState.OWNED_BY_OTHER); - - writeNode(entityPath(entity1.getType(), entity1.getIdentifier()), entityEntryWithOwner(entity1.getIdentifier(), - ""), shardDataTree); - verifyGetOwnershipState(service, entity1, EntityOwnershipState.NO_OWNER); - - DOMEntity entity2 = new DOMEntity(ENTITY_TYPE, "two"); - Optional state = service.getOwnershipState(entity2); - assertFalse("getOwnershipState present", state.isPresent()); - - writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, entity2.getIdentifier(), "member-1"), - shardDataTree); - writeNode(entityPath(entity2.getType(), entity2.getIdentifier()), ImmutableNodes.mapEntry(ENTITY_QNAME, - ENTITY_ID_QNAME, entity2.getIdentifier()), shardDataTree); - verifyGetOwnershipState(service, entity2, EntityOwnershipState.NO_OWNER); - - deleteNode(candidatePath(entityPath(entity2.getType(), entity2.getIdentifier()), "member-1"), shardDataTree); - Optional state2 = service.getOwnershipState(entity2); - assertFalse("getOwnershipState present", state2.isPresent()); - service.close(); - } - - @Test - public void testIsCandidateRegistered() throws CandidateAlreadyRegisteredException { - DistributedEntityOwnershipService service = DistributedEntityOwnershipService.start(dataStore.getActorUtils(), - EntityOwnerSelectionStrategyConfig.newBuilder().build()); - - final DOMEntity test = new DOMEntity("test-type", "test"); - - assertFalse(service.isCandidateRegistered(test)); - - service.registerCandidate(test); - - assertTrue(service.isCandidateRegistered(test)); - - service.close(); - } - - private static void verifyGetOwnershipState(final DistributedEntityOwnershipService service, final DOMEntity entity, - final EntityOwnershipState expState) { - Optional state = service.getOwnershipState(entity); - assertTrue("getOwnershipState present", state.isPresent()); - assertEquals("EntityOwnershipState", expState, state.get()); - } - - @SuppressWarnings("checkstyle:IllegalCatch") - private void verifyEntityCandidate(final ActorRef entityOwnershipShard, final String entityType, - final YangInstanceIdentifier entityId, final String candidateName) { - verifyEntityCandidate(entityType, entityId, candidateName, path -> { - try { - return dataStore.newReadOnlyTransaction().read(path).get(5, TimeUnit.SECONDS).get(); - } catch (Exception e) { - return null; - } - }); - } - - private static void verifyRegisterCandidateLocal(final DistributedEntityOwnershipService service, - final DOMEntity entity) { - RegisterCandidateLocal regCandidate = verifyMessage(service, RegisterCandidateLocal.class); - assertEquals("getEntity", entity, regCandidate.getEntity()); - } - - private static void verifyEntityOwnershipCandidateRegistration(final DOMEntity entity, - final DOMEntityOwnershipCandidateRegistration reg) { - assertNotNull("EntityOwnershipCandidateRegistration null", reg); - assertEquals("getInstance", entity, reg.getInstance()); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EOSTestUtils.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EOSTestUtils.java deleted file mode 100644 index dda340468a..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EOSTestUtils.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.entityownership; - -import java.io.File; -import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; -import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; - -final class EOSTestUtils { - static final EffectiveModelContext SCHEMA_CONTEXT = YangParserTestUtils.parseYangFiles( - new File("src/main/yang/entity-owners.yang")); - - private EOSTestUtils() { - // Hidden on purpose - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnerChangeListenerTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnerChangeListenerTest.java deleted file mode 100644 index 6eb5847e2b..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnerChangeListenerTest.java +++ /dev/null @@ -1,143 +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.entityownership; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNERS_PATH; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityEntryWithOwner; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityOwnersWithCandidate; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityPath; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.opendaylight.controller.cluster.access.concepts.MemberName; -import org.opendaylight.controller.cluster.datastore.Shard; -import org.opendaylight.controller.cluster.datastore.ShardDataTree; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.yangtools.yang.common.QName; -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.DataValidationFailedException; -import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; - -/** - * Unit tests for EntityOwnerChangeListener. - * - * @author Thomas Pantelis - */ -public class EntityOwnerChangeListenerTest { - private static final String LOCAL_MEMBER_NAME = "member-1"; - private static final String REMOTE_MEMBER_NAME1 = "member-2"; - private static final String REMOTE_MEMBER_NAME2 = "member-3"; - private static final String ENTITY_TYPE = "test"; - private static final YangInstanceIdentifier ENTITY_ID1 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity1")); - private static final YangInstanceIdentifier ENTITY_ID2 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity2")); - private static final DOMEntity ENTITY1 = new DOMEntity(ENTITY_TYPE, ENTITY_ID1); - private static final DOMEntity ENTITY2 = new DOMEntity(ENTITY_TYPE, ENTITY_ID2); - - private final Shard mockShard = Mockito.mock(Shard.class); - - private final ShardDataTree shardDataTree = new ShardDataTree(mockShard, EOSTestUtils.SCHEMA_CONTEXT, - TreeType.OPERATIONAL); - private final EntityOwnershipListenerSupport mockListenerSupport = mock(EntityOwnershipListenerSupport.class); - private EntityOwnerChangeListener listener; - - @Before - public void setup() { - listener = new EntityOwnerChangeListener(MemberName.forName(LOCAL_MEMBER_NAME), mockListenerSupport); - listener.init(shardDataTree); - } - - @Test - 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(DOMEntity.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, 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(DOMEntity.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, 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).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, 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).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, 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(DOMEntity.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, 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(DOMEntity.class), anyBoolean(), - anyBoolean(), anyBoolean()); - } - - private void writeNode(final YangInstanceIdentifier path, final NormalizedNode node) - throws DataValidationFailedException { - AbstractEntityOwnershipTest.writeNode(path, node, shardDataTree); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerActorTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerActorTest.java deleted file mode 100644 index 7b14e739fd..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerActorTest.java +++ /dev/null @@ -1,78 +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.entityownership; - -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; - -import akka.actor.ActorRef; -import akka.testkit.TestActorRef; -import org.junit.After; -import org.junit.Test; -import org.opendaylight.controller.cluster.raft.TestActorFactory; -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.mdsal.eos.dom.api.DOMEntityOwnershipListener; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; - -/** - * Unit tests for EntityOwnershipListenerActor. - * - * @author Thomas Pantelis - */ -public class EntityOwnershipListenerActorTest extends AbstractEntityOwnershipTest { - private final TestActorFactory actorFactory = new TestActorFactory(getSystem()); - - @After - public void tearDown() { - actorFactory.close(); - } - - @Test - public void testOnEntityOwnershipChanged() { - DOMEntityOwnershipListener mockListener = mock(DOMEntityOwnershipListener.class); - - TestActorRef listenerActor = actorFactory.createTestActor( - EntityOwnershipListenerActor.props(mockListener), actorFactory.generateActorId("listener")); - - DOMEntity entity = new DOMEntity("test", YangInstanceIdentifier.of(QName.create("test", "id1"))); - boolean wasOwner = false; - boolean isOwner = true; - boolean hasOwner = true; - listenerActor.tell(new DOMEntityOwnershipChange(entity, EntityOwnershipChangeState.from( - wasOwner, isOwner, hasOwner)), ActorRef.noSender()); - - verify(mockListener, timeout(5000)).ownershipChanged(ownershipChange(entity, wasOwner, isOwner, hasOwner)); - } - - @Test - public void testOnEntityOwnershipChangedWithListenerEx() { - DOMEntityOwnershipListener mockListener = mock(DOMEntityOwnershipListener.class); - - DOMEntity entity1 = new DOMEntity("test", YangInstanceIdentifier.of(QName.create("test", "id1"))); - doThrow(new RuntimeException("mock")).when(mockListener).ownershipChanged( - ownershipChange(entity1, false, true, true)); - DOMEntity entity2 = new DOMEntity("test", YangInstanceIdentifier.of(QName.create("test", "id2"))); - doNothing().when(mockListener).ownershipChanged(ownershipChange(entity2, true, false, false)); - - TestActorRef listenerActor = actorFactory.createTestActor( - EntityOwnershipListenerActor.props(mockListener), actorFactory.generateActorId("listener")); - - listenerActor.tell(new DOMEntityOwnershipChange(entity1, EntityOwnershipChangeState.from( - false, true, true)), ActorRef.noSender()); - listenerActor.tell(new DOMEntityOwnershipChange(entity2, EntityOwnershipChangeState.from( - true, false, false)), ActorRef.noSender()); - - verify(mockListener, timeout(5000)).ownershipChanged(ownershipChange(entity2, true, false, false)); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerSupportTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerSupportTest.java deleted file mode 100644 index 00fb6fa1ae..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipListenerSupportTest.java +++ /dev/null @@ -1,164 +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.entityownership; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -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 akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.actor.Props; -import akka.testkit.TestActorRef; -import akka.testkit.javadsl.TestKit; -import com.google.common.util.concurrent.Uninterruptibles; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.opendaylight.controller.cluster.raft.TestActorFactory; -import org.opendaylight.controller.cluster.raft.utils.DoNothingActor; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import scala.collection.Iterator; -import scala.collection.immutable.Iterable; - -/** - * Unit tests for EntityOwnershipListenerSupport. - * - * @author Thomas Pantelis - */ -public class EntityOwnershipListenerSupportTest extends AbstractEntityOwnershipTest { - private final TestActorFactory actorFactory = new TestActorFactory(getSystem()); - private ActorContext actorContext; - - @Before - public void setup() { - TestActorRef actor = actorFactory.createTestActor( - Props.create(DoNothingActor.class), actorFactory.generateActorId("test")); - - actorContext = actor.underlyingActor().getContext(); - } - - @After - public void tearDown() { - actorFactory.close(); - } - - @Test - public void testNotifyEntityOwnershipListeners() { - EntityOwnershipListenerSupport support = new EntityOwnershipListenerSupport(actorContext, "test"); - - DOMEntityOwnershipListener mockListener1 = mock(DOMEntityOwnershipListener.class, "EntityOwnershipListener1"); - DOMEntityOwnershipListener mockListener2 = mock(DOMEntityOwnershipListener.class, "EntityOwnershipListener2"); - DOMEntityOwnershipListener mockListener12 = mock(DOMEntityOwnershipListener.class, - "EntityOwnershipListener1_2"); - String entityType1 = "type1"; - String entityType2 = "type2"; - final DOMEntity entity1 = new DOMEntity(entityType1, YangInstanceIdentifier.of(QName.create("test", "id1"))); - final DOMEntity entity2 = new DOMEntity(entityType2, YangInstanceIdentifier.of(QName.create("test", "id2"))); - final DOMEntity entity3 = new DOMEntity("noListener", YangInstanceIdentifier.of(QName.create("test", "id5"))); - - // Add EntityOwnershipListener registrations. - - support.addEntityOwnershipListener(entityType1, mockListener1); - support.addEntityOwnershipListener(entityType1, mockListener1); // register again - should be noop - support.addEntityOwnershipListener(entityType1, mockListener12); - support.addEntityOwnershipListener(entityType2, mockListener2); - - // Notify entity1 changed and verify appropriate listeners are notified. - - support.notifyEntityOwnershipListeners(entity1, false, true, true); - - verify(mockListener1, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, true, true)); - verify(mockListener12, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, true, true)); - Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener2, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - assertEquals("# of listener actors", 2, actorContext.children().size()); - reset(mockListener1, mockListener2, mockListener12); - - // Notify entity2 changed and verify appropriate listeners are notified. - - support.notifyEntityOwnershipListeners(entity2, false, true, true); - - verify(mockListener2, timeout(5000)).ownershipChanged(ownershipChange(entity2, false, true, true)); - Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener1, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - verify(mockListener12, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - assertEquals("# of listener actors", 3, actorContext.children().size()); - reset(mockListener1, mockListener2, mockListener12); - - // Notify entity3 changed and verify no listeners are notified. - - support.notifyEntityOwnershipListeners(entity3, true, false, true); - - Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener1, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - verify(mockListener2, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - verify(mockListener12, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - reset(mockListener1, mockListener2, mockListener12); - - Iterable listenerActors = actorContext.children(); - assertEquals("# of listener actors", 3, listenerActors.size()); - - // Unregister mockListener1, issue a change for entity1 and verify only remaining listeners are notified. - - support.removeEntityOwnershipListener(entityType1, mockListener1); - support.notifyEntityOwnershipListeners(entity1, true, false, true); - - verify(mockListener12, timeout(5000)).ownershipChanged(ownershipChange(entity1, true, false, true)); - Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); - verify(mockListener1, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - reset(mockListener1, mockListener2, mockListener12); - - // Unregister all listeners and verify their listener actors are destroyed. - - List watchers = new ArrayList<>(); - for (Iterator iter = listenerActors.iterator(); iter.hasNext();) { - TestKit kit = new TestKit(getSystem()); - kit.watch(iter.next()); - watchers.add(kit); - } - - support.removeEntityOwnershipListener(entityType1, mockListener12); - support.removeEntityOwnershipListener(entityType1, mockListener12); // un-register again - should be noop - support.removeEntityOwnershipListener(entityType2, mockListener2); - - Iterator iter = listenerActors.iterator(); - for (TestKit kit: watchers) { - kit.expectTerminated(kit.duration("3 seconds"), iter.next()); - } - - assertEquals("# of listener actors", 0, actorContext.children().size()); - - // Re-register mockListener1 and verify it is notified. - - reset(mockListener1, mockListener2); - - support.addEntityOwnershipListener(entityType1, mockListener1); - support.notifyEntityOwnershipListeners(entity1, false, false, true); - - verify(mockListener1, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, false, true)); - verify(mockListener12, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - verify(mockListener2, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - - // Quickly register and unregister mockListener2 - expecting no exceptions. - - support.addEntityOwnershipListener(entityType1, mockListener2); - support.removeEntityOwnershipListener(entityType1, mockListener2); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipShardTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipShardTest.java deleted file mode 100644 index 000ad3b628..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipShardTest.java +++ /dev/null @@ -1,1310 +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.entityownership; - -import static org.junit.Assert.assertEquals; -import static org.mockito.AdditionalMatchers.or; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor.clearMessages; -import static org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor.expectFirstMatching; -import static org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor.expectMatching; - -import akka.actor.ActorRef; -import akka.actor.PoisonPill; -import akka.actor.Props; -import akka.actor.Terminated; -import akka.dispatch.Dispatchers; -import akka.testkit.TestActorRef; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.Uninterruptibles; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Predicate; -import org.junit.After; -import org.junit.Test; -import org.opendaylight.controller.cluster.access.concepts.MemberName; -import org.opendaylight.controller.cluster.datastore.DatastoreContext; -import org.opendaylight.controller.cluster.datastore.DatastoreContext.Builder; -import org.opendaylight.controller.cluster.datastore.ShardTestKit; -import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; -import org.opendaylight.controller.cluster.datastore.messages.BatchedModifications; -import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolved; -import org.opendaylight.controller.cluster.datastore.messages.PeerDown; -import org.opendaylight.controller.cluster.datastore.messages.PeerUp; -import org.opendaylight.controller.cluster.datastore.messages.SuccessReply; -import org.opendaylight.controller.cluster.entityownership.messages.CandidateAdded; -import org.opendaylight.controller.cluster.entityownership.messages.RegisterCandidateLocal; -import org.opendaylight.controller.cluster.entityownership.messages.RegisterListenerLocal; -import org.opendaylight.controller.cluster.entityownership.messages.UnregisterCandidateLocal; -import org.opendaylight.controller.cluster.entityownership.messages.UnregisterListenerLocal; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.EntityOwnerSelectionStrategyConfig; -import org.opendaylight.controller.cluster.entityownership.selectionstrategy.LastCandidateSelectionStrategy; -import org.opendaylight.controller.cluster.raft.RaftState; -import org.opendaylight.controller.cluster.raft.TestActorFactory; -import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout; -import org.opendaylight.controller.cluster.raft.base.messages.TimeoutNow; -import org.opendaylight.controller.cluster.raft.messages.AppendEntries; -import org.opendaylight.controller.cluster.raft.messages.RequestVote; -import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor; -import org.opendaylight.mdsal.eos.dom.api.DOMEntity; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange; -import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; - -/** - * Unit tests for EntityOwnershipShard. - * - * @author Thomas Pantelis - */ -public class EntityOwnershipShardTest extends AbstractEntityOwnershipTest { - private static final String ENTITY_TYPE = "test type"; - private static final YangInstanceIdentifier ENTITY_ID1 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity1")); - private static final YangInstanceIdentifier ENTITY_ID2 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity2")); - private static final YangInstanceIdentifier ENTITY_ID3 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity3")); - private static final YangInstanceIdentifier ENTITY_ID4 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity4")); - private static final YangInstanceIdentifier ENTITY_ID5 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity5")); - private static final String LOCAL_MEMBER_NAME = "local-member-1"; - private static final String PEER_MEMBER_1_NAME = "peer-member-1"; - private static final String PEER_MEMBER_2_NAME = "peer-member-2"; - - private Builder dataStoreContextBuilder = DatastoreContext.newBuilder().persistent(false); - private final TestActorFactory actorFactory = new TestActorFactory(getSystem()); - - @After - public void tearDown() { - actorFactory.close(); - } - - @Test - public void testOnRegisterCandidateLocal() { - testLog.info("testOnRegisterCandidateLocal starting"); - - ShardTestKit kit = new ShardTestKit(getSystem()); - - TestActorRef shard = actorFactory.createTestActor(newLocalShardProps()); - - ShardTestKit.waitUntilLeader(shard); - - YangInstanceIdentifier entityId = ENTITY_ID1; - DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId); - - shard.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyCommittedEntityCandidate(shard, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - verifyOwner(shard, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - - testLog.info("testOnRegisterCandidateLocal ending"); - } - - @Test - public void testOnRegisterCandidateLocalWithNoInitialLeader() { - testLog.info("testOnRegisterCandidateLocalWithNoInitialLeader starting"); - - final ShardTestKit kit = new ShardTestKit(getSystem()); - - dataStoreContextBuilder.shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(2); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId = newShardId(PEER_MEMBER_1_NAME); - - TestActorRef peer = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId, peerMap(leaderId.toString()), PEER_MEMBER_1_NAME)), peerId.toString()); - TestEntityOwnershipShard peerShard = peer.underlyingActor(); - peerShard.startDroppingMessagesOfType(RequestVote.class); - peerShard.startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef shard = actorFactory.createTestActor( - newShardProps(leaderId, peerMap(peerId.toString()), LOCAL_MEMBER_NAME), leaderId.toString()); - - YangInstanceIdentifier entityId = ENTITY_ID1; - DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId); - - shard.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Now allow RequestVotes to the peer so the shard becomes the leader. This should retry the commit. - peerShard.stopDroppingMessagesOfType(RequestVote.class); - - verifyCommittedEntityCandidate(shard, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - verifyOwner(shard, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - - testLog.info("testOnRegisterCandidateLocalWithNoInitialLeader ending"); - } - - @Test - public void testOnRegisterCandidateLocalWithNoInitialConsensus() { - testLog.info("testOnRegisterCandidateLocalWithNoInitialConsensus starting"); - - final ShardTestKit kit = new ShardTestKit(getSystem()); - - dataStoreContextBuilder.shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(2) - .shardTransactionCommitTimeoutInSeconds(1); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId = newShardId(PEER_MEMBER_1_NAME); - - TestActorRef peer = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId, peerMap(leaderId.toString()), PEER_MEMBER_1_NAME)), peerId.toString()); - TestEntityOwnershipShard peerShard = peer.underlyingActor(); - peerShard.startDroppingMessagesOfType(ElectionTimeout.class); - - // Drop AppendEntries so consensus isn't reached. - peerShard.startDroppingMessagesOfType(AppendEntries.class); - - TestActorRef leader = actorFactory.createTestActor( - newShardProps(leaderId, peerMap(peerId.toString()), LOCAL_MEMBER_NAME), leaderId.toString()); - - ShardTestKit.waitUntilLeader(leader); - - YangInstanceIdentifier entityId = ENTITY_ID1; - DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId); - - leader.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Wait enough time for the commit to timeout. - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - - // Resume AppendEntries - the follower should ack the commit which should then result in the candidate - // write being applied to the state. - peerShard.stopDroppingMessagesOfType(AppendEntries.class); - - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - verifyOwner(leader, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - - testLog.info("testOnRegisterCandidateLocalWithNoInitialConsensus ending"); - } - - @Test - public void testOnRegisterCandidateLocalWithIsolatedLeader() throws Exception { - testLog.info("testOnRegisterCandidateLocalWithIsolatedLeader starting"); - - final ShardTestKit kit = new ShardTestKit(getSystem()); - - dataStoreContextBuilder.shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(2) - .shardIsolatedLeaderCheckIntervalInMillis(50); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId = newShardId(PEER_MEMBER_1_NAME); - - TestActorRef peer = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId, peerMap(leaderId.toString()), PEER_MEMBER_1_NAME)), peerId.toString()); - TestEntityOwnershipShard peerShard = peer.underlyingActor(); - peerShard.startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef leader = actorFactory.createTestActor( - newShardProps(leaderId, peerMap(peerId.toString()), LOCAL_MEMBER_NAME)); - - ShardTestKit.waitUntilLeader(leader); - - // Drop AppendEntries and wait enough time for the shard to switch to IsolatedLeader. - peerShard.startDroppingMessagesOfType(AppendEntries.class); - verifyRaftState(leader, state -> - assertEquals("getRaftState", RaftState.IsolatedLeader.toString(), state.getRaftState())); - - YangInstanceIdentifier entityId = ENTITY_ID1; - DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId); - - leader.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Resume AppendEntries - the candidate write should now be committed. - peerShard.stopDroppingMessagesOfType(AppendEntries.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - verifyOwner(leader, ENTITY_TYPE, entityId, LOCAL_MEMBER_NAME); - - testLog.info("testOnRegisterCandidateLocalWithIsolatedLeader ending"); - } - - @Test - public void testOnRegisterCandidateLocalWithRemoteLeader() { - testLog.info("testOnRegisterCandidateLocalWithRemoteLeader starting"); - - ShardTestKit kit = new ShardTestKit(getSystem()); - - dataStoreContextBuilder.shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(2) - .shardBatchedModificationCount(5); - - ShardIdentifier leaderId = newShardId(PEER_MEMBER_1_NAME); - ShardIdentifier localId = newShardId(LOCAL_MEMBER_NAME); - TestActorRef leader = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(leaderId, peerMap(localId.toString()), PEER_MEMBER_1_NAME), - actorFactory.createActor(MessageCollectorActor.props())), leaderId.toString()); - final TestEntityOwnershipShard leaderShard = leader.underlyingActor(); - - TestActorRef local = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(localId, peerMap(leaderId.toString()),LOCAL_MEMBER_NAME)), localId.toString()); - local.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - local.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID1)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - - // Test with initial commit timeout and subsequent retry. - - local.tell(dataStoreContextBuilder.shardTransactionCommitTimeoutInSeconds(1).build(), ActorRef.noSender()); - leaderShard.startDroppingMessagesOfType(BatchedModifications.class); - - local.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID2)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - expectFirstMatching(leaderShard.collectorActor(), BatchedModifications.class); - - // Send a bunch of registration messages quickly and verify. - - leaderShard.stopDroppingMessagesOfType(BatchedModifications.class); - clearMessages(leaderShard.collectorActor()); - - int max = 100; - List entityIds = new ArrayList<>(); - for (int i = 1; i <= max; i++) { - YangInstanceIdentifier id = YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "test" + i)); - entityIds.add(id); - local.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, id)), kit.getRef()); - } - - for (int i = 0; i < max; i++) { - verifyCommittedEntityCandidate(local, ENTITY_TYPE, entityIds.get(i), LOCAL_MEMBER_NAME); - } - - testLog.info("testOnRegisterCandidateLocalWithRemoteLeader ending"); - } - - @Test - public void testOnUnregisterCandidateLocal() { - testLog.info("testOnUnregisterCandidateLocal starting"); - - ShardTestKit kit = new ShardTestKit(getSystem()); - TestActorRef shard = actorFactory.createTestActor(newLocalShardProps()); - ShardTestKit.waitUntilLeader(shard); - - DOMEntity entity = new DOMEntity(ENTITY_TYPE, ENTITY_ID1); - - // Register - - shard.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyCommittedEntityCandidate(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - - // Unregister - - shard.tell(new UnregisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, ""); - - // Register again - - shard.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyCommittedEntityCandidate(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(shard, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - - testLog.info("testOnUnregisterCandidateLocal ending"); - } - - @Test - public void testOwnershipChanges() { - testLog.info("testOwnershipChanges starting"); - - final ShardTestKit kit = new ShardTestKit(getSystem()); - - dataStoreContextBuilder.shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(2); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId1 = newShardId(PEER_MEMBER_1_NAME); - ShardIdentifier peerId2 = newShardId(PEER_MEMBER_2_NAME); - - TestActorRef peer1 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId1, peerMap(leaderId.toString(), peerId2.toString()), PEER_MEMBER_1_NAME)), - peerId1.toString()); - peer1.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef peer2 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId2, peerMap(leaderId.toString(), peerId1.toString()), PEER_MEMBER_2_NAME)), - peerId2.toString()); - peer2.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef leader = actorFactory.createTestActor( - newShardProps(leaderId, peerMap(peerId1.toString(), peerId2.toString()), LOCAL_MEMBER_NAME), - leaderId.toString()); - - ShardTestKit.waitUntilLeader(leader); - - DOMEntity entity = new DOMEntity(ENTITY_TYPE, ENTITY_ID1); - - // Add a remote candidate - - peer1.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyCommittedEntityCandidate(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_1_NAME); - - // Register local - - leader.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Verify the remote candidate becomes owner - - verifyCommittedEntityCandidate(leader, entity.getType(), entity.getIdentifier(), LOCAL_MEMBER_NAME); - verifyOwner(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_1_NAME); - - // Add another remote candidate and verify ownership doesn't change - - peer2.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyCommittedEntityCandidate(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_2_NAME); - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verifyOwner(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_1_NAME); - - // Remove the second remote candidate and verify ownership doesn't change - - peer2.tell(new UnregisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyEntityCandidateRemoved(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_2_NAME); - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verifyOwner(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_1_NAME); - - // Remove the first remote candidate and verify the local candidate becomes owner - - peer1.tell(new UnregisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyEntityCandidateRemoved(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_1_NAME); - verifyOwner(leader, entity.getType(), entity.getIdentifier(), LOCAL_MEMBER_NAME); - - // Add the second remote candidate back and verify ownership doesn't change - - peer2.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyCommittedEntityCandidate(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_2_NAME); - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verifyOwner(leader, entity.getType(), entity.getIdentifier(), LOCAL_MEMBER_NAME); - - // Unregister the local candidate and verify the second remote candidate becomes owner - - leader.tell(new UnregisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyEntityCandidateRemoved(leader, entity.getType(), entity.getIdentifier(), LOCAL_MEMBER_NAME); - verifyOwner(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_2_NAME); - - testLog.info("testOwnershipChanges ending"); - } - - @Test - public void testOwnerChangesOnPeerAvailabilityChanges() throws Exception { - testLog.info("testOwnerChangesOnPeerAvailabilityChanges starting"); - - final ShardTestKit kit = new ShardTestKit(getSystem()); - - dataStoreContextBuilder.shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(4) - .shardIsolatedLeaderCheckIntervalInMillis(100000); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId1 = newShardId(PEER_MEMBER_1_NAME); - ShardIdentifier peerId2 = newShardId(PEER_MEMBER_2_NAME); - - TestActorRef peer1 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId1, peerMap(leaderId.toString(), peerId2.toString()), PEER_MEMBER_1_NAME)), - peerId1.toString()); - peer1.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef peer2 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId2, peerMap(leaderId.toString(), peerId1.toString()), PEER_MEMBER_2_NAME)), - peerId2.toString()); - peer2.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef leader = actorFactory.createTestActor( - newShardProps(leaderId, peerMap(peerId1.toString(), peerId2.toString()), LOCAL_MEMBER_NAME), - leaderId.toString()); - - verifyRaftState(leader, state -> - assertEquals("getRaftState", RaftState.Leader.toString(), state.getRaftState())); - - // Send PeerDown and PeerUp with no entities - - leader.tell(new PeerDown(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - leader.tell(new PeerUp(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - - // Add candidates for entity1 with the local leader as the owner - - leader.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID1)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - - peer2.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID1)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID1, PEER_MEMBER_2_NAME); - - peer1.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID1)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID1, PEER_MEMBER_1_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - - // Add candidates for entity2 with peerMember2 as the owner - - peer2.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID2)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - - peer1.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID2)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_1_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - - // Add candidates for entity3 with peerMember2 as the owner. - - peer2.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID3)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_2_NAME); - - leader.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID3)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID3, LOCAL_MEMBER_NAME); - - peer1.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID3)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_1_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_2_NAME); - - // Add only candidate peerMember2 for entity4. - - peer2.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID4)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID4, PEER_MEMBER_2_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID4, PEER_MEMBER_2_NAME); - - // Add only candidate peerMember1 for entity5. - - peer1.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID5)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID5, PEER_MEMBER_1_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID5, PEER_MEMBER_1_NAME); - - // Kill peerMember2 and send PeerDown - the entities (2, 3, 4) owned by peerMember2 should get a new - // owner selected - - kit.watch(peer2); - peer2.tell(PoisonPill.getInstance(), ActorRef.noSender()); - kit.expectMsgClass(Duration.ofSeconds(5), Terminated.class); - kit.unwatch(peer2); - - leader.tell(new PeerDown(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - // Send PeerDown again - should be noop - leader.tell(new PeerDown(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - peer1.tell(new PeerDown(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_1_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID3, LOCAL_MEMBER_NAME); - // no other candidates for entity4 so peerMember2 should remain owner. - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID4, PEER_MEMBER_2_NAME); - - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID1, PEER_MEMBER_2_NAME); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_2_NAME); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID4, PEER_MEMBER_2_NAME); - - // Reinstate peerMember2 - - peer2 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId2, peerMap(leaderId.toString(), peerId1.toString()), PEER_MEMBER_2_NAME)), - peerId2.toString()); - peer2.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - leader.tell(new PeerUp(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - // Send PeerUp again - should be noop - leader.tell(new PeerUp(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - peer1.tell(new PeerUp(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - - // peerMember2's candidates should be removed on startup. - verifyNoEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID1, PEER_MEMBER_2_NAME); - verifyNoEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - verifyNoEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_2_NAME); - verifyNoEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID4, PEER_MEMBER_2_NAME); - - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_1_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID3, LOCAL_MEMBER_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID4, ""); - - // Add back candidate peerMember2 for entities 1, 2, & 3. - - peer2.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID1)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - peer2.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID2)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - peer2.tell(new RegisterCandidateLocal(new DOMEntity(ENTITY_TYPE, ENTITY_ID3)), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID1, PEER_MEMBER_2_NAME); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_2_NAME); - verifyCommittedEntityCandidate(peer2, ENTITY_TYPE, ENTITY_ID1, PEER_MEMBER_2_NAME); - verifyCommittedEntityCandidate(peer2, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - verifyCommittedEntityCandidate(peer2, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_2_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_1_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID3, LOCAL_MEMBER_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_1_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID3, LOCAL_MEMBER_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID4, ""); - - // Kill peerMember1 and send PeerDown - entity 2 should get a new owner selected - - kit.watch(peer1); - peer1.tell(PoisonPill.getInstance(), ActorRef.noSender()); - kit.expectMsgClass(Duration.ofSeconds(5), Terminated.class); - kit.unwatch(peer1); - leader.tell(new PeerDown(peerId1.getMemberName(), peerId1.toString()), ActorRef.noSender()); - - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - - // Verify the reinstated peerMember2 is fully synced. - - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID3, LOCAL_MEMBER_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID4, ""); - - // Reinstate peerMember1 and verify no owner changes - - peer1 = actorFactory.createTestActor(TestEntityOwnershipShard.props(newShardBuilder( - peerId1, peerMap(leaderId.toString(), peerId2.toString()), PEER_MEMBER_1_NAME)), peerId1.toString()); - peer1.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - leader.tell(new PeerUp(peerId1.getMemberName(), peerId1.toString()), ActorRef.noSender()); - - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID3, LOCAL_MEMBER_NAME); - verifyOwner(leader, ENTITY_TYPE, ENTITY_ID4, ""); - - verifyNoEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID1, PEER_MEMBER_1_NAME); - verifyNoEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_1_NAME); - verifyNoEntityCandidate(leader, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_1_NAME); - - verifyNoEntityCandidate(peer2, ENTITY_TYPE, ENTITY_ID1, PEER_MEMBER_1_NAME); - verifyNoEntityCandidate(peer2, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_1_NAME); - verifyNoEntityCandidate(peer2, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_1_NAME); - - // Verify the reinstated peerMember1 is fully synced. - - verifyOwner(peer1, ENTITY_TYPE, ENTITY_ID1, LOCAL_MEMBER_NAME); - verifyOwner(peer1, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - verifyOwner(peer1, ENTITY_TYPE, ENTITY_ID3, LOCAL_MEMBER_NAME); - verifyOwner(peer1, ENTITY_TYPE, ENTITY_ID4, ""); - - AtomicLong leaderLastApplied = new AtomicLong(); - verifyRaftState(leader, rs -> { - assertEquals("LastApplied up-to-date", rs.getLastApplied(), rs.getLastIndex()); - leaderLastApplied.set(rs.getLastApplied()); - }); - - verifyRaftState(peer2, rs -> assertEquals("LastApplied", leaderLastApplied.get(), rs.getLastIndex())); - - // Kill the local leader and elect peer2 the leader. This should cause a new owner to be selected for - // the entities (1 and 3) previously owned by the local leader member. - - peer2.tell(new PeerAddressResolved(peerId1.toString(), peer1.path().toString()), ActorRef.noSender()); - peer2.tell(new PeerUp(leaderId.getMemberName(), leaderId.toString()), ActorRef.noSender()); - peer2.tell(new PeerUp(peerId1.getMemberName(), peerId1.toString()), ActorRef.noSender()); - - kit.watch(leader); - leader.tell(PoisonPill.getInstance(), ActorRef.noSender()); - kit.expectMsgClass(Duration.ofSeconds(5), Terminated.class); - kit.unwatch(leader); - peer2.tell(new PeerDown(leaderId.getMemberName(), leaderId.toString()), ActorRef.noSender()); - peer2.tell(TimeoutNow.INSTANCE, peer2); - - verifyRaftState(peer2, state -> - assertEquals("getRaftState", RaftState.Leader.toString(), state.getRaftState())); - - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID1, PEER_MEMBER_2_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID2, PEER_MEMBER_2_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID3, PEER_MEMBER_2_NAME); - verifyOwner(peer2, ENTITY_TYPE, ENTITY_ID4, ""); - - testLog.info("testOwnerChangesOnPeerAvailabilityChanges ending"); - } - - @Test - public void testLeaderIsolation() throws Exception { - testLog.info("testLeaderIsolation starting"); - - final ShardTestKit kit = new ShardTestKit(getSystem()); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId1 = newShardId(PEER_MEMBER_1_NAME); - ShardIdentifier peerId2 = newShardId(PEER_MEMBER_2_NAME); - - dataStoreContextBuilder.shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(4) - .shardIsolatedLeaderCheckIntervalInMillis(100000); - - TestActorRef peer1 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId1, peerMap(leaderId.toString(), peerId2.toString()), PEER_MEMBER_1_NAME)), - peerId1.toString()); - peer1.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef peer2 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId2, peerMap(leaderId.toString(), peerId1.toString()), PEER_MEMBER_2_NAME)), - peerId2.toString()); - peer2.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - dataStoreContextBuilder = DatastoreContext.newBuilderFrom(dataStoreContextBuilder.build()) - .shardIsolatedLeaderCheckIntervalInMillis(500); - - TestActorRef leader = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(leaderId, peerMap(peerId1.toString(), peerId2.toString()), LOCAL_MEMBER_NAME)), - leaderId.toString()); - - ShardTestKit.waitUntilLeader(leader); - - // Add entity1 candidates for all members with the leader as the owner - - DOMEntity entity1 = new DOMEntity(ENTITY_TYPE, ENTITY_ID1); - leader.tell(new RegisterCandidateLocal(entity1), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity1.getType(), entity1.getIdentifier(), LOCAL_MEMBER_NAME); - - peer1.tell(new RegisterCandidateLocal(entity1), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity1.getType(), entity1.getIdentifier(), PEER_MEMBER_1_NAME); - - peer2.tell(new RegisterCandidateLocal(entity1), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity1.getType(), entity1.getIdentifier(), PEER_MEMBER_2_NAME); - - verifyOwner(leader, entity1.getType(), entity1.getIdentifier(), LOCAL_MEMBER_NAME); - verifyOwner(peer1, entity1.getType(), entity1.getIdentifier(), LOCAL_MEMBER_NAME); - verifyOwner(peer2, entity1.getType(), entity1.getIdentifier(), LOCAL_MEMBER_NAME); - - // Add entity2 candidates for all members with peer1 as the owner - - DOMEntity entity2 = new DOMEntity(ENTITY_TYPE, ENTITY_ID2); - peer1.tell(new RegisterCandidateLocal(entity2), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity2.getType(), entity2.getIdentifier(), PEER_MEMBER_1_NAME); - - peer2.tell(new RegisterCandidateLocal(entity2), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity2.getType(), entity2.getIdentifier(), PEER_MEMBER_2_NAME); - - leader.tell(new RegisterCandidateLocal(entity2), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity2.getType(), entity2.getIdentifier(), LOCAL_MEMBER_NAME); - - verifyOwner(leader, entity2.getType(), entity2.getIdentifier(), PEER_MEMBER_1_NAME); - verifyOwner(peer1, entity2.getType(), entity2.getIdentifier(), PEER_MEMBER_1_NAME); - verifyOwner(peer2, entity2.getType(), entity2.getIdentifier(), PEER_MEMBER_1_NAME); - - // Add entity3 candidates for all members with peer2 as the owner - - DOMEntity entity3 = new DOMEntity(ENTITY_TYPE, ENTITY_ID3); - peer2.tell(new RegisterCandidateLocal(entity3), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity3.getType(), entity3.getIdentifier(), PEER_MEMBER_2_NAME); - - leader.tell(new RegisterCandidateLocal(entity3), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity3.getType(), entity3.getIdentifier(), LOCAL_MEMBER_NAME); - - peer1.tell(new RegisterCandidateLocal(entity3), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity3.getType(), entity3.getIdentifier(), PEER_MEMBER_1_NAME); - - verifyOwner(leader, entity3.getType(), entity3.getIdentifier(), PEER_MEMBER_2_NAME); - verifyOwner(peer1, entity3.getType(), entity3.getIdentifier(), PEER_MEMBER_2_NAME); - verifyOwner(peer2, entity3.getType(), entity3.getIdentifier(), PEER_MEMBER_2_NAME); - - // Add listeners on all members - - DOMEntityOwnershipListener leaderListener = mock(DOMEntityOwnershipListener.class); - leader.tell(new RegisterListenerLocal(leaderListener, ENTITY_TYPE), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verify(leaderListener, timeout(5000).times(3)).ownershipChanged(or(or( - ownershipChange(entity1, false, true, true), ownershipChange(entity2, false, false, true)), - ownershipChange(entity3, false, false, true))); - reset(leaderListener); - - DOMEntityOwnershipListener peer1Listener = mock(DOMEntityOwnershipListener.class); - peer1.tell(new RegisterListenerLocal(peer1Listener, ENTITY_TYPE), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verify(peer1Listener, timeout(5000).times(3)).ownershipChanged(or(or( - ownershipChange(entity1, false, false, true), ownershipChange(entity2, false, true, true)), - ownershipChange(entity3, false, false, true))); - reset(peer1Listener); - - DOMEntityOwnershipListener peer2Listener = mock(DOMEntityOwnershipListener.class); - peer2.tell(new RegisterListenerLocal(peer2Listener, ENTITY_TYPE), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verify(peer2Listener, timeout(5000).times(3)).ownershipChanged(or(or( - ownershipChange(entity1, false, false, true), ownershipChange(entity2, false, false, true)), - ownershipChange(entity3, false, true, true))); - reset(peer2Listener); - - // Isolate the leader by dropping AppendEntries to the followers and incoming messages from the followers. - - leader.underlyingActor().startDroppingMessagesOfType(RequestVote.class); - leader.underlyingActor().startDroppingMessagesOfType(AppendEntries.class); - - peer2.underlyingActor().startDroppingMessagesOfType(AppendEntries.class, - ae -> ae.getLeaderId().equals(leaderId.toString())); - peer1.underlyingActor().startDroppingMessagesOfType(AppendEntries.class); - - // Make peer1 start an election and become leader by enabling the ElectionTimeout message. - - peer1.underlyingActor().stopDroppingMessagesOfType(ElectionTimeout.class); - - // Send PeerDown to the isolated leader so it tries to re-assign ownership for the entities owned by the - // isolated peers. - - leader.tell(new PeerDown(peerId1.getMemberName(), peerId1.toString()), ActorRef.noSender()); - leader.tell(new PeerDown(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - - verifyRaftState(leader, state -> - assertEquals("getRaftState", RaftState.IsolatedLeader.toString(), state.getRaftState())); - - // Expect inJeopardy notification on the isolated leader. - - verify(leaderListener, timeout(5000).times(3)).ownershipChanged(or(or( - ownershipChange(entity1, true, true, true, true), ownershipChange(entity2, false, false, true, true)), - ownershipChange(entity3, false, false, true, true))); - reset(leaderListener); - - verifyRaftState(peer1, state -> - assertEquals("getRaftState", RaftState.Leader.toString(), state.getRaftState())); - - // Send PeerDown to the new leader peer1 so it re-assigns ownership for the entities owned by the - // isolated leader. - - peer1.tell(new PeerDown(leaderId.getMemberName(), leaderId.toString()), ActorRef.noSender()); - - verifyOwner(peer1, entity1.getType(), entity1.getIdentifier(), PEER_MEMBER_1_NAME); - - verify(peer1Listener, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, true, true)); - reset(peer1Listener); - - verify(peer2Listener, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, false, true)); - reset(peer2Listener); - - // Remove the isolation. - - leader.underlyingActor().stopDroppingMessagesOfType(RequestVote.class); - leader.underlyingActor().stopDroppingMessagesOfType(AppendEntries.class); - peer2.underlyingActor().stopDroppingMessagesOfType(AppendEntries.class); - peer1.underlyingActor().stopDroppingMessagesOfType(AppendEntries.class); - - // Previous leader should switch to Follower and send inJeopardy cleared notifications for all entities. - - verifyRaftState(leader, state -> - assertEquals("getRaftState", RaftState.Follower.toString(), state.getRaftState())); - - verify(leaderListener, timeout(5000).times(3)).ownershipChanged(or(or( - ownershipChange(entity1, true, true, true), ownershipChange(entity2, false, false, true)), - ownershipChange(entity3, false, false, true))); - - verifyOwner(leader, entity1.getType(), entity1.getIdentifier(), PEER_MEMBER_1_NAME); - verify(leaderListener, timeout(5000)).ownershipChanged(ownershipChange(entity1, true, false, true)); - - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verifyOwner(leader, entity2.getType(), entity2.getIdentifier(), PEER_MEMBER_1_NAME); - verifyOwner(leader, entity3.getType(), entity3.getIdentifier(), PEER_MEMBER_2_NAME); - - verifyNoMoreInteractions(leaderListener); - verifyNoMoreInteractions(peer1Listener); - verifyNoMoreInteractions(peer2Listener); - - testLog.info("testLeaderIsolation ending"); - } - - @Test - public void testLeaderIsolationWithPendingCandidateAdded() throws Exception { - testLog.info("testLeaderIsolationWithPendingCandidateAdded starting"); - - final ShardTestKit kit = new ShardTestKit(getSystem()); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId1 = newShardId(PEER_MEMBER_1_NAME); - ShardIdentifier peerId2 = newShardId(PEER_MEMBER_2_NAME); - - dataStoreContextBuilder.shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(4) - .shardIsolatedLeaderCheckIntervalInMillis(100000); - - TestActorRef peer1 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId1, peerMap(leaderId.toString(), peerId2.toString()), PEER_MEMBER_1_NAME), - actorFactory.createActor(MessageCollectorActor.props())), peerId1.toString()); - peer1.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef peer2 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId2, peerMap(leaderId.toString(), peerId1.toString()), PEER_MEMBER_2_NAME), - actorFactory.createTestActor(MessageCollectorActor.props())), peerId2.toString()); - peer2.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - dataStoreContextBuilder = DatastoreContext.newBuilderFrom(dataStoreContextBuilder.build()) - .shardIsolatedLeaderCheckIntervalInMillis(500); - - TestActorRef leader = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(leaderId, peerMap(peerId1.toString(), peerId2.toString()), LOCAL_MEMBER_NAME), - actorFactory.createTestActor(MessageCollectorActor.props())), leaderId.toString()); - - ShardTestKit.waitUntilLeader(leader); - - // Add listeners on all members - - DOMEntityOwnershipListener leaderListener = mock(DOMEntityOwnershipListener.class, - "DOMEntityOwnershipListener-" + LOCAL_MEMBER_NAME); - leader.tell(new RegisterListenerLocal(leaderListener, ENTITY_TYPE), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - DOMEntityOwnershipListener peer1Listener = mock(DOMEntityOwnershipListener.class, - "DOMEntityOwnershipListener-" + PEER_MEMBER_1_NAME); - peer1.tell(new RegisterListenerLocal(peer1Listener, ENTITY_TYPE), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - DOMEntityOwnershipListener peer2Listener = mock(DOMEntityOwnershipListener.class, - "DOMEntityOwnershipListener-" + PEER_MEMBER_2_NAME); - peer2.tell(new RegisterListenerLocal(peer2Listener, ENTITY_TYPE), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Drop the CandidateAdded message to the leader for now. - - leader.underlyingActor().startDroppingMessagesOfType(CandidateAdded.class); - - // Add an entity candidates for the leader. Since we've blocked the CandidateAdded message, it won't be - // assigned the owner. - - DOMEntity entity1 = new DOMEntity(ENTITY_TYPE, ENTITY_ID1); - leader.tell(new RegisterCandidateLocal(entity1), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity1.getType(), entity1.getIdentifier(), LOCAL_MEMBER_NAME); - verifyCommittedEntityCandidate(peer1, entity1.getType(), entity1.getIdentifier(), LOCAL_MEMBER_NAME); - verifyCommittedEntityCandidate(peer2, entity1.getType(), entity1.getIdentifier(), LOCAL_MEMBER_NAME); - - DOMEntity entity2 = new DOMEntity(ENTITY_TYPE, ENTITY_ID2); - leader.tell(new RegisterCandidateLocal(entity2), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, entity2.getType(), entity2.getIdentifier(), LOCAL_MEMBER_NAME); - verifyCommittedEntityCandidate(peer1, entity2.getType(), entity2.getIdentifier(), LOCAL_MEMBER_NAME); - verifyCommittedEntityCandidate(peer2, entity2.getType(), entity2.getIdentifier(), LOCAL_MEMBER_NAME); - - // Capture the CandidateAdded messages. - - final List candidateAdded = expectMatching(leader.underlyingActor().collectorActor(), - CandidateAdded.class, 2); - - // Drop AppendEntries to the followers containing a log entry, which will be for the owner writes after we - // forward the CandidateAdded messages to the leader. This will leave the pending owner write tx's uncommitted. - - peer1.underlyingActor().startDroppingMessagesOfType(AppendEntries.class, ae -> ae.getEntries().size() > 0); - peer2.underlyingActor().startDroppingMessagesOfType(AppendEntries.class, ae -> ae.getEntries().size() > 0); - - // Now forward the CandidateAdded messages to the leader and wait for it to send out the AppendEntries. - - leader.underlyingActor().stopDroppingMessagesOfType(CandidateAdded.class); - leader.tell(candidateAdded.get(0), leader); - leader.tell(candidateAdded.get(1), leader); - - expectMatching(peer1.underlyingActor().collectorActor(), AppendEntries.class, 2, - ae -> ae.getEntries().size() > 0); - - // Verify no owner assigned. - - verifyNoOwnerSet(leader, entity1.getType(), entity1.getIdentifier()); - verifyNoOwnerSet(leader, entity2.getType(), entity2.getIdentifier()); - - // Isolate the leader by dropping AppendEntries to the followers and incoming messages from the followers. - - leader.underlyingActor().startDroppingMessagesOfType(RequestVote.class); - leader.underlyingActor().startDroppingMessagesOfType(AppendEntries.class); - - peer2.underlyingActor().startDroppingMessagesOfType(AppendEntries.class, - ae -> ae.getLeaderId().equals(leaderId.toString())); - peer1.underlyingActor().startDroppingMessagesOfType(AppendEntries.class); - - // Send PeerDown to the isolated leader - should be no-op since there's no owned entities. - - leader.tell(new PeerDown(peerId1.getMemberName(), peerId1.toString()), ActorRef.noSender()); - leader.tell(new PeerDown(peerId2.getMemberName(), peerId2.toString()), ActorRef.noSender()); - - // Verify the leader transitions to IsolatedLeader. - - verifyRaftState(leader, state -> assertEquals("getRaftState", RaftState.IsolatedLeader.toString(), - state.getRaftState())); - - // Send PeerDown to the new leader peer1. - - peer1.tell(new PeerDown(leaderId.getMemberName(), leaderId.toString()), ActorRef.noSender()); - - // Make peer1 start an election and become leader by sending the TimeoutNow message. - - peer1.tell(TimeoutNow.INSTANCE, ActorRef.noSender()); - - // Verify the peer1 transitions to Leader. - - verifyRaftState(peer1, state -> assertEquals("getRaftState", RaftState.Leader.toString(), - state.getRaftState())); - - verifyNoOwnerSet(peer1, entity1.getType(), entity1.getIdentifier()); - verifyNoOwnerSet(peer2, entity1.getType(), entity2.getIdentifier()); - - verifyNoMoreInteractions(peer1Listener); - verifyNoMoreInteractions(peer2Listener); - - // Add candidate peer1 candidate for entity2. - - peer1.tell(new RegisterCandidateLocal(entity2), kit.getRef()); - - verifyOwner(peer1, entity2.getType(), entity2.getIdentifier(), PEER_MEMBER_1_NAME); - verify(peer1Listener, timeout(5000)).ownershipChanged(ownershipChange(entity2, false, true, true)); - verify(peer2Listener, timeout(5000)).ownershipChanged(ownershipChange(entity2, false, false, true)); - - reset(leaderListener, peer1Listener, peer2Listener); - - // Remove the isolation. - - leader.underlyingActor().stopDroppingMessagesOfType(RequestVote.class); - leader.underlyingActor().stopDroppingMessagesOfType(AppendEntries.class); - peer2.underlyingActor().stopDroppingMessagesOfType(AppendEntries.class); - peer1.underlyingActor().stopDroppingMessagesOfType(AppendEntries.class); - - // Previous leader should switch to Follower. - - verifyRaftState(leader, state -> assertEquals("getRaftState", RaftState.Follower.toString(), - state.getRaftState())); - - // Send PeerUp to peer1 and peer2. - - peer1.tell(new PeerUp(leaderId.getMemberName(), leaderId.toString()), ActorRef.noSender()); - peer2.tell(new PeerUp(leaderId.getMemberName(), leaderId.toString()), ActorRef.noSender()); - - // The previous leader should become the owner of entity1. - - verifyOwner(leader, entity1.getType(), entity1.getIdentifier(), LOCAL_MEMBER_NAME); - - // The previous leader's DOMEntityOwnershipListener should get 4 total notifications: - // - inJeopardy cleared for entity1 (wasOwner=false, isOwner=false, hasOwner=false, inJeopardy=false) - // - inJeopardy cleared for entity2 (wasOwner=false, isOwner=false, hasOwner=false, inJeopardy=false) - // - local owner granted for entity1 (wasOwner=false, isOwner=true, hasOwner=true, inJeopardy=false) - // - remote owner for entity2 (wasOwner=false, isOwner=false, hasOwner=true, inJeopardy=false) - verify(leaderListener, timeout(5000).times(4)).ownershipChanged(or( - or(ownershipChange(entity1, false, false, false), ownershipChange(entity2, false, false, false)), - or(ownershipChange(entity1, false, true, true), ownershipChange(entity2, false, false, true)))); - - verify(peer1Listener, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, false, true)); - verify(peer2Listener, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, false, true)); - - // Verify entity2's owner doesn't change. - - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verifyOwner(peer1, entity2.getType(), entity2.getIdentifier(), PEER_MEMBER_1_NAME); - - verifyNoMoreInteractions(leaderListener); - verifyNoMoreInteractions(peer1Listener); - verifyNoMoreInteractions(peer2Listener); - - testLog.info("testLeaderIsolationWithPendingCandidateAdded ending"); - } - - @Test - public void testListenerRegistration() { - testLog.info("testListenerRegistration starting"); - - ShardTestKit kit = new ShardTestKit(getSystem()); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId = newShardId(PEER_MEMBER_1_NAME); - - TestActorRef peer = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId, peerMap(leaderId.toString()), PEER_MEMBER_1_NAME)), peerId.toString()); - peer.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef leader = actorFactory.createTestActor( - newShardProps(leaderId, peerMap(peerId.toString()), LOCAL_MEMBER_NAME), leaderId.toString()); - - ShardTestKit.waitUntilLeader(leader); - - String otherEntityType = "otherEntityType"; - final DOMEntity entity1 = new DOMEntity(ENTITY_TYPE, ENTITY_ID1); - final DOMEntity entity2 = new DOMEntity(ENTITY_TYPE, ENTITY_ID2); - final DOMEntity entity3 = new DOMEntity(ENTITY_TYPE, ENTITY_ID3); - final DOMEntity entity4 = new DOMEntity(otherEntityType, ENTITY_ID3); - DOMEntityOwnershipListener listener = mock(DOMEntityOwnershipListener.class); - - // Register listener - - leader.tell(new RegisterListenerLocal(listener, ENTITY_TYPE), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Register a couple candidates for the desired entity type and verify listener is notified. - - leader.tell(new RegisterCandidateLocal(entity1), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verify(listener, timeout(5000)).ownershipChanged(ownershipChange(entity1, false, true, true)); - - leader.tell(new RegisterCandidateLocal(entity2), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - 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. - - leader.tell(new RegisterCandidateLocal(entity4), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verify(listener, never()).ownershipChanged(ownershipChange(entity4)); - - // Register remote candidate for entity1 - - peer.tell(new RegisterCandidateLocal(entity1), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - verifyCommittedEntityCandidate(leader, ENTITY_TYPE, entity1.getIdentifier(), PEER_MEMBER_1_NAME); - - // Unregister the local candidate for entity1 and verify listener is notified - - leader.tell(new UnregisterCandidateLocal(entity1), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - 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 - - leader.tell(new UnregisterListenerLocal(listener, ENTITY_TYPE), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - leader.tell(new RegisterCandidateLocal(entity3), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - verifyOwner(leader, ENTITY_TYPE, entity3.getIdentifier(), LOCAL_MEMBER_NAME); - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - verify(listener, never()).ownershipChanged(any(DOMEntityOwnershipChange.class)); - - // Re-register the listener and verify it gets notified of currently owned entities - - reset(listener); - - leader.tell(new RegisterListenerLocal(listener, ENTITY_TYPE), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - 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(ownershipChange(entity4)); - verify(listener, times(1)).ownershipChanged(ownershipChange(entity1)); - - testLog.info("testListenerRegistration ending"); - } - - @Test - public void testDelayedEntityOwnerSelectionWhenMaxPeerRequestsReceived() { - testLog.info("testDelayedEntityOwnerSelectionWhenMaxPeerRequestsReceived starting"); - - ShardTestKit kit = new ShardTestKit(getSystem()); - EntityOwnerSelectionStrategyConfig.Builder builder = EntityOwnerSelectionStrategyConfig.newBuilder() - .addStrategy(ENTITY_TYPE, LastCandidateSelectionStrategy.class, 500); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId = newShardId(PEER_MEMBER_1_NAME); - - TestActorRef peer = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId, peerMap(leaderId.toString()), PEER_MEMBER_1_NAME)), peerId.toString()); - peer.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef leader = actorFactory.createTestActor( - newShardProps(leaderId, peerMap(peerId.toString()), LOCAL_MEMBER_NAME, builder.build()), - leaderId.toString()); - - ShardTestKit.waitUntilLeader(leader); - - DOMEntity entity = new DOMEntity(ENTITY_TYPE, ENTITY_ID1); - - // Add a remote candidate - - peer.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Register local - - leader.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Verify the local candidate becomes owner - - verifyCommittedEntityCandidate(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_1_NAME); - verifyCommittedEntityCandidate(leader, entity.getType(), entity.getIdentifier(), LOCAL_MEMBER_NAME); - verifyOwner(leader, entity.getType(), entity.getIdentifier(), LOCAL_MEMBER_NAME); - - testLog.info("testDelayedEntityOwnerSelectionWhenMaxPeerRequestsReceived ending"); - } - - @Test - public void testDelayedEntityOwnerSelection() { - testLog.info("testDelayedEntityOwnerSelection starting"); - - final ShardTestKit kit = new ShardTestKit(getSystem()); - EntityOwnerSelectionStrategyConfig.Builder builder = EntityOwnerSelectionStrategyConfig.newBuilder() - .addStrategy(ENTITY_TYPE, LastCandidateSelectionStrategy.class, 500); - - dataStoreContextBuilder.shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(2); - - ShardIdentifier leaderId = newShardId(LOCAL_MEMBER_NAME); - ShardIdentifier peerId1 = newShardId(PEER_MEMBER_1_NAME); - ShardIdentifier peerId2 = newShardId(PEER_MEMBER_2_NAME); - - TestActorRef peer1 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId1, peerMap(leaderId.toString(), peerId2.toString()), PEER_MEMBER_1_NAME)), - peerId1.toString()); - peer1.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef peer2 = actorFactory.createTestActor(TestEntityOwnershipShard.props( - newShardBuilder(peerId2, peerMap(leaderId.toString(), peerId1.toString()), PEER_MEMBER_2_NAME)), - peerId2.toString()); - peer2.underlyingActor().startDroppingMessagesOfType(ElectionTimeout.class); - - TestActorRef leader = actorFactory.createTestActor( - newShardProps(leaderId, peerMap(peerId1.toString(), peerId2.toString()), LOCAL_MEMBER_NAME, - builder.build()), leaderId.toString()); - - ShardTestKit.waitUntilLeader(leader); - - DOMEntity entity = new DOMEntity(ENTITY_TYPE, ENTITY_ID1); - - // Add a remote candidate - - peer1.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Register local - - leader.tell(new RegisterCandidateLocal(entity), kit.getRef()); - kit.expectMsgClass(SuccessReply.class); - - // Verify the local candidate becomes owner - - verifyCommittedEntityCandidate(leader, entity.getType(), entity.getIdentifier(), PEER_MEMBER_1_NAME); - verifyCommittedEntityCandidate(leader, entity.getType(), entity.getIdentifier(), LOCAL_MEMBER_NAME); - verifyOwner(leader, entity.getType(), entity.getIdentifier(), LOCAL_MEMBER_NAME); - - testLog.info("testDelayedEntityOwnerSelection ending"); - } - - private Props newLocalShardProps() { - return newShardProps(newShardId(LOCAL_MEMBER_NAME), Collections.emptyMap(), LOCAL_MEMBER_NAME); - } - - private Props newShardProps(final ShardIdentifier shardId, final Map peers, - final String memberName) { - return newShardProps(shardId, peers, memberName, EntityOwnerSelectionStrategyConfig.newBuilder().build()); - } - - private Props newShardProps(final ShardIdentifier shardId, final Map peers, final String memberName, - final EntityOwnerSelectionStrategyConfig config) { - return newShardBuilder(shardId, peers, memberName).ownerSelectionStrategyConfig(config).props() - .withDispatcher(Dispatchers.DefaultDispatcherId()); - } - - private EntityOwnershipShard.Builder newShardBuilder(final ShardIdentifier shardId, final Map peers, - final String memberName) { - return EntityOwnershipShard.newBuilder() - .id(shardId) - .peerAddresses(peers) - .datastoreContext(dataStoreContextBuilder.build()) - .schemaContextProvider(() -> EOSTestUtils.SCHEMA_CONTEXT) - .localMemberName(MemberName.forName(memberName)) - .ownerSelectionStrategyConfig(EntityOwnerSelectionStrategyConfig.newBuilder().build()); - } - - private Map peerMap(final String... peerIds) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (String peerId: peerIds) { - builder.put(peerId, actorFactory.createTestActorPath(peerId)).build(); - } - - return builder.build(); - } - - private static class TestEntityOwnershipShard extends EntityOwnershipShard { - private final ActorRef collectorActor; - private final Map, Predicate> dropMessagesOfType = new ConcurrentHashMap<>(); - - TestEntityOwnershipShard(final Builder builder, final ActorRef collectorActor) { - super(builder); - this.collectorActor = collectorActor; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void handleCommand(final Object message) { - Predicate drop = dropMessagesOfType.get(message.getClass()); - if (drop == null || !drop.test(message)) { - super.handleCommand(message); - } - - if (collectorActor != null) { - collectorActor.tell(message, ActorRef.noSender()); - } - } - - void startDroppingMessagesOfType(final Class msgClass) { - dropMessagesOfType.put(msgClass, msg -> true); - } - - void startDroppingMessagesOfType(final Class msgClass, final Predicate filter) { - dropMessagesOfType.put(msgClass, filter); - } - - void stopDroppingMessagesOfType(final Class msgClass) { - dropMessagesOfType.remove(msgClass); - } - - ActorRef collectorActor() { - return collectorActor; - } - - static Props props(final Builder builder) { - return props(builder, null); - } - - static Props props(final Builder builder, final ActorRef collectorActor) { - return Props.create(TestEntityOwnershipShard.class, builder, collectorActor) - .withDispatcher(Dispatchers.DefaultDispatcherId()); - } - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipStatisticsTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipStatisticsTest.java deleted file mode 100644 index 4ef77e5cdc..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/EntityOwnershipStatisticsTest.java +++ /dev/null @@ -1,146 +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.entityownership; - -import static org.junit.Assert.assertEquals; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.ENTITY_OWNERS_PATH; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityEntryWithOwner; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityOwnersWithCandidate; -import static org.opendaylight.controller.cluster.entityownership.EntityOwnersModel.entityPath; - -import java.util.Map; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.opendaylight.controller.cluster.datastore.AbstractActorTest; -import org.opendaylight.controller.cluster.datastore.Shard; -import org.opendaylight.controller.cluster.datastore.ShardDataTree; -import org.opendaylight.yangtools.yang.common.QName; -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.DataValidationFailedException; -import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; - -public class EntityOwnershipStatisticsTest extends AbstractActorTest { - private static final String LOCAL_MEMBER_NAME = "member-1"; - private static final String REMOTE_MEMBER_NAME1 = "member-2"; - private static final String REMOTE_MEMBER_NAME2 = "member-3"; - private static final String ENTITY_TYPE = "test"; - private static final YangInstanceIdentifier ENTITY_ID1 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity1")); - private static final YangInstanceIdentifier ENTITY_ID2 = - YangInstanceIdentifier.of(QName.create("test", "2015-08-14", "entity2")); - - private final Shard mockShard = Mockito.mock(Shard.class); - - private final ShardDataTree shardDataTree = new ShardDataTree(mockShard, EOSTestUtils.SCHEMA_CONTEXT, - TreeType.OPERATIONAL); - private EntityOwnershipStatistics ownershipStatistics; - - @Before - public void setup() { - ownershipStatistics = new EntityOwnershipStatistics(); - ownershipStatistics.init(shardDataTree); - } - - @Test - 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)); - - // Write local member as owner for entity 1 - - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, LOCAL_MEMBER_NAME)); - assertStatistics(ownershipStatistics.all(), LOCAL_MEMBER_NAME, 1L); - - // Add remote member 1 as candidate for entity 1 - ownershipStatistics support should not get notified - - writeNode(ENTITY_OWNERS_PATH, entityOwnersWithCandidate(ENTITY_TYPE, ENTITY_ID1, REMOTE_MEMBER_NAME1)); - assertStatistics(ownershipStatistics.all(), LOCAL_MEMBER_NAME, 1L); - - // Change owner to remote member 1 for entity 1 - - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, REMOTE_MEMBER_NAME1)); - Map> statistics = ownershipStatistics.all(); - assertStatistics(statistics, LOCAL_MEMBER_NAME, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME1, 1L); - - // Change owner to remote member 2 for entity 1 - - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, REMOTE_MEMBER_NAME2)); - statistics = ownershipStatistics.all(); - assertStatistics(statistics, LOCAL_MEMBER_NAME, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME1, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME2, 1L); - - // Clear the owner for entity 1 - - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, "")); - statistics = ownershipStatistics.all(); - assertStatistics(statistics, LOCAL_MEMBER_NAME, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME1, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME2, 0L); - - // Change owner to the local member for entity 1 - - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID1), entityEntryWithOwner(ENTITY_ID1, LOCAL_MEMBER_NAME)); - statistics = ownershipStatistics.all(); - assertStatistics(statistics, LOCAL_MEMBER_NAME, 1L); - assertStatistics(statistics, REMOTE_MEMBER_NAME1, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME2, 0L); - - // Change owner to remote member 1 for entity 2 - - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, REMOTE_MEMBER_NAME1)); - statistics = ownershipStatistics.all(); - assertStatistics(statistics, LOCAL_MEMBER_NAME, 1L); - assertStatistics(statistics, REMOTE_MEMBER_NAME1, 1L); - assertStatistics(statistics, REMOTE_MEMBER_NAME2, 0L); - - // Change owner to the local member for entity 2 - - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, LOCAL_MEMBER_NAME)); - statistics = ownershipStatistics.all(); - assertStatistics(statistics, LOCAL_MEMBER_NAME, 2L); - assertStatistics(statistics, REMOTE_MEMBER_NAME1, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME2, 0L); - - // Write local member owner for entity 2 again - expect no change - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, LOCAL_MEMBER_NAME)); - statistics = ownershipStatistics.all(); - assertStatistics(statistics, LOCAL_MEMBER_NAME, 2L); - assertStatistics(statistics, REMOTE_MEMBER_NAME1, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME2, 0L); - - // Clear the owner for entity 2 - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, "")); - statistics = ownershipStatistics.all(); - assertStatistics(statistics, LOCAL_MEMBER_NAME, 1L); - assertStatistics(statistics, REMOTE_MEMBER_NAME1, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME2, 0L); - - // Clear the owner for entity 2 again - expect no change - - writeNode(entityPath(ENTITY_TYPE, ENTITY_ID2), entityEntryWithOwner(ENTITY_ID2, "")); - statistics = ownershipStatistics.all(); - assertStatistics(statistics, LOCAL_MEMBER_NAME, 1L); - assertStatistics(statistics, REMOTE_MEMBER_NAME1, 0L); - assertStatistics(statistics, REMOTE_MEMBER_NAME2, 0L); - - } - - private static void assertStatistics(final Map> statistics, final String memberName, - final long val) { - assertEquals(val, statistics.get(ENTITY_TYPE).get(memberName).longValue()); - } - - private void writeNode(final YangInstanceIdentifier path, final NormalizedNode node) - throws DataValidationFailedException { - AbstractEntityOwnershipTest.writeNode(path, node, shardDataTree); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategyConfigReaderTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategyConfigReaderTest.java deleted file mode 100644 index b83f85e167..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/EntityOwnerSelectionStrategyConfigReaderTest.java +++ /dev/null @@ -1,95 +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.entityownership.selectionstrategy; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.junit.Test; - -public class EntityOwnerSelectionStrategyConfigReaderTest { - - @Test - public void testReadStrategies() { - final Map props = new java.util.HashMap<>(); - props.put("entity.type.test", - "org.opendaylight.controller.cluster.entityownership.selectionstrategy.LastCandidateSelectionStrategy,100"); - - - final EntityOwnerSelectionStrategyConfig config = EntityOwnerSelectionStrategyConfigReader - .loadStrategyWithConfig(props); - - assertTrue(config.isStrategyConfigured("test")); - - final EntityOwnerSelectionStrategy strategy = config.createStrategy("test", - Collections.emptyMap()); - assertTrue(strategy.toString(), strategy instanceof LastCandidateSelectionStrategy); - assertEquals(100L, strategy.getSelectionDelayInMillis()); - - final EntityOwnerSelectionStrategy strategy1 = config.createStrategy("test", Collections.emptyMap()); - assertEquals(strategy, strategy1); - - config.clearStrategies(); - - final EntityOwnerSelectionStrategy strategy2 = config.createStrategy("test", Collections.emptyMap()); - assertNotEquals(strategy1, strategy2); - } - - @Test - public void testReadStrategiesWithEmptyConfiguration() { - - final Map props = new HashMap<>(); - final EntityOwnerSelectionStrategyConfig config = EntityOwnerSelectionStrategyConfigReader - .loadStrategyWithConfig(props); - - assertFalse(config.isStrategyConfigured("test")); - } - - @Test - public void testReadStrategiesWithNullConfiguration() { - final EntityOwnerSelectionStrategyConfig config = EntityOwnerSelectionStrategyConfigReader - .loadStrategyWithConfig(null); - assertFalse(config.isStrategyConfigured("test")); - } - - @Test(expected = IllegalArgumentException.class) - public void testReadStrategiesInvalidDelay() { - final Map props = new HashMap<>(); - props.put("entity.type.test", - "org.opendaylight.controller.cluster.entityownership.selectionstrategy.LastCandidateSelectionStrategy,foo"); - EntityOwnerSelectionStrategyConfigReader.loadStrategyWithConfig(props); - } - - @Test(expected = IllegalArgumentException.class) - public void testReadStrategiesInvalidClassType() { - final Map props = new HashMap<>(); - props.put("entity.type.test", "String,100"); - EntityOwnerSelectionStrategyConfigReader.loadStrategyWithConfig(props); - } - - @Test - public void testReadStrategiesMissingDelay() { - final Map props = new HashMap<>(); - props.put("entity.type.test", - "org.opendaylight.controller.cluster.entityownership.selectionstrategy.LastCandidateSelectionStrategy,100"); - props.put("entity.type.test1", - "org.opendaylight.controller.cluster.entityownership.selectionstrategy.LastCandidateSelectionStrategy"); - - - final EntityOwnerSelectionStrategyConfig config = EntityOwnerSelectionStrategyConfigReader - .loadStrategyWithConfig(props); - - assertEquals(100, config.createStrategy("test", Collections.emptyMap()).getSelectionDelayInMillis()); - assertEquals(0, config.createStrategy("test2", Collections.emptyMap()).getSelectionDelayInMillis()); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/LastCandidateSelectionStrategy.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/LastCandidateSelectionStrategy.java deleted file mode 100644 index 07d6f63e8d..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/LastCandidateSelectionStrategy.java +++ /dev/null @@ -1,24 +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.entityownership.selectionstrategy; - -import com.google.common.collect.Iterables; -import java.util.Collection; -import java.util.Map; - -public class LastCandidateSelectionStrategy extends AbstractEntityOwnerSelectionStrategy { - public LastCandidateSelectionStrategy(long selectionDelayInMillis, Map initialStatistics) { - super(selectionDelayInMillis, initialStatistics); - } - - @Override - public String newOwner(String currentOwner, Collection viableCandidates) { - return Iterables.getLast(viableCandidates); - } -} diff --git a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/LeastLoadedCandidateSelectionStrategyTest.java b/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/LeastLoadedCandidateSelectionStrategyTest.java deleted file mode 100644 index 73628408d4..0000000000 --- a/opendaylight/md-sal/sal-distributed-eos/src/test/java/org/opendaylight/controller/cluster/entityownership/selectionstrategy/LeastLoadedCandidateSelectionStrategyTest.java +++ /dev/null @@ -1,89 +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.entityownership.selectionstrategy; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.junit.Test; - -public class LeastLoadedCandidateSelectionStrategyTest { - - @Test - public void testLeastLoadedStrategy() { - LeastLoadedCandidateSelectionStrategy strategy = new LeastLoadedCandidateSelectionStrategy( - 0L, Collections.emptyMap()); - - String owner = strategy.newOwner(null, prepareViableCandidates(3)); - assertEquals("member-1", owner); - - Map localStatistics = strategy.getLocalStatistics(); - assertEquals(1L, (long) localStatistics.get("member-1")); - - // member-2 has least load - strategy = new LeastLoadedCandidateSelectionStrategy(0L, prepareStatistics(5,2,4)); - owner = strategy.newOwner(null, prepareViableCandidates(3)); - assertEquals("member-2", owner); - - assertStatistics(strategy.getLocalStatistics(), 5,3,4); - - // member-3 has least load - strategy = new LeastLoadedCandidateSelectionStrategy(0L, prepareStatistics(5,7,4)); - owner = strategy.newOwner(null, prepareViableCandidates(3)); - assertEquals("member-3", owner); - - assertStatistics(strategy.getLocalStatistics(), 5,7,5); - - // member-1 has least load - strategy = new LeastLoadedCandidateSelectionStrategy(0L, prepareStatistics(1,7,4)); - owner = strategy.newOwner(null, prepareViableCandidates(3)); - assertEquals("member-1", owner); - - assertStatistics(strategy.getLocalStatistics(), 2,7,4); - - // Let member-3 become the owner - strategy = new LeastLoadedCandidateSelectionStrategy(0L, prepareStatistics(3,3,0)); - owner = strategy.newOwner(null, prepareViableCandidates(3)); - assertEquals("member-3", owner); - - assertStatistics(strategy.getLocalStatistics(), 3,3,1); - - // member-3 is no longer viable so choose a new owner - owner = strategy.newOwner("member-3", prepareViableCandidates(2)); - assertEquals("member-1", owner); - - assertStatistics(strategy.getLocalStatistics(), 4,3,0); - - } - - private static Map prepareStatistics(long... count) { - Map statistics = new HashMap<>(); - for (int i = 0; i < count.length; i++) { - statistics.put("member-" + (i + 1), count[i]); - } - return statistics; - } - - private static Collection prepareViableCandidates(int count) { - Collection viableCandidates = new ArrayList<>(); - for (int i = 0; i < count; i++) { - viableCandidates.add("member-" + (i + 1)); - } - return viableCandidates; - } - - private static void assertStatistics(Map statistics, long... count) { - for (int i = 0; i < count.length; i++) { - assertEquals(count[i], (long) statistics.get("member-" + (i + 1))); - } - } -}