[SDFAB-500][SDFAB-527] Meters cleanup and leftovers

- Improve ONOS cli enabling CRUD of p4rt trtcm
- Improve ONOS rest enabling CRUD of p4rt trtcm
- Improve MeterService with scope defined reads and integrate in cli/rest
- Add support along the stack for BYTE_PER_SEC unit
- Add support along the stack for COMMITTED and PEAK bands
- Fix several bugs in ONOS cli/rest interfaces
- Improve REST codecs
- Fix NPE in MeterDriverProvider
- Improve PiMeterTransalation by enforcing trtcm config
- Implement explicit translation of the bands
- Fix ONOS reconciliation by removing from the mirror the wrong configs
- Remove unnecessary checks in MeterEntryCodec
- Update unit tests

It will follow a 2nd patch to complete SDFAB-527

Change-Id: I855235b17f60cb1d39f5b9a042c1015105a8a269
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java
index 9d4f7e4..e98734d 100644
--- a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java
@@ -40,6 +40,7 @@
 import org.onosproject.net.meter.MeterListener;
 import org.onosproject.net.meter.MeterOperation;
 import org.onosproject.net.meter.MeterRequest;
+import org.onosproject.net.meter.MeterScope;
 import org.onosproject.net.meter.MeterService;
 import org.onosproject.net.meter.MeterState;
 import org.onosproject.net.meter.MeterStoreDelegate;
@@ -50,6 +51,7 @@
 import org.slf4j.Logger;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
@@ -180,6 +182,11 @@
     }
 
     @Override
+    public Collection<Meter> getMeters(DeviceId deviceId, MeterScope scope) {
+        return Collections.emptyList();
+    }
+
+    @Override
     public Collection<Meter> getAllMeters() {
         return store.getAllMeters(networkId());
     }
diff --git a/cli/src/main/java/org/onosproject/cli/net/MeterAddCommand.java b/cli/src/main/java/org/onosproject/cli/net/MeterAddCommand.java
index 3e71ad9..4ddf91d 100644
--- a/cli/src/main/java/org/onosproject/cli/net/MeterAddCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/MeterAddCommand.java
@@ -28,6 +28,7 @@
 import org.onosproject.net.meter.DefaultMeterRequest;
 import org.onosproject.net.meter.Meter;
 import org.onosproject.net.meter.MeterRequest;
+import org.onosproject.net.meter.MeterScope;
 import org.onosproject.net.meter.MeterService;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
@@ -45,9 +46,8 @@
 
     private Meter.Unit unit;
     private Set<Band> bands = new HashSet<>();
-    private Long rate;
-    private Long burstSize;
-
+    private MeterScope scope;
+    private Long index;
 
     @Option(name = "-bd", aliases = "--bandDrop",
             description = "Assign band DROP to this meter",
@@ -59,6 +59,16 @@
             required = false, multiValued = false)
     private boolean hasBandRemark = false;
 
+    @Option(name = "-by", aliases = "--bandYel",
+            description = "Assign band MARK_YELLOW to this meter",
+            required = false, multiValued = false)
+    private boolean hasBandYel = false;
+
+    @Option(name = "-bre", aliases = "--bandRed",
+            description = "Assign band MARK_RED to this meter",
+            required = false, multiValued = false)
+    private boolean hasBandRed = false;
+
     @Option(name = "-up", aliases = "--unitPkts",
             description = "Assign unit Packets per Second to this meter",
             required = false, multiValued = false)
@@ -69,18 +79,31 @@
             required = false, multiValued = false)
     private boolean hasKbps = false;
 
+    @Option(name = "-ub", aliases = "--unitBytes",
+            description = "Assign unit Bytes per Second to this meter",
+            required = false, multiValued = false)
+    private boolean hasBytes = false;
+
     @Option(name = "-ib", aliases = "--isBurst",
             description = "Set meter applicable only to burst",
             required = false, multiValued = false)
     private boolean isBurst = false;
 
     @Option(name = "-b", aliases = "--bandwidth", description = "Bandwidth",
-            required = false, multiValued = false)
-    private String bandwidthString = null;
+            required = false, multiValued = true)
+    private String[] bandwidthString = null;
 
     @Option(name = "-bs", aliases = "--burstSize", description = "Burst size",
+            required = false, multiValued = true)
+    private String[] burstSizeString = null;
+
+    @Option(name = "-sc", aliases = "--scope", description = "Scope",
             required = false, multiValued = false)
-    private String burstSizeString = null;
+    private String scopeString = null;
+
+    @Option(name = "-id", aliases = "--index", description = "Index",
+            required = false, multiValued = false)
+    private String indexString = null;
 
     @Argument(index = 0, name = "uri", description = "Device ID",
             required = true, multiValued = false)
@@ -93,53 +116,110 @@
         // check units
         if (hasPkts) {
             unit = Meter.Unit.PKTS_PER_SEC;
-        } else {
+        } else if (hasKbps) {
             unit = Meter.Unit.KB_PER_SEC;
+        } else if (hasBytes) {
+            unit = Meter.Unit.BYTES_PER_SEC;
         }
 
+        int numBands = 0;
+        if (hasBandDrop) {
+            numBands++;
+        }
+        if (hasBandRemark) {
+            numBands++;
+        }
+        if (hasBandYel) {
+            numBands++;
+        }
+        if (hasBandRed) {
+            numBands++;
+        }
+
+        long[] rates = new long[numBands];
+        long[] bursts = new long[numBands];
         // check rate (does not take into account if it is kbps or pkts)
-        if (!isNullOrEmpty(bandwidthString)) {
-            rate = Long.parseLong(bandwidthString);
-        } else {
-            rate = 500L;
-        }
-
-        // burst size
-        if (!isNullOrEmpty(burstSizeString)) {
-            burstSize = Long.parseLong(burstSizeString);
-        } else {
-            burstSize = 0L;
+        if (bandwidthString != null && bandwidthString.length == numBands &&
+                burstSizeString != null && burstSizeString.length == numBands) {
+            for (int i = 0; i < bandwidthString.length; i++) {
+                rates[i] = 500L;
+                bursts[i] = 0L;
+                if (!isNullOrEmpty(bandwidthString[i])) {
+                    rates[i] = Long.parseLong(bandwidthString[i]);
+                }
+                if (!isNullOrEmpty(burstSizeString[i])) {
+                    bursts[i] = Long.parseLong(burstSizeString[i]);
+                }
+            }
+        } else if (bandwidthString != null && bandwidthString.length < numBands &&
+                burstSizeString != null && burstSizeString.length < numBands) {
+            for (int i = 0; i < numBands; i++) {
+                rates[i] = 500L;
+                bursts[i] = 0L;
+                if (i < bandwidthString.length && !isNullOrEmpty(bandwidthString[i])) {
+                    rates[i] = Long.parseLong(bandwidthString[i]);
+                }
+                if (i < burstSizeString.length && !isNullOrEmpty(burstSizeString[i])) {
+                    bursts[i] = Long.parseLong(burstSizeString[i]);
+                }
+            }
         }
 
         // Create bands
+        int i = 0;
         if (hasBandDrop) {
             Band band = DefaultBand.builder()
                     .ofType(Band.Type.DROP)
-                    .withRate(rate)
-                    .burstSize(burstSize)
+                    .withRate(rates[i])
+                    .burstSize(bursts[i])
                     .build();
             bands.add(band);
+            i++;
         }
         if (hasBandRemark) {
             Band band = DefaultBand.builder()
                     .ofType(Band.Type.REMARK)
-                    .withRate(rate)
-                    .burstSize(burstSize)
+                    .withRate(rates[i])
+                    .burstSize(bursts[i])
+                    .build();
+            bands.add(band);
+            i++;
+        }
+        if (hasBandYel) {
+            Band band = DefaultBand.builder()
+                    .ofType(Band.Type.MARK_YELLOW)
+                    .withRate(rates[i])
+                    .burstSize(bursts[i])
+                    .build();
+            bands.add(band);
+            i++;
+        }
+        if (hasBandRed) {
+            Band band = DefaultBand.builder()
+                    .ofType(Band.Type.MARK_RED)
+                    .withRate(rates[i])
+                    .burstSize(bursts[i])
                     .build();
             bands.add(band);
         }
+
         // default band is drop
         if (bands.size() == 0) {
             Band band = DefaultBand.builder()
                     .ofType(Band.Type.DROP)
-                    .withRate(rate)
-                    .burstSize(burstSize)
+                    .withRate(500L)
+                    .burstSize(0L)
                     .build();
             bands.add(band);
         }
 
+        if (!isNullOrEmpty(scopeString)) {
+            scope = MeterScope.of(scopeString);
+        }
 
-
+        if (!isNullOrEmpty(indexString) && scope != null) {
+            index = Long.parseLong(indexString);
+        }
     }
 
     @Override
@@ -151,22 +231,32 @@
 
         checkOptions();
 
-
         MeterRequest.Builder builder = DefaultMeterRequest.builder()
                 .forDevice(deviceId)
                 .fromApp(coreService.registerApplication(appId))
                 .withUnit(unit)
                 .withBands(bands);
 
-
         if (isBurst) {
             builder = builder.burst();
         }
 
+        // Scope is by default global but we can still provide the index
+        // otherwise we can specify both scope and index or let the meter
+        // service allocate the meter for us. User defined index requires
+        // the user defined mode being active.
+        if (scope != null) {
+            builder = builder.withScope(scope);
+        }
+
+        if (index != null) {
+            builder = builder.withIndex(index);
+        }
+
         MeterRequest request = builder.add();
 
         Meter m = service.submit(request);
-        log.info("Requested meter with id {}: {}", m.id().toString(), m.toString());
-        print("Requested meter with id %s: %s", m.id().toString(), m.toString());
+        log.info("Requested meter with cellId {}: {}", m.meterCellId().toString(), m.toString());
+        print("Requested meter with cellId %s: %s", m.meterCellId().toString(), m.toString());
     }
 }
diff --git a/cli/src/main/java/org/onosproject/cli/net/MeterRemoveCommand.java b/cli/src/main/java/org/onosproject/cli/net/MeterRemoveCommand.java
index 6c3f1bb..941c910 100644
--- a/cli/src/main/java/org/onosproject/cli/net/MeterRemoveCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/MeterRemoveCommand.java
@@ -18,19 +18,21 @@
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.meter.Band;
-import org.onosproject.net.meter.DefaultBand;
 import org.onosproject.net.meter.DefaultMeterRequest;
-import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterCellId;
 import org.onosproject.net.meter.MeterId;
 import org.onosproject.net.meter.MeterRequest;
+import org.onosproject.net.meter.MeterScope;
 import org.onosproject.net.meter.MeterService;
+import org.onosproject.net.pi.model.PiMeterId;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
 
-import java.util.Collections;
+import static com.google.common.base.Strings.isNullOrEmpty;
 
 /**
  * Remove existing meter from device.
@@ -45,9 +47,13 @@
     @Completion(DeviceIdCompleter.class)
     private String uri = null;
 
-    @Argument(index = 1, name = "meterId", description = "Meter ID hexadecimal value",
+    @Argument(index = 1, name = "index", description = "Index",
             required = true, multiValued = false)
-    private String meterIdstr = null;
+    private String indexString = null;
+
+    @Option(name = "-sc", aliases = "--scope", description = "Scope",
+            required = false, multiValued = false)
+    private String scopeString = null;
 
     private final String appId = "org.onosproject.cli.meterCmd";
 
@@ -57,19 +63,27 @@
         CoreService coreService = get(CoreService.class);
 
         DeviceId deviceId = DeviceId.deviceId(uri);
-        MeterId meterId = MeterId.meterId(Long.parseLong(meterIdstr, 16));
 
-        Band b = new DefaultBand(Band.Type.DROP, 0L, 0L, (short) 0);
+        MeterScope scope = MeterScope.globalScope();
+        if (!isNullOrEmpty(scopeString)) {
+            scope = MeterScope.of(scopeString);
+        }
+
+        MeterCellId meterCellId;
+        long index = Long.parseLong(indexString);
+        if (scope.equals(MeterScope.globalScope())) {
+            meterCellId = MeterId.meterId(index);
+        } else {
+            meterCellId = PiMeterCellId.ofIndirect(PiMeterId.of(scope.id()), index);
+        }
 
         MeterRequest.Builder builder = DefaultMeterRequest.builder()
                 .forDevice(deviceId)
-                .withBands(Collections.singleton(b))
-                .withUnit(Meter.Unit.PKTS_PER_SEC)
                 .fromApp(coreService.registerApplication(appId));
         MeterRequest meterRequest = builder.remove();
-        service.withdraw(meterRequest, meterId);
-        log.info("Requested meter removal: {}", meterRequest.toString());
+        service.withdraw(builder.remove(), meterCellId);
 
-        print("Requested meter removal: %s", meterRequest.toString());
+        log.info("Requested meter {} removal: {}", meterCellId.toString(), meterRequest.toString());
+        print("Requested meter %s removal: %s", meterCellId.toString(), meterRequest.toString());
     }
 }
diff --git a/cli/src/main/java/org/onosproject/cli/net/MetersListCommand.java b/cli/src/main/java/org/onosproject/cli/net/MetersListCommand.java
index 4fe3590..c5e7fbf 100644
--- a/cli/src/main/java/org/onosproject/cli/net/MetersListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/MetersListCommand.java
@@ -15,16 +15,20 @@
  */
 package org.onosproject.cli.net;
 
-import com.google.common.collect.Collections2;
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterCellId;
 import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterScope;
 import org.onosproject.net.meter.MeterService;
+import org.onosproject.net.pi.model.PiMeterId;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
 
 import java.util.Collection;
 
@@ -43,35 +47,60 @@
     @Completion(DeviceIdCompleter.class)
     String uri = null;
 
-    @Argument(index = 1, name = "meter", description = "Meter ID",
+    @Argument(index = 1, name = "index", description = "Index",
             required = false, multiValued = false)
-    String meterstr = null;
+    private String indexString = null;
 
-    MeterId meterId = null;
+    @Option(name = "-sc", aliases = "--scope", description = "Scope",
+            required = false, multiValued = false)
+    private String scopeString = null;
+
+    MeterScope meterScope;
+    Long index;
+    MeterCellId meterCellId;
 
     @Override
     protected void doExecute() {
 
-        if (!isNullOrEmpty(meterstr)) {
-            meterId = MeterId.meterId(Long.parseLong(meterstr));
+        if (!isNullOrEmpty(scopeString)) {
+            meterScope = MeterScope.of(scopeString);
+        }
+
+        if (!isNullOrEmpty(indexString)) {
+            index = Long.parseLong(indexString);
+            if (meterScope == null) {
+                meterScope = MeterScope.globalScope();
+            }
+        }
+
+        if (index != null) {
+            if (meterScope != null && meterScope.equals(MeterScope.globalScope())) {
+                meterCellId = MeterId.meterId(index);
+            } else if (meterScope != null) {
+                meterCellId = PiMeterCellId.ofIndirect(PiMeterId.of(meterScope.id()), index);
+            }
         }
 
         MeterService service = get(MeterService.class);
 
-        if (meterId != null && uri != null) {
-            Meter m = service.getMeter(DeviceId.deviceId(uri), meterId);
-            if (m != null) {
-                print("%s", m);
-            } else {
-                error("Meter %s not found for device %s", meterId, uri);
-            }
+        Collection<Meter> meters;
+        if (isNullOrEmpty(uri)) {
+            meters = service.getAllMeters();
+            printMeters(meters);
         } else {
-            Collection<Meter> meters = service.getAllMeters();
-            if (uri == null) {
+            if (meterCellId != null) {
+                Meter m = service.getMeter(DeviceId.deviceId(uri), meterCellId);
+                if (m != null) {
+                    print("%s", m);
+                } else {
+                    error("Meter %s not found for device %s", meterCellId, uri);
+                }
+            } else if (meterScope != null) {
+                meters = service.getMeters(DeviceId.deviceId(uri), meterScope);
                 printMeters(meters);
             } else {
-                printMeters(Collections2.filter(meters,
-                        m -> m.deviceId().equals(DeviceId.deviceId(uri))));
+                meters = service.getMeters(DeviceId.deviceId(uri));
+                printMeters(meters);
             }
         }
     }
diff --git a/core/api/src/main/java/org/onosproject/net/meter/Band.java b/core/api/src/main/java/org/onosproject/net/meter/Band.java
index 9f9479c..befbead 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/Band.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/Band.java
@@ -51,8 +51,27 @@
          * Defines a meter band with no action, used to mark
          * packets internally in the pipeline, i.e. without
          * modifying the packet headers.
+         *
+         * @deprecated in onos-2.5, replace by MARK_YELLOW and MARK_RED
          */
+        @Deprecated
         NONE,
+
+        /**
+         * Defines a meter band for the configuration of the committed
+         * rate AND the committed burst size. Used in conjunction with MARK_RED
+         * to implement a srTCM or trTCM, see RFCs 2697 and 2698 respectively.
+         */
+        MARK_YELLOW,
+
+        /**
+         * Defines a meter band for the configuration of the peak rate
+         * AND the peak burst size OR the excess burst size. When used to
+         * configure a srTCM excess rate must be 0. Used in conjunction with
+         * MARK_YELLOW to implement a srTCM or trTCM, see RFCs 2697 and 2698
+         * respectively.
+         */
+        MARK_RED,
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java b/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java
index 0dbd4f7..617151d 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java
@@ -33,8 +33,6 @@
  */
 public final class DefaultMeterRequest extends AbstractAnnotated implements MeterRequest {
 
-
-
     private final ApplicationId appId;
     private final Meter.Unit unit;
     private final boolean burst;
@@ -122,6 +120,8 @@
     }
 
     public static final class Builder implements MeterRequest.Builder {
+        // Relevant only for delete
+        private static final Band DUMMY_BAND = new DefaultBand(Band.Type.DROP, 0L, 0L, (short) 0);
 
         private ApplicationId appId;
         private Meter.Unit unit = Meter.Unit.KB_PER_SEC;
@@ -196,6 +196,11 @@
 
         @Override
         public MeterRequest remove() {
+            // Allow to create the removal request without specifying bands
+            if (bands == null || bands.isEmpty()) {
+                bands = ImmutableSet.of(DUMMY_BAND);
+            }
+
             validate();
             return new DefaultMeterRequest(deviceId, appId, unit, burst, bands, context,
                                            Type.REMOVE, scope, desiredIndex, annotations);
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterService.java b/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
index 5b3ca3b..2d37f87 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
@@ -91,6 +91,15 @@
     Collection<Meter> getMeters(DeviceId deviceId);
 
     /**
+     * Fetches the meters by the device id and scope.
+     *
+     * @param deviceId a device id
+     * @param scope meters scope
+     * @return a collection of meters
+     */
+    Collection<Meter> getMeters(DeviceId deviceId, MeterScope scope);
+
+    /**
      * Allocates a new meter id in the system.
      *
      * @param deviceId the device id
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java b/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java
index 7f4de4b..4abf233 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java
@@ -118,6 +118,17 @@
     Collection<Meter> getAllMeters(DeviceId deviceId);
 
     /**
+     * Returns all meters stored in the store for a
+     * precise device and scope.
+     *
+     * @param deviceId a device id
+     * @param scope meters scope
+     * @return an immutable copy of the meters stored for a given device
+     *         withing a given scope
+     */
+    Collection<Meter> getAllMeters(DeviceId deviceId, MeterScope scope);
+
+    /**
      * Update the store by deleting the failed meter.
      * Notifies the delegate that the meter failed to allow it
      * to nofity the app.
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterBand.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterBand.java
index b1dbcf5..ea8c34d 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterBand.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterBand.java
@@ -27,21 +27,33 @@
  */
 @Beta
 public final class PiMeterBand {
+    private final PiMeterBandType type;
     private final long rate;
     private final long burst;
 
     /**
      * Creates a band with rate and burst.
      *
+     * @param type type of this band
      * @param rate  rate of this band
      * @param burst burst of this band
      */
-    public PiMeterBand(long rate, long burst) {
+    public PiMeterBand(PiMeterBandType type, long rate, long burst) {
+        this.type = type;
         this.rate = rate;
         this.burst = burst;
     }
 
     /**
+     * Returns the type of this band.
+     *
+     * @return type of this band
+     */
+    public PiMeterBandType type() {
+        return type;
+    }
+
+    /**
      * Returns the rate of this band.
      *
      * @return rate of this band
@@ -61,7 +73,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(rate, burst);
+        return Objects.hash(type, rate, burst);
     }
 
     @Override
@@ -71,7 +83,8 @@
         }
         if (obj instanceof PiMeterBand) {
             PiMeterBand that = (PiMeterBand) obj;
-            return Objects.equals(rate, that.rate) &&
+            return Objects.equals(type, that.type) &&
+                    Objects.equals(rate, that.rate) &&
                     Objects.equals(burst, that.burst);
 
         }
@@ -80,6 +93,7 @@
 
     public String toString() {
         return toStringHelper(this)
+                .add("type", type)
                 .add("rate", rate)
                 .add("burst", burst).toString();
     }
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterBandType.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterBandType.java
new file mode 100644
index 0000000..ee15a57
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterBandType.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Type of meter band of a protocol-independent pipeline.
+ */
+@Beta
+public enum PiMeterBandType {
+    /**
+     * Committed band config.
+     */
+    COMMITTED("committed"),
+
+    /**
+     * Peak band config.
+     */
+    PEAK("peak");
+
+    private final String humanReadableName;
+
+    PiMeterBandType(String humanReadableName) {
+        this.humanReadableName = humanReadableName;
+    }
+
+    /**
+     * Returns a human readable representation of this PI meter band type (useful
+     * for logging).
+     *
+     * @return string
+     */
+    public String humanReadableName() {
+        return humanReadableName;
+    }
+}
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 d3da340..ecbfe07 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
@@ -19,13 +19,12 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import org.onosproject.net.DeviceId;
 
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
+import java.util.Map;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -36,7 +35,7 @@
 public final class PiMeterCellConfig implements PiEntity {
 
     private final PiMeterCellId cellId;
-    private final ImmutableList<PiMeterBand> piMeterBands;
+    private final ImmutableMap<PiMeterBandType, PiMeterBand> piMeterBands;
 
     /**
      * Creates a new meter cell configuration for the given cell identifier and meter bands.
@@ -44,9 +43,9 @@
      * @param cellId  meter cell identifier
      * @param piMeterBands meter bands
      */
-    private PiMeterCellConfig(PiMeterCellId cellId, Collection<PiMeterBand> piMeterBands) {
+    private PiMeterCellConfig(PiMeterCellId cellId, Map<PiMeterBandType, PiMeterBand> piMeterBands) {
         this.cellId = cellId;
-        this.piMeterBands = ImmutableList.copyOf(piMeterBands);
+        this.piMeterBands = ImmutableMap.copyOf(piMeterBands);
     }
 
     /**
@@ -59,11 +58,11 @@
     }
 
     /**
-     * Returns the collection of bands of this cell.
+     * Returns the map of bands of this cell.
      *
      * @return meter bands
      */
-    public Collection<PiMeterBand> meterBands() {
+    public Map<PiMeterBandType, PiMeterBand> meterBands() {
         return piMeterBands;
     }
 
@@ -88,6 +87,24 @@
     }
 
     /**
+     * Returns the committed configuration if present.
+     *
+     * @return the committed band. Null otherwise
+     */
+    public PiMeterBand committedBand() {
+        return piMeterBands.get(PiMeterBandType.COMMITTED);
+    }
+
+    /**
+     * Returns the peak configuration if present.
+     *
+     * @return the peak band. Null otherwise
+     */
+    public PiMeterBand peakBand() {
+        return piMeterBands.get(PiMeterBandType.PEAK);
+    }
+
+    /**
      * Returns a PiMeterCellConfig with no bands.
      * Used to reset a PI meter cell.
      *
@@ -95,7 +112,7 @@
      * @return a PiMeterCellConfig with no bands
      */
     public static PiMeterCellConfig reset(PiMeterCellId piMeterCellId) {
-        return new PiMeterCellConfig(piMeterCellId, Collections.emptyList());
+        return new PiMeterCellConfig(piMeterCellId, Collections.emptyMap());
     }
 
     @Override
@@ -118,8 +135,7 @@
         }
         PiMeterCellConfig that = (PiMeterCellConfig) o;
 
-        return piMeterBands.containsAll((that.piMeterBands)) &&
-                piMeterBands.size() == that.piMeterBands.size() &&
+        return Objects.equal(piMeterBands, that.piMeterBands) &&
                 Objects.equal(cellId, that.cellId);
     }
 
@@ -146,8 +162,8 @@
     }
 
     public static final class Builder {
-        private  PiMeterCellId cellId;
-        private List<PiMeterBand> bands = new ArrayList<>();
+        private PiMeterCellId cellId;
+        private Map<PiMeterBandType, PiMeterBand> bands = Maps.newHashMap();
 
 
         private Builder() {
@@ -173,7 +189,31 @@
          * @return this
          */
         public PiMeterCellConfig.Builder withMeterBand(PiMeterBand band) {
-            this.bands.add(band);
+            this.bands.put(band.type(), band);
+            return this;
+        }
+
+        /**
+         * Sets the committed band of this meter.
+         *
+         * @param rate committed rate
+         * @param burst committed burst
+         * @return this
+         */
+        public PiMeterCellConfig.Builder withCommittedBand(long rate, long burst) {
+            this.bands.put(PiMeterBandType.COMMITTED, new PiMeterBand(PiMeterBandType.COMMITTED, rate, burst));
+            return this;
+        }
+
+        /**
+         * Sets the peak band of this meter.
+         *
+         * @param rate peak rate
+         * @param burst peak burst
+         * @return this
+         */
+        public PiMeterCellConfig.Builder withPeakBand(long rate, long burst) {
+            this.bands.put(PiMeterBandType.PEAK, new PiMeterBand(PiMeterBandType.PEAK, rate, burst));
             return this;
         }
 
diff --git a/core/api/src/test/java/org/onosproject/net/meter/MeterServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/meter/MeterServiceAdapter.java
index 2a298b4..86795e9 100644
--- a/core/api/src/test/java/org/onosproject/net/meter/MeterServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/meter/MeterServiceAdapter.java
@@ -61,6 +61,11 @@
     }
 
     @Override
+    public Collection<Meter> getMeters(DeviceId deviceId, MeterScope scope) {
+        return null;
+    }
+
+    @Override
     public MeterId allocateMeterId(DeviceId deviceId) {
         return null;
     }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/MeterBandCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/MeterBandCodec.java
index 4621e94..3cb68a0 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/MeterBandCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/MeterBandCodec.java
@@ -21,17 +21,14 @@
 import org.onosproject.net.meter.Band;
 import org.onosproject.net.meter.Band.Builder;
 import org.onosproject.net.meter.DefaultBand;
-import org.slf4j.Logger;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onlab.util.Tools.nullIsIllegal;
-import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Meter band JSON codec.
  */
 public final class MeterBandCodec extends JsonCodec<Band> {
-    private final Logger log = getLogger(getClass());
 
     // JSON field names
     private static final String TYPE = "type";
@@ -93,6 +90,18 @@
                 builder.ofType(type);
                 builder.dropPrecedence(precedence);
                 break;
+            case "NONE":
+                type = Band.Type.NONE;
+                builder.ofType(type);
+                break;
+            case "MARK_YELLOW":
+                type = Band.Type.MARK_YELLOW;
+                builder.ofType(type);
+                break;
+            case "MARK_RED":
+                type = Band.Type.MARK_RED;
+                builder.ofType(type);
+                break;
             default:
                 nullIsIllegal(type, "The requested type " + typeStr + " is not defined for band.");
         }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/MeterCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/MeterCodec.java
index 5cf1d13..26b6677 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/MeterCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/MeterCodec.java
@@ -21,17 +21,14 @@
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.net.meter.Band;
 import org.onosproject.net.meter.Meter;
-import org.slf4j.Logger;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.slf4j.LoggerFactory.getLogger;
 
 
 /**
  * Meter JSON codec.
  */
 public final class MeterCodec extends JsonCodec<Meter> {
-    private final Logger log = getLogger(getClass());
 
     // JSON field names
     private static final String ID = "id";
@@ -51,7 +48,7 @@
     public ObjectNode encode(Meter meter, CodecContext context) {
         checkNotNull(meter, "Meter cannot be null");
         ObjectNode result = context.mapper().createObjectNode()
-                .put(ID, meter.id().toString())
+                .put(ID, meter.meterCellId().toString())
                 .put(LIFE, meter.life())
                 .put(PACKETS, meter.packetsSeen())
                 .put(BYTES, meter.bytesSeen())
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/MeterRequestCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/MeterRequestCodec.java
index d254428..d46e257 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/MeterRequestCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/MeterRequestCodec.java
@@ -26,29 +26,32 @@
 import org.onosproject.net.meter.DefaultMeterRequest;
 import org.onosproject.net.meter.Meter;
 import org.onosproject.net.meter.MeterRequest;
-import org.slf4j.Logger;
+import org.onosproject.net.meter.MeterScope;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.IntStream;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.onlab.util.Tools.nullIsIllegal;
-import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * MeterRequest JSON codec.
  */
 public final class MeterRequestCodec extends JsonCodec<MeterRequest> {
-    private final Logger log = getLogger(getClass());
 
     // JSON field names
     private static final String DEVICE_ID = "deviceId";
     private static final String UNIT = "unit";
     private static final String BANDS = "bands";
-    public static final String REST_APP_ID = "org.onosproject.rest";
+    private static final String SCOPE = "scope";
+    private static final String INDEX = "index";
+    private static final String REST_APP_ID = "org.onosproject.rest";
     private static final String MISSING_MEMBER_MESSAGE = " member is required in MeterRequest";
 
+    private ApplicationId applicationId;
+
     @Override
     public MeterRequest decode(ObjectNode json, CodecContext context) {
         if (json == null || !json.isObject()) {
@@ -56,14 +59,17 @@
         }
 
         final JsonCodec<Band> meterBandCodec = context.codec(Band.class);
-        CoreService coreService = context.getService(CoreService.class);
+
 
         // parse device id
         DeviceId deviceId = DeviceId.deviceId(nullIsIllegal(json.get(DEVICE_ID),
                 DEVICE_ID + MISSING_MEMBER_MESSAGE).asText());
 
         // application id
-        ApplicationId appId = coreService.registerApplication(REST_APP_ID);
+        if (applicationId == null) {
+            CoreService coreService = context.getService(CoreService.class);
+            applicationId = coreService.registerApplication(REST_APP_ID);
+        }
 
         // parse burst
         boolean burst = false;
@@ -83,6 +89,9 @@
             case "PKTS_PER_SEC":
                 meterUnit = Meter.Unit.PKTS_PER_SEC;
                 break;
+            case "BYTES_PER_SEC":
+                meterUnit = Meter.Unit.BYTES_PER_SEC;
+                break;
             default:
                 nullIsIllegal(meterUnit, "The requested unit " + unit + " is not defined for meter.");
         }
@@ -98,22 +107,39 @@
             });
         }
 
-        MeterRequest meterRequest;
-        if (burst) {
-            meterRequest = DefaultMeterRequest.builder()
-                    .fromApp(appId)
-                    .forDevice(deviceId)
-                    .withUnit(meterUnit)
-                    .withBands(bandList)
-                    .burst().add();
-        } else {
-            meterRequest = DefaultMeterRequest.builder()
-                    .fromApp(appId)
-                    .forDevice(deviceId)
-                    .withUnit(meterUnit)
-                    .withBands(bandList).add();
+        // parse scope and index
+        JsonNode scopeJson = json.get(SCOPE);
+        MeterScope scope = null;
+        if (scopeJson != null && !isNullOrEmpty(scopeJson.asText())) {
+            scope = MeterScope.of(scopeJson.asText());
         }
 
-        return meterRequest;
+        JsonNode indexJson = json.get(INDEX);
+        Long index = null;
+        if (indexJson != null && !isNullOrEmpty(indexJson.asText()) && scope != null) {
+            index = indexJson.asLong();
+        }
+
+        // build the final request
+        MeterRequest.Builder meterRequest = DefaultMeterRequest.builder();
+        if (scope != null) {
+            meterRequest.withScope(scope);
+        }
+
+        if (index != null) {
+            meterRequest.withIndex(index);
+        }
+
+        meterRequest.fromApp(applicationId)
+                .forDevice(deviceId)
+                .withUnit(meterUnit)
+                .withBands(bandList);
+
+        if (burst) {
+            meterRequest.burst();
+        }
+
+        return meterRequest.add();
     }
+
 }
diff --git a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterDriverProvider.java b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterDriverProvider.java
index cf7d198..b1422e8 100644
--- a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterDriverProvider.java
+++ b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterDriverProvider.java
@@ -152,7 +152,9 @@
     private void getMeterFeatures(DeviceId deviceId) {
         Collection<MeterFeatures> meterFeatures = Collections.emptySet();
         try {
-            meterFeatures = getMeterProgrammable(deviceId).getMeterFeatures().get(pollFrequency, TimeUnit.SECONDS);
+            if (isMeterProgrammable(deviceId)) {
+                meterFeatures = getMeterProgrammable(deviceId).getMeterFeatures().get(pollFrequency, TimeUnit.SECONDS);
+            }
         } catch (Exception e) {
             log.warn("Unable to get the Meter Features from {}, error: {}", deviceId, e.getMessage());
             log.debug("Exception: ", e);
@@ -160,6 +162,11 @@
         meterProviderService.pushMeterFeatures(deviceId, meterFeatures);
     }
 
+    private boolean isMeterProgrammable(DeviceId deviceId) {
+        Device device = deviceService.getDevice(deviceId);
+        return device.is(MeterProgrammable.class);
+    }
+
     private MeterProgrammable getMeterProgrammable(DeviceId deviceId) {
         Device device = deviceService.getDevice(deviceId);
         if (device.is(MeterProgrammable.class)) {
diff --git a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
index b2e6730..98e79a9 100644
--- a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
+++ b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
@@ -343,8 +343,12 @@
 
     @Override
     public Collection<Meter> getMeters(DeviceId deviceId) {
-        return store.getAllMeters().stream().filter(m ->
-                m.deviceId().equals(deviceId)).collect(Collectors.toList());
+        return store.getAllMeters(deviceId);
+    }
+
+    @Override
+    public Collection<Meter> getMeters(DeviceId deviceId, MeterScope scope) {
+        return store.getAllMeters(deviceId, scope);
     }
 
     @Override
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java
index 3831699..48cd4ba 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java
@@ -20,7 +20,6 @@
 import org.onosproject.net.meter.Band;
 import org.onosproject.net.meter.Meter;
 import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.runtime.PiMeterBand;
 import org.onosproject.net.pi.runtime.PiMeterCellConfig;
 import org.onosproject.net.pi.runtime.PiMeterCellId;
 import org.onosproject.net.pi.service.PiTranslationException;
@@ -36,7 +35,7 @@
         // Hides constructor.
     }
 
-    private static final int TRTCM_RATES = 2;
+    private static final int TCM_BANDS = 2;
 
     /**
      * Returns a PI meter config equivalent to the given meter, for the given pipeconf and device.
@@ -53,23 +52,52 @@
             throw new PiTranslationException("PI meter cell type must be PIPELINE_INDEPENDENT!");
         }
 
-        // FIXME: we might want to move this check to P4Runtime driver or protocol layer.
-        // In general, This check is more of P4Runtime limitation, we should do this check in the low level layer.
-        if (meter.bands().size() > TRTCM_RATES) {
-            throw new PiTranslationException("PI meter can not have more than 2 bands!");
+        // In general, this check is more of P4Runtime limitation, we should do this check in the low level layer.
+        // At the same time we could extend to support other configurations (for example srTCM).
+        // TODO implement SRTCM and TRTCM helper classes to improve readability of the code.
+        //  Or in future when we support other markers we can simply create two different methods.
+        if (meter.bands().size() != TCM_BANDS) {
+            throw new PiTranslationException("PI meter must have 2 bands in order to implement TCM metering!");
         }
 
-
-        PiMeterCellConfig.Builder builder = PiMeterCellConfig.builder();
-        for (Band band : meter.bands()) {
-            if (band.type() != Band.Type.NONE) {
-                throw new PiTranslationException("PI meter can not have band with other types except NONE!");
-            }
-
-            PiMeterBand piMeterBand = new PiMeterBand(band.rate(), band.burst());
-            builder.withMeterBand(piMeterBand);
+        final Band[] bands = meter.bands().toArray(new Band[0]);
+        // Validate proper config of the TCM settings.
+        if ((bands[0].type() != Band.Type.MARK_YELLOW && bands[0].type() != Band.Type.MARK_RED) ||
+                (bands[1].type() != Band.Type.MARK_YELLOW && bands[1].type() != Band.Type.MARK_RED) ||
+                (bands[0].type() == bands[1].type())) {
+            throw new PiTranslationException("PI TCM meter must have a MARK_YELLOW band and a MARK_RED band!");
         }
 
-        return builder.withMeterCellId((PiMeterCellId) meter.meterCellId()).build();
+        // Validate proper config of the trTCM settings
+        if (bands[0].burst() <= 0 || bands[1].burst() <= 0) {
+            throw new PiTranslationException("PI trTCM meter can not have band with burst <= 0!");
+        }
+        if (bands[0].rate() <= 0 || bands[1].rate() <= 0) {
+            throw new PiTranslationException("PI trTCM meter can not have band with rate <= 0!");
+        }
+
+        long cir, cburst, pir, pburst;
+        if (bands[0].type() == Band.Type.MARK_YELLOW) {
+            cir = bands[0].rate();
+            cburst = bands[0].burst();
+            pir = bands[1].rate();
+            pburst = bands[1].burst();
+        } else {
+            pir = bands[0].rate();
+            pburst = bands[0].burst();
+            cir = bands[1].rate();
+            cburst = bands[1].burst();
+        }
+
+        if (cir > pir) {
+            throw new PiTranslationException("PI trTCM meter must have a pir >= cir!");
+        }
+
+        return PiMeterCellConfig.builder()
+                .withCommittedBand(cir, cburst)
+                .withPeakBand(pir, pburst)
+                .withMeterCellId((PiMeterCellId) meter.meterCellId())
+                .build();
     }
+
 }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/meter/impl/DistributedMeterStore.java b/core/store/dist/src/main/java/org/onosproject/store/meter/impl/DistributedMeterStore.java
index afc6d94..7d8c7a5 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/meter/impl/DistributedMeterStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/meter/impl/DistributedMeterStore.java
@@ -365,6 +365,21 @@
     }
 
     @Override
+    public Collection<Meter> getAllMeters(DeviceId deviceId, MeterScope scope) {
+        if (scope.equals(MeterScope.globalScope())) {
+            return Collections2.transform(
+                    Collections2.filter(ImmutableSet.copyOf(metersMap.values()),
+                            (MeterData m) -> m.meter().meterCellId().type() == INDEX),
+                    MeterData::meter);
+        }
+        return Collections2.transform(
+                Collections2.filter(ImmutableSet.copyOf(metersMap.values()),
+                        (MeterData m) -> m.meter().meterCellId().type() == PIPELINE_INDEPENDENT &&
+                                ((PiMeterCellId) m.meter().meterCellId()).meterId().id().equals(scope.id())),
+                MeterData::meter);
+    }
+
+    @Override
     public void failedMeter(MeterOperation op, MeterFailReason reason) {
         // Meter ops failed (got notification from the sb)
         MeterKey key = MeterKey.key(op.meter().deviceId(), op.meter().meterCellId());
@@ -701,8 +716,9 @@
                                         return null;
                                     });
                                     notifyDelegate(new MeterEvent(MeterEvent.Type.METER_ADDED, data.meter()));
-                                // Update stats case
-                                } else if (data.meter().referenceCount() == 0) {
+                                // Update stats case - we report reference count zero only for INDEX based meters
+                                } else if (data.meter().referenceCount() == 0 &&
+                                        data.meter().meterCellId().type() == INDEX) {
                                     notifyDelegate(new MeterEvent(MeterEvent.Type.METER_REFERENCE_COUNT_ZERO,
                                             data.meter()));
                                 }
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 a7dbadd..9a04a35 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
@@ -23,7 +23,6 @@
 import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.meter.Band;
-import org.onosproject.net.meter.DefaultBand;
 import org.onosproject.net.meter.DefaultMeter;
 import org.onosproject.net.meter.DefaultMeterFeatures;
 import org.onosproject.net.meter.Meter;
@@ -42,7 +41,6 @@
 import org.onosproject.net.pi.service.PiTranslatedEntity;
 import org.onosproject.net.pi.service.PiTranslationException;
 import org.onosproject.p4runtime.api.P4RuntimeWriteClient.WriteRequest;
-import org.onosproject.p4runtime.api.P4RuntimeWriteClient.WriteResponse;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -52,12 +50,8 @@
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.locks.Lock;
-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;
 import static org.onosproject.p4runtime.api.P4RuntimeWriteClient.UpdateType;
 
 /**
@@ -178,15 +172,23 @@
             }
         }
 
-        // Reset all inconsistent meter cells to default state
+        // Reset all inconsistent meter cells to the default config
         if (!inconsistentOrDefaultCells.isEmpty()) {
             WriteRequest request = client.write(p4DeviceId, pipeconf);
             for (PiMeterCellId cellId : inconsistentOrDefaultCells) {
                 PiMeterCellHandle handle = PiMeterCellHandle.of(deviceId, cellId);
                 appendEntryToWriteRequestOrSkip(request, handle, PiMeterCellConfig.reset(cellId));
             }
-            WriteResponse response = request.submitSync();
-            meterMirror.applyWriteResponse(response);
+
+            request.submit().whenComplete((response, ex) -> {
+                if (ex != null) {
+                    log.error("Exception resetting inconsistent meter entries", ex);
+                } else {
+                    log.debug("Successfully removed {} out of {} inconsistent meter entries",
+                            response.success().size(), response.all().size());
+                }
+                response.success().forEach(entity -> meterMirror.remove((PiMeterCellHandle) entity.handle()));
+            });
         }
 
         return CompletableFuture.completedFuture(meters);
@@ -219,10 +221,11 @@
                          "one in in translation store: device={}, store=Default", deviceId, config);
             } else {
                 log.debug("Configs obtained from device: {} and present in the store are default, " +
-                          "skipping the forge section");
+                          "skipping the forge section", deviceId);
             }
             return null;
         }
+
         // The config is not consistent
         if (!translatedEntity.get().translated().equals(config)) {
             log.warn("Meter Cell Config obtained from device {} is different from " +
@@ -230,20 +233,20 @@
                      deviceId, config, translatedEntity.get().translated());
             return null;
         }
+
+        // Big problems in the mirror ? This could happen in case of issues with
+        // the eventual consistent maps used in the AbstractDistributedP4RuntimeMirror
         if (timedEntry == null) {
             log.warn("Meter entry handle not found in device mirror: {}", handle);
             return null;
         }
 
-        // Forge a meter with MeterCellId, Bands and DeviceId
+        Meter original = translatedEntity.get().original();
+        // Forge a meter with MeterCellId, Bands and DeviceId using the original value.
         // Other values are not required because we cannot retrieve them from the south
         DefaultMeter meter = (DefaultMeter) DefaultMeter.builder()
-                            .withBands(config.meterBands().stream().map(b -> DefaultBand.builder()
-                                    .withRate(b.rate())
-                                    .burstSize(b.burst())
-                                    .ofType(Band.Type.NONE)
-                                    .build()).collect(Collectors.toList()))
-                            .withCellId(config.cellId())
+                            .withBands(original.bands())
+                            .withCellId(original.meterCellId())
                             .forDevice(deviceId)
                             .build();
         meter.setState(MeterState.ADDED);
@@ -270,7 +273,7 @@
     /**
      * P4 meter features builder.
      */
-    public class P4RuntimeMeterFeaturesBuilder {
+    public static class P4RuntimeMeterFeaturesBuilder {
         private final PiMeterModel piMeterModel;
         private DeviceId deviceId;
 
@@ -290,9 +293,7 @@
          * @return the meter features object
          */
         public MeterFeatures build() {
-            /*
-             * We set the basic values before to extract the other information.
-             */
+            // We set the basic values before to extract the other information.
             MeterFeatures.Builder builder = DefaultMeterFeatures.builder()
                     .forDevice(deviceId)
                     // The scope value will be PiMeterId
@@ -301,36 +302,31 @@
                     .withMaxColors(PI_METER_MAX_COLOR)
                     .withStartIndex(PI_METER_START_INDEX)
                     .withEndIndex(piMeterModel.size() - 1);
-            /*
-             * Pi meter only support NONE type
-             */
+
+            // p4rt meters support MARK_YELLOW (committed config) and
+            // MARK_RED (peak config) band types.
             Set<Band.Type> bands = Sets.newHashSet();
-            bands.add(Band.Type.NONE);
+            bands.add(Band.Type.MARK_YELLOW);
+            bands.add(Band.Type.MARK_RED);
             builder.withBandTypes(bands);
-            /*
-             * We extract the supported units;
-             */
+
+            // We extract the supported units;
             Set<Meter.Unit> units = Sets.newHashSet();
             if (piMeterModel.unit() == PiMeterModel.Unit.BYTES) {
-                units.add(Meter.Unit.KB_PER_SEC);
+                units.add(Meter.Unit.BYTES_PER_SEC);
             } else if (piMeterModel.unit() == PiMeterModel.Unit.PACKETS) {
                 units.add(Meter.Unit.PKTS_PER_SEC);
             }
-            builder.withUnits(units);
-            /*
-             * Burst is supported ?
-             */
-            builder.hasBurst(true);
-            /*
-             * Stats are supported ?
-             */
-            builder.hasStats(false);
 
-            return builder.build();
+            return builder.withUnits(units)
+                    .hasBurst(true)
+                    .hasStats(false)
+                    .build();
         }
 
         /**
          * To build an empty meter features.
+         *
          * @param deviceId the device id
          * @return the meter features
          */
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 88f0639..3584527 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
@@ -32,37 +32,19 @@
         extends AbstractEntityCodec<PiMeterCellConfig, PiMeterCellHandle,
         P4RuntimeOuterClass.MeterEntry, Object> {
 
-    static P4RuntimeOuterClass.MeterConfig getP4Config(PiMeterCellConfig piConfig)
-            throws CodecException {
+    static P4RuntimeOuterClass.MeterConfig getP4Config(PiMeterCellConfig piConfig) {
         // The config has no band, we don't have to create a P4RT meter config
         if (piConfig.isDefaultConfig()) {
             return null;
         }
-        // If it is not a reset operation, the config must be a modify config and has exactly 2 bands
-        if (!piConfig.isModifyConfig()) {
-            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;
-        // The band with bigger burst is peak if rate of them is equal.
-        if (bands[0].rate() > bands[1].rate() ||
-                (bands[0].rate() == bands[1].rate() &&
-                        bands[0].burst() >= bands[1].burst())) {
-            cir = bands[1].rate();
-            cburst = bands[1].burst();
-            pir = bands[0].rate();
-            pburst = bands[0].burst();
-        } else {
-            cir = bands[0].rate();
-            cburst = bands[0].burst();
-            pir = bands[1].rate();
-            pburst = bands[1].burst();
-        }
+
+        final PiMeterBand committedBand = piConfig.committedBand();
+        final PiMeterBand peakBand = piConfig.peakBand();
         return P4RuntimeOuterClass.MeterConfig.newBuilder()
-                .setCir(cir)
-                .setCburst(cburst)
-                .setPir(pir)
-                .setPburst(pburst)
+                .setCir(committedBand.rate())
+                .setCburst(committedBand.burst())
+                .setPir(peakBand.rate())
+                .setPburst(peakBand.burst())
                 .build();
     }
 
@@ -70,7 +52,7 @@
     protected P4RuntimeOuterClass.MeterEntry encode(
             PiMeterCellConfig piEntity, Object ignored, PiPipeconf pipeconf,
             P4InfoBrowser browser)
-            throws P4InfoBrowser.NotFoundException, CodecException {
+            throws P4InfoBrowser.NotFoundException {
         final int meterId = browser.meters().getByName(
                 piEntity.cellId().meterId().id()).getPreamble().getId();
         P4RuntimeOuterClass.MeterEntry.Builder builder =
@@ -139,9 +121,8 @@
         PiMeterCellConfig.Builder builder =
             PiMeterCellConfig.builder().withMeterCellId(cellId);
         if (p4Config != null) {
-            builder = builder
-                .withMeterBand(new PiMeterBand(p4Config.getCir(), p4Config.getCburst()))
-                .withMeterBand(new PiMeterBand(p4Config.getPir(), p4Config.getPburst()));
+            builder.withCommittedBand(p4Config.getCir(), p4Config.getCburst())
+                    .withPeakBand(p4Config.getPir(), p4Config.getPburst());
         }
         return builder.build();
     }
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/MetersWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/MetersWebResource.java
index 93d924a..f341bed 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/MetersWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/MetersWebResource.java
@@ -18,13 +18,19 @@
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.meter.DefaultMeterRequest;
 import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterCellId;
 import org.onosproject.net.meter.MeterId;
 import org.onosproject.net.meter.MeterRequest;
+import org.onosproject.net.meter.MeterScope;
 import org.onosproject.net.meter.MeterService;
+import org.onosproject.net.pi.model.PiMeterId;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
 import org.onosproject.rest.AbstractWebResource;
 import org.slf4j.Logger;
 
@@ -63,6 +69,9 @@
     private final ObjectNode root = mapper().createObjectNode();
     private final ArrayNode metersNode = root.putArray("meters");
 
+    private static final String REST_APP_ID = "org.onosproject.rest";
+    private ApplicationId applicationId;
+
     /**
      * Returns all meters of all devices.
      *
@@ -114,16 +123,72 @@
     public Response getMeterByDeviceIdAndMeterId(@PathParam("deviceId") String deviceId,
                                                  @PathParam("meterId") String meterId) {
         DeviceId did = DeviceId.deviceId(deviceId);
-        MeterId mid = MeterId.meterId(Long.valueOf(meterId));
+        MeterCellId mid = MeterId.meterId(Long.valueOf(meterId));
         MeterService meterService = get(MeterService.class);
         final Meter meter = nullIsNotFound(meterService.getMeter(did, mid),
-                METER_NOT_FOUND + mid.id());
+                METER_NOT_FOUND + mid);
 
         metersNode.add(codec(Meter.class).encode(meter, this));
         return ok(root).build();
     }
 
     /**
+     * Returns a meter by the meter cell id.
+     *
+     * @param deviceId device identifier
+     * @param scope scope identifier
+     * @param index index
+     * @return 200 OK with a meter, return 404 if no entry has been found
+     * @onos.rsModel Meter
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("{deviceId}/{scope}/{index}")
+    public Response getMeterByDeviceIdAndMeterCellId(@PathParam("deviceId") String deviceId,
+                                                     @PathParam("scope") String scope,
+                                                     @PathParam("index") String index) {
+        DeviceId did = DeviceId.deviceId(deviceId);
+        MeterScope meterScope = MeterScope.of(scope);
+        long meterIndex = Long.parseLong(index);
+        MeterCellId meterCellId;
+        if (meterScope.equals(MeterScope.globalScope())) {
+            meterCellId = MeterId.meterId(meterIndex);
+        } else {
+            meterCellId = PiMeterCellId.ofIndirect(PiMeterId.of(meterScope.id()), meterIndex);
+        }
+
+        MeterService meterService = get(MeterService.class);
+        final Meter meter = nullIsNotFound(meterService.getMeter(did, meterCellId),
+                METER_NOT_FOUND + meterCellId);
+
+        metersNode.add(codec(Meter.class).encode(meter, this));
+        return ok(root).build();
+    }
+
+    /**
+     * Returns a collection of meters by the device id and meter scope.
+     *
+     * @param deviceId device identifier
+     * @param scope scope identifier
+     * @return 200 OK with array of meters which belongs to specified device
+     * @onos.rsModel Meters
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("scope/{deviceId}/{scope}")
+    public Response getMetersByDeviceIdAndScope(@PathParam("deviceId") String deviceId,
+                                                @PathParam("scope") String scope) {
+        DeviceId did = DeviceId.deviceId(deviceId);
+        MeterScope meterScope = MeterScope.of(scope);
+        MeterService meterService = get(MeterService.class);
+        final Iterable<Meter> meters = meterService.getMeters(did, meterScope);
+        if (meters != null) {
+            meters.forEach(meter -> metersNode.add(codec(Meter.class).encode(meter, this)));
+        }
+        return ok(root).build();
+    }
+
+    /**
      * Creates new meter rule. Creates and installs a new meter rule for the
      * specified device.
      *
@@ -170,7 +235,7 @@
     }
 
     /**
-     * Removes the specified meter.
+     * Removes the meter by device id and meter id.
      *
      * @param deviceId device identifier
      * @param meterId  meter identifier
@@ -179,65 +244,56 @@
     @DELETE
     @Path("{deviceId}/{meterId}")
     public Response deleteMeterByDeviceIdAndMeterId(@PathParam("deviceId") String deviceId,
-                                                @PathParam("meterId") String meterId) {
+                                                    @PathParam("meterId") String meterId) {
         DeviceId did = DeviceId.deviceId(deviceId);
-        MeterId mid = MeterId.meterId(Long.valueOf(meterId));
+        MeterCellId mid = MeterId.meterId(Long.valueOf(meterId));
+        MeterRequest meterRequest = deleteRequest(did);
 
         MeterService meterService = get(MeterService.class);
-        final Meter tmpMeter = meterService.getMeter(did, mid);
-        if (tmpMeter != null) {
-            final MeterRequest meterRequest = meterToMeterRequest(tmpMeter, "REMOVE");
-            if (meterRequest != null) {
-                meterService.withdraw(meterRequest, tmpMeter.id());
-            }
-        } else {
-            log.warn("Meter {}, is not present", tmpMeter);
-        }
+        meterService.withdraw(meterRequest, mid);
+
         return Response.noContent().build();
     }
 
     /**
-     * Converts a meter instance to meterRequest instance with a certain operation.
+     * Removes the meter by the device id and meter cell id.
      *
-     * @param meter     meter instance
-     * @param operation operation
-     * @return converted meterRequest instance
+     * @param deviceId device identifier
+     * @param scope scope identifier
+     * @param index index
+     * @return 204 NO CONTENT
      */
-    private MeterRequest meterToMeterRequest(Meter meter, String operation) {
-        MeterRequest.Builder builder;
-        MeterRequest meterRequest;
-
-        if (meter == null) {
-            return null;
-        }
-
-        if (meter.isBurst()) {
-            builder = DefaultMeterRequest.builder()
-                    .fromApp(meter.appId())
-                    .forDevice(meter.deviceId())
-                    .withUnit(meter.unit())
-                    .withBands(meter.bands())
-                    .burst();
+    @DELETE
+    @Path("{deviceId}/{scope}/{index}")
+    public Response deleteMeterByDeviceIdAndMeterCellId(@PathParam("deviceId") String deviceId,
+                                                        @PathParam("scope") String scope,
+                                                        @PathParam("index") String index) {
+        DeviceId did = DeviceId.deviceId(deviceId);
+        MeterScope meterScope = MeterScope.of(scope);
+        long meterIndex = Long.parseLong(index);
+        MeterCellId meterCellId;
+        if (meterScope.equals(MeterScope.globalScope())) {
+            meterCellId = MeterId.meterId(meterIndex);
         } else {
-            builder = DefaultMeterRequest.builder()
-                    .fromApp(meter.appId())
-                    .forDevice(meter.deviceId())
-                    .withUnit(meter.unit())
-                    .withBands(meter.bands());
+            meterCellId = PiMeterCellId.ofIndirect(PiMeterId.of(meterScope.id()), meterIndex);
+        }
+        MeterRequest meterRequest = deleteRequest(did);
+
+        MeterService meterService = get(MeterService.class);
+        meterService.withdraw(meterRequest, meterCellId);
+
+        return Response.noContent().build();
+    }
+
+    private MeterRequest deleteRequest(DeviceId did) {
+        CoreService coreService = getService(CoreService.class);
+        if (applicationId == null) {
+            applicationId = coreService.registerApplication(REST_APP_ID);
         }
 
-        switch (operation) {
-            case "ADD":
-                meterRequest = builder.add();
-                break;
-            case "REMOVE":
-                meterRequest = builder.remove();
-                break;
-            default:
-                log.warn("Invalid operation {}.", operation);
-                return null;
-        }
-
-        return meterRequest;
+        return DefaultMeterRequest.builder()
+                .forDevice(did)
+                .fromApp(applicationId)
+                .remove();
     }
 }
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java
index d7db030..e9efb18 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java
@@ -442,7 +442,7 @@
     public void testMeterSingleDeviceWithId() {
         setupMockMeters();
 
-        expect(mockMeterService.getMeter(anyObject(), anyObject()))
+        expect(mockMeterService.getMeter(anyObject(), anyObject(MeterCellId.class)))
                 .andReturn(meter5).anyTimes();
         replay(mockMeterService);
         replay(mockDeviceService);
@@ -467,7 +467,7 @@
     public void testMeterByDeviceIdAndMeterId() {
         setupMockMeters();
 
-        expect(mockMeterService.getMeter(anyObject(), anyObject()))
+        expect(mockMeterService.getMeter(anyObject(), anyObject(MeterCellId.class)))
                 .andReturn(null).anyTimes();
         replay(mockMeterService);
 
@@ -526,9 +526,7 @@
     @Test
     public void testDelete() {
         setupMockMeters();
-        expect(mockMeterService.getMeter(anyObject(), anyObject()))
-                .andReturn(meter5).anyTimes();
-        mockMeterService.withdraw(anyObject(), anyObject());
+        mockMeterService.withdraw(anyObject(), anyObject(MeterCellId.class));
         expectLastCall();
         replay(mockMeterService);