[ONOS-7051] Support for P4Runtime meters

Change-Id: Id71374af65aeb84b71636b4ec230dc6001a77a8b
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 5b75ab7..9f9479c 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
@@ -45,7 +45,14 @@
         /**
          * Defines an experimental meter band.
          */
-        EXPERIMENTAL
+        EXPERIMENTAL,
+
+        /**
+         * Defines a meter band with no action, used to mark
+         * packets internally in the pipeline, i.e. without
+         * modifying the packet headers.
+         */
+        NONE,
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/meter/DefaultMeter.java b/core/api/src/main/java/org/onosproject/net/meter/DefaultMeter.java
index 7be5e90..ad0256e 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/DefaultMeter.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/DefaultMeter.java
@@ -25,6 +25,7 @@
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.meter.MeterCellId.MeterCellType.INDEX;
 
 /**
  * A default implementation of a meter.
@@ -32,7 +33,7 @@
 public final class DefaultMeter implements Meter, MeterEntry  {
 
 
-    private final MeterId id;
+    private final MeterCellId cellId;
     private final ApplicationId appId;
     private final Unit unit;
     private final boolean burst;
@@ -45,11 +46,10 @@
     private long packets;
     private long bytes;
 
-    private DefaultMeter(DeviceId deviceId, MeterId id, ApplicationId appId,
-                        Unit unit, boolean burst,
-                        Collection<Band> bands) {
+    private DefaultMeter(DeviceId deviceId, MeterCellId cellId, ApplicationId appId,
+                         Unit unit, boolean burst, Collection<Band> bands) {
         this.deviceId = deviceId;
-        this.id = id;
+        this.cellId = cellId;
         this.appId = appId;
         this.unit = unit;
         this.burst = burst;
@@ -63,7 +63,16 @@
 
     @Override
     public MeterId id() {
-        return id;
+        // Workaround until we remove this method. Deprecated in 1.13.
+        // Should use meterCellId() instead.
+        return cellId.type() == INDEX
+                ? (MeterId) cellId
+                : MeterId.meterId((cellId.hashCode()));
+    }
+
+    @Override
+    public MeterCellId meterCellId() {
+        return cellId;
     }
 
     @Override
@@ -144,7 +153,7 @@
     public String toString() {
         return toStringHelper(this)
                 .add("device", deviceId)
-                .add("id", id)
+                .add("cellId", cellId)
                 .add("appId", appId.name())
                 .add("unit", unit)
                 .add("isBurst", burst)
@@ -161,7 +170,7 @@
             return false;
         }
         DefaultMeter that = (DefaultMeter) o;
-        return Objects.equal(id, that.id) &&
+        return Objects.equal(cellId, that.cellId) &&
                 Objects.equal(appId, that.appId) &&
                 Objects.equal(unit, that.unit) &&
                 Objects.equal(deviceId, that.deviceId);
@@ -169,19 +178,18 @@
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(id, appId, unit, deviceId);
+        return Objects.hashCode(cellId, appId, unit, deviceId);
     }
 
     public static final class Builder implements Meter.Builder {
 
-        private MeterId id;
+        private MeterCellId cellId;
         private ApplicationId appId;
         private Unit unit = Unit.KB_PER_SEC;
         private boolean burst = false;
         private Collection<Band> bands;
         private DeviceId deviceId;
 
-
         @Override
         public Meter.Builder forDevice(DeviceId deviceId) {
             this.deviceId = deviceId;
@@ -190,7 +198,13 @@
 
         @Override
         public Meter.Builder withId(MeterId id) {
-            this.id = id;
+            this.withCellId(id);
+            return this;
+        }
+
+        @Override
+        public Meter.Builder withCellId(MeterCellId cellId) {
+            this.cellId = cellId;
             return this;
         }
 
@@ -224,8 +238,8 @@
             checkNotNull(bands, "Must have bands.");
             checkArgument(!bands.isEmpty(), "Must have at least one band.");
             checkNotNull(appId, "Must have an application id");
-            checkNotNull(id, "Must specify a meter id");
-            return new DefaultMeter(deviceId, id, appId, unit, burst, bands);
+            checkArgument(cellId != null, "Must specify a cell id.");
+            return new DefaultMeter(deviceId, cellId, appId, unit, burst, bands);
         }
 
 
diff --git a/core/api/src/main/java/org/onosproject/net/meter/Meter.java b/core/api/src/main/java/org/onosproject/net/meter/Meter.java
index 3cb4511..3d7544e 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/Meter.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/Meter.java
@@ -17,13 +17,14 @@
 
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.pi.service.PiTranslatable;
 
 import java.util.Collection;
 
 /**
- * Represents a generalized meter to be deployed on a device.
+ * Represents a generalized meter cell configuration to be deployed on a device.
  */
-public interface Meter {
+public interface Meter extends PiTranslatable {
 
     enum Unit {
         /**
@@ -48,10 +49,19 @@
      * This meters id.
      *
      * @return a meter id
+     * @deprecated in Nightingale release (version 1.13.0). Use {@link #meterCellId()} instead.
      */
+    @Deprecated
     MeterId id();
 
     /**
+     * Returns the meter cell identifier of this meter.
+     *
+     * @return a meter identifier
+     */
+    MeterCellId meterCellId();
+
+    /**
      * The id of the application which created this meter.
      *
      * @return an application id
@@ -132,10 +142,21 @@
          *
          * @param id a e
          * @return this
+         * @deprecated in Nightingale release (version 1.13.0). Use {@link
+         * #withCellId(MeterCellId)} instead.
          */
+        @Deprecated
         Builder withId(MeterId id);
 
         /**
+         * Assigns the id to this meter cell.
+         *
+         * @param meterId a meter cell identifier
+         * @return this
+         */
+        Builder withCellId(MeterCellId meterId);
+
+        /**
          * Assigns the application that built this meter.
          *
          * @param appId an application id
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterCellId.java b/core/api/src/main/java/org/onosproject/net/meter/MeterCellId.java
new file mode 100644
index 0000000..900b79c
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterCellId.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017-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.meter;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * A representation of a meter cell identifier.
+ * Uniquely identifies a meter cell in the scope of a single device.
+ */
+@Beta
+public interface MeterCellId {
+
+    /**
+     * Types of meter cell identifier.
+     */
+    enum MeterCellType {
+        /**
+         * Signifies that the meter cell can be identified with an integer index.
+         * Valid for pipelines that defines one global meter table, e.g. as with
+         * OpenFlow meters.
+         */
+        INDEX,
+
+        /**
+         * Signifies that the meter cell identifier is pipeline-independent.
+         */
+        PIPELINE_INDEPENDENT
+    }
+
+    /**
+     * Return the type of this meter cell identifier.
+     *
+     * @return type
+     */
+    MeterCellType type();
+}
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterId.java b/core/api/src/main/java/org/onosproject/net/meter/MeterId.java
index e735984..7c28880 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterId.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterId.java
@@ -20,15 +20,26 @@
 import static com.google.common.base.Preconditions.checkArgument;
 
 /**
- * A representation of a meter identifier.
- * Uniquely identifies a meter in the scope of a single device.
+ * A representation of a meter cell identifier. Uniquely identifies a meter cell
+ * in the scope of a single device.
  * <p>
- * The meter_id field uniquely identifies a meter within a switch. Meters are
- * defined starting with meter_id=1 up to the maximum number of meters that the
- * switch can support. The OpenFlow protocol also defines some additional
- * virtual meters that can not be associated with flows:
+ * This ID uniquely identifies a meter cell within in a switch that maintains
+ * only one meter instance. If a switch supports multiple meter instances (like
+ * in P4), then {@link org.onosproject.net.pi.runtime.PiMeterCellId} should be
+ * used. In this case, meter cells are defined starting with id=1 up to the
+ * maximum number of cells that the switch can support. The OpenFlow protocol
+ * also defines some additional virtual meter cells that can not be associated
+ * with flows.
  */
-public final class MeterId extends Identifier<Long> {
+public final class MeterId extends Identifier<Long> implements MeterCellId {
+
+    // TODO: should rename this class to SimpleMeterCellId to distinguish it
+    // from PiMeterId and PiMeterCellId. From ONOS-7051, to follow the P4
+    // abstraction there can be multiple instances of a meter in a data plane,
+    // each meter instance is made of multiple cells. This class is based on the
+    // OpenFlow abstraction where, following P4 terminology, the data plane
+    // maintains only one meter instance. What is described here as a MeterId is
+    // indeed the identifier of a meter cell.
 
     /**  Flow meters can use any number up to MAX. */
     public static final long MAX = 0xFFFF0000L;
@@ -63,4 +74,9 @@
         checkArgument(id <= MAX, "id cannot be larger than {}", MAX);
         return new MeterId(id);
     }
+
+    @Override
+    public MeterCellType type() {
+        return MeterCellType.INDEX;
+    }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterProgrammable.java b/core/api/src/main/java/org/onosproject/net/meter/MeterProgrammable.java
index 06a303f..4b8ea07 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterProgrammable.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterProgrammable.java
@@ -42,6 +42,4 @@
      * @return completable future with the collection of meters
      */
     CompletableFuture<Collection<Meter>> getMeters();
-}
-
-
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterRequest.java b/core/api/src/main/java/org/onosproject/net/meter/MeterRequest.java
index bba2388..9060b95 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterRequest.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterRequest.java
@@ -141,7 +141,6 @@
          * @return a meter request
          */
         MeterRequest remove();
-
     }
 
 }
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java
index 21c09de..5ef452b 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java
@@ -36,5 +36,10 @@
     /**
      * Action profile group member.
      */
-    GROUP_MEMBER
+    GROUP_MEMBER,
+
+    /**
+     * Meter config.
+     */
+    METER_CELL_CONFIG
 }
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
new file mode 100644
index 0000000..efa53ff
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterBand.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017-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;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Represents a band used within a meter.
+ */
+@Beta
+public class PiMeterBand {
+    private final long rate;
+    private final long burst;
+
+    /**
+     * Creates a band with rate and burst.
+     *
+     * @param rate  rate of this band
+     * @param burst burst of this band
+     */
+    public PiMeterBand(long rate, long burst) {
+        this.rate = rate;
+        this.burst = burst;
+    }
+
+    /**
+     * Returns the rate of this band.
+     *
+     * @return rate of this band
+     */
+    public long rate() {
+        return rate;
+    }
+
+    /**
+     * Returns the burst of this band.
+     *
+     * @return burst of this band
+     */
+    public long burst() {
+        return burst;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(rate, burst);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof PiMeterBand) {
+            PiMeterBand that = (PiMeterBand) obj;
+            return Objects.equals(rate, that.rate) &&
+                    Objects.equals(burst, that.burst);
+
+        }
+        return false;
+    }
+
+    public String toString() {
+        return toStringHelper(this)
+                .add("rate", rate)
+                .add("burst", burst).toString();
+    }
+}
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
new file mode 100644
index 0000000..96ba124
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellConfig.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2017-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;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Configuration of a meter cell of a protocol-independent pipeline.
+ */
+@Beta
+public final class PiMeterCellConfig implements PiEntity {
+
+    private final PiMeterCellId cellId;
+    private final ImmutableList<PiMeterBand> piMeterBands;
+
+    /**
+     * Creates a new meter cell configuration for the given cell identifier and meter bands.
+     *
+     * @param cellId  meter cell identifier
+     * @param piMeterBands meter bands
+     */
+    private PiMeterCellConfig(PiMeterCellId cellId, Collection<PiMeterBand> piMeterBands) {
+        this.cellId = cellId;
+        this.piMeterBands = ImmutableList.copyOf(piMeterBands);
+    }
+
+    /**
+     * Returns the cell identifier.
+     *
+     * @return cell identifier
+     */
+    public PiMeterCellId cellId() {
+        return cellId;
+    }
+
+    /**
+     * Returns the collection of bands of this cell.
+     *
+     * @return meter bands
+     */
+    public Collection<PiMeterBand> meterBands() {
+        return piMeterBands;
+    }
+
+    @Override
+    public PiEntityType piEntityType() {
+        return PiEntityType.METER_CELL_CONFIG;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof PiMeterCellConfig)) {
+            return false;
+        }
+        PiMeterCellConfig that = (PiMeterCellConfig) o;
+
+        return piMeterBands.containsAll((that.piMeterBands)) &&
+                piMeterBands.size() == that.piMeterBands.size() &&
+                Objects.equal(cellId, that.cellId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(cellId, piMeterBands);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("cellId", cellId)
+                .add("meterBands", piMeterBands)
+                .toString();
+    }
+
+    /**
+     * Returns a meter cell configuration builder.
+     *
+     * @return a new builder
+     */
+    public static PiMeterCellConfig.Builder builder() {
+        return new PiMeterCellConfig.Builder();
+    }
+
+    public static final class Builder {
+        private  PiMeterCellId cellId;
+        private List<PiMeterBand> bands = new ArrayList<>();
+
+
+        private Builder() {
+            // Hides constructor.
+        }
+
+        /**
+         * Sets the meter cell identifier for this meter.
+         *
+         * @param meterCellId meter cell identifier
+         * @return this
+         */
+        public PiMeterCellConfig.Builder withMeterCellId(PiMeterCellId meterCellId) {
+            this.cellId = meterCellId;
+            return this;
+        }
+
+
+        /**
+         * Sets a meter band of this meter.
+         *
+         * @param band meter band
+         * @return this
+         */
+        public PiMeterCellConfig.Builder withMeterBand(PiMeterBand band) {
+            this.bands.add(band);
+            return this;
+        }
+
+        /**
+         * Builds the meter cell configuration.
+         *
+         * @return a new meter cell configuration
+         */
+        public PiMeterCellConfig build() {
+            checkNotNull(cellId);
+            return new PiMeterCellConfig(cellId, bands);
+        }
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellId.java
new file mode 100644
index 0000000..09cf7bf
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterCellId.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2017-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;
+import com.google.common.base.Objects;
+import org.onosproject.net.meter.MeterCellId;
+import org.onosproject.net.pi.model.PiMeterId;
+import org.onosproject.net.pi.model.PiMeterType;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Identifier of a meter cell in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiMeterCellId implements MeterCellId {
+
+    private final PiMeterId meterId;
+    private final PiMeterType meterType;
+    private final long index;
+    private final PiTableEntry tableEntry;
+
+    private PiMeterCellId(PiMeterId meterId, PiMeterType meterType, long index,
+                            PiTableEntry tableEntry) {
+        this.meterId = meterId;
+        this.meterType = meterType;
+        this.index = index;
+        this.tableEntry = tableEntry;
+    }
+
+    /**
+     * Returns the identifier of the meter instance where this cell is contained.
+     *
+     * @return meter identifier
+     */
+    public PiMeterId meterId() {
+        return meterId;
+    }
+
+    /**
+     * Returns the type of the meter identified.
+     *
+     * @return meter type
+     */
+    public PiMeterType meterType() {
+        return meterType;
+    }
+
+    /**
+     * Returns the meter index to which this cell ID is associated.
+     * Meaningful only if the meter is of type {@link PiMeterType#INDIRECT}.
+     *
+     * @return meter index
+     */
+    public long index() {
+        return index;
+    }
+
+    /**
+     * Returns the table entry to which this cell ID is associated.
+     * Meaningful only if the meter is of type {@link PiMeterType#DIRECT}, otherwise returns null.
+     *
+     * @return PI table entry or null
+     */
+    public PiTableEntry tableEntry() {
+        return tableEntry;
+    }
+
+    @Override
+    public MeterCellType type() {
+        return MeterCellType.PIPELINE_INDEPENDENT;
+    }
+
+    /**
+     * Return a direct meter cell ID for the given meter ID and table entry.
+     *
+     * @param meterId  meter ID
+     * @param tableEntry table entry
+     * @return meter cell ID
+     */
+    public static PiMeterCellId ofDirect(PiMeterId meterId, PiTableEntry tableEntry) {
+        checkNotNull(meterId);
+        checkNotNull(tableEntry);
+        return new PiMeterCellId(meterId, PiMeterType.DIRECT, -1, tableEntry);
+    }
+
+    /**
+     * Return an indirect meter cell ID for the given meter ID and index.
+     *
+     * @param meterId meter ID
+     * @param index     index
+     * @return meter cell ID
+     */
+    public static PiMeterCellId ofIndirect(PiMeterId meterId, long index) {
+        checkNotNull(meterId);
+        checkArgument(index >= 0, "Index must be a positive number");
+        return new PiMeterCellId(meterId, PiMeterType.INDIRECT, index, null);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final PiMeterCellId other = (PiMeterCellId) obj;
+        return Objects.equal(this.meterId, other.meterId)
+                && Objects.equal(this.meterType, other.meterType)
+                && Objects.equal(this.index, other.index)
+                && Objects.equal(this.tableEntry, other.tableEntry);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(meterId, meterType, index, tableEntry);
+    }
+
+    @Override
+    public String toString() {
+        return meterId.toString() + ':'
+                + (meterType == PiMeterType.DIRECT ? tableEntry.toString() : String.valueOf(index));
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterHandle.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterHandle.java
new file mode 100644
index 0000000..ad2af9d
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMeterHandle.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-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;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Global identifier of a PI meter cell configuration applied to a device, uniquely defined
+ * by a device ID and meter cell ID.
+ */
+@Beta
+public final class PiMeterHandle extends PiHandle<PiMeterCellConfig> {
+
+    private PiMeterHandle(DeviceId deviceId, PiMeterCellConfig meterCellConfig) {
+        super(deviceId, meterCellConfig);
+    }
+
+    /**
+     * Creates a new handle for the given device ID and PI meter cell configuration.
+     *
+     * @param deviceId device ID
+     * @param meterCellConfig meter config
+     * @return PI meter handle
+     */
+    public static PiMeterHandle of(DeviceId deviceId,
+                                   PiMeterCellConfig meterCellConfig) {
+        return new PiMeterHandle(deviceId, meterCellConfig);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(deviceId(),
+                                piEntity().cellId());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PiMeterHandle that = (PiMeterHandle) o;
+        return Objects.equal(deviceId(), that.deviceId()) &&
+                Objects.equal(piEntity().cellId(),
+                              that.piEntity().cellId());
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("deviceId", deviceId())
+                .add("meterCellId", piEntity().cellId())
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiMeterTranslationStore.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiMeterTranslationStore.java
new file mode 100644
index 0000000..ddd6ab3
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiMeterTranslationStore.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017-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.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+
+/**
+ * A PI translation store that keeps track of which meters have been
+ * translated to which PI meters.
+ */
+@Beta
+public interface PiMeterTranslationStore
+        extends PiTranslationStore<Meter, PiMeterCellConfig> {
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiMeterTranslator.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiMeterTranslator.java
new file mode 100644
index 0000000..b52ad64
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiMeterTranslator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017-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.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+
+/**
+ * A translator of meters to PI Meter Configs.
+ */
+@Beta
+public interface PiMeterTranslator
+        extends PiTranslator<Meter, PiMeterCellConfig> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java
index c7a95c3..20cef35 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java
@@ -38,4 +38,11 @@
      * @return group translator
      */
     PiGroupTranslator groupTranslator();
+
+    /**
+     * Returns a meter translator.
+     *
+     * @return meter translator
+     */
+    PiMeterTranslator meterTranslator();
 }