Trigger pipeconf deploy right after registration

Without waiting for the next pipeconf watchdog periodic probe.
To support this, this patch extends the PiPipeconfService to advertise
pipeconf registration events.

Change-Id: Ib44f1813bd37083c666a5e7980de320ce469c2d2
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfManager.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfManager.java
index 3070d66..8fdbfc8 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfManager.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfManager.java
@@ -23,6 +23,7 @@
 import org.onlab.util.HexString;
 import org.onlab.util.ItemNotFoundException;
 import org.onlab.util.SharedExecutors;
+import org.onosproject.event.AbstractListenerManager;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.config.basics.BasicDeviceConfig;
@@ -36,6 +37,8 @@
 import org.onosproject.net.driver.DriverProvider;
 import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.service.PiPipeconfEvent;
+import org.onosproject.net.pi.service.PiPipeconfListener;
 import org.onosproject.net.pi.service.PiPipeconfMappingStore;
 import org.onosproject.net.pi.service.PiPipeconfService;
 import org.osgi.service.component.annotations.Activate;
@@ -68,7 +71,9 @@
  */
 @Component(immediate = true, service = PiPipeconfService.class)
 @Beta
-public class PiPipeconfManager implements PiPipeconfService {
+public class PiPipeconfManager
+        extends AbstractListenerManager<PiPipeconfEvent, PiPipeconfListener>
+        implements PiPipeconfService {
 
     private final Logger log = getLogger(getClass());
 
@@ -100,6 +105,7 @@
     @Activate
     public void activate() {
         driverAdminService.addListener(driverListener);
+        eventDispatcher.addSink(PiPipeconfEvent.class, listenerRegistry);
         checkMissingMergedDrivers();
         if (!missingMergedDrivers.isEmpty()) {
             // Missing drivers should be created upon detecting registration
@@ -114,6 +120,7 @@
 
     @Deactivate
     public void deactivate() {
+        eventDispatcher.removeSink(PiPipeconfEvent.class);
         executor.shutdown();
         driverAdminService.removeListener(driverListener);
         pipeconfs.clear();
@@ -133,10 +140,11 @@
         log.info("New pipeconf registered: {} (fingerprint={})",
                  pipeconf.id(), HexString.toHexString(pipeconf.fingerprint()));
         executor.execute(() -> attemptMergeAll(pipeconf.id()));
+        post(new PiPipeconfEvent(PiPipeconfEvent.Type.REGISTERED, pipeconf));
     }
 
     @Override
-    public void remove(PiPipeconfId pipeconfId) throws IllegalStateException {
+    public void unregister(PiPipeconfId pipeconfId) throws IllegalStateException {
         checkNotNull(pipeconfId);
         // TODO add mechanism to remove from device.
         if (!pipeconfs.containsKey(pipeconfId)) {
@@ -147,6 +155,7 @@
         final PiPipeconf pipeconf = pipeconfs.remove(pipeconfId);
         log.info("Unregistered pipeconf: {} (fingerprint={})",
                  pipeconfId, HexString.toHexString(pipeconf.fingerprint()));
+        post(new PiPipeconfEvent(PiPipeconfEvent.Type.UNREGISTERED, pipeconfId));
     }
 
     @Override
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfWatchdogManager.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfWatchdogManager.java
index e76d673..21df778 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfWatchdogManager.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfWatchdogManager.java
@@ -35,6 +35,8 @@
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.service.PiPipeconfEvent;
+import org.onosproject.net.pi.service.PiPipeconfListener;
 import org.onosproject.net.pi.service.PiPipeconfMappingStore;
 import org.onosproject.net.pi.service.PiPipeconfService;
 import org.onosproject.net.pi.service.PiPipeconfWatchdogEvent;
@@ -57,6 +59,7 @@
 
 import java.util.Dictionary;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.ExecutorService;
@@ -75,11 +78,11 @@
  * pipeline.
  */
 @Component(
-    immediate = true,
-    service = PiPipeconfWatchdogService.class,
-    property = {
-        PWM_PROBE_INTERVAL + ":Integer=" + PWM_PROBE_INTERVAL_DEFAULT
-    }
+        immediate = true,
+        service = PiPipeconfWatchdogService.class,
+        property = {
+                PWM_PROBE_INTERVAL + ":Integer=" + PWM_PROBE_INTERVAL_DEFAULT
+        }
 )
 public class PiPipeconfWatchdogManager
         extends AbstractListenerManager<PiPipeconfWatchdogEvent, PiPipeconfWatchdogListener>
@@ -107,13 +110,16 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     private ComponentConfigService componentConfigService;
 
-    /** Configure interval in seconds for device pipeconf probing. */
+    /**
+     * Configure interval in seconds for device pipeconf probing.
+     */
     private int probeInterval = PWM_PROBE_INTERVAL_DEFAULT;
 
     protected ExecutorService executor = Executors.newFixedThreadPool(
             30, groupedThreads("onos/pipeconf-watchdog", "%d", log));
 
-    private final InternalDeviceListener deviceListener = new InternalDeviceListener();
+    private final DeviceListener deviceListener = new InternalDeviceListener();
+    private final PiPipeconfListener pipeconfListener = new InternalPipeconfListener();
 
     private Timer timer;
     private TimerTask task;
@@ -141,8 +147,9 @@
         // Start periodic watchdog task.
         timer = new Timer();
         startProbeTask();
-        // Add device listener.
+        // Add listeners.
         deviceService.addListener(deviceListener);
+        pipeconfService.addListener(pipeconfListener);
         log.info("Started");
     }
 
@@ -167,6 +174,7 @@
     @Deactivate
     public void deactivate() {
         eventDispatcher.removeSink(PiPipeconfWatchdogEvent.class);
+        pipeconfService.removeListener(pipeconfListener);
         deviceService.removeListener(deviceListener);
         stopProbeTask();
         timer = null;
@@ -205,7 +213,8 @@
             }
 
             if (!pipeconfService.getPipeconf(pipeconfId).isPresent()) {
-                log.error("Pipeconf {} is not registered", pipeconfId);
+                log.warn("Pipeconf {} is not registered, skipping probe for {}",
+                         pipeconfId, device.id());
                 return;
             }
 
@@ -356,6 +365,19 @@
         }
     }
 
+    private class InternalPipeconfListener implements PiPipeconfListener {
+        @Override
+        public void event(PiPipeconfEvent event) {
+            pipeconfMappingStore.getDevices(event.subject())
+                    .forEach(PiPipeconfWatchdogManager.this::triggerProbe);
+        }
+
+        @Override
+        public boolean isRelevant(PiPipeconfEvent event) {
+            return Objects.equals(event.type(), PiPipeconfEvent.Type.REGISTERED);
+        }
+    }
+
     private class StatusMapListener
             implements EventuallyConsistentMapListener<DeviceId, PipelineStatus> {