[SDFAB-356] Extend P4RuntimeMeterProgrammable and Codecs to support reset scenario

Change-Id: Ifad0b296568d3f78b2aa792fa63f2f81fa80ebae
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellConfig.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellConfig.java
index f13e276..e62482b 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellConfig.java
@@ -24,6 +24,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -66,6 +67,35 @@
         return piMeterBands;
     }
 
+    /**
+     * Check if the config represents a modify operation.
+     *
+     * @return true if there are exactly 2 bands
+     */
+    public boolean isModify() {
+        return piMeterBands.size() == 2;
+    }
+
+    /**
+     * Check if the config represents a reset operation.
+     *
+     * @return true if there is no band.
+     */
+    public boolean isReset() {
+        return piMeterBands.isEmpty();
+    }
+
+    /**
+     * Returns a PiMeterCellConfig with no bands.
+     * Used to reset a PI meter cell.
+     *
+     * @param piMeterCellId the PiMeterCellId need to be reset
+     * @return a PiMeterCellConfig with no bands
+     */
+    public static PiMeterCellConfig reset(PiMeterCellId piMeterCellId) {
+        return new PiMeterCellConfig(piMeterCellId, Collections.emptyList());
+    }
+
     @Override
     public PiEntityType piEntityType() {
         return PiEntityType.METER_CELL_CONFIG;
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeMeterProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeMeterProgrammable.java
index 0bcfcaf..caaa751 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeMeterProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeMeterProgrammable.java
@@ -37,6 +37,7 @@
 import org.onosproject.net.pi.model.PiPipelineModel;
 import org.onosproject.net.pi.runtime.PiMeterCellConfig;
 import org.onosproject.net.pi.runtime.PiMeterCellHandle;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
 import org.onosproject.net.pi.service.PiMeterTranslator;
 import org.onosproject.net.pi.service.PiTranslationException;
 
@@ -51,6 +52,9 @@
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.meter.MeterOperation.Type.ADD;
+import static org.onosproject.net.meter.MeterOperation.Type.MODIFY;
+import static org.onosproject.net.meter.MeterOperation.Type.REMOVE;
 
 /**
  * Implementation of MeterProgrammable behaviour for P4Runtime.
@@ -95,19 +99,27 @@
     }
 
     private boolean processMeterOp(MeterOperation meterOp) {
-
-        if (meterOp.type() != MeterOperation.Type.MODIFY) {
-            log.warn("P4Runtime meter operations must be MODIFY!");
-            return false;
-        }
-
         PiMeterCellConfig piMeterCellConfig;
-        try {
-            piMeterCellConfig = translator.translate(meterOp.meter(), pipeconf);
-        } catch (PiTranslationException e) {
-            log.warn("Unable translate meter, aborting meter operation {}: {}", meterOp.type(), e.getMessage());
-            log.debug("exception", e);
-            return false;
+        switch (meterOp.type()) {
+            case ADD:
+            case MODIFY:
+                // Create a config for modify operation
+                try {
+                    piMeterCellConfig = translator.translate(meterOp.meter(), pipeconf);
+                } catch (PiTranslationException e) {
+                    log.warn("Unable translate meter, aborting meter operation {}: {}", meterOp.type(), e.getMessage());
+                    log.debug("exception", e);
+                    return false;
+                }
+                break;
+            case REMOVE:
+                // Create a empty config for reset operation
+                PiMeterCellId piMeterCellId = (PiMeterCellId) meterOp.meter().meterCellId();
+                piMeterCellConfig = PiMeterCellConfig.reset(piMeterCellId);
+                break;
+            default:
+                log.warn("Meter Operation type {} not supported", meterOp.type());
+                return false;
         }
 
         final PiMeterCellHandle handle = PiMeterCellHandle.of(deviceId, piMeterCellConfig);
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectMeterEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectMeterEntryCodec.java
index 3bedfbf..0f32417 100644
--- a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectMeterEntryCodec.java
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/DirectMeterEntryCodec.java
@@ -38,11 +38,16 @@
             PiMeterCellConfig piEntity, Object ignored, PiPipeconf pipeconf,
             P4InfoBrowser browser)
             throws CodecException {
-        return P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
+        P4RuntimeOuterClass.DirectMeterEntry.Builder builder =
+            P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
                 .setTableEntry(CODECS.tableEntry().encode(
-                        piEntity.cellId().tableEntry(), null, pipeconf))
-                .setConfig(MeterEntryCodec.getP4Config(piEntity))
-                .build();
+                        piEntity.cellId().tableEntry(), null, pipeconf));
+        // We keep the config field unset if it is reset scenario
+        P4RuntimeOuterClass.MeterConfig meterConfig = MeterEntryCodec.getP4Config(piEntity);
+        if (meterConfig != null) {
+            builder = builder.setConfig(meterConfig);
+        }
+        return builder.build();
     }
 
     @Override
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MeterEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MeterEntryCodec.java
index b210709..2da12d1 100644
--- a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MeterEntryCodec.java
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/MeterEntryCodec.java
@@ -34,8 +34,13 @@
 
     static P4RuntimeOuterClass.MeterConfig getP4Config(PiMeterCellConfig piConfig)
             throws CodecException {
-        if (piConfig.meterBands().size() != 2) {
-            throw new CodecException("Number of meter bands should be 2");
+        // A reset config has no band
+        if (piConfig.isReset()) {
+            return null;
+        }
+        // A modify config has exactly 2 bands
+        if (!piConfig.isModify()) {
+            throw new CodecException("Number of meter bands should be 2 (Modify) or 0 (Reset)");
         }
         final PiMeterBand[] bands = piConfig.meterBands().toArray(new PiMeterBand[0]);
         long cir, cburst, pir, pburst;
@@ -68,12 +73,17 @@
             throws P4InfoBrowser.NotFoundException, CodecException {
         final int meterId = browser.meters().getByName(
                 piEntity.cellId().meterId().id()).getPreamble().getId();
-        return P4RuntimeOuterClass.MeterEntry.newBuilder()
+        P4RuntimeOuterClass.MeterEntry.Builder builder =
+            P4RuntimeOuterClass.MeterEntry.newBuilder()
                 .setMeterId(meterId)
                 .setIndex(P4RuntimeOuterClass.Index.newBuilder()
-                                  .setIndex(piEntity.cellId().index()).build())
-                .setConfig(getP4Config(piEntity))
-                .build();
+                                .setIndex(piEntity.cellId().index()).build());
+        // We keep the config field unset if it is reset scenario
+        P4RuntimeOuterClass.MeterConfig meterConfig = getP4Config(piEntity);
+        if (meterConfig != null) {
+            builder = builder.setConfig(meterConfig);
+        }
+        return builder.build();
     }
 
     @Override