Add support for one shot action profile programming in PI
A P4 table annotated with @oneshot annotation can be programmed
only with the action profile action set. For these kind of tables
we don't issue read request for action profile groups and members.
Change-Id: I7b6a743f4f4df4190f17d958ebb4807aca5feda5
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiTableModel.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiTableModel.java
index 80e41df..51ba53d 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/PiTableModel.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiTableModel.java
@@ -109,6 +109,14 @@
boolean isConstantTable();
/**
+ * Returns true if the table supports one shot only when associated to an
+ * action profile.
+ *
+ * @return true if table support one shot only, false otherwise
+ */
+ boolean oneShotOnly();
+
+ /**
* Returns the action model associated with the given ID, if present. If not
* present, it means that this table does not support such an action.
*
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionSet.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionSet.java
new file mode 100644
index 0000000..f2908d4
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionSet.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2020-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.Sets;
+
+import java.util.Set;
+
+/**
+ * Instance of an action set of a protocol-independent pipeline used
+ * when doing one-shot action selector programming. Contains a set of weighted
+ * actions, and it is equivalent to the action profile action set from P4Runtime
+ * specifications.
+ */
+@Beta
+public final class PiActionSet implements PiTableAction {
+
+ private final Set<WeightedAction> actionSet;
+
+ private PiActionSet(Set<WeightedAction> actionSet) {
+ this.actionSet = actionSet;
+ }
+
+ /**
+ * Returns the set of actions.
+ *
+ * @return the set of actions
+ */
+ public Set<WeightedAction> actions() {
+ return actionSet;
+ }
+
+ @Override
+ public Type type() {
+ return Type.ACTION_SET;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PiActionSet that = (PiActionSet) o;
+ return Objects.equal(actionSet, that.actionSet);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(actionSet);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("actionSet", actionSet)
+ .toString();
+ }
+
+ /**
+ * Returns a new builder of an action set.
+ *
+ * @return action set builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder of an action set.
+ */
+ public static final class Builder {
+
+ private final Set<WeightedAction> actionSet = Sets.newHashSet();
+
+ private Builder() {
+ // hides constructor.
+ }
+
+ /**
+ * Adds a weighted action to this action set.
+ *
+ * @param action The action to add
+ * @param weight The weight associated to the action
+ * @return this
+ */
+ public Builder addWeightedAction(
+ PiAction action, int weight) {
+ actionSet.add(new WeightedAction(action, weight));
+ return this;
+ }
+
+ /**
+ * Creates a new action profile action set.
+ *
+ * @return action profile action set
+ */
+ public PiActionSet build() {
+ return new PiActionSet(Set.copyOf(actionSet));
+ }
+ }
+
+ /**
+ * Weighted action used in an actions set.
+ */
+ public static final class WeightedAction {
+ public static final int DEFAULT_WEIGHT = 1;
+
+ private final int weight;
+ private final PiAction action;
+
+ /**
+ * Creates a new weighted action instance that can be used in an action
+ * set, from the given PI action and weight.
+ *
+ * @param action the action
+ * @param weight the weigh
+ */
+ public WeightedAction(PiAction action, int weight) {
+ this.weight = weight;
+ this.action = action;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ WeightedAction that = (WeightedAction) o;
+ return weight == that.weight &&
+ Objects.equal(action, that.action);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(weight, action);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("weight", weight)
+ .add("action", action)
+ .toString();
+ }
+
+ public int weight() {
+ return weight;
+ }
+
+ public PiAction action() {
+ return action;
+ }
+ }
+}
+
+
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableAction.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableAction.java
index 284dde6..d803ca6 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableAction.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableAction.java
@@ -43,7 +43,13 @@
* Executes the action profile member specified by the given
* identifier.
*/
- ACTION_PROFILE_MEMBER_ID
+ ACTION_PROFILE_MEMBER_ID,
+
+ /**
+ * Executes the given action set. Used in one-shot action profile
+ * programming.
+ */
+ ACTION_SET
}
/**
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
index ee3c3c3..40ad4de 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
@@ -185,6 +185,8 @@
return "ACT_PROF_GROUP:" + ((PiActionProfileGroupId) tableAction).id();
case ACTION_PROFILE_MEMBER_ID:
return "ACT_PROF_MEMBER:" + ((PiActionProfileMemberId) tableAction).id();
+ case ACTION_SET:
+ return "ACTION_SET:" + tableAction.toString();
case ACTION:
default:
return tableAction.toString();
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
index 7307939..e3efae2 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
@@ -329,6 +329,7 @@
return Instructions.piTableAction(piActionProfileMemberId);
}
+ // TODO: implement JSON decoder for ACTION_SET
throw new IllegalArgumentException("Protocol-independent Instruction subtype "
+ subType + " is not supported");
}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
index 996fa8c..cab335c 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java
@@ -289,6 +289,7 @@
final PiActionProfileMemberId memberId = (PiActionProfileMemberId) piInstruction.action();
result.put(InstructionCodec.PI_ACTION_PROFILE_MEMBER_ID, memberId.id());
break;
+ //TODO: implement JSON encoder for ACTION_SET
default:
throw new IllegalArgumentException("Cannot convert protocol-independent subtype of" +
piInstruction.action().type().name());
diff --git a/core/net/BUILD b/core/net/BUILD
index fbc6fb6..8b38727 100644
--- a/core/net/BUILD
+++ b/core/net/BUILD
@@ -10,6 +10,7 @@
"//core/store/dist:onos-core-dist-tests",
"//utils/osgi:onlab-osgi-tests",
"//pipelines/basic:onos-pipelines-basic",
+ "//protocols/p4runtime/model:onos-protocols-p4runtime-model-native",
"@minimal_json//jar",
]
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
index f399508..2b4b0f3 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
@@ -40,6 +40,7 @@
import org.onosproject.net.pi.model.PiTableType;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.net.pi.runtime.PiActionSet;
import org.onosproject.net.pi.runtime.PiExactFieldMatch;
import org.onosproject.net.pi.runtime.PiFieldMatch;
import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
@@ -234,19 +235,28 @@
switch (piTableAction.type()) {
case ACTION:
return checkPiAction((PiAction) piTableAction, table);
+ case ACTION_SET:
+ for (var actProfAct : ((PiActionSet) piTableAction).actions()) {
+ checkPiAction(actProfAct.action(), table);
+ }
case ACTION_PROFILE_GROUP_ID:
+ if (table.actionProfile() == null || !table.actionProfile().hasSelector()) {
+ throw new PiTranslationException(format(
+ "action is of type '%s', but table '%s' does not" +
+ "implement an action profile with dynamic selection",
+ piTableAction.type(), table.id()));
+ }
case ACTION_PROFILE_MEMBER_ID:
if (!table.tableType().equals(PiTableType.INDIRECT)) {
throw new PiTranslationException(format(
"action is indirect of type '%s', but table '%s' is of type '%s'",
piTableAction.type(), table.id(), table.tableType()));
}
- if (piTableAction.type().equals(PiTableAction.Type.ACTION_PROFILE_GROUP_ID)
- && (table.actionProfile() == null || !table.actionProfile().hasSelector())) {
+ if (!piTableAction.type().equals(PiTableAction.Type.ACTION_SET) &&
+ table.oneShotOnly()) {
throw new PiTranslationException(format(
- "action is of type '%s', but table '%s' does not" +
- "implement an action profile with dynamic selection",
- piTableAction.type(), table.id()));
+ "table '%s' supports only one shot programming", table.id()
+ ));
}
return piTableAction;
default:
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslatorImpl.java
index 42a29a8..e06f54e 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslatorImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslatorImpl.java
@@ -94,6 +94,18 @@
actionProfileId));
}
+ // Check if the table associated with the action profile supports only
+ // one-shot action profile programming.
+ boolean isTableOneShot = actionProfileModel.tables().stream()
+ .map(tableId -> pipeconf.pipelineModel().table(tableId))
+ .allMatch(piTableModel -> piTableModel.isPresent() &&
+ piTableModel.get().oneShotOnly());
+ if (isTableOneShot) {
+ throw new PiTranslationException(format(
+ "Table associated to action profile '%s' supports only one-shot action profile programming",
+ actionProfileId));
+ }
+
// Check group validity.
if (actionProfileModel.maxGroupSize() > 0
&& group.buckets().buckets().size() > actionProfileModel.maxGroupSize()) {
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiGroupTranslatorImplTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiGroupTranslatorImplTest.java
index ac9817d..1da3997 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiGroupTranslatorImplTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiGroupTranslatorImplTest.java
@@ -20,7 +20,9 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.testing.EqualsTester;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.TestApplicationId;
import org.onosproject.core.ApplicationId;
@@ -36,7 +38,10 @@
import org.onosproject.net.group.GroupBucket;
import org.onosproject.net.group.GroupBuckets;
import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.pi.model.DefaultPiPipeconf;
import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.model.PiPipelineModel;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionProfileGroup;
@@ -44,18 +49,24 @@
import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
import org.onosproject.net.pi.runtime.PiGroupKey;
import org.onosproject.net.pi.runtime.PiTableAction;
+import org.onosproject.net.pi.service.PiTranslationException;
+import org.onosproject.p4runtime.model.P4InfoParser;
+import org.onosproject.p4runtime.model.P4InfoParserException;
import org.onosproject.pipelines.basic.PipeconfLoader;
+import java.net.URL;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
+import static java.lang.String.format;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onosproject.net.group.GroupDescription.Type.SELECT;
+import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
import static org.onosproject.net.pi.runtime.PiActionProfileGroup.WeightedMember.DEFAULT_WEIGHT;
import static org.onosproject.pipelines.basic.BasicConstants.INGRESS_WCMP_CONTROL_SET_EGRESS_PORT;
import static org.onosproject.pipelines.basic.BasicConstants.INGRESS_WCMP_CONTROL_WCMP_SELECTOR;
@@ -88,9 +99,15 @@
private PiPipeconf pipeconf;
+ // Derived from basic.p4info, with wcmp_table annotated with @oneshot
+ private static final String PATH_ONESHOT_P4INFO = "oneshot.p4info";
+ private static final PiPipeconfId ONE_SHOT_PIPECONF_ID = new PiPipeconfId("org.onosproject.pipelines.wcmp_oneshot");
+ private PiPipeconf pipeconfOneShot;
+
@Before
public void setUp() throws Exception {
pipeconf = PipeconfLoader.BASIC_PIPECONF;
+ pipeconfOneShot = loadP4InfoPipeconf(ONE_SHOT_PIPECONF_ID, PATH_ONESHOT_P4INFO);
expectedMemberInstances = ImmutableSet.of(outputMember(1),
outputMember(2),
outputMember(3));
@@ -159,4 +176,34 @@
&& expectedMemberInstances.containsAll(memberInstances));
}
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ /**
+ * Test add group with buckets.
+ */
+ @Test
+ public void testTranslateGroupsOneShotError() throws Exception {
+ thrown.expect(PiTranslationException.class);
+ thrown.expectMessage(format("Table associated to action profile '%s' " +
+ "supports only one-shot action profile programming",
+ INGRESS_WCMP_CONTROL_WCMP_SELECTOR.id()));
+ PiGroupTranslatorImpl.translate(SELECT_GROUP, pipeconfOneShot, null);
+ }
+
+ private static PiPipeconf loadP4InfoPipeconf(PiPipeconfId pipeconfId, String p4infoPath) {
+ final URL p4InfoUrl = PiGroupTranslatorImpl.class.getResource(p4infoPath);
+ final PiPipelineModel pipelineModel;
+ try {
+ pipelineModel = P4InfoParser.parse(p4InfoUrl);
+ } catch (P4InfoParserException e) {
+ throw new IllegalStateException(e);
+ }
+ return DefaultPiPipeconf.builder()
+ .withId(pipeconfId)
+ .withPipelineModel(pipelineModel)
+ .addExtension(P4_INFO_TEXT, p4InfoUrl)
+ .build();
+ }
}
diff --git a/core/net/src/test/resources/org/onosproject/net/pi/impl/oneshot.p4info b/core/net/src/test/resources/org/onosproject/net/pi/impl/oneshot.p4info
new file mode 100644
index 0000000..334398b
--- /dev/null
+++ b/core/net/src/test/resources/org/onosproject/net/pi/impl/oneshot.p4info
@@ -0,0 +1,94 @@
+pkg_info {
+ arch: "v1model"
+}
+tables {
+ preamble {
+ id: 33594717
+ name: "ingress.wcmp_control.wcmp_table"
+ alias: "wcmp_table"
+ annotations: "@oneshot"
+ }
+ match_fields {
+ id: 1
+ name: "local_metadata.next_hop_id"
+ bitwidth: 16
+ match_type: EXACT
+ }
+ action_refs {
+ id: 16796092
+ }
+ action_refs {
+ id: 16800567
+ annotations: "@defaultonly"
+ scope: DEFAULT_ONLY
+ }
+ implementation_id: 285253634
+ size: 1024
+}
+actions {
+ preamble {
+ id: 16800567
+ name: "NoAction"
+ alias: "NoAction"
+ }
+}
+actions {
+ preamble {
+ id: 16796092
+ name: "ingress.wcmp_control.set_egress_port"
+ alias: "wcmp_control.set_egress_port"
+ }
+ params {
+ id: 1
+ name: "port"
+ bitwidth: 9
+ }
+}
+action_profiles {
+ preamble {
+ id: 285253634
+ name: "ingress.wcmp_control.wcmp_selector"
+ alias: "wcmp_selector"
+ }
+ table_ids: 33594717
+ with_selector: true
+ size: 64
+}
+controller_packet_metadata {
+ preamble {
+ id: 67146229
+ name: "packet_in"
+ alias: "packet_in"
+ annotations: "@controller_header(\"packet_in\")"
+ }
+ metadata {
+ id: 1
+ name: "ingress_port"
+ bitwidth: 9
+ }
+ metadata {
+ id: 2
+ name: "_padding"
+ bitwidth: 7
+ }
+}
+controller_packet_metadata {
+ preamble {
+ id: 67121543
+ name: "packet_out"
+ alias: "packet_out"
+ annotations: "@controller_header(\"packet_out\")"
+ }
+ metadata {
+ id: 1
+ name: "egress_port"
+ bitwidth: 9
+ }
+ metadata {
+ id: 2
+ name: "_padding"
+ bitwidth: 7
+ }
+}
+type_info {
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeActionGroupProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeActionGroupProgrammable.java
index e0805a6..d1d6902 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeActionGroupProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeActionGroupProgrammable.java
@@ -131,8 +131,17 @@
// Dump groups and members from device for all action profiles.
final P4RuntimeReadClient.ReadRequest request = client.read(
p4DeviceId, pipeconf);
- pipeconf.pipelineModel().actionProfiles()
- .stream().map(PiActionProfileModel::id)
+
+ pipeconf.pipelineModel().actionProfiles().stream()
+ // Do not issue groups and members reads for one-shot tables.
+ // Those tables won't use separate groups and members, but the
+ // action profile elements are embedded in the table entry via
+ // action sets and weighted actions.
+ .filter(piActionProfileModel -> piActionProfileModel.tables().stream()
+ .map(tableId -> pipeconf.pipelineModel().table(tableId))
+ .allMatch(piTableModel -> piTableModel.isPresent() &&
+ !piTableModel.get().oneShotOnly()))
+ .map(PiActionProfileModel::id)
.forEach(id -> request.actionProfileGroups(id)
.actionProfileMembers(id));
final P4RuntimeReadClient.ReadResponse response = request.submitSync();
diff --git a/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/upf/MockTableModel.java b/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/upf/MockTableModel.java
index 6584c18..b8b9d26 100644
--- a/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/upf/MockTableModel.java
+++ b/pipelines/fabric/impl/src/test/java/org/onosproject/pipelines/fabric/impl/behaviour/upf/MockTableModel.java
@@ -95,6 +95,11 @@
}
@Override
+ public boolean oneShotOnly() {
+ return false;
+ }
+
+ @Override
public Optional<PiActionModel> action(PiActionId actionId) {
return Optional.empty();
}
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoAnnotationUtils.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoAnnotationUtils.java
new file mode 100644
index 0000000..3540819
--- /dev/null
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoAnnotationUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.model;
+
+import p4.config.v1.P4InfoOuterClass;
+
+/**
+ * Provides utility methods for P4Info annotations.
+ */
+public final class P4InfoAnnotationUtils {
+
+ public static final String ONE_SHOT_ONLY_ANNOTATION = "oneshot";
+ public static final String MAX_GROUP_SIZE_ANNOTATION = "max_group_size";
+
+ private P4InfoAnnotationUtils() {
+ }
+
+ /**
+ * Gets the annotation value if available in the given P4Info preamble.
+ * Supports annotation in the form @my_annotation(value).
+ *
+ * @param name Annotation name
+ * @param preamble preamble of the P4Info object
+ * @return The annotation value if present, null otherwise
+ */
+ public static String getAnnotationValue(String name, P4InfoOuterClass.Preamble preamble) {
+ return preamble.getAnnotationsList().stream()
+ .filter(a -> a.startsWith("@" + name))
+ // e.g. @my_annotation(value)
+ .map(a -> a.substring(name.length() + 2, a.length() - 1))
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * Checks if the given annotation name is present in the given P4Info preamble.
+ * Supports annotation in the form @my_annotation* (i.e., @oneshot, @max_group_size(10)).
+ *
+ * @param name Annotation name
+ * @param preamble preamble of the P4Info object
+ * @return True if the annotation is available, False otherwise.
+ */
+ public static boolean isAnnotationPresent(String name, P4InfoOuterClass.Preamble preamble) {
+ return preamble.getAnnotationsList().stream()
+ .anyMatch(a -> a.startsWith("@" + name));
+ }
+}
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java
index 0bb0360..cbe1d6e 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4InfoParser.java
@@ -75,6 +75,10 @@
import java.util.stream.Collectors;
import static java.lang.String.format;
+import static org.onosproject.p4runtime.model.P4InfoAnnotationUtils.MAX_GROUP_SIZE_ANNOTATION;
+import static org.onosproject.p4runtime.model.P4InfoAnnotationUtils.ONE_SHOT_ONLY_ANNOTATION;
+import static org.onosproject.p4runtime.model.P4InfoAnnotationUtils.getAnnotationValue;
+import static org.onosproject.p4runtime.model.P4InfoAnnotationUtils.isAnnotationPresent;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -87,6 +91,7 @@
private static final String PACKET_IN = "packet_in";
private static final String PACKET_OUT = "packet_out";
+
private static final Map<CounterSpec.Unit, PiCounterModel.Unit> COUNTER_UNIT_MAP =
new ImmutableMap.Builder<CounterSpec.Unit, PiCounterModel.Unit>()
.put(CounterSpec.Unit.BYTES, PiCounterModel.Unit.BYTES)
@@ -224,6 +229,8 @@
// Filter out missed mapping.
.filter(Objects::nonNull)
.forEach(counterModel -> tableCounterMapBuilder.put(counterModel.id(), counterModel));
+ // Check if table supports one-shot only
+ boolean oneShotOnly = isAnnotationPresent(ONE_SHOT_ONLY_ANNOTATION, tableMsg.getPreamble());
tableImmMapBuilder.put(
tableId,
new P4TableModel(
@@ -238,8 +245,7 @@
tableFieldMapBuilder.build(),
tableActionMapBuilder.build(),
actionMap.get(tableMsg.getConstDefaultActionId()),
- tableMsg.getIsConstTable()));
-
+ tableMsg.getIsConstTable(), oneShotOnly));
}
// Get a map with proper PI IDs for some of those maps we created at the beginning.
@@ -355,8 +361,8 @@
// correctly interpret P4Runtime-defined max_group_size annotation:
// https://s3-us-west-2.amazonaws.com/p4runtime/docs/master/
// P4Runtime-Spec.html#sec-p4info-action-profile
- final String maxSizeAnnString = findAnnotation(
- "max_group_size", actProfileMsg.getPreamble());
+ final String maxSizeAnnString = getAnnotationValue(
+ MAX_GROUP_SIZE_ANNOTATION, actProfileMsg.getPreamble());
final int maxSizeAnn = maxSizeAnnString != null
? Integer.valueOf(maxSizeAnnString) : 0;
final int maxGroupSize;
@@ -481,15 +487,6 @@
return MATCH_TYPE_MAP.get(type);
}
- private static String findAnnotation(String name, P4InfoOuterClass.Preamble preamble) {
- return preamble.getAnnotationsList().stream()
- .filter(a -> a.startsWith("@" + name))
- // e.g. @my_annotaion(value)
- .map(a -> a.substring(name.length() + 2, a.length() - 1))
- .findFirst()
- .orElse(null);
- }
-
private static boolean isFieldString(P4Info p4info, String fieldTypeName) {
P4Types.P4TypeInfo p4TypeInfo = p4info.getTypeInfo();
return p4TypeInfo.containsNewTypes(fieldTypeName) &&
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4TableModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4TableModel.java
index be1b71c..ad32dab 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4TableModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4TableModel.java
@@ -52,6 +52,7 @@
private final ImmutableMap<PiActionId, PiActionModel> actions;
private final PiActionModel constDefaultAction;
private final boolean isConstTable;
+ private final boolean oneShotOnly;
P4TableModel(PiTableId id, PiTableType tableType,
PiActionProfileModel actionProfile, long maxSize,
@@ -60,7 +61,7 @@
ImmutableMap<PiMatchFieldId, PiMatchFieldModel> matchFields,
ImmutableMap<PiActionId, PiActionModel> actions,
PiActionModel constDefaultAction,
- boolean isConstTable) {
+ boolean isConstTable, boolean oneShotOnly) {
this.id = id;
this.tableType = tableType;
this.actionProfile = actionProfile;
@@ -72,6 +73,7 @@
this.actions = actions;
this.constDefaultAction = constDefaultAction;
this.isConstTable = isConstTable;
+ this.oneShotOnly = oneShotOnly;
}
@Override
@@ -129,6 +131,10 @@
return isConstTable;
}
+ public boolean oneShotOnly() {
+ return oneShotOnly;
+ }
+
@Override
public Optional<PiActionModel> action(PiActionId actionId) {
return Optional.ofNullable(actions.get(actionId));
@@ -164,7 +170,8 @@
&& Objects.equals(this.supportAging, other.supportAging)
&& Objects.equals(this.matchFields, other.matchFields)
&& Objects.equals(this.actions, other.actions)
- && Objects.equals(this.constDefaultAction, other.constDefaultAction);
+ && Objects.equals(this.constDefaultAction, other.constDefaultAction)
+ && Objects.equals(this.oneShotOnly, other.oneShotOnly);
}
@Override
diff --git a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4InfoParserTest.java b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4InfoParserTest.java
index 558acb1..3785c96 100644
--- a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4InfoParserTest.java
+++ b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4InfoParserTest.java
@@ -83,10 +83,8 @@
// Generate two PiPipelineModels from p4Info file
PiPipelineModel model = P4InfoParser.parse(p4InfoUrl);
PiPipelineModel model2 = P4InfoParser.parse(p4InfoUrl);
-
// Check equality
new EqualsTester().addEqualityGroup(model, model2).testEquals();
-
// Generate a P4Info object from the file
final P4Info p4info;
try {
@@ -94,30 +92,33 @@
} catch (IOException e) {
throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
}
-
List<Table> tableMsgs = p4info.getTablesList();
PiTableId table0Id = PiTableId.of(tableMsgs.get(0).getPreamble().getName());
PiTableId wcmpTableId = PiTableId.of(tableMsgs.get(1).getPreamble().getName());
-
+ PiTableId wcmpTableOneShotId = PiTableId.of(tableMsgs.get(2).getPreamble().getName());
//parse tables
PiTableModel table0Model = model.table(table0Id).orElse(null);
PiTableModel wcmpTableModel = model.table(wcmpTableId).orElse(null);
+ PiTableModel wcmpTableOneShotModel = model.table(wcmpTableOneShotId).orElse(null);
PiTableModel table0Model2 = model2.table(table0Id).orElse(null);
PiTableModel wcmpTableModel2 = model2.table(wcmpTableId).orElse(null);
-
new EqualsTester().addEqualityGroup(table0Model, table0Model2)
.addEqualityGroup(wcmpTableModel, wcmpTableModel2).testEquals();
-
// Check existence
assertThat("model parsed value is null", table0Model, notNullValue());
assertThat("model parsed value is null", wcmpTableModel, notNullValue());
+ assertThat("model parsed value is null", wcmpTableOneShotModel, notNullValue());
assertThat("Incorrect size for table0 size", table0Model.maxSize(), is(equalTo(DEFAULT_MAX_TABLE_SIZE)));
assertThat("Incorrect size for wcmp_table size", wcmpTableModel.maxSize(), is(equalTo(DEFAULT_MAX_TABLE_SIZE)));
+ assertThat("Incorrect size for wcmp_table_one_shot size", wcmpTableOneShotModel.maxSize(),
+ is(equalTo(DEFAULT_MAX_TABLE_SIZE)));
+ // Check one-shot annotation
+ assertThat("error parsing one-shot annotation", wcmpTableModel.oneShotOnly(), is(false));
+ assertThat("error parsing one-shot annotation", wcmpTableOneShotModel.oneShotOnly(), is(true));
// Check matchFields
List<MatchField> matchFieldList = tableMsgs.get(0).getMatchFieldsList();
List<PiMatchFieldModel> piMatchFieldList = new ArrayList<>();
-
for (MatchField matchFieldIter : matchFieldList) {
MatchField.MatchType matchType = matchFieldIter.getMatchType();
PiMatchType piMatchType;
@@ -140,7 +141,6 @@
piMatchFieldList.get(4), piMatchFieldList.get(5),
piMatchFieldList.get(6), piMatchFieldList.get(7),
piMatchFieldList.get(8)));
-
assertThat("Incorrect size for matchFields", wcmpTableModel.matchFields().size(), is(equalTo(1)));
// check if matchFields are in order
@@ -256,8 +256,8 @@
Collection<PiMeterModel> meterModel = model.meters();
Collection<PiMeterModel> meterModel2 = model2.meters();
- assertThat("model pased meter collaction should be empty", meterModel.isEmpty(), is(true));
- assertThat("model pased meter collaction should be empty", meterModel2.isEmpty(), is(true));
+ assertThat("model parsed meter collection should be empty", meterModel.isEmpty(), is(true));
+ assertThat("model parsed meter collection should be empty", meterModel2.isEmpty(), is(true));
//parse packet operations
PiPacketOperationModel packetInOperationalModel =
diff --git a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4PipelineModelTest.java b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4PipelineModelTest.java
index fdc3d2e..3da1ffb 100644
--- a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4PipelineModelTest.java
+++ b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4PipelineModelTest.java
@@ -27,8 +27,6 @@
import org.onosproject.net.pi.model.PiActionParamModel;
import org.onosproject.net.pi.model.PiActionProfileId;
import org.onosproject.net.pi.model.PiActionProfileModel;
-import org.onosproject.net.pi.model.PiPacketMetadataId;
-import org.onosproject.net.pi.model.PiPacketMetadataModel;
import org.onosproject.net.pi.model.PiCounterId;
import org.onosproject.net.pi.model.PiCounterModel;
import org.onosproject.net.pi.model.PiCounterType;
@@ -38,6 +36,8 @@
import org.onosproject.net.pi.model.PiMeterId;
import org.onosproject.net.pi.model.PiMeterModel;
import org.onosproject.net.pi.model.PiMeterType;
+import org.onosproject.net.pi.model.PiPacketMetadataId;
+import org.onosproject.net.pi.model.PiPacketMetadataModel;
import org.onosproject.net.pi.model.PiPacketOperationModel;
import org.onosproject.net.pi.model.PiPacketOperationType;
import org.onosproject.net.pi.model.PiPipelineModel;
@@ -261,6 +261,7 @@
/* Table Models */
private static final PiTableId PI_TABLE_ID_1 = PiTableId.of("Table1");
private static final PiTableId PI_TABLE_ID_2 = PiTableId.of("Table2");
+ private static final PiTableId PI_TABLE_ID_3 = PiTableId.of("Table3");
private static final PiTableType PI_TABLE_TYPE_1 = PiTableType.DIRECT;
private static final PiTableType PI_TABLE_TYPE_2 = PiTableType.INDIRECT;
@@ -277,11 +278,15 @@
private static final PiTableModel P4_TABLE_MODEL_1 =
new P4TableModel(PI_TABLE_ID_1, PI_TABLE_TYPE_1, P4_ACTION_PROFILE_MODEL_1, MAX_SIZE_1, COUNTERS_1,
METERS_1, SUPPORT_AGING_1, MATCH_FIELDS_1, ACTIONS_1, P4_ACTION_MODEL_DEFAULT_1,
- IS_CONST_TABLE_1);
+ IS_CONST_TABLE_1, false);
private static final PiTableModel P4_TABLE_MODEL_2 =
new P4TableModel(PI_TABLE_ID_2, PI_TABLE_TYPE_2, P4_ACTION_PROFILE_MODEL_2, MAX_SIZE_2, COUNTERS_2,
METERS_2, SUPPORT_AGING_2, MATCH_FIELDS_2, ACTIONS_2, P4_ACTION_MODEL_DEFAULT_2,
- IS_CONST_TABLE_2);
+ IS_CONST_TABLE_2, false);
+ private static final PiTableModel P4_TABLE_MODEL_3 =
+ new P4TableModel(PI_TABLE_ID_2, PI_TABLE_TYPE_2, P4_ACTION_PROFILE_MODEL_2, MAX_SIZE_2, COUNTERS_2,
+ METERS_2, SUPPORT_AGING_2, MATCH_FIELDS_2, ACTIONS_2, P4_ACTION_MODEL_DEFAULT_2,
+ IS_CONST_TABLE_2, true);
/* Packet operations */
private static final PiPacketOperationType PI_PACKET_OPERATION_TYPE_1 = PiPacketOperationType.PACKET_IN;
@@ -309,6 +314,10 @@
new ImmutableMap.Builder<PiTableId, PiTableModel>()
.put(PI_TABLE_ID_2, P4_TABLE_MODEL_2)
.build();
+ private static final ImmutableMap<PiTableId, PiTableModel> TABLES_3 =
+ new ImmutableMap.Builder<PiTableId, PiTableModel>()
+ .put(PI_TABLE_ID_3, P4_TABLE_MODEL_3)
+ .build();
private static final ImmutableMap<PiActionProfileId, PiActionProfileModel> ACTION_PROFILES_1 =
new ImmutableMap.Builder<PiActionProfileId, PiActionProfileModel>()
@@ -357,16 +366,19 @@
private static final PiPipelineModel P4_PIPELINE_MODEL_1 =
new P4PipelineModel(TABLES_1, COUNTERS_1, METERS_1, REGISTERS_1, ACTION_PROFILES_1, PACKET_OPERATIONS_1,
- FINGER_PRINT_1);
+ FINGER_PRINT_1);
private static final PiPipelineModel SAME_AS_P4_PIPELINE_MODEL_1 =
new P4PipelineModel(TABLES_1, COUNTERS_1, METERS_1, REGISTERS_1, ACTION_PROFILES_1, PACKET_OPERATIONS_1,
- FINGER_PRINT_1);
+ FINGER_PRINT_1);
private static final PiPipelineModel P4_PIPELINE_MODEL_2 =
new P4PipelineModel(TABLES_2, COUNTERS_2, METERS_2, REGISTERS_1, ACTION_PROFILES_2, PACKET_OPERATIONS_2,
- FINGER_PRINT_2);
+ FINGER_PRINT_2);
private static final PiPipelineModel P4_PIPELINE_MODEL_3 =
new P4PipelineModel(TABLES_2, COUNTERS_2, METERS_2, REGISTERS_1, ACTION_PROFILES_2, PACKET_OPERATIONS_3,
FINGER_PRINT_2);
+ private static final PiPipelineModel P4_PIPELINE_MODEL_4 =
+ new P4PipelineModel(TABLES_3, COUNTERS_2, METERS_2, REGISTERS_1, ACTION_PROFILES_2, PACKET_OPERATIONS_3,
+ FINGER_PRINT_2);
/**
* Checks that the P4PipelineModel class is immutable.
@@ -385,6 +397,7 @@
.addEqualityGroup(P4_PIPELINE_MODEL_1, SAME_AS_P4_PIPELINE_MODEL_1)
.addEqualityGroup(P4_PIPELINE_MODEL_2)
.addEqualityGroup(P4_PIPELINE_MODEL_3)
+ .addEqualityGroup(P4_PIPELINE_MODEL_4)
.testEquals();
}
}
diff --git a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4TableModelTest.java b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4TableModelTest.java
index 13677c0..d28d984 100644
--- a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4TableModelTest.java
+++ b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4TableModelTest.java
@@ -250,15 +250,19 @@
private static final PiTableModel P4_TABLE_MODEL_1 =
new P4TableModel(PI_TABLE_ID_1, PI_TABLE_TYPE_1, P4_ACTION_PROFILE_MODEL_1, MAX_SIZE_1, COUNTERS_1,
METERS_1, SUPPORT_AGING_1, MATCH_FIELDS_1, ACTIONS_1, P4_ACTION_MODEL_DEFAULT_1,
- IS_CONST_TABLE_1);
+ IS_CONST_TABLE_1, false);
private static final PiTableModel SAME_AS_P4_TABLE_MODEL_1 =
new P4TableModel(PI_TABLE_ID_1, PI_TABLE_TYPE_1, P4_ACTION_PROFILE_MODEL_1, MAX_SIZE_1, COUNTERS_1,
METERS_1, SUPPORT_AGING_1, MATCH_FIELDS_1, ACTIONS_1, P4_ACTION_MODEL_DEFAULT_1,
- IS_CONST_TABLE_1);
+ IS_CONST_TABLE_1, false);
private static final PiTableModel P4_TABLE_MODEL_2 =
new P4TableModel(PI_TABLE_ID_2, PI_TABLE_TYPE_2, P4_ACTION_PROFILE_MODEL_2, MAX_SIZE_2, COUNTERS_2,
METERS_2, SUPPORT_AGING_2, MATCH_FIELDS_2, ACTIONS_2, P4_ACTION_MODEL_DEFAULT_2,
- IS_CONST_TABLE_2);
+ IS_CONST_TABLE_2, false);
+ private static final PiTableModel P4_TABLE_MODEL_3 =
+ new P4TableModel(PI_TABLE_ID_2, PI_TABLE_TYPE_2, P4_ACTION_PROFILE_MODEL_2, MAX_SIZE_2, COUNTERS_2,
+ METERS_2, SUPPORT_AGING_2, MATCH_FIELDS_2, ACTIONS_2, P4_ACTION_MODEL_DEFAULT_2,
+ IS_CONST_TABLE_2, true);
/**
* Checks that the P4TableModel class is immutable.
@@ -276,6 +280,7 @@
new EqualsTester()
.addEqualityGroup(P4_TABLE_MODEL_1, SAME_AS_P4_TABLE_MODEL_1)
.addEqualityGroup(P4_TABLE_MODEL_2)
+ .addEqualityGroup(P4_TABLE_MODEL_3)
.testEquals();
}
}
diff --git a/protocols/p4runtime/model/src/test/resources/org/onosproject/p4runtime/model/basic.p4info b/protocols/p4runtime/model/src/test/resources/org/onosproject/p4runtime/model/basic.p4info
index bc6ca71..f886e14 100644
--- a/protocols/p4runtime/model/src/test/resources/org/onosproject/p4runtime/model/basic.p4info
+++ b/protocols/p4runtime/model/src/test/resources/org/onosproject/p4runtime/model/basic.p4info
@@ -97,6 +97,30 @@
direct_resource_ids: 302001091
size: 1024
}
+tables {
+ preamble {
+ id: 33592598
+ name: "wcmp_control.wcmp_table_oneshot"
+ alias: "wcmp_table_oneshot"
+ annotations: "@oneshot"
+ }
+ match_fields {
+ id: 1
+ name: "local_metadata.next_hop_id"
+ bitwidth: 16
+ match_type: EXACT
+ }
+ action_refs {
+ id: 16794308
+ }
+ action_refs {
+ id: 16800567
+ annotations: "@defaultonly()"
+ }
+ implementation_id: 285259294
+ direct_resource_ids: 302001091
+ size: 1024
+}
actions {
preamble {
id: 16794308
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionSetCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionSetCodec.java
new file mode 100644
index 0000000..68ded36
--- /dev/null
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/ActionSetCodec.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020-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.codec;
+
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiActionSet;
+import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
+import p4.v1.P4RuntimeOuterClass;
+
+/**
+ * Codec for ActionSet.
+ */
+public class ActionSetCodec extends
+ AbstractCodec<PiActionSet, P4RuntimeOuterClass.ActionProfileActionSet, Object> {
+
+ @Override
+ protected P4RuntimeOuterClass.ActionProfileActionSet encode(
+ PiActionSet piActionSet, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ final var actProActSetBuilder =
+ P4RuntimeOuterClass.ActionProfileActionSet.newBuilder();
+ for (PiActionSet.WeightedAction act : piActionSet.actions()) {
+ // TODO: currently we don't set "watch_port" field
+ final var actProfAct =
+ P4RuntimeOuterClass.ActionProfileAction.newBuilder();
+ actProfAct.setAction(Codecs.CODECS.action().encode(
+ act.action(), null, pipeconf));
+ actProfAct.setWeight(act.weight());
+ actProActSetBuilder.addActionProfileActions(actProfAct.build());
+ }
+ return actProActSetBuilder.build();
+ }
+
+ @Override
+ protected PiActionSet decode(
+ P4RuntimeOuterClass.ActionProfileActionSet message, Object ignored,
+ PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ final var builder = PiActionSet.builder();
+ for (P4RuntimeOuterClass.ActionProfileAction act : message.getActionProfileActionsList()) {
+ final var piAction = Codecs.CODECS.action().decode(
+ act.getAction(), null, pipeconf);
+ builder.addWeightedAction(piAction, act.getWeight());
+ }
+ return builder.build();
+ }
+}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Codecs.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Codecs.java
index 30e8d9d..06a2d18 100644
--- a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Codecs.java
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/Codecs.java
@@ -40,6 +40,7 @@
private final PacketMetadataCodec packetMetadata;
private final PacketOutCodec packetOut;
private final TableEntryCodec tableEntry;
+ private final ActionSetCodec actionSet;
private Codecs() {
this.action = new ActionCodec();
@@ -59,6 +60,7 @@
this.packetMetadata = new PacketMetadataCodec();
this.packetOut = new PacketOutCodec();
this.tableEntry = new TableEntryCodec();
+ this.actionSet = new ActionSetCodec();
}
public EntityCodec entity() {
@@ -128,4 +130,8 @@
DirectCounterEntryCodec directCounterEntry() {
return directCounterEntry;
}
+
+ ActionSetCodec actionSet() {
+ return actionSet;
+ }
}
diff --git a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/TableEntryCodec.java b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/TableEntryCodec.java
index 513b4fa..a2455c9 100644
--- a/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/TableEntryCodec.java
+++ b/protocols/p4runtime/utils/src/main/java/org/onosproject/p4runtime/ctl/codec/TableEntryCodec.java
@@ -21,6 +21,7 @@
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
+import org.onosproject.net.pi.runtime.PiActionSet;
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiMatchKey;
import org.onosproject.net.pi.runtime.PiTableAction;
@@ -179,6 +180,12 @@
tableActionMsgBuilder.setActionProfileMemberId(
((PiActionProfileMemberId) piTableAction).id());
break;
+ case ACTION_SET:
+ P4RuntimeOuterClass.ActionProfileActionSet theActionProfileActionSet =
+ CODECS.actionSet().encode(
+ (PiActionSet) piTableAction, null, pipeconf);
+ tableActionMsgBuilder.setActionProfileActionSet(theActionProfileActionSet);
+ break;
default:
throw new CodecException(
format("Building of table action type %s not implemented",
@@ -202,6 +209,9 @@
case ACTION_PROFILE_MEMBER_ID:
return PiActionProfileMemberId.of(
tableActionMsg.getActionProfileMemberId());
+ case ACTION_PROFILE_ACTION_SET:
+ return CODECS.actionSet().decode(
+ tableActionMsg.getActionProfileActionSet(), null, pipeconf);
default:
throw new CodecException(
format("Decoding of table action type %s not implemented",