use of org.onosproject.segmentrouting.mcast.McastRole.TRANSIT in project trellis-control by opennetworkinglab.
the class McastHandler method recoverFailure.
/**
* General failure recovery procedure.
*
* @param mcastIp the group to recover
* @param failedElement the failed element
*/
private void recoverFailure(IpAddress mcastIp, Object failedElement) {
// Do not proceed if we are not the leaders
if (!mcastUtils.isLeader(mcastIp)) {
log.debug("Skip {} due to lack of leadership", mcastIp);
return;
}
// Skip if it is not an infra failure
Set<DeviceId> transitDevices = getDevice(mcastIp, TRANSIT);
if (!mcastUtils.isInfraFailure(transitDevices, failedElement)) {
log.debug("Skip {} not an infrastructure failure", mcastIp);
return;
}
// Do not proceed if the sources of this group are missing
Set<ConnectPoint> sources = getSources(mcastIp);
if (sources.isEmpty()) {
log.warn("Missing sources for group {}", mcastIp);
return;
}
// Get all the paths, affected paths, good links and good devices
Set<List<Link>> storedPaths = getStoredPaths(mcastIp);
Set<List<Link>> affectedPaths = mcastUtils.getAffectedPaths(storedPaths, failedElement);
Set<Link> goodLinks = Sets.newHashSet();
Map<DeviceId, Set<DeviceId>> goodDevicesBySource = Maps.newHashMap();
Map<DeviceId, Set<ConnectPoint>> processedSourcesByEgress = Maps.newHashMap();
Sets.difference(storedPaths, affectedPaths).forEach(goodPath -> {
goodLinks.addAll(goodPath);
DeviceId srcDevice = goodPath.get(0).src().deviceId();
Set<DeviceId> goodDevices = Sets.newHashSet();
goodPath.forEach(link -> goodDevices.add(link.src().deviceId()));
goodDevicesBySource.compute(srcDevice, (k, v) -> {
v = v == null ? Sets.newHashSet() : v;
v.addAll(goodDevices);
return v;
});
});
affectedPaths.forEach(affectedPath -> {
// TODO remove
log.info("Good links {}", goodLinks);
// TODO remove
log.info("Good devices {}", goodDevicesBySource);
// TODO trace
log.info("Healing the path {}", affectedPath);
DeviceId srcDevice = affectedPath.get(0).src().deviceId();
DeviceId dstDevice = affectedPath.get(affectedPath.size() - 1).dst().deviceId();
// Fix in one shot multiple sources
Set<ConnectPoint> affectedSources = sources.stream().filter(device -> device.deviceId().equals(srcDevice)).collect(Collectors.toSet());
Set<ConnectPoint> processedSources = processedSourcesByEgress.getOrDefault(dstDevice, Collections.emptySet());
Optional<Path> alternativePath = getPath(srcDevice, dstDevice, mcastIp, null);
// If an alternative is possible go ahead
if (alternativePath.isPresent()) {
// TODO trace
log.info("Alternative path {}", alternativePath.get().links());
} else {
// Otherwise try to come up with an alternative
// TODO trace
log.info("No alternative path");
Set<ConnectPoint> notAffectedSources = Sets.difference(sources, affectedSources);
Set<ConnectPoint> remainingSources = Sets.difference(notAffectedSources, processedSources);
alternativePath = recoverSinks(dstDevice, mcastIp, affectedSources, remainingSources);
processedSourcesByEgress.compute(dstDevice, (k, v) -> {
v = v == null ? Sets.newHashSet() : v;
v.addAll(affectedSources);
return v;
});
}
// Recover from the failure if possible
Optional<Path> finalPath = alternativePath;
affectedSources.forEach(affectedSource -> {
// Update the mcastPath store
McastPathStoreKey mcastPathStoreKey = new McastPathStoreKey(mcastIp, affectedSource);
// Verify if there are local sinks
Set<DeviceId> localSinks = getSinks(mcastIp, srcDevice, affectedSource).stream().map(ConnectPoint::deviceId).collect(Collectors.toSet());
Set<DeviceId> goodDevices = goodDevicesBySource.compute(affectedSource.deviceId(), (k, v) -> {
v = v == null ? Sets.newHashSet() : v;
v.addAll(localSinks);
return v;
});
// TODO remove
log.info("Good devices {}", goodDevicesBySource);
Collection<? extends List<Link>> storedPathsBySource = Versioned.valueOrElse(mcastPathStore.get(mcastPathStoreKey), Lists.newArrayList());
Optional<? extends List<Link>> storedPath = storedPathsBySource.stream().filter(path -> path.equals(affectedPath)).findFirst();
// Remove bad links
affectedPath.forEach(affectedLink -> {
DeviceId affectedDevice = affectedLink.src().deviceId();
// If there is overlap with good paths - skip it
if (!goodLinks.contains(affectedLink)) {
removePortFromDevice(affectedDevice, affectedLink.src().port(), mcastIp, mcastUtils.assignedVlan(affectedDevice.equals(affectedSource.deviceId()) ? affectedSource : null));
}
// Remove role on the affected links if last
if (!goodDevices.contains(affectedDevice)) {
mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, affectedDevice, affectedSource));
}
});
// trying with the original object as workaround
if (storedPath.isPresent()) {
mcastPathStore.remove(mcastPathStoreKey, storedPath.get());
} else {
log.warn("Unable to find the corresponding path - trying removeal");
mcastPathStore.remove(mcastPathStoreKey, affectedPath);
}
// Program new links
if (finalPath.isPresent()) {
List<Link> links = finalPath.get().links();
installPath(mcastIp, affectedSource, links);
mcastPathStore.put(mcastPathStoreKey, links);
links.forEach(link -> goodDevices.add(link.src().deviceId()));
goodDevicesBySource.compute(srcDevice, (k, v) -> {
v = v == null ? Sets.newHashSet() : v;
v.addAll(goodDevices);
return v;
});
goodLinks.addAll(finalPath.get().links());
}
});
});
}
use of org.onosproject.segmentrouting.mcast.McastRole.TRANSIT in project trellis-control by opennetworkinglab.
the class McastHandler method processSourcesRemovedInternal.
/**
* Process the SOURCES_REMOVED event.
*
* @param sourcesToBeRemoved the source connect points to be removed
* @param remainingSources the remainig source connect points
* @param mcastIp the group address
* @param sinks the sinks connect points
*/
private void processSourcesRemovedInternal(Set<ConnectPoint> sourcesToBeRemoved, Set<ConnectPoint> remainingSources, IpAddress mcastIp, Map<HostId, Set<ConnectPoint>> sinks) {
lastMcastChange.set(Instant.now());
log.info("Processing sources removed {} for group {}", sourcesToBeRemoved, mcastIp);
if (!mcastUtils.isLeader(mcastIp)) {
log.debug("Skip {} due to lack of leadership", mcastIp);
return;
}
if (remainingSources.isEmpty()) {
log.debug("There are no more sources for {}", mcastIp);
processRouteRemovedInternal(sourcesToBeRemoved, mcastIp);
return;
}
// Let's heal the trees
Set<Link> notAffectedLinks = Sets.newHashSet();
Map<ConnectPoint, Set<Link>> affectedLinks = Maps.newHashMap();
Map<ConnectPoint, Set<ConnectPoint>> candidateSinks = Maps.newHashMap();
Set<ConnectPoint> totalSources = Sets.newHashSet(sourcesToBeRemoved);
totalSources.addAll(remainingSources);
// Calculate all the links used by the sources and the current sinks
totalSources.forEach(source -> {
Set<ConnectPoint> currentSinks = sinks.values().stream().flatMap(Collection::stream).filter(sink -> isSinkForSource(mcastIp, sink, source)).collect(Collectors.toSet());
candidateSinks.put(source, currentSinks);
McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
Collection<? extends List<Link>> storedPaths = Versioned.valueOrElse(mcastPathStore.get(pathStoreKey), Lists.newArrayList());
currentSinks.forEach(currentSink -> {
Optional<? extends List<Link>> currentPath = mcastUtils.getStoredPath(currentSink.deviceId(), storedPaths);
if (currentPath.isPresent()) {
if (!sourcesToBeRemoved.contains(source)) {
notAffectedLinks.addAll(currentPath.get());
} else {
affectedLinks.compute(source, (k, v) -> {
v = v == null ? Sets.newHashSet() : v;
v.addAll(currentPath.get());
return v;
});
}
}
});
});
// Clean transit links
affectedLinks.forEach((source, currentCandidateLinks) -> {
Set<Link> linksToBeRemoved = Sets.difference(currentCandidateLinks, notAffectedLinks).immutableCopy();
if (!linksToBeRemoved.isEmpty()) {
currentCandidateLinks.forEach(link -> {
DeviceId srcLink = link.src().deviceId();
// Remove ports only on links to be removed
if (linksToBeRemoved.contains(link)) {
removePortFromDevice(link.src().deviceId(), link.src().port(), mcastIp, mcastUtils.assignedVlan(srcLink.equals(source.deviceId()) ? source : null));
}
// Remove role on the candidate links
mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, srcLink, source));
});
}
});
// Clean ingress and egress
sourcesToBeRemoved.forEach(source -> {
Set<ConnectPoint> currentSinks = candidateSinks.get(source);
McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
currentSinks.forEach(currentSink -> {
VlanId assignedVlan = mcastUtils.assignedVlan(source.deviceId().equals(currentSink.deviceId()) ? source : null);
// Sinks co-located with the source
if (source.deviceId().equals(currentSink.deviceId())) {
if (source.port().equals(currentSink.port())) {
log.warn("Skip {} since sink {} is on the same port of source {}. Abort", mcastIp, currentSink, source);
return;
}
// We need to check against the other sources and if it is
// necessary remove the port from the device - no overlap
Set<VlanId> otherVlans = remainingSources.stream().filter(remainingSource -> remainingSource.deviceId().equals(source.deviceId()) && candidateSinks.get(remainingSource).contains(currentSink)).map(remainingSource -> mcastUtils.assignedVlan(remainingSource.deviceId().equals(currentSink.deviceId()) ? remainingSource : null)).collect(Collectors.toSet());
if (!otherVlans.contains(assignedVlan)) {
removePortFromDevice(currentSink.deviceId(), currentSink.port(), mcastIp, assignedVlan);
}
mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, currentSink.deviceId(), source));
return;
}
Set<VlanId> otherVlans = remainingSources.stream().filter(remainingSource -> candidateSinks.get(remainingSource).contains(currentSink)).map(remainingSource -> mcastUtils.assignedVlan(remainingSource.deviceId().equals(currentSink.deviceId()) ? remainingSource : null)).collect(Collectors.toSet());
// Sinks on other leaves
if (!otherVlans.contains(assignedVlan)) {
removePortFromDevice(currentSink.deviceId(), currentSink.port(), mcastIp, assignedVlan);
}
mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, currentSink.deviceId(), source));
});
// Clean the mcast paths
mcastPathStore.removeAll(pathStoreKey);
});
}
Aggregations