mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-12-06 01:41:26 +01:00
[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:
parent
dcd56ba4ce
commit
2d7ff64d09
@ -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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user