CLI command to help debug the mapping of next-Objectives to the
groups that are created by device drivers.

Change-Id: Iff9e04e5e96b2cabbdb40e83215315d2e27791a6
diff --git a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/FlowObjectiveAdapter.java b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/FlowObjectiveAdapter.java
index 11f3667..36c5ca6 100644
--- a/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/FlowObjectiveAdapter.java
+++ b/apps/vtn/sfcmgr/src/test/java/org/onosproject/sfc/util/FlowObjectiveAdapter.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.sfc.util;
 
+import java.util.List;
+
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.flowobjective.FilteringObjective;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
@@ -55,4 +57,9 @@
     public ForwardingObjective forwardingObjective() {
         return forwardingObjective;
     }
+
+    @Override
+    public List<String> getNextMappings() {
+        return null;
+    }
 }
diff --git a/cli/src/main/java/org/onosproject/cli/net/FlowObjectiveNextListCommand.java b/cli/src/main/java/org/onosproject/cli/net/FlowObjectiveNextListCommand.java
new file mode 100644
index 0000000..0b3c09c
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/FlowObjectiveNextListCommand.java
@@ -0,0 +1,33 @@
+package org.onosproject.cli.net;
+
+import java.util.List;
+
+//import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+
+/**
+ * Returns a mapping of FlowObjective next-ids to the groups that get created
+ * by a device driver.
+ */
+@Command(scope = "onos", name = "next-ids",
+        description = "flow-objective next-ids to group-ids mapping")
+public class FlowObjectiveNextListCommand extends AbstractShellCommand {
+
+    /*@Argument(index = 1, name = "uri", description = "Device ID",
+            required = false, multiValued = false)
+    String uri = null;
+     */
+    private static final String FORMAT_MAPPING =
+            "  %s";
+    @Override
+    protected void execute() {
+        FlowObjectiveService service = get(FlowObjectiveService.class);
+        printNexts(service.getNextMappings());
+    }
+
+    private void printNexts(List<String> nextGroupMappings) {
+        nextGroupMappings.forEach(str -> print(FORMAT_MAPPING, str));
+    }
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index a98a38a..a7f533e 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -27,6 +27,10 @@
         </command>
 
         <command>
+            <action class="org.onosproject.cli.net.FlowObjectiveNextListCommand"/>
+        </command>
+
+        <command>
             <action class="org.onosproject.cli.net.FlowObjectiveCompositionCommand"/>
         </command>
 
diff --git a/core/api/src/main/java/org/onosproject/net/behaviour/Pipeliner.java b/core/api/src/main/java/org/onosproject/net/behaviour/Pipeliner.java
index dcfc588..d1604be 100644
--- a/core/api/src/main/java/org/onosproject/net/behaviour/Pipeliner.java
+++ b/core/api/src/main/java/org/onosproject/net/behaviour/Pipeliner.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.net.behaviour;
 
+import java.util.List;
+
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.driver.HandlerBehaviour;
 import org.onosproject.net.flowobjective.FilteringObjective;
@@ -54,4 +56,18 @@
      * @param nextObjective a next objectives
      */
     void next(NextObjective nextObjective);
+
+    /**
+     *  Retrieves a mapping of the nextObjective to the groups in the dataplane,
+     *  and returns it in a form that can be displayed on the CLI. Typically
+     *  group-ids are returned for groups with multiple buckets, where each list element
+     *  represents a bucket. For nextObjectives that are converted to flow-actions,
+     *  an empty list is returned.
+     *
+     *  @param nextGroup representation of the nextObjective. This representation
+     *                   is stored in the distributed group store
+     *  @return a list of preformatted strings representing group information, or
+     *          an empty list if no groups were created
+     */
+    List<String> getNextMappings(NextGroup nextGroup);
 }
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 415264e..6e905de 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
@@ -16,6 +16,9 @@
 package org.onosproject.net.flowobjective;
 
 import com.google.common.annotations.Beta;
+
+import java.util.List;
+
 import org.onosproject.net.DeviceId;
 
 /**
@@ -82,4 +85,17 @@
             throw new UnsupportedOperationException("Unsupported objective of type " + objective.getClass());
         }
     }
+
+    /**
+     * 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
+     * between next-ids and group-ids done by device-drivers.
+     *
+     * @return a list of strings preformatted by the device-drivers to provide
+     *         information on next-id to group-id mapping. Consumed by the
+     *         "next-ids" command on the CLI.
+     */
+    List<String> getNextMappings();
 }
