Do not insert or delete default action entries in P4Runtime

Spec says:
the default entry for a table is always set. It can be set at
compile-time by the P4 programmer - or defaults to NoAction (which is a
no-op) otherwise - and assuming it is not declared as const, can be
modified by the P4Runtime client. Because the default entry is always
set, we do not allow INSERT and DELETE updates on the default entry and
the P4Runtime server must return an INVALID_ARGUMENT error code if the
client attempts one.

With this patch we convert insert or delete operations into modify ones
(unless specified by a driver property, to support non-compliant devices).
For delete, we use the interpreter to suggest a default action that is
the same as the one when the pipeline was originally deployed.

Also, we introduce the capability of synchronizing the device mirror
with the device state.

Change-Id: I3758fc11780eb0f1cf4ed5a295bd98b54b182e29
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
index 7b8797a..04b2528 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
@@ -38,15 +38,18 @@
     private final PiTableId tableId;
     private final PiMatchKey matchKey;
     private final PiTableAction tableAction;
+    private final boolean isDefaultAction;
     private final long cookie;
     private final int priority;
     private final double timeout;
 
     private PiTableEntry(PiTableId tableId, PiMatchKey matchKey,
-                         PiTableAction tableAction, long cookie, int priority, double timeout) {
+                         PiTableAction tableAction, boolean isDefaultAction,
+                         long cookie, int priority, double timeout) {
         this.tableId = tableId;
         this.matchKey = matchKey;
         this.tableAction = tableAction;
+        this.isDefaultAction = isDefaultAction;
         this.cookie = cookie;
         this.priority = priority;
         this.timeout = timeout;
@@ -63,6 +66,9 @@
 
     /**
      * Returns the match key of this table entry.
+     * <p>
+     * If {@link #isDefaultAction()} is {@code true} this method returns the
+     * empty match key ({@link PiMatchKey#EMPTY}).
      *
      * @return match key
      */
@@ -80,6 +86,16 @@
     }
 
     /**
+     * Returns true if this table entry contains the default action for this
+     * table, a.k.a. table-miss entry, false otherwise.
+     *
+     * @return boolean
+     */
+    public boolean isDefaultAction() {
+        return isDefaultAction;
+    }
+
+    /**
      * Returns the cookie of this table entry.
      *
      * @return cookie
@@ -89,8 +105,8 @@
     }
 
     /**
-     * Returns the priority of this table entry, if present. If the priority value is not present, then this table entry
-     * has no explicit priority.
+     * Returns the priority of this table entry, if present. If the priority
+     * value is not present, then this table entry has no explicit priority.
      *
      * @return optional priority
      */
@@ -99,8 +115,9 @@
     }
 
     /**
-     * Returns the timeout in seconds of this table entry, if present. If the timeout value is not present, then this
-     * table entry is meant to be permanent.
+     * Returns the timeout in seconds of this table entry, if present. If the
+     * timeout value is not present, then this table entry is meant to be
+     * permanent.
      *
      * @return optional timeout value in seconds
      */
@@ -121,25 +138,42 @@
                 Double.compare(that.timeout, timeout) == 0 &&
                 Objects.equal(tableId, that.tableId) &&
                 Objects.equal(matchKey, that.matchKey) &&
+                Objects.equal(isDefaultAction, that.isDefaultAction) &&
                 Objects.equal(tableAction, that.tableAction);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(tableId, matchKey, tableAction, priority, timeout);
+        return Objects.hashCode(tableId, matchKey, isDefaultAction, tableAction,
+                                priority, timeout);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
                 .add("tableId", tableId)
-                .add("matchKey", matchKey)
-                .add("tableAction", tableAction)
+                .add("matchKey", isDefaultAction ? "DEFAULT-ACTION" : matchKey)
+                .add("tableAction", tableActionToString(tableAction))
                 .add("priority", priority == NO_PRIORITY ? "N/A" : String.valueOf(priority))
                 .add("timeout", timeout == NO_TIMEOUT ? "PERMANENT" : String.valueOf(timeout))
                 .toString();
     }
 
+    private String tableActionToString(PiTableAction tableAction) {
+        if (tableAction == null) {
+            return "null";
+        }
+        switch (tableAction.type()) {
+            case ACTION_GROUP_ID:
+                return "GROUP:" + ((PiActionGroupId) tableAction).id();
+            case GROUP_MEMBER_ID:
+                return "GROUP_MEMBER:" + ((PiActionGroupMemberId) tableAction).id();
+            case ACTION:
+            default:
+                return tableAction.toString();
+        }
+    }
+
     /**
      * Returns a table entry builder.
      *
@@ -244,7 +278,9 @@
         public PiTableEntry build() {
             checkNotNull(tableId);
             checkNotNull(matchKey);
-            return new PiTableEntry(tableId, matchKey, tableAction, cookie, priority, timeout);
+            final boolean isDefaultAction = matchKey.equals(PiMatchKey.EMPTY);
+            return new PiTableEntry(tableId, matchKey, tableAction,
+                                    isDefaultAction, cookie, priority, timeout);
         }
     }
 }