[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;
+ }
+}
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 e6151b0..cf7d198 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
@@ -24,6 +24,7 @@
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterFeatures;
import org.onosproject.net.meter.MeterOperation;
import org.onosproject.net.meter.MeterOperations;
import org.onosproject.net.meter.MeterProgrammable;
@@ -36,6 +37,7 @@
import org.slf4j.LoggerFactory;
import java.util.Collection;
+import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
@@ -147,6 +149,17 @@
meterProviderService.pushMeterMetrics(deviceId, meters);
}
+ private void getMeterFeatures(DeviceId deviceId) {
+ Collection<MeterFeatures> meterFeatures = Collections.emptySet();
+ try {
+ 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);
+ }
+ meterProviderService.pushMeterFeatures(deviceId, meterFeatures);
+ }
+
private MeterProgrammable getMeterProgrammable(DeviceId deviceId) {
Device device = deviceService.getDevice(deviceId);
if (device.is(MeterProgrammable.class)) {
@@ -173,6 +186,19 @@
private void handleEvent(DeviceEvent event) {
Device device = event.subject();
+
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ getMeterFeatures(device.id());
+ break;
+ case DEVICE_REMOVED:
+ case DEVICE_SUSPENDED:
+ meterProviderService.deleteMeterFeatures(device.id());
+ break;
+ default:
+ break;
+ }
+
boolean isRelevant = mastershipService.isLocalMaster(device.id()) &&
deviceService.isAvailable(device.id());
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 363876c..03bb5ca 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
@@ -417,7 +417,7 @@
// and we purge the meter from the store
} else if (m.state() == MeterState.PENDING_REMOVE) {
log.debug("Delete meter {} now in store", m.id());
- store.deleteMeterNow(m);
+ store.purgeMeter(m);
}
});
}
@@ -428,6 +428,11 @@
}
@Override
+ public void pushMeterFeatures(DeviceId deviceId, Collection<MeterFeatures> meterfeatures) {
+ meterfeatures.forEach(mf -> store.storeMeterFeatures(mf));
+ }
+
+ @Override
public void deleteMeterFeatures(DeviceId deviceId) {
store.deleteMeterFeatures(deviceId);
}
diff --git a/core/net/src/test/java/org/onosproject/net/meter/impl/MeterManagerTest.java b/core/net/src/test/java/org/onosproject/net/meter/impl/MeterManagerTest.java
index 52970dd..09e9774 100644
--- a/core/net/src/test/java/org/onosproject/net/meter/impl/MeterManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/meter/impl/MeterManagerTest.java
@@ -575,6 +575,12 @@
mProgrammableAdded.setState(MeterState.ADDED);
return CompletableFuture.completedFuture(ImmutableList.of(mProgrammableAdded));
}
+
+ @Override
+ public CompletableFuture<Collection<MeterFeatures>> getMeterFeatures() {
+ //Currently unused.
+ return CompletableFuture.completedFuture(Collections.emptySet());
+ }
}
private class TestProvider extends AbstractProvider implements MeterProvider {
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 11570f7..2fdba6f 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
@@ -31,6 +31,7 @@
import org.onosproject.net.meter.DefaultMeter;
import org.onosproject.net.meter.DefaultMeterFeatures;
import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterCellId;
import org.onosproject.net.meter.MeterEvent;
import org.onosproject.net.meter.MeterFailReason;
import org.onosproject.net.meter.MeterFeatures;
@@ -39,10 +40,14 @@
import org.onosproject.net.meter.MeterId;
import org.onosproject.net.meter.MeterKey;
import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterScope;
import org.onosproject.net.meter.MeterState;
import org.onosproject.net.meter.MeterStore;
import org.onosproject.net.meter.MeterStoreDelegate;
import org.onosproject.net.meter.MeterStoreResult;
+import org.onosproject.net.meter.MeterTableKey;
+import org.onosproject.net.pi.model.PiMeterId;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.primitives.DefaultDistributedSet;
import org.onosproject.store.serializers.KryoNamespaces;
@@ -50,12 +55,16 @@
import org.onosproject.store.service.ConsistentMap;
import org.onosproject.store.service.DistributedPrimitive;
import org.onosproject.store.service.DistributedSet;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapEvent;
+import org.onosproject.store.service.EventuallyConsistentMapListener;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageException;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.Versioned;
+import org.onosproject.store.service.WallClockTimestamp;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@@ -64,6 +73,8 @@
import org.slf4j.Logger;
import java.util.Collection;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -73,6 +84,8 @@
import static org.onosproject.store.meter.impl.DistributedMeterStore.ReuseStrategy.FIRST_FIT;
import static org.onosproject.net.meter.MeterFailReason.TIMEOUT;
+import static org.onosproject.net.meter.MeterCellId.MeterCellType.INDEX;
+import static org.onosproject.net.meter.MeterCellId.MeterCellType.PIPELINE_INDEPENDENT;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -88,20 +101,22 @@
// Meters map related objects
private static final String METERSTORE = "onos-meter-store";
private ConsistentMap<MeterKey, MeterData> meters;
- private MapEventListener<MeterKey, MeterData> mapListener = new InternalMapEventListener();
+ private MapEventListener<MeterKey, MeterData> metersMapListener = new InternalMetersMapEventListener();
private Map<MeterKey, MeterData> metersMap;
// Meters features related objects
private static final String METERFEATURESSTORE = "onos-meter-features-store";
- private ConsistentMap<MeterFeaturesKey, MeterFeatures> meterFeatures;
+ private EventuallyConsistentMap<MeterTableKey, MeterFeatures> metersFeatures;
+ private EventuallyConsistentMapListener<MeterTableKey, MeterFeatures> featuresMapListener =
+ new InternalFeaturesMapEventListener();
// Meters id related objects
private static final String AVAILABLEMETERIDSTORE = "onos-meters-available-store";
// Available meter identifiers
- private DistributedSet<MeterKey> availableMeterIds;
+ private ConcurrentMap<MeterTableKey, DistributedSet<MeterKey>> availableMeterIds;
// Atomic counter map for generation of new identifiers;
private static final String METERIDSTORE = "onos-meters-id-store";
- private AtomicCounterMap<DeviceId> meterIdGenerators;
+ private AtomicCounterMap<MeterTableKey> meterIdGenerators;
// Serializer related objects
private static final KryoNamespace.Builder APP_KRYO_BUILDER = KryoNamespace.newBuilder()
@@ -147,35 +162,44 @@
meters = storageService.<MeterKey, MeterData>consistentMapBuilder()
.withName(METERSTORE)
.withSerializer(serializer).build();
- meters.addListener(mapListener);
- // Init meter features map (meaningful only for OpenFlow protocol)
- meterFeatures = storageService.<MeterFeaturesKey, MeterFeatures>consistentMapBuilder()
- .withName(METERFEATURESSTORE)
- .withSerializer(Serializer.using(KryoNamespaces.API,
- MeterFeaturesKey.class,
- MeterFeatures.class,
- DefaultMeterFeatures.class,
- Band.Type.class,
- Meter.Unit.class,
- MeterFailReason.class,
- MeterFeaturesFlag.class)).build();
+ meters.addListener(metersMapListener);
metersMap = meters.asJavaMap();
- // Init the set of the available ids
- availableMeterIds = new DefaultDistributedSet<>(storageService.<MeterKey>setBuilder()
- .withName(AVAILABLEMETERIDSTORE)
- .withSerializer(Serializer.using(KryoNamespaces.API,
- MeterKey.class)).build(),
- DistributedPrimitive.DEFAULT_OPERATION_TIMEOUT_MILLIS);
+ // Init meter features map
+ metersFeatures = storageService.<MeterTableKey, MeterFeatures>eventuallyConsistentMapBuilder()
+ .withName(METERFEATURESSTORE)
+ .withTimestampProvider((key, features) -> new WallClockTimestamp())
+ .withSerializer(KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(MeterTableKey.class)
+ .register(MeterFeatures.class)
+ .register(DefaultMeterFeatures.class)
+ .register(DefaultBand.class)
+ .register(Band.Type.class)
+ .register(Meter.Unit.class)
+ .register(MeterFailReason.class)
+ .register(MeterFeaturesFlag.class)).build();
+ metersFeatures.addListener(featuresMapListener);
+ // Init the map of the available ids set
+ // Set will be created when a new Meter Features is pushed to the store
+ availableMeterIds = new ConcurrentHashMap<>();
// Init atomic map counters
- meterIdGenerators = storageService.<DeviceId>atomicCounterMapBuilder()
+ meterIdGenerators = storageService.<MeterTableKey>atomicCounterMapBuilder()
.withName(METERIDSTORE)
- .withSerializer(Serializer.using(KryoNamespaces.API)).build();
+ .withSerializer(Serializer.using(KryoNamespaces.API,
+ MeterTableKey.class,
+ MeterScope.class)).build();
log.info("Started");
}
@Deactivate
public void deactivate() {
- meters.removeListener(mapListener);
+ meters.removeListener(metersMapListener);
+ metersFeatures.removeListener(featuresMapListener);
+ meters.destroy();
+ metersFeatures.destroy();
+ availableMeterIds.forEach((key, set) -> {
+ set.destroy();
+ });
log.info("Stopped");
}
@@ -229,11 +253,11 @@
@Override
public MeterStoreResult storeMeterFeatures(MeterFeatures meterfeatures) {
- // Store meter features, this is done once for each device
+ // Store meter features, this is done once for each features of every device
MeterStoreResult result = MeterStoreResult.success();
- MeterFeaturesKey key = MeterFeaturesKey.key(meterfeatures.deviceId());
+ MeterTableKey key = MeterTableKey.key(meterfeatures.deviceId(), meterfeatures.scope());
try {
- meterFeatures.putIfAbsent(key, meterfeatures);
+ metersFeatures.put(key, meterfeatures);
} catch (StorageException e) {
log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
e.getMessage(), e);
@@ -244,16 +268,20 @@
@Override
public MeterStoreResult deleteMeterFeatures(DeviceId deviceId) {
- // Remove meter features - these ops are meaningful only for OpenFlow
MeterStoreResult result = MeterStoreResult.success();
- MeterFeaturesKey key = MeterFeaturesKey.key(deviceId);
try {
- meterFeatures.remove(key);
+ Set<MeterTableKey> keys = metersFeatures.keySet().stream()
+ .filter(key -> key.deviceId().equals(deviceId))
+ .collect(Collectors.toUnmodifiableSet());
+ keys.forEach(k -> {
+ metersFeatures.remove(k);
+ });
} catch (StorageException e) {
log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
- e.getMessage(), e);
+ e.getMessage(), e);
result = MeterStoreResult.fail(TIMEOUT);
}
+
return result;
}
@@ -327,13 +355,27 @@
@Override
public void deleteMeterNow(Meter m) {
+ // This method is renamed in onos-2.5
+ purgeMeter(m);
+ }
+
+ @Override
+ public void purgeMeter(Meter m) {
// Once we receive the ack from the sb
// create the key and remove definitely the meter
MeterKey key = MeterKey.key(m.deviceId(), m.id());
try {
if (Versioned.valueOrNull(meters.remove(key)) != null) {
// Free the id
- freeMeterId(m.deviceId(), m.id());
+ MeterScope scope;
+ if (m.meterCellId().type() == PIPELINE_INDEPENDENT) {
+ PiMeterCellId piMeterCellId = (PiMeterCellId) m.meterCellId();
+ scope = MeterScope.of(piMeterCellId.meterId().id());
+ } else {
+ scope = MeterScope.globalScope();
+ }
+ MeterTableKey meterTableKey = MeterTableKey.key(m.deviceId(), scope);
+ freeMeterId(meterTableKey, m.meterCellId());
}
} catch (StorageException e) {
log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
@@ -350,16 +392,41 @@
.collect(Collectors.toList());
// Remove definitely the meter
metersPendingRemove.forEach(versionedMeterKey
- -> deleteMeterNow(versionedMeterKey.value().meter()));
+ -> purgeMeter(versionedMeterKey.value().meter()));
}
@Override
public long getMaxMeters(MeterFeaturesKey key) {
// Leverage the meter features to know the max id
- MeterFeatures features = Versioned.valueOrElse(meterFeatures.get(key), null);
+ // Create a Meter Table key with FeaturesKey's device and global scope
+ MeterTableKey meterTableKey = MeterTableKey.key(key.deviceId(), MeterScope.globalScope());
+ return getMaxMeters(meterTableKey);
+ }
+
+ private long getMaxMeters(MeterTableKey key) {
+ // Leverage the meter features to know the max id
+ MeterFeatures features = metersFeatures.get(key);
return features == null ? 0L : features.maxMeter();
}
+ private long getStartIndex(MeterTableKey key) {
+ // Leverage the meter features to know the start id
+ // Since we are using index now
+ // if there is no features related to the key
+ // -1 is returned
+ MeterFeatures features = metersFeatures.get(key);
+ return features == null ? -1L : features.startIndex();
+ }
+
+ private long getEndIndex(MeterTableKey key) {
+ // Leverage the meter features to know the max id
+ // Since we are using index now
+ // if there is no features related to the key
+ // -1 is returned
+ MeterFeatures features = metersFeatures.get(key);
+ return features == null ? -1L : features.endIndex();
+ }
+
// queryMaxMeters is implemented in FullMetersAvailable behaviour.
private long queryMaxMeters(DeviceId device) {
// Get driver handler for this device
@@ -371,18 +438,31 @@
}
// Get the behavior
MeterQuery query = handler.behaviour(MeterQuery.class);
+ // Insert a new available key set to the map
+ String setName = AVAILABLEMETERIDSTORE + "-" + device + "global";
+ MeterTableKey meterTableKey = MeterTableKey.key(device, MeterScope.globalScope());
+ insertAvailableKeySet(meterTableKey, setName);
// Return as max meter the result of the query
return query.getMaxMeters();
}
- private boolean updateMeterIdAvailability(DeviceId deviceId, MeterId id,
+ private boolean updateMeterIdAvailability(MeterTableKey meterTableKey, MeterCellId id,
boolean available) {
+ // Retrieve the set first
+ DistributedSet<MeterKey> keySet = availableMeterIds.get(meterTableKey);
+ if (keySet == null) {
+ // A reusable set should be inserted when a features is pushed
+ log.warn("Reusable Key set for device: {} scope: {} not found",
+ meterTableKey.deviceId(), meterTableKey.scope());
+ return false;
+ }
// According to available, make available or unavailable a meter key
- return available ? availableMeterIds.add(MeterKey.key(deviceId, id)) :
- availableMeterIds.remove(MeterKey.key(deviceId, id));
+ DeviceId deviceId = meterTableKey.deviceId();
+ return available ? keySet.add(MeterKey.key(deviceId, id)) :
+ keySet.remove(MeterKey.key(deviceId, id));
}
- private MeterId getNextAvailableId(Set<MeterId> availableIds) {
+ private MeterCellId getNextAvailableId(Set<MeterCellId> availableIds) {
// If there are no available ids
if (availableIds.isEmpty()) {
// Just end the cycle
@@ -399,18 +479,27 @@
}
// Implements reuse strategy
- private MeterId firstReusableMeterId(DeviceId deviceId) {
+ private MeterCellId firstReusableMeterId(MeterTableKey meterTableKey) {
+ // Create a Table key and use it to retrieve the reusable meterCellId set
+ DistributedSet<MeterKey> keySet = availableMeterIds.get(meterTableKey);
+ if (keySet == null) {
+ // A reusable set should be inserted when a features is pushed
+ log.warn("Reusable Key set for device: {} scope: {} not found",
+ meterTableKey.deviceId(), meterTableKey.scope());
+ return null;
+ }
// Filter key related to device id, and reduce to meter ids
- Set<MeterId> localAvailableMeterIds = availableMeterIds.stream()
- .filter(meterKey -> meterKey.deviceId().equals(deviceId))
+ Set<MeterCellId> localAvailableMeterIds = keySet.stream()
+ .filter(meterKey ->
+ meterKey.deviceId().equals(meterTableKey.deviceId()))
.map(MeterKey::meterId)
.collect(Collectors.toSet());
// Get next available id
- MeterId meterId = getNextAvailableId(localAvailableMeterIds);
+ MeterCellId meterId = getNextAvailableId(localAvailableMeterIds);
// Iterate until there are items
while (meterId != null) {
// If we are able to reserve the id
- if (updateMeterIdAvailability(deviceId, meterId, false)) {
+ if (updateMeterIdAvailability(meterTableKey, meterId, false)) {
// Just end
return meterId;
}
@@ -425,49 +514,86 @@
@Override
public MeterId allocateMeterId(DeviceId deviceId) {
- // Init steps
- MeterId meterId;
+ // We use global scope for MeterId
+ return (MeterId) allocateMeterId(deviceId, MeterScope.globalScope());
+ }
+
+ @Override
+ public MeterCellId allocateMeterId(DeviceId deviceId, MeterScope meterScope) {
+ MeterTableKey meterTableKey = MeterTableKey.key(deviceId, meterScope);
+ MeterCellId meterCellId;
long id;
- // Try to reuse meter id
- meterId = firstReusableMeterId(deviceId);
- // We found a reusable id, return
- if (meterId != null) {
- return meterId;
+ // First, search for reusable key
+ meterCellId = firstReusableMeterId(meterTableKey);
+ if (meterCellId != null) {
+ // A reusable key is found
+ return meterCellId;
}
- // If there was no reusable MeterId we have to generate a new value
- // using maxMeters as upper limit.
- long maxMeters = getMaxMeters(MeterFeaturesKey.key(deviceId));
+ // If there was no reusable meter id we have to generate a new value
+ // using start and end index as lower and upper bound respectively.
+ long startIndex = getStartIndex(meterTableKey);
+ long endIndex = getEndIndex(meterTableKey);
// If the device does not give us MeterFeatures
- if (maxMeters == 0L) {
+ if (startIndex == -1L || endIndex == -1L) {
// MeterFeatures couldn't be retrieved, fallback to queryMeters.
- maxMeters = queryMaxMeters(deviceId);
- }
- // If we don't know the max, cannot proceed
- if (maxMeters == 0L) {
- return null;
+ // Only meaningful to OpenFLow
+ long maxMeters = queryMaxMeters(deviceId);
+ // If we don't know the max, cannot proceed
+ if (maxMeters == 0L) {
+ return null;
+ } else {
+ // OpenFlow meter index starts from 1, ends with max-1
+ startIndex = 1L;
+ endIndex = maxMeters - 1;
+ }
}
// Get a new value
- id = meterIdGenerators.incrementAndGet(deviceId);
- // Check with the max, and if the value is bigger, cannot proceed
- if (id >= maxMeters) {
+ // If the value is smaller than the start index, get another one
+ do {
+ id = meterIdGenerators.incrementAndGet(meterTableKey);
+ } while (id < startIndex);
+ // Check with the end index, and if the value is bigger, cannot proceed
+ if (id > endIndex) {
return null;
}
// Done, return the value
- return MeterId.meterId(id);
+ // If we are using global scope, return a MeterId
+ // Else, return a PiMeterId
+ if (meterScope.isGlobal()) {
+ return MeterId.meterId(id);
+ } else {
+ return PiMeterCellId.ofIndirect(PiMeterId.of(meterScope.id()), id);
+ }
+
}
@Override
public void freeMeterId(DeviceId deviceId, MeterId meterId) {
+ MeterTableKey meterTableKey = MeterTableKey.key(deviceId, MeterScope.globalScope());
+ freeMeterId(meterTableKey, meterId);
+ }
+
+ private void freeMeterId(MeterTableKey meterTableKey, MeterCellId meterCellId) {
+ long index;
+ if (meterCellId.type() == PIPELINE_INDEPENDENT) {
+ PiMeterCellId piMeterCellId = (PiMeterCellId) meterCellId;
+ index = piMeterCellId.index();
+ } else if (meterCellId.type() == INDEX) {
+ MeterId meterId = (MeterId) meterCellId;
+ index = meterId.id();
+ } else {
+ return;
+ }
// Avoid to free meter not allocated
- if (meterIdGenerators.get(deviceId) < meterId.id()) {
+ if (meterIdGenerators.get(meterTableKey) < index) {
return;
}
// Update the availability
- updateMeterIdAvailability(deviceId, meterId, true);
+ updateMeterIdAvailability(meterTableKey, meterCellId, true);
}
// Enabling the events distribution across the cluster
- private class InternalMapEventListener implements MapEventListener<MeterKey, MeterData> {
+ private class InternalMetersMapEventListener implements MapEventListener<MeterKey, MeterData> {
@Override
public void event(MapEvent<MeterKey, MeterData> event) {
MeterKey key = event.key();
@@ -523,8 +649,42 @@
default:
log.warn("Unknown Map event type {}", event.type());
}
-
}
}
+ private class InternalFeaturesMapEventListener implements
+ EventuallyConsistentMapListener<MeterTableKey, MeterFeatures> {
+ @Override
+ public void event(EventuallyConsistentMapEvent<MeterTableKey, MeterFeatures> event) {
+ MeterTableKey meterTableKey = event.key();
+ MeterFeatures meterFeatures = event.value();
+ switch (event.type()) {
+ case PUT:
+ // Put a new available meter id set to the map
+ String setName = AVAILABLEMETERIDSTORE + "-" +
+ meterFeatures.deviceId() + meterFeatures.scope().id();
+ insertAvailableKeySet(meterTableKey, setName);
+ break;
+ case REMOVE:
+ // Remove the set
+ DistributedSet<MeterKey> set = availableMeterIds.remove(meterTableKey);
+ if (set != null) {
+ set.destroy();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private void insertAvailableKeySet(MeterTableKey meterTableKey, String setName) {
+ DistributedSet<MeterKey> availableMeterIdSet =
+ new DefaultDistributedSet<>(storageService.<MeterKey>setBuilder()
+ .withName(setName)
+ .withSerializer(Serializer.using(KryoNamespaces.API,
+ MeterKey.class)).build(),
+ DistributedPrimitive.DEFAULT_OPERATION_TIMEOUT_MILLIS);
+ availableMeterIds.put(meterTableKey, availableMeterIdSet);
+ }
}
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 796bea6..0bcfcaf 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
@@ -19,13 +19,18 @@
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Sets;
import org.onosproject.drivers.p4runtime.mirror.P4RuntimeMeterMirror;
+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;
+import org.onosproject.net.meter.MeterFeatures;
import org.onosproject.net.meter.MeterOperation;
import org.onosproject.net.meter.MeterProgrammable;
+import org.onosproject.net.meter.MeterScope;
import org.onosproject.net.meter.MeterState;
import org.onosproject.net.pi.model.PiMeterId;
import org.onosproject.net.pi.model.PiMeterModel;
@@ -45,6 +50,8 @@
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
* Implementation of MeterProgrammable behaviour for P4Runtime.
*/
@@ -148,4 +155,90 @@
return CompletableFuture.completedFuture(meters);
}
+
+ @Override
+ public CompletableFuture<Collection<MeterFeatures>> getMeterFeatures() {
+
+ if (!setupBehaviour("getMeterFeatures()")) {
+ return CompletableFuture.completedFuture(Collections.emptyList());
+ }
+
+ Collection<MeterFeatures> meterFeatures = new HashSet<>();
+ pipeconf.pipelineModel().meters().forEach(
+ m -> meterFeatures.add(new P4RuntimeMeterFeaturesBuilder(m, deviceId).build()));
+
+ return CompletableFuture.completedFuture(meterFeatures);
+ }
+
+ /**
+ * P4 meter features builder.
+ */
+ public class P4RuntimeMeterFeaturesBuilder {
+ private final PiMeterModel piMeterModel;
+ private DeviceId deviceId;
+
+ private static final long PI_METER_START_INDEX = 0L;
+ private static final short PI_METER_MAX_BAND = 2;
+ private static final short PI_METER_MAX_COLOR = 3;
+
+ public P4RuntimeMeterFeaturesBuilder(PiMeterModel piMeterModel, DeviceId deviceId) {
+ this.piMeterModel = checkNotNull(piMeterModel);
+ this.deviceId = deviceId;
+ }
+
+ /**
+ * To build a MeterFeatures using the PiMeterModel object
+ * retrieved from pipeconf.
+ *
+ * @return the meter features object
+ */
+ public MeterFeatures build() {
+ /*
+ * We set the basic values before to extract the other information.
+ */
+ MeterFeatures.Builder builder = DefaultMeterFeatures.builder()
+ .forDevice(deviceId)
+ // The scope value will be PiMeterId
+ .withScope(MeterScope.of(piMeterModel.id().id()))
+ .withMaxBands(PI_METER_MAX_BAND)
+ .withMaxColors(PI_METER_MAX_COLOR)
+ .withStartIndex(PI_METER_START_INDEX)
+ .withEndIndex(piMeterModel.size() - 1);
+ /*
+ * Pi meter only support NONE type
+ */
+ Set<Band.Type> bands = Sets.newHashSet();
+ bands.add(Band.Type.NONE);
+ builder.withBandTypes(bands);
+ /*
+ * We extract the supported units;
+ */
+ Set<Meter.Unit> units = Sets.newHashSet();
+ if (piMeterModel.unit() == PiMeterModel.Unit.BYTES) {
+ units.add(Meter.Unit.KB_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();
+ }
+
+ /**
+ * To build an empty meter features.
+ * @param deviceId the device id
+ * @return the meter features
+ */
+ public MeterFeatures noMeterFeatures(DeviceId deviceId) {
+ return DefaultMeterFeatures.noMeterFeatures(deviceId);
+ }
+ }
}
diff --git a/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml b/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml
index 0750805..67edaab 100644
--- a/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml
+++ b/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml
@@ -28,9 +28,8 @@
impl="org.onosproject.drivers.p4runtime.P4RuntimeFlowRuleProgrammable"/>
<behaviour api="org.onosproject.net.group.GroupProgrammable"
impl="org.onosproject.drivers.p4runtime.P4RuntimeGroupProgrammable"/>
- <!-- FIXME: re-enable when ONOS-7720 is fixed
<behaviour api="org.onosproject.net.meter.MeterProgrammable"
- impl="org.onosproject.drivers.p4runtime.P4RuntimeMeterProgrammable"/>-->
+ impl="org.onosproject.drivers.p4runtime.P4RuntimeMeterProgrammable"/>
<property name="supportPacketRequest">true</property>
</driver>
</drivers>
diff --git a/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/util/MeterFeaturesBuilder.java b/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/util/MeterFeaturesBuilder.java
index 35f9dde..4fc61aa 100644
--- a/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/util/MeterFeaturesBuilder.java
+++ b/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/util/MeterFeaturesBuilder.java
@@ -53,6 +53,8 @@
private final OFMeterFeatures ofMeterFeatures;
private DeviceId deviceId;
+ private static final long OF_METER_START_INDEX = 1L;
+
public MeterFeaturesBuilder(OFMeterFeatures features, DeviceId deviceId) {
this.ofMeterFeatures = checkNotNull(features);
this.deviceId = deviceId;
@@ -72,7 +74,8 @@
.forDevice(deviceId)
.withMaxBands(ofMeterFeatures.getMaxBands())
.withMaxColors(ofMeterFeatures.getMaxColor())
- .withMaxMeters(ofMeterFeatures.getMaxMeter());
+ .withStartIndex(OF_METER_START_INDEX)
+ .withEndIndex(ofMeterFeatures.getMaxMeter());
/*
* We extract the supported band types.
*/