Added SDN-IP Leader election mechanism based on the Global Lock service.
NOTE: This is a short-term solution until the Leadership Service is
implemented.
Change-Id: Id1f21c06e67e08efb444ebec4e45eeca360e613d
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/IntentSynchronizer.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/IntentSynchronizer.java
index bf7f141..55a9d24 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/IntentSynchronizer.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/IntentSynchronizer.java
@@ -69,7 +69,7 @@
bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder()
- .setNameFormat("bgp-intents-synchronizer-%d").build());
+ .setNameFormat("sdnip-intents-synchronizer-%d").build());
}
/**
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
index 6b5f2f4..b442b09 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
@@ -114,7 +114,8 @@
HashMultimap.<Ip4Address, RouteEntry>create());
bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
- new ThreadFactoryBuilder().setNameFormat("bgp-updates-%d").build());
+ new ThreadFactoryBuilder()
+ .setNameFormat("sdnip-bgp-updates-%d").build());
}
/**
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
index aa84747..80f8e9e 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
@@ -18,6 +18,8 @@
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -33,8 +35,12 @@
import org.onlab.onos.sdnip.bgp.BgpSession;
import org.onlab.onos.sdnip.bgp.BgpSessionManager;
import org.onlab.onos.sdnip.config.SdnIpConfigReader;
+import org.onlab.onos.store.service.Lock;
+import org.onlab.onos.store.service.LockService;
import org.slf4j.Logger;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
/**
* Component for the SDN-IP peering application.
*/
@@ -43,6 +49,9 @@
public class SdnIp implements SdnIpService {
private static final String SDN_IP_APP = "org.onlab.onos.sdnip";
+ // NOTE: Must be 5s for now
+ private static final int LEASE_DURATION_MS = 5 * 1000;
+ private static final int LEASE_EXTEND_RETRY_MAX = 3;
private final Logger log = getLogger(getClass());
@@ -55,15 +64,23 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LockService lockService;
+
private IntentSynchronizer intentSynchronizer;
private SdnIpConfigReader config;
private PeerConnectivityManager peerConnectivity;
private Router router;
private BgpSessionManager bgpSessionManager;
+ private ExecutorService leaderElectionExecutor;
+ private Lock leaderLock;
+ private volatile boolean isShutdown = true;
+
@Activate
protected void activate() {
- log.debug("SDN-IP started");
+ log.info("SDN-IP started");
+ isShutdown = false;
ApplicationId appId = coreService.registerApplication(SDN_IP_APP);
config = new SdnIpConfigReader();
@@ -83,9 +100,20 @@
interfaceService);
router.start();
+ leaderLock = lockService.create(SDN_IP_APP + "/sdnIpLeaderLock");
+ leaderElectionExecutor = Executors.newSingleThreadExecutor(
+ new ThreadFactoryBuilder()
+ .setNameFormat("sdnip-leader-election-%d").build());
+ leaderElectionExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ doLeaderElectionThread();
+ }
+ });
+
// Manually set the instance as the leader to allow testing
// TODO change this when we get a leader election
- intentSynchronizer.leaderChanged(true);
+ // intentSynchronizer.leaderChanged(true);
bgpSessionManager = new BgpSessionManager(router);
// TODO: the local BGP listen port number should be configurable
@@ -96,11 +124,16 @@
@Deactivate
protected void deactivate() {
+ isShutdown = true;
+
bgpSessionManager.stop();
router.stop();
peerConnectivity.stop();
intentSynchronizer.stop();
+ // Stop the thread(s)
+ leaderElectionExecutor.shutdownNow();
+
log.info("Stopped");
}
@@ -127,4 +160,66 @@
static String dpidToUri(String dpid) {
return "of:" + dpid.replace(":", "");
}
+
+ /**
+ * Performs the leader election.
+ */
+ private void doLeaderElectionThread() {
+
+ //
+ // Try to acquire the lock and keep extending it until the instance
+ // is shutdown.
+ //
+ while (!isShutdown) {
+ log.debug("SDN-IP Leader Election begin");
+
+ // Block until it becomes the leader
+ leaderLock.lock(LEASE_DURATION_MS);
+
+ // This instance is the leader
+ log.info("SDN-IP Leader Elected");
+ intentSynchronizer.leaderChanged(true);
+
+ // Keep extending the expiration until shutdown
+ int extensionFailedCountdown = LEASE_EXTEND_RETRY_MAX - 1;
+
+ //
+ // Keep periodically extending the lock expiration.
+ // If there are multiple back-to-back failures to extend (with
+ // extra sleep time between retrials), then release the lock.
+ //
+ while (!isShutdown) {
+ try {
+ Thread.sleep(LEASE_DURATION_MS / LEASE_EXTEND_RETRY_MAX);
+ if (leaderLock.extendExpiration(LEASE_DURATION_MS)) {
+ log.trace("SDN-IP Leader Extended");
+ extensionFailedCountdown = LEASE_EXTEND_RETRY_MAX;
+ } else {
+ log.debug("SDN-IP Leader Cannot Extend Election");
+ if (!leaderLock.isLocked()) {
+ log.debug("SDN-IP Leader Lock Lost");
+ intentSynchronizer.leaderChanged(false);
+ break; // Try again to get the lock
+ }
+ extensionFailedCountdown--;
+ if (extensionFailedCountdown <= 0) {
+ // Failed too many times to extend.
+ // Release the lock.
+ log.debug("SDN-IP Leader Lock Released");
+ intentSynchronizer.leaderChanged(false);
+ leaderLock.unlock();
+ break; // Try again to get the lock
+ }
+ }
+ } catch (InterruptedException e) {
+ // Thread interrupted. Time to shutdown
+ log.debug("SDN-IP Leader Interrupted");
+ }
+ }
+ }
+
+ // If we reach here, the instance was shutdown
+ intentSynchronizer.leaderChanged(false);
+ leaderLock.unlock();
+ }
}