diff --git a/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveStore.java b/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveStore.java
index 85dec0f..a0a7634 100644
--- a/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveStore.java
+++ b/core/api/src/main/java/org/onosproject/net/flowobjective/FlowObjectiveStore.java
@@ -16,6 +16,9 @@
 package org.onosproject.net.flowobjective;
 
 import com.google.common.annotations.Beta;
+
+import java.util.Map;
+
 import org.onosproject.net.behaviour.NextGroup;
 import org.onosproject.store.Store;
 
@@ -53,6 +56,13 @@
     NextGroup removeNextGroup(Integer nextId);
 
     /**
+     * Fetch all groups from the store and their mapping to nextIds.
+     *
+     * @return a map that represents the current snapshot of Next-ids to NextGroups
+     */
+    Map<Integer, NextGroup> getAllGroups();
+
+    /**
      * Allocates a next objective id. This id is globally unique
      *
      * @return an integer
diff --git a/core/api/src/test/java/org/onosproject/net/behaviour/PipelinerAdapter.java b/core/api/src/test/java/org/onosproject/net/behaviour/PipelinerAdapter.java
index 73e83cd..203bbbe 100644
--- a/core/api/src/test/java/org/onosproject/net/behaviour/PipelinerAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/behaviour/PipelinerAdapter.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.net.behaviour;
 
+import java.util.List;
+
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.driver.DriverData;
 import org.onosproject.net.driver.DriverHandler;
@@ -65,4 +67,9 @@
     public void setData(DriverData data) {
 
     }
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        return null;
+    }
 }
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 d82b6f7..b7287c7 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
@@ -30,6 +30,7 @@
 import org.onosproject.mastership.MastershipListener;
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.NextGroup;
 import org.onosproject.net.behaviour.Pipeliner;
 import org.onosproject.net.behaviour.PipelinerContext;
 import org.onosproject.net.device.DeviceEvent;
@@ -53,7 +54,9 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -123,6 +126,10 @@
 
     private Map<Integer, Set<PendingNext>> pendingForwards = Maps.newConcurrentMap();
 
+    // local store to track which nextObjectives were sent to which device
+    // for debugging purposes
+    private Map<Integer, DeviceId> nextToDevice = Maps.newConcurrentMap();
+
     private ExecutorService executorService;
 
     @Activate
@@ -143,6 +150,7 @@
         executorService.shutdown();
         pipeliners.clear();
         driverHandlers.clear();
+        nextToDevice.clear();
         log.info("Stopped");
     }
 
@@ -215,6 +223,7 @@
     @Override
     public void next(DeviceId deviceId, NextObjective nextObjective) {
         checkPermission(FLOWRULE_WRITE);
+        nextToDevice.put(nextObjective.id(), deviceId);
         executorService.submit(new ObjectiveInstaller(deviceId, nextObjective));
     }
 
@@ -443,4 +452,33 @@
             return false;
         }
     }
+
+    @Override
+    public List<String> getNextMappings() {
+        List<String> mappings = new ArrayList<>();
+        Map<Integer, NextGroup> allnexts = flowObjectiveStore.getAllGroups();
+        // XXX if the NextGroup upon decoding 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());
+            mappings.add("NextId " + e.getKey() + ": " +
+                    ((deviceId != null) ? deviceId : "nextId not in this onos instance"));
+            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);
+                }
+            }
+        }
+        return mappings;
+    }
 }
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 2041b5b..b0bca13 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
@@ -436,4 +436,10 @@
         str += ")";
         return str;
     }
+
+    @Override
+    public List<String> getNextMappings() {
+        // TODO Implementation deferred as this is an experimental component.
+        return null;
+    }
 }
diff --git a/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveStoreAdapter.java b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveStoreAdapter.java
index 378ea73..d73cc59 100644
--- a/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveStoreAdapter.java
+++ b/core/net/src/test/java/org/onosproject/net/flowobjective/impl/FlowObjectiveStoreAdapter.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.net.flowobjective.impl;
 
+import java.util.Map;
+
 import org.onosproject.net.behaviour.NextGroup;
 import org.onosproject.net.flowobjective.FlowObjectiveStore;
 import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
@@ -57,4 +59,9 @@
     public boolean hasDelegate() {
         return false;
     }
+
+    @Override
+    public Map<Integer, NextGroup> getAllGroups() {
+        return null;
+    }
 }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/flowobjective/impl/DistributedFlowObjectiveStore.java b/core/store/dist/src/main/java/org/onosproject/store/flowobjective/impl/DistributedFlowObjectiveStore.java
index 75a9b89..534289c 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/flowobjective/impl/DistributedFlowObjectiveStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/flowobjective/impl/DistributedFlowObjectiveStore.java
@@ -37,6 +37,9 @@
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Manages the inventory of created next groups.
  */
@@ -106,6 +109,18 @@
     }
 
     @Override
+    public Map<Integer, NextGroup> getAllGroups() {
+        Map<Integer, NextGroup> nextGroupMappings = new HashMap<>();
+        for (int key : nextGroups.keySet()) {
+            NextGroup nextGroup = getNextGroup(key);
+            if (nextGroup != null) {
+                nextGroupMappings.put(key, nextGroup);
+            }
+        }
+        return nextGroupMappings;
+    }
+
+    @Override
     public int allocateNextId() {
         return (int) nextIds.incrementAndGet();
     }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/CentecV350Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/CentecV350Pipeline.java
index 9203a00..6cafdb4 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/CentecV350Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/CentecV350Pipeline.java
@@ -74,6 +74,7 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executors;
@@ -634,4 +635,10 @@
         }
 
     }
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        // TODO Implementation deferred to vendor
+        return null;
+    }
 }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/DefaultSingleTablePipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/DefaultSingleTablePipeline.java
index a58aeff..8e37746 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/DefaultSingleTablePipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/DefaultSingleTablePipeline.java
@@ -17,6 +17,7 @@
 
 import org.onlab.osgi.ServiceDirectory;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.NextGroup;
 import org.onosproject.net.behaviour.Pipeliner;
 import org.onosproject.net.behaviour.PipelinerContext;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
@@ -39,6 +40,8 @@
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.List;
+
 /**
  * Simple single table pipeline abstraction.
  */
@@ -167,4 +170,10 @@
     public void next(NextObjective nextObjective) {
     }
 
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        // Default single table pipeline does not use nextObjectives or groups
+        return null;
+    }
+
 }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java
index 2b27f0e..72fe8aa 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java
@@ -1029,4 +1029,28 @@
     protected static void fail(Objective obj, ObjectiveError error) {
         obj.context().ifPresent(context -> context.onError(obj, error));
     }
+
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        List<String> mappings = new ArrayList<>();
+        List<Deque<GroupKey>> gkeys = appKryo.deserialize(nextGroup.data());
+        for (Deque<GroupKey> gkd : gkeys) {
+            Group lastGroup = null;
+            String gchain = "";
+            for (GroupKey gk : gkd) {
+                Group g = groupService.getGroup(deviceId, gk);
+                gchain += "  0x" + Integer.toHexString(g.id().id()) + " -->";
+                lastGroup = g;
+            }
+            // add port information for last group in group-chain
+            for (Instruction i: lastGroup.buckets().buckets().get(0).treatment().allInstructions()) {
+                if (i instanceof OutputInstruction) {
+                    gchain += " port:" + ((OutputInstruction) i).port();
+                }
+            }
+            mappings.add(gchain);
+        }
+        return mappings;
+    }
 }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OVSCorsaPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OVSCorsaPipeline.java
index 7e9dfec..b47012d 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OVSCorsaPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OVSCorsaPipeline.java
@@ -72,6 +72,7 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executors;
@@ -857,4 +858,10 @@
         }
 
     }
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        // TODO Implementation deferred to vendor
+        return null;
+    }
 }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
index 8c1307a..0d78def 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/OltPipeline.java
@@ -702,4 +702,10 @@
         }
 
     }
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        // TODO Implementation deferred to vendor
+        return null;
+    }
 }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java
index 14b729c..65c3b72 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java
@@ -537,4 +537,10 @@
             return appKryo.serialize(nextActions);
         }
     }
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        // TODO Implementation deferred to vendor
+        return null;
+    }
 }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java
index 55e8bbf..0379c0e 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java
@@ -57,6 +57,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 import static org.onlab.util.Tools.delay;
@@ -467,4 +468,10 @@
 
     }
 
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        // nextObjectives converted to flow-actions not groups
+        return Collections.emptyList();
+    }
+
 }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
index 50b439f..0d54239 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
@@ -1126,4 +1126,10 @@
         }
 
     }
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        // TODO Implementation deferred to vendor
+        return null;
+    }
 }