CORD-48 Implementation of hashing Next Objective in OF-DPA driver. Major changes to ensure multi-ONOS-instance group-chain installation.
Also includes:
     Changes to Next Objective that adds metadata field for applications to optionally send auxillary info to drivers
     Changes to Next Objective that allows more explicit modification of the next objective
     Changes to Forwarding Objective and PendingNext to include hashCode() and equals() method
     MplsBosInstruction included in kryo serializer
     GroupKey's byte[] represented as a hex string
     Bug fix in mpls flow installation to report failure in install
     Bug fix in linkUp in SR app to disallow non-masters to modify groups
     Bug fix in ordering of actions in group

Change-Id: I3e7003f55724c2de79589e43e11d05ff4815a81d
diff --git a/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultForwardingObjective.java b/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultForwardingObjective.java
index 0abf5ab..af48180 100644
--- a/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultForwardingObjective.java
+++ b/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultForwardingObjective.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.flowobjective;
 
 import com.google.common.annotations.Beta;
+
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
@@ -119,6 +120,53 @@
         return context;
     }
 
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(selector, flag, permanent,
+                            timeout, appId, priority, nextId,
+                            treatment, op);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof DefaultForwardingObjective)) {
+            return false;
+        }
+        final DefaultForwardingObjective other = (DefaultForwardingObjective) obj;
+        boolean nextEq = false, treatmentEq = false;
+        if (this.selector.equals(other.selector) &&
+                this.flag == other.flag &&
+                this.permanent == other.permanent &&
+                this.timeout == other.timeout &&
+                this.appId.equals(other.appId) &&
+                this.priority == other.priority &&
+                this.op == other.op) {
+            if (this.nextId != null && other.nextId != null) {
+                nextEq = this.nextId == other.nextId;
+            }
+            if (this.treatment != null && other.treatment != null) {
+                treatmentEq = this.treatment.equals(other.treatment);
+            }
+            if (nextEq && treatmentEq) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Returns a new builder.
      *
diff --git a/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultNextObjective.java b/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultNextObjective.java
index 20e8929..4701589 100644
--- a/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultNextObjective.java
+++ b/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultNextObjective.java
@@ -18,6 +18,7 @@
 import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableList;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 
 import java.util.Collection;
@@ -39,6 +40,7 @@
     private final Integer id;
     private final Operation op;
     private final Optional<ObjectiveContext> context;
+    private final TrafficSelector meta;
 
     private DefaultNextObjective(Builder builder) {
         this.treatments = builder.treatments;
@@ -47,6 +49,7 @@
         this.id = builder.id;
         this.op = builder.op;
         this.context = Optional.ofNullable(builder.context);
+        this.meta = builder.meta;
     }
 
     @Override
@@ -94,6 +97,11 @@
         return context;
     }
 
+    @Override
+    public TrafficSelector meta() {
+        return meta;
+    }
+
     /**
      * Returns a new builder.
      *
@@ -111,6 +119,7 @@
         private List<TrafficTreatment> treatments;
         private Operation op;
         private ObjectiveContext context;
+        private TrafficSelector meta;
 
         private final ImmutableList.Builder<TrafficTreatment> listBuilder
                 = ImmutableList.builder();
@@ -172,6 +181,12 @@
         }
 
         @Override
+        public Builder setMeta(TrafficSelector meta) {
+            this.meta = meta;
+            return this;
+        }
+
+        @Override
         public NextObjective add() {
             treatments = listBuilder.build();
             op = Operation.ADD;
@@ -218,5 +233,55 @@
 
             return new DefaultNextObjective(this);
         }
+
+        @Override
+        public NextObjective addToExisting() {
+            treatments = listBuilder.build();
+            op = Operation.ADD_TO_EXISTING;
+            checkNotNull(appId, "Must supply an application id");
+            checkNotNull(id, "id cannot be null");
+            checkNotNull(type, "The type cannot be null");
+            checkArgument(!treatments.isEmpty(), "Must have at least one treatment");
+
+            return new DefaultNextObjective(this);
+        }
+
+        @Override
+        public NextObjective removeFromExisting() {
+            treatments = listBuilder.build();
+            op = Operation.REMOVE_FROM_EXISTING;
+            checkNotNull(appId, "Must supply an application id");
+            checkNotNull(id, "id cannot be null");
+            checkNotNull(type, "The type cannot be null");
+
+            return new DefaultNextObjective(this);
+        }
+
+        @Override
+        public NextObjective addToExisting(ObjectiveContext context) {
+            treatments = listBuilder.build();
+            op = Operation.ADD_TO_EXISTING;
+            this.context = context;
+            checkNotNull(appId, "Must supply an application id");
+            checkNotNull(id, "id cannot be null");
+            checkNotNull(type, "The type cannot be null");
+            checkArgument(!treatments.isEmpty(), "Must have at least one treatment");
+
+            return new DefaultNextObjective(this);
+        }
+
+        @Override
+        public NextObjective removeFromExisting(ObjectiveContext context) {
+            treatments = listBuilder.build();
+            op = Operation.REMOVE_FROM_EXISTING;
+            this.context = context;
+            checkNotNull(appId, "Must supply an application id");
+            checkNotNull(id, "id cannot be null");
+            checkNotNull(type, "The type cannot be null");
+
+            return new DefaultNextObjective(this);
+        }
+
     }
+
 }
diff --git a/core/api/src/main/java/org/onosproject/net/flowobjective/NextObjective.java b/core/api/src/main/java/org/onosproject/net/flowobjective/NextObjective.java
index 1350d7a..08916eb 100644
--- a/core/api/src/main/java/org/onosproject/net/flowobjective/NextObjective.java
+++ b/core/api/src/main/java/org/onosproject/net/flowobjective/NextObjective.java
@@ -17,6 +17,7 @@
 
 import com.google.common.annotations.Beta;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 
 import java.util.Collection;
@@ -34,7 +35,7 @@
  * - Failover
  * - Simple
  *
- * These types will indicate to the driver what the intended behaviour is.
+ * These types will indicate to the driver what the intended behavior is.
  * For example, a broadcast next objective with a collection of output
  * treatments will indicate to a driver that all output actions are expected
  * to be executed simultaneously. The driver is then free to implement this
@@ -84,6 +85,16 @@
     Type type();
 
     /**
+     * Auxiliary optional information provided to the device-driver.Typically
+     * conveys information about selectors (matches) that are intended to
+     * use this Next Objective.
+     *
+     * @return a selector intended to pass meta information to the device driver.
+     *         Value may be null if no meta information is provided.
+     */
+    TrafficSelector meta();
+
+    /**
      * A next step builder.
      */
     interface Builder extends Objective.Builder {
@@ -131,6 +142,14 @@
         Builder withPriority(int priority);
 
         /**
+         * Set meta information related to this next objective.
+         *
+         * @param selector match conditions
+         * @return an objective builder
+         */
+        Builder setMeta(TrafficSelector selector);
+
+        /**
          * Builds the next objective that will be added.
          *
          * @return a next objective
@@ -162,6 +181,40 @@
          */
         NextObjective remove(ObjectiveContext context);
 
+        /**
+         * Build the next objective that will be added, with {@link Operation}
+         * ADD_TO_EXISTING.
+         *
+         * @return a next objective
+         */
+        NextObjective addToExisting();
+
+        /**
+         * Build the next objective that will be removed, with {@link Operation}
+         * REMOVE_FROM_EXISTING.
+         *
+         * @return a next objective
+         */
+        NextObjective removeFromExisting();
+
+        /**
+         * Builds the next objective that will be added, with {@link Operation}
+         * ADD_TO_EXISTING. The context will be used to notify the calling application.
+         *
+         * @param context an objective context
+         * @return a next objective
+         */
+        NextObjective addToExisting(ObjectiveContext context);
+
+        /**
+         * Builds the next objective that will be removed, with {@link Operation}
+         * REMOVE_FROM_EXISTING. The context will be used to notify the calling application.
+         *
+         * @param context an objective context
+         * @return a next objective
+         */
+        NextObjective removeFromExisting(ObjectiveContext context);
+
     }
 
 }
diff --git a/core/api/src/main/java/org/onosproject/net/flowobjective/Objective.java b/core/api/src/main/java/org/onosproject/net/flowobjective/Objective.java
index 6ac7a7a..b1d73a7 100644
--- a/core/api/src/main/java/org/onosproject/net/flowobjective/Objective.java
+++ b/core/api/src/main/java/org/onosproject/net/flowobjective/Objective.java
@@ -21,7 +21,7 @@
 import java.util.Optional;
 
 /**
- * Base representation of an flow description.
+ * Base representation of a flow-objective description.
  */
 @Beta
 public interface Objective {
@@ -35,14 +35,30 @@
      */
     enum Operation {
         /**
-         * Adds the objective.
+         * Adds the objective. Can be used for any flow objective. For forwarding
+         * and filtering objectives, existing objectives with identical selector
+         * and priority fields (but different treatments or next) will be replaced.
+         * For next objectives, if modification is desired, ADD will not
+         * do anything - use ADD_TO_EXISTING.
          */
         ADD,
 
         /**
-         * Removes the objective.
+         * Removes the objective. Can be used for any flow objective.
          */
-        REMOVE
+        REMOVE,
+
+        /**
+         * Add to an existing Next Objective. Should not be used for any other
+         * objective.
+         */
+        ADD_TO_EXISTING,
+
+        /**
+         * Remove from an existing Next Objective. Should not be used for any
+         * other objective.
+         */
+        REMOVE_FROM_EXISTING
     }
 
     /**
@@ -129,6 +145,7 @@
          * @return an objective builder
          */
         Builder withPriority(int priority);
+
     }
 
 }
diff --git a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupKey.java b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupKey.java
index 7f00ae7..e1eacd1 100644
--- a/core/api/src/main/java/org/onosproject/net/group/DefaultGroupKey.java
+++ b/core/api/src/main/java/org/onosproject/net/group/DefaultGroupKey.java
@@ -25,6 +25,7 @@
 public class DefaultGroupKey implements GroupKey {
 
     private final byte[] key;
+    protected static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
 
     public DefaultGroupKey(byte[] key) {
         this.key = checkNotNull(key);
@@ -52,4 +53,20 @@
         return Arrays.hashCode(this.key);
     }
 
+    /**
+     * Returns a hex string representation of the byte array that is used
+     * as a group key. This solution was adapted from
+     * http://stackoverflow.com/questions/9655181/
+     */
+    @Override
+    public String toString() {
+        char[] hexChars = new char[key.length * 2];
+        for (int j = 0; j < key.length; j++) {
+            int v = key[j] & 0xFF;
+            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
+            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
+        }
+        return "GroupKey:0x" + new String(hexChars);
+    }
+
 }
\ No newline at end of file