CORD-60 Support dynamic vSG creation/deletion

We no longer need to configure /32 IP in interfaces.
SR will push a per-host route when discovering a host
with IP address(es) that does not belong to configured subnet.

Also includes:
- HostHandler refactoring

Change-Id: Ic1ad42d1ccdfee32be85f49e6fc94d9026000ffc
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index 9f2e84e..1324795 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -15,11 +15,13 @@
  */
 package org.onosproject.segmentrouting;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpPrefix;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
@@ -45,9 +47,9 @@
  * routing rule population.
  */
 public class DefaultRoutingHandler {
-
-    private static Logger log = LoggerFactory
-            .getLogger(DefaultRoutingHandler.class);
+    private static final int MAX_RETRY_ATTEMPTS = 5;
+    private static final String ECMPSPG_MISSING = "ECMP shortest path graph not found";
+    private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
 
     private SegmentRoutingManager srManager;
     private RoutingRulePopulator rulePopulator;
@@ -56,7 +58,6 @@
     private DeviceConfiguration config;
     private final Lock statusLock = new ReentrantLock();
     private volatile Status populationStatus;
-    private static final int MAX_RETRY_ATTEMPTS = 5;
     private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
 
     /**
@@ -113,7 +114,7 @@
                 }
 
                 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(sw.id(), srManager);
-                if (!populateEcmpRoutingRules(sw.id(), ecmpSpg)) {
+                if (!populateEcmpRoutingRules(sw.id(), ecmpSpg, ImmutableSet.of())) {
                     log.debug("populateAllRoutingRules: populationStatus is ABORTED");
                     populationStatus = Status.ABORTED;
                     log.debug("Abort routing rule population");
@@ -210,7 +211,7 @@
             if (link.size() == 1) {
                 log.trace("repopulateRoutingRulesForRoutes: running ECMP graph for device {}", link.get(0));
                 EcmpShortestPathGraph ecmpSpg = new EcmpShortestPathGraph(link.get(0), srManager);
-                if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
+                if (populateEcmpRoutingRules(link.get(0), ecmpSpg, ImmutableSet.of())) {
                     log.debug("Populating flow rules from {} to all is successful",
                               link.get(0));
                     currentEcmpSpgMap.put(link.get(0), ecmpSpg);
@@ -255,7 +256,8 @@
                                 nextHops.add(via.get(0));
                             }
                         }
-                        if (!populateEcmpRoutingRulePartial(targetSw, dst, nextHops)) {
+                        if (!populateEcmpRoutingRulePartial(targetSw, dst,
+                                nextHops, ImmutableSet.of())) {
                             return false;
                         }
                         log.debug("Populating flow rules from {} to {} is successful",
@@ -422,8 +424,17 @@
         return subLinks;
     }
 
+    /**
+     * Populate ECMP rules for subnets from all switches to destination.
+     *
+     * @param destSw Device ID of destination switch
+     * @param ecmpSPG ECMP shortest path graph
+     * @param subnets Subnets to be populated. If empty, populate all configured subnets.
+     * @return true if succeed
+     */
     private boolean populateEcmpRoutingRules(DeviceId destSw,
-                                             EcmpShortestPathGraph ecmpSPG) {
+                                             EcmpShortestPathGraph ecmpSPG,
+                                             Set<Ip4Prefix> subnets) {
 
         HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia = ecmpSPG
                 .getAllLearnedSwitchesAndVia();
@@ -440,7 +451,7 @@
                         nextHops.add(via.get(0));
                     }
                 }
-                if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops)) {
+                if (!populateEcmpRoutingRulePartial(targetSw, destSw, nextHops, subnets)) {
                     return false;
                 }
             }
@@ -449,9 +460,19 @@
         return true;
     }
 
+    /**
+     * Populate ECMP rules for subnets from target to destination via nexthops.
+     *
+     * @param targetSw Device ID of target switch
+     * @param destSw Device ID of destination switch
+     * @param nextHops List of next hops
+     * @param subnets Subnets to be populated. If empty, populate all configured subnets.
+     * @return true if succeed
+     */
     private boolean populateEcmpRoutingRulePartial(DeviceId targetSw,
                                                    DeviceId destSw,
-                                                   Set<DeviceId> nextHops) {
+                                                   Set<DeviceId> nextHops,
+                                                   Set<Ip4Prefix> subnets) {
         boolean result;
 
         if (nextHops.isEmpty()) {
@@ -473,13 +494,11 @@
         }
 
         if (targetIsEdge && destIsEdge) {
-            Set<Ip4Prefix> subnets = config.getSubnets(destSw);
+            subnets = (subnets != null && !subnets.isEmpty()) ? subnets : config.getSubnets(destSw);
             log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
                       targetSw, destSw, subnets);
-            result = rulePopulator.populateIpRuleForSubnet(targetSw,
-                                                           subnets,
-                                                           destSw,
-                                                           nextHops);
+            result = rulePopulator.populateIpRuleForSubnet(targetSw, subnets,
+                                                           destSw, nextHops);
             if (!result) {
                 return false;
             }
@@ -575,18 +594,54 @@
         }
     }
 
-    public void purgeEcmpGraph(DeviceId deviceId) {
+    /**
+     * Populate rules of given subnet at given location.
+     *
+     * @param cp connect point of the subnet being added
+     * @param subnets subnet being added
+     * @return true if succeed
+     */
+    protected boolean populateSubnet(ConnectPoint cp, Set<Ip4Prefix> subnets) {
+        statusLock.lock();
+        try {
+            EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(cp.deviceId());
+            if (ecmpSpg == null) {
+                log.warn("Fail to populating subnet {}: {}", subnets, ECMPSPG_MISSING);
+                return false;
+            }
+            return populateEcmpRoutingRules(cp.deviceId(), ecmpSpg, subnets);
+        } finally {
+            statusLock.unlock();
+        }
+    }
+
+    /**
+     * Revoke rules of given subnet at given location.
+     *
+     * @param subnets subnet being removed
+     * @return true if succeed
+     */
+    protected boolean revokeSubnet(Set<Ip4Prefix> subnets) {
+        statusLock.lock();
+        try {
+            return srManager.routingRulePopulator.revokeIpRuleForSubnet(subnets);
+        } finally {
+            statusLock.unlock();
+        }
+    }
+
+    protected void purgeEcmpGraph(DeviceId deviceId) {
         currentEcmpSpgMap.remove(deviceId);
         if (updatedEcmpSpgMap != null) {
             updatedEcmpSpgMap.remove(deviceId);
         }
     }
 
-    private class RetryFilters implements Runnable {
+    private final class RetryFilters implements Runnable {
         int attempts = MAX_RETRY_ATTEMPTS;
         DeviceId devId;
 
-        public RetryFilters(DeviceId deviceId) {
+        private RetryFilters(DeviceId deviceId) {
             devId = deviceId;
         }