[ONOS-8085] Route Refresh timers in Controller

- Add timers in BgpController
- Add triggers in BgpLocalRib
- Code to send Route Refresh messages

Change-Id: Id45f1bcb3325ccc21c0d0d2e673c2b0097aac552
This commit is contained in:
Ankur Aggarwal 2020-07-29 08:46:04 +00:00 committed by Pier Luigi Ventre
parent dcd56ba4ce
commit 2d7ff64d09
7 changed files with 228 additions and 2 deletions

View File

@ -221,4 +221,9 @@ public interface BgpController {
*/
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();
}

View File

@ -155,4 +155,9 @@ public interface BgpPeer {
void updateEvpnNlri(FlowSpecOperation operType, IpAddress nextHop,
List<BgpValueType> extcommunity,
List<BgpEvpnNlri> evpnNlris);
/**
* Send the Route Refresh message to the connected BGP peer.
*/
void sendRouteRefreshMessage();
}

View File

@ -22,6 +22,8 @@ public final class Constants {
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;

View File

@ -46,9 +46,16 @@ import java.util.Set;
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 @@ public class BgpControllerImpl implements BgpController {
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 @@ public class BgpControllerImpl implements BgpController {
@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 @@ public class BgpControllerImpl implements BgpController {
// Close all connected peers
closeConnectedPeers();
this.ctrl.stop();
executor.shutdown();
log.info("Stopped");
}
@ -265,6 +289,17 @@ public class BgpControllerImpl implements BgpController {
} 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 class BgpControllerImpl implements BgpController {
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);
}
};
}

View File

@ -153,7 +153,8 @@ public class BgpLocalRibImpl implements BgpLocalRib {
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 @@ public class BgpLocalRibImpl implements BgpLocalRib {
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 @@ public class BgpLocalRibImpl implements BgpLocalRib {
l.deleteNode((BgpNodeLSNlriVer4) nlri);
}
nodeTree.remove(nodeLsIdentifier);
bgpController.notifyTopologyChange();
}
}
@ -379,7 +382,7 @@ public class BgpLocalRibImpl implements BgpLocalRib {
l.deleteLink((BgpLinkLsNlriVer4) nlri);
}
linkTree.remove(linkLsIdentifier);
bgpController.notifyTopologyChange();
}
}

View File

@ -46,6 +46,7 @@ import org.onosproject.bgpio.types.Med;
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 @@ public class BgpPeerImpl implements BgpPeer {
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.
*
@ -319,6 +333,24 @@ public class BgpPeerImpl implements BgpPeer {
channel.write(Collections.singletonList(msg));
}
@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();

View File

@ -181,4 +181,9 @@ public class BgpControllerAdapter implements BgpController {
public void removePrefixListener(BgpPrefixListener listener) {
// TODO Auto-generated method stub
}
@Override
public void notifyTopologyChange() {
// TODO Auto-generated method stub
}
}