use of org.onosproject.net.Path in project trellis-control by opennetworkinglab.
the class McastHandler method recoverSinks.
/**
* Try to recover sinks using alternative locations.
*
* @param notRecovered the device not recovered
* @param mcastIp the group address
* @param affectedSources affected sources
* @param goodSources sources not affected
*/
private Optional<Path> recoverSinks(DeviceId notRecovered, IpAddress mcastIp, Set<ConnectPoint> affectedSources, Set<ConnectPoint> goodSources) {
log.debug("Processing recover sinks on {} for group {}", notRecovered, mcastIp);
Map<ConnectPoint, Set<ConnectPoint>> affectedSinksBySource = Maps.newHashMap();
Map<ConnectPoint, Set<ConnectPoint>> sinksBySource = Maps.newHashMap();
Set<ConnectPoint> sources = Sets.union(affectedSources, goodSources);
// Hosts influenced by the failure
Map<HostId, Set<ConnectPoint>> hostIdSetMap = mcastUtils.getAffectedSinks(notRecovered, mcastIp);
// Locations influenced by the failure
Set<ConnectPoint> affectedSinks = hostIdSetMap.values().stream().flatMap(Collection::stream).filter(connectPoint -> connectPoint.deviceId().equals(notRecovered)).collect(Collectors.toSet());
// All locations
Set<ConnectPoint> sinks = hostIdSetMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
// Maps sinks with the sources
sources.forEach(source -> {
Set<ConnectPoint> currentSinks = affectedSinks.stream().filter(sink -> isSinkForSource(mcastIp, sink, source)).collect(Collectors.toSet());
affectedSinksBySource.put(source, currentSinks);
});
// Remove sinks one by one if they are not used by other sources
affectedSources.forEach(affectedSource -> {
Set<ConnectPoint> currentSinks = affectedSinksBySource.get(affectedSource);
log.info("Current sinks {} for source {}", currentSinks, affectedSource);
currentSinks.forEach(currentSink -> {
VlanId assignedVlan = mcastUtils.assignedVlan(affectedSource.deviceId().equals(currentSink.deviceId()) ? affectedSource : null);
log.info("Assigned vlan {}", assignedVlan);
Set<VlanId> otherVlans = goodSources.stream().filter(remainingSource -> affectedSinksBySource.get(remainingSource).contains(currentSink)).map(remainingSource -> mcastUtils.assignedVlan(remainingSource.deviceId().equals(currentSink.deviceId()) ? remainingSource : null)).collect(Collectors.toSet());
log.info("Other vlans {}", otherVlans);
// Sinks on other leaves
if (!otherVlans.contains(assignedVlan)) {
removePortFromDevice(currentSink.deviceId(), currentSink.port(), mcastIp, assignedVlan);
}
mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, currentSink.deviceId(), affectedSource));
});
});
// Get the sinks to be added and the new egress
Set<DeviceId> newEgress = Sets.newHashSet();
affectedSources.forEach(affectedSource -> {
Set<ConnectPoint> currentSinks = affectedSinksBySource.get(affectedSource);
Set<ConnectPoint> newSinks = Sets.difference(sinks, currentSinks);
sinksBySource.put(affectedSource, newSinks);
newSinks.stream().map(ConnectPoint::deviceId).forEach(newEgress::add);
});
log.info("newEgress {}", newEgress);
// If there are more than one new egresses, return the problem
if (newEgress.size() != 1) {
log.warn("There are {} new egress, wrong configuration. Abort.", newEgress.size());
return Optional.empty();
}
DeviceId egress = newEgress.stream().findFirst().orElse(null);
DeviceId ingress = affectedSources.stream().map(ConnectPoint::deviceId).findFirst().orElse(null);
log.info("Ingress {}", ingress);
if (ingress == null) {
log.warn("No new ingress, wrong configuration. Abort.");
return Optional.empty();
}
// Get an alternative path
Optional<Path> alternativePath = getPath(ingress, egress, mcastIp, null);
// If there are new path install sinks and return path
if (alternativePath.isPresent()) {
log.info("Alternative path {}", alternativePath.get().links());
affectedSources.forEach(affectedSource -> {
Set<ConnectPoint> newSinks = sinksBySource.get(affectedSource);
newSinks.forEach(newSink -> {
addPortToDevice(newSink.deviceId(), newSink.port(), mcastIp, mcastUtils.assignedVlan(null));
mcastRoleStore.put(new McastRoleStoreKey(mcastIp, newSink.deviceId(), affectedSource), EGRESS);
});
});
return alternativePath;
}
// No new path but sinks co-located with sources install sinks and return empty
if (ingress.equals(egress)) {
log.info("No Alternative path but sinks co-located");
affectedSources.forEach(affectedSource -> {
Set<ConnectPoint> newSinks = sinksBySource.get(affectedSource);
newSinks.forEach(newSink -> {
if (affectedSource.port().equals(newSink.port())) {
log.warn("Skip {} since sink {} is on the same port of source {}. Abort", mcastIp, newSink, affectedSource);
return;
}
addPortToDevice(newSink.deviceId(), newSink.port(), mcastIp, mcastUtils.assignedVlan(affectedSource));
mcastRoleStore.put(new McastRoleStoreKey(mcastIp, newSink.deviceId(), affectedSource), INGRESS);
});
});
}
return Optional.empty();
}
use of org.onosproject.net.Path in project trellis-control by opennetworkinglab.
the class McastHandler method getPath.
/**
* Gets a path from src to dst.
* If a path was allocated before, returns the allocated path.
* Otherwise, randomly pick one from available paths.
*
* @param src source device ID
* @param dst destination device ID
* @param mcastIp multicast group
* @param allPaths paths list
*
* @return an optional path from src to dst
*/
private Optional<Path> getPath(DeviceId src, DeviceId dst, IpAddress mcastIp, List<Path> allPaths) {
if (allPaths == null) {
allPaths = mcastUtils.getPaths(src, dst, Collections.emptySet());
}
if (allPaths.isEmpty()) {
return Optional.empty();
}
// Create a map index of suitability-to-list of paths. For example
// a path in the list associated to the index 1 shares only one link
// and it is less suitable of a path belonging to the index 2
Map<Integer, List<Path>> eligiblePaths = Maps.newHashMap();
int score;
// Let's build the multicast tree
Set<List<Link>> storedPaths = getStoredPaths(mcastIp);
Set<Link> storedTree = storedPaths.stream().flatMap(Collection::stream).collect(Collectors.toSet());
log.trace("Stored tree {}", storedTree);
Set<Link> pathLinks;
for (Path path : allPaths) {
if (!src.equals(path.links().get(0).src().deviceId())) {
continue;
}
pathLinks = Sets.newHashSet(path.links());
score = Sets.intersection(pathLinks, storedTree).size();
// score defines the index
if (score > 0) {
eligiblePaths.compute(score, (index, paths) -> {
paths = paths == null ? Lists.newArrayList() : paths;
paths.add(path);
return paths;
});
}
}
if (eligiblePaths.isEmpty()) {
log.trace("No eligiblePath(s) found from {} to {}", src, dst);
Collections.shuffle(allPaths);
return allPaths.stream().findFirst();
}
// Let's take the best ones
Integer bestIndex = eligiblePaths.keySet().stream().sorted(Comparator.reverseOrder()).findFirst().orElse(null);
List<Path> bestPaths = eligiblePaths.get(bestIndex);
log.trace("{} eligiblePath(s) found from {} to {}", bestPaths.size(), src, dst);
Collections.shuffle(bestPaths);
return bestPaths.stream().findFirst();
}
use of org.onosproject.net.Path in project trellis-control by opennetworkinglab.
the class DefaultL2TunnelHandler method getPath.
/**
* Returns the path betwwen two connect points.
*
* @param srcCp source connect point
* @param dstCp destination connect point
* @return the path
*/
private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
// use SRLinkWeigher to avoid pair links, and also
// avoid going from the spine to the leaf and to the
// spine again, we need to have the leaf as CP1 here.
LinkWeigher srLw = new SRLinkWeigher(srManager, srcCp.deviceId(), new HashSet<Link>());
Set<Path> paths = srManager.topologyService.getPaths(srManager.topologyService.currentTopology(), srcCp.deviceId(), dstCp.deviceId(), srLw);
log.debug("Paths obtained from topology service {}", paths);
// We randomly pick a path.
if (paths.isEmpty()) {
return null;
}
int size = paths.size();
int index = RandomUtils.nextInt(0, size);
List<Link> result = Iterables.get(paths, index).links();
log.debug("Randomly picked a path {}", result);
return result;
}
use of org.onosproject.net.Path in project trellis-control by opennetworkinglab.
the class McastUtils method exploreMcastTree.
/**
* Go through all the paths, looking for shared links to be used
* in the final path computation.
*
* @param egresses egress devices
* @param availablePaths all the available paths towards the egress
* @return shared links between egress devices
*/
private Set<Link> exploreMcastTree(Set<DeviceId> egresses, Map<DeviceId, List<Path>> availablePaths) {
int minLength = Integer.MAX_VALUE;
int length;
List<Path> currentPaths;
// Verify the source can still reach all the egresses
for (DeviceId egress : egresses) {
// From the source we cannot reach all the sinks
// just continue and let's figure out after
currentPaths = availablePaths.get(egress);
if (currentPaths.isEmpty()) {
continue;
}
// Get the length of the first one available, update the min length
length = currentPaths.get(0).links().size();
if (length < minLength) {
minLength = length;
}
}
// If there are no paths
if (minLength == Integer.MAX_VALUE) {
return Collections.emptySet();
}
int index = 0;
Set<Link> sharedLinks = Sets.newHashSet();
Set<Link> currentSharedLinks;
Set<Link> currentLinks;
DeviceId egressToRemove = null;
// Let's find out the shared links
while (index < minLength) {
// Initialize the intersection with the paths related to the first egress
currentPaths = availablePaths.get(egresses.stream().findFirst().orElse(null));
currentSharedLinks = Sets.newHashSet();
// Iterate over the paths and take the "index" links
for (Path path : currentPaths) {
currentSharedLinks.add(path.links().get(index));
}
// Iterate over the remaining egress
for (DeviceId egress : egresses) {
// Iterate over the paths and take the "index" links
currentLinks = Sets.newHashSet();
for (Path path : availablePaths.get(egress)) {
currentLinks.add(path.links().get(index));
}
// Do intersection
currentSharedLinks = Sets.intersection(currentSharedLinks, currentLinks);
// we have to retry with a subset of sinks
if (currentSharedLinks.isEmpty()) {
egressToRemove = egress;
index = minLength;
break;
}
}
sharedLinks.addAll(currentSharedLinks);
index++;
}
// we can still build optimal subtrees
if (sharedLinks.isEmpty() && egresses.size() > 1 && egressToRemove != null) {
egresses.remove(egressToRemove);
sharedLinks = exploreMcastTree(egresses, availablePaths);
}
return sharedLinks;
}
use of org.onosproject.net.Path in project trellis-control by opennetworkinglab.
the class McastUtils method getPaths.
/**
* Gets path from src to dst computed using the custom link weigher.
*
* @param src source device ID
* @param dst destination device ID
* @param linksToEnforce links to be enforced
* @return list of paths from src to dst
*/
List<Path> getPaths(DeviceId src, DeviceId dst, Set<Link> linksToEnforce) {
final Topology currentTopology = topologyService.currentTopology();
final LinkWeigher linkWeigher = new SRLinkWeigher(srManager, src, linksToEnforce);
List<Path> allPaths = Lists.newArrayList(topologyService.getPaths(currentTopology, src, dst, linkWeigher));
log.trace("{} path(s) found from {} to {}", allPaths.size(), src, dst);
return allPaths;
}
Aggregations