Phased recovery

- Implemented a set of CLI commands
    - Enable/disable group of ports
    - List recovery phase of each device
    - Force a specific device to enter given phase
- Return CompletableFuture in RRP
- Introduce completeAfter method in Tools
- Introduce submit method in PredictableExecutor which returns a CompletableFuture

Change-Id: I60b0fb7b67e392b33b52d908d2b53f7acbddc565
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 12bd9e4..5f11501 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
@@ -104,6 +104,7 @@
 import org.onosproject.segmentrouting.mcast.McastRole;
 import org.onosproject.segmentrouting.mcast.McastRoleStoreKey;
 import org.onosproject.segmentrouting.mcast.McastStoreKey;
+import org.onosproject.segmentrouting.phasedrecovery.api.PhasedRecoveryService;
 import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
 import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
 import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelHandler;
@@ -261,6 +262,10 @@
             policy = ReferencePolicy.DYNAMIC)
     public volatile XconnectService xconnectService;
 
+    @Reference(cardinality = ReferenceCardinality.OPTIONAL,
+            policy = ReferencePolicy.DYNAMIC)
+    volatile PhasedRecoveryService phasedRecoveryService;
+
     /** Enable active probing to discover dual-homed hosts. */
     boolean activeProbing = ACTIVE_PROBING_DEFAULT;
 
@@ -1049,6 +1054,21 @@
     }
 
     @Override
+    public boolean isRoutingStable() {
+        return defaultRoutingHandler.isRoutingStable();
+    }
+
+    @Override
+    public void initHost(DeviceId deviceId) {
+        hostEventExecutor.execute(() -> hostHandler.init(deviceId));
+    }
+
+    @Override
+    public void initRoute(DeviceId deviceId) {
+        routeEventExecutor.execute(() -> routeHandler.init(deviceId));
+    }
+
+    @Override
     public ApplicationId appId() {
         return appId;
     }
@@ -1146,6 +1166,25 @@
         return Optional.ofNullable(deviceConfig).map(SegmentRoutingDeviceConfig::pairLocalPort);
     }
 
+    @Override
+    public Set<PortNumber> getInfraPorts(DeviceId deviceId) {
+        return deviceService.getPorts(deviceId).stream()
+                .map(port -> new ConnectPoint(port.element().id(), port.number()))
+                .filter(cp -> interfaceService.getInterfacesByPort(cp).isEmpty())
+                .map(ConnectPoint::port)
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public Set<PortNumber> getEdgePorts(DeviceId deviceId) {
+        return deviceService.getPorts(deviceId).stream()
+                .map(port -> new ConnectPoint(port.element().id(), port.number()))
+                .filter(cp -> !interfaceService.getInterfacesByPort(cp).isEmpty() &&
+                        !cp.port().equals(getPairLocalPort(deviceId).orElse(null)))
+                .map(ConnectPoint::port)
+                .collect(Collectors.toSet());
+    }
+
     /**
      * Returns locations of given resolved route.
      *
@@ -1570,11 +1609,10 @@
             DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId);
             groupHandler.createGroupsFromVlanConfig();
             routingRulePopulator.populateSubnetBroadcastRule(deviceId);
+            phasedRecoveryService.init(deviceId);
         }
 
         appCfgHandler.init(deviceId);
-        hostEventExecutor.execute(() -> hostHandler.init(deviceId));
-        routeEventExecutor.execute(() -> routeHandler.init(deviceId));
     }
 
     private void processDeviceRemoved(Device device) {
@@ -1610,6 +1648,8 @@
         // done after all rerouting or rehashing has been completed
         groupHandlerMap.entrySet()
             .forEach(entry -> entry.getValue().cleanUpForNeighborDown(device.id()));
+
+        phasedRecoveryService.reset(device.id());
     }
 
     /**