cf456fec75ea65cbcd31052d6d147cad6d57ec79
[controller.git] / opendaylight / md-sal / eos-dom-akka / src / test / java / org / opendaylight / controller / eos / akka / AkkaEntityOwnershipServiceTest.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.eos.akka;
9
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.assertTrue;
15 import static org.junit.Assert.fail;
16
17 import akka.actor.ActorSystem;
18 import akka.actor.testkit.typed.javadsl.ActorTestKit;
19 import akka.actor.typed.ActorRef;
20 import akka.actor.typed.javadsl.Adapter;
21 import akka.actor.typed.javadsl.AskPattern;
22 import akka.cluster.ddata.ORMap;
23 import akka.cluster.ddata.ORSet;
24 import akka.cluster.ddata.typed.javadsl.DistributedData;
25 import akka.cluster.ddata.typed.javadsl.Replicator;
26 import com.typesafe.config.ConfigFactory;
27 import java.time.Duration;
28 import java.util.Map;
29 import java.util.Optional;
30 import java.util.concurrent.CompletionStage;
31 import java.util.concurrent.ExecutionException;
32 import org.awaitility.Durations;
33 import org.junit.After;
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.opendaylight.controller.eos.akka.bootstrap.command.RunningContext;
37 import org.opendaylight.controller.eos.akka.owner.supervisor.command.OwnerSupervisorCommand;
38 import org.opendaylight.controller.eos.akka.registry.candidate.CandidateRegistry;
39 import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
40 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
41 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
42 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration;
43 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration;
44 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService;
45 import org.opendaylight.yangtools.yang.common.QName;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
47
48 public class AkkaEntityOwnershipServiceTest extends AbstractNativeEosTest {
49     static final String ENTITY_TYPE = "test";
50     static final String ENTITY_TYPE2 = "test2";
51     static final QName QNAME = QName.create("test", "2015-08-11", "foo");
52     static int ID_COUNTER = 1;
53
54     private ActorSystem system;
55     private akka.actor.typed.ActorSystem<Void> typedSystem;
56     private AkkaEntityOwnershipService service;
57     private ActorRef<Replicator.Command> replicator;
58
59     @Before
60     public void setUp() throws Exception {
61         system = ActorSystem.create("ClusterSystem", ConfigFactory.load());
62         typedSystem = Adapter.toTyped(this.system);
63         replicator = DistributedData.get(typedSystem).replicator();
64
65         service = new AkkaEntityOwnershipService(system);
66     }
67
68     @After
69     public void tearDown() throws InterruptedException, ExecutionException {
70         service.close();
71         ActorTestKit.shutdown(Adapter.toTyped(system), Duration.ofSeconds(20));
72     }
73
74     @Test
75     public void testRegisterCandidate() throws Exception {
76         final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME);
77         final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
78
79         final DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(entity);
80
81         verifyEntityOwnershipCandidateRegistration(entity, reg);
82         verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
83
84         try {
85             service.registerCandidate(entity);
86             fail("Expected CandidateAlreadyRegisteredException");
87         } catch (final CandidateAlreadyRegisteredException e) {
88             // expected
89             assertEquals("getEntity", entity, e.getEntity());
90         }
91
92         final DOMEntity entity2 = new DOMEntity(ENTITY_TYPE2, entityId);
93         final DOMEntityOwnershipCandidateRegistration reg2 = service.registerCandidate(entity2);
94
95         verifyEntityOwnershipCandidateRegistration(entity2, reg2);
96         verifyEntityCandidateRegistered(ENTITY_TYPE2, entityId, "member-1");
97     }
98
99     @Test
100     public void testUnregisterCandidate() throws Exception {
101         final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME);
102         final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
103
104         final DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(entity);
105
106         verifyEntityOwnershipCandidateRegistration(entity, reg);
107         verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
108
109         reg.close();
110         verifyEntityCandidateMissing(ENTITY_TYPE, entityId, "member-1");
111
112         service.registerCandidate(entity);
113         verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
114     }
115
116     @Test
117     public void testListenerRegistration() throws Exception {
118
119         final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME);
120         final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
121         final MockEntityOwnershipListener listener = new MockEntityOwnershipListener("member-1");
122
123         final DOMEntityOwnershipListenerRegistration reg = service.registerListener(entity.getType(), listener);
124
125         assertNotNull("EntityOwnershipListenerRegistration null", reg);
126         assertEquals("getEntityType", entity.getType(), reg.getEntityType());
127         assertEquals("getInstance", listener, reg.getInstance());
128
129         final DOMEntityOwnershipCandidateRegistration candidate = service.registerCandidate(entity);
130
131         verifyListenerState(listener, entity, true, true, false);
132         final int changes = listener.getChanges().size();
133
134         reg.close();
135         candidate.close();
136
137         verifyEntityCandidateMissing(ENTITY_TYPE, entityId, "member-1");
138
139         service.registerCandidate(entity);
140         // check listener not called when listener registration closed
141         await().pollDelay(Durations.TWO_SECONDS).until(() -> listener.getChanges().size() == changes);
142     }
143
144     @Test
145     public void testGetOwnershipState() throws Exception {
146         final DOMEntity entity = new DOMEntity(ENTITY_TYPE, "one");
147
148         final DOMEntityOwnershipCandidateRegistration registration = service.registerCandidate(entity);
149         verifyGetOwnershipState(service, entity, EntityOwnershipState.IS_OWNER);
150
151         final RunningContext runningContext = service.getRunningContext();
152         registerCandidates(runningContext.getCandidateRegistry(), entity, "member-2");
153
154         final ActorRef<OwnerSupervisorCommand> ownerSupervisor = runningContext.getOwnerSupervisor();
155         reachableMember(ownerSupervisor, "member-2", DEFAULT_DATACENTER);
156         unreachableMember(ownerSupervisor, "member-1", DEFAULT_DATACENTER);
157         verifyGetOwnershipState(service, entity, EntityOwnershipState.OWNED_BY_OTHER);
158
159         final DOMEntity entity2 = new DOMEntity(ENTITY_TYPE, "two");
160         final Optional<EntityOwnershipState> state = service.getOwnershipState(entity2);
161         assertFalse(state.isPresent());
162
163         unreachableMember(ownerSupervisor, "member-2", DEFAULT_DATACENTER);
164         verifyGetOwnershipState(service, entity, EntityOwnershipState.NO_OWNER);
165     }
166
167     @Test
168     public void testIsCandidateRegistered() throws Exception {
169         final DOMEntity test = new DOMEntity("test-type", "test");
170
171         assertFalse(service.isCandidateRegistered(test));
172
173         service.registerCandidate(test);
174
175         assertTrue(service.isCandidateRegistered(test));
176     }
177
178     private static void verifyGetOwnershipState(final DOMEntityOwnershipService service, final DOMEntity entity,
179                                                 final EntityOwnershipState expState) {
180         await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
181             final Optional<EntityOwnershipState> state = service.getOwnershipState(entity);
182             assertTrue("getOwnershipState present", state.isPresent());
183             assertEquals("EntityOwnershipState", expState, state.get());
184         });
185     }
186
187     private void verifyEntityCandidateRegistered(final String entityType,
188                                                  final YangInstanceIdentifier entityId,
189                                                  final String candidateName) {
190         await().atMost(Duration.ofSeconds(5))
191                 .untilAsserted(() -> doVerifyEntityCandidateRegistered(entityType, entityId, candidateName));
192     }
193
194     private void doVerifyEntityCandidateRegistered(final String entityType,
195                                                    final YangInstanceIdentifier entityId,
196                                                    final String candidateName)
197             throws ExecutionException, InterruptedException {
198         final Map<DOMEntity, ORSet<String>> entries = getCandidateData();
199         final DOMEntity entity = new DOMEntity(entityType, entityId);
200         assertTrue(entries.containsKey(entity));
201         assertTrue(entries.get(entity).getElements().contains(candidateName));
202     }
203
204     private void verifyEntityCandidateMissing(final String entityType,
205                                               final YangInstanceIdentifier entityId,
206                                               final String candidateName) {
207         await().atMost(Duration.ofSeconds(5))
208                 .untilAsserted(() -> doVerifyEntityCandidateMissing(entityType, entityId, candidateName));
209     }
210
211     private void doVerifyEntityCandidateMissing(final String entityType,
212                                                 final YangInstanceIdentifier entityId,
213                                                 final String candidateName)
214             throws ExecutionException, InterruptedException {
215         final Map<DOMEntity, ORSet<String>> entries = getCandidateData();
216         final DOMEntity entity = new DOMEntity(entityType, entityId);
217         assertTrue(entries.containsKey(entity));
218         assertFalse(entries.get(entity).getElements().contains(candidateName));
219     }
220
221     private Map<DOMEntity, ORSet<String>> getCandidateData() throws ExecutionException, InterruptedException {
222         final CompletionStage<Replicator.GetResponse<ORMap<DOMEntity, ORSet<String>>>> ask =
223                 AskPattern.ask(replicator, replyTo ->
224                                 new Replicator.Get<>(
225                                         CandidateRegistry.KEY,
226                                         Replicator.readLocal(),
227                                         replyTo),
228                         Duration.ofSeconds(5),
229                         typedSystem.scheduler());
230
231         final Replicator.GetResponse<ORMap<DOMEntity, ORSet<String>>> response = ask.toCompletableFuture().get();
232         assertTrue(response instanceof Replicator.GetSuccess);
233
234         final Replicator.GetSuccess<ORMap<DOMEntity, ORSet<String>>> success =
235                 (Replicator.GetSuccess<ORMap<DOMEntity, ORSet<String>>>) response;
236
237         return success.get(CandidateRegistry.KEY).getEntries();
238     }
239
240     private static void verifyEntityOwnershipCandidateRegistration(final DOMEntity entity,
241                                                                    final DOMEntityOwnershipCandidateRegistration reg) {
242         assertNotNull("EntityOwnershipCandidateRegistration null", reg);
243         assertEquals("getInstance", entity, reg.getInstance());
244     }
245 }