BUG 2185 : Introduce RaftPolicy & DefaultRaftPolicy
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / MockRaftActorContext.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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
9 package org.opendaylight.controller.cluster.raft;
10
11 import akka.actor.ActorRef;
12 import akka.actor.ActorSelection;
13 import akka.actor.ActorSystem;
14 import akka.actor.Props;
15 import akka.japi.Procedure;
16 import com.google.common.base.Preconditions;
17 import com.google.common.base.Supplier;
18 import com.google.protobuf.GeneratedMessage;
19 import java.io.Serializable;
20 import java.util.HashMap;
21 import java.util.Map;
22 import org.opendaylight.controller.cluster.DataPersistenceProvider;
23 import org.opendaylight.controller.cluster.NonPersistentDataProvider;
24 import org.opendaylight.controller.cluster.raft.policy.DefaultRaftPolicy;
25 import org.opendaylight.controller.cluster.raft.policy.RaftPolicy;
26 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
27 import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages;
28 import org.opendaylight.controller.protobuff.messages.cluster.raft.test.MockPayloadMessages;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 public class MockRaftActorContext implements RaftActorContext {
33
34     private String id;
35     private ActorSystem system;
36     private ActorRef actor;
37     private long index = 0;
38     private long lastApplied = 0;
39     private final ElectionTerm electionTerm;
40     private ReplicatedLog replicatedLog;
41     private Map<String, String> peerAddresses = new HashMap<>();
42     private ConfigParams configParams;
43     private boolean snapshotCaptureInitiated;
44     private SnapshotManager snapshotManager;
45     private DataPersistenceProvider persistenceProvider = new NonPersistentDataProvider();
46     private short payloadVersion;
47     private RaftPolicy raftPolicy = DefaultRaftPolicy.INSTANCE;
48
49     public MockRaftActorContext(){
50         electionTerm = new ElectionTerm() {
51             private long currentTerm = 1;
52             private String votedFor = "";
53
54             @Override
55             public long getCurrentTerm() {
56                 return currentTerm;
57             }
58
59             @Override
60             public String getVotedFor() {
61                 return votedFor;
62             }
63
64             @Override
65             public void update(long currentTerm, String votedFor){
66                 this.currentTerm = currentTerm;
67                 this.votedFor = votedFor;
68
69                 // TODO : Write to some persistent state
70             }
71
72             @Override public void updateAndPersist(long currentTerm,
73                 String votedFor) {
74                 update(currentTerm, votedFor);
75             }
76         };
77
78         configParams = new DefaultConfigParamsImpl();
79     }
80
81     public MockRaftActorContext(String id, ActorSystem system, ActorRef actor){
82         this();
83         this.id = id;
84         this.system = system;
85         this.actor = actor;
86
87         initReplicatedLog();
88     }
89
90
91     public void initReplicatedLog(){
92         this.replicatedLog = new SimpleReplicatedLog();
93         long term = getTermInformation().getCurrentTerm();
94         this.replicatedLog.append(new MockReplicatedLogEntry(term, 0, new MockPayload("1")));
95         this.replicatedLog.append(new MockReplicatedLogEntry(term, 1, new MockPayload("2")));
96     }
97
98     @Override public ActorRef actorOf(Props props) {
99         return system.actorOf(props);
100     }
101
102     @Override public ActorSelection actorSelection(String path) {
103         return system.actorSelection(path);
104     }
105
106     @Override public String getId() {
107         return id;
108     }
109
110     @Override public ActorRef getActor() {
111         return actor;
112     }
113
114     @Override public ElectionTerm getTermInformation() {
115         return electionTerm;
116     }
117
118     public void setIndex(long index){
119         this.index = index;
120     }
121
122     @Override public long getCommitIndex() {
123         return index;
124     }
125
126     @Override public void setCommitIndex(long commitIndex) {
127         this.index = commitIndex;
128     }
129
130     @Override public void setLastApplied(long lastApplied){
131         this.lastApplied = lastApplied;
132     }
133
134     @Override public long getLastApplied() {
135         return lastApplied;
136     }
137
138     @Override
139     // FIXME : A lot of tests try to manipulate the replicated log by setting it using this method
140     // This is OK to do if the underlyingActor is not RafActor or a derived class. If not then you should not
141     // used this way to manipulate the log because the RaftActor actually has a field replicatedLog
142     // which it creates internally and sets on the RaftActorContext
143     // The only right way to manipulate the replicated log therefore is to get it from either the RaftActor
144     // or the RaftActorContext and modify the entries in there instead of trying to replace it by using this setter
145     // Simple assertion that will fail if you do so
146     // ReplicatedLog log = new ReplicatedLogImpl();
147     // raftActor.underlyingActor().getRaftActorContext().setReplicatedLog(log);
148     // assertEquals(log, raftActor.underlyingActor().getReplicatedLog())
149     public void setReplicatedLog(ReplicatedLog replicatedLog) {
150         this.replicatedLog = replicatedLog;
151     }
152
153     @Override public ReplicatedLog getReplicatedLog() {
154         return replicatedLog;
155     }
156
157     @Override public ActorSystem getActorSystem() {
158         return this.system;
159     }
160
161     @Override public Logger getLogger() {
162         return LoggerFactory.getLogger(getClass());
163     }
164
165     @Override public Map<String, String> getPeerAddresses() {
166         return peerAddresses;
167     }
168
169     @Override public String getPeerAddress(String peerId) {
170         return peerAddresses.get(peerId);
171     }
172
173     @Override public void addToPeers(String name, String address) {
174         peerAddresses.put(name, address);
175     }
176
177     @Override public void removePeer(String name) {
178         peerAddresses.remove(name);
179     }
180
181     @Override public ActorSelection getPeerActorSelection(String peerId) {
182         String peerAddress = getPeerAddress(peerId);
183         if(peerAddress != null){
184             return actorSelection(peerAddress);
185         }
186         return null;
187     }
188
189     @Override public void setPeerAddress(String peerId, String peerAddress) {
190         Preconditions.checkState(peerAddresses.containsKey(peerId));
191         peerAddresses.put(peerId, peerAddress);
192     }
193
194     public void setPeerAddresses(Map<String, String> peerAddresses) {
195         this.peerAddresses = peerAddresses;
196     }
197
198     @Override
199     public ConfigParams getConfigParams() {
200         return configParams;
201     }
202
203     @Override
204     public SnapshotManager getSnapshotManager() {
205         if(this.snapshotManager == null){
206             this.snapshotManager = new SnapshotManager(this, getLogger());
207             this.snapshotManager.setCreateSnapshotCallable(NoopProcedure.<Void>instance());
208         }
209         return this.snapshotManager;
210     }
211
212     public void setConfigParams(ConfigParams configParams) {
213         this.configParams = configParams;
214     }
215
216     @Override
217     public long getTotalMemory() {
218         return Runtime.getRuntime().totalMemory();
219     }
220
221     @Override
222     public void setTotalMemoryRetriever(Supplier<Long> retriever) {
223     }
224
225     @Override
226     public boolean hasFollowers() {
227         return getPeerAddresses().keySet().size() > 0;
228     }
229
230     @Override
231     public DataPersistenceProvider getPersistenceProvider() {
232         return persistenceProvider;
233     }
234
235     public void setPersistenceProvider(DataPersistenceProvider persistenceProvider) {
236         this.persistenceProvider = persistenceProvider;
237     }
238
239     @Override
240     public short getPayloadVersion() {
241         return payloadVersion;
242     }
243
244     @Override
245     public RaftPolicy getRaftPolicy() {
246         return this.raftPolicy;
247     }
248
249     public void setRaftPolicy(RaftPolicy raftPolicy){
250         this.raftPolicy = raftPolicy;
251     }
252
253     public void setPayloadVersion(short payloadVersion) {
254         this.payloadVersion = payloadVersion;
255     }
256
257     public static class SimpleReplicatedLog extends AbstractReplicatedLogImpl {
258         @Override
259         public void appendAndPersist(
260             ReplicatedLogEntry replicatedLogEntry) {
261             append(replicatedLogEntry);
262         }
263
264         @Override
265         public int dataSize() {
266             return -1;
267         }
268
269         @Override
270         public void captureSnapshotIfReady(ReplicatedLogEntry replicatedLogEntry) {
271         }
272
273         @Override public void removeFromAndPersist(long index) {
274             removeFrom(index);
275         }
276
277         @Override
278         public void appendAndPersist(ReplicatedLogEntry replicatedLogEntry, Procedure<ReplicatedLogEntry> callback) {
279             append(replicatedLogEntry);
280
281             if(callback != null) {
282                 try {
283                     callback.apply(replicatedLogEntry);
284                 } catch (Exception e) {
285                     e.printStackTrace();
286                 }
287             }
288         }
289     }
290
291     public static class MockPayload extends Payload implements Serializable {
292         private static final long serialVersionUID = 3121380393130864247L;
293         private String value = "";
294         private int size;
295
296         public MockPayload() {
297         }
298
299         public MockPayload(String s) {
300             this.value = s;
301             size = value.length();
302         }
303
304         public MockPayload(String s, int size) {
305             this(s);
306             this.size = size;
307         }
308
309         @Override public Map<GeneratedMessage.GeneratedExtension<?, ?>, String> encode() {
310             Map<GeneratedMessage.GeneratedExtension<?, ?>, String> map = new HashMap<>();
311             map.put(MockPayloadMessages.value, value);
312             return map;
313         }
314
315         @Override public Payload decode(
316             AppendEntriesMessages.AppendEntries.ReplicatedLogEntry.Payload payloadProtoBuff) {
317             String value = payloadProtoBuff.getExtension(MockPayloadMessages.value);
318             this.value = value;
319             return this;
320         }
321
322         @Override
323         public int size() {
324             return size;
325         }
326
327         @Override public String getClientPayloadClassName() {
328             return MockPayload.class.getName();
329         }
330
331         @Override
332         public String toString() {
333             return value;
334         }
335
336         @Override
337         public int hashCode() {
338             final int prime = 31;
339             int result = 1;
340             result = prime * result + ((value == null) ? 0 : value.hashCode());
341             return result;
342         }
343
344         @Override
345         public boolean equals(Object obj) {
346             if (this == obj) {
347                 return true;
348             }
349             if (obj == null) {
350                 return false;
351             }
352             if (getClass() != obj.getClass()) {
353                 return false;
354             }
355             MockPayload other = (MockPayload) obj;
356             if (value == null) {
357                 if (other.value != null) {
358                     return false;
359                 }
360             } else if (!value.equals(other.value)) {
361                 return false;
362             }
363             return true;
364         }
365     }
366
367     public static class MockReplicatedLogEntry implements ReplicatedLogEntry, Serializable {
368         private static final long serialVersionUID = 1L;
369
370         private final long term;
371         private final long index;
372         private final Payload data;
373
374         public MockReplicatedLogEntry(long term, long index, Payload data){
375
376             this.term = term;
377             this.index = index;
378             this.data = data;
379         }
380
381         @Override public Payload getData() {
382             return data;
383         }
384
385         @Override public long getTerm() {
386             return term;
387         }
388
389         @Override public long getIndex() {
390             return index;
391         }
392
393         @Override
394         public int size() {
395             return getData().size();
396         }
397
398         @Override
399         public int hashCode() {
400             final int prime = 31;
401             int result = 1;
402             result = prime * result + ((data == null) ? 0 : data.hashCode());
403             result = prime * result + (int) (index ^ (index >>> 32));
404             result = prime * result + (int) (term ^ (term >>> 32));
405             return result;
406         }
407
408         @Override
409         public boolean equals(Object obj) {
410             if (this == obj) {
411                 return true;
412             }
413             if (obj == null) {
414                 return false;
415             }
416             if (getClass() != obj.getClass()) {
417                 return false;
418             }
419             MockReplicatedLogEntry other = (MockReplicatedLogEntry) obj;
420             if (data == null) {
421                 if (other.data != null) {
422                     return false;
423                 }
424             } else if (!data.equals(other.data)) {
425                 return false;
426             }
427             if (index != other.index) {
428                 return false;
429             }
430             if (term != other.term) {
431                 return false;
432             }
433             return true;
434         }
435
436         @Override
437         public String toString() {
438             StringBuilder builder = new StringBuilder();
439             builder.append("MockReplicatedLogEntry [term=").append(term).append(", index=").append(index)
440                     .append(", data=").append(data).append("]");
441             return builder.toString();
442         }
443     }
444
445     public static class MockReplicatedLogBuilder {
446         private final ReplicatedLog mockLog = new SimpleReplicatedLog();
447
448         public  MockReplicatedLogBuilder createEntries(int start, int end, int term) {
449             for (int i=start; i<end; i++) {
450                 this.mockLog.append(new ReplicatedLogImplEntry(i, term, new MockRaftActorContext.MockPayload(Integer.toString(i))));
451             }
452             return this;
453         }
454
455         public  MockReplicatedLogBuilder addEntry(int index, int term, MockPayload payload) {
456             this.mockLog.append(new ReplicatedLogImplEntry(index, term, payload));
457             return this;
458         }
459
460         public ReplicatedLog build() {
461             return this.mockLog;
462         }
463     }
464 }