CORD-1304 Set of changes for fabric routing to optimize use of ECMP groups

Also removing some old demo code in the SR app
Adding a couple of CLI commands for debugging
Bug fix in the DistributedGroupStore for group_exists error message
Bug fixes for ofdpa driver:
    - synchronized update of flowObjectiveStore when buckets are added to or removed from groups
      to avoid one thread from overwriting an update from another thread doing an update at the same time
    - addBucketToL2FloodGroup now updates flowObjectiveStore after accounting for changes
    - addBucketToHashGroup accounts for all added buckets, not just the first one

Change-Id: I6207c1c3c1b4379986805d73a73bc460fea8fe3f
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
index 06f7861..b2f4fd1 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
@@ -17,6 +17,7 @@
 package org.onosproject.segmentrouting.grouphandler;
 
 import org.onosproject.net.DeviceId;
+import org.slf4j.Logger;
 
 import java.util.HashSet;
 import java.util.Objects;
@@ -24,47 +25,56 @@
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Representation of a set of neighbor switch dpids along with edge node
- * label. Meant to be used as a lookup-key in a hash-map to retrieve an
- * ECMP-group that hashes packets to a set of ports connecting to the
- * neighbors in this set.
+ * label and a destination switch. Meant to be used as a lookup-key in a hash-map
+ * to retrieve an ECMP-group that hashes packets to a set of ports connecting to
+ * the neighbors in this set towards a specific destination switch.
  */
 public class NeighborSet {
     private final Set<DeviceId> neighbors;
     private final int edgeLabel;
     public static final int NO_EDGE_LABEL = -1;
     private boolean mplsSet;
+    // the destination switch towards which the neighbors are the next-hops.
+    private final DeviceId dstSw;
+    protected static final Logger log = getLogger(NeighborSet.class);
 
     /**
      * Constructor with set of neighbors. Edge label is
      * default to -1.
      *
-     * @param neighbors set of neighbors to be part of neighbor set
+     * @param neighbors set of neighbors representing the next-hops
      * @param isMplsSet indicates if it is a mpls neighbor set
+     * @param dstSw the destination switch
      */
-    public NeighborSet(Set<DeviceId> neighbors, boolean isMplsSet) {
+    public NeighborSet(Set<DeviceId> neighbors, boolean isMplsSet, DeviceId dstSw) {
         checkNotNull(neighbors);
         this.edgeLabel = NO_EDGE_LABEL;
         this.neighbors = new HashSet<>();
         this.neighbors.addAll(neighbors);
         this.mplsSet = isMplsSet;
+        this.dstSw = dstSw;
     }
 
     /**
      * Constructor with set of neighbors and edge label.
      *
-     * @param neighbors set of neighbors to be part of neighbor set
+     * @param neighbors set of neighbors representing the next-hops
      * @param isMplsSet indicates if it is a mpls neighbor set
      * @param edgeLabel label to be pushed as part of group operation
+     * @param dstSw the destination switch
      */
-    public NeighborSet(Set<DeviceId> neighbors, boolean isMplsSet, int edgeLabel) {
+    public NeighborSet(Set<DeviceId> neighbors, boolean isMplsSet,
+                       int edgeLabel, DeviceId dstSw) {
         checkNotNull(neighbors);
         this.edgeLabel = edgeLabel;
         this.neighbors = new HashSet<>();
         this.neighbors.addAll(neighbors);
         this.mplsSet = isMplsSet;
+        this.dstSw = dstSw;
     }
 
     /**
@@ -74,35 +84,39 @@
         this.edgeLabel = NO_EDGE_LABEL;
         this.neighbors = new HashSet<>();
         this.mplsSet = true;
+        this.dstSw = DeviceId.NONE;
     }
 
     /**
      * Factory method for NeighborSet hierarchy.
      *
      * @param random the expected behavior.
-     * @param neighbors the set of neighbors to be part of neighbor set
+     * @param neighbors the set of neighbors representing the next-hops
      * @param isMplsSet indicates if it is a mpls neighbor set
+     * @param dstSw the destination switch
      * @return the neighbor set object.
      */
-    public static NeighborSet neighborSet(boolean random, Set<DeviceId> neighbors, boolean isMplsSet) {
-        return random ?
-                new RandomNeighborSet(neighbors) :
-                new NeighborSet(neighbors, isMplsSet);
+    public static NeighborSet neighborSet(boolean random, Set<DeviceId> neighbors,
+                                          boolean isMplsSet, DeviceId dstSw) {
+        return random ? new RandomNeighborSet(neighbors, dstSw)
+                      : new NeighborSet(neighbors, isMplsSet, dstSw);
     }
 
     /**
      * Factory method for NeighborSet hierarchy.
      *
      * @param random the expected behavior.
-     * @param neighbors the set of neighbors to be part of neighbor set
+     * @param neighbors the set of neighbors representing the next-hops
      * @param isMplsSet indicates if it is a mpls neighbor set
      * @param edgeLabel label to be pushed as part of group operation
+     * @param dstSw the destination switch
      * @return the neighbor set object
      */
-    public static NeighborSet neighborSet(boolean random, Set<DeviceId> neighbors, boolean isMplsSet, int edgeLabel) {
-        return random ?
-                new RandomNeighborSet(neighbors, edgeLabel) :
-                new NeighborSet(neighbors, isMplsSet, edgeLabel);
+    public static NeighborSet neighborSet(boolean random, Set<DeviceId> neighbors,
+                                          boolean isMplsSet, int edgeLabel,
+                                          DeviceId dstSw) {
+        return random ? new RandomNeighborSet(neighbors, edgeLabel, dstSw)
+                      : new NeighborSet(neighbors, isMplsSet, edgeLabel, dstSw);
     }
 
     /**
@@ -134,6 +148,15 @@
     }
 
     /**
+     * Gets the destination switch for this neighbor set.
+     *
+     * @return the destination switch id
+     */
+    public DeviceId getDestinationSw() {
+        return dstSw;
+    }
+
+    /**
      * Gets the first neighbor of the set. The default
      * implementation assure the first neighbor is the
      * first of the set. Subclasses can modify this.
@@ -165,22 +188,24 @@
         NeighborSet that = (NeighborSet) o;
         return (this.neighbors.containsAll(that.neighbors) &&
                 that.neighbors.containsAll(this.neighbors) &&
-                (this.edgeLabel == that.edgeLabel) &&
-                (this.mplsSet == that.mplsSet));
+                this.edgeLabel == that.edgeLabel &&
+                this.mplsSet == that.mplsSet &&
+                this.dstSw.equals(that.dstSw));
     }
 
     // The list of neighbor ids and label are used for comparison.
     @Override
     public int hashCode() {
-        return Objects.hash(neighbors, edgeLabel, mplsSet);
+        return Objects.hash(neighbors, edgeLabel, mplsSet, dstSw);
     }
 
     @Override
     public String toString() {
         return toStringHelper(this)
-                .add("Neighborset Sw", neighbors)
+                .add("Neighbors", neighbors)
                 .add("Label", edgeLabel)
                 .add("MplsSet", mplsSet)
+                .add("DstSw", dstSw)
                 .toString();
     }
 }