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/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 {
+}