use of org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey in project trellis-control by opennetworkinglab.
the class DefaultGroupHandler method retryHash.
/**
* 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 instance leading the programming of
* the src-switch of link. Typically used when there are no route-path
* changes due to the link up or down, as the ECMPspg does not change.
*
* @param link the infrastructure link that has gone down or come up
* @param linkDown true if link has gone down
* @param firstTime true if link has come up for the first time i.e a link
* not seen-before
*/
public void retryHash(Link link, boolean linkDown, boolean firstTime) {
MacAddress neighborMac;
try {
neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting retryHash.");
return;
}
// find all the destinationSets related to link
Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet().stream().filter(entry -> entry.getKey().deviceId().equals(deviceId)).filter(entry -> !entry.getKey().destinationSet().notBos() || (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp())).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());
log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}", deviceId, link.dst().deviceId(), dsKeySet);
for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
NextNeighbors nextHops = dsNextObjStore.get(dsKey);
if (nextHops == null) {
log.warn("retryHash in device {}, but global store has no record " + "for dsKey:{}", deviceId, dsKey);
continue;
}
int nextId = nextHops.nextId();
Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
if (!linkDown) {
List<PortLabel> pl = Lists.newArrayList();
if (firstTime) {
// to the same hash group are avoided by the driver.
for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
dstSet.forEach(dst -> {
int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
pl.add(new PortLabel(p, edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
});
}
addToHashedNextObjective(pl, neighborMac, nextId);
} else {
// handle only the port that came up
dstSet.forEach(dst -> {
int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
});
addToHashedNextObjective(pl, neighborMac, nextId);
}
} else {
// linkdown
List<PortLabel> pl = Lists.newArrayList();
dstSet.forEach(dst -> {
int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
});
removeFromHashedNextObjective(pl, neighborMac, nextId);
}
}
}
use of org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey in project trellis-control by opennetworkinglab.
the class DefaultGroupHandler method createGroupFromDestinationSet.
/**
* Creates a NextObjective for a hash group in this device from a given
* 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 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 simple) {
int nextId = flowObjectiveService.allocateNextId();
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;
}
NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder().withId(nextId).withType(type).fromApp(appId);
if (meta != null) {
// Udate the meta VLAN id to match the PW transport label
if (!popVlanInHashGroup(ds)) {
TrafficSelector newMeta = DefaultTrafficSelector.builder(meta).matchVlanId(srManager.getPwTransportVlan()).build();
meta = newMeta;
}
nextObjBuilder.withMeta(meta);
}
// create treatment buckets for each neighbor for each dst Device
// except in the special case where we only want to pick a single
// neighbor/port for a simple nextObj
boolean foundSingleNeighbor = false;
boolean treatmentAdded = false;
Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
for (DeviceId dst : ds.getDestinationSwitches()) {
Set<DeviceId> nextHops = neighbors.get(dst);
if (nextHops == null || nextHops.isEmpty()) {
continue;
}
if (foundSingleNeighbor) {
break;
}
for (DeviceId neighborId : nextHops) {
if (devicePortMap.get(neighborId) == null) {
log.warn("Neighbor {} is not in the port map yet for dev:{}", neighborId, deviceId);
return;
} else if (devicePortMap.get(neighborId).isEmpty()) {
log.warn("There are no ports for " + "the Device {} in the port map yet", neighborId);
return;
}
MacAddress neighborMac;
try {
neighborMac = deviceConfig.getDeviceMac(neighborId);
} catch (DeviceConfigNotFoundException e) {
log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
return;
}
// For each port to the neighbor, we create a new treatment
Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
// 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(Iterables.get(devicePortMap.get(neighborId), index));
foundSingleNeighbor = true;
}
for (PortNumber sp : neighborPorts) {
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
int edgeLabel = ds.getEdgeLabel(dst);
if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
if (simple) {
// swap label case
tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
} else {
// ecmp with label push case
tBuilder.pushMpls().copyTtlOut().setMpls(MplsLabel.mplsLabel(edgeLabel));
}
}
// Set VLAN ID for PW transport. Otherwise pop vlan
if (!popVlanInHashGroup(ds)) {
tBuilder.setVlanId(srManager.getPwTransportVlan());
} else {
tBuilder.popVlan();
}
tBuilder.setOutput(sp);
nextObjBuilder.addTreatment(tBuilder.build());
treatmentAdded = true;
// update store
Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
if (existingNeighbors == null) {
existingNeighbors = new HashSet<>();
}
existingNeighbors.add(neighborId);
dstNextHops.put(dst, existingNeighbors);
log.debug("creating treatment for port/label {}/{} in next:{}", sp, edgeLabel, nextId);
}
if (foundSingleNeighbor) {
break;
}
}
}
if (!treatmentAdded) {
log.warn("Could not createGroup from DestinationSet {} without any" + "next hops {}", ds, neighbors);
return;
}
ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("createGroupsFromDestinationSet installed " + "NextObj {} on {}", nextId, deviceId), (objective, error) -> {
log.warn("createGroupsFromDestinationSet failed to install NextObj {} on {}: {}", nextId, deviceId, error);
srManager.invalidateNextObj(objective.id());
});
NextObjective nextObj = nextObjBuilder.add(context);
log.debug(".. createGroupsFromDestinationSet: Submitted " + "next objective {} in device {}", nextId, deviceId);
flowObjectiveService.next(deviceId, nextObj);
// update store
dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds), new NextNeighbors(dstNextHops, nextId));
}
use of org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey in project trellis-control by opennetworkinglab.
the class DefaultGroupHandler method getNextObjectiveId.
/**
* 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 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.
*/
public int getNextObjectiveId(DestinationSet ds, Map<DeviceId, Set<DeviceId>> nextHops, TrafficSelector meta, boolean simple) {
NextNeighbors next = dsNextObjStore.get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
if (next == null) {
log.debug("getNextObjectiveId in device{}: Next objective id " + "not found for {} ... creating", deviceId, ds);
log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}", deviceId, dsNextObjStore.entrySet().stream().filter((nsStoreEntry) -> (nsStoreEntry.getKey().deviceId().equals(deviceId))).collect(Collectors.toList()));
createGroupFromDestinationSet(ds, nextHops, meta, simple);
next = dsNextObjStore.get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
if (next == null) {
log.warn("getNextObjectiveId: unable to create next objective");
// failure in creating group
return -1;
} else {
log.debug("getNextObjectiveId in device{}: Next objective id {} " + "created for {}", deviceId, next.nextId(), ds);
}
} else {
log.trace("getNextObjectiveId in device{}: Next objective id {} " + "found for {}", deviceId, next.nextId(), ds);
// should fix hash groups too if next-hops have changed
if (!next.dstNextHops().equals(nextHops)) {
log.debug("Nexthops have changed for dev:{} nextId:{} ..updating", deviceId, next.nextId());
if (!updateNextHops(ds, nextHops)) {
// failure in updating group
return -1;
}
}
}
return next.nextId();
}
use of org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey in project trellis-control by opennetworkinglab.
the class DefaultGroupHandler method fixHashGroups.
/**
* Checks all the hash-groups in the target-switch meant for the destination
* switch, and either adds or removes buckets to make the neighbor-set
* match the given next-hops. Typically called by the instance leading the programming
* of the destination switch, which may be different from the instance leading the
* programming of the target switch where hash-group changes are made.
*
* @param targetSw the switch in which the hash groups will be edited
* @param nextHops the current next hops for the target switch to reach
* the dest sw
* @param destSw the destination switch
* @param revoke true if hash groups need to remove buckets from the
* the groups to match the current next hops
* @return true if calls are made to edit buckets, or if no edits are required
*/
public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops, DeviceId destSw, boolean revoke) {
// temporary storage of keys to be updated
Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore = new HashMap<>();
boolean foundNextObjective = false, success = true;
// with different neighbors than the given next-hops
for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
if (!dskey.deviceId().equals(targetSw) || !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
continue;
}
foundNextObjective = true;
NextNeighbors nhops = dsNextObjStore.get(dskey);
Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
Integer nextId = nhops.nextId();
if (currNeighbors == null || nextHops == null) {
log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}" + " in targetSw:{} for dstSw:{}", currNeighbors, nextHops, targetSw, destSw);
success &= false;
continue;
}
// 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.debug("Simple next objective cannot be edited to " + "move from {} to {}", currNeighbors, nextHops);
}
continue;
}
Set<DeviceId> diff;
if (revoke) {
diff = Sets.difference(currNeighbors, nextHops);
log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next " + "hops:{} ..removing {}", targetSw, destSw, nextId, currNeighbors, diff);
} else {
diff = Sets.difference(nextHops, currNeighbors);
log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next " + "hops:{} ..adding {}", targetSw, destSw, nextId, currNeighbors, diff);
}
boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId, popVlanInHashGroup(dskey.destinationSet()), revoke);
if (suc) {
// to update neighbor set with changes made
if (revoke) {
tempStore.put(dskey, Sets.difference(currNeighbors, diff));
} else {
tempStore.put(dskey, Sets.union(currNeighbors, diff));
}
}
success &= suc;
}
if (!foundNextObjective) {
log.debug("Cannot find any nextObjectives for route targetSw:{} " + "-> dstSw:{}", targetSw, destSw);
// nothing to do, return false so re-route will be performed
return false;
}
// update the dsNextObjectiveStore with new destinationSet to nextId mappings
for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
NextNeighbors currentNextHops = dsNextObjStore.get(key);
if (currentNextHops == null) {
log.warn("fixHashGroups could not update global store in " + "device {} .. missing nextNeighbors for key {}", deviceId, key);
continue;
}
Set<DeviceId> newNeighbors = new HashSet<>();
newNeighbors.addAll(tempStore.get(key));
Map<DeviceId, Set<DeviceId>> oldDstNextHops = ImmutableMap.copyOf(currentNextHops.dstNextHops());
// local change
currentNextHops.dstNextHops().put(destSw, newNeighbors);
log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}", targetSw, destSw, key, currentNextHops.nextId());
log.debug("Old dstNextHops: {}", oldDstNextHops);
log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
// update global store
dsNextObjStore.put(key, new NextNeighbors(currentNextHops.dstNextHops(), currentNextHops.nextId()));
}
// even if one fails and others succeed, return false so ECMPspg not updated
return success;
}
use of org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey in project trellis-control by opennetworkinglab.
the class DefaultGroupHandler method updateNextHops.
/**
* Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
* that are not already in the store for the given DestinationSet. Note that
* this method does not remove existing next hops for the destinations in the
* DestinationSet.
*
* @param ds the DestinationSet for which the next hops need to be updated
* @param newDstNextHops a map of per-destination next hops to update the
* destinationSet with
* @return true if successful in updating all next hops
*/
private boolean updateNextHops(DestinationSet ds, Map<DeviceId, Set<DeviceId>> newDstNextHops) {
DestinationSetNextObjectiveStoreKey key = new DestinationSetNextObjectiveStoreKey(deviceId, ds);
NextNeighbors currNext = dsNextObjStore.get(key);
Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
// add newDstNextHops to currDstNextHops for each dst
boolean success = true;
for (DeviceId dstSw : ds.getDestinationSwitches()) {
Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
int edgeLabel = ds.getEdgeLabel(dstSw);
int nextId = currNext.nextId();
// new next hops should be added
boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops), edgeLabel, nextId, popVlanInHashGroup(key.destinationSet()), false);
if (suc) {
currNhops.addAll(newNhops);
// this is only a local change
currDstNextHops.put(dstSw, currNhops);
}
success &= suc;
}
if (success) {
// update global store
dsNextObjStore.put(key, new NextNeighbors(currDstNextHops, currNext.nextId()));
log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds, dsNextObjStore.get(key));
}
return success;
}
Aggregations