Handling multiple layers of spines.
Also in this commit:
- Triggering swap group creation and accounting for it in DestinationSet
- Fixes in ofdpa2 and ofdpa3 pipeline to allow SR Continue operation
- Renaming mplsSet in DestinationSet to notBos
- Removing unused RandomDestinationSet
- Bug fix in ofdpa driver for swap group chain creation
- Bug fix in ofdpa driver for verify group operation
- Better internal bookeeping of device ports and associated neighbors
Change-Id: I2b8f1c4c0b305ef847d57ca7a5320943e06d190d
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index 3cf1331..1e0e6fd 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -188,7 +188,8 @@
}
/**
- * Updates local stores for link-src device/port to neighbor (link-dst).
+ * Updates local stores for link-src-device/port to neighbor (link-dst) for
+ * link that has come up.
*
* @param link the infrastructure link
*/
@@ -207,11 +208,13 @@
}
/**
- * Updates local stores for port that has gone down.
+ * Updates local stores for link-src-device/port to neighbor (link-dst) for
+ * link that has gone down.
*
- * @param port port number that has gone down
+ * @param link the infrastructure link
*/
- public void portDown(PortNumber port) {
+ public void portDownForLink(Link link) {
+ PortNumber port = link.src().port();
if (portDeviceMap.get(port) == null) {
log.warn("portDown: unknown port");
return;
@@ -224,6 +227,18 @@
}
/**
+ * Cleans up local stores for removed neighbor device.
+ *
+ * @param neighborId the device identifier for the neighbor device
+ */
+ public void cleanUpForNeighborDown(DeviceId neighborId) {
+ Set<PortNumber> ports = devicePortMap.remove(neighborId);
+ if (ports != null) {
+ ports.forEach(p -> portDeviceMap.remove(p));
+ }
+ }
+
+ /**
* Checks all groups in the src-device of link for neighbor sets that include
* the dst-device of link, and edits the hash groups according to link up
* or down. Should only be called by the master instance of the src-switch
@@ -248,8 +263,11 @@
.stream()
.filter(entry -> entry.getKey().deviceId().equals(deviceId))
// Filter out PW transit groups or include them if MPLS ECMP is supported
- .filter(entry -> !entry.getKey().destinationSet().mplsSet() ||
- (entry.getKey().destinationSet().mplsSet() && srManager.getMplsEcmp()))
+ .filter(entry -> !entry.getKey().destinationSet().notBos() ||
+ (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
+ // Filter out simple SWAP groups or include them if MPLS ECMP is supported
+ .filter(entry -> !entry.getKey().destinationSet().swap() ||
+ (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
.filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
.map(entry -> entry.getKey())
.collect(Collectors.toSet());
@@ -452,6 +470,21 @@
int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
Integer nextId = nhops.nextId();
+ // some store elements may not be hashed next-objectives - ignore them
+ if (isSimpleNextObjective(dskey)) {
+ log.debug("Ignoring {} of SIMPLE nextObj for targetSw:{}"
+ + " -> dstSw:{} with current nextHops:{} to new"
+ + " nextHops: {} in nextId:{}",
+ (revoke) ? "removal" : "addition", targetSw, destSw,
+ currNeighbors, nextHops, nextId);
+ if ((revoke && !nextHops.isEmpty())
+ || (!revoke && !nextHops.equals(currNeighbors))) {
+ log.warn("Simple next objective cannot be edited to "
+ + "move from {} to {}", currNeighbors, nextHops);
+ }
+ continue;
+ }
+
if (currNeighbors == null || nextHops == null) {
log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
+ " in targetSw:{} for dstSw:{}", currNeighbors, nextHops,
@@ -514,6 +547,7 @@
new NextNeighbors(currentNextHops.dstNextHops(),
currentNextHops.nextId()));
}
+
// even if one fails and others succeed, return false so ECMPspg not updated
return success;
}
@@ -615,6 +649,18 @@
}
/**
+ * Returns true if the destination set is meant for swap or multi-labeled
+ * packet transport, and MPLS ECMP is not supported.
+ *
+ * @param dskey the key representing the destination set
+ * @return true if destination set is meant for simple next objectives
+ */
+ boolean isSimpleNextObjective(DestinationSetNextObjectiveStoreKey dskey) {
+ return (dskey.destinationSet().notBos() || dskey.destinationSet().swap())
+ && !srManager.getMplsEcmp();
+ }
+
+ /**
* Adds or removes a port that has been configured with a vlan to a broadcast group
* for bridging. Should only be called by the master instance for this device.
*
@@ -678,23 +724,26 @@
}
/**
- * Returns the next objective of type hashed associated with the destination set.
- * In addition, updates the existing next-objective if new route-route paths found
- * have resulted in the addition of new next-hops to a particular destination.
- * If there is no existing next objective for this destination set, this method
- * would create a next objective and return the nextId. Optionally metadata can be
- * passed in for the creation of the next objective.
+ * Returns the next objective of type hashed (or simple) associated with the
+ * destination set. In addition, updates the existing next-objective if new
+ * route-paths found have resulted in the addition of new next-hops to a
+ * particular destination. If there is no existing next objective for this
+ * destination set, this method would create a next objective and return the
+ * nextId. Optionally metadata can be passed in for the creation of the next
+ * objective. If the parameter simple is true then a simple next objective
+ * is created instead of a hashed one.
*
* @param ds destination set
* @param nextHops a map of per destination next hops
* @param meta metadata passed into the creation of a Next Objective
- * @param isBos if Bos is set
+ * @param simple if true, a simple next objective will be created instead of
+ * a hashed next objective
* @return int if found or -1 if there are errors in the creation of the
- * neighbor set.
+ * neighbor set.
*/
public int getNextObjectiveId(DestinationSet ds,
Map<DeviceId, Set<DeviceId>> nextHops,
- TrafficSelector meta, boolean isBos) {
+ TrafficSelector meta, boolean simple) {
NextNeighbors next = dsNextObjStore.
get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
if (next == null) {
@@ -708,7 +757,7 @@
(nsStoreEntry.getKey().deviceId().equals(deviceId)))
.collect(Collectors.toList()));
- createGroupFromDestinationSet(ds, nextHops, meta, isBos);
+ createGroupFromDestinationSet(ds, nextHops, meta, simple);
next = dsNextObjStore.
get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
if (next == null) {
@@ -833,39 +882,38 @@
}
// Update portToDevice database
- DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
+ // should always update as neighbor could have changed on this port
+ DeviceId prev = portDeviceMap.put(portToNeighbor, neighborId);
if (prev != null) {
- log.debug("Device: {} port: {} already has neighbor: {} ",
+ log.debug("Device/port: {}/{} previous neighbor: {}, current neighbor: {} ",
deviceId, portToNeighbor, prev, neighborId);
}
}
/**
* Creates a NextObjective for a hash group in this device from a given
- * DestinationSet.
+ * DestinationSet. If the parameter simple is true, a simple next objective
+ * is created instead.
*
* @param ds the DestinationSet
* @param neighbors a map for each destination and its next-hops
* @param meta metadata passed into the creation of a Next Objective
- * @param isBos if BoS is set
+ * @param simple if true, a simple next objective will be created instead of
+ * a hashed next objective
*/
public void createGroupFromDestinationSet(DestinationSet ds,
Map<DeviceId, Set<DeviceId>> neighbors,
TrafficSelector meta,
- boolean isBos) {
+ boolean simple) {
int nextId = flowObjectiveService.allocateNextId();
- NextObjective.Type type = NextObjective.Type.HASHED;
+ NextObjective.Type type = (simple) ? NextObjective.Type.SIMPLE
+ : NextObjective.Type.HASHED;
if (neighbors == null || neighbors.isEmpty()) {
log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
+ "to create group in dev:{} for ds: {} with next-hops {}",
deviceId, ds, neighbors);
return;
}
- // If Bos == False and MPLS-ECMP == false, we have
- // to use simple group and we will pick a single neighbor for a single dest.
- if (!isBos && !srManager.getMplsEcmp()) {
- type = NextObjective.Type.SIMPLE;
- }
NextObjective.Builder nextObjBuilder = DefaultNextObjective
.builder()
@@ -878,7 +926,7 @@
// create treatment buckets for each neighbor for each dst Device
// except in the special case where we only want to pick a single
- // neighbor for a simple group
+ // neighbor/port for a simple nextObj
boolean foundSingleNeighbor = false;
boolean treatmentAdded = false;
Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
@@ -912,8 +960,8 @@
}
// For each port to the neighbor, we create a new treatment
Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
- // In this case we are using a SIMPLE group. We randomly pick a port
- if (!isBos && !srManager.getMplsEcmp()) {
+ // In this case we need a SIMPLE nextObj. We randomly pick a port
+ if (simple) {
int size = devicePortMap.get(neighborId).size();
int index = RandomUtils.nextInt(0, size);
neighborPorts = Collections.singleton(
@@ -928,9 +976,14 @@
tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
int edgeLabel = ds.getEdgeLabel(dst);
if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
- tBuilder.pushMpls()
- .copyTtlOut()
- .setMpls(MplsLabel.mplsLabel(edgeLabel));
+ if (simple) {
+ // swap label case
+ tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
+ } else {
+ // ecmp with label push case
+ tBuilder.pushMpls().copyTtlOut()
+ .setMpls(MplsLabel.mplsLabel(edgeLabel));
+ }
}
tBuilder.setOutput(sp);
nextObjBuilder.addTreatment(tBuilder.build());
@@ -1395,8 +1448,11 @@
.stream()
.filter(entry -> entry.getKey().deviceId().equals(deviceId))
// Filter out PW transit groups or include them if MPLS ECMP is supported
- .filter(entry -> !entry.getKey().destinationSet().mplsSet() ||
- (entry.getKey().destinationSet().mplsSet() && srManager.getMplsEcmp()))
+ .filter(entry -> !entry.getKey().destinationSet().notBos() ||
+ (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
+ // Filter out simple SWAP groups or include them if MPLS ECMP is supported
+ .filter(entry -> !entry.getKey().destinationSet().swap() ||
+ (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
.map(entry -> entry.getKey())
.collect(Collectors.toSet());
for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
@@ -1434,8 +1490,11 @@
+ " port/label {}/{} to dst {} via {}",
deviceId, nid, port, edgeLabel,
dstDev, neighbor);
- nextObjBuilder.addTreatment(treatmentBuilder(port,
- neighborMac, edgeLabel));
+ nextObjBuilder
+ .addTreatment(treatmentBuilder(port,
+ neighborMac,
+ dsKey.destinationSet().swap(),
+ edgeLabel));
});
});
});
@@ -1450,16 +1509,22 @@
}
TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
- int edgeLabel) {
+ boolean swap, int edgeLabel) {
TrafficTreatment.Builder tBuilder =
DefaultTrafficTreatment.builder();
tBuilder.setOutput(outport)
.setEthDst(dstMac)
.setEthSrc(nodeMacAddr);
if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
- tBuilder.pushMpls()
- .copyTtlOut()
- .setMpls(MplsLabel.mplsLabel(edgeLabel));
+ if (swap) {
+ // swap label case
+ tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
+ } else {
+ // ecmp with label push case
+ tBuilder.pushMpls()
+ .copyTtlOut()
+ .setMpls(MplsLabel.mplsLabel(edgeLabel));
+ }
}
return tBuilder.build();
}