[AETHER-1104] Calculate PiPipeconf fingerprint in a deterministic way

New master after taking over a switch was pushing again the pipeline
and all the flows and groups. This was happening because DefaultPiPipeconf
fingerprint was not calculated in a deterministic way across the cluster.

This patch introduces the following changes:
- Implements toString method in each abstraction representing a pipeline
- Hashes the p4Info file to generate a consistent hash of the pipeline model
- Uses a sorted collection to generate a consistent hash of the extensions

Change-Id: I792283b0a9b821284add36b3aba52843f33527c3
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/DefaultPiPipeconf.java b/core/api/src/main/java/org/onosproject/net/pi/model/DefaultPiPipeconf.java
index 464bdd7..2041cdb 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/DefaultPiPipeconf.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/DefaultPiPipeconf.java
@@ -192,21 +192,25 @@
             Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours =
                     behaviourMapBuilder.build();
             return new DefaultPiPipeconf(
-                    id, pipelineModel, generateFingerprint(extensions.values()),
+                    id, pipelineModel, generateFingerprint(extensions),
                     behaviours, extensions);
         }
 
-        private long generateFingerprint(Collection<URL> extensions) {
+        private long generateFingerprint(Map<ExtensionType, URL> extensions) {
             Collection<Integer> hashes = new ArrayList<>();
-            for (URL extUrl : extensions) {
+            for (ExtensionType extensionType : ExtensionType.values()) {
                 try {
-                    HashingInputStream hin = new HashingInputStream(
-                            Hashing.crc32(), extUrl.openStream());
-                    //noinspection StatementWithEmptyBody
-                    while (hin.read() != -1) {
-                        // Do nothing. Reading all input stream to update hash.
+                    // Get the extension if present and then hash the content
+                    URL extUrl = extensions.get(extensionType);
+                    if (extUrl != null) {
+                        HashingInputStream hin = new HashingInputStream(
+                                Hashing.crc32(), extUrl.openStream());
+                        //noinspection StatementWithEmptyBody
+                        while (hin.read() != -1) {
+                            // Do nothing. Reading all input stream to update hash.
+                        }
+                        hashes.add(hin.hash().asInt());
                     }
-                    hashes.add(hin.hash().asInt());
                 } catch (IOException e) {
                     throw new IllegalArgumentException(e);
                 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionModel.java
index dec4d1c..6b1af83 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionModel.java
@@ -26,6 +26,8 @@
 import java.util.Objects;
 import java.util.Optional;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiActionModel for P4Runtime.
  */
@@ -72,4 +74,12 @@
         return Objects.equals(this.id, other.id)
                 && Objects.equals(this.params, other.params);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("params", params.values())
+                .toString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionParamModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionParamModel.java
index 1c1f0a5..d4a6c4d 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionParamModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionParamModel.java
@@ -21,6 +21,8 @@
 
 import java.util.Objects;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiActionParamModel for P4Runtime.
  */
@@ -61,4 +63,12 @@
         return Objects.equals(this.id, other.id)
                 && Objects.equals(this.bitWidth, other.bitWidth);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("bitWidth", bitWidth)
+                .toString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionProfileModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionProfileModel.java
index 443d407..5059bce 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionProfileModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionProfileModel.java
@@ -24,6 +24,8 @@
 import java.util.Collection;
 import java.util.Objects;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiActionProfileModel for P4Runtime.
  */
@@ -90,4 +92,15 @@
                 && Objects.equals(this.size, other.size)
                 && Objects.equals(this.maxGroupSize, other.maxGroupSize);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("tables", tables)
+                .add("hasSelector", hasSelector)
+                .add("size", size)
+                .add("maxGroupSize", maxGroupSize)
+                .toString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ControlMetadataModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ControlMetadataModel.java
index a6e2a43..19a6fa7 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ControlMetadataModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ControlMetadataModel.java
@@ -21,6 +21,8 @@
 
 import java.util.Objects;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiPacketMetadataModel for P4Runtime.
  */
@@ -61,4 +63,12 @@
         return Objects.equals(this.id, other.id)
                 && Objects.equals(this.bitWidth, other.bitWidth);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("bitWidth", bitWidth)
+                .toString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4CounterModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4CounterModel.java
index de6e755..01f33ff 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4CounterModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4CounterModel.java
@@ -23,6 +23,8 @@
 
 import java.util.Objects;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiCounterModel for P4Runtime.
  */
@@ -88,4 +90,15 @@
                 && Objects.equals(this.table, other.table)
                 && Objects.equals(this.size, other.size);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("counterType", counterType)
+                .add("unit", unit)
+                .add("table", table)
+                .add("size", size)
+                .toString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java
index d5bf2e6..7c1fd1a 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java
@@ -20,6 +20,8 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
+import com.google.common.hash.Hashing;
+import com.google.common.hash.HashingInputStream;
 import com.google.protobuf.ExtensionRegistry;
 import com.google.protobuf.TextFormat;
 import org.onosproject.net.pi.model.PiActionId;
@@ -135,6 +137,19 @@
             throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
         }
 
+        // Generate fingerprint of the pipeline by hashing p4info file
+        final int fingerprint;
+        try {
+            HashingInputStream hin = new HashingInputStream(Hashing.crc32(), p4InfoUrl.openStream());
+            //noinspection StatementWithEmptyBody
+            while (hin.read() != -1) {
+                // Do nothing. Reading all input stream to update hash.
+            }
+            fingerprint = hin.hash().asInt();
+        } catch (IOException e) {
+            throw new P4InfoParserException("Unable to generate fingerprint " + p4InfoUrl.toString(), e);
+        }
+
         // Start by parsing and mapping instances to to their integer P4Info IDs.
         // Convenient to build the table model at the end.
 
@@ -243,7 +258,8 @@
                 meterImmMap,
                 registerImmMap,
                 actProfileImmMap,
-                ImmutableMap.copyOf(pktOpMap));
+                ImmutableMap.copyOf(pktOpMap),
+                fingerprint);
     }
 
 
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MatchFieldModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MatchFieldModel.java
index f3b5ddc..f311acf 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MatchFieldModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MatchFieldModel.java
@@ -22,6 +22,8 @@
 
 import java.util.Objects;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiMatchFieldModel for P4Runtime.
  */
@@ -70,4 +72,13 @@
                 && Objects.equals(this.bitWidth, other.bitWidth)
                 && Objects.equals(this.matchType, other.matchType);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("bitWidth", bitWidth)
+                .add("matchType", matchType)
+                .toString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MeterModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MeterModel.java
index 93827cc..a11c8ec 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MeterModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4MeterModel.java
@@ -23,6 +23,8 @@
 
 import java.util.Objects;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiMeterModel for P4Runtime.
  */
@@ -87,4 +89,15 @@
                 && Objects.equals(this.table, other.table)
                 && Objects.equals(this.size, other.size);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("meterType", meterType)
+                .add("unit", unit)
+                .add("table", table)
+                .add("size", size)
+                .toString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4PacketOperationModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4PacketOperationModel.java
index e29553f..3421e70 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4PacketOperationModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4PacketOperationModel.java
@@ -24,6 +24,8 @@
 import java.util.List;
 import java.util.Objects;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiPacketOperationModel for P4Runtime.
  */
@@ -33,7 +35,7 @@
     private final ImmutableList<PiPacketMetadataModel> metadatas;
 
     P4PacketOperationModel(PiPacketOperationType type,
-                                  ImmutableList<PiPacketMetadataModel> metadatas) {
+                           ImmutableList<PiPacketMetadataModel> metadatas) {
         this.type = type;
         this.metadatas = metadatas;
     }
@@ -65,4 +67,12 @@
         return Objects.equals(this.type, other.type)
                 && Objects.equals(this.metadatas, other.metadatas);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("type", type)
+                .add("metadatas", metadatas)
+                .toString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4PipelineModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4PipelineModel.java
index ce9ec13..28e5372 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4PipelineModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4PipelineModel.java
@@ -35,6 +35,8 @@
 import java.util.Objects;
 import java.util.Optional;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiPipelineModel for P4Runtime.
  */
@@ -46,6 +48,7 @@
     private final ImmutableMap<PiRegisterId, PiRegisterModel> registers;
     private final ImmutableMap<PiActionProfileId, PiActionProfileModel> actionProfiles;
     private final ImmutableMap<PiPacketOperationType, PiPacketOperationModel> packetOperations;
+    private final int fingerprint;
 
     P4PipelineModel(
             ImmutableMap<PiTableId, PiTableModel> tables,
@@ -53,13 +56,15 @@
             ImmutableMap<PiMeterId, PiMeterModel> meters,
             ImmutableMap<PiRegisterId, PiRegisterModel> registers,
             ImmutableMap<PiActionProfileId, PiActionProfileModel> actionProfiles,
-            ImmutableMap<PiPacketOperationType, PiPacketOperationModel> packetOperations) {
+            ImmutableMap<PiPacketOperationType, PiPacketOperationModel> packetOperations,
+            int fingerprint) {
         this.tables = tables;
         this.counters = counters;
         this.meters = meters;
         this.registers = registers;
         this.actionProfiles = actionProfiles;
         this.packetOperations = packetOperations;
+        this.fingerprint = fingerprint;
     }
 
     @Override
@@ -120,7 +125,11 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(tables, counters, meters, actionProfiles, packetOperations);
+        // NOTE: that the fingerprint is derived by hashing the p4Info file
+        //       this because the hashcode is not deterministic across multiple
+        //       JVMs instance. This hashcode is also used to derive the fingerprint
+        //       of the pipeconf.
+        return fingerprint;
     }
 
     @Override
@@ -135,7 +144,22 @@
         return Objects.equals(this.tables, other.tables)
                 && Objects.equals(this.counters, other.counters)
                 && Objects.equals(this.meters, other.meters)
+                && Objects.equals(this.registers, other.registers)
                 && Objects.equals(this.actionProfiles, other.actionProfiles)
-                && Objects.equals(this.packetOperations, other.packetOperations);
+                && Objects.equals(this.packetOperations, other.packetOperations)
+                && this.fingerprint == other.fingerprint;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("tables", tables.values())
+                .add("counters", counters.values())
+                .add("meters", meters.values())
+                .add("registers", registers.values())
+                .add("actionProfiles", actionProfiles.values())
+                .add("packetOperations", packetOperations.values())
+                .add("fingerprint", fingerprint)
+                .toString();
     }
 }
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4RegisterModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4RegisterModel.java
index 7e4fcda..44af1e0 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4RegisterModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4RegisterModel.java
@@ -21,6 +21,8 @@
 
 import java.util.Objects;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiRegisterModel for P4Runtime.
  */
@@ -61,4 +63,12 @@
         return Objects.equals(this.id, other.id)
                 && Objects.equals(this.size, other.size);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("size", size)
+                .toString();
+    }
 }
\ No newline at end of file
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4TableModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4TableModel.java
index 2eed1bb..be1b71c 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4TableModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4TableModel.java
@@ -34,6 +34,8 @@
 import java.util.Objects;
 import java.util.Optional;
 
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Implementation of PiTableModel for P4Runtime.
  */
@@ -164,4 +166,21 @@
                 && Objects.equals(this.actions, other.actions)
                 && Objects.equals(this.constDefaultAction, other.constDefaultAction);
     }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("id", id)
+                .add("tableType", tableType)
+                .add("actionProfile", actionProfile)
+                .add("maxSize", maxSize)
+                .add("counters", counters)
+                .add("meters", meters)
+                .add("supportAging", supportAging)
+                .add("matchFields", matchFields)
+                .add("actions", actions)
+                .add("constDefaultAction", constDefaultAction)
+                .add("isConstTable", isConstTable)
+                .toString();
+    }
 }
diff --git a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4PipelineModelTest.java b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4PipelineModelTest.java
index e8b1c70..a6bd76f 100644
--- a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4PipelineModelTest.java
+++ b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4PipelineModelTest.java
@@ -340,12 +340,18 @@
                     .put(PI_PACKET_OPERATION_TYPE_2, P4_PACKET_OPERATION_MODEL_2)
                     .build();
 
+    private static final int FINGER_PRINT_1 = 0;
+    private static final int FINGER_PRINT_2 = 1;
+
     private static final PiPipelineModel P4_PIPELINE_MODEL_1 =
-            new P4PipelineModel(TABLES_1, COUNTERS_1, METERS_1, REGISTERS_1, ACTION_PROFILES_1, PACKET_OPERATIONS_1);
+            new P4PipelineModel(TABLES_1, COUNTERS_1, METERS_1, REGISTERS_1, ACTION_PROFILES_1, PACKET_OPERATIONS_1,
+                    FINGER_PRINT_1);
     private static final PiPipelineModel SAME_AS_P4_PIPELINE_MODEL_1 =
-            new P4PipelineModel(TABLES_1, COUNTERS_1, METERS_1, REGISTERS_1, ACTION_PROFILES_1, PACKET_OPERATIONS_1);
+            new P4PipelineModel(TABLES_1, COUNTERS_1, METERS_1, REGISTERS_1, ACTION_PROFILES_1, PACKET_OPERATIONS_1,
+                    FINGER_PRINT_1);
     private static final PiPipelineModel P4_PIPELINE_MODEL_2 =
-            new P4PipelineModel(TABLES_2, COUNTERS_2, METERS_2, REGISTERS_1, ACTION_PROFILES_2, PACKET_OPERATIONS_2);
+            new P4PipelineModel(TABLES_2, COUNTERS_2, METERS_2, REGISTERS_1, ACTION_PROFILES_2, PACKET_OPERATIONS_2,
+                    FINGER_PRINT_2);
 
     /**
      * Checks that the P4PipelineModel class is immutable.