sangho
Committed by Jonathan Hart

ONOS-1438: Improved the routing rule population process for link add and failure…

…; computes the routes changed from the link changes and populates the rules only for the routes.

Change-Id: Id4dbd80da37b333f2c19bc97333472dc8031481b
......@@ -15,10 +15,13 @@
*/
package org.onosproject.segmentrouting;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.IpPrefix;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.flow.FlowRule;
import org.slf4j.Logger;
......@@ -39,6 +42,7 @@ public class DefaultRoutingHandler {
private SegmentRoutingManager srManager;
private RoutingRulePopulator rulePopulator;
private NetworkConfigHandler config;
private HashMap<DeviceId, ECMPShortestPathGraph> currentEcmpSpgMap;
private Status populationStatus;
/**
......@@ -68,6 +72,7 @@ public class DefaultRoutingHandler {
this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
this.config = checkNotNull(srManager.networkConfigHandler);
this.populationStatus = Status.IDLE;
this.currentEcmpSpgMap = Maps.newHashMap();
}
/**
......@@ -79,6 +84,7 @@ public class DefaultRoutingHandler {
public boolean populateAllRoutingRules() {
populationStatus = Status.STARTED;
rulePopulator.resetCounter();
log.info("Starts to populate routing rules");
for (Device sw : srManager.deviceService.getDevices()) {
......@@ -87,22 +93,233 @@ public class DefaultRoutingHandler {
continue;
}
ECMPShortestPathGraph ecmpSPG = new ECMPShortestPathGraph(sw.id(), srManager);
if (!populateEcmpRoutingRules(sw, ecmpSPG)) {
ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(sw.id(), srManager);
if (!populateEcmpRoutingRules(sw.id(), ecmpSpg)) {
populationStatus = Status.ABORTED;
log.debug("Abort routing rule population");
return false;
}
currentEcmpSpgMap.put(sw.id(), ecmpSpg);
// TODO: Set adjacency routing rule for all switches
}
populationStatus = Status.SUCCEEDED;
log.info("Completes routing rule population");
log.info("Completes routing rule population. Total # of rules pushed : {}",
rulePopulator.getCounter());
return true;
}
private boolean populateEcmpRoutingRules(Device sw,
/**
* Populates the routing rules according to the route changes due to the link
* failure or link add. It computes the routes changed due to the link changes and
* repopulates the rules only for the routes.
*
* @param linkFail link failed, null for link added
* @return true if it succeeds to populate all rules, false otherwise
*/
public boolean populateRoutingRulesForLinkStatusChange(Link linkFail) {
synchronized (populationStatus) {
if (populationStatus == Status.STARTED) {
return true;
}
Set<ArrayList<DeviceId>> routeChanges;
populationStatus = Status.STARTED;
if (linkFail == null) {
// Compare all routes of existing ECMP SPG with the new ones
routeChanges = computeRouteChange();
} else {
// Compare existing ECMP SPG only with the link removed
routeChanges = computeDamagedRoutes(linkFail);
}
if (routeChanges.isEmpty()) {
log.debug("No route changes for the link status change");
populationStatus = Status.SUCCEEDED;
return true;
}
if (repopulateRoutingRulesForRoutes(routeChanges)) {
populationStatus = Status.SUCCEEDED;
log.info("Complete to repopulate the rules. # of rules populated : {}",
rulePopulator.getCounter());
return true;
} else {
populationStatus = Status.ABORTED;
log.warn("Failed to repopulate the rules.");
return false;
}
}
}
private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
rulePopulator.resetCounter();
for (ArrayList<DeviceId> link: routes) {
if (link.size() == 1) {
ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(link.get(0), srManager);
if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
currentEcmpSpgMap.put(link.get(0), ecmpSpg);
}
continue;
}
DeviceId src = link.get(0);
DeviceId dst = link.get(1);
ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(dst, srManager);
currentEcmpSpgMap.put(dst, ecmpSpg);
HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
ecmpSpg.getAllLearnedSwitchesAndVia();
for (Integer itrIdx : switchVia.keySet()) {
HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
switchVia.get(itrIdx);
for (DeviceId targetSw : swViaMap.keySet()) {
if (!targetSw.equals(src)) {
continue;
}
Set<DeviceId> nextHops = new HashSet<>();
for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
if (via.isEmpty()) {
nextHops.add(dst);
} else {
nextHops.add(via.get(0));
}
}
if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) {
return false;
}
}
}
}
return true;
}
private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
Set<ArrayList<DeviceId>> routes = new HashSet<>();
for (Device sw : srManager.deviceService.getDevices()) {
if (srManager.mastershipService.
getLocalRole(sw.id()) != MastershipRole.MASTER) {
continue;
}
ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
if (ecmpSpg == null) {
log.error("No existing ECMP path for switch {}", sw.id());
continue;
}
HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
ecmpSpg.getAllLearnedSwitchesAndVia();
for (Integer itrIdx : switchVia.keySet()) {
HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
switchVia.get(itrIdx);
for (DeviceId targetSw : swViaMap.keySet()) {
DeviceId destSw = sw.id();
Set<ArrayList<DeviceId>> subLinks =
computeLinks(targetSw, destSw, swViaMap);
for (ArrayList<DeviceId> alink: subLinks) {
if (alink.get(0).equals(linkFail.src().deviceId()) &&
alink.get(1).equals(linkFail.dst().deviceId())) {
ArrayList<DeviceId> aRoute = new ArrayList<>();
aRoute.add(targetSw);
aRoute.add(destSw);
routes.add(aRoute);
break;
}
}
}
}
}
return routes;
}
private Set<ArrayList<DeviceId>> computeRouteChange() {
Set<ArrayList<DeviceId>> routes = new HashSet<>();
for (Device sw : srManager.deviceService.getDevices()) {
if (srManager.mastershipService.
getLocalRole(sw.id()) != MastershipRole.MASTER) {
continue;
}
ECMPShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(sw.id());
if (ecmpSpg == null) {
log.debug("No existing ECMP path for Switch {}", sw.id());
ArrayList<DeviceId> route = new ArrayList<>();
route.add(sw.id());
routes.add(route);
continue;
}
ECMPShortestPathGraph newEcmpSpg =
new ECMPShortestPathGraph(sw.id(), srManager);
HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
ecmpSpg.getAllLearnedSwitchesAndVia();
HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchViaUpdated =
newEcmpSpg.getAllLearnedSwitchesAndVia();
for (Integer itrIdx : switchVia.keySet()) {
HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
switchVia.get(itrIdx);
for (DeviceId srcSw : swViaMap.keySet()) {
ArrayList<ArrayList<DeviceId>> via1 = swViaMap.get(srcSw);
ArrayList<ArrayList<DeviceId>> via2 = getVia(switchViaUpdated, srcSw);
if (!via1.equals(via2)) {
ArrayList<DeviceId> route = new ArrayList<>();
route.add(srcSw);
route.add(sw.id());
routes.add(route);
}
}
}
}
return routes;
}
private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId srcSw) {
for (Integer itrIdx : switchVia.keySet()) {
HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
switchVia.get(itrIdx);
if (swViaMap.get(srcSw) == null) {
continue;
} else {
return swViaMap.get(srcSw);
}
}
return new ArrayList<>();
}
private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
DeviceId dst,
HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
for (ArrayList<DeviceId> via : viaMap.get(src)) {
DeviceId linkSrc = src;
DeviceId linkDst = dst;
for (DeviceId viaDevice: via) {
ArrayList<DeviceId> link = new ArrayList<>();
linkDst = viaDevice;
link.add(linkSrc);
link.add(linkDst);
subLinks.add(link);
linkSrc = viaDevice;
}
ArrayList<DeviceId> link = new ArrayList<>();
link.add(linkSrc);
link.add(dst);
subLinks.add(link);
}
return subLinks;
}
private boolean populateEcmpRoutingRules(DeviceId destSw,
ECMPShortestPathGraph ecmpSPG) {
HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
......@@ -111,7 +328,6 @@ public class DefaultRoutingHandler {
HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
switchVia.get(itrIdx);
for (DeviceId targetSw : swViaMap.keySet()) {
DeviceId destSw = sw.id();
Set<DeviceId> nextHops = new HashSet<>();
for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
......
......@@ -139,6 +139,7 @@ public class IpHandler {
OutboundPacket packet = new DefaultOutboundPacket(deviceId,
treatment, ByteBuffer.wrap(eth.serialize()));
srManager.packetService.emit(packet);
ipPacketQueue.get(destIpAddress).remove(ipPacket);
}
}
}
......
......@@ -40,6 +40,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import static com.google.common.base.Preconditions.checkNotNull;
......@@ -47,8 +48,9 @@ public class RoutingRulePopulator {
private static final Logger log = LoggerFactory.getLogger(RoutingRulePopulator.class);
private SegmentRoutingManager srManager;
private NetworkConfigHandler config;
private final SegmentRoutingManager srManager;
private final NetworkConfigHandler config;
private AtomicLong rulePopulationCounter;
/**
* Creates a RoutingRulePopulator object.
......@@ -58,6 +60,21 @@ public class RoutingRulePopulator {
public RoutingRulePopulator(SegmentRoutingManager srManager) {
this.srManager = srManager;
this.config = checkNotNull(srManager.networkConfigHandler);
this.rulePopulationCounter = new AtomicLong(0);
}
/**
* Resets the population counter.
*/
public void resetCounter() {
rulePopulationCounter.set(0);
}
/**
* Returns the number of rules populated.
*/
public long getCounter() {
return rulePopulationCounter.get();
}
/**
......@@ -87,6 +104,7 @@ public class RoutingRulePopulator {
srManager.appId, 600, false, FlowRule.Type.IP);
srManager.flowRuleService.applyFlowRules(f);
rulePopulationCounter.incrementAndGet();
log.debug("Flow rule {} is set to switch {}", f, deviceId);
}
......@@ -162,6 +180,7 @@ public class RoutingRulePopulator {
srManager.appId, 600, false, FlowRule.Type.IP);
srManager.flowRuleService.applyFlowRules(f);
rulePopulationCounter.incrementAndGet();
log.debug("IP flow rule {} is set to switch {}", f, deviceId);
return true;
......@@ -216,6 +235,7 @@ public class RoutingRulePopulator {
FlowRule f = new DefaultFlowRule(deviceId, selector, treatment, 100,
srManager.appId, 600, false, FlowRule.Type.MPLS);
srManager.flowRuleService.applyFlowRules(f);
rulePopulationCounter.incrementAndGet();
log.debug("MPLS rule {} is set to {}", f, deviceId);
}
......
......@@ -151,8 +151,7 @@ public class SegmentRoutingManager {
groupHandler.createGroups();
groupHandlerMap.put(device.id(), groupHandler);
log.debug("Initiating default group handling for {}", device.id());
defaultRoutingHandler.startPopulationProcess();
defaultRoutingHandler.populateTtpRules(device.id());
} else {
log.debug("Activate: Local role {} "
+ "is not MASTER for device {}",
......@@ -162,6 +161,8 @@ public class SegmentRoutingManager {
}
}
defaultRoutingHandler.startPopulationProcess();
log.info("Started");
}
......@@ -239,6 +240,8 @@ public class SegmentRoutingManager {
switch (event.type()) {
case DEVICE_ADDED:
case PORT_REMOVED:
case DEVICE_UPDATED:
case DEVICE_AVAILABILITY_CHANGED:
scheduleEventHandlerIfNotScheduled(event);
break;
default:
......@@ -294,8 +297,12 @@ public class SegmentRoutingManager {
processLinkRemoved((Link) event.subject());
} else if (event.type() == GroupEvent.Type.GROUP_ADDED) {
processGroupAdded((Group) event.subject());
} else if (event.type() == DeviceEvent.Type.DEVICE_ADDED) {
} else if (event.type() == DeviceEvent.Type.DEVICE_ADDED ||
event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
event.type() == DeviceEvent.Type.DEVICE_UPDATED) {
if (deviceService.isAvailable(((Device) event.subject()).id())) {
processDeviceAdded((Device) event.subject());
}
} else if (event.type() == DeviceEvent.Type.PORT_REMOVED) {
processPortRemoved((Device) event.subject(),
((DeviceEvent) event).port());
......@@ -321,12 +328,12 @@ public class SegmentRoutingManager {
groupHandler.linkUp(link);
}
}
defaultRoutingHandler.startPopulationProcess();
defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(null);
}
private void processLinkRemoved(Link link) {
log.debug("A link {} was removed", link.toString());
defaultRoutingHandler.startPopulationProcess();
defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(link);
}
......