[SDFAB-352][SDFAB-353] Retrieve MeterFeatures from the P4RT southbound, Extend MeterProviderService and revisit MeterStore

Change-Id: If0dae53643988cb551ff5020abd792cb6d33ff6b
diff --git a/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterFeatures.java b/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterFeatures.java
index bd5d998..3806250 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterFeatures.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterFeatures.java
@@ -22,6 +22,7 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
@@ -29,7 +30,8 @@
  */
 public final class DefaultMeterFeatures implements MeterFeatures {
     private DeviceId deviceId;
-    private long maxMeter;
+    private long startIndex;
+    private long endIndex;
     private Set<Band.Type> bandTypes;
     private Set<Meter.Unit> units;
     private boolean burst;
@@ -37,13 +39,16 @@
     private short maxBands;
     private short maxColor;
     private Set<MeterFeaturesFlag> features;
+    private MeterScope scope;
 
-    private DefaultMeterFeatures(DeviceId did, long maxMeter,
+    private DefaultMeterFeatures(DeviceId did, long startIndex, long endIndex,
                                  Set<Band.Type> bandTypes, Set<Meter.Unit> units,
                                  boolean burst, boolean stats,
-                                 short maxBands, short maxColor, Set<MeterFeaturesFlag> flag) {
+                                 short maxBands, short maxColor, Set<MeterFeaturesFlag> flag,
+                                 MeterScope scope) {
         this.deviceId = did;
-        this.maxMeter = maxMeter;
+        this.startIndex = startIndex;
+        this.endIndex = endIndex;
         this.bandTypes = bandTypes;
         this.burst = burst;
         this.stats = stats;
@@ -51,6 +56,7 @@
         this.maxBands = maxBands;
         this.maxColor = maxColor;
         this.features = flag;
+        this.scope = scope;
     }
 
     @Override
@@ -60,7 +66,18 @@
 
     @Override
     public long maxMeter() {
-        return maxMeter;
+        // For OpenFlow meter, return end index as maxMeter
+        return scope.isGlobal() ? endIndex + 1 : endIndex - startIndex + 1;
+    }
+
+    @Override
+    public long startIndex() {
+        return startIndex;
+    }
+
+    @Override
+    public long endIndex() {
+        return endIndex;
     }
 
     @Override
@@ -98,6 +115,11 @@
         return features;
     }
 
+    @Override
+    public MeterScope scope() {
+        return scope;
+    }
+
     public static Builder builder() {
         return new Builder();
     }
@@ -111,13 +133,15 @@
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
                 .add("deviceId", deviceId())
-                .add("maxMeter", maxMeter())
+                .add("startIndex", startIndex())
+                .add("endIndex", endIndex())
                 .add("maxBands", maxBands())
                 .add("maxColor", maxColor())
                 .add("bands", bandTypes())
                 .add("burst", isBurstSupported())
                 .add("stats", isStatsSupported())
                 .add("units", unitTypes())
+                .add("scope", scope())
                 .toString();
     }
 
@@ -127,6 +151,8 @@
     public static final class Builder implements MeterFeatures.Builder {
         private DeviceId did;
         private long mmeter = 0L;
+        private long starti = -1L;
+        private long endi = -1L;
         private short mbands = 0;
         private short mcolors = 0;
         private Set<Band.Type> bandTypes = new HashSet<>();
@@ -134,6 +160,7 @@
         private boolean burst = false;
         private boolean stats = false;
         private Set<MeterFeaturesFlag> features = Sets.newHashSet();
+        private MeterScope mscope = MeterScope.globalScope();
 
         @Override
         public MeterFeatures.Builder forDevice(DeviceId deviceId) {
@@ -148,6 +175,18 @@
         }
 
         @Override
+        public MeterFeatures.Builder withStartIndex(long startIndex) {
+            starti = startIndex;
+            return this;
+        }
+
+        @Override
+        public MeterFeatures.Builder withEndIndex(long endIndex) {
+            endi = endIndex;
+            return this;
+        }
+
+        @Override
         public MeterFeatures.Builder withMaxBands(short maxBands) {
             mbands = maxBands;
             return this;
@@ -190,9 +229,33 @@
         }
 
         @Override
+        public MeterFeatures.Builder withScope(MeterScope scope) {
+            mscope = scope;
+            return this;
+        }
+
+        @Override
         public MeterFeatures build() {
+            // In case some functions are using maxMeter
+            // and both indexes are not set
+            // Start index will be
+            // 1, if it is global scope (An OpenFlow meter)
+            // 0, for the rest (A P4RT meter)
+            if (mmeter != 0L && starti == -1L && endi == -1L) {
+                starti = mscope.isGlobal() ? 1 : 0;
+                endi = mmeter - 1;
+            }
+            // If one of the index is unset/unvalid value, treated as no meter features
+            if (starti <= -1 || endi <= -1) {
+                starti = -1;
+                endi = -1;
+            }
+
             checkNotNull(did, "Must specify a device");
-            return new DefaultMeterFeatures(did, mmeter, bandTypes, units1, burst, stats, mbands, mcolors, features);
+            checkArgument(starti <= endi, "Start index must be less than or equal to end index");
+
+            return new DefaultMeterFeatures(did, starti, endi, bandTypes, units1, burst,
+                                            stats, mbands, mcolors, features, mscope);
         }
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterFeatures.java b/core/api/src/main/java/org/onosproject/net/meter/MeterFeatures.java
index 2749687..ff846eb 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterFeatures.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterFeatures.java
@@ -35,10 +35,26 @@
      * Returns the maximum number of meters accepted by the device.
      *
      * @return the maximum meter value.
+     * @deprecated in onos-2.5 replaced by {@link #startIndex()} and {@link #endIndex()}
      */
+    @Deprecated
     long maxMeter();
 
     /**
+     * Returns the start index (inclusive) of the meters.
+     *
+     * @return the start index
+     */
+    long startIndex();
+
+    /**
+     * Returns the end index (inclusive) of the meters.
+     *
+     * @return the end index
+     */
+    long endIndex();
+
+    /**
      * Returns band types supported.
      *
      * @return the band types supported.
@@ -89,6 +105,13 @@
     Set<MeterFeaturesFlag> features();
 
     /**
+     * Returns Meter Scope.
+     *
+     * @return meter scope
+     */
+    MeterScope scope();
+
+    /**
      * A meter features builder.
      */
     interface Builder {
@@ -105,10 +128,28 @@
          *
          * @param maxMeter the maximum meters available
          * @return this builder
+         * @deprecated in onos-2.5 replaced by {@link #withStartIndex(long)} and {@link #withEndIndex(long)}
          */
+        @Deprecated
         Builder withMaxMeters(long maxMeter);
 
         /**
+         * Assigns the start index (inclusive) for this meter features.
+         *
+         * @param startIndex the start index
+         * @return this builder
+         */
+        Builder withStartIndex(long startIndex);
+
+        /**
+         * Assigns the end index (inclusive) for this meter features.
+         *
+         * @param endIndex the end index
+         * @return this builder
+         */
+        Builder withEndIndex(long endIndex);
+
+        /**
          * Assigns the max bands value for this meter features.
          *
          * @param maxBands the maximum bands available.
@@ -165,6 +206,14 @@
         Builder withFeatures(Set<MeterFeaturesFlag> featureFlags);
 
         /**
+         * Assigns the meter scope.
+         *
+         * @param scope the scope
+         * @return this builder
+         */
+        Builder withScope(MeterScope scope);
+
+        /**
          * Builds the Meter Features based on the specified parameters.
          *
          * @return the meter features
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterFeaturesKey.java b/core/api/src/main/java/org/onosproject/net/meter/MeterFeaturesKey.java
index fa19a5f..2b5eb07 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterFeaturesKey.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterFeaturesKey.java
@@ -23,7 +23,9 @@
  * A meter features key represents a meter features uniquely.
  * Right now only deviceId is used but this class might be useful in
  * virtualization in which a unique deviceId could have multiple features (guess).
+ * @deprecated in onos-2.5 replaced by {@link MeterTableKey}
  */
+@Deprecated
 public final class MeterFeaturesKey {
     private final DeviceId deviceId;
 
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java b/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java
index 8c528b2..21717b6 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java
@@ -26,9 +26,9 @@
 public final class MeterKey {
 
     private final DeviceId deviceId;
-    private final MeterId id;
+    private final MeterCellId id;
 
-    private MeterKey(DeviceId deviceId, MeterId id) {
+    private MeterKey(DeviceId deviceId, MeterCellId id) {
         this.deviceId = deviceId;
         this.id = id;
     }
@@ -37,7 +37,22 @@
         return deviceId;
     }
 
+    /**
+     * @return a MeterId iff the id is a MeterId
+     * otherwise, return null
+     * @deprecated in onos-2.5 replaced by {@link #key(DeviceId,MeterCellId)}
+     * extends MeterKey to support both MeterId and PiMeterCellId
+     */
+    @Deprecated
     public MeterId meterId() {
+        if (id instanceof MeterId) {
+            return (MeterId) id;
+        } else {
+            return null;
+        }
+    }
+
+    public MeterCellId meterCellId() {
         return id;
     }
 
@@ -63,10 +78,22 @@
     public String toString() {
         return toStringHelper(this)
                 .add("deviceId", deviceId)
-                .add("meterId", id).toString();
+                .add("meterCellId", id).toString();
     }
 
+    /**
+     * @param deviceId a DeviceId
+     * @param id a MeterId
+     * @return a MeterKey contains DeviceId and MeterId
+     * @deprecated in onos-2.5 replaced by {@link #key(DeviceId,MeterCellId)}
+     * extends MeterKey to support both MeterId and PiMeterCellId
+     */
+    @Deprecated
     public static MeterKey key(DeviceId deviceId, MeterId id) {
         return new MeterKey(deviceId, id);
     }
+
+    public static MeterKey key(DeviceId deviceId, MeterCellId id) {
+        return new MeterKey(deviceId, id);
+    }
 }
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 4b8ea07..74eab4d 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,4 +42,11 @@
      * @return completable future with the collection of meters
      */
     CompletableFuture<Collection<Meter>> getMeters();
+
+    /**
+     * Queries the meter features from the device.
+     *
+     * @return completable future with the collection of meter features
+     */
+    CompletableFuture<Collection<MeterFeatures>> getMeterFeatures();
 }
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterProviderService.java b/core/api/src/main/java/org/onosproject/net/meter/MeterProviderService.java
index 29c251c..e96e224 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterProviderService.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterProviderService.java
@@ -54,6 +54,14 @@
     void pushMeterFeatures(DeviceId deviceId,
                            MeterFeatures meterfeatures);
 
+    /**
+     * Pushes the collection of meter features collected from the device.
+     *
+     * @param deviceId the device Id
+     * @param meterfeatures the meter features
+     */
+    void pushMeterFeatures(DeviceId deviceId,
+                           Collection<MeterFeatures> meterfeatures);
 
     /**
      * Delete meter features collected from the device.
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterScope.java b/core/api/src/main/java/org/onosproject/net/meter/MeterScope.java
new file mode 100644
index 0000000..06eb448
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterScope.java
@@ -0,0 +1,69 @@
+/*
+ * 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.meter;
+
+import org.onlab.util.Identifier;
+
+/**
+ * Scope of Meter Features.
+ *
+ * There are multiple meter tables in a P4RT device,
+ * to distinguish and represent them uniquely,
+ * we added a Scope field.
+ *
+ * For P4RT device, value will be the PiMeterId.
+ * For OF device, we use "global" by default, since there is only 1 table in OF.
+ * In general, a Meter Scope is referring to a Meter Table.
+ * It can be a PiMeterId or "global" for the single OF meter table.
+ *
+ * During runtime, users need to provide a PiMeterId to indicate which Meter Cell they
+ * are intended to modify. The value will then be used to create a Meter Scope
+ * for the rest of the process.
+ * If no PiMeterId is provided, a "global" Meter Scope is created.
+ */
+public class MeterScope extends Identifier<String> {
+
+    public static final String METER_GLOBAL_SCOPE = "global";
+
+    /**
+     * Create a Meter Scope from id string.
+     * @param scope the scope
+     * @return a Meter Scope
+     */
+    public static MeterScope of(String scope) {
+        return new MeterScope(scope);
+    }
+
+    /**
+     * Create a global Meter Scope.
+     * @return a Meter Scope
+     */
+    public static MeterScope globalScope() {
+        return new MeterScope(METER_GLOBAL_SCOPE);
+    }
+
+    MeterScope(String scope) {
+        super(scope);
+    }
+
+    /**
+     * Global scope or not.
+     * @return true if global scope, false if not.
+     */
+    public boolean isGlobal() {
+        return identifier.equals(METER_GLOBAL_SCOPE);
+    }
+}
\ No newline at end of file
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 991715c..cf99b5e 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
@@ -74,7 +74,9 @@
      * @param deviceId the device id
      * @return the allocated meter id, null if there is an internal error
      * or there are no meter ids available
+     * @deprecated in onos-2.5
      */
+    @Deprecated
     MeterId allocateMeterId(DeviceId deviceId);
 
     /**
@@ -82,7 +84,9 @@
      *
      * @param deviceId the device id
      * @param meterId the id to be freed
+     * @deprecated in onos-2.5
      */
+    @Deprecated
     void freeMeterId(DeviceId deviceId, MeterId meterId);
 
     /**
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 675592a..21696e4 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
@@ -112,15 +112,26 @@
      * Delete this meter immediately.
      *
      * @param m a meter
+     * @deprecated in onos-2.5 renamed {@link #purgeMeter(Meter)}
      */
+    @Deprecated
     void deleteMeterNow(Meter m);
 
     /**
+     * Delete this meter immediately.
+     *
+     * @param m a meter
+     */
+    void purgeMeter(Meter m);
+
+    /**
      * Retrieve maximum meters available for the device.
      *
      * @param key the meter features key
      * @return the maximum number of meters supported by the device
+     * @deprecated in onos-2.5, Max meters is replaced by start and end index
      */
+    @Deprecated
     long getMaxMeters(MeterFeaturesKey key);
 
     /**
@@ -129,15 +140,30 @@
      * @param deviceId the device id
      * @return the meter Id or null if it was not possible
      * to allocate a meter id
+     * @deprecated in onos-2.5 replaced by {@link #allocateMeterId(DeviceId, MeterScope)}
      */
+    @Deprecated
     MeterId allocateMeterId(DeviceId deviceId);
 
     /**
+     * Allocates the first available MeterId.
+     *
+     * @param deviceId the device id
+     * @param meterScope the meter scope
+     * @return the meter Id or null if it was not possible
+     * to allocate a meter id
+     */
+    MeterCellId allocateMeterId(DeviceId deviceId, MeterScope meterScope);
+
+    /**
      * Frees the given meter id.
      *
      * @param deviceId the device id
      * @param meterId  the id to be freed
+     * @deprecated in onos-2.5, freeing an ID is closely related to removal of a meter
+     * so, this function is no longer exposed on interface
      */
+    @Deprecated
     void freeMeterId(DeviceId deviceId, MeterId meterId);
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterTableKey.java b/core/api/src/main/java/org/onosproject/net/meter/MeterTableKey.java
new file mode 100644
index 0000000..dc76fee
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterTableKey.java
@@ -0,0 +1,67 @@
+/*
+ * 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.meter;
+
+import org.onosproject.net.DeviceId;
+
+import java.util.Objects;
+
+/**
+ * MeterTableKey is used to represent a single meter table in each device uniquely.
+ */
+public final class MeterTableKey {
+    private final DeviceId deviceId;
+    private final MeterScope scope;
+
+    private MeterTableKey(DeviceId deviceId, MeterScope scope) {
+        this.deviceId = deviceId;
+        this.scope = scope;
+    }
+
+    public static MeterTableKey key(DeviceId did, MeterScope scope) {
+        return new MeterTableKey(did, scope);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        MeterTableKey mtk = (MeterTableKey) obj;
+        return Objects.equals(deviceId, mtk.deviceId()) && Objects.equals(scope, mtk.scope());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(deviceId, scope);
+    }
+
+    @Override
+    public String toString() {
+        return "mtk@" + deviceId.toString() + " scope:" + scope.toString();
+    }
+
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    public MeterScope scope() {
+        return scope;
+    }
+}