Fixing an issue where intents fail to re-route after a node is restarted; caused by failure to register intent resources correctly.

Change-Id: I239e3b538d5b9134422fa629514e095e4914bb0c
diff --git a/core/api/src/main/java/org/onosproject/net/intent/IntentService.java b/core/api/src/main/java/org/onosproject/net/intent/IntentService.java
index 2b1b246..d6e1185 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/IntentService.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/IntentService.java
@@ -66,6 +66,13 @@
     Iterable<Intent> getIntents();
 
     /**
+     * Returns an iterable of intent data objects currently in the system.
+     *
+     * @return set of intent data objects
+     */
+    Iterable<IntentData> getIntentData();
+
+    /**
      * Returns the number of intents currently in the system.
      *
      * @return number of intents
diff --git a/core/api/src/main/java/org/onosproject/net/intent/IntentStoreDelegate.java b/core/api/src/main/java/org/onosproject/net/intent/IntentStoreDelegate.java
index a40ea99..dcb7469 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/IntentStoreDelegate.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/IntentStoreDelegate.java
@@ -29,4 +29,12 @@
      * @param intentData    intent data object
      */
     void process(IntentData intentData);
+
+    /**
+     * Called when a new intent has been updated for which this node is the master.
+     *
+     * @param intentData intent data object
+     */
+    default void onUpdate(IntentData intentData) {
+    }
 }
diff --git a/core/api/src/test/java/org/onosproject/net/intent/FakeIntentManager.java b/core/api/src/test/java/org/onosproject/net/intent/FakeIntentManager.java
index a96967a..a31e986 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/FakeIntentManager.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/FakeIntentManager.java
@@ -183,6 +183,11 @@
     }
 
     @Override
+    public Iterable<IntentData> getIntentData() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public long getIntentCount() {
         return intents.size();
     }
diff --git a/core/api/src/test/java/org/onosproject/net/intent/IntentServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/intent/IntentServiceAdapter.java
index dea1e64..13786b4 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/IntentServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/IntentServiceAdapter.java
@@ -43,6 +43,11 @@
     }
 
     @Override
+    public Iterable<IntentData> getIntentData() {
+        return null;
+    }
+
+    @Override
     public long getIntentCount() {
         return 0;
     }
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
index 62174e0..3c73851 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
@@ -182,6 +182,12 @@
     }
 
     @Override
+    public Iterable<IntentData> getIntentData() {
+        checkPermission(Permission.INTENT_READ);
+        return store.getIntentData(false, 0);
+    }
+
+    @Override
     public long getIntentCount() {
         checkPermission(Permission.INTENT_READ);
 
@@ -258,6 +264,11 @@
         public void process(IntentData data) {
             accumulator.add(data);
         }
+
+        @Override
+        public void onUpdate(IntentData intentData) {
+            trackerService.trackIntent(intentData);
+        }
     }
 
     private void buildAndSubmitBatches(Iterable<Key> intentKeys,
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java b/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java
index 62d67ed..5f6bc5d 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java
@@ -17,6 +17,7 @@
 
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.collect.SetMultimap;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -40,6 +41,7 @@
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.PartitionEvent;
@@ -57,7 +59,9 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -69,7 +73,9 @@
 import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onlab.util.Tools.isNullOrEmpty;
 import static org.onosproject.net.LinkKey.linkKey;
+import static org.onosproject.net.intent.IntentState.INSTALLED;
 import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
 import static org.onosproject.net.link.LinkEvent.Type.LINK_UPDATED;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -84,6 +90,8 @@
 
     private final Logger log = getLogger(getClass());
 
+    private final ConcurrentMap<Key, Intent> intents = Maps.newConcurrentMap();
+
     private final SetMultimap<LinkKey, Key> intentsByLink =
             //TODO this could be slow as a point of synchronization
             synchronizedSetMultimap(HashMultimap.<LinkKey, Key>create());
@@ -195,6 +203,46 @@
         }
     }
 
+    @Override
+    public void trackIntent(IntentData intentData) {
+
+        //NOTE: This will be called for intents that are being added to the store
+        //      locally (i.e. every intent update)
+
+        Key key = intentData.key();
+        Intent intent = intentData.intent();
+        boolean isLocal = intentService.isLocal(key);
+        List<Intent> installables = intentData.installables();
+
+        if (log.isTraceEnabled()) {
+            log.trace("intent {}, old: {}, new: {}, installableCount: {}, resourceCount: {}",
+                      key,
+                      intentsByDevice.values().contains(key),
+                      isLocal,
+                      installables.size(),
+                      intent.resources().size() +
+                          installables.stream()
+                              .mapToLong(i -> i.resources().size()).sum());
+        }
+
+        if (isNullOrEmpty(installables) && intentData.state() == INSTALLED) {
+            log.warn("Intent {} is INSTALLED with no installables", key);
+        }
+
+        if (isLocal) {
+            addTrackedResources(key, intent.resources());
+            for (Intent installable : installables) {
+                addTrackedResources(key, installable.resources());
+            }
+            // FIXME check all resources against current topo service(s); recompile if necessary
+        } else {
+            removeTrackedResources(key, intent.resources());
+            for (Intent installable : installables) {
+                removeTrackedResources(key, installable.resources());
+            }
+        }
+    }
+
     // Internal re-actor to topology change events.
     private class InternalTopologyListener implements TopologyListener {
         @Override
@@ -371,25 +419,11 @@
         }
         try {
             //FIXME very inefficient
-            for (Intent intent : intentService.getIntents()) {
+            for (IntentData intentData : intentService.getIntentData()) {
                 try {
-                    if (intentService.isLocal(intent.key())) {
-                        log.trace("intent {}, old: {}, new: {}",
-                                 intent.key(), intentsByDevice.values().contains(intent.key()), true);
-                        addTrackedResources(intent.key(), intent.resources());
-                        intentService.getInstallableIntents(intent.key()).stream()
-                                .forEach(installable ->
-                                                 addTrackedResources(intent.key(), installable.resources()));
-                    } else {
-                        log.trace("intent {}, old: {}, new: {}",
-                                 intent.key(), intentsByDevice.values().contains(intent.key()), false);
-                        removeTrackedResources(intent.key(), intent.resources());
-                        intentService.getInstallableIntents(intent.key()).stream()
-                                .forEach(installable ->
-                                                 removeTrackedResources(intent.key(), installable.resources()));
-                    }
+                    trackIntent(intentData);
                 } catch (NullPointerException npe) {
-                    log.warn("intent error {}", intent.key(), npe);
+                    log.warn("intent error {}", intentData.key(), npe);
                 }
             }
         } catch (Exception e) {
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTrackerService.java b/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTrackerService.java
index 667803c..b7d367d 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTrackerService.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTrackerService.java
@@ -15,11 +15,12 @@
  */
 package org.onosproject.net.intent.impl;
 
-import java.util.Collection;
-
 import org.onosproject.net.NetworkResource;
+import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.Key;
 
+import java.util.Collection;
+
 /**
  * Auxiliary service for tracking intent path flows and for notifying the
  * intent service of environment changes via topology change delegate.
@@ -59,4 +60,10 @@
     void removeTrackedResources(Key intentKey,
                                        Collection<NetworkResource> resources);
 
+    /**
+     * Submits the specified intent data to be tracked.
+     *
+     * @param intentData intent data object to be tracked
+     */
+    void trackIntent(IntentData intentData);
 }
diff --git a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
index ca75a20..4ddb332 100644
--- a/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java
@@ -28,13 +28,14 @@
 import org.junit.Test;
 import org.onosproject.TestApplicationId;
 import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.common.event.impl.TestEventDispatcher;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.impl.TestCoreManager;
-import org.onosproject.common.event.impl.TestEventDispatcher;
 import org.onosproject.net.NetworkResource;
 import org.onosproject.net.intent.FlowRuleIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentEvent;
 import org.onosproject.net.intent.IntentEvent.Type;
 import org.onosproject.net.intent.IntentExtensionService;
@@ -145,6 +146,11 @@
         public void removeTrackedResources(Key key, Collection<NetworkResource> resources) {
             //TODO
         }
+
+        @Override
+        public void trackIntent(IntentData intentData) {
+            //TODO
+        }
     }
 
     private static class MockInstallableIntent extends FlowRuleIntent {
diff --git a/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java b/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java
index 9412dac..683d058 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java
@@ -288,11 +288,16 @@
     private final class InternalCurrentListener implements
             EventuallyConsistentMapListener<Key, IntentData> {
         @Override
-        public void event(
-                EventuallyConsistentMapEvent<Key, IntentData> event) {
-            if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
-                IntentData intentData = event.value();
+        public void event(EventuallyConsistentMapEvent<Key, IntentData> event) {
+            IntentData intentData = event.value();
 
+            if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
+                // The current intents map has been updated. If we are master for
+                // this intent's partition, notify the Manager that it should
+                // emit notifications about updated tracked resources.
+                if (delegate != null && isMaster(event.value().intent().key())) {
+                    delegate.onUpdate(new IntentData(intentData)); // copy for safety, likely unnecessary
+                }
                 notifyDelegateIfNotNull(IntentEvent.getEvent(intentData));
             }
         }
diff --git a/tools/test/cells/tomx b/tools/test/cells/tomx
index 8522bd5..3e528ee 100644
--- a/tools/test/cells/tomx
+++ b/tools/test/cells/tomx
@@ -7,3 +7,4 @@
 export OCN="10.128.11.4"
 
 export OCT=$OC1
+export ONOS_APPS=drivers,openflow,proxyarp
\ No newline at end of file