Search in sources :

Example 1 with INGRESS

use of org.onosproject.segmentrouting.mcast.McastRole.INGRESS 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();
}
Also used : ConsistentMap(org.onosproject.store.service.ConsistentMap) CoreService(org.onosproject.core.CoreService) PortNumber(org.onosproject.net.PortNumber) LoggerFactory(org.slf4j.LoggerFactory) Tools.groupedThreads(org.onlab.util.Tools.groupedThreads) ForwardingObjective(org.onosproject.net.flowobjective.ForwardingObjective) ConsistentMultimap(org.onosproject.store.service.ConsistentMultimap) Link(org.onosproject.net.Link) SINKS_REMOVED(org.onosproject.mcast.api.McastEvent.Type.SINKS_REMOVED) ConnectPoint(org.onosproject.net.ConnectPoint) HashMultimap(com.google.common.collect.HashMultimap) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Executors.newScheduledThreadPool(java.util.concurrent.Executors.newScheduledThreadPool) McastEvent(org.onosproject.mcast.api.McastEvent) Port(org.onosproject.net.Port) Map(java.util.Map) ApplicationId(org.onosproject.core.ApplicationId) NextObjective(org.onosproject.net.flowobjective.NextObjective) SOURCES_ADDED(org.onosproject.mcast.api.McastEvent.Type.SOURCES_ADDED) INGRESS(org.onosproject.segmentrouting.mcast.McastRole.INGRESS) KryoNamespaces(org.onosproject.store.serializers.KryoNamespaces) SOURCES_REMOVED(org.onosproject.mcast.api.McastEvent.Type.SOURCES_REMOVED) Objects(com.google.common.base.Objects) ROUTE_ADDED(org.onosproject.mcast.api.McastEvent.Type.ROUTE_ADDED) NodeId(org.onosproject.cluster.NodeId) Serializer(org.onosproject.store.service.Serializer) ImmutableSet(com.google.common.collect.ImmutableSet) Device(org.onosproject.net.Device) Collection(java.util.Collection) Set(java.util.Set) Instant(java.time.Instant) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) SINKS_ADDED(org.onosproject.mcast.api.McastEvent.Type.SINKS_ADDED) Versioned(org.onosproject.store.service.Versioned) List(java.util.List) ObjectiveContext(org.onosproject.net.flowobjective.ObjectiveContext) Entry(java.util.Map.Entry) Optional(java.util.Optional) Path(org.onosproject.net.Path) DeviceId(org.onosproject.net.DeviceId) McastRouteUpdate(org.onosproject.mcast.api.McastRouteUpdate) ROUTE_REMOVED(org.onosproject.mcast.api.McastEvent.Type.ROUTE_REMOVED) Multimap(com.google.common.collect.Multimap) KryoNamespace(org.onlab.util.KryoNamespace) AtomicReference(java.util.concurrent.atomic.AtomicReference) Lists(com.google.common.collect.Lists) TRANSIT(org.onosproject.segmentrouting.mcast.McastRole.TRANSIT) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) HostId(org.onosproject.net.HostId) DefaultObjectiveContext(org.onosproject.net.flowobjective.DefaultObjectiveContext) IpAddress(org.onlab.packet.IpAddress) SegmentRoutingManager(org.onosproject.segmentrouting.SegmentRoutingManager) McastRouteData(org.onosproject.mcast.api.McastRouteData) Logger(org.slf4j.Logger) EGRESS(org.onosproject.segmentrouting.mcast.McastRole.EGRESS) Iterator(java.util.Iterator) VlanId(org.onlab.packet.VlanId) Maps(com.google.common.collect.Maps) McastRoute(org.onosproject.mcast.api.McastRoute) TimeUnit(java.util.concurrent.TimeUnit) DistributedSet(org.onosproject.store.service.DistributedSet) Comparator(java.util.Comparator) Collections(java.util.Collections) Path(org.onosproject.net.Path) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) DistributedSet(org.onosproject.store.service.DistributedSet) DeviceId(org.onosproject.net.DeviceId) HostId(org.onosproject.net.HostId) ConnectPoint(org.onosproject.net.ConnectPoint) Collection(java.util.Collection) VlanId(org.onlab.packet.VlanId)

Example 2 with INGRESS

use of org.onosproject.segmentrouting.mcast.McastRole.INGRESS 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);
    });
}
Also used : ConsistentMap(org.onosproject.store.service.ConsistentMap) CoreService(org.onosproject.core.CoreService) PortNumber(org.onosproject.net.PortNumber) LoggerFactory(org.slf4j.LoggerFactory) Tools.groupedThreads(org.onlab.util.Tools.groupedThreads) ForwardingObjective(org.onosproject.net.flowobjective.ForwardingObjective) ConsistentMultimap(org.onosproject.store.service.ConsistentMultimap) Link(org.onosproject.net.Link) SINKS_REMOVED(org.onosproject.mcast.api.McastEvent.Type.SINKS_REMOVED) ConnectPoint(org.onosproject.net.ConnectPoint) HashMultimap(com.google.common.collect.HashMultimap) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Executors.newScheduledThreadPool(java.util.concurrent.Executors.newScheduledThreadPool) McastEvent(org.onosproject.mcast.api.McastEvent) Port(org.onosproject.net.Port) Map(java.util.Map) ApplicationId(org.onosproject.core.ApplicationId) NextObjective(org.onosproject.net.flowobjective.NextObjective) SOURCES_ADDED(org.onosproject.mcast.api.McastEvent.Type.SOURCES_ADDED) INGRESS(org.onosproject.segmentrouting.mcast.McastRole.INGRESS) KryoNamespaces(org.onosproject.store.serializers.KryoNamespaces) SOURCES_REMOVED(org.onosproject.mcast.api.McastEvent.Type.SOURCES_REMOVED) Objects(com.google.common.base.Objects) ROUTE_ADDED(org.onosproject.mcast.api.McastEvent.Type.ROUTE_ADDED) NodeId(org.onosproject.cluster.NodeId) Serializer(org.onosproject.store.service.Serializer) ImmutableSet(com.google.common.collect.ImmutableSet) Device(org.onosproject.net.Device) Collection(java.util.Collection) Set(java.util.Set) Instant(java.time.Instant) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) SINKS_ADDED(org.onosproject.mcast.api.McastEvent.Type.SINKS_ADDED) Versioned(org.onosproject.store.service.Versioned) List(java.util.List) ObjectiveContext(org.onosproject.net.flowobjective.ObjectiveContext) Entry(java.util.Map.Entry) Optional(java.util.Optional) Path(org.onosproject.net.Path) DeviceId(org.onosproject.net.DeviceId) McastRouteUpdate(org.onosproject.mcast.api.McastRouteUpdate) ROUTE_REMOVED(org.onosproject.mcast.api.McastEvent.Type.ROUTE_REMOVED) Multimap(com.google.common.collect.Multimap) KryoNamespace(org.onlab.util.KryoNamespace) AtomicReference(java.util.concurrent.atomic.AtomicReference) Lists(com.google.common.collect.Lists) TRANSIT(org.onosproject.segmentrouting.mcast.McastRole.TRANSIT) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) HostId(org.onosproject.net.HostId) DefaultObjectiveContext(org.onosproject.net.flowobjective.DefaultObjectiveContext) IpAddress(org.onlab.packet.IpAddress) SegmentRoutingManager(org.onosproject.segmentrouting.SegmentRoutingManager) McastRouteData(org.onosproject.mcast.api.McastRouteData) Logger(org.slf4j.Logger) EGRESS(org.onosproject.segmentrouting.mcast.McastRole.EGRESS) Iterator(java.util.Iterator) VlanId(org.onlab.packet.VlanId) Maps(com.google.common.collect.Maps) McastRoute(org.onosproject.mcast.api.McastRoute) TimeUnit(java.util.concurrent.TimeUnit) DistributedSet(org.onosproject.store.service.DistributedSet) Comparator(java.util.Comparator) Collections(java.util.Collections) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) DistributedSet(org.onosproject.store.service.DistributedSet) DeviceId(org.onosproject.net.DeviceId) ConnectPoint(org.onosproject.net.ConnectPoint) Collection(java.util.Collection) Link(org.onosproject.net.Link) VlanId(org.onlab.packet.VlanId)

Example 3 with INGRESS

use of org.onosproject.segmentrouting.mcast.McastRole.INGRESS 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;
}
Also used : ConsistentMap(org.onosproject.store.service.ConsistentMap) CoreService(org.onosproject.core.CoreService) PortNumber(org.onosproject.net.PortNumber) LoggerFactory(org.slf4j.LoggerFactory) Tools.groupedThreads(org.onlab.util.Tools.groupedThreads) ForwardingObjective(org.onosproject.net.flowobjective.ForwardingObjective) ConsistentMultimap(org.onosproject.store.service.ConsistentMultimap) Link(org.onosproject.net.Link) SINKS_REMOVED(org.onosproject.mcast.api.McastEvent.Type.SINKS_REMOVED) ConnectPoint(org.onosproject.net.ConnectPoint) HashMultimap(com.google.common.collect.HashMultimap) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Executors.newScheduledThreadPool(java.util.concurrent.Executors.newScheduledThreadPool) McastEvent(org.onosproject.mcast.api.McastEvent) Port(org.onosproject.net.Port) Map(java.util.Map) ApplicationId(org.onosproject.core.ApplicationId) NextObjective(org.onosproject.net.flowobjective.NextObjective) SOURCES_ADDED(org.onosproject.mcast.api.McastEvent.Type.SOURCES_ADDED) INGRESS(org.onosproject.segmentrouting.mcast.McastRole.INGRESS) KryoNamespaces(org.onosproject.store.serializers.KryoNamespaces) SOURCES_REMOVED(org.onosproject.mcast.api.McastEvent.Type.SOURCES_REMOVED) Objects(com.google.common.base.Objects) ROUTE_ADDED(org.onosproject.mcast.api.McastEvent.Type.ROUTE_ADDED) NodeId(org.onosproject.cluster.NodeId) Serializer(org.onosproject.store.service.Serializer) ImmutableSet(com.google.common.collect.ImmutableSet) Device(org.onosproject.net.Device) Collection(java.util.Collection) Set(java.util.Set) Instant(java.time.Instant) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) SINKS_ADDED(org.onosproject.mcast.api.McastEvent.Type.SINKS_ADDED) Versioned(org.onosproject.store.service.Versioned) List(java.util.List) ObjectiveContext(org.onosproject.net.flowobjective.ObjectiveContext) Entry(java.util.Map.Entry) Optional(java.util.Optional) Path(org.onosproject.net.Path) DeviceId(org.onosproject.net.DeviceId) McastRouteUpdate(org.onosproject.mcast.api.McastRouteUpdate) ROUTE_REMOVED(org.onosproject.mcast.api.McastEvent.Type.ROUTE_REMOVED) Multimap(com.google.common.collect.Multimap) KryoNamespace(org.onlab.util.KryoNamespace) AtomicReference(java.util.concurrent.atomic.AtomicReference) Lists(com.google.common.collect.Lists) TRANSIT(org.onosproject.segmentrouting.mcast.McastRole.TRANSIT) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) HostId(org.onosproject.net.HostId) DefaultObjectiveContext(org.onosproject.net.flowobjective.DefaultObjectiveContext) IpAddress(org.onlab.packet.IpAddress) SegmentRoutingManager(org.onosproject.segmentrouting.SegmentRoutingManager) McastRouteData(org.onosproject.mcast.api.McastRouteData) Logger(org.slf4j.Logger) EGRESS(org.onosproject.segmentrouting.mcast.McastRole.EGRESS) Iterator(java.util.Iterator) VlanId(org.onlab.packet.VlanId) Maps(com.google.common.collect.Maps) McastRoute(org.onosproject.mcast.api.McastRoute) TimeUnit(java.util.concurrent.TimeUnit) DistributedSet(org.onosproject.store.service.DistributedSet) Comparator(java.util.Comparator) Collections(java.util.Collections) DeviceId(org.onosproject.net.DeviceId)

Aggregations

Objects (com.google.common.base.Objects)3 HashMultimap (com.google.common.collect.HashMultimap)3 ImmutableSet (com.google.common.collect.ImmutableSet)3 Lists (com.google.common.collect.Lists)3 Maps (com.google.common.collect.Maps)3 Multimap (com.google.common.collect.Multimap)3 Sets (com.google.common.collect.Sets)3 Instant (java.time.Instant)3 Collection (java.util.Collection)3 Collections (java.util.Collections)3 Comparator (java.util.Comparator)3 Iterator (java.util.Iterator)3 List (java.util.List)3 Map (java.util.Map)3 Entry (java.util.Map.Entry)3 Optional (java.util.Optional)3 Set (java.util.Set)3 Executors.newScheduledThreadPool (java.util.concurrent.Executors.newScheduledThreadPool)3 ScheduledExecutorService (java.util.concurrent.ScheduledExecutorService)3 TimeUnit (java.util.concurrent.TimeUnit)3