REST API for obj-nextids

Change-Id: Ibad03de0a6ffa8a478037120c84edfe6722f4d9b
(cherry picked from commit 0acb326963dda08a5eef16134eca5cba84b8e75d)
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java
index c4e2031..0a5a509 100644
--- a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java
@@ -22,6 +22,8 @@
 import com.google.common.cache.RemovalNotification;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.lang3.tuple.Pair;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.incubator.net.virtual.AbstractVnetService;
@@ -172,6 +174,11 @@
     }
 
     @Override
+    public Map<Pair<Integer, DeviceId>, List<String>> getNextMappingsChain() {
+        return ImmutableMap.of();
+    }
+
+    @Override
     public List<String> getNextMappings() {
         List<String> mappings = new ArrayList<>();
         Map<Integer, NextGroup> allnexts = flowObjectiveStore.getAllGroups();
diff --git a/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java b/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java
index 103691c..11a7f5f 100644
--- a/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java
+++ b/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveService.java
@@ -24,6 +24,7 @@
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Maps;
 import org.apache.commons.lang.NotImplementedException;
+import org.apache.commons.lang3.tuple.Pair;
 import org.onosproject.net.DeviceId;
 
 /**
@@ -93,6 +94,18 @@
 
     /**
      * Retrieve all nextObjective to group mappings known to this onos instance,
+     * in a format meant for display via REST API, to help with debugging. Applications
+     * are only aware of next-Ids, while the group sub-system is only aware of group-ids.
+     * This method fills in the gap by providing information on the mapping
+     * between next-ids and group-ids done by device-drivers.
+     *
+     * @return a map of key as a pair of next-id and Device id to group-id mapping.
+     * Consumed by the REST API.
+     */
+    Map<Pair<Integer, DeviceId>, List<String>> getNextMappingsChain();
+
+    /**
+     * Retrieve all nextObjective to group mappings known to this onos instance,
      * in a format meant for display on the CLI, to help with debugging. Applications
      * are only aware of next-Ids, while the group sub-system is only aware of group-ids.
      * This method fills in the gap by providing information on the mapping
diff --git a/core/api/src/test/java/org/onosproject/net/flowobjective/FlowObjectiveServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/flowobjective/FlowObjectiveServiceAdapter.java
index 360c1f2..0b17ce8 100644
--- a/core/api/src/test/java/org/onosproject/net/flowobjective/FlowObjectiveServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/flowobjective/FlowObjectiveServiceAdapter.java
@@ -17,7 +17,10 @@
 package org.onosproject.net.flowobjective;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import org.onosproject.net.DeviceId;
+import org.apache.commons.lang3.tuple.Pair;
+import java.util.Map;
 
 import java.util.List;
 
@@ -65,4 +68,10 @@
     public List<String> getPendingFlowObjectives() {
         return ImmutableList.of();
     }
+
+    @Override
+    public Map<Pair<Integer, DeviceId>, List<String>> getNextMappingsChain() {
+        return ImmutableMap.of();
+    }
+
 }
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
index d0bd1c5..de7d785 100644
--- a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
@@ -16,9 +16,11 @@
 package org.onosproject.net.flowobjective.impl;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import org.apache.commons.lang3.tuple.Pair;
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.util.ItemNotFoundException;
@@ -65,6 +67,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.HashMap;
 import java.util.concurrent.ExecutorService;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -738,6 +741,38 @@
     }
 
     @Override
+    public Map<Pair<Integer, DeviceId>, List<String>> getNextMappingsChain() {
+        Map<Pair<Integer, DeviceId>, List<String>> nextObjGroupMap = new HashMap<>();
+        Map<Integer, NextGroup> allnexts = flowObjectiveStore.getAllGroups();
+
+        // XXX if the NextGroup after de-serialization actually stored info of the deviceId
+        // then info on any nextObj could be retrieved from one controller instance.
+        // Right now the drivers on one instance can only fetch for next-ids that came
+        // to them.
+        // Also, we still need to send the right next-id to the right driver as potentially
+        // there can be different drivers for different devices. But on that account,
+        // no instance should be decoding for another instance's nextIds.
+
+        for (Map.Entry<Integer, NextGroup> e : allnexts.entrySet()) {
+            // get the device this next Objective was sent to
+            DeviceId deviceId = nextToDevice.get(e.getKey());
+                if (deviceId != null) {
+                // this instance of the controller sent the nextObj to a driver
+                Pipeliner pipeliner = getDevicePipeliner(deviceId);
+                List<String> nextMappings = pipeliner.getNextMappings(e.getValue());
+                if (nextMappings != null) {
+                    //mappings.addAll(nextMappings);
+                    nextObjGroupMap.put(Pair.of(e.getKey(), deviceId), nextMappings);
+                }
+            } else {
+               nextObjGroupMap.put(Pair.of(e.getKey(), deviceId), ImmutableList.of("nextId not in this onos instance"));
+            }
+        }
+        return nextObjGroupMap;
+    }
+
+
+    @Override
     public List<String> getPendingFlowObjectives() {
         List<String> pendingFlowObjectives = new ArrayList<>();
 
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
index e09f8a1..dc5ef05 100644
--- a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.flowobjective.impl.composition;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.onlab.osgi.DefaultServiceDirectory;
@@ -25,7 +26,6 @@
 import org.onosproject.mastership.MastershipEvent;
 import org.onosproject.mastership.MastershipListener;
 import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.DeviceId;
 import org.onosproject.net.behaviour.Pipeliner;
 import org.onosproject.net.behaviour.PipelinerContext;
 import org.onosproject.net.device.DeviceEvent;
@@ -57,6 +57,8 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onosproject.net.DeviceId;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.concurrent.Executors.newFixedThreadPool;
@@ -430,6 +432,11 @@
     }
 
     @Override
+    public Map<Pair<Integer, DeviceId>, List<String>> getNextMappingsChain() {
+        return ImmutableMap.of();
+    }
+
+    @Override
     public List<String> getPendingFlowObjectives() {
         // TODO Implementation deferred as this is an experimental component.
         return ImmutableList.of();
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java b/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java
index a6227bc..906e27c 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/CoreWebApplication.java
@@ -38,6 +38,7 @@
                 HostsWebResource.class,
                 IntentsWebResource.class,
                 FlowsWebResource.class,
+                FlowObjectiveNextListWebResource.class,
                 GroupsWebResource.class,
                 MetersWebResource.class,
                 TopologyWebResource.class,
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveNextListWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveNextListWebResource.java
new file mode 100644
index 0000000..4e9b427
--- /dev/null
+++ b/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveNextListWebResource.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.rest.resources;
+
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.List;
+import java.util.Map;
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * Get Flow objective next list.
+ */
+@Path("nextobjectives")
+public class FlowObjectiveNextListWebResource extends AbstractWebResource {
+
+    private final Logger log = getLogger(getClass());
+
+    /**
+     * To get all obj-next-Ids.
+     * @return 200 OK with flow objective Next ids.
+     * @onos.rsModel NextObjectives
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getObjNextids() {
+        ObjectNode node = getGroupChainByIdJsonOutput(null, null);
+        return Response.status(200).entity(node).build();
+    }
+
+    /**
+     * Returns all group-chains associated with the given nextId.
+     *
+     * @param nextId nextid mapping
+     * @return 200 OK with array of all the group chain.
+     *
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("nextobjective/{nextId}")
+    public Response getGroupChainByNextid(@PathParam("nextId") String nextId) {
+        ObjectNode node = getGroupChainByIdJsonOutput(Integer.parseInt(nextId), null);
+        return Response.status(200).entity(node).build();
+    }
+
+
+    /**
+     * Returns all group-chains associated with the given deviceId.
+     *
+     * @param deviceId deviceId mapping
+     * @return 200 OK with array of all the group chain.
+     *
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("{deviceId}")
+    public Response getGroupChainByDeviceId(@PathParam("deviceId") String deviceId) {
+        ObjectNode node = getGroupChainByIdJsonOutput(null, DeviceId.deviceId(deviceId));
+        return Response.status(200).entity(node).build();
+    }
+
+    private ObjectNode getGroupChainByIdJsonOutput(Integer nextId, DeviceId deviceId) {
+        ObjectNode root = mapper().createObjectNode();
+        ArrayNode connectionArray = mapper().createArrayNode();
+        FlowObjectiveService service = get(FlowObjectiveService.class);
+        Map<Pair<Integer, DeviceId>, List<String>> nextObjGroupMap = service.getNextMappingsChain();
+
+        if (nextId == null && deviceId == null) {
+            nextObjGroupMap.forEach((key, value) -> {
+                ObjectNode mappingNode = mapper().createObjectNode();
+                String keyString = String.format("NextId %s: %s", key.getLeft(), key.getRight());
+                mappingNode.put(keyString, value.toString());
+                connectionArray.add(mappingNode);
+            });
+        } else {
+            nextObjGroupMap.forEach((key, value) -> {
+                ObjectNode mappingNode = mapper().createObjectNode();
+                if ((key.getLeft().equals(nextId)) || (key.getRight().equals(deviceId))) {
+                    List groupchain = value;
+                    if (deviceId != null && groupchain != null) {
+                        String keyString = String.format("NextId %s:", key.getLeft());
+                        mappingNode.put(keyString, groupchain.toString());
+                    } else if (groupchain != null) {
+                        mappingNode.put("groupChain", groupchain.toString());
+                    }
+                    connectionArray.add(mappingNode);
+                }
+            });
+        }
+        root.put("obj-next-ids", connectionArray);
+
+        return root;
+    }
+
+
+}
diff --git a/web/api/src/main/resources/definitions/NextObjectives.json b/web/api/src/main/resources/definitions/NextObjectives.json
new file mode 100644
index 0000000..686e452
--- /dev/null
+++ b/web/api/src/main/resources/definitions/NextObjectives.json
@@ -0,0 +1,25 @@
+{
+  "type": "object",
+  "title": "obj-next-ids",
+  "required": [
+    "obj-next-ids"
+  ],
+  "properties": {
+    "devices": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "title": "nextId",
+        "required": [
+          "groupChain"
+        ],
+        "properties": {
+          "groupChain": {
+            "type": "array",
+            "example": "[0x1234 --> 0x1246 --> port :1, 0x1234 --> 0x12786 --> port :2] "
+          }
+        }
+      }
+    }
+  }
+}