Fix for the issue: CM routes pointing to spine instead of pg pod after updating netcfg.
DualHomed subnets add and remove issues addressed.

Change-Id: I41c333923e2f2170834d1c83485a418b893f44da
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 8a2e353..b4aaec8 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
@@ -1303,6 +1303,26 @@
         return checkJobs(futures);
     }
 
+    /**
+     * Revoke rules of given subnets in the given switches.
+     *
+     * @param targetSwitches switched from which subnets to be removed
+     * @param subnets subnet bring removed
+     * @return true if succeed
+     */
+    protected boolean revokeSubnet(Set<DeviceId> targetSwitches, Set<IpPrefix> subnets) {
+        List<Future<Boolean>> futures = Lists.newArrayList();
+        for (DeviceId targetSw : targetSwitches) {
+            if (shouldProgram(targetSw)) {
+                futures.add(routePopulators.submit(new RevokeSubnet(targetSw, subnets)));
+            } else {
+                futures.add(CompletableFuture.completedFuture(true));
+            }
+        }
+        // check the execution of each job
+        return checkJobs(futures);
+    }
+
     private final class RevokeSubnet implements PickyCallable<Boolean> {
         private DeviceId targetSw;
         private Set<IpPrefix> subnets;
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index d121cff..6d2b8cc 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -2222,10 +2222,37 @@
                 .map(InterfaceIpAddress::subnetAddress)
                 .collect(Collectors.toSet());
 
-        defaultRoutingHandler.revokeSubnet(
-                ipPrefixSet.stream()
-                        .filter(ipPrefix -> !deviceIpPrefixSet.contains(ipPrefix))
-                        .collect(Collectors.toSet()));
+        Set<IpPrefix> subnetsToBeRevoked = ipPrefixSet.stream()
+                .filter(ipPrefix -> !deviceIpPrefixSet.contains(ipPrefix))
+                .collect(Collectors.toSet());
+
+        // Check if any of the subnets to be revoked is configured in the pairDevice.
+        // If any, repopulate the subnet with pairDevice connectPoint instead of revoking.
+        Optional<DeviceId> pairDevice = getPairDeviceId(cp.deviceId());
+        if (pairDevice.isPresent()) {
+            Set<IpPrefix> pairDeviceIpPrefix = getDeviceSubnetMap().get(pairDevice.get());
+
+            Set<IpPrefix> subnetsExistingInPairDevice = subnetsToBeRevoked.stream()
+                    .filter(ipPrefix -> pairDeviceIpPrefix.contains(ipPrefix))
+                    .collect(Collectors.toSet());
+
+            // Update the subnets existing in pair device with pair device connect point.
+            if (!subnetsExistingInPairDevice.isEmpty()) {
+                // PortNumber of connect point is not relevant in populate subnet and hence providing as ANY.
+                ConnectPoint pairDeviceCp = new ConnectPoint(pairDevice.get(), PortNumber.ANY);
+                log.debug("Updating the subnets: {} with pairDevice connectPoint as it exists in the Pair device: {}",
+                        subnetsExistingInPairDevice, pairDeviceCp);
+                defaultRoutingHandler.populateSubnet(Collections.singleton(pairDeviceCp), subnetsExistingInPairDevice);
+            }
+
+            // Remove only the subnets that are not configured in the pairDevice.
+            subnetsToBeRevoked = Sets.difference(subnetsToBeRevoked, subnetsExistingInPairDevice);
+        }
+
+        if (!subnetsToBeRevoked.isEmpty()) {
+            log.debug("Removing subnets for connectPoint: {}, subnets: {}", cp, subnetsToBeRevoked);
+            defaultRoutingHandler.revokeSubnet(subnetsToBeRevoked);
+        }
 
         // 2. Interface IP punts
         // Remove IP punts for old Intf address
@@ -2259,12 +2286,56 @@
         Set<IpPrefix> deviceIpPrefixSet = deviceIntfIpAddrs.stream()
                 .map(InterfaceIpAddress::subnetAddress)
                 .collect(Collectors.toSet());
+        Set<IpPrefix> subnetsToBePopulated = ipPrefixSet.stream()
+                .filter(ipPrefix -> !deviceIpPrefixSet.contains(ipPrefix))
+                .collect(Collectors.toSet());
 
-        defaultRoutingHandler.populateSubnet(
-                Collections.singleton(cp),
-                ipPrefixSet.stream()
-                        .filter(ipPrefix -> !deviceIpPrefixSet.contains(ipPrefix))
-                        .collect(Collectors.toSet()));
+        if (!subnetsToBePopulated.isEmpty()) {
+            log.debug("Adding subnets for connectPoint: {}, subnets: {}", cp, subnetsToBePopulated);
+
+            // check if pair-device has the same subnet configured?
+            Optional<DeviceId> pairDevice = getPairDeviceId(cp.deviceId());
+            if (pairDevice.isPresent()) {
+                Set<IpPrefix> pairDeviceIpPrefix = getDeviceSubnetMap().get(pairDevice.get());
+
+                Set<IpPrefix>  subnetsToBePopulatedAsDualHomed = subnetsToBePopulated.stream()
+                        .filter(ipPrefix -> pairDeviceIpPrefix.contains(ipPrefix))
+                        .collect(Collectors.toSet());
+                Set<IpPrefix> subnetsToBePopulatedAsSingleHomed = Sets.difference(subnetsToBePopulated,
+                        subnetsToBePopulatedAsDualHomed);
+
+                if (!subnetsToBePopulatedAsSingleHomed.isEmpty()) {
+                    defaultRoutingHandler.populateSubnet(
+                            Collections.singleton(cp),
+                            subnetsToBePopulatedAsSingleHomed);
+                }
+
+                if (!subnetsToBePopulatedAsDualHomed.isEmpty()) {
+                    Set<ConnectPoint> cpts = new HashSet<>();
+                    cpts.add(cp);
+                    // As Subnets is DualHomed adding the pairDevice also as ConnectPoint.
+                    // PortNumber of connect point is not relevant in populate subnet and hence providing as ANY.
+                    ConnectPoint pairCp = new ConnectPoint(pairDevice.get(), PortNumber.ANY);
+                    cpts.add(pairCp);
+
+                    log.debug("Adding DualHomed subnets for connectPoint: {} and its pair device: {}, subnets: {}",
+                            cp, pairCp, subnetsToBePopulatedAsDualHomed);
+
+                    // populating the subnets as DualHomed
+                    defaultRoutingHandler.populateSubnet(
+                            cpts,
+                            subnetsToBePopulated);
+
+                    // revoking the subnets populated in the device as it is now Dualhomed.
+                    defaultRoutingHandler.revokeSubnet(Collections.singleton(cp.deviceId()),
+                            subnetsToBePopulatedAsDualHomed);
+                }
+            } else {
+                defaultRoutingHandler.populateSubnet(
+                        Collections.singleton(cp),
+                        subnetsToBePopulated);
+            }
+        }
 
         // 2. Interface IP punts
         // Add IP punts for new Intf address