use of org.onosproject.segmentrouting.mcast.McastRole.EGRESS in project trellis-control by opennetworkinglab.
the class McastHandler method processSinksToBeAdded.
/**
* Process all the sinks related to a mcast group and return
* the ones to be processed.
*
* @param source the source connect point
* @param mcastIp the group address
* @param sinks the sinks to be evaluated
* @return the set of the sinks to be processed
*/
private Set<ConnectPoint> processSinksToBeAdded(ConnectPoint source, IpAddress mcastIp, Map<HostId, Set<ConnectPoint>> sinks) {
final Set<ConnectPoint> sinksToBeProcessed = Sets.newHashSet();
log.debug("Processing sinks to be added for Multicast group {}, source {}", mcastIp, source);
sinks.forEach(((hostId, connectPoints) -> {
// add all connect points that are not tied with any host
if (Objects.equal(HostId.NONE, hostId)) {
sinksToBeProcessed.addAll(connectPoints);
return;
}
// If it has more than 2 locations
if (connectPoints.size() > 2 || connectPoints.size() == 0) {
log.debug("Skip {} since sink {} has {} locations", mcastIp, hostId, connectPoints.size());
return;
}
// If it has one location, just use it
if (connectPoints.size() == 1) {
sinksToBeProcessed.add(connectPoints.stream().findFirst().orElse(null));
return;
}
// We prefer to reuse existing flows
ConnectPoint sinkToBeProcessed = connectPoints.stream().filter(connectPoint -> {
if (!isSinkForGroup(mcastIp, connectPoint, source)) {
return false;
}
if (!isSinkReachable(mcastIp, connectPoint, source)) {
return false;
}
ConnectPoint other = connectPoints.stream().filter(remaining -> !remaining.equals(connectPoint)).findFirst().orElse(null);
// We are already serving the sink
return !isSinkForSource(mcastIp, other, source);
}).findFirst().orElse(null);
if (sinkToBeProcessed != null) {
sinksToBeProcessed.add(sinkToBeProcessed);
return;
}
// Otherwise we prefer to reuse existing egresses
Set<DeviceId> egresses = getDevice(mcastIp, EGRESS, source);
sinkToBeProcessed = connectPoints.stream().filter(connectPoint -> {
if (!egresses.contains(connectPoint.deviceId())) {
return false;
}
if (!isSinkReachable(mcastIp, connectPoint, source)) {
return false;
}
ConnectPoint other = connectPoints.stream().filter(remaining -> !remaining.equals(connectPoint)).findFirst().orElse(null);
return !isSinkForSource(mcastIp, other, source);
}).findFirst().orElse(null);
if (sinkToBeProcessed != null) {
sinksToBeProcessed.add(sinkToBeProcessed);
return;
}
// Otherwise we prefer a location co-located with the source (if it exists)
sinkToBeProcessed = connectPoints.stream().filter(connectPoint -> connectPoint.deviceId().equals(source.deviceId())).findFirst().orElse(null);
if (sinkToBeProcessed != null) {
sinksToBeProcessed.add(sinkToBeProcessed);
return;
}
// Finally, we randomly pick a new location if it is reachable
sinkToBeProcessed = connectPoints.stream().filter(connectPoint -> {
if (!isSinkReachable(mcastIp, connectPoint, source)) {
return false;
}
ConnectPoint other = connectPoints.stream().filter(remaining -> !remaining.equals(connectPoint)).findFirst().orElse(null);
return !isSinkForSource(mcastIp, other, source);
}).findFirst().orElse(null);
if (sinkToBeProcessed != null) {
sinksToBeProcessed.add(sinkToBeProcessed);
}
}));
return sinksToBeProcessed;
}
use of org.onosproject.segmentrouting.mcast.McastRole.EGRESS 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.segmentrouting.mcast.McastRole.EGRESS 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);
});
}
use of org.onosproject.segmentrouting.mcast.McastRole.EGRESS in project trellis-control by opennetworkinglab.
the class McastHandler method isSinkForSource.
/**
* Verify if a given connect point is sink for this group and for this source.
*
* @param mcastIp group address
* @param connectPoint connect point to be verified
* @param source source connect point
* @return true if the connect point is sink of the group
*/
private boolean isSinkForSource(IpAddress mcastIp, ConnectPoint connectPoint, ConnectPoint source) {
boolean isSink = isSinkForGroup(mcastIp, connectPoint, source);
DeviceId device;
if (connectPoint.deviceId().equals(source.deviceId())) {
device = getDevice(mcastIp, INGRESS, source).stream().filter(deviceId -> deviceId.equals(connectPoint.deviceId())).findFirst().orElse(null);
} else {
device = getDevice(mcastIp, EGRESS, source).stream().filter(deviceId -> deviceId.equals(connectPoint.deviceId())).findFirst().orElse(null);
}
return isSink && device != null;
}
Aggregations