[ONOS-8085] Route Refresh timers in Controller
- Add timers in BgpController
- Add triggers in BgpLocalRib
- Code to send Route Refresh messages
Change-Id: Id45f1bcb3325ccc21c0d0d2e673c2b0097aac552
diff --git a/protocols/bgp/api/src/main/java/org/onosproject/bgp/controller/BgpController.java b/protocols/bgp/api/src/main/java/org/onosproject/bgp/controller/BgpController.java
index 4620d18..8030a57 100644
--- a/protocols/bgp/api/src/main/java/org/onosproject/bgp/controller/BgpController.java
+++ b/protocols/bgp/api/src/main/java/org/onosproject/bgp/controller/BgpController.java
@@ -221,4 +221,9 @@
*/
Set<BgpRouteListener> routeListener();
+ /**
+ * Helper function to notify the controller if the topology has changed.
+ * Controller will decide if route-refresh needs to be triggered
+ */
+ void notifyTopologyChange();
}
diff --git a/protocols/bgp/api/src/main/java/org/onosproject/bgp/controller/BgpPeer.java b/protocols/bgp/api/src/main/java/org/onosproject/bgp/controller/BgpPeer.java
index ab26173..5e4e532 100644
--- a/protocols/bgp/api/src/main/java/org/onosproject/bgp/controller/BgpPeer.java
+++ b/protocols/bgp/api/src/main/java/org/onosproject/bgp/controller/BgpPeer.java
@@ -155,4 +155,9 @@
void updateEvpnNlri(FlowSpecOperation operType, IpAddress nextHop,
List<BgpValueType> extcommunity,
List<BgpEvpnNlri> evpnNlris);
+
+ /**
+ * Send the Route Refresh message to the connected BGP peer.
+ */
+ void sendRouteRefreshMessage();
}
diff --git a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Constants.java b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Constants.java
index 4b6c6eb..7d62818 100644
--- a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Constants.java
+++ b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Constants.java
@@ -22,6 +22,8 @@
private Constants() {
}
+ public static final byte EMPTY = 0x00; //Empty byte
+
public static final short TYPE_AND_LEN = 4;
public static final short TYPE_AND_LEN_AS_SHORT = 4;
public static final short TYPE_AND_LEN_AS_BYTE = 3;
diff --git a/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpControllerImpl.java b/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpControllerImpl.java
index e23b838..9a2df47 100644
--- a/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpControllerImpl.java
+++ b/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpControllerImpl.java
@@ -46,9 +46,16 @@
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import static org.onlab.util.Tools.groupedThreads;
+
@Component(immediate = true, service = BgpController.class)
public class BgpControllerImpl implements BgpController {
@@ -72,6 +79,19 @@
private Map<String, List<String>> closedSessionExceptionMap = new TreeMap<>();
protected Set<BgpRouteListener> bgpRouteListener = new CopyOnWriteArraySet<>();
+ //IDs for timers
+ private static final int PERIODIC_TIMER = 1001;
+ private static final int WARMUP_TIMER = 1002;
+ private static final int COOLDOWN_TIMER = 1003;
+
+ private static final int POOL_SIZE = 3; //Current pool size is 3
+ private ScheduledExecutorService executor;
+ private ScheduledFuture<?> cooldownFuture;
+ private ScheduledFuture<?> periodicFuture;
+ private ScheduledFuture<?> warmupFuture;
+
+ private AtomicBoolean hasTopologyChanged = new AtomicBoolean(false);
+
@Override
public void activeSessionExceptionAdd(String peerId, String exception) {
if (peerId != null) {
@@ -131,6 +151,9 @@
@Activate
public void activate() {
this.ctrl.start();
+ executor = Executors.newScheduledThreadPool(
+ POOL_SIZE,
+ groupedThreads("onos/apps/bgpcontroller", "bgp-rr-timer"));
log.info("Started");
}
@@ -141,6 +164,7 @@
// Close all connected peers
closeConnectedPeers();
this.ctrl.stop();
+ executor.shutdown();
log.info("Stopped");
}
@@ -265,6 +289,17 @@
} else {
this.log.debug("Added Peer {}", bgpId.toString());
connectedPeers.put(bgpId, bgpPeer);
+
+ //If all timers are stopped, start periodic timer
+ this.log.info("Start periodic timer");
+ if (bgpconfig.isRouteRefreshEnabled()
+ && (periodicFuture == null || periodicFuture.isCancelled())
+ && (cooldownFuture == null || cooldownFuture.isCancelled())
+ && (warmupFuture == null || warmupFuture.isCancelled())) {
+ periodicFuture = executor.schedule(periodicTimerTask,
+ bgpconfig.getRouteRefreshPeriodicTimer(), TimeUnit.SECONDS);
+ }
+
return true;
}
}
@@ -383,4 +418,143 @@
public Set<BgpPrefixListener> prefixListener() {
return bgpPrefixListener;
}
+
+ @Override
+ public void notifyTopologyChange() {
+ log.info("Topology change received");
+
+ hasTopologyChanged.set(true);
+
+ //If cooldown timer is running, do nothing further because routeRefresh will be sent when it expires
+ if (cooldownFuture != null && !cooldownFuture.isCancelled()) {
+ log.debug("Do nothing : Cooldown timer running");
+ return;
+ }
+
+ //If warmup timer is running, refresh it. If not, start it
+ if (warmupFuture != null && !warmupFuture.isCancelled()) {
+ warmupFuture.cancel(true);
+ warmupFuture = null;
+
+ warmupFuture = executor.schedule(warmupTimerTask,
+ bgpconfig.getRouteRefreshWarmupTimer(), TimeUnit.SECONDS);
+
+ log.debug("Warmup timer running. Re-started warmup timer");
+ return;
+ } else {
+ warmupFuture = executor.schedule(warmupTimerTask,
+ bgpconfig.getRouteRefreshWarmupTimer(), TimeUnit.SECONDS);
+ log.debug("Warmup timer started");
+ return;
+ }
+ }
+
+ protected void resetTimers() {
+ if (periodicFuture != null && !periodicFuture.isCancelled()) {
+ periodicFuture.cancel(true);
+ periodicFuture = null;
+ }
+
+ if (warmupFuture != null && !warmupFuture.isCancelled()) {
+ warmupFuture.cancel(true);
+ warmupFuture = null;
+ }
+
+ if (cooldownFuture != null && !cooldownFuture.isCancelled()) {
+ cooldownFuture.cancel(true);
+ cooldownFuture = null;
+ }
+ }
+
+ protected synchronized void timerCallback(int timerId) {
+ switch (timerId) {
+ case PERIODIC_TIMER:
+ //Cancel periodic timer and run cooldown timer
+ periodicFuture.cancel(true);
+ periodicFuture = null;
+
+ sendRouteRefreshToPeers();
+
+ //Cancel warmup timer if it is running
+ if (warmupFuture != null && !warmupFuture.isCancelled()) {
+ warmupFuture.cancel(true);
+ warmupFuture = null;
+ }
+
+ cooldownFuture = executor.schedule(cooldownTimerTask,
+ bgpconfig.getRouteRefreshCooldownTimer(), TimeUnit.SECONDS);
+ log.debug("Cooldown timer started");
+ break;
+ case WARMUP_TIMER:
+ //Send route refresh and start cooldown timer
+ warmupFuture.cancel(true);
+ warmupFuture = null;
+
+ sendRouteRefreshToPeers();
+
+ cooldownFuture = executor.schedule(cooldownTimerTask,
+ bgpconfig.getRouteRefreshCooldownTimer(), TimeUnit.SECONDS);
+ //Cancel periodic timer, if it is running
+ if (periodicFuture != null && !periodicFuture.isCancelled()) {
+ periodicFuture.cancel(true);
+ periodicFuture = null;
+ }
+ log.debug("Cooldown timer started");
+ break;
+ case COOLDOWN_TIMER:
+ //If hasTopologyChanged is true, we need to restart cooldown timer.
+ //Otherwise, start periodic timer
+ boolean hasTopologyChangedValue = hasTopologyChanged.get();
+
+ cooldownFuture.cancel(true);
+ cooldownFuture = null;
+
+ if (hasTopologyChangedValue) {
+ sendRouteRefreshToPeers();
+ cooldownFuture = executor.schedule(cooldownTimerTask,
+ bgpconfig.getRouteRefreshCooldownTimer(), TimeUnit.SECONDS);
+ log.debug("Cooldown timer started");
+ } else {
+ periodicFuture = executor.schedule(periodicTimerTask,
+ bgpconfig.getRouteRefreshPeriodicTimer(), TimeUnit.SECONDS);
+ log.debug("Periodic timer started");
+ }
+ break;
+ default:
+ log.error("Invalid timerId in callback");
+ }
+
+ }
+
+ private synchronized void sendRouteRefreshToPeers() {
+ //Iterate over peers and send route refresh
+ connectedPeers.forEach((k, v) -> v.sendRouteRefreshMessage());
+
+ //Refresh hasTopologyChanged variable
+ hasTopologyChanged.set(false);
+ }
+
+ private Runnable periodicTimerTask = new Runnable() {
+ @Override
+ public void run() {
+ log.debug("Periodic Timer Expired");
+ timerCallback(PERIODIC_TIMER);
+ }
+ };
+
+ private Runnable cooldownTimerTask = new Runnable() {
+ @Override
+ public void run() {
+ log.info("Cooldown Timer Expired");
+ timerCallback(COOLDOWN_TIMER);
+ }
+ };
+
+ private Runnable warmupTimerTask = new Runnable() {
+ @Override
+ public void run() {
+ log.debug("Warmup Timer Expired");
+ timerCallback(WARMUP_TIMER);
+ }
+ };
}
diff --git a/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpLocalRibImpl.java b/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpLocalRibImpl.java
index edbe75c..abea91c 100644
--- a/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpLocalRibImpl.java
+++ b/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpLocalRibImpl.java
@@ -153,7 +153,8 @@
for (BgpNodeListener l : bgpController.listener()) {
l.addNode((BgpNodeLSNlriVer4) nlri, details);
}
- log.debug("Local RIB ad node: {}", detailsLocRib.toString());
+ bgpController.notifyTopologyChange();
+ log.debug("Local RIB add node: {}", detailsLocRib.toString());
}
} else if (nlri instanceof BgpLinkLsNlriVer4) {
BgpLinkLSIdentifier linkLsIdentifier = ((BgpLinkLsNlriVer4) nlri).getLinkIdentifier();
@@ -173,6 +174,7 @@
for (BgpLinkListener l : bgpController.linkListener()) {
l.addLink((BgpLinkLsNlriVer4) nlri, details);
}
+ bgpController.notifyTopologyChange();
log.debug("Local RIB add link: {}", detailsLocRib.toString());
}
} else if (nlri instanceof BgpPrefixIPv4LSNlriVer4) {
@@ -314,6 +316,7 @@
l.deleteNode((BgpNodeLSNlriVer4) nlri);
}
nodeTree.remove(nodeLsIdentifier);
+ bgpController.notifyTopologyChange();
}
}
@@ -379,7 +382,7 @@
l.deleteLink((BgpLinkLsNlriVer4) nlri);
}
linkTree.remove(linkLsIdentifier);
-
+ bgpController.notifyTopologyChange();
}
}
diff --git a/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpPeerImpl.java b/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpPeerImpl.java
index 4dc8fee..d53bbc2 100644
--- a/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpPeerImpl.java
+++ b/protocols/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BgpPeerImpl.java
@@ -46,6 +46,7 @@
import org.onosproject.bgpio.types.MpReachNlri;
import org.onosproject.bgpio.types.MpUnReachNlri;
import org.onosproject.bgpio.types.MultiProtocolExtnCapabilityTlv;
+import org.onosproject.bgpio.types.RouteRefreshCapabilityTlv;
import org.onosproject.bgpio.types.Origin;
import org.onosproject.bgpio.types.RpdCapabilityTlv;
import org.onosproject.bgpio.types.attr.WideCommunity;
@@ -160,6 +161,19 @@
return false;
}
+ private final boolean isRouteRefreshSupported() {
+ List<BgpValueType> capabilities = sessionInfo.remoteBgpCapability();
+
+ for (BgpValueType currentCapability : capabilities) {
+ if (currentCapability instanceof RouteRefreshCapabilityTlv) {
+ //Presence of Reoute Refresh capability TLV means route refresh is supported
+ log.debug("Route Refresh is supported by peer");
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Send flow specification update message to peer.
*
@@ -320,6 +334,24 @@
}
@Override
+ public void sendRouteRefreshMessage() {
+ if (!isRouteRefreshSupported()) {
+ log.debug("Route Refresh not supported by peer, so cannot send message");
+ return;
+ }
+
+ BgpMessage msg = Controller.getBgpMessageFactory4()
+ .routeRefreshMsgBuilder()
+ .addAfiSafiValue(Constants.AFI_IPV6_UNICAST, Constants.EMPTY, Constants.SAFI_UNICAST)
+ .addAfiSafiValue(Constants.AFI_VALUE, Constants.EMPTY, Constants.SAFI_VALUE)
+ .build();
+
+ channel.write(Collections.singletonList(msg));
+
+ log.info("Route Refresh sent to {}", channelId);
+ }
+
+ @Override
public void buildAdjRibIn(List<BgpValueType> pathAttr) throws BgpParseException {
ListIterator<BgpValueType> iterator = pathAttr.listIterator();
while (iterator.hasNext()) {
diff --git a/providers/bgp/topology/src/test/java/org/onosproject/provider/bgp/topology/impl/BgpControllerAdapter.java b/providers/bgp/topology/src/test/java/org/onosproject/provider/bgp/topology/impl/BgpControllerAdapter.java
index c1b53ed..8ba85bf 100644
--- a/providers/bgp/topology/src/test/java/org/onosproject/provider/bgp/topology/impl/BgpControllerAdapter.java
+++ b/providers/bgp/topology/src/test/java/org/onosproject/provider/bgp/topology/impl/BgpControllerAdapter.java
@@ -181,4 +181,9 @@
public void removePrefixListener(BgpPrefixListener listener) {
// TODO Auto-generated method stub
}
+
+ @Override
+ public void notifyTopologyChange() {
+ // TODO Auto-generated method stub
+ }
}