Improve segmented journal actor metrics
[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.assertNull;
15 import static org.junit.Assert.assertTrue;
16 import static org.junit.Assert.fail;
17
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;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Optional;
32 import java.util.concurrent.CompletionStage;
33 import java.util.concurrent.ExecutionException;
34 import org.awaitility.Durations;
35 import org.junit.After;
36 import org.junit.Before;
37 import org.junit.Test;
38 import org.opendaylight.controller.eos.akka.bootstrap.command.RunningContext;
39 import org.opendaylight.controller.eos.akka.owner.supervisor.command.OwnerSupervisorCommand;
40 import org.opendaylight.controller.eos.akka.registry.candidate.CandidateRegistry;
41 import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
42 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
43 import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
44 import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.EntityName;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.EntityType;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntitiesInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntityInputBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.GetEntityOwnerInputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.NodeName;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.entity.owners.norev.get.entities.output.EntitiesKey;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
55 import org.opendaylight.yangtools.concepts.Registration;
56 import org.opendaylight.yangtools.yang.common.QName;
57 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
58 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
59 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
60
61 public class AkkaEntityOwnershipServiceTest extends AbstractNativeEosTest {
62     static final String ENTITY_TYPE = "test";
63     static final String ENTITY_TYPE2 = "test2";
64     static final QName QNAME = QName.create("test", "2015-08-11", "foo");
65
66     private ActorSystem system;
67     private akka.actor.typed.ActorSystem<Void> typedSystem;
68     private AkkaEntityOwnershipService service;
69     private ActorRef<Replicator.Command> replicator;
70
71     @Before
72     public void setUp() throws Exception {
73         system = ActorSystem.create("ClusterSystem", ConfigFactory.load());
74         typedSystem = Adapter.toTyped(system);
75         replicator = DistributedData.get(typedSystem).replicator();
76
77         service = new AkkaEntityOwnershipService(system, CODEC_CONTEXT);
78     }
79
80     @After
81     public void tearDown() throws InterruptedException, ExecutionException {
82         service.close();
83         ActorTestKit.shutdown(Adapter.toTyped(system), Duration.ofSeconds(20));
84     }
85
86     @Test
87     public void testRegisterCandidate() throws Exception {
88         final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME);
89         final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
90
91         final Registration reg = service.registerCandidate(entity);
92         assertNotNull(reg);
93
94         verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
95
96         try {
97             service.registerCandidate(entity);
98             fail("Expected CandidateAlreadyRegisteredException");
99         } catch (final CandidateAlreadyRegisteredException e) {
100             // expected
101             assertEquals("getEntity", entity, e.getEntity());
102         }
103
104         final DOMEntity entity2 = new DOMEntity(ENTITY_TYPE2, entityId);
105         final Registration reg2 = service.registerCandidate(entity2);
106
107         assertNotNull(reg2);
108         verifyEntityCandidateRegistered(ENTITY_TYPE2, entityId, "member-1");
109     }
110
111     @Test
112     public void testUnregisterCandidate() throws Exception {
113         final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME);
114         final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
115
116         final Registration reg = service.registerCandidate(entity);
117         assertNotNull(reg);
118
119         verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
120
121         reg.close();
122         verifyEntityCandidateMissing(ENTITY_TYPE, entityId, "member-1");
123
124         service.registerCandidate(entity);
125         verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
126     }
127
128     @Test
129     public void testListenerRegistration() throws Exception {
130
131         final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(QNAME);
132         final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
133         final MockEntityOwnershipListener listener = new MockEntityOwnershipListener("member-1");
134
135         final Registration reg = service.registerListener(entity.getType(), listener);
136
137         assertNotNull("EntityOwnershipListenerRegistration null", reg);
138
139         final Registration candidate = service.registerCandidate(entity);
140
141         verifyListenerState(listener, entity, true, true, false);
142         final int changes = listener.getChanges().size();
143
144         reg.close();
145         candidate.close();
146
147         verifyEntityCandidateMissing(ENTITY_TYPE, entityId, "member-1");
148
149         service.registerCandidate(entity);
150         // check listener not called when listener registration closed
151         await().pollDelay(Durations.TWO_SECONDS).until(() -> listener.getChanges().size() == changes);
152     }
153
154     @Test
155     public void testGetOwnershipState() throws Exception {
156         final DOMEntity entity = new DOMEntity(ENTITY_TYPE, "one");
157
158         final Registration registration = service.registerCandidate(entity);
159         verifyGetOwnershipState(service, entity, EntityOwnershipState.IS_OWNER);
160
161         final RunningContext runningContext = service.getRunningContext();
162         registerCandidates(runningContext.getCandidateRegistry(), entity, "member-2");
163
164         final ActorRef<OwnerSupervisorCommand> ownerSupervisor = runningContext.getOwnerSupervisor();
165         reachableMember(ownerSupervisor, "member-2", DEFAULT_DATACENTER);
166         unreachableMember(ownerSupervisor, "member-1", DEFAULT_DATACENTER);
167         verifyGetOwnershipState(service, entity, EntityOwnershipState.OWNED_BY_OTHER);
168
169         final DOMEntity entity2 = new DOMEntity(ENTITY_TYPE, "two");
170         final Optional<EntityOwnershipState> state = service.getOwnershipState(entity2);
171         assertFalse(state.isPresent());
172
173         unreachableMember(ownerSupervisor, "member-2", DEFAULT_DATACENTER);
174         verifyGetOwnershipState(service, entity, EntityOwnershipState.NO_OWNER);
175     }
176
177     @Test
178     public void testIsCandidateRegistered() throws Exception {
179         final DOMEntity test = new DOMEntity("test-type", "test");
180
181         assertFalse(service.isCandidateRegistered(test));
182
183         service.registerCandidate(test);
184
185         assertTrue(service.isCandidateRegistered(test));
186     }
187
188     @Test
189     public void testEntityRetrievalWithYiid() throws Exception {
190         final YangInstanceIdentifier entityId = YangInstanceIdentifier.of(new NodeIdentifier(NetworkTopology.QNAME),
191                 new NodeIdentifier(Topology.QNAME),
192                 NodeIdentifierWithPredicates.of(Topology.QNAME, QName.create(Topology.QNAME, "topology-id"), "test"),
193                 new NodeIdentifier(Node.QNAME),
194                 NodeIdentifierWithPredicates.of(Node.QNAME, QName.create(Node.QNAME, "node-id"), "test://test-node"));
195
196         final DOMEntity entity = new DOMEntity(ENTITY_TYPE, entityId);
197
198         final Registration reg = service.registerCandidate(entity);
199
200         assertNotNull(reg);
201         verifyEntityCandidateRegistered(ENTITY_TYPE, entityId, "member-1");
202
203         var result = service.getEntity(new GetEntityInputBuilder()
204             .setName(new EntityName(CODEC_CONTEXT.fromYangInstanceIdentifier(entityId)))
205             .setType(new EntityType(ENTITY_TYPE))
206             .build())
207             .get()
208             .getResult();
209
210         assertEquals(result.getOwnerNode().getValue(), "member-1");
211         assertEquals(result.getCandidateNodes().get(0).getValue(), "member-1");
212
213         // we should not be able to retrieve the entity when using string
214         final String entityPathEncoded =
215                 "/network-topology:network-topology/topology[topology-id='test']/node[node-id='test://test-node']";
216
217         result = service.getEntity(new GetEntityInputBuilder()
218             .setName(new EntityName(entityPathEncoded))
219             .setType(new EntityType(ENTITY_TYPE))
220             .build())
221             .get()
222             .getResult();
223
224         assertNull(result.getOwnerNode());
225         assertEquals(List.of(), result.getCandidateNodes());
226
227         final var getEntitiesResult = service.getEntities(new GetEntitiesInputBuilder().build()).get().getResult();
228         final var entities = getEntitiesResult.nonnullEntities();
229         assertEquals(1, entities.size());
230         assertTrue(entities.get(new EntitiesKey(new EntityName(CODEC_CONTEXT.fromYangInstanceIdentifier(entityId)),
231             new EntityType(ENTITY_TYPE))).getCandidateNodes().contains(new NodeName("member-1")));
232         assertTrue(entities.get(new EntitiesKey(
233                         new EntityName(CODEC_CONTEXT.fromYangInstanceIdentifier(entityId)),
234                         new EntityType(ENTITY_TYPE)))
235                 .getOwnerNode().getValue().equals("member-1"));
236
237         final var getOwnerResult = service.getEntityOwner(new GetEntityOwnerInputBuilder()
238             .setName(new EntityName(CODEC_CONTEXT.fromYangInstanceIdentifier(entityId)))
239             .setType(new EntityType(ENTITY_TYPE))
240             .build()).get().getResult();
241
242         assertEquals(getOwnerResult.getOwnerNode().getValue(), "member-1");
243     }
244
245     private static void verifyGetOwnershipState(final DOMEntityOwnershipService service, final DOMEntity entity,
246                                                 final EntityOwnershipState expState) {
247         await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
248             assertEquals(Optional.of(expState), service.getOwnershipState(entity));
249         });
250     }
251
252     private void verifyEntityCandidateRegistered(final String entityType,
253                                                  final YangInstanceIdentifier entityId,
254                                                  final String candidateName) {
255         await().atMost(Duration.ofSeconds(5))
256                 .untilAsserted(() -> doVerifyEntityCandidateRegistered(entityType, entityId, candidateName));
257     }
258
259     private void doVerifyEntityCandidateRegistered(final String entityType,
260                                                    final YangInstanceIdentifier entityId,
261                                                    final String candidateName)
262             throws ExecutionException, InterruptedException {
263         final Map<DOMEntity, ORSet<String>> entries = getCandidateData();
264         final DOMEntity entity = new DOMEntity(entityType, entityId);
265         assertTrue(entries.containsKey(entity));
266         assertTrue(entries.get(entity).getElements().contains(candidateName));
267     }
268
269     private void verifyEntityCandidateMissing(final String entityType,
270                                               final YangInstanceIdentifier entityId,
271                                               final String candidateName) {
272         await().atMost(Duration.ofSeconds(5))
273                 .untilAsserted(() -> doVerifyEntityCandidateMissing(entityType, entityId, candidateName));
274     }
275
276     private void doVerifyEntityCandidateMissing(final String entityType,
277                                                 final YangInstanceIdentifier entityId,
278                                                 final String candidateName)
279             throws ExecutionException, InterruptedException {
280         final Map<DOMEntity, ORSet<String>> entries = getCandidateData();
281         final DOMEntity entity = new DOMEntity(entityType, entityId);
282         assertTrue(entries.containsKey(entity));
283         assertFalse(entries.get(entity).getElements().contains(candidateName));
284     }
285
286     private Map<DOMEntity, ORSet<String>> getCandidateData() throws ExecutionException, InterruptedException {
287         final CompletionStage<Replicator.GetResponse<ORMap<DOMEntity, ORSet<String>>>> ask =
288                 AskPattern.ask(replicator, replyTo ->
289                                 new Replicator.Get<>(
290                                         CandidateRegistry.KEY,
291                                         Replicator.readLocal(),
292                                         replyTo),
293                         Duration.ofSeconds(5),
294                         typedSystem.scheduler());
295
296         final Replicator.GetResponse<ORMap<DOMEntity, ORSet<String>>> response = ask.toCompletableFuture().get();
297         assertTrue(response instanceof Replicator.GetSuccess);
298
299         final Replicator.GetSuccess<ORMap<DOMEntity, ORSet<String>>> success =
300                 (Replicator.GetSuccess<ORMap<DOMEntity, ORSet<String>>>) response;
301
302         return success.get(CandidateRegistry.KEY).getEntries();
303     }
304 }