Support for PI table entry with default action

Equivalent to a flow rule with empty selector

Change-Id: I5dcbc234e59e3d8647476ffa54d80f5861bad077
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiMatchKeyTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiMatchKeyTest.java
index a6d62af..765deca 100644
--- a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiMatchKeyTest.java
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiMatchKeyTest.java
@@ -75,6 +75,7 @@
 
         new EqualsTester()
                 .addEqualityGroup(piMatchKey1, sameAsPiMatchKey1)
+                .addEqualityGroup(PiMatchKey.EMPTY, PiMatchKey.EMPTY)
                 .addEqualityGroup(piMatchKey2)
                 .testEquals();
     }
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
index 3230f41..c637144 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
@@ -97,27 +97,35 @@
         final PiTableId piTableId = translateTableId(rule.table(), interpreter);
         final PiTableModel tableModel = getTableModel(piTableId, pipelineModel);
         // Translate selector.
-        final Collection<PiFieldMatch> fieldMatches = translateFieldMatches(interpreter, rule.selector(), tableModel);
+        final PiMatchKey piMatchKey;
+        final boolean needPriority;
+        if (rule.selector().criteria().isEmpty()) {
+            piMatchKey = PiMatchKey.EMPTY;
+            needPriority = false;
+        } else {
+            final Collection<PiFieldMatch> fieldMatches = translateFieldMatches(
+                    interpreter, rule.selector(), tableModel);
+            piMatchKey = PiMatchKey.builder()
+                    .addFieldMatches(fieldMatches)
+                    .build();
+            // FIXME: P4Runtime limit
+            // Need to ignore priority if no TCAM lookup match field
+            needPriority = fieldMatches.stream()
+                    .anyMatch(match -> match.type() == PiMatchType.TERNARY ||
+                            match.type() == PiMatchType.RANGE);
+        }
         // Translate treatment.
         final PiTableAction piTableAction = translateTreatment(rule.treatment(), interpreter, piTableId, pipelineModel);
 
         // Build PI entry.
         final PiTableEntry.Builder tableEntryBuilder = PiTableEntry.builder();
 
-        // FIXME: P4Runtime limit
-        // Need to ignore priority if no TCAM lookup match field
-        boolean dontIgnorePriority = fieldMatches.stream()
-                .anyMatch(match -> match.type() == PiMatchType.TERNARY ||
-                        match.type() == PiMatchType.RANGE);
-
         tableEntryBuilder
                 .forTable(piTableId)
-                .withMatchKey(PiMatchKey.builder()
-                                      .addFieldMatches(fieldMatches)
-                                      .build())
+                .withMatchKey(piMatchKey)
                 .withAction(piTableAction);
 
-        if (dontIgnorePriority) {
+        if (needPriority) {
             tableEntryBuilder.withPriority(rule.priority());
         }
 
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
index 500f8cf..1d0caee 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
@@ -50,6 +50,7 @@
 import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
 import org.onosproject.net.pi.runtime.PiActionParam;
 import org.onosproject.net.pi.runtime.PiGroupKey;
+import org.onosproject.net.pi.runtime.PiMatchKey;
 import org.onosproject.net.pi.runtime.PiTableAction;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
@@ -134,6 +135,9 @@
                 .matchEthType(ethType)
                 .build();
 
+        TrafficSelector emptySelector = DefaultTrafficSelector
+                .builder().build();
+
         TrafficTreatment outPort2 = DefaultTrafficTreatment
                 .builder()
                 .setOutput(PortNumber.portNumber(outPort))
@@ -159,8 +163,19 @@
                 .withPriority(priority)
                 .build();
 
+        FlowRule defActionRule = DefaultFlowRule.builder()
+                .forDevice(DEVICE_ID)
+                .forTable(tableId)
+                .fromApp(appId)
+                .withSelector(emptySelector)
+                .withTreatment(outPort2)
+                .makeTemporary(timeout)
+                .withPriority(priority)
+                .build();
+
         PiTableEntry entry1 = PiFlowRuleTranslatorImpl.translate(rule1, pipeconf, null);
-        PiTableEntry entry2 = PiFlowRuleTranslatorImpl.translate(rule1, pipeconf, null);
+        PiTableEntry entry2 = PiFlowRuleTranslatorImpl.translate(rule2, pipeconf, null);
+        PiTableEntry defActionEntry = PiFlowRuleTranslatorImpl.translate(defActionRule, pipeconf, null);
 
         // check equality, i.e. same rules must produce same entries
         new EqualsTester()
@@ -204,6 +219,9 @@
         //            entry1.priority().get(), is(equalTo(MAX_PI_PRIORITY - rule1.priority())));
         assertThat("Incorrect timeout value",
                    entry1.timeout(), is(equalTo(expectedTimeout)));
+        assertThat("Match key should be empty",
+                   defActionEntry.matchKey(), is(equalTo(PiMatchKey.EMPTY)));
+        assertThat("Priority should not be set", !defActionEntry.priority().isPresent());
 
     }
 
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
index b61ae69..c95e2f3 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
@@ -201,8 +201,12 @@
         tableEntryMsgBuilder.setTableId(tableInfo.getPreamble().getId());
 
         // Field matches.
-        for (PiFieldMatch piFieldMatch : matchKey.fieldMatches()) {
-            tableEntryMsgBuilder.addMatch(encodePiFieldMatch(piFieldMatch, tableInfo, browser));
+        if (matchKey.equals(PiMatchKey.EMPTY)) {
+            tableEntryMsgBuilder.setIsDefaultAction(true);
+        } else {
+            for (PiFieldMatch piFieldMatch : matchKey.fieldMatches()) {
+                tableEntryMsgBuilder.addMatch(encodePiFieldMatch(piFieldMatch, tableInfo, browser));
+            }
         }
 
         return tableEntryMsgBuilder.build();
@@ -234,8 +238,12 @@
         tableEntryMsgBuilder.setAction(encodePiTableAction(piTableEntry.action(), browser));
 
         // Field matches.
-        for (PiFieldMatch piFieldMatch : piTableEntry.matchKey().fieldMatches()) {
-            tableEntryMsgBuilder.addMatch(encodePiFieldMatch(piFieldMatch, tableInfo, browser));
+        if (piTableEntry.matchKey().equals(PiMatchKey.EMPTY)) {
+            tableEntryMsgBuilder.setIsDefaultAction(true);
+        } else {
+            for (PiFieldMatch piFieldMatch : piTableEntry.matchKey().fieldMatches()) {
+                tableEntryMsgBuilder.addMatch(encodePiFieldMatch(piFieldMatch, tableInfo, browser));
+            }
         }
 
         return tableEntryMsgBuilder.build();
@@ -360,7 +368,11 @@
             throws P4InfoBrowser.NotFoundException, EncodeException {
         P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
         P4InfoOuterClass.Table tableInfo = browser.tables().getById(tableEntryMsg.getTableId());
-        return decodeFieldMatchMsgs(tableEntryMsg.getMatchList(), tableInfo, browser);
+        if (tableEntryMsg.getMatchCount() == 0) {
+            return PiMatchKey.EMPTY;
+        } else {
+            return decodeFieldMatchMsgs(tableEntryMsg.getMatchList(), tableInfo, browser);
+        }
     }
 
     private static PiMatchKey decodeFieldMatchMsgs(Collection<FieldMatch> fieldMatchs, P4InfoOuterClass.Table tableInfo,