Prioritize certain type of routes among others

FPM, STATIC and configured subnets go to high priority batch, while others (e.g. DHCP) go to low priority batch

Change-Id: I8dcef67945c31bd0eab869510bea0f1f278b2925
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index 149e837..f456e66 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.Sets;
 
 import org.onlab.packet.EthType;
+import com.google.common.collect.Streams;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpPrefix;
@@ -59,6 +60,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -694,56 +696,82 @@
                               route.get(0), route.get(1), nhops);
                     perDstNextHops.put(route.get(1), nhops);
                 });
-                Set<IpPrefix> ipDev1 = (subnets == null) ? config.getSubnets(ep.dev1)
-                                                         : subnets;
-                Set<IpPrefix> ipDev2 = (subnets == null) ? config.getSubnets(ep.dev2)
-                                                         : subnets;
-                ipDev1 = (ipDev1 == null) ? Sets.newHashSet() : ipDev1;
-                ipDev2 = (ipDev2 == null) ? Sets.newHashSet() : ipDev2;
+
+                List<Set<IpPrefix>> batchedSubnetDev1, batchedSubnetDev2;
+                if (subnets != null) {
+                    batchedSubnetDev1 = Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(subnets));
+                    batchedSubnetDev2 = Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(subnets));
+                } else {
+                    batchedSubnetDev1 = config.getBatchedSubnets(ep.dev1);
+                    batchedSubnetDev2 = config.getBatchedSubnets(ep.dev2);
+                }
+                List<Set<IpPrefix>> batchedSubnetBoth = Streams
+                        .zip(batchedSubnetDev1.stream(), batchedSubnetDev2.stream(), (a, b) -> Sets.intersection(a, b))
+                        .filter(set -> !set.isEmpty())
+                        .collect(Collectors.toList());
+                List<Set<IpPrefix>> batchedSubnetDev1Only = Streams
+                        .zip(batchedSubnetDev1.stream(), batchedSubnetDev2.stream(), (a, b) -> Sets.difference(a, b))
+                        .filter(set -> !set.isEmpty())
+                        .collect(Collectors.toList());
+                List<Set<IpPrefix>> batchedSubnetDev2Only = Streams
+                        .zip(batchedSubnetDev1.stream(), batchedSubnetDev2.stream(), (a, b) -> Sets.difference(b, a))
+                        .filter(set -> !set.isEmpty())
+                        .collect(Collectors.toList());
+
                 Set<DeviceId> nhDev1 = perDstNextHops.get(ep.dev1);
                 Set<DeviceId> nhDev2 = perDstNextHops.get(ep.dev2);
+
                 // handle routing to subnets common to edge-pair
                 // only if the targetSw is not part of the edge-pair and there
                 // exists a next hop to at least one of the devices in the edge-pair
                 if (!ep.includes(targetSw)
-                        && ((nhDev1 != null && !nhDev1.isEmpty())
-                                || (nhDev2 != null && !nhDev2.isEmpty()))) {
-                    if (!populateEcmpRoutingRulePartial(
-                             targetSw,
-                             ep.dev1, ep.dev2,
-                             perDstNextHops,
-                             Sets.intersection(ipDev1, ipDev2))) {
-                        return false; // abort everything and fail fast
+                        && ((nhDev1 != null && !nhDev1.isEmpty()) || (nhDev2 != null && !nhDev2.isEmpty()))) {
+                    log.trace("getSubnets on both {} and {}: {}", ep.dev1, ep.dev2, batchedSubnetBoth);
+                    for (Set<IpPrefix> prefixes : batchedSubnetBoth) {
+                        if (!populateEcmpRoutingRulePartial(
+                                targetSw,
+                                ep.dev1, ep.dev2,
+                                perDstNextHops,
+                                prefixes)) {
+                            return false; // abort everything and fail fast
+                        }
                     }
+
                 }
                 // handle routing to subnets that only belong to dev1 only if
                 // a next-hop exists from the target to dev1
-                Set<IpPrefix> onlyDev1Subnets = Sets.difference(ipDev1, ipDev2);
-                if (!onlyDev1Subnets.isEmpty()
-                        && nhDev1 != null  && !nhDev1.isEmpty()) {
+                if (!batchedSubnetDev1Only.isEmpty() &&
+                        batchedSubnetDev1Only.stream().anyMatch(subnet -> !subnet.isEmpty()) &&
+                        nhDev1 != null  && !nhDev1.isEmpty()) {
                     Map<DeviceId, Set<DeviceId>> onlyDev1NextHops = new HashMap<>();
                     onlyDev1NextHops.put(ep.dev1, nhDev1);
-                    if (!populateEcmpRoutingRulePartial(
-                            targetSw,
-                            ep.dev1, null,
-                            onlyDev1NextHops,
-                            onlyDev1Subnets)) {
-                        return false; // abort everything and fail fast
+                    log.trace("getSubnets on {} only: {}", ep.dev1, batchedSubnetDev1Only);
+                    for (Set<IpPrefix> prefixes : batchedSubnetDev1Only) {
+                        if (!populateEcmpRoutingRulePartial(
+                                targetSw,
+                                ep.dev1, null,
+                                onlyDev1NextHops,
+                                prefixes)) {
+                            return false; // abort everything and fail fast
+                        }
                     }
                 }
                 // handle routing to subnets that only belong to dev2 only if
                 // a next-hop exists from the target to dev2
-                Set<IpPrefix> onlyDev2Subnets = Sets.difference(ipDev2, ipDev1);
-                if (!onlyDev2Subnets.isEmpty()
-                        && nhDev2 != null && !nhDev2.isEmpty()) {
+                if (!batchedSubnetDev2Only.isEmpty() &&
+                        batchedSubnetDev2Only.stream().anyMatch(subnet -> !subnet.isEmpty()) &&
+                        nhDev2 != null && !nhDev2.isEmpty()) {
                     Map<DeviceId, Set<DeviceId>> onlyDev2NextHops = new HashMap<>();
                     onlyDev2NextHops.put(ep.dev2, nhDev2);
-                    if (!populateEcmpRoutingRulePartial(
-                            targetSw,
-                            ep.dev2, null,
-                            onlyDev2NextHops,
-                            onlyDev2Subnets)) {
-                        return false; // abort everything and fail fast
+                    log.trace("getSubnets on {} only: {}", ep.dev2, batchedSubnetDev2Only);
+                    for (Set<IpPrefix> prefixes : batchedSubnetDev2Only) {
+                        if (!populateEcmpRoutingRulePartial(
+                                targetSw,
+                                ep.dev2, null,
+                                onlyDev2NextHops,
+                                prefixes)) {
+                            return false; // abort everything and fail fast
+                        }
                     }
                 }
             }
@@ -860,9 +888,12 @@
         }
 
         if (targetIsEdge && dest1IsEdge) {
-            subnets = (subnets != null && !subnets.isEmpty())
-                            ? Sets.newHashSet(subnets)
-                            : Sets.newHashSet(config.getSubnets(destSw1));
+            List<Set<IpPrefix>> batchedSubnets;
+            if (subnets != null && !subnets.isEmpty()) {
+                batchedSubnets = Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(subnets));
+            } else {
+                batchedSubnets = config.getBatchedSubnets(destSw1);
+            }
             // XXX - Rethink this - ignoring routerIPs in all other switches
             // even edge to edge switches
             /*subnets.add(dest1RouterIpv4.toIpPrefix());
@@ -875,15 +906,15 @@
                     subnets.add(dest2RouterIpv6.toIpPrefix());
                 }
             }*/
-            log.debug(". populateEcmpRoutingRulePartial in device {} towards {} {} "
-                    + "for subnets {}", targetSw, destSw1,
-                                        (destSw2 != null) ? ("& " + destSw2) : "",
-                                        subnets);
-            result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
-                                                           destSw1, destSw2,
-                                                           nextHops);
-            if (!result) {
-                return false;
+            log.trace("getSubnets on {}: {}", destSw1, batchedSubnets);
+            for (Set<IpPrefix> prefixes : batchedSubnets) {
+                log.debug(". populateEcmpRoutingRulePartial in device {} towards {} {} "
+                                + "for subnets {}", targetSw, destSw1,
+                        (destSw2 != null) ? ("& " + destSw2) : "",
+                        prefixes);
+                if (!rulePopulator.populateIpRuleForSubnet(targetSw, prefixes, destSw1, destSw2, nextHops)) {
+                    return false;
+                }
             }
         }