[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();
}
diff --git a/core/api/src/test/java/org/onosproject/net/meter/MeterOperationTest.java b/core/api/src/test/java/org/onosproject/net/meter/MeterOperationTest.java
index 3d69601..0716d7d 100644
--- a/core/api/src/test/java/org/onosproject/net/meter/MeterOperationTest.java
+++ b/core/api/src/test/java/org/onosproject/net/meter/MeterOperationTest.java
@@ -80,6 +80,11 @@
}
@Override
+ public MeterCellId meterCellId() {
+ return null;
+ }
+
+ @Override
public ApplicationId appId() {
return null;
}
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 5d7965b..04e3e56 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
@@ -30,6 +30,7 @@
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.meter.DefaultMeter;
+import org.onosproject.net.meter.MeterCellId.MeterCellType;
import org.onosproject.net.meter.Meter;
import org.onosproject.net.meter.MeterEvent;
import org.onosproject.net.meter.MeterFailReason;
@@ -132,10 +133,10 @@
}
});
- };
+ };
executorService = newFixedThreadPool(numThreads,
- groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
+ groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
modified(context);
log.info("Started");
}
@@ -146,7 +147,7 @@
readComponentConfiguration(context);
}
defaultProvider.init(deviceService, createProviderService(defaultProvider),
- mastershipService, fallbackMeterPollFrequency);
+ mastershipService, fallbackMeterPollFrequency);
}
@Deactivate
@@ -289,7 +290,7 @@
// The meter is missing in the device. Reinstall!
log.debug("Adding meter missing in device {} {}", deviceId, m);
provider().performMeterOperation(deviceId,
- new MeterOperation(m, MeterOperation.Type.ADD));
+ new MeterOperation(m, MeterOperation.Type.ADD));
}
});
@@ -297,12 +298,19 @@
meterEntriesMap.entrySet().stream()
.filter(md -> !allMeters.stream().anyMatch(m -> m.id().equals(md.getKey())))
.forEach(mio -> {
- // The meter is missin in onos. Uninstall!
- log.debug("Remove meter in device not in onos {} {}", deviceId, mio.getKey());
Meter meter = mio.getValue();
- provider().performMeterOperation(deviceId,
- new MeterOperation(meter, MeterOperation.Type.REMOVE));
- });
+ // FIXME: Removing a meter is meaningfull for OpenFlow, but not for P4Runtime.
+ // In P4Runtime meter cells cannot be removed. For the
+ // moment, we make the distinction between OpenFlow and
+ // P4Runtime by looking at the MeterCellType (always
+ // INDEX for OpenFlow).
+ if (meter.meterCellId().type() == MeterCellType.INDEX) {
+ // The meter is missing in onos. Uninstall!
+ log.debug("Remove meter in device not in onos {} {}", deviceId, mio.getKey());
+ provider().performMeterOperation(deviceId,
+ new MeterOperation(meter, MeterOperation.Type.REMOVE));
+ }
+ });
meterEntries.stream()
.filter(m -> allMeters.stream()
@@ -339,11 +347,11 @@
switch (event.type()) {
case METER_ADD_REQ:
executorService.execute(new MeterInstaller(deviceId, event.subject(),
- MeterOperation.Type.ADD));
+ MeterOperation.Type.ADD));
break;
case METER_REM_REQ:
executorService.execute(new MeterInstaller(deviceId, event.subject(),
- MeterOperation.Type.REMOVE));
+ MeterOperation.Type.REMOVE));
break;
case METER_ADDED:
log.info("Meter added {}", event.subject());
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java
new file mode 100644
index 0000000..3831699
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java
@@ -0,0 +1,75 @@
+/*
+ * 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.impl;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiMeterBand;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
+import org.onosproject.net.pi.service.PiTranslationException;
+
+import static org.onosproject.net.meter.MeterCellId.MeterCellType.PIPELINE_INDEPENDENT;
+
+/**
+ * Implementation of meter translation logic.
+ */
+final class PiMeterTranslatorImpl {
+
+ private PiMeterTranslatorImpl() {
+ // Hides constructor.
+ }
+
+ private static final int TRTCM_RATES = 2;
+
+ /**
+ * Returns a PI meter config equivalent to the given meter, for the given pipeconf and device.
+ *
+ * @param meter meter
+ * @param pipeconf pipeconf
+ * @param device device
+ * @return PI meter configs
+ * @throws PiTranslationException if the meter cannot be translated
+ */
+ static PiMeterCellConfig translate(Meter meter, PiPipeconf pipeconf, Device device) throws PiTranslationException {
+
+ if (meter.meterCellId().type() != PIPELINE_INDEPENDENT) {
+ throw new PiTranslationException("PI meter cell type must be PIPELINE_INDEPENDENT!");
+ }
+
+ // FIXME: we might want to move this check to P4Runtime driver or protocol layer.
+ // In general, This check is more of P4Runtime limitation, we should do this check in the low level layer.
+ if (meter.bands().size() > TRTCM_RATES) {
+ throw new PiTranslationException("PI meter can not have more than 2 bands!");
+ }
+
+
+ PiMeterCellConfig.Builder builder = PiMeterCellConfig.builder();
+ for (Band band : meter.bands()) {
+ if (band.type() != Band.Type.NONE) {
+ throw new PiTranslationException("PI meter can not have band with other types except NONE!");
+ }
+
+ PiMeterBand piMeterBand = new PiMeterBand(band.rate(), band.burst());
+ builder.withMeterBand(piMeterBand);
+ }
+
+ return builder.withMeterCellId((PiMeterCellId) meter.meterCellId()).build();
+ }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
index 9780fac..c69ced9 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
@@ -27,13 +27,17 @@
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.group.Group;
+import org.onosproject.net.meter.Meter;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.service.PiFlowRuleTranslationStore;
import org.onosproject.net.pi.service.PiFlowRuleTranslator;
import org.onosproject.net.pi.service.PiGroupTranslationStore;
import org.onosproject.net.pi.service.PiGroupTranslator;
+import org.onosproject.net.pi.service.PiMeterTranslationStore;
+import org.onosproject.net.pi.service.PiMeterTranslator;
import org.onosproject.net.pi.service.PiTranslationException;
import org.onosproject.net.pi.service.PiTranslationService;
import org.slf4j.Logger;
@@ -59,13 +63,18 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private PiGroupTranslationStore groupTranslationStore;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private PiMeterTranslationStore meterTranslationStore;
+
private PiFlowRuleTranslator flowRuleTranslator;
private PiGroupTranslator groupTranslator;
+ private PiMeterTranslator meterTranslator;
@Activate
public void activate() {
flowRuleTranslator = new InternalFlowRuleTranslator(flowRuleTranslationStore);
groupTranslator = new InternalGroupTranslator(groupTranslationStore);
+ meterTranslator = new InternalMeterTranslator(meterTranslationStore);
log.info("Started");
}
@@ -73,6 +82,7 @@
public void deactivate() {
flowRuleTranslator = null;
groupTranslator = null;
+ meterTranslator = null;
log.info("Stopped");
}
@@ -86,6 +96,11 @@
return groupTranslator;
}
+ @Override
+ public PiMeterTranslator meterTranslator() {
+ return meterTranslator;
+ }
+
private Device getDevice(DeviceId deviceId) throws PiTranslationException {
final Device device = deviceService.getDevice(deviceId);
if (device == null) {
@@ -125,5 +140,21 @@
.translate(original, pipeconf, getDevice(original.deviceId()));
}
}
+
+ private final class InternalMeterTranslator
+ extends AbstractPiTranslatorImpl<Meter, PiMeterCellConfig>
+ implements PiMeterTranslator {
+
+ private InternalMeterTranslator(PiMeterTranslationStore store) {
+ super(store);
+ }
+
+ @Override
+ public PiMeterCellConfig translate(Meter original, PiPipeconf pipeconf)
+ throws PiTranslationException {
+ return PiMeterTranslatorImpl
+ .translate(original, pipeconf, getDevice(original.deviceId()));
+ }
+ }
}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiMeterTranslationStore.java b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiMeterTranslationStore.java
new file mode 100644
index 0000000..ef7e9f1
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiMeterTranslationStore.java
@@ -0,0 +1,40 @@
+/*
+ * 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.store.pi.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.service.PiMeterTranslationStore;
+
+/**
+ * Distributed implementation of a PI translation store for meters.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedPiMeterTranslationStore
+ extends AbstractDistributedPiTranslationStore<Meter, PiMeterCellConfig>
+ implements PiMeterTranslationStore {
+
+ private static final String MAP_SIMPLE_NAME = "meter";
+
+ @Override
+ protected String mapSimpleName() {
+ return MAP_SIMPLE_NAME;
+ }
+}
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index 90bb979..6e8b25a 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -199,6 +199,8 @@
import org.onosproject.net.intent.constraint.ProtectionConstraint;
import org.onosproject.net.intent.constraint.WaypointConstraint;
import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.meter.MeterCellId;
+import org.onosproject.net.meter.MeterCellId.MeterCellType;
import org.onosproject.net.meter.MeterId;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.DefaultPacketRequest;
@@ -235,6 +237,7 @@
import org.onosproject.net.pi.runtime.PiHandle;
import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
import org.onosproject.net.pi.runtime.PiMatchKey;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.service.PiPipeconfConfig;
import org.onosproject.net.pi.runtime.PiRangeFieldMatch;
@@ -409,6 +412,8 @@
Instructions.StatTriggerInstruction.class,
StatTriggerFlag.class,
StatTriggerField.class,
+ MeterCellId.class,
+ MeterCellType.class,
MeterId.class,
Version.class,
ControllerNode.State.class,
@@ -660,6 +665,7 @@
PiMatchFieldId.class,
PiMatchType.class,
PiMeterId.class,
+ PiMeterCellId.class,
PiMeterType.class,
PiPacketOperationType.class,
PiPipeconfId.class,
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
new file mode 100644
index 0000000..04654c8
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeMeterProgrammable.java
@@ -0,0 +1,161 @@
+/*
+ * 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.drivers.p4runtime;
+
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import org.onosproject.drivers.p4runtime.mirror.P4RuntimeMeterMirror;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.DefaultBand;
+import org.onosproject.net.meter.DefaultMeter;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterProgrammable;
+import org.onosproject.net.meter.MeterState;
+import org.onosproject.net.pi.model.PiMeterId;
+import org.onosproject.net.pi.model.PiMeterModel;
+import org.onosproject.net.pi.model.PiPipelineModel;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMeterHandle;
+import org.onosproject.net.pi.service.PiMeterTranslator;
+import org.onosproject.net.pi.service.PiTranslationException;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * Implementation of MeterProgrammable behaviour for P4Runtime.
+ */
+public class P4RuntimeMeterProgrammable extends AbstractP4RuntimeHandlerBehaviour implements MeterProgrammable {
+
+ private static final int METER_LOCK_EXPIRE_TIME_IN_MIN = 10;
+ private static final LoadingCache<PiMeterHandle, Lock>
+ ENTRY_LOCKS = CacheBuilder.newBuilder()
+ .expireAfterAccess(METER_LOCK_EXPIRE_TIME_IN_MIN, TimeUnit.MINUTES)
+ .build(new CacheLoader<PiMeterHandle, Lock>() {
+ @Override
+ public Lock load(PiMeterHandle handle) {
+ return new ReentrantLock();
+ }
+ });
+
+ private PiMeterTranslator translator;
+ private P4RuntimeMeterMirror meterMirror;
+ private PiPipelineModel pipelineModel;
+
+ @Override
+ protected boolean setupBehaviour() {
+ if (!super.setupBehaviour()) {
+ return false;
+ }
+
+ translator = piTranslationService.meterTranslator();
+ meterMirror = handler().get(P4RuntimeMeterMirror.class);
+ pipelineModel = pipeconf.pipelineModel();
+ return true;
+ }
+
+ @Override
+ public CompletableFuture<Boolean> performMeterOperation(MeterOperation meterOp) {
+
+ return CompletableFuture.completedFuture(processMeterOp(meterOp));
+ }
+
+ private boolean processMeterOp(MeterOperation meterOp) {
+
+ if (meterOp.type() != MeterOperation.Type.MODIFY) {
+ log.warn("P4runtime meter operations must be MODIFY!");
+ return false;
+ }
+
+ PiMeterCellConfig piMeterCellConfig;
+ try {
+ piMeterCellConfig = translator.translate(meterOp.meter(), pipeconf);
+ } catch (PiTranslationException e) {
+ log.warn("Unable translate meter, aborting meter operation {}: {}", meterOp.type(), e.getMessage());
+ log.debug("exception", e);
+ return false;
+ }
+
+ final PiMeterHandle handle = PiMeterHandle.of(deviceId, piMeterCellConfig);
+ ENTRY_LOCKS.getUnchecked(handle).lock();
+ boolean result = false;
+ try {
+ if (client.writeMeterCells(newArrayList(piMeterCellConfig), pipeconf).get()) {
+ meterMirror.put(handle, piMeterCellConfig);
+ result = true;
+ }
+
+ } catch (InterruptedException | ExecutionException e) {
+ log.warn("Exception while modify meter entry:", e);
+ } finally {
+ ENTRY_LOCKS.getUnchecked(handle).unlock();
+ }
+
+ return result;
+ }
+
+ @Override
+ public CompletableFuture<Collection<Meter>> getMeters() {
+
+ if (!setupBehaviour()) {
+ return CompletableFuture.completedFuture(Collections.emptyList());
+ }
+
+ Collection<PiMeterCellConfig> piMeterCellConfigs;
+
+ Set<PiMeterId> meterIds = new HashSet<>();
+ for (PiMeterModel mode : pipelineModel.meters()) {
+ meterIds.add(mode.id());
+ }
+
+ try {
+ piMeterCellConfigs = client.readAllMeterCells(meterIds, pipeconf).get();
+ } catch (InterruptedException | ExecutionException e) {
+ log.warn("Exception while reading meters from {}: {}", deviceId, e.toString());
+ log.debug("", e);
+ return CompletableFuture.completedFuture(Collections.emptyList());
+ }
+
+ Collection<Meter> meters = piMeterCellConfigs.stream()
+ .map(p -> {
+ DefaultMeter meter = (DefaultMeter) DefaultMeter.builder()
+ .withBands(p.meterBands().stream().map(b -> DefaultBand.builder()
+ .withRate(b.rate())
+ .burstSize(b.burst())
+ .ofType(Band.Type.NONE)
+ .build()).collect(Collectors.toList()))
+ .withCellId(p.cellId()).build();
+ meter.setState(MeterState.ADDED);
+ return meter;
+ })
+ .collect(Collectors.toList());
+
+ return CompletableFuture.completedFuture(meters);
+ }
+}
\ No newline at end of file
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeMeterMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeMeterMirror.java
new file mode 100644
index 0000000..f5c1778
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeMeterMirror.java
@@ -0,0 +1,50 @@
+/*
+ * 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.drivers.p4runtime.mirror;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.pi.runtime.PiMeterHandle;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+/**
+ * Distributed implementation of a P4Runtime meter mirror.
+ */
+@Component(immediate = true)
+@Service
+public final class DistributedP4RuntimeMeterMirror
+ extends AbstractDistributedP4RuntimeMirror
+ <PiMeterHandle, PiMeterCellConfig>
+ implements P4RuntimeMeterMirror {
+
+ private static final String DIST_MAP_NAME = "onos-p4runtime-meter-mirror";
+
+ @Override
+ String mapName() {
+ return DIST_MAP_NAME;
+ }
+
+ @Override
+ KryoNamespace storeSerializer() {
+ return KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(TimedEntry.class)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMeterMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMeterMirror.java
new file mode 100644
index 0000000..668492a
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMeterMirror.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.drivers.p4runtime.mirror;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMeterHandle;
+
+/**
+ * Mirror of meters installed on a P4Runtime device.
+ */
+@Beta
+public interface P4RuntimeMeterMirror
+ extends P4RuntimeMirror<PiMeterHandle, PiMeterCellConfig> {
+}
diff --git a/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml b/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml
index 3f00bde..5ee2c92 100644
--- a/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml
+++ b/drivers/p4runtime/src/main/resources/p4runtime-drivers.xml
@@ -24,6 +24,8 @@
impl="org.onosproject.drivers.p4runtime.P4RuntimeFlowRuleProgrammable"/>
<behaviour api="org.onosproject.net.group.GroupProgrammable"
impl="org.onosproject.drivers.p4runtime.P4RuntimeGroupProgrammable"/>
+ <behaviour api="org.onosproject.net.meter.MeterProgrammable"
+ impl="org.onosproject.drivers.p4runtime.P4RuntimeMeterProgrammable"/>
<property name="supportPacketRequest">true</property>
</driver>
</drivers>
diff --git a/incubator/protobuf/services/nb/src/test/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMeterServiceTest.java b/incubator/protobuf/services/nb/src/test/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMeterServiceTest.java
index 46ade10..c9e7180 100644
--- a/incubator/protobuf/services/nb/src/test/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMeterServiceTest.java
+++ b/incubator/protobuf/services/nb/src/test/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMeterServiceTest.java
@@ -32,22 +32,22 @@
import org.onosproject.grpc.net.meter.models.MeterProtoOuterClass;
import org.onosproject.grpc.net.meter.models.MeterRequestProtoOuterClass;
import org.onosproject.net.DeviceId;
-
import org.onosproject.net.meter.Band;
import org.onosproject.net.meter.DefaultBand;
import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterCellId;
import org.onosproject.net.meter.MeterId;
-import org.onosproject.net.meter.MeterService;
-import org.onosproject.net.meter.MeterState;
import org.onosproject.net.meter.MeterListener;
import org.onosproject.net.meter.MeterRequest;
+import org.onosproject.net.meter.MeterService;
+import org.onosproject.net.meter.MeterState;
import java.io.IOException;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import static org.junit.Assert.assertTrue;
@@ -231,6 +231,11 @@
}
@Override
+ public MeterCellId meterCellId() {
+ return this.id();
+ }
+
+ @Override
public ApplicationId appId() {
return this.appId;
}
diff --git a/pipelines/basic/src/main/resources/basic.p4 b/pipelines/basic/src/main/resources/basic.p4
index 387cd0d..a44e10e 100644
--- a/pipelines/basic/src/main/resources/basic.p4
+++ b/pipelines/basic/src/main/resources/basic.p4
@@ -23,9 +23,11 @@
#include "include/parsers.p4"
#include "include/actions.p4"
#include "include/port_counters.p4"
+#include "include/port_meters.p4"
#include "include/checksums.p4"
#include "include/packet_io.p4"
#include "include/table0.p4"
+#include "include/host_meter_table.p4"
#include "include/wcmp.p4"
//------------------------------------------------------------------------------
@@ -38,8 +40,10 @@
apply {
port_counters_ingress.apply(hdr, standard_metadata);
+ port_meters_ingress.apply(hdr, standard_metadata);
packetio_ingress.apply(hdr, standard_metadata);
table0_control.apply(hdr, local_metadata, standard_metadata);
+ host_meter_control.apply(hdr, local_metadata, standard_metadata);
wcmp_control.apply(hdr, local_metadata, standard_metadata);
}
}
@@ -54,6 +58,7 @@
apply {
port_counters_egress.apply(hdr, standard_metadata);
+ port_meters_egress.apply(hdr, standard_metadata);
packetio_egress.apply(hdr, standard_metadata);
}
}
diff --git a/pipelines/basic/src/main/resources/include/custom_headers.p4 b/pipelines/basic/src/main/resources/include/custom_headers.p4
index 035e322..a57b076 100644
--- a/pipelines/basic/src/main/resources/include/custom_headers.p4
+++ b/pipelines/basic/src/main/resources/include/custom_headers.p4
@@ -29,6 +29,7 @@
bit<16> l4_src_port;
bit<16> l4_dst_port;
next_hop_id_t next_hop_id;
+ bit<32> meter_tag;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/pipelines/basic/src/main/resources/include/defines.p4 b/pipelines/basic/src/main/resources/include/defines.p4
index 0b0eeda..d9ec28d 100644
--- a/pipelines/basic/src/main/resources/include/defines.p4
+++ b/pipelines/basic/src/main/resources/include/defines.p4
@@ -27,4 +27,5 @@
const port_t CPU_PORT = 255;
+enum MeterColor_t {GREEN, YELLOW, RED};
#endif
diff --git a/pipelines/basic/src/main/resources/include/host_meter_table.p4 b/pipelines/basic/src/main/resources/include/host_meter_table.p4
new file mode 100644
index 0000000..6f581e1
--- /dev/null
+++ b/pipelines/basic/src/main/resources/include/host_meter_table.p4
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef __HOST_METER_TABLE__
+#define __HOST_METER_TABLE__
+
+#include "headers.p4"
+#include "defines.p4"
+
+control host_meter_control(inout headers_t hdr,
+ inout local_metadata_t local_metadata,
+ inout standard_metadata_t standard_metadata) {
+
+ direct_meter<bit<32>>(MeterType.bytes) host_meter;
+
+ action read_meter() {
+ host_meter.read(local_metadata.meter_tag);
+ }
+
+ table host_meter_table {
+ key = {
+ hdr.ethernet.src_addr : lpm;
+ }
+ actions = {
+ read_meter();
+ NoAction;
+ }
+ meters = host_meter;
+ default_action = NoAction();
+ }
+
+ apply {
+ if (host_meter_table.apply().hit && local_metadata.meter_tag == 2) {
+ mark_to_drop();
+ }
+ }
+}
+
+#endif
diff --git a/pipelines/basic/src/main/resources/include/port_meters.p4 b/pipelines/basic/src/main/resources/include/port_meters.p4
new file mode 100644
index 0000000..710d19a
--- /dev/null
+++ b/pipelines/basic/src/main/resources/include/port_meters.p4
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef METERS
+#define METERS
+#include "defines.p4"
+
+control port_meters_ingress(inout headers_t hdr,
+ inout standard_metadata_t standard_metadata) {
+ meter(MAX_PORTS, MeterType.bytes) ingress_port_meter;
+ MeterColor_t ingress_color = MeterColor_t.GREEN;
+
+ apply {
+ ingress_port_meter.execute_meter<MeterColor_t>((bit<32>)standard_metadata.ingress_port, ingress_color);
+ if (ingress_color == MeterColor_t.RED) {
+ mark_to_drop();
+ }
+ }
+}
+
+control port_meters_egress(inout headers_t hdr,
+ inout standard_metadata_t standard_metadata) {
+
+ meter(MAX_PORTS, MeterType.bytes) egress_port_meter;
+ MeterColor_t egress_color = MeterColor_t.GREEN;
+
+ apply {
+ egress_port_meter.execute_meter<MeterColor_t>((bit<32>)standard_metadata.egress_port, egress_color);
+ if (egress_color == MeterColor_t.RED) {
+ mark_to_drop();
+ }
+ }
+}
+#endif
diff --git a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
index ac7b4f2..b90e0d6 100644
--- a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
+++ b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
@@ -11,9 +11,17 @@
"fields" : [
["tmp", 32, false],
["tmp_0", 32, false],
+ ["port_meters_ingress_ingress_color_0", 32, false],
+ ["host_meter_control_tmp_1", 1, false],
+ ["host_meter_control_tmp_2", 1, false],
+ ["tmp_1", 32, false],
+ ["tmp_2", 32, false],
+ ["port_meters_egress_egress_color_0", 32, false],
["local_metadata_t.l4_src_port", 16, false],
["local_metadata_t.l4_dst_port", 16, false],
- ["local_metadata_t.next_hop_id", 16, false]
+ ["local_metadata_t.next_hop_id", 16, false],
+ ["local_metadata_t.meter_tag", 32, false],
+ ["_padding_2", 6, false]
]
},
{
@@ -177,14 +185,23 @@
"header_union_stacks" : [],
"field_lists" : [],
"errors" : [
- ["NoError", 0],
- ["PacketTooShort", 1],
- ["NoMatch", 2],
- ["StackOutOfBounds", 3],
- ["HeaderTooShort", 4],
- ["ParserTimeout", 5]
+ ["NoError", 1],
+ ["PacketTooShort", 2],
+ ["NoMatch", 3],
+ ["StackOutOfBounds", 4],
+ ["HeaderTooShort", 5],
+ ["ParserTimeout", 6]
],
- "enums" : [],
+ "enums" : [
+ {
+ "name" : "MeterColor_t",
+ "entries" : [
+ ["GREEN", 0],
+ ["RED", 2],
+ ["YELLOW", 1]
+ ]
+ }
+ ],
"parsers" : [
{
"name" : "parser",
@@ -414,7 +431,7 @@
"name" : "deparser",
"id" : 0,
"source_info" : {
- "filename" : "./include/parsers.p4",
+ "filename" : "include/parsers.p4",
"line" : 72,
"column" : 8,
"source_fragment" : "deparser"
@@ -422,13 +439,58 @@
"order" : ["packet_in", "ethernet", "ipv4", "tcp", "udp"]
}
],
- "meter_arrays" : [],
+ "meter_arrays" : [
+ {
+ "name" : "port_meters_ingress.ingress_port_meter",
+ "id" : 0,
+ "source_info" : {
+ "filename" : "include/port_meters.p4",
+ "line" : 23,
+ "column" : 32,
+ "source_fragment" : "ingress_port_meter"
+ },
+ "is_direct" : false,
+ "size" : 511,
+ "rate_count" : 2,
+ "type" : "bytes"
+ },
+ {
+ "name" : "host_meter_control.host_meter",
+ "id" : 1,
+ "source_info" : {
+ "filename" : "include/host_meter_table.p4",
+ "line" : 27,
+ "column" : 43,
+ "source_fragment" : "host_meter"
+ },
+ "is_direct" : true,
+ "rate_count" : 2,
+ "type" : "bytes",
+ "size" : 1024,
+ "binding" : "host_meter_control.host_meter_table",
+ "result_target" : ["scalars", "local_metadata_t.meter_tag"]
+ },
+ {
+ "name" : "port_meters_egress.egress_port_meter",
+ "id" : 2,
+ "source_info" : {
+ "filename" : "include/port_meters.p4",
+ "line" : 37,
+ "column" : 32,
+ "source_fragment" : "egress_port_meter"
+ },
+ "is_direct" : false,
+ "size" : 511,
+ "rate_count" : 2,
+ "type" : "bytes"
+ }
+ ],
"counter_arrays" : [
{
"name" : "port_counters_ingress.ingress_port_counter",
"id" : 0,
"source_info" : {
- "filename" : "./include/port_counters.p4",
+ "filename" : "include/port_counters.p4",
"line" : 26,
"column" : 38,
"source_fragment" : "ingress_port_counter"
@@ -452,7 +514,7 @@
"name" : "port_counters_egress.egress_port_counter",
"id" : 3,
"source_info" : {
- "filename" : "./include/port_counters.p4",
+ "filename" : "include/port_counters.p4",
"line" : 36,
"column" : 38,
"source_fragment" : "egress_port_counter"
@@ -488,7 +550,7 @@
}
],
"source_info" : {
- "filename" : "./include/actions.p4",
+ "filename" : "include/actions.p4",
"line" : 28,
"column" : 36,
"source_fragment" : "port; ..."
@@ -519,7 +581,7 @@
}
],
"source_info" : {
- "filename" : "./include/actions.p4",
+ "filename" : "include/actions.p4",
"line" : 28,
"column" : 36,
"source_fragment" : "port; ..."
@@ -545,8 +607,8 @@
}
],
"source_info" : {
- "filename" : "./include/headers.p4",
- "line" : 19,
+ "filename" : "include/defines.p4",
+ "line" : 28,
"column" : 24,
"source_fragment" : "255; ..."
}
@@ -562,7 +624,7 @@
"op" : "drop",
"parameters" : [],
"source_info" : {
- "filename" : "./include/actions.p4",
+ "filename" : "include/actions.p4",
"line" : 32,
"column" : 4,
"source_fragment" : "mark_to_drop()"
@@ -577,8 +639,14 @@
"primitives" : []
},
{
- "name" : "table0_control.set_next_hop_id",
+ "name" : "NoAction",
"id" : 5,
+ "runtime_data" : [],
+ "primitives" : []
+ },
+ {
+ "name" : "table0_control.set_next_hop_id",
+ "id" : 6,
"runtime_data" : [
{
"name" : "next_hop_id",
@@ -599,7 +667,7 @@
}
],
"source_info" : {
- "filename" : "./include/table0.p4",
+ "filename" : "include/table0.p4",
"line" : 30,
"column" : 8,
"source_fragment" : "local_metadata.next_hop_id = next_hop_id"
@@ -608,49 +676,31 @@
]
},
{
+ "name" : "host_meter_control.read_meter",
+ "id" : 7,
+ "runtime_data" : [],
+ "primitives" : []
+ },
+ {
"name" : "act",
- "id" : 6,
+ "id" : 8,
"runtime_data" : [],
"primitives" : [
{
- "op" : "assign",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["standard_metadata", "egress_spec"]
- },
- {
- "type" : "field",
- "value" : ["packet_out", "egress_port"]
- }
- ],
+ "op" : "drop",
+ "parameters" : [],
"source_info" : {
- "filename" : "./include/packet_io.p4",
- "line" : 27,
+ "filename" : "include/port_meters.p4",
+ "line" : 29,
"column" : 12,
- "source_fragment" : "standard_metadata.egress_spec = hdr.packet_out.egress_port"
- }
- },
- {
- "op" : "remove_header",
- "parameters" : [
- {
- "type" : "header",
- "value" : "packet_out"
- }
- ],
- "source_info" : {
- "filename" : "./include/packet_io.p4",
- "line" : 28,
- "column" : 12,
- "source_fragment" : "hdr.packet_out.setInvalid()"
+ "source_fragment" : "mark_to_drop()"
}
}
]
},
{
"name" : "act_0",
- "id" : 7,
+ "id" : 9,
"runtime_data" : [],
"primitives" : [
{
@@ -692,58 +742,66 @@
}
],
"source_info" : {
- "filename" : "./include/port_counters.p4",
+ "filename" : "include/port_counters.p4",
"line" : 29,
"column" : 8,
"source_fragment" : "ingress_port_counter.count((bit<32>) standard_metadata.ingress_port)"
}
- }
- ]
- },
- {
- "name" : "act_1",
- "id" : 8,
- "runtime_data" : [],
- "primitives" : [
- {
- "op" : "add_header",
- "parameters" : [
- {
- "type" : "header",
- "value" : "packet_in"
- }
- ],
- "source_info" : {
- "filename" : "./include/packet_io.p4",
- "line" : 38,
- "column" : 12,
- "source_fragment" : "hdr.packet_in.setValid()"
- }
},
{
"op" : "assign",
"parameters" : [
{
"type" : "field",
- "value" : ["packet_in", "ingress_port"]
+ "value" : ["scalars", "tmp_0"]
+ },
+ {
+ "type" : "expression",
+ "value" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "&",
+ "left" : {
+ "type" : "field",
+ "value" : ["standard_metadata", "ingress_port"]
+ },
+ "right" : {
+ "type" : "hexstr",
+ "value" : "0xffffffff"
+ }
+ }
+ }
+ }
+ ]
+ },
+ {
+ "op" : "execute_meter",
+ "parameters" : [
+ {
+ "type" : "meter_array",
+ "value" : "port_meters_ingress.ingress_port_meter"
},
{
"type" : "field",
- "value" : ["standard_metadata", "ingress_port"]
+ "value" : ["scalars", "tmp_0"]
+ },
+ {
+ "type" : "field",
+ "value" : ["scalars", "port_meters_ingress_ingress_color_0"]
}
],
"source_info" : {
- "filename" : "./include/packet_io.p4",
- "line" : 39,
- "column" : 12,
- "source_fragment" : "hdr.packet_in.ingress_port = standard_metadata.ingress_port"
+ "filename" : "include/port_meters.p4",
+ "line" : 27,
+ "column" : 8,
+ "source_fragment" : "ingress_port_meter.execute_meter<MeterColor_t>((bit<32>)standard_metadata.ingress_port, ingress_color)"
}
}
]
},
{
- "name" : "act_2",
- "id" : 9,
+ "name" : "act_1",
+ "id" : 10,
"runtime_data" : [],
"primitives" : [
{
@@ -751,7 +809,224 @@
"parameters" : [
{
"type" : "field",
- "value" : ["scalars", "tmp_0"]
+ "value" : ["standard_metadata", "egress_spec"]
+ },
+ {
+ "type" : "field",
+ "value" : ["packet_out", "egress_port"]
+ }
+ ],
+ "source_info" : {
+ "filename" : "include/packet_io.p4",
+ "line" : 27,
+ "column" : 12,
+ "source_fragment" : "standard_metadata.egress_spec = hdr.packet_out.egress_port"
+ }
+ },
+ {
+ "op" : "remove_header",
+ "parameters" : [
+ {
+ "type" : "header",
+ "value" : "packet_out"
+ }
+ ],
+ "source_info" : {
+ "filename" : "include/packet_io.p4",
+ "line" : 28,
+ "column" : 12,
+ "source_fragment" : "hdr.packet_out.setInvalid()"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "act_2",
+ "id" : 11,
+ "runtime_data" : [],
+ "primitives" : [
+ {
+ "op" : "assign",
+ "parameters" : [
+ {
+ "type" : "field",
+ "value" : ["scalars", "host_meter_control_tmp_1"]
+ },
+ {
+ "type" : "expression",
+ "value" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "b2d",
+ "left" : null,
+ "right" : {
+ "type" : "bool",
+ "value" : true
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name" : "act_3",
+ "id" : 12,
+ "runtime_data" : [],
+ "primitives" : [
+ {
+ "op" : "assign",
+ "parameters" : [
+ {
+ "type" : "field",
+ "value" : ["scalars", "host_meter_control_tmp_1"]
+ },
+ {
+ "type" : "expression",
+ "value" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "b2d",
+ "left" : null,
+ "right" : {
+ "type" : "bool",
+ "value" : false
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name" : "act_4",
+ "id" : 13,
+ "runtime_data" : [],
+ "primitives" : [
+ {
+ "op" : "assign",
+ "parameters" : [
+ {
+ "type" : "field",
+ "value" : ["scalars", "host_meter_control_tmp_2"]
+ },
+ {
+ "type" : "expression",
+ "value" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "b2d",
+ "left" : null,
+ "right" : {
+ "type" : "bool",
+ "value" : false
+ }
+ }
+ }
+ }
+ ],
+ "source_info" : {
+ "filename" : "include/host_meter_table.p4",
+ "line" : 46,
+ "column" : 12,
+ "source_fragment" : "host_meter_table.apply().hit && local_metadata.meter_tag == 2"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "act_5",
+ "id" : 14,
+ "runtime_data" : [],
+ "primitives" : [
+ {
+ "op" : "assign",
+ "parameters" : [
+ {
+ "type" : "field",
+ "value" : ["scalars", "host_meter_control_tmp_2"]
+ },
+ {
+ "type" : "expression",
+ "value" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "b2d",
+ "left" : null,
+ "right" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "==",
+ "left" : {
+ "type" : "field",
+ "value" : ["scalars", "local_metadata_t.meter_tag"]
+ },
+ "right" : {
+ "type" : "hexstr",
+ "value" : "0x00000002"
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "source_info" : {
+ "filename" : "include/host_meter_table.p4",
+ "line" : 46,
+ "column" : 12,
+ "source_fragment" : "host_meter_table.apply().hit && local_metadata.meter_tag == 2"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "act_6",
+ "id" : 15,
+ "runtime_data" : [],
+ "primitives" : [
+ {
+ "op" : "drop",
+ "parameters" : [],
+ "source_info" : {
+ "filename" : "include/host_meter_table.p4",
+ "line" : 47,
+ "column" : 12,
+ "source_fragment" : "mark_to_drop()"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "act_7",
+ "id" : 16,
+ "runtime_data" : [],
+ "primitives" : [
+ {
+ "op" : "drop",
+ "parameters" : [],
+ "source_info" : {
+ "filename" : "include/port_meters.p4",
+ "line" : 43,
+ "column" : 12,
+ "source_fragment" : "mark_to_drop()"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "act_8",
+ "id" : 17,
+ "runtime_data" : [],
+ "primitives" : [
+ {
+ "op" : "assign",
+ "parameters" : [
+ {
+ "type" : "field",
+ "value" : ["scalars", "tmp_1"]
},
{
"type" : "expression",
@@ -781,15 +1056,105 @@
},
{
"type" : "field",
- "value" : ["scalars", "tmp_0"]
+ "value" : ["scalars", "tmp_1"]
}
],
"source_info" : {
- "filename" : "./include/port_counters.p4",
+ "filename" : "include/port_counters.p4",
"line" : 39,
"column" : 8,
"source_fragment" : "egress_port_counter.count((bit<32>) standard_metadata.egress_port)"
}
+ },
+ {
+ "op" : "assign",
+ "parameters" : [
+ {
+ "type" : "field",
+ "value" : ["scalars", "tmp_2"]
+ },
+ {
+ "type" : "expression",
+ "value" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "&",
+ "left" : {
+ "type" : "field",
+ "value" : ["standard_metadata", "egress_port"]
+ },
+ "right" : {
+ "type" : "hexstr",
+ "value" : "0xffffffff"
+ }
+ }
+ }
+ }
+ ]
+ },
+ {
+ "op" : "execute_meter",
+ "parameters" : [
+ {
+ "type" : "meter_array",
+ "value" : "port_meters_egress.egress_port_meter"
+ },
+ {
+ "type" : "field",
+ "value" : ["scalars", "tmp_2"]
+ },
+ {
+ "type" : "field",
+ "value" : ["scalars", "port_meters_egress_egress_color_0"]
+ }
+ ],
+ "source_info" : {
+ "filename" : "include/port_meters.p4",
+ "line" : 41,
+ "column" : 8,
+ "source_fragment" : "egress_port_meter.execute_meter<MeterColor_t>((bit<32>)standard_metadata.egress_port, egress_color)"
+ }
+ }
+ ]
+ },
+ {
+ "name" : "act_9",
+ "id" : 18,
+ "runtime_data" : [],
+ "primitives" : [
+ {
+ "op" : "add_header",
+ "parameters" : [
+ {
+ "type" : "header",
+ "value" : "packet_in"
+ }
+ ],
+ "source_info" : {
+ "filename" : "include/packet_io.p4",
+ "line" : 38,
+ "column" : 12,
+ "source_fragment" : "hdr.packet_in.setValid()"
+ }
+ },
+ {
+ "op" : "assign",
+ "parameters" : [
+ {
+ "type" : "field",
+ "value" : ["packet_in", "ingress_port"]
+ },
+ {
+ "type" : "field",
+ "value" : ["standard_metadata", "ingress_port"]
+ }
+ ],
+ "source_info" : {
+ "filename" : "include/packet_io.p4",
+ "line" : 39,
+ "column" : 12,
+ "source_fragment" : "hdr.packet_in.ingress_port = standard_metadata.ingress_port"
+ }
}
]
}
@@ -800,7 +1165,7 @@
"id" : 0,
"source_info" : {
"filename" : "basic.p4",
- "line" : 35,
+ "line" : 37,
"column" : 8,
"source_fragment" : "ingress"
},
@@ -816,14 +1181,14 @@
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
- "action_ids" : [7],
+ "action_ids" : [9],
"actions" : ["act_0"],
"base_default_next" : "node_3",
"next_tables" : {
"act_0" : "node_3"
},
"default_entry" : {
- "action_id" : 7,
+ "action_id" : 9,
"action_const" : true,
"action_data" : [],
"action_entry_const" : true
@@ -839,14 +1204,37 @@
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
- "action_ids" : [6],
+ "action_ids" : [8],
"actions" : ["act"],
- "base_default_next" : null,
+ "base_default_next" : "node_5",
"next_tables" : {
- "act" : null
+ "act" : "node_5"
},
"default_entry" : {
- "action_id" : 6,
+ "action_id" : 8,
+ "action_const" : true,
+ "action_data" : [],
+ "action_entry_const" : true
+ }
+ },
+ {
+ "name" : "tbl_act_1",
+ "id" : 2,
+ "key" : [],
+ "match_type" : "exact",
+ "type" : "simple",
+ "max_size" : 1024,
+ "with_counters" : false,
+ "support_timeout" : false,
+ "direct_meters" : null,
+ "action_ids" : [10],
+ "actions" : ["act_1"],
+ "base_default_next" : null,
+ "next_tables" : {
+ "act_1" : null
+ },
+ "default_entry" : {
+ "action_id" : 10,
"action_const" : true,
"action_data" : [],
"action_entry_const" : true
@@ -854,9 +1242,9 @@
},
{
"name" : "table0_control.table0",
- "id" : 2,
+ "id" : 3,
"source_info" : {
- "filename" : "./include/table0.p4",
+ "filename" : "include/table0.p4",
"line" : 33,
"column" : 10,
"source_fragment" : "table0"
@@ -911,16 +1299,17 @@
"match_type" : "ternary",
"type" : "simple",
"max_size" : 1024,
+ "with_counters" : true,
"support_timeout" : false,
"direct_meters" : null,
- "action_ids" : [0, 2, 5, 3],
+ "action_ids" : [0, 2, 6, 3],
"actions" : ["set_egress_port", "send_to_cpu", "table0_control.set_next_hop_id", "_drop"],
- "base_default_next" : "node_6",
+ "base_default_next" : "host_meter_control.host_meter_table",
"next_tables" : {
- "set_egress_port" : "node_6",
- "send_to_cpu" : "node_6",
- "table0_control.set_next_hop_id" : "node_6",
- "_drop" : "node_6"
+ "set_egress_port" : "host_meter_control.host_meter_table",
+ "send_to_cpu" : "host_meter_control.host_meter_table",
+ "table0_control.set_next_hop_id" : "host_meter_control.host_meter_table",
+ "_drop" : "host_meter_control.host_meter_table"
},
"default_entry" : {
"action_id" : 3,
@@ -930,10 +1319,161 @@
}
},
{
- "name" : "wcmp_control.wcmp_table",
- "id" : 3,
+ "name" : "host_meter_control.host_meter_table",
+ "id" : 4,
"source_info" : {
- "filename" : "./include/wcmp.p4",
+ "filename" : "include/host_meter_table.p4",
+ "line" : 33,
+ "column" : 10,
+ "source_fragment" : "host_meter_table"
+ },
+ "key" : [
+ {
+ "match_type" : "lpm",
+ "target" : ["ethernet", "src_addr"],
+ "mask" : null
+ }
+ ],
+ "match_type" : "lpm",
+ "type" : "simple",
+ "max_size" : 1024,
+ "with_counters" : false,
+ "support_timeout" : false,
+ "direct_meters" : "host_meter_control.host_meter",
+ "action_ids" : [7, 4],
+ "actions" : ["host_meter_control.read_meter", "NoAction"],
+ "base_default_next" : null,
+ "next_tables" : {
+ "__HIT__" : "tbl_act_2",
+ "__MISS__" : "tbl_act_3"
+ },
+ "default_entry" : {
+ "action_id" : 4,
+ "action_const" : false,
+ "action_data" : [],
+ "action_entry_const" : false
+ }
+ },
+ {
+ "name" : "tbl_act_2",
+ "id" : 5,
+ "key" : [],
+ "match_type" : "exact",
+ "type" : "simple",
+ "max_size" : 1024,
+ "with_counters" : false,
+ "support_timeout" : false,
+ "direct_meters" : null,
+ "action_ids" : [11],
+ "actions" : ["act_2"],
+ "base_default_next" : "node_11",
+ "next_tables" : {
+ "act_2" : "node_11"
+ },
+ "default_entry" : {
+ "action_id" : 11,
+ "action_const" : true,
+ "action_data" : [],
+ "action_entry_const" : true
+ }
+ },
+ {
+ "name" : "tbl_act_3",
+ "id" : 6,
+ "key" : [],
+ "match_type" : "exact",
+ "type" : "simple",
+ "max_size" : 1024,
+ "with_counters" : false,
+ "support_timeout" : false,
+ "direct_meters" : null,
+ "action_ids" : [12],
+ "actions" : ["act_3"],
+ "base_default_next" : "node_11",
+ "next_tables" : {
+ "act_3" : "node_11"
+ },
+ "default_entry" : {
+ "action_id" : 12,
+ "action_const" : true,
+ "action_data" : [],
+ "action_entry_const" : true
+ }
+ },
+ {
+ "name" : "tbl_act_4",
+ "id" : 7,
+ "key" : [],
+ "match_type" : "exact",
+ "type" : "simple",
+ "max_size" : 1024,
+ "with_counters" : false,
+ "support_timeout" : false,
+ "direct_meters" : null,
+ "action_ids" : [13],
+ "actions" : ["act_4"],
+ "base_default_next" : "node_14",
+ "next_tables" : {
+ "act_4" : "node_14"
+ },
+ "default_entry" : {
+ "action_id" : 13,
+ "action_const" : true,
+ "action_data" : [],
+ "action_entry_const" : true
+ }
+ },
+ {
+ "name" : "tbl_act_5",
+ "id" : 8,
+ "key" : [],
+ "match_type" : "exact",
+ "type" : "simple",
+ "max_size" : 1024,
+ "with_counters" : false,
+ "support_timeout" : false,
+ "direct_meters" : null,
+ "action_ids" : [14],
+ "actions" : ["act_5"],
+ "base_default_next" : "node_14",
+ "next_tables" : {
+ "act_5" : "node_14"
+ },
+ "default_entry" : {
+ "action_id" : 14,
+ "action_const" : true,
+ "action_data" : [],
+ "action_entry_const" : true
+ }
+ },
+ {
+ "name" : "tbl_act_6",
+ "id" : 9,
+ "key" : [],
+ "match_type" : "exact",
+ "type" : "simple",
+ "max_size" : 1024,
+ "with_counters" : false,
+ "support_timeout" : false,
+ "direct_meters" : null,
+ "action_ids" : [15],
+ "actions" : ["act_6"],
+ "base_default_next" : "node_16",
+ "next_tables" : {
+ "act_6" : "node_16"
+ },
+ "default_entry" : {
+ "action_id" : 15,
+ "action_const" : true,
+ "action_data" : [],
+ "action_entry_const" : true
+ }
+ },
+ {
+ "name" : "wcmp_control.wcmp_table",
+ "id" : 10,
+ "source_info" : {
+ "filename" : "include/wcmp.p4",
"line" : 30,
"column" : 10,
"source_fragment" : "wcmp_table"
@@ -949,9 +1489,10 @@
"type" : "indirect_ws",
"action_profile" : "wcmp_control.wcmp_selector",
"max_size" : 1024,
+ "with_counters" : true,
"support_timeout" : false,
"direct_meters" : null,
- "action_ids" : [1, 4],
+ "action_ids" : [1, 5],
"actions" : ["set_egress_port", "NoAction"],
"base_default_next" : null,
"next_tables" : {
@@ -997,7 +1538,33 @@
"name" : "node_3",
"id" : 0,
"source_info" : {
- "filename" : "./include/packet_io.p4",
+ "filename" : "include/port_meters.p4",
+ "line" : 28,
+ "column" : 12,
+ "source_fragment" : "ingress_color == MeterColor_t.RED"
+ },
+ "expression" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "==",
+ "left" : {
+ "type" : "field",
+ "value" : ["scalars", "port_meters_ingress_ingress_color_0"]
+ },
+ "right" : {
+ "type" : "hexstr",
+ "value" : "0x00000002"
+ }
+ }
+ },
+ "true_next" : "tbl_act_0",
+ "false_next" : "node_5"
+ },
+ {
+ "name" : "node_5",
+ "id" : 1,
+ "source_info" : {
+ "filename" : "include/packet_io.p4",
"line" : 26,
"column" : 12,
"source_fragment" : "standard_metadata.ingress_port == CPU_PORT"
@@ -1016,14 +1583,55 @@
}
}
},
- "true_next" : "tbl_act_0",
+ "true_next" : "tbl_act_1",
"false_next" : "table0_control.table0"
},
{
- "name" : "node_6",
- "id" : 1,
+ "name" : "node_11",
+ "id" : 2,
+ "expression" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "not",
+ "left" : null,
+ "right" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "d2b",
+ "left" : null,
+ "right" : {
+ "type" : "field",
+ "value" : ["scalars", "host_meter_control_tmp_1"]
+ }
+ }
+ }
+ }
+ },
+ "true_next" : "tbl_act_4",
+ "false_next" : "tbl_act_5"
+ },
+ {
+ "name" : "node_14",
+ "id" : 3,
+ "expression" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "d2b",
+ "left" : null,
+ "right" : {
+ "type" : "field",
+ "value" : ["scalars", "host_meter_control_tmp_2"]
+ }
+ }
+ },
+ "true_next" : "tbl_act_6",
+ "false_next" : "node_16"
+ },
+ {
+ "name" : "node_16",
+ "id" : 4,
"source_info" : {
- "filename" : "./include/wcmp.p4",
+ "filename" : "include/wcmp.p4",
"line" : 48,
"column" : 12,
"source_fragment" : "local_metadata.next_hop_id != 0"
@@ -1052,15 +1660,15 @@
"id" : 1,
"source_info" : {
"filename" : "basic.p4",
- "line" : 51,
+ "line" : 55,
"column" : 8,
"source_fragment" : "egress"
},
- "init_table" : "tbl_act_1",
+ "init_table" : "tbl_act_7",
"tables" : [
{
- "name" : "tbl_act_1",
- "id" : 4,
+ "name" : "tbl_act_7",
+ "id" : 11,
"key" : [],
"match_type" : "exact",
"type" : "simple",
@@ -1068,22 +1676,22 @@
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
- "action_ids" : [9],
- "actions" : ["act_2"],
- "base_default_next" : "node_11",
+ "action_ids" : [17],
+ "actions" : ["act_8"],
+ "base_default_next" : "node_21",
"next_tables" : {
- "act_2" : "node_11"
+ "act_8" : "node_21"
},
"default_entry" : {
- "action_id" : 9,
+ "action_id" : 17,
"action_const" : true,
"action_data" : [],
"action_entry_const" : true
}
},
{
- "name" : "tbl_act_2",
- "id" : 5,
+ "name" : "tbl_act_8",
+ "id" : 12,
"key" : [],
"match_type" : "exact",
"type" : "simple",
@@ -1091,14 +1699,37 @@
"with_counters" : false,
"support_timeout" : false,
"direct_meters" : null,
- "action_ids" : [8],
- "actions" : ["act_1"],
- "base_default_next" : null,
+ "action_ids" : [16],
+ "actions" : ["act_7"],
+ "base_default_next" : "node_23",
"next_tables" : {
- "act_1" : null
+ "act_7" : "node_23"
},
"default_entry" : {
- "action_id" : 8,
+ "action_id" : 16,
+ "action_const" : true,
+ "action_data" : [],
+ "action_entry_const" : true
+ }
+ },
+ {
+ "name" : "tbl_act_9",
+ "id" : 13,
+ "key" : [],
+ "match_type" : "exact",
+ "type" : "simple",
+ "max_size" : 1024,
+ "with_counters" : false,
+ "support_timeout" : false,
+ "direct_meters" : null,
+ "action_ids" : [18],
+ "actions" : ["act_9"],
+ "base_default_next" : null,
+ "next_tables" : {
+ "act_9" : null
+ },
+ "default_entry" : {
+ "action_id" : 18,
"action_const" : true,
"action_data" : [],
"action_entry_const" : true
@@ -1108,10 +1739,36 @@
"action_profiles" : [],
"conditionals" : [
{
- "name" : "node_11",
- "id" : 2,
+ "name" : "node_21",
+ "id" : 5,
"source_info" : {
- "filename" : "./include/packet_io.p4",
+ "filename" : "include/port_meters.p4",
+ "line" : 42,
+ "column" : 12,
+ "source_fragment" : "egress_color == MeterColor_t.RED"
+ },
+ "expression" : {
+ "type" : "expression",
+ "value" : {
+ "op" : "==",
+ "left" : {
+ "type" : "field",
+ "value" : ["scalars", "port_meters_egress_egress_color_0"]
+ },
+ "right" : {
+ "type" : "hexstr",
+ "value" : "0x00000002"
+ }
+ }
+ },
+ "true_next" : "tbl_act_8",
+ "false_next" : "node_23"
+ },
+ {
+ "name" : "node_23",
+ "id" : 6,
+ "source_info" : {
+ "filename" : "include/packet_io.p4",
"line" : 37,
"column" : 12,
"source_fragment" : "standard_metadata.egress_port == CPU_PORT"
@@ -1131,7 +1788,7 @@
}
},
"false_next" : null,
- "true_next" : "tbl_act_2"
+ "true_next" : "tbl_act_9"
}
]
}
diff --git a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.p4info b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.p4info
index 8010892..9ec66ba 100644
--- a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.p4info
+++ b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.p4info
@@ -76,6 +76,27 @@
}
tables {
preamble {
+ id: 33597882
+ name: "host_meter_control.host_meter_table"
+ alias: "host_meter_table"
+ }
+ match_fields {
+ id: 1
+ name: "hdr.ethernet.src_addr"
+ bitwidth: 48
+ match_type: LPM
+ }
+ action_refs {
+ id: 16832719
+ }
+ action_refs {
+ id: 16800567
+ }
+ direct_resource_ids: 318776014
+ size: 1024
+}
+tables {
+ preamble {
id: 33592597
name: "wcmp_control.wcmp_table"
alias: "wcmp_table"
@@ -142,6 +163,13 @@
bitwidth: 16
}
}
+actions {
+ preamble {
+ id: 16832719
+ name: "host_meter_control.read_meter"
+ alias: "read_meter"
+ }
+}
action_profiles {
preamble {
id: 285259294
@@ -196,6 +224,39 @@
}
direct_table_id: 33592597
}
+meters {
+ preamble {
+ id: 318770010
+ name: "port_meters_ingress.ingress_port_meter"
+ alias: "ingress_port_meter"
+ }
+ spec {
+ unit: BYTES
+ }
+ size: 511
+}
+meters {
+ preamble {
+ id: 318779497
+ name: "port_meters_egress.egress_port_meter"
+ alias: "egress_port_meter"
+ }
+ spec {
+ unit: BYTES
+ }
+ size: 511
+}
+direct_meters {
+ preamble {
+ id: 318776014
+ name: "host_meter_control.host_meter"
+ alias: "host_meter"
+ }
+ spec {
+ unit: BYTES
+ }
+ direct_table_id: 33597882
+}
controller_packet_metadata {
preamble {
id: 2868941301
diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
index 7ecbd80..91254cf 100644
--- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
+++ b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeClient.java
@@ -23,6 +23,9 @@
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.model.PiCounterId;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
+import org.onosproject.net.pi.model.PiMeterId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.model.PiTableId;
@@ -152,6 +155,36 @@
PiPipeconf pipeconf);
/**
+ * Returns the configuration of all meter cells for the given set of meter identifiers and pipeconf.
+ *
+ * @param meterIds meter identifiers
+ * @param pipeconf pipeconf
+ * @return collection of meter configurations
+ */
+ CompletableFuture<Collection<PiMeterCellConfig>> readAllMeterCells(Set<PiMeterId> meterIds,
+ PiPipeconf pipeconf);
+
+ /**
+ * Returns a collection of meter configurations corresponding to the given set of meter cell identifiers,
+ * for the given pipeconf.
+ *
+ * @param cellIds set of meter cell identifiers
+ * @param pipeconf pipeconf
+ * @return collection of meter configrations
+ */
+ CompletableFuture<Collection<PiMeterCellConfig>> readMeterCells(Set<PiMeterCellId> cellIds,
+ PiPipeconf pipeconf);
+
+ /**
+ * Performs a write operation for the given meter configurations and pipeconf.
+ *
+ * @param cellConfigs meter cell configurations
+ * @param pipeconf pipeconf currently deployed on the device
+ * @return true if the operation was successful, false otherwise.
+ */
+ CompletableFuture<Boolean> writeMeterCells(Collection<PiMeterCellConfig> cellConfigs, PiPipeconf pipeconf);
+
+ /**
* Shutdown the client by terminating any active RPC such as the stream channel.
*/
void shutdown();
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java
new file mode 100644
index 0000000..c30e2c8
--- /dev/null
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java
@@ -0,0 +1,242 @@
+/*
+ * 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.p4runtime.ctl;
+
+import org.onosproject.net.pi.model.PiMeterType;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiMeterBand;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
+import org.onosproject.net.pi.model.PiMeterId;
+import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.slf4j.Logger;
+import p4.P4RuntimeOuterClass.MeterConfig;
+import p4.P4RuntimeOuterClass.MeterEntry;
+import p4.P4RuntimeOuterClass.DirectMeterEntry;
+import p4.P4RuntimeOuterClass.Entity;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static org.slf4j.LoggerFactory.getLogger;
+import static p4.P4RuntimeOuterClass.Entity.EntityCase.*;
+
+/**
+ * Encoder/decoder of PI meter cell configurations to meter entry protobuf messages, and vice versa.
+ */
+final class MeterEntryCodec {
+
+ private static final Logger log = getLogger(MeterEntryCodec.class);
+
+ private MeterEntryCodec() {
+ // Hides constructor.
+ }
+
+ /**
+ * Returns a collection of P4Runtime entity protobuf messages describing both meter or direct meter entries,
+ * encoded from the given collection of PI meter cell configurations, for the given pipeconf. If a PI meter cell
+ * configurations cannot be encoded, it is skipped, hence the returned collection might have different size than the
+ * input one.
+ * <p>
+ * This method takes as parameter also a map between numeric P4Info IDs and PI meter IDs, that will be populated
+ * during the process and that is then needed to aid in the decode process.
+ *
+ * @param cellConfigs meter cell configurations
+ * @param meterIdMap meter ID map (empty, it will be populated during this method execution)
+ * @param pipeconf pipeconf
+ * @return collection of entity messages describing both meter or direct meter entries
+ */
+ static Collection<Entity> encodePiMeterCellConfigs(Collection<PiMeterCellConfig> cellConfigs,
+ Map<Integer, PiMeterId> meterIdMap,
+ PiPipeconf pipeconf) {
+ return cellConfigs
+ .stream()
+ .map(cellConfig -> {
+ try {
+ return encodePiMeterCellConfig(cellConfig, meterIdMap, pipeconf);
+ } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+ log.warn("Unable to encode PI meter cell id: {}", e.getMessage());
+ log.debug("exception", e);
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns a collection of PI meter cell configurations, decoded from the given P4Runtime entity protobuf messages
+ * describing both meter or direct meter entries, for the given meter ID map (populated by {@link
+ * #encodePiMeterCellConfigs(Collection, Map, PiPipeconf)}), and pipeconf. If an entity message cannot be encoded,
+ * it is skipped, hence the returned collection might have different size than the input one.
+ *
+ * @param entities P4Runtime entity messages
+ * @param meterIdMap meter ID map (previously populated)
+ * @param pipeconf pipeconf
+ * @return collection of PI meter cell data
+ */
+ static Collection<PiMeterCellConfig> decodeMeterEntities(Collection<Entity> entities,
+ Map<Integer, PiMeterId> meterIdMap,
+ PiPipeconf pipeconf) {
+ return entities
+ .stream()
+ .filter(entity -> entity.getEntityCase() == METER_ENTRY ||
+ entity.getEntityCase() == DIRECT_METER_ENTRY)
+ .map(entity -> {
+ try {
+ return decodeMeterEntity(entity, meterIdMap, pipeconf);
+ } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
+ log.warn("Unable to decode meter entity message: {}", e.getMessage());
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ private static Entity encodePiMeterCellConfig(PiMeterCellConfig config, Map<Integer, PiMeterId> meterIdMap,
+ PiPipeconf pipeconf)
+ throws P4InfoBrowser.NotFoundException, EncodeException {
+
+ final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+ int meterId;
+ Entity entity;
+ //The band with bigger burst is peak if rate of them is equal,
+ //if bands are not specificed, using default value(0).
+ long cir = 0;
+ long cburst = 0;
+ long pir = 0;
+ long pburst = 0;
+ PiMeterBand[] bands = config.meterBands().toArray(new PiMeterBand[config.meterBands().size()]);
+ if (bands.length == 2) {
+ if (bands[0].rate() > bands[1].rate()) {
+ cir = bands[1].rate();
+ cburst = bands[1].burst();
+ pir = bands[0].rate();
+ pburst = bands[0].burst();
+ } else {
+ cir = bands[0].rate();
+ cburst = bands[0].burst();
+ pir = bands[1].rate();
+ pburst = bands[1].burst();
+ }
+ }
+
+ // Encode PI cell ID into entity message and add to read request.
+ switch (config.cellId().meterType()) {
+ case INDIRECT:
+ meterId = browser.meters().getByName(config.cellId().meterId().id()).getPreamble().getId();
+ entity = Entity.newBuilder().setMeterEntry(MeterEntry
+ .newBuilder().setMeterId(meterId)
+ .setIndex(config.cellId().index())
+ .setConfig(MeterConfig.newBuilder()
+ .setCir(cir)
+ .setCburst(cburst)
+ .setPir(pir)
+ .setPburst(pburst)
+ .build())
+ .build())
+ .build();
+ break;
+ case DIRECT:
+ meterId = browser.directMeters().getByName(config.cellId().meterId().id()).getPreamble().getId();
+ DirectMeterEntry.Builder entryBuilder = DirectMeterEntry.newBuilder()
+ .setMeterId(meterId)
+ .setConfig(MeterConfig.newBuilder()
+ .setCir(cir)
+ .setCburst(cburst)
+ .setPir(pir)
+ .setPburst(pburst)
+ .build());
+
+ if (!config.cellId().tableEntry().equals(PiTableEntry.EMTPY)) {
+ entryBuilder.setTableEntry(TableEntryEncoder.encode(config.cellId().tableEntry(), pipeconf));
+ }
+ entity = Entity.newBuilder().setDirectMeterEntry(entryBuilder.build()).build();
+ break;
+ default:
+ throw new EncodeException(format("Unrecognized PI meter cell ID type '%s'",
+ config.cellId().meterType()));
+ }
+ meterIdMap.put(meterId, config.cellId().meterId());
+
+ return entity;
+ }
+
+ private static PiMeterCellConfig decodeMeterEntity(Entity entity, Map<Integer, PiMeterId> meterIdMap,
+ PiPipeconf pipeconf)
+ throws EncodeException, P4InfoBrowser.NotFoundException {
+
+ int meterId;
+ MeterConfig meterConfig;
+
+ if (entity.getEntityCase() == METER_ENTRY) {
+ meterId = entity.getMeterEntry().getMeterId();
+ meterConfig = entity.getMeterEntry().getConfig();
+ } else {
+ meterId = entity.getDirectMeterEntry().getMeterId();
+ meterConfig = entity.getDirectMeterEntry().getConfig();
+ }
+
+ // Process only meter IDs that were requested in the first place.
+ if (!meterIdMap.containsKey(meterId)) {
+ throw new EncodeException(format("Unrecognized meter ID '%s'", meterId));
+ }
+
+ PiMeterId piMeterId = meterIdMap.get(meterId);
+ if (!pipeconf.pipelineModel().meter(piMeterId).isPresent()) {
+ throw new EncodeException(format("Unable to find meter '{}' in pipeline model", meterId));
+ }
+
+ PiMeterType piMeterType = pipeconf.pipelineModel().meter(piMeterId).get().meterType();
+ // Compute PI cell ID.
+ PiMeterCellId piCellId;
+
+ switch (piMeterType) {
+ case INDIRECT:
+ if (entity.getEntityCase() != METER_ENTRY) {
+ throw new EncodeException(format(
+ "Meter ID '%s' is indirect, but processed entity is %s",
+ piMeterId, entity.getEntityCase()));
+ }
+ piCellId = PiMeterCellId.ofIndirect(piMeterId, entity.getMeterEntry().getIndex());
+ break;
+ case DIRECT:
+ if (entity.getEntityCase() != DIRECT_METER_ENTRY) {
+ throw new EncodeException(format(
+ "Meter ID '%s' is direct, but processed entity is %s",
+ piMeterId, entity.getEntityCase()));
+ }
+ PiTableEntry piTableEntry = TableEntryEncoder.decode(entity.getDirectMeterEntry().getTableEntry(),
+ pipeconf);
+ piCellId = PiMeterCellId.ofDirect(piMeterId, piTableEntry);
+ break;
+ default:
+ throw new EncodeException(format("Unrecognized PI meter ID type '%s'", piMeterType));
+ }
+
+ PiMeterCellConfig.Builder builder = PiMeterCellConfig.builder();
+ builder.withMeterBand(new PiMeterBand(meterConfig.getCir(), meterConfig.getCburst()));
+ builder.withMeterBand(new PiMeterBand(meterConfig.getPir(), meterConfig.getPburst()));
+
+ return builder.withMeterCellId(piCellId).build();
+ }
+}
\ No newline at end of file
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
index ac6d9f0..609c5c5 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
@@ -44,6 +44,10 @@
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
+import org.onosproject.net.pi.model.PiMeterType;
+import org.onosproject.net.pi.model.PiMeterId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.service.PiPipeconfService;
@@ -127,6 +131,8 @@
private Map<Uint128, CompletableFuture<Boolean>> arbitrationUpdateMap = Maps.newConcurrentMap();
protected Uint128 p4RuntimeElectionId;
+ private static final long DEFAULT_INDEX = 0;
+
/**
* Default constructor.
*
@@ -275,6 +281,51 @@
public CompletableFuture<Boolean> sendMasterArbitrationUpdate() {
return supplyInContext(this::doArbitrationUpdate, "arbitrationUpdate");
}
+ public CompletableFuture<Boolean> writeMeterCells(Collection<PiMeterCellConfig> cellIds, PiPipeconf pipeconf) {
+
+ return supplyInContext(() -> doWriteMeterCells(cellIds, pipeconf),
+ "writeMeterCells");
+ }
+
+ @Override
+ public CompletableFuture<Collection<PiMeterCellConfig>> readMeterCells(Set<PiMeterCellId> cellIds,
+ PiPipeconf pipeconf) {
+ return supplyInContext(() -> doReadMeterCells(cellIds, pipeconf),
+ "readMeterCells-" + cellIds.hashCode());
+ }
+
+ @Override
+ public CompletableFuture<Collection<PiMeterCellConfig>> readAllMeterCells(Set<PiMeterId> meterIds,
+ PiPipeconf pipeconf) {
+
+ /*
+ From p4runtime.proto, the scope of a ReadRequest is defined as follows:
+ MeterEntry:
+ - All meter cells for all meters if meter_id = 0 (default).
+ - All meter cells for given meter_id if index = 0 (default).
+ DirectCounterEntry:
+ - All meter cells for all meters if meter_id = 0 (default).
+ - All meter cells for given meter_id if table_entry.match is empty.
+ */
+
+ Set<PiMeterCellId> cellIds = Sets.newHashSet();
+ for (PiMeterId meterId : meterIds) {
+ PiMeterType meterType = pipeconf.pipelineModel().meter(meterId).get().meterType();
+ switch (meterType) {
+ case INDIRECT:
+ cellIds.add(PiMeterCellId.ofIndirect(meterId, DEFAULT_INDEX));
+ break;
+ case DIRECT:
+ cellIds.add(PiMeterCellId.ofDirect(meterId, PiTableEntry.EMTPY));
+ break;
+ default:
+ log.warn("Unrecognized PI meter type '{}'", meterType);
+ }
+ }
+
+ return supplyInContext(() -> doReadMeterCells(cellIds, pipeconf),
+ "readAllMeterCells-" + cellIds.hashCode());
+ }
/* Blocking method implementations below */
@@ -738,6 +789,74 @@
}
}
+ private Collection<PiMeterCellConfig> doReadMeterCells(Collection<PiMeterCellId> cellIds, PiPipeconf pipeconf) {
+
+ // We use this map to remember the original PI meter IDs of the returned response.
+ Map<Integer, PiMeterId> meterIdMap = Maps.newHashMap();
+ Collection<PiMeterCellConfig> piMeterCellConfigs = cellIds.stream()
+ .map(cellId -> PiMeterCellConfig.builder()
+ .withMeterCellId(cellId).build())
+ .collect(Collectors.toList());
+
+ final ReadRequest request = ReadRequest.newBuilder()
+ .setDeviceId(p4DeviceId)
+ .addAllEntities(MeterEntryCodec.encodePiMeterCellConfigs(piMeterCellConfigs, meterIdMap, pipeconf))
+ .build();
+
+ if (request.getEntitiesList().size() == 0) {
+ return Collections.emptyList();
+ }
+
+ final Iterable<ReadResponse> responses;
+ try {
+ responses = () -> blockingStub.read(request);
+ } catch (StatusRuntimeException e) {
+ log.warn("Unable to read meters config: {}", e.getMessage());
+ log.debug("exception", e);
+ return Collections.emptyList();
+ }
+
+ List<Entity> entities = StreamSupport.stream(responses.spliterator(), false)
+ .map(ReadResponse::getEntitiesList)
+ .flatMap(List::stream)
+ .collect(Collectors.toList());
+
+ return MeterEntryCodec.decodeMeterEntities(entities, meterIdMap, pipeconf);
+ }
+
+ private boolean doWriteMeterCells(Collection<PiMeterCellConfig> cellIds, PiPipeconf pipeconf) {
+
+ final Map<Integer, PiMeterId> meterIdMap = Maps.newHashMap();
+ WriteRequest.Builder writeRequestBuilder = WriteRequest.newBuilder();
+
+ Collection<Update> updateMsgs = MeterEntryCodec.encodePiMeterCellConfigs(cellIds, meterIdMap, pipeconf)
+ .stream()
+ .map(meterEntryMsg ->
+ Update.newBuilder()
+ .setEntity(meterEntryMsg)
+ .setType(UPDATE_TYPES.get(WriteOperationType.MODIFY))
+ .build())
+ .collect(Collectors.toList());
+
+ if (updateMsgs.size() == 0) {
+ return true;
+ }
+
+ writeRequestBuilder
+ .setDeviceId(p4DeviceId)
+ .setElectionId(p4RuntimeElectionId)
+ .addAllUpdates(updateMsgs)
+ .build();
+ try {
+ blockingStub.write(writeRequestBuilder.build());
+ return true;
+ } catch (StatusRuntimeException e) {
+ log.warn("Unable to write meter entries : {}", e.getMessage());
+ log.debug("exception", e);
+ return false;
+ }
+ }
+
/**
* Returns the internal P4 device ID associated with this client.
*
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java
index 235ca0f..65b932d 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java
@@ -42,6 +42,7 @@
import org.onosproject.net.meter.Band;
import org.onosproject.net.meter.DefaultBand;
import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterCellId;
import org.onosproject.net.meter.MeterId;
import org.onosproject.net.meter.MeterService;
import org.onosproject.net.meter.MeterState;
@@ -138,6 +139,11 @@
}
@Override
+ public MeterCellId meterCellId() {
+ return this.id();
+ }
+
+ @Override
public ApplicationId appId() {
return this.appId;
}