Pi classes to support P4Runtime action profiles

+ modified default.p4 with ECMP capabilities (via action profiles)
+ sketched translation logic of ONOS groups (in Bmv2GroupProgrammable)
+ replaced existing instances of default.json/p4info with symlinks to
	p4src build directory (to avoid inconsistencies)

Change-Id: If82f0b8ce296c9b616415d99864d216b77645a87
This commit is contained in:
Carmelo Cascone 2017-07-27 12:07:09 -04:00 committed by Thomas Vachuska
parent f3a1a3897e
commit b2e3dbaef0
17 changed files with 1335 additions and 3059 deletions

View File

@ -8,6 +8,7 @@ COMPILE_DEPS = [
'//protocols/p4runtime/api:onos-protocols-p4runtime-api', '//protocols/p4runtime/api:onos-protocols-p4runtime-api',
'//protocols/p4runtime/ctl:onos-protocols-p4runtime-ctl', '//protocols/p4runtime/ctl:onos-protocols-p4runtime-ctl',
'//protocols/p4runtime/proto:onos-protocols-p4runtime-proto', '//protocols/p4runtime/proto:onos-protocols-p4runtime-proto',
'//drivers/bmv2:onos-drivers-bmv2',
'//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER, '//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER,
'//lib:grpc-stub-' + GRPC_VER, '//lib:grpc-stub-' + GRPC_VER,
'//lib:protobuf-java-' + PROTOBUF_VER, '//lib:protobuf-java-' + PROTOBUF_VER,

View File

@ -18,66 +18,165 @@ package org.onosproject.p4runtime.test;
import io.grpc.ManagedChannelBuilder; import io.grpc.ManagedChannelBuilder;
import io.grpc.netty.NettyChannelBuilder; import io.grpc.netty.NettyChannelBuilder;
import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.model.Bmv2PipelineModelParser; import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
import org.onosproject.drivers.bmv2.Bmv2DefaultInterpreter;
import org.onosproject.grpc.ctl.GrpcControllerImpl; import org.onosproject.grpc.ctl.GrpcControllerImpl;
import org.onosproject.net.DeviceId; import org.onosproject.net.DeviceId;
import org.onosproject.net.pi.model.DefaultPiPipeconf; import org.onosproject.net.pi.model.DefaultPiPipeconf;
import org.onosproject.net.pi.model.PiPipeconf; import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipeconfId; import org.onosproject.net.pi.model.PiPipeconfId;
import org.onosproject.net.pi.model.PiPipelineInterpreter; import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.p4runtime.api.P4RuntimeClient; import org.onosproject.net.pi.runtime.PiPacketMetadata;
import org.onosproject.net.pi.runtime.PiPacketMetadataId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableId;
import org.onosproject.p4runtime.ctl.P4RuntimeClientImpl;
import org.onosproject.p4runtime.ctl.P4RuntimeControllerImpl; import org.onosproject.p4runtime.ctl.P4RuntimeControllerImpl;
import p4.P4RuntimeGrpc;
import p4.P4RuntimeOuterClass;
import java.net.URL; import java.net.URL;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onlab.util.ImmutableByteSequence.fit;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.BMV2_JSON; import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.BMV2_JSON;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT; import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
import static org.onosproject.net.pi.runtime.PiPacketOperation.Type.PACKET_OUT;
import static p4.P4RuntimeOuterClass.ActionProfileGroup.Type.SELECT;
import static p4.P4RuntimeOuterClass.Update.Type.INSERT;
/** /**
* Class used for quick testing of P4Runtime with real devices. To be removed before release. * Class used for quick testing of P4Runtime with real devices. To be removed before release.
*/ */
public class P4RuntimeTest { public class P4RuntimeTest {
private final URL p4InfoUrl = this.getClass().getResource("/default.p4info"); private static final String GRPC_SERVER_ADDR = "192.168.56.102";
private final URL jsonUrl = this.getClass().getResource("/default.json"); private static final int GRPC_SERVER_PORT = 55044;
private final URL p4InfoUrl = this.getClass().getResource("/bmv2/default.p4info");
private final URL jsonUrl = this.getClass().getResource("/bmv2/default.json");
private final PiPipeconf bmv2DefaultPipeconf = DefaultPiPipeconf.builder() private final PiPipeconf bmv2DefaultPipeconf = DefaultPiPipeconf.builder()
.withId(new PiPipeconfId("mock")) .withId(new PiPipeconfId("mock-bmv2"))
.withPipelineModel(Bmv2PipelineModelParser.parse(jsonUrl)) .withPipelineModel(Bmv2PipelineModelParser.parse(jsonUrl))
// .addBehaviour(PiPipelineInterpreter.class, Bmv2DefaultInterpreter.class) .addBehaviour(PiPipelineInterpreter.class, Bmv2DefaultInterpreter.class)
.addExtension(P4_INFO_TEXT, p4InfoUrl) .addExtension(P4_INFO_TEXT, p4InfoUrl)
.addExtension(BMV2_JSON, jsonUrl) .addExtension(BMV2_JSON, jsonUrl)
.build(); .build();
private final P4RuntimeControllerImpl controller = new P4RuntimeControllerImpl();
private final GrpcControllerImpl grpcController = new GrpcControllerImpl();
private final DeviceId deviceId = DeviceId.deviceId("dummy:1");
private final ManagedChannelBuilder channelBuilder = NettyChannelBuilder
.forAddress(GRPC_SERVER_ADDR, GRPC_SERVER_PORT)
.usePlaintext(true);
private P4RuntimeClientImpl client;
@Test @Before
@Ignore public void setUp() throws Exception {
public void testRuntime() throws ExecutionException, InterruptedException,
PiPipelineInterpreter.PiInterpreterException, IllegalAccessException, InstantiationException {
// FIXME: remove me.
P4RuntimeControllerImpl controller = new P4RuntimeControllerImpl();
GrpcControllerImpl grpcController = new GrpcControllerImpl();
controller.grpcController = grpcController; controller.grpcController = grpcController;
GrpcControllerImpl.enableMessageLog = true; GrpcControllerImpl.enableMessageLog = true;
grpcController.activate(); grpcController.activate();
DeviceId deviceId = DeviceId.deviceId("dummy:1"); }
ManagedChannelBuilder channelBuilder = NettyChannelBuilder private void createClientAndSetPipelineConfig(PiPipeconf pipeconf, PiPipeconf.ExtensionType extensionType)
.forAddress("192.168.56.102", 59975) throws ExecutionException, InterruptedException, PiPipelineInterpreter.PiInterpreterException,
.usePlaintext(true); IllegalAccessException, InstantiationException {
assert (controller.createClient(deviceId, 1, channelBuilder)); assert (controller.createClient(deviceId, 1, channelBuilder));
P4RuntimeClient client = controller.getClient(deviceId); client = (P4RuntimeClientImpl) controller.getClient(deviceId);
assert (client.setPipelineConfig(bmv2DefaultPipeconf, PiPipeconf.ExtensionType.BMV2_JSON).get());
assert (client.setPipelineConfig(pipeconf, extensionType).get());
assert (client.initStreamChannel().get()); assert (client.initStreamChannel().get());
}
private void testActionProfile(int actionProfileId) {
P4RuntimeGrpc.P4RuntimeBlockingStub stub = client.blockingStub();
P4RuntimeOuterClass.ActionProfileMember profileMemberMsg = P4RuntimeOuterClass.ActionProfileMember.newBuilder()
.setActionProfileId(actionProfileId)
// .setMemberId(1)
.setAction(P4RuntimeOuterClass.Action.newBuilder()
.setActionId(16793508)
.build())
.build();
P4RuntimeOuterClass.ActionProfileGroup groupMsg = P4RuntimeOuterClass.ActionProfileGroup.newBuilder()
.setActionProfileId(actionProfileId)
.setGroupId(1)
.setType(SELECT)
.addMembers(P4RuntimeOuterClass.ActionProfileGroup.Member.newBuilder()
.setMemberId(1)
.setWeight(1)
.build())
.setMaxSize(3)
.build();
P4RuntimeOuterClass.WriteRequest writeRequest = P4RuntimeOuterClass.WriteRequest.newBuilder()
.setDeviceId(client.p4DeviceId())
.addUpdates(P4RuntimeOuterClass.Update.newBuilder()
.setType(INSERT)
.setEntity(P4RuntimeOuterClass.Entity.newBuilder()
.setActionProfileGroup(groupMsg)
.build())
.build())
.addUpdates(P4RuntimeOuterClass.Update.newBuilder()
.setType(INSERT)
.setEntity(P4RuntimeOuterClass.Entity.newBuilder()
.setActionProfileMember(profileMemberMsg)
.build())
.build())
.build();
stub.write(writeRequest);
}
private void testPacketOut() throws IllegalAccessException, InstantiationException, ExecutionException,
InterruptedException, ImmutableByteSequence.ByteSequenceTrimException {
PiPacketOperation packetOperation = PiPacketOperation.builder()
.withData(ImmutableByteSequence.ofOnes(10))
.withType(PACKET_OUT)
.withMetadata(PiPacketMetadata.builder()
.withId(PiPacketMetadataId.of("egress_port"))
.withValue(fit(copyFrom(1), 9))
.build())
.build();
assert (client.packetOut(packetOperation, bmv2DefaultPipeconf).get());
}
private void testDumpTable(String tableName, PiPipeconf pipeconf) throws ExecutionException, InterruptedException {
assert (client.dumpTable(PiTableId.of(tableName), pipeconf).get().size() == 0);
}
@Test
@Ignore
public void testBmv2() throws Exception {
createClientAndSetPipelineConfig(bmv2DefaultPipeconf, BMV2_JSON);
testDumpTable("table0", bmv2DefaultPipeconf);
// testPacketOut();
testActionProfile(285261835);
}
@Test
@Ignore
public void testTofino() throws Exception {
createClientAndSetPipelineConfig(bmv2DefaultPipeconf, null);
}
// OLD STUFF
// log.info("++++++++++++++++++++++++++++"); // log.info("++++++++++++++++++++++++++++");
// //
// PiPipelineInterpreter interpreter = (PiPipelineInterpreter) defaultPipeconf // PiPipelineInterpreter interpreter = (PiPipelineInterpreter) defaultPipeconf
@ -111,4 +210,3 @@ public class P4RuntimeTest {
// assert(client.dumpTable(PiTableId.of(TABLE_0), defaultPipeconf).get().size() == 1); // assert(client.dumpTable(PiTableId.of(TABLE_0), defaultPipeconf).get().size() == 1);
} }
}

View File

@ -0,0 +1 @@
../../../../../../tools/test/p4src/p4-16/p4c-out/default.json

View File

@ -0,0 +1 @@
../../../../../../tools/test/p4src/p4-16/p4c-out/default.p4info

View File

@ -0,0 +1,190 @@
/*
* Copyright 2017-present Open Networking Laboratory
*
* 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.ImmutableSet;
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.Map;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Action group of a protocol-independent pipeline.
*/
@Beta
public final class PiActionGroup {
/**
* Type of action group.
*/
public enum Type {
/**
* Load-balancing among different members in a group.
*/
SELECT
}
private final PiActionGroupId id;
private final Type type;
private final ImmutableSet<PiActionGroupMember> members;
private PiActionGroup(PiActionGroupId id, Type type, ImmutableSet<PiActionGroupMember> members) {
this.id = id;
this.type = type;
this.members = members;
}
/**
* Returns the identifier of this action group.
*
* @return action group identifier
*/
public PiActionGroupId id() {
return id;
}
/**
* Returns the type of this action group.
*
* @return action group type
*/
public Type type() {
return type;
}
/**
* Returns the members of this action group.
*
* @return collection of action members.
*/
public Collection<PiActionGroupMember> members() {
return members;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PiActionGroup that = (PiActionGroup) o;
return id == that.id &&
Objects.equal(type, that.type) &&
Objects.equal(members, that.members);
}
@Override
public int hashCode() {
return Objects.hashCode(id, type, members);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("groupId", id)
.add("type", type)
.add("members", members)
.toString();
}
/**
* Returns a new builder of action groups.
*
* @return action group builder
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder of action groups.
*/
public static final class Builder {
private PiActionGroupId id;
private Type type;
private Map<PiActionGroupMemberId, PiActionGroupMember> members = Maps.newHashMap();
private Builder() {
// hides constructor.
}
/**
* Sets the identifier of this action group.
*
* @param id action group identifier
* @return this
*/
public Builder withId(PiActionGroupId id) {
this.id = id;
return this;
}
/**
* Sets the type of this action group.
*
* @param type action group type
* @return this
*/
public Builder withType(Type type) {
this.type = type;
return this;
}
/**
* Adds one member to this action group.
*
* @param member action group member
* @return this
*/
public Builder addMember(PiActionGroupMember member) {
members.put(member.id(), member);
return this;
}
/**
* Adds many members to this action group.
*
* @param members action group members
* @return this
*/
public Builder addMembers(Collection<PiActionGroupMember> members) {
members.forEach(this::addMember);
return this;
}
/**
* Creates a new action group.
*
* @return action group
*/
public PiActionGroup build() {
checkNotNull(id);
checkNotNull(type);
checkArgument(members.size() > 0, "Members cannot be empty");
return new PiActionGroup(id, type, ImmutableSet.copyOf(members.values()));
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2017-present Open Networking Laboratory
*
* 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 org.onlab.util.Identifier;
/**
* Identifier of an action group in a protocol-independent pipeline.
*/
@Beta
public final class PiActionGroupId extends Identifier<Integer> implements PiTableAction {
private PiActionGroupId(int id) {
super(id);
}
/**
* Returns an action group identifier for the given integer value.
*
* @param id identifier
* @return action group
*/
public static PiActionGroupId of(int id) {
return new PiActionGroupId(id);
}
/*
In P4Runtime, groups can be referenced directly as table actions (i.e. without invoking the selector).
In future we should consider having a more appropriate wrapper class for group IDs, instead of implementing
the PiTableAction interface.
*/
@Override
public Type type() {
return Type.ACTION_GROUP_ID;
}
}

View File

@ -0,0 +1,165 @@
/*
* Copyright 2017-present Open Networking Laboratory
*
* 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 static com.google.common.base.Preconditions.checkNotNull;
/**
* Member of an action group in a protocol-independent pipeline.
*/
@Beta
public final class PiActionGroupMember {
private final PiActionGroupMemberId id;
private final PiAction action;
private final int weight;
private PiActionGroupMember(PiActionGroupMemberId id, PiAction action, int weight) {
this.id = id;
this.action = action;
this.weight = weight;
}
/**
* Returns the identifier of this member.
*
* @return member identifier
*/
public PiActionGroupMemberId id() {
return id;
}
/**
* Returns the action associated to this member.
*
* @return action
*/
public PiAction action() {
return action;
}
/**
* Returns the weight associated to this member. Valid if the action group is of type {@link
* PiActionGroup.Type#SELECT}.
*
* @return weight
*/
public int weight() {
return weight;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PiActionGroupMember)) {
return false;
}
PiActionGroupMember that = (PiActionGroupMember) o;
return weight == that.weight &&
Objects.equal(id, that.id) &&
Objects.equal(action, that.action);
}
@Override
public int hashCode() {
return Objects.hashCode(id, action, weight);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("action", action)
.add("weight", weight)
.toString();
}
/**
* Returns a new builder of action group members.
*
* @return member builder
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder of action group members.
*/
public static final class Builder {
private PiActionGroupMemberId id;
private PiAction action;
private int weight;
private Builder() {
// Hides constructor.
}
/**
* Sets the identifier of this member.
*
* @param id member identifier
* @return this
*/
public Builder withId(PiActionGroupMemberId id) {
this.id = id;
return this;
}
/**
* Sets the action of this member.
*
* @param action action
* @return this
*/
public Builder withAction(PiAction action) {
this.action = action;
return this;
}
/**
* Sets the weight of this member. Valid if the action group is of type {@link PiActionGroup.Type#SELECT}.
* <p>
* Default value is 0.
*
* @param weight weight
* @return this
*/
public Builder withWeight(int weight) {
this.weight = weight;
return this;
}
/**
* Creates a new action group member.
*
* @return action group member
*/
public PiActionGroupMember build() {
checkNotNull(id);
checkNotNull(action);
return new PiActionGroupMember(id, action, weight);
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2017-present Open Networking Laboratory
*
* 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 org.onlab.util.Identifier;
/**
* Identifier of a member of an action group in a protocol-independent pipeline.
*/
@Beta
public final class PiActionGroupMemberId extends Identifier<Integer> implements PiTableAction {
private PiActionGroupMemberId(int id) {
super(id);
}
/**
* Returns an action group identifier for the given integer value.
*
* @param id identifier
* @return action group
*/
public static PiActionGroupMemberId of(int id) {
return new PiActionGroupMemberId(id);
}
/*
In P4Runtime, group members can be referenced directly as table actions.
In future we should consider having a more appropriate wrapper class for group member IDs, instead of implementing
the PiTableAction interface.
*/
@Override
public Type type() {
return Type.GROUP_MEMBER_ID;
}
}

View File

@ -38,15 +38,14 @@ public interface PiTableAction {
*/ */
ACTION, ACTION,
// TODO: in P4Runtime a table action can be any of the following 3. /**
// How to represent action profiles? * Executes the action group specified by the given identifier.
/* message TableAction {
oneof type {
Action action = 1;
uint32 action_profile_member_id = 2;
uint32 action_profile_group_id = 3;
}
}
*/ */
ACTION_GROUP_ID,
/**
* Executes the action member group specified by the given identifier.
*/
GROUP_MEMBER_ID
} }
} }

View File

@ -0,0 +1,151 @@
/*
* Copyright 2017-present Open Networking Laboratory
*
* 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.bmv2;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.group.GroupBucket;
import org.onosproject.net.group.GroupDescription;
import org.onosproject.net.group.GroupOperation;
import org.onosproject.net.group.GroupOperations;
import org.onosproject.net.group.GroupProgrammable;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiActionGroupId;
import org.onosproject.net.pi.runtime.PiActionGroupMember;
import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
import org.onosproject.net.pi.runtime.PiTableId;
import java.nio.ByteBuffer;
public class Bmv2GroupProgrammable extends AbstractHandlerBehaviour implements GroupProgrammable {
/*
Work in progress.
*/
private Device device;
/*
About action groups in P4runtime:
The type field is a place holder in p4runtime.proto right now, and we haven't defined it yet. You can assume all
the groups are "select" as per the OF spec. As a remainder, in the P4 terminology a member corresponds to an OF
bucket. Each member can also be used directly in the match table (kind of like an OF indirect group).
*/
@Override
public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
for (GroupOperation groupOp : groupOps.operations()) {
switch (groupOp.opType()) {
case ADD:
addGroup(deviceId, groupOp);
break;
default:
throw new UnsupportedOperationException();
}
}
}
private void addGroup(DeviceId deviceId, GroupOperation groupOp) {
// Most of this logic can go in a core service, e.g. PiGroupTranslationService
// From a P4Runtime perspective, we need first to insert members, then the group.
PiActionGroupId piActionGroupId = PiActionGroupId.of(groupOp.groupId().id());
PiActionGroup.Builder piActionGroupBuilder = PiActionGroup.builder()
.withId(piActionGroupId)
.withType(PiActionGroup.Type.SELECT);
if (groupOp.groupType() != GroupDescription.Type.SELECT) {
// log error
}
int bucketIdx = 0;
for (GroupBucket bucket : groupOp.buckets().buckets()) {
/*
Problem:
In P4Runtime action group members, i.e. action buckets, are associated to a numeric ID chosen
at member insertion time. This ID must be unique for the whole action profile (i.e. the group table in
OpenFlow). In ONOS, GroupBucket doesn't specify any ID.
Solutions:
- Change GroupBucket API to force application wanting to perform group operations to specify a member id.
- Maintain state to dynamically allocate/deallocate member IDs, e.g. in a dedicated service, or in a
P4Runtime Group Provider.
Hack:
Statically derive member ID by combining groupId and position of the bucket in the list.
*/
int memberId = ByteBuffer.allocate(4)
.putShort((short) (piActionGroupId.id() % 2 ^ 16))
.putShort((short) (bucketIdx % 2 ^ 16))
.getInt();
// Need an interpreter to map the bucket treatment to a PI action
if (!device.is(PiPipelineInterpreter.class)) {
// log error
}
PiPipelineInterpreter interpreter = device.as(PiPipelineInterpreter.class);
/*
Problem:
In P4Runtime, action profiles (i.e. group tables) are specific to one or more tables.
Mapping of treatments depends on the target table. How do we derive the target table from here?
Solution:
- Change GroupDescription to allow applications to specify a table where this group will be called from.
Hack:
Assume we support pipelines with only one action profile associated to only one table, i.e. derive the
table ID by looking at the P4Info.
*/
PiTableId piTableId = PiTableId.of("derive from P4Info");
PiAction action = null;
try {
action = interpreter.mapTreatment(bucket.treatment(), piTableId);
} catch (PiPipelineInterpreter.PiInterpreterException e) {
// log error
}
PiActionGroupMember member = PiActionGroupMember.builder()
.withId(PiActionGroupMemberId.of(memberId))
.withAction(action)
.withWeight(bucket.weight())
.build();
piActionGroupBuilder.addMember(member);
// Use P4RuntimeClient to install member;
// TODO: implement P4RuntimeClient method.
}
PiActionGroup piActionGroup = piActionGroupBuilder.build();
// Use P4RuntimeClient to insert group.
// TODO: implement P4RuntimeClient method.
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
../../../../../tools/test/p4src/p4-16/p4c-out/default.json

View File

@ -1,113 +0,0 @@
tables {
preamble {
id: 33617813
name: "table0"
alias: "table0"
}
match_fields {
id: 1
name: "standard_metadata.ingress_port"
bitwidth: 9
match_type: TERNARY
}
match_fields {
id: 2
name: "hdr.ethernet.dstAddr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 3
name: "hdr.ethernet.srcAddr"
bitwidth: 48
match_type: TERNARY
}
match_fields {
id: 4
name: "hdr.ethernet.etherType"
bitwidth: 16
match_type: TERNARY
}
action_refs {
id: 16794308
}
action_refs {
id: 16829080
}
action_refs {
id: 16793508
}
action_refs {
id: 16800567
annotations: "@defaultonly()"
}
direct_resource_ids: 301990488
size: 1024
with_entry_timeout: true
}
actions {
preamble {
id: 16794308
name: "set_egress_port"
alias: "set_egress_port"
}
params {
id: 1
name: "port"
bitwidth: 9
}
}
actions {
preamble {
id: 16829080
name: "send_to_cpu"
alias: "send_to_cpu"
}
}
actions {
preamble {
id: 16793508
name: "drop"
alias: "drop"
}
}
actions {
preamble {
id: 16800567
name: "NoAction"
alias: "NoAction"
}
}
counters {
preamble {
id: 302025528
name: "port_counters_control.egress_port_counter"
alias: "egress_port_counter"
}
spec {
unit: PACKETS
}
size: 254
}
counters {
preamble {
id: 301999025
name: "port_counters_control.ingress_port_counter"
alias: "ingress_port_counter"
}
spec {
unit: PACKETS
}
size: 254
}
direct_counters {
preamble {
id: 301990488
name: "table0_counter"
alias: "table0_counter"
}
spec {
unit: PACKETS
}
direct_table_id: 33617813
}

View File

@ -0,0 +1 @@
../../../../../tools/test/p4src/p4-16/p4c-out/default.p4info

View File

@ -349,6 +349,24 @@ public final class P4RuntimeClientImpl implements P4RuntimeClient {
return true; return true;
} }
/**
* Returns the internal P4 device ID associated with this client.
*
* @return P4 device ID
*/
public int p4DeviceId() {
return p4DeviceId;
}
/**
* For testing purpose only. TODO: remove before release.
*
* @return blocking stub
*/
public P4RuntimeGrpc.P4RuntimeBlockingStub blockingStub() {
return this.blockingStub;
}
@Override @Override
public void shutdown() { public void shutdown() {

View File

@ -11,21 +11,32 @@
control ingress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) { control ingress(inout headers_t hdr, inout metadata_t meta, inout standard_metadata_t standard_metadata) {
direct_counter(CounterType.packets) table0_counter; direct_counter(CounterType.packets) table0_counter;
action_selector(HashAlgorithm.crc16, 32w64, 32w16) ecmp_selector;
table table0 { table table0 {
support_timeout = true; support_timeout = true;
actions = {
set_egress_port(standard_metadata);
send_to_cpu(standard_metadata);
drop(standard_metadata);
}
key = { key = {
standard_metadata.ingress_port : ternary; standard_metadata.ingress_port : ternary;
hdr.ethernet.dstAddr : ternary; hdr.ethernet.dstAddr : ternary;
hdr.ethernet.srcAddr : ternary; hdr.ethernet.srcAddr : ternary;
hdr.ethernet.etherType : ternary; hdr.ethernet.etherType : ternary;
// Not for matching.
// Inputs to the hash function of the action selector.
hdr.ipv4.srcAddr : selector;
hdr.ipv4.dstAddr : selector;
hdr.ipv4.protocol : selector;
hdr.tcp.srcPort : selector;
hdr.tcp.dstPort : selector;
hdr.udp.srcPort : selector;
hdr.udp.dstPort : selector;
}
actions = {
set_egress_port(standard_metadata);
send_to_cpu(standard_metadata);
drop(standard_metadata);
} }
counters = table0_counter; counters = table0_counter;
implementation = ecmp_selector;
} }
PacketIoIngressControl() packet_io_ingress_control; PacketIoIngressControl() packet_io_ingress_control;

View File

@ -1,2 +0,0 @@
*.json
*.p4info

View File

@ -41,6 +41,7 @@ tables {
id: 16800567 id: 16800567
annotations: "@defaultonly()" annotations: "@defaultonly()"
} }
implementation_id: 285227860
direct_resource_ids: 301990488 direct_resource_ids: 301990488
size: 1024 size: 1024
with_entry_timeout: true with_entry_timeout: true
@ -78,6 +79,16 @@ actions {
alias: "NoAction" alias: "NoAction"
} }
} }
action_profiles {
preamble {
id: 285227860
name: "ecmp_selector"
alias: "ecmp_selector"
}
table_ids: 33617813
with_selector: true
size: 64
}
counters { counters {
preamble { preamble {
id: 302025528 id: 302025528
@ -122,11 +133,6 @@ controller_packet_metadata {
name: "ingress_port" name: "ingress_port"
bitwidth: 9 bitwidth: 9
} }
metadata {
id: 2
name: "other1"
bitwidth: 32
}
} }
controller_packet_metadata { controller_packet_metadata {
preamble { preamble {
@ -139,9 +145,4 @@ controller_packet_metadata {
name: "egress_port" name: "egress_port"
bitwidth: 9 bitwidth: 9
} }
metadata {
id: 2
name: "other2"
bitwidth: 32
}
} }