2 * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.eos.akka;
10 import static org.awaitility.Awaitility.await;
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertNull;
15 import static org.junit.Assert.assertTrue;
16 import static org.junit.Assert.fail;
18 import akka.actor.ActorSystem;
19 import akka.actor.testkit.typed.javadsl.ActorTestKit;
20 import akka.actor.typed.ActorRef;
21 import akka.actor.typed.javadsl.Adapter;
22 import akka.actor.typed.javadsl.AskPattern;
23 import akka.cluster.ddata.ORMap;
24 import akka.cluster.ddata.ORSet;
25 import akka.cluster.ddata.typed.javadsl.DistributedData;
26 import akka.cluster.ddata.typed.javadsl.Replicator;
27 import com.typesafe.config.ConfigFactory;
28 import java.time.Duration;
30 import java.util.Optional;
31 import java.util.concurrent.CompletionStage;
32 import java.util.concurrent.ExecutionException;
33 import org.awaitility.Durations;
34 import org.junit.After;
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.opendaylight.controller.eos.akka.bootstrap.command.RunningContext;
38 import org.opendaylight.controller.eos.akka.owner.supervisor.command.OwnerSupervisorCommand;
39 import org.opendaylight.controller.eos.akka.registry.candidate.CandidateRegistry;
40 import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
41 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
42 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
43 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration;
44 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration;
45 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.EntityName;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.EntityType;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntitiesInputBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntitiesOutput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntityInputBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntityOutput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntityOwnerInputBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntityOwnerOutput;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.NodeName;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.get.entities.output.EntitiesKey;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
59 import org.opendaylight.yangtools.yang.common.QName;
60 import org.opendaylight.yangtools.yang.common.RpcResult;
61 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
63 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
65 public class AkkaEntityOwnershipServiceTest extends AbstractNativeEosTest {
66 static final String ENTITY_TYPE = "test";
67 static final String ENTITY_TYPE2 = "test2";
68 static final QName QNAME = QName.create("test", "2015-08-11", "foo");
70 private ActorSystem system;
71 private akka.actor.typed.ActorSystem<Void> typedSystem;
72 private AkkaEntityOwnershipService service;
73 private ActorRef<Replicator.Command> replicator;
76 public void setUp() throws Exception {
77 system = ActorSystem.create("ClusterSystem", ConfigFactory.load());
78 typedSystem = Adapter.toTyped(this.system);
79 replicator = DistributedData.get(typedSystem).replicator();
81 service = new AkkaEntityOwnershipService(system, CODEC_CONTEXT);
85 public void tearDown() throws InterruptedException, ExecutionException {
87 ActorTestKit.shutdown(Adapter.toTyped(system), Duration.ofSeconds(20));
91 public void testRegisterCandidate() throws Exception {
92 final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME);
93 final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
95 final DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(entity);
97 verifyEntityOwnershipCandidateRegistration(entity, reg);
98 verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
101 service.registerCandidate(entity);
102 fail("Expected CandidateAlreadyRegisteredException");
103 } catch (final CandidateAlreadyRegisteredException e) {
105 assertEquals("getEntity", entity, e.getEntity());
108 final DOMEntity entity2 = new DOMEntity(ENTITY_TYPE2, entityId);
109 final DOMEntityOwnershipCandidateRegistration reg2 = service.registerCandidate(entity2);
111 verifyEntityOwnershipCandidateRegistration(entity2, reg2);
112 verifyEntityCandidateRegistered(ENTITY_TYPE2, entityId, "member-1");
116 public void testUnregisterCandidate() throws Exception {
117 final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME);
118 final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
120 final DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(entity);
122 verifyEntityOwnershipCandidateRegistration(entity, reg);
123 verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
126 verifyEntityCandidateMissing(ENTITY_TYPE, entityId, "member-1");
128 service.registerCandidate(entity);
129 verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
133 public void testListenerRegistration() throws Exception {
135 final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME);
136 final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
137 final MockEntityOwnershipListener listener = new MockEntityOwnershipListener("member-1");
139 final DOMEntityOwnershipListenerRegistration reg = service.registerListener(entity.getType(), listener);
141 assertNotNull("EntityOwnershipListenerRegistration null", reg);
142 assertEquals("getEntityType", entity.getType(), reg.getEntityType());
143 assertEquals("getInstance", listener, reg.getInstance());
145 final DOMEntityOwnershipCandidateRegistration candidate = service.registerCandidate(entity);
147 verifyListenerState(listener, entity, true, true, false);
148 final int changes = listener.getChanges().size();
153 verifyEntityCandidateMissing(ENTITY_TYPE, entityId, "member-1");
155 service.registerCandidate(entity);
156 // check listener not called when listener registration closed
157 await().pollDelay(Durations.TWO_SECONDS).until(() -> listener.getChanges().size() == changes);
161 public void testGetOwnershipState() throws Exception {
162 final DOMEntity entity = new DOMEntity(ENTITY_TYPE, "one");
164 final DOMEntityOwnershipCandidateRegistration registration = service.registerCandidate(entity);
165 verifyGetOwnershipState(service, entity, EntityOwnershipState.IS_OWNER);
167 final RunningContext runningContext = service.getRunningContext();
168 registerCandidates(runningContext.getCandidateRegistry(), entity, "member-2");
170 final ActorRef<OwnerSupervisorCommand> ownerSupervisor = runningContext.getOwnerSupervisor();
171 reachableMember(ownerSupervisor, "member-2", DEFAULT_DATACENTER);
172 unreachableMember(ownerSupervisor, "member-1", DEFAULT_DATACENTER);
173 verifyGetOwnershipState(service, entity, EntityOwnershipState.OWNED_BY_OTHER);
175 final DOMEntity entity2 = new DOMEntity(ENTITY_TYPE, "two");
176 final Optional<EntityOwnershipState> state = service.getOwnershipState(entity2);
177 assertFalse(state.isPresent());
179 unreachableMember(ownerSupervisor, "member-2", DEFAULT_DATACENTER);
180 verifyGetOwnershipState(service, entity, EntityOwnershipState.NO_OWNER);
184 public void testIsCandidateRegistered() throws Exception {
185 final DOMEntity test = new DOMEntity("test-type", "test");
187 assertFalse(service.isCandidateRegistered(test));
189 service.registerCandidate(test);
191 assertTrue(service.isCandidateRegistered(test));
195 public void testEntityRetrievalWithYiid() throws Exception {
196 final YangInstanceIdentifier entityId = YangInstanceIdentifier.create(new NodeIdentifier(NetworkTopology.QNAME),
197 new NodeIdentifier(Topology.QNAME),
198 NodeIdentifierWithPredicates.of(Topology.QNAME, QName.create(Topology.QNAME, "topology-id"), "test"),
199 new NodeIdentifier(Node.QNAME),
200 NodeIdentifierWithPredicates.of(Node.QNAME, QName.create(Node.QNAME, "node-id"), "test://test-node"));
202 final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
204 final DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(entity);
206 verifyEntityOwnershipCandidateRegistration(entity, reg);
207 verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
209 RpcResult<GetEntityOutput> getEntityResult = service.getEntity(new GetEntityInputBuilder()
210 .setName(new EntityName(CODEC_CONTEXT.fromYangInstanceIdentifier(entityId)))
211 .setType(new EntityType(ENTITY_TYPE))
215 assertEquals(getEntityResult.getResult().getOwnerNode().getValue(), "member-1");
216 assertEquals(getEntityResult.getResult().getCandidateNodes().get(0).getValue(), "member-1");
218 // we should not be able to retrieve the entity when using string
219 final String entityPathEncoded =
220 "/network-topology:network-topology/topology[topology-id='test']/node[node-id='test://test-node']";
222 getEntityResult = service.getEntity(new GetEntityInputBuilder()
223 .setName(new EntityName(entityPathEncoded))
224 .setType(new EntityType(ENTITY_TYPE))
228 assertNull(getEntityResult.getResult().getOwnerNode());
229 assertTrue(getEntityResult.getResult().getCandidateNodes().isEmpty());
231 final GetEntitiesOutput getEntitiesResult =
232 service.getEntities(new GetEntitiesInputBuilder().build()).get().getResult();
234 assertEquals(getEntitiesResult.getEntities().size(), 1);
235 assertTrue(getEntitiesResult.getEntities().get(new EntitiesKey(
236 new EntityName(CODEC_CONTEXT.fromYangInstanceIdentifier(entityId)), new EntityType(ENTITY_TYPE)))
237 .getCandidateNodes().contains(new NodeName("member-1")));
238 assertTrue(getEntitiesResult.getEntities().get(new EntitiesKey(
239 new EntityName(CODEC_CONTEXT.fromYangInstanceIdentifier(entityId)),
240 new EntityType(ENTITY_TYPE)))
241 .getOwnerNode().getValue().equals("member-1"));
243 final GetEntityOwnerOutput getOwnerResult = service.getEntityOwner(new GetEntityOwnerInputBuilder()
244 .setName(new EntityName(CODEC_CONTEXT.fromYangInstanceIdentifier(entityId)))
245 .setType(new EntityType(ENTITY_TYPE))
249 assertEquals(getOwnerResult.getOwnerNode().getValue(), "member-1");
252 private static void verifyGetOwnershipState(final DOMEntityOwnershipService service, final DOMEntity entity,
253 final EntityOwnershipState expState) {
254 await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
255 final Optional<EntityOwnershipState> state = service.getOwnershipState(entity);
256 assertTrue("getOwnershipState present", state.isPresent());
257 assertEquals("EntityOwnershipState", expState, state.get());
261 private void verifyEntityCandidateRegistered(final String entityType,
262 final YangInstanceIdentifier entityId,
263 final String candidateName) {
264 await().atMost(Duration.ofSeconds(5))
265 .untilAsserted(() -> doVerifyEntityCandidateRegistered(entityType, entityId, candidateName));
268 private void doVerifyEntityCandidateRegistered(final String entityType,
269 final YangInstanceIdentifier entityId,
270 final String candidateName)
271 throws ExecutionException, InterruptedException {
272 final Map<DOMEntity, ORSet<String>> entries = getCandidateData();
273 final DOMEntity entity = new DOMEntity(entityType, entityId);
274 assertTrue(entries.containsKey(entity));
275 assertTrue(entries.get(entity).getElements().contains(candidateName));
278 private void verifyEntityCandidateMissing(final String entityType,
279 final YangInstanceIdentifier entityId,
280 final String candidateName) {
281 await().atMost(Duration.ofSeconds(5))
282 .untilAsserted(() -> doVerifyEntityCandidateMissing(entityType, entityId, candidateName));
285 private void doVerifyEntityCandidateMissing(final String entityType,
286 final YangInstanceIdentifier entityId,
287 final String candidateName)
288 throws ExecutionException, InterruptedException {
289 final Map<DOMEntity, ORSet<String>> entries = getCandidateData();
290 final DOMEntity entity = new DOMEntity(entityType, entityId);
291 assertTrue(entries.containsKey(entity));
292 assertFalse(entries.get(entity).getElements().contains(candidateName));
295 private Map<DOMEntity, ORSet<String>> getCandidateData() throws ExecutionException, InterruptedException {
296 final CompletionStage<Replicator.GetResponse<ORMap<DOMEntity, ORSet<String>>>> ask =
297 AskPattern.ask(replicator, replyTo ->
298 new Replicator.Get<>(
299 CandidateRegistry.KEY,
300 Replicator.readLocal(),
302 Duration.ofSeconds(5),
303 typedSystem.scheduler());
305 final Replicator.GetResponse<ORMap<DOMEntity, ORSet<String>>> response = ask.toCompletableFuture().get();
306 assertTrue(response instanceof Replicator.GetSuccess);
308 final Replicator.GetSuccess<ORMap<DOMEntity, ORSet<String>>> success =
309 (Replicator.GetSuccess<ORMap<DOMEntity, ORSet<String>>>) response;
311 return success.get(CandidateRegistry.KEY).getEntries();
314 private static void verifyEntityOwnershipCandidateRegistration(final DOMEntity entity,
315 final DOMEntityOwnershipCandidateRegistration reg) {
316 assertNotNull("EntityOwnershipCandidateRegistration null", reg);
317 assertEquals("getInstance", entity, reg.getInstance());