Add PeerAddressResolver for raft actor 88/27588/3
authorTom Pantelis <tpanteli@brocade.com>
Sun, 27 Sep 2015 21:09:14 +0000 (17:09 -0400)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 5 Oct 2015 13:03:22 +0000 (13:03 +0000)
For upcoming work to dynamically add peers, the peer address may not be
known and, if the cluster MemberUp has already occurred, no
UpdatePeerAddress message will be sent. We need to be bale to
dynamically resolve peer addresses so I added a new PeerAddressResolver
interface whose instance is obtained from the ConfigParams and used by
the RaftActorContextImpl..

Change-Id: I38807b4b6a59a7cb1359d85a9550cd6e98cb13a4
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ConfigParams.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/DefaultConfigParamsImpl.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/NoopPeerAddressResolver.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/PeerAddressResolver.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorContextImpl.java
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorContextImplTest.java [new file with mode: 0644]

index a2d30f7ae6d048495fde8cca2bddd6093c54de2e..3645f615e68cc3f8e8a4958f12a7ce1543f636fd 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.cluster.raft;
 
+import javax.annotation.Nonnull;
 import org.opendaylight.controller.cluster.raft.policy.RaftPolicy;
 import scala.concurrent.duration.FiniteDuration;
 
@@ -92,4 +93,9 @@ public interface ConfigParams {
      * DefaultRaftPolicy
      */
     RaftPolicy getRaftPolicy();
+
+    /**
+     * Returns the PeerAddressResolver.
+     */
+    @Nonnull PeerAddressResolver getPeerAddressResolver();
 }
index c8cc88c3edd45256011f050a5a4c83b749b37730..c4df54778763f0f1bbb39aa0702a7da367c62007 100644 (file)
@@ -7,10 +7,12 @@
  */
 package org.opendaylight.controller.cluster.raft;
 
+import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
 import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
 import org.opendaylight.controller.cluster.raft.policy.DefaultRaftPolicy;
 import org.opendaylight.controller.cluster.raft.policy.RaftPolicy;
 import org.slf4j.Logger;
@@ -63,7 +65,9 @@ public class DefaultConfigParamsImpl implements ConfigParams {
     private long electionTimeoutFactor = 2;
     private String customRaftPolicyImplementationClass;
 
-    Supplier<RaftPolicy> policySupplier = Suppliers.memoize(new PolicySupplier());
+    private final Supplier<RaftPolicy> policySupplier = Suppliers.memoize(new PolicySupplier());
+
+    private PeerAddressResolver peerAddressResolver = NoopPeerAddressResolver.INSTANCE;
 
     public void setHeartBeatInterval(FiniteDuration heartBeatInterval) {
         this.heartBeatInterval = heartBeatInterval;
@@ -177,4 +181,13 @@ public class DefaultConfigParamsImpl implements ConfigParams {
             return DefaultRaftPolicy.INSTANCE;
         }
     }
+
+    @Override
+    public PeerAddressResolver getPeerAddressResolver() {
+        return peerAddressResolver;
+    }
+
+    public void setPeerAddressResolver(@Nonnull PeerAddressResolver peerAddressResolver) {
+        this.peerAddressResolver = Preconditions.checkNotNull(peerAddressResolver);
+    }
 }
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/NoopPeerAddressResolver.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/NoopPeerAddressResolver.java
new file mode 100644 (file)
index 0000000..162521a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.raft;
+
+/**
+ * Implementation of PeerAddressResolver that does nothing.
+ *
+ * @author Thomas Pantelis
+ */
+public final class NoopPeerAddressResolver implements PeerAddressResolver {
+    public static NoopPeerAddressResolver INSTANCE = new NoopPeerAddressResolver();
+
+    private NoopPeerAddressResolver() {
+    }
+
+    @Override
+    public String resolve(String peerId) {
+        return null;
+    }
+}
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/PeerAddressResolver.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/PeerAddressResolver.java
new file mode 100644 (file)
index 0000000..13bbda6
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.raft;
+
+import javax.annotation.Nullable;
+
+/**
+ * Interface to resolve raft actor peer addresses.
+ *
+ * @author Thomas Pantelis
+ */
+public interface PeerAddressResolver {
+    /**
+     * Resolves a raft actor peer id to it's remote actor address.
+     *
+     * @param peerId the id of the peer to resolve
+     * @return the peer's actor path string or null if not found
+     */
+    @Nullable String resolve(String peerId);
+}
index f14ee7dfa8fc6917e58d3a51312b6d63ba280d7d..82bfb128f7c51b29a7c81047a2bfc34cb55a2ab1 100644 (file)
@@ -146,7 +146,13 @@ public class RaftActorContextImpl implements RaftActorContext {
     }
 
     @Override public String getPeerAddress(String peerId) {
-        return peerAddresses.get(peerId);
+        String peerAddress = peerAddresses.get(peerId);
+        if(peerAddress == null) {
+            peerAddress = configParams.getPeerAddressResolver().resolve(peerId);
+            peerAddresses.put(peerId, peerAddress);
+        }
+
+        return peerAddress;
     }
 
     @Override public ConfigParams getConfigParams() {
diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorContextImplTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorContextImplTest.java
new file mode 100644 (file)
index 0000000..26fdf8f
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.raft;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+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 akka.actor.Props;
+import akka.testkit.TestActorRef;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.junit.After;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.NonPersistentDataProvider;
+import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Unit tests for RaftActorContextImpl.
+ *
+ * @author Thomas Pantelis
+ */
+public class RaftActorContextImplTest extends AbstractActorTest {
+    private final TestActorFactory actorFactory = new TestActorFactory(getSystem());
+
+    private final TestActorRef<DoNothingActor> actor = actorFactory.createTestActor(
+            Props.create(DoNothingActor.class), actorFactory.generateActorId("actor"));
+
+    private final Logger log = LoggerFactory.getLogger(RaftActorContextImplTest.class);
+
+    @After
+    public void tearDown() {
+        actorFactory.close();
+    }
+
+    @Test
+    public void testGetPeerAddress() {
+        DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
+        RaftActorContextImpl context = new RaftActorContextImpl(actor, actor.underlyingActor().getContext(),
+                "test", new ElectionTermImpl(new NonPersistentDataProvider(), "test", log), -1, -1,
+                Maps.newHashMap(ImmutableMap.<String, String>of("peer1", "peerAddress1")), configParams,
+                new NonPersistentDataProvider(), log);
+
+        assertEquals("getPeerAddress", "peerAddress1", context.getPeerAddress("peer1"));
+        assertEquals("getPeerAddress", null, context.getPeerAddress("peer2"));
+
+        PeerAddressResolver mockResolver = mock(PeerAddressResolver.class);
+        doReturn("peerAddress2").when(mockResolver).resolve("peer2");
+        configParams.setPeerAddressResolver(mockResolver);
+
+        assertEquals("getPeerAddress", "peerAddress2", context.getPeerAddress("peer2"));
+
+        reset(mockResolver);
+        assertEquals("getPeerAddress", "peerAddress2", context.getPeerAddress("peer2"));
+        assertEquals("getPeerAddress", "peerAddress1", context.getPeerAddress("peer1"));
+        verify(mockResolver, never()).resolve(anyString());
+    }
+}