ONOS-7898 Action profile group/member refactoring
Also includes:
- New abstract P4Runtime codec implementation. Currently used for action
profile members/groups encoding/deconding, the plan is to handle all
other codecs via this.
- Improved read requests in P4RuntimeClientImpl
- Removed handling of max group size in P4Runtime driver. Instead, added
modified group translator to specify a max group size by using
information from the pipeline model.
Change-Id: I684bae0184d683bb448ba19863c561f9848479d2
(cherry picked from commit 99c59dbb0351f5389b822bfe2628faf0ff5ab22e)
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiActionProfileModel.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiActionProfileModel.java
index 66b8c53..0a66ee9 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/PiActionProfileModel.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiActionProfileModel.java
@@ -41,16 +41,28 @@
Collection<PiTableId> tables();
/**
- * Returns true if this action profile implements dynamic selection, false otherwise.
+ * Returns true if this action profile implements dynamic selection, false
+ * otherwise.
*
- * @return true if action profile implements dynamic selection, false otherwise
+ * @return true if action profile implements dynamic selection, false
+ * otherwise
*/
boolean hasSelector();
/**
- * Returns the maximum number of member entries of this action profile.
+ * Returns the maximum number of member entries that this action profile can
+ * hold.
*
* @return maximum number of member entries
*/
- long maxSize();
+ long size();
+
+ /**
+ * Returns the maximum number of members a group of this action profile can
+ * hold. This method is meaningful only if the action profile implements
+ * dynamic selection. 0 signifies that an explicit limit is not set.
+ *
+ * @return maximum number of members a group can hold
+ */
+ int maxGroupSize();
}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java
index 24655b6..78a62fd 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java
@@ -30,21 +30,24 @@
import static com.google.common.base.Preconditions.checkNotNull;
/**
- * Instance of an action, and its runtime parameters, of a table entry in a protocol-independent pipeline.
+ * Instance of an action, and its runtime parameters, of a table entry in a
+ * protocol-independent pipeline.
*/
@Beta
public final class PiAction implements PiTableAction {
private final PiActionId actionId;
- private final Map<PiActionParamId, PiActionParam> runtimeParams;
+ private final ImmutableMap<PiActionParamId, PiActionParam> runtimeParams;
/**
- * Creates a new action instance for the given action identifier and runtime parameters.
+ * Creates a new action instance for the given action identifier and runtime
+ * parameters.
*
* @param actionId action identifier
* @param runtimeParams list of runtime parameters
*/
- private PiAction(PiActionId actionId, Map<PiActionParamId, PiActionParam> runtimeParams) {
+ private PiAction(PiActionId actionId,
+ Map<PiActionParamId, PiActionParam> runtimeParams) {
this.actionId = actionId;
this.runtimeParams = ImmutableMap.copyOf(runtimeParams);
}
@@ -64,8 +67,8 @@
}
/**
- * Returns all runtime parameters of this action. Return an empty collection if the action doesn't take any runtime
- * parameters.
+ * Returns all runtime parameters of this action. Return an empty collection
+ * if the action doesn't take any runtime parameters.
*
* @return list of byte sequences
*/
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileGroup.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileGroup.java
index 96168b9..f86e70c 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileGroup.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileGroup.java
@@ -19,13 +19,15 @@
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.ImmutableMap;
import com.google.common.collect.Maps;
import org.onosproject.net.pi.model.PiActionProfileId;
import java.util.Collection;
import java.util.Map;
+import java.util.Optional;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
@@ -34,70 +36,96 @@
@Beta
public final class PiActionProfileGroup implements PiEntity {
- private final PiActionProfileGroupId id;
- private final ImmutableSet<PiActionProfileMember> members;
private final PiActionProfileId actionProfileId;
+ private final PiActionProfileGroupId groupId;
+ private final ImmutableMap<PiActionProfileMemberId, WeightedMember> members;
+ private final int maxSize;
- private PiActionProfileGroup(PiActionProfileGroupId id,
- ImmutableSet<PiActionProfileMember> members,
- PiActionProfileId actionProfileId) {
- this.id = id;
+ private PiActionProfileGroup(PiActionProfileGroupId groupId,
+ ImmutableMap<PiActionProfileMemberId, WeightedMember> members,
+ PiActionProfileId actionProfileId,
+ int maxSize) {
+ this.groupId = groupId;
this.members = members;
this.actionProfileId = actionProfileId;
+ this.maxSize = maxSize;
}
/**
- * Returns the identifier of this action profile group.
+ * Returns the ID of this action profile group.
*
- * @return action profile group identifier
+ * @return action profile group ID
*/
public PiActionProfileGroupId id() {
- return id;
+ return groupId;
}
/**
- * Returns the members of this action profile group.
+ * Returns the list of member references of this action profile group.
*
* @return collection of action profile members.
*/
- public Collection<PiActionProfileMember> members() {
- return members;
+ public Collection<WeightedMember> members() {
+ return members.values();
}
/**
- * Gets identifier of the action profile.
+ * Returns the group member identified by the given action profile member
+ * ID, if present.
*
- * @return action profile id
+ * @param memberId action profile member ID
+ * @return optional group member
*/
- public PiActionProfileId actionProfileId() {
+ public Optional<WeightedMember> member(PiActionProfileMemberId memberId) {
+ return Optional.of(members.get(memberId));
+ }
+
+ /**
+ * Returns the maximum number of members that this group can hold. 0
+ * signifies that a limit is not set.
+ *
+ * @return maximum number of members that this group can hold
+ */
+ public int maxSize() {
+ return maxSize;
+ }
+
+ /**
+ * Returns the ID of the action profile where this group belong.
+ *
+ * @return action profile ID
+ */
+ public PiActionProfileId actionProfile() {
return actionProfileId;
}
@Override
- public boolean equals(Object o) {
- if (this == o) {
+ public boolean equals(Object obj) {
+ if (this == obj) {
return true;
}
- if (o == null || !(o instanceof PiActionProfileGroup)) {
+ if (obj == null || getClass() != obj.getClass()) {
return false;
}
- PiActionProfileGroup that = (PiActionProfileGroup) o;
- return Objects.equal(id, that.id) &&
- Objects.equal(members, that.members) &&
- Objects.equal(actionProfileId, that.actionProfileId);
+ final PiActionProfileGroup other = (PiActionProfileGroup) obj;
+ return Objects.equal(this.groupId, other.groupId)
+ && Objects.equal(this.members, other.members)
+ && Objects.equal(this.maxSize, other.maxSize)
+ && Objects.equal(this.actionProfileId, other.actionProfileId);
}
@Override
public int hashCode() {
- return Objects.hashCode(id, members);
+ return Objects.hashCode(groupId, members, maxSize, actionProfileId);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
- .add("groupId", id)
+ .add("actionProfile", actionProfileId)
+ .add("id", groupId)
.add("members", members)
- .add("piActionProfileId", actionProfileId)
+ .add("maxSize", maxSize)
.toString();
}
@@ -120,55 +148,115 @@
*/
public static final class Builder {
- private PiActionProfileGroupId id;
- private Map<PiActionProfileMemberId, PiActionProfileMember> members = Maps.newHashMap();
- private PiActionProfileId piActionProfileId;
+ private PiActionProfileGroupId groupId;
+ private Map<PiActionProfileMemberId, WeightedMember> members = Maps.newHashMap();
+ private PiActionProfileId actionProfileId;
+ private int maxSize;
private Builder() {
// hides constructor.
}
/**
- * Sets the identifier of this action profile group.
+ * Sets the ID of this action profile group.
*
- * @param id action profile group identifier
+ * @param id action profile group ID
* @return this
*/
public Builder withId(PiActionProfileGroupId id) {
- this.id = id;
+ this.groupId = id;
return this;
}
/**
- * Adds one member to this action profile group.
+ * Adds one member to this action profile.
*
- * @param member action profile member
+ * @param member member to add
* @return this
*/
- public Builder addMember(PiActionProfileMember member) {
+ public Builder addMember(WeightedMember member) {
+ checkNotNull(member);
members.put(member.id(), member);
return this;
}
/**
- * Adds many members to this action profile group.
+ * Adds one member to this action profile group with default weight.
*
- * @param members action profile members
+ * @param memberId ID of the action profile member to add
* @return this
*/
- public Builder addMembers(Collection<PiActionProfileMember> members) {
- members.forEach(this::addMember);
+ public Builder addMember(PiActionProfileMemberId memberId) {
+ addMember(new WeightedMember(memberId, WeightedMember.DEFAULT_WEIGHT));
return this;
}
/**
- * Sets the identifier of the action profile.
+ * Adds one member to this action profile group with default weight.
*
- * @param piActionProfileId the identifier of the action profile
+ * @param memberInstance the action profile member instance to add
+ * @return this
+ */
+ public Builder addMember(PiActionProfileMember memberInstance) {
+ addMember(new WeightedMember(memberInstance, WeightedMember.DEFAULT_WEIGHT));
+ return this;
+ }
+
+ /**
+ * Adds all members to this action profile group with default weight.
+ *
+ * @param memberInstances the action profile member instance to add
+ * @return this
+ */
+ public Builder addMembers(Iterable<PiActionProfileMember> memberInstances) {
+ memberInstances.forEach(this::addMember);
+ return this;
+ }
+
+ /**
+ * Adds one member to this action profile group with the given weight.
+ *
+ * @param memberId ID of the action profile member to add
+ * @param weight weight
+ * @return this
+ */
+ public Builder addMember(PiActionProfileMemberId memberId, int weight) {
+ addMember(new WeightedMember(memberId, weight));
+ return this;
+ }
+
+ /**
+ * Adds one member to this action profile group with the given weight.
+ *
+ * @param memberInstance the action profile member instance to add
+ * @param weight weight
+ * @return this
+ */
+ public Builder addMember(PiActionProfileMember memberInstance, int weight) {
+ addMember(new WeightedMember(memberInstance, weight));
+ return this;
+ }
+
+ /**
+ * Sets the ID of the action profile.
+ *
+ * @param piActionProfileId the ID of the action profile
* @return this
*/
public Builder withActionProfileId(PiActionProfileId piActionProfileId) {
- this.piActionProfileId = piActionProfileId;
+ this.actionProfileId = piActionProfileId;
+ return this;
+ }
+
+ /**
+ * Sets the maximum number of members that this group can hold.
+ *
+ * @param maxSize maximum number of members that this group can hold
+ * @return this
+ */
+ public Builder withMaxSize(int maxSize) {
+ checkArgument(maxSize >= 0, "maxSize cannot be negative");
+ this.maxSize = maxSize;
return this;
}
@@ -178,10 +266,119 @@
* @return action profile group
*/
public PiActionProfileGroup build() {
- checkNotNull(id);
- checkNotNull(piActionProfileId);
+ checkNotNull(groupId);
+ checkNotNull(actionProfileId);
+ checkArgument(maxSize == 0 || members.size() <= maxSize,
+ "The number of members cannot exceed maxSize");
+ final boolean validActionProfileId = members.isEmpty() || members.values()
+ .stream().allMatch(m -> m.instance() == null || m.instance()
+ .actionProfile().equals(actionProfileId));
+ checkArgument(
+ validActionProfileId,
+ "The members' action profile ID must match the group one");
return new PiActionProfileGroup(
- id, ImmutableSet.copyOf(members.values()), piActionProfileId);
+ groupId, ImmutableMap.copyOf(members), actionProfileId, maxSize);
+ }
+ }
+
+ /**
+ * Weighted reference to an action profile member as used in an action
+ * profile group.
+ */
+ public static final class WeightedMember {
+
+ public static final int DEFAULT_WEIGHT = 1;
+
+ private final PiActionProfileMemberId memberId;
+ private final int weight;
+ private final PiActionProfileMember memberInstance;
+
+ /**
+ * Creates a new reference for the given action profile member ID and
+ * weight.
+ *
+ * @param memberId action profile member ID
+ * @param weight weight
+ */
+ public WeightedMember(PiActionProfileMemberId memberId, int weight) {
+ checkNotNull(memberId);
+ this.memberId = memberId;
+ this.weight = weight;
+ this.memberInstance = null;
+ }
+
+ /**
+ * Creates a new reference from the given action profile member instance
+ * and weight. This constructor should be used when performing one-shot
+ * group programming (see {@link #instance()}).
+ *
+ * @param memberInstance action profile member instance
+ * @param weight weight
+ */
+ public WeightedMember(PiActionProfileMember memberInstance, int weight) {
+ checkNotNull(memberInstance);
+ this.memberId = memberInstance.id();
+ this.weight = weight;
+ this.memberInstance = memberInstance;
+ }
+
+ /**
+ * Returns the ID of the action profile member.
+ *
+ * @return action profile member ID
+ */
+ public PiActionProfileMemberId id() {
+ return memberId;
+ }
+
+ /**
+ * Returns the weight of this group member.
+ *
+ * @return weight
+ */
+ public int weight() {
+ return weight;
+ }
+
+ /**
+ * If present, returns the instance of the action profile member pointed
+ * by this reference, otherwise returns null. This method is provided as
+ * a convenient way to perform one-shot group programming, and as such
+ * is meaningful only when performing write operations to a device. In
+ * other words, when reading groups from a device only the member
+ * reference should be returned and not the actual instance, hence this
+ * method should return null.
+ *
+ * @return action profile member instance, or null
+ */
+ public PiActionProfileMember instance() {
+ return memberInstance;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(memberId, weight);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final WeightedMember other = (WeightedMember) obj;
+ return Objects.equal(this.memberId, other.memberId)
+ && Objects.equal(this.weight, other.weight);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("memberId", memberId)
+ .add("weight", weight)
+ .toString();
}
}
}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileGroupHandle.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileGroupHandle.java
index 42ed272..519a1c4 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileGroupHandle.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileGroupHandle.java
@@ -34,7 +34,7 @@
private PiActionProfileGroupHandle(DeviceId deviceId, PiActionProfileGroup group) {
super(deviceId);
- actionProfileId = group.actionProfileId();
+ actionProfileId = group.actionProfile();
groupId = group.id();
}
@@ -81,7 +81,7 @@
public String toString() {
return MoreObjects.toStringHelper(this)
.add("deviceId", deviceId())
- .add("actionProfileId", actionProfileId)
+ .add("actionProfile", actionProfileId)
.add("groupId", groupId)
.toString();
}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileMember.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileMember.java
index 92c3562..b5df9d0 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileMember.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileMember.java
@@ -32,19 +32,13 @@
private final PiActionProfileId actionProfileId;
private final PiActionProfileMemberId memberId;
private final PiAction action;
- // FIXME: in P4Runtime weight is an attribute of the member reference in a
- // group. Either remove it from this class or define the containing group
- // ID.
- private final int weight;
private PiActionProfileMember(PiActionProfileId actionProfileId,
PiActionProfileMemberId memberId,
- PiAction action,
- int weight) {
+ PiAction action) {
this.actionProfileId = actionProfileId;
this.memberId = memberId;
this.action = action;
- this.weight = weight;
}
/**
@@ -74,15 +68,6 @@
return action;
}
- /**
- * Returns the weight associated to this member.
- *
- * @return weight
- */
- public int weight() {
- return weight;
- }
-
@Override
public PiEntityType piEntityType() {
return PiEntityType.ACTION_PROFILE_MEMBER;
@@ -97,15 +82,14 @@
return false;
}
PiActionProfileMember that = (PiActionProfileMember) o;
- return weight == that.weight &&
- Objects.equal(actionProfileId, that.actionProfileId) &&
+ return Objects.equal(actionProfileId, that.actionProfileId) &&
Objects.equal(memberId, that.memberId) &&
Objects.equal(action, that.action);
}
@Override
public int hashCode() {
- return Objects.hashCode(actionProfileId, memberId, action, weight);
+ return Objects.hashCode(actionProfileId, memberId, action);
}
@Override
@@ -114,7 +98,6 @@
.add("actionProfile", actionProfileId)
.add("id", memberId)
.add("action", action)
- .add("weight", weight)
.toString();
}
@@ -133,9 +116,8 @@
public static final class Builder {
private PiActionProfileId actionProfileId;
- private PiActionProfileMemberId id;
+ private PiActionProfileMemberId memberId;
private PiAction action;
- private int weight;
private Builder() {
// Hides constructor.
@@ -159,7 +141,7 @@
* @return this
*/
public Builder withId(PiActionProfileMemberId id) {
- this.id = id;
+ this.memberId = id;
return this;
}
@@ -175,28 +157,15 @@
}
/**
- * Sets the weight of this member.
- * <p>
- * Default value is 0.
- *
- * @param weight weight
- * @return this
- */
- public Builder withWeight(int weight) {
- this.weight = weight;
- return this;
- }
-
- /**
* Creates a new action profile member.
*
* @return action profile member
*/
public PiActionProfileMember build() {
checkNotNull(actionProfileId);
- checkNotNull(id);
+ checkNotNull(memberId);
checkNotNull(action);
- return new PiActionProfileMember(actionProfileId, id, action, weight);
+ return new PiActionProfileMember(actionProfileId, memberId, action);
}
}
}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileMemberHandle.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileMemberHandle.java
index 8771650..40cb960 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileMemberHandle.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileMemberHandle.java
@@ -24,17 +24,17 @@
import static com.google.common.base.Preconditions.checkNotNull;
/**
- * Global identifier of a PI action profile group member, uniquely defined by a
+ * Global identifier of a PI action profile member, uniquely defined by a
* device ID, action profile ID, and member ID.
*/
public final class PiActionProfileMemberHandle extends PiHandle<PiActionProfileMember> {
- private final PiActionProfileMemberId memberId;
private final PiActionProfileId actionProfileId;
+ private final PiActionProfileMemberId memberId;
private PiActionProfileMemberHandle(DeviceId deviceId,
- PiActionProfileId actionProfileId,
- PiActionProfileMemberId memberId) {
+ PiActionProfileId actionProfileId,
+ PiActionProfileMemberId memberId) {
super(deviceId);
this.actionProfileId = actionProfileId;
this.memberId = memberId;
@@ -119,7 +119,7 @@
public String toString() {
return MoreObjects.toStringHelper(this)
.add("deviceId", deviceId())
- .add("actionProfileId", actionProfileId)
+ .add("actionProfile", actionProfileId)
.add("memberId", memberId)
.toString();
}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionProfileGroupTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionProfileGroupTest.java
index 0fecc72..4e6a30d 100644
--- a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionProfileGroupTest.java
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionProfileGroupTest.java
@@ -16,58 +16,118 @@
package org.onosproject.net.pi.runtime;
-import com.google.common.collect.Lists;
import com.google.common.testing.EqualsTester;
-import org.apache.commons.collections.CollectionUtils;
import org.junit.Test;
import org.onosproject.net.pi.model.PiActionId;
import org.onosproject.net.pi.model.PiActionParamId;
-
-import java.util.Collection;
+import org.onosproject.net.pi.model.PiActionProfileId;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
-import static org.onosproject.net.pi.runtime.PiConstantsTest.ACTION_PROF_ID;
+import static org.onosproject.net.pi.runtime.PiActionProfileGroup.WeightedMember.DEFAULT_WEIGHT;
import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
import static org.onosproject.net.pi.runtime.PiConstantsTest.MOD_NW_DST;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.MOD_VLAN_VID;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.VID;
/**
* Unit tests for PiActionProfileGroup class.
*/
public class PiActionProfileGroupTest {
- private final PiActionProfileMemberId piActionProfileMemberId = PiActionProfileMemberId.of(10);
- private final PiAction piAction = PiAction.builder().withId(PiActionId.of(MOD_NW_DST))
+ private final PiActionProfileId actionProfileId1 = PiActionProfileId.of("foo");
+ private final PiActionProfileId actionProfileId2 = PiActionProfileId.of("bar");
+
+ private final PiActionProfileGroupId groupId1 = PiActionProfileGroupId.of(100);
+ private final PiActionProfileGroupId groupId2 = PiActionProfileGroupId.of(200);
+
+ private final PiActionProfileMemberId actProfMemberId1 = PiActionProfileMemberId.of(10);
+ private final PiActionProfileMemberId actProfMemberId2 = PiActionProfileMemberId.of(20);
+
+ private final PiAction piAction1 = PiAction.builder().withId(PiActionId.of(MOD_NW_DST))
.withParameter(new PiActionParam(PiActionParamId.of(DST_ADDR), copyFrom(0x0a010101)))
.build();
-
- private final PiActionProfileMember piActionProfileMember = PiActionProfileMember.builder()
- .forActionProfile(ACTION_PROF_ID)
- .withId(piActionProfileMemberId)
- .withAction(piAction)
- .withWeight(10)
- .build();
- private PiActionProfileGroupId piActionGroupId = PiActionProfileGroupId.of(10);
- private PiActionProfileGroup piActionGroup1 = PiActionProfileGroup.builder()
- .addMember(piActionProfileMember)
- .withId(piActionGroupId)
- .withActionProfileId(ACTION_PROF_ID)
+ private final PiAction piAction2 = PiAction.builder().withId(PiActionId.of(MOD_VLAN_VID))
+ .withParameter(new PiActionParam(PiActionParamId.of(VID), copyFrom(0x0b)))
.build();
- private PiActionProfileGroup sameAsPiActionProfileGroup1 = PiActionProfileGroup.builder()
- .addMember(piActionProfileMember)
- .withId(piActionGroupId)
- .withActionProfileId(ACTION_PROF_ID)
+ private final PiActionProfileMember actProfMember11 = PiActionProfileMember.builder()
+ .forActionProfile(actionProfileId1)
+ .withId(actProfMemberId1)
+ .withAction(piAction1)
+ .build();
+ private final PiActionProfileMember actProfMember12 = PiActionProfileMember.builder()
+ .forActionProfile(actionProfileId1)
+ .withId(actProfMemberId2)
+ .withAction(piAction2)
+ .build();
+ private final PiActionProfileMember actProfMember21 = PiActionProfileMember.builder()
+ .forActionProfile(actionProfileId2)
+ .withId(actProfMemberId1)
+ .withAction(piAction1)
+ .build();
+ private final PiActionProfileMember actProfMember22 = PiActionProfileMember.builder()
+ .forActionProfile(actionProfileId2)
+ .withId(actProfMemberId2)
+ .withAction(piAction2)
.build();
- private PiActionProfileGroupId piActionGroupId2 = PiActionProfileGroupId.of(20);
- private PiActionProfileGroup piActionGroup2 = PiActionProfileGroup.builder()
- .addMember(piActionProfileMember)
- .withId(piActionGroupId2)
- .withActionProfileId(ACTION_PROF_ID)
+ private final PiActionProfileGroup.WeightedMember weightedMember1 = new PiActionProfileGroup.WeightedMember(
+ actProfMemberId1, DEFAULT_WEIGHT);
+ private final PiActionProfileGroup.WeightedMember weightedMember2 = new PiActionProfileGroup.WeightedMember(
+ actProfMemberId2, DEFAULT_WEIGHT);
+
+ private PiActionProfileGroup group1 = PiActionProfileGroup.builder()
+ .withActionProfileId(actionProfileId1)
+ // Group members defined with PiActionProfileMember instance.
+ .addMember(actProfMember11)
+ .addMember(actProfMember12)
+ .withId(groupId1)
+ .build();
+
+ private PiActionProfileGroup sameAsGroup1 = PiActionProfileGroup.builder()
+ .withActionProfileId(actionProfileId1)
+ // Group members defined with PiActionProfileMember instance, in
+ // different order.
+ .addMember(actProfMember12)
+ .addMember(actProfMember11)
+ .withId(groupId1)
+ .build();
+
+ private PiActionProfileGroup sameAsGroup1NoInstance = PiActionProfileGroup.builder()
+ .withActionProfileId(actionProfileId1)
+ // Group members defined with WeightedMember instances.
+ .addMember(weightedMember1)
+ .addMember(weightedMember2)
+ .withId(groupId1)
+ .build();
+
+ private PiActionProfileGroup group2 = PiActionProfileGroup.builder()
+ .withActionProfileId(actionProfileId2)
+ // Group members defined with PiActionProfileMember instance.
+ .addMember(actProfMember21)
+ .addMember(actProfMember22)
+ .withId(groupId2)
+ .build();
+
+ private PiActionProfileGroup sameAsGroup2NoInstance = PiActionProfileGroup.builder()
+ .withActionProfileId(actionProfileId2)
+ // Members defined by their ID only.
+ .addMember(actProfMemberId1)
+ .addMember(actProfMemberId2)
+ .withId(groupId2)
+ .build();
+
+ private PiActionProfileGroup asGroup2WithDifferentWeights = PiActionProfileGroup.builder()
+ .withActionProfileId(actionProfileId2)
+ // Members defined by their ID only and different weight.
+ .addMember(actProfMemberId1, 100)
+ .addMember(actProfMemberId2, 100)
+ .withId(groupId2)
.build();
/**
@@ -86,8 +146,9 @@
public void testEquals() {
new EqualsTester()
- .addEqualityGroup(piActionGroup1, sameAsPiActionProfileGroup1)
- .addEqualityGroup(piActionGroup2)
+ .addEqualityGroup(group1, sameAsGroup1, sameAsGroup1NoInstance)
+ .addEqualityGroup(group2, sameAsGroup2NoInstance)
+ .addEqualityGroup(asGroup2WithDifferentWeights)
.testEquals();
}
@@ -96,13 +157,21 @@
*/
@Test
public void testMethods() {
-
- Collection<PiActionProfileMember> piActionProfileMembers = Lists.newArrayList();
-
- piActionProfileMembers.add(piActionProfileMember);
- assertThat(piActionGroup1, is(notNullValue()));
- assertThat(piActionGroup1.id(), is(piActionGroupId));
- assertThat("Incorrect members value",
- CollectionUtils.isEqualCollection(piActionGroup1.members(), piActionProfileMembers));
+ assertThat(group1, is(notNullValue()));
+ assertThat(group1.id(), is(groupId1));
+ assertThat(group1.actionProfile(), is(actionProfileId1));
+ assertThat(group1.members().size(), is(2));
+ // Check members (with instance)
+ assertThat(group1.members().contains(weightedMember1), is(true));
+ assertThat(group1.members().contains(weightedMember2), is(true));
+ assertThat(group1.member(actProfMemberId1).isPresent(), is(notNullValue()));
+ assertThat(group1.member(actProfMemberId2).isPresent(), is(notNullValue()));
+ assertThat(group1.member(actProfMemberId1).get().instance(), is(actProfMember11));
+ assertThat(group1.member(actProfMemberId2).get().instance(), is(actProfMember12));
+ // Check members (no instance)
+ assertThat(sameAsGroup2NoInstance.member(actProfMemberId1).isPresent(), is(true));
+ assertThat(sameAsGroup2NoInstance.member(actProfMemberId2).isPresent(), is(true));
+ assertThat(sameAsGroup2NoInstance.member(actProfMemberId1).get().instance(), is(nullValue()));
+ assertThat(sameAsGroup2NoInstance.member(actProfMemberId2).get().instance(), is(nullValue()));
}
}
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionProfileMemberTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionProfileMemberTest.java
index 317f322..5d8f99e 100644
--- a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionProfileMemberTest.java
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiActionProfileMemberTest.java
@@ -29,6 +29,8 @@
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
import static org.onosproject.net.pi.runtime.PiConstantsTest.MOD_NW_DST;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.MOD_VLAN_VID;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.VID;
/**
* Unit tests for PiActionProfileMember class.
@@ -37,34 +39,34 @@
private final PiActionProfileId actionProfileId1 = PiActionProfileId.of("foo");
private final PiActionProfileId actionProfileId2 = PiActionProfileId.of("bar");
- private final PiActionProfileMemberId piActionProfileMemberId = PiActionProfileMemberId.of(10);
- private final PiAction piAction = PiAction.builder().withId(PiActionId.of(MOD_NW_DST))
+ private final PiActionProfileMemberId piActionProfileMemberId1 = PiActionProfileMemberId.of(10);
+ private final PiActionProfileMemberId piActionProfileMemberId2 = PiActionProfileMemberId.of(20);
+ private final PiAction piAction1 = PiAction.builder().withId(PiActionId.of(MOD_NW_DST))
.withParameter(new PiActionParam(PiActionParamId.of(DST_ADDR), copyFrom(0x0a010101)))
.build();
+ private final PiAction piAction2 = PiAction.builder().withId(PiActionId.of(MOD_VLAN_VID))
+ .withParameter(new PiActionParam(PiActionParamId.of(VID), copyFrom(0x0b)))
+ .build();
private final PiActionProfileMember piActionProfileMember1 = PiActionProfileMember.builder()
.forActionProfile(actionProfileId1)
- .withId(piActionProfileMemberId)
- .withAction(piAction)
- .withWeight(10)
+ .withId(piActionProfileMemberId1)
+ .withAction(piAction1)
.build();
private final PiActionProfileMember sameAsPiActionProfileMember1 = PiActionProfileMember.builder()
.forActionProfile(actionProfileId1)
- .withId(piActionProfileMemberId)
- .withAction(piAction)
- .withWeight(10)
+ .withId(piActionProfileMemberId1)
+ .withAction(piAction1)
.build();
private final PiActionProfileMember piActionProfileMember2 = PiActionProfileMember.builder()
.forActionProfile(actionProfileId1)
- .withId(piActionProfileMemberId)
- .withAction(piAction)
- .withWeight(20)
+ .withId(piActionProfileMemberId2)
+ .withAction(piAction2)
.build();
- private final PiActionProfileMember piActionGroupMember1ForOtherProfile = PiActionProfileMember.builder()
+ private final PiActionProfileMember piActionProfileMember3 = PiActionProfileMember.builder()
.forActionProfile(actionProfileId2)
- .withId(piActionProfileMemberId)
- .withAction(piAction)
- .withWeight(10)
+ .withId(piActionProfileMemberId1)
+ .withAction(piAction1)
.build();
/**
@@ -85,7 +87,7 @@
new EqualsTester()
.addEqualityGroup(piActionProfileMember1, sameAsPiActionProfileMember1)
.addEqualityGroup(piActionProfileMember2)
- .addEqualityGroup(piActionGroupMember1ForOtherProfile)
+ .addEqualityGroup(piActionProfileMember3)
.testEquals();
}
@@ -96,8 +98,7 @@
public void testMethods() {
assertThat(piActionProfileMember1, is(notNullValue()));
- assertThat(piActionProfileMember1.weight(), is(10));
- assertThat(piActionProfileMember1.id(), is(piActionProfileMemberId));
- assertThat(piActionProfileMember1.action(), is(piAction));
+ assertThat(piActionProfileMember1.id(), is(piActionProfileMemberId1));
+ assertThat(piActionProfileMember1.action(), is(piAction1));
}
}
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 38efdb9..e910116 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
@@ -37,6 +37,7 @@
import org.onosproject.net.pi.model.PiPipelineModel;
import org.onosproject.net.pi.model.PiTableId;
import org.onosproject.net.pi.model.PiTableModel;
+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.PiExactFieldMatch;
@@ -227,9 +228,24 @@
switch (piTableAction.type()) {
case ACTION:
return checkPiAction((PiAction) piTableAction, table);
- default:
- // FIXME: should we check? how?
+ case ACTION_PROFILE_GROUP_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())) {
+ 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()));
+ }
return piTableAction;
+ default:
+ throw new PiTranslationException(format(
+ "Unknown table action type %s", piTableAction.type()));
}
}
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 de32b60..5302856 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
@@ -21,6 +21,8 @@
import org.onosproject.net.group.Group;
import org.onosproject.net.group.GroupBucket;
import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.pi.model.PiActionProfileId;
+import org.onosproject.net.pi.model.PiActionProfileModel;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.runtime.PiAction;
@@ -55,7 +57,8 @@
}
/**
- * Returns a PI action profile group equivalent to the given group, for the given pipeconf and device.
+ * Returns a PI action profile group equivalent to the given group, for the
+ * given pipeconf and device.
*
* @param group group
* @param pipeconf pipeconf
@@ -71,60 +74,90 @@
"group type %s not supported", group.type()));
}
- final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf);
-
- final PiActionProfileGroup.Builder piActionGroupBuilder = PiActionProfileGroup.builder()
- .withId(PiActionProfileGroupId.of(group.id().id()));
-
+ // Get action profile from group key.
+ // TODO: define proper field in group class.
if (!(group.appCookie() instanceof PiGroupKey)) {
- throw new PiTranslationException("group app cookie is not PI (class should be PiGroupKey)");
+ throw new PiTranslationException(
+ "group app cookie is not PI (class should be PiGroupKey)");
}
final PiGroupKey groupKey = (PiGroupKey) group.appCookie();
+ final PiActionProfileId actionProfileId = groupKey.actionProfileId();
- piActionGroupBuilder.withActionProfileId(groupKey.actionProfileId());
+ // Check validity of action profile against pipeconf.
+ final PiActionProfileModel actionProfileModel = pipeconf.pipelineModel()
+ .actionProfiles(actionProfileId)
+ .orElseThrow(() -> new PiTranslationException(format(
+ "no such action profile '%s'", actionProfileId)));
+ if (!actionProfileModel.hasSelector()) {
+ throw new PiTranslationException(format(
+ "action profile '%s' does not support dynamic selection",
+ actionProfileId));
+ }
+
+ // Check group validity.
+ if (actionProfileModel.maxGroupSize() > 0
+ && group.buckets().buckets().size() > actionProfileModel.maxGroupSize()) {
+ throw new PiTranslationException(format(
+ "too many buckets, max group size for action profile '%s' is %d",
+ actionProfileId, actionProfileModel.maxGroupSize()));
+ }
+
+ final PiActionProfileGroup.Builder piActionGroupBuilder = PiActionProfileGroup.builder()
+ .withId(PiActionProfileGroupId.of(group.id().id()))
+ .withActionProfileId(groupKey.actionProfileId())
+ // We set the maximum group size as specified in the model,
+ // however this might be highly inefficient for some HW targets
+ // which pre-allocate resources for the whole group.
+ .withMaxSize(actionProfileModel.maxGroupSize());
// Translate group buckets to PI group members
+ final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf);
short bucketIdx = 0;
for (GroupBucket bucket : group.buckets().buckets()) {
/*
FIXME: the way member IDs are computed can cause collisions!
- Problem:
- In P4Runtime action profile 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.
+ Problem: In P4Runtime action profile 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.
+ - 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.
+ Hack: Statically derive member ID by combining groupId and position
+ of the bucket in the list.
*/
- ByteBuffer bb = ByteBuffer.allocate(4)
+ final ByteBuffer bb = ByteBuffer.allocate(4)
.putShort((short) (group.id().id() & 0xffff))
.putShort(bucketIdx);
bb.rewind();
- int memberId = bb.getInt();
+ final int memberId = bb.getInt();
bucketIdx++;
- final PiTableAction tableAction = translateTreatment(bucket.treatment(), interpreter, groupKey.tableId(),
- pipeconf.pipelineModel());
+ final PiTableAction tableAction = translateTreatment(
+ bucket.treatment(), interpreter,
+ groupKey.tableId(), pipeconf.pipelineModel());
if (tableAction == null) {
- throw new PiTranslationException("The PI table action returned by the interpreter is null");
+ throw new PiTranslationException(
+ "bucket treatment translator returned null");
}
if (tableAction.type() != ACTION) {
throw new PiTranslationException(format(
- "PI table action of type %s is not supported in groups", tableAction.type()));
+ "action of type '%s' cannot be used in action profile members",
+ tableAction.type()));
}
- piActionGroupBuilder.addMember(PiActionProfileMember.builder()
- .forActionProfile(groupKey.actionProfileId())
- .withId(PiActionProfileMemberId.of(memberId))
- .withAction((PiAction) tableAction)
- .withWeight(bucket.weight())
- .build());
+ final PiActionProfileMember member = PiActionProfileMember.builder()
+ .forActionProfile(groupKey.actionProfileId())
+ .withId(PiActionProfileMemberId.of(memberId))
+ .withAction((PiAction) tableAction)
+ .build();
+
+ piActionGroupBuilder.addMember(member, bucket.weight());
}
return piActionGroupBuilder.build();
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 adc96a0..fd445d7 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
@@ -48,12 +48,15 @@
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
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.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;
import static org.onosproject.pipelines.basic.BasicConstants.INGRESS_WCMP_CONTROL_WCMP_TABLE;
@@ -80,16 +83,21 @@
private static final int DEFAULT_MEMBER_WEIGHT = 1;
private static final int BASE_MEM_ID = 65535;
private static final int PORT_BITWIDTH = 9;
- private Collection<PiActionProfileMember> expectedMembers;
+ private Collection<PiActionProfileMember> expectedMemberInstances;
+ private Collection<PiActionProfileGroup.WeightedMember> expectedWeightedMembers;
private PiPipeconf pipeconf;
@Before
public void setUp() throws Exception {
pipeconf = PipeconfLoader.BASIC_PIPECONF;
- expectedMembers = ImmutableSet.of(outputMember(1),
- outputMember(2),
- outputMember(3));
+ expectedMemberInstances = ImmutableSet.of(outputMember(1),
+ outputMember(2),
+ outputMember(3));
+ expectedWeightedMembers = expectedMemberInstances.stream()
+ .map(m -> new PiActionProfileGroup.WeightedMember(m, DEFAULT_WEIGHT))
+ .collect(Collectors.toSet());
+
}
private static GroupBucket selectOutputBucket(int portNum) {
@@ -114,7 +122,6 @@
.forActionProfile(INGRESS_WCMP_CONTROL_WCMP_SELECTOR)
.withAction(piAction)
.withId(PiActionProfileMemberId.of(BASE_MEM_ID + portNum))
- .withWeight(DEFAULT_MEMBER_WEIGHT)
.build();
}
@@ -134,13 +141,22 @@
assertThat("Group ID must be equal",
piGroup1.id().id(), is(equalTo(GROUP_ID.id())));
assertThat("Action profile ID must be equal",
- piGroup1.actionProfileId(), is(equalTo(INGRESS_WCMP_CONTROL_WCMP_SELECTOR)));
+ piGroup1.actionProfile(), is(equalTo(INGRESS_WCMP_CONTROL_WCMP_SELECTOR)));
// members installed
- Collection<PiActionProfileMember> members = piGroup1.members();
+ Collection<PiActionProfileGroup.WeightedMember> weightedMembers = piGroup1.members();
+ Collection<PiActionProfileMember> memberInstances = weightedMembers.stream()
+ .map(PiActionProfileGroup.WeightedMember::instance)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
assertThat("The number of group members must be equal",
- piGroup1.members().size(), is(expectedMembers.size()));
- assertThat("Group members must be equal",
- members.containsAll(expectedMembers) && expectedMembers.containsAll(members));
+ piGroup1.members().size(), is(expectedWeightedMembers.size()));
+ assertThat("Group weighted members must be equal",
+ weightedMembers.containsAll(expectedWeightedMembers)
+ && expectedWeightedMembers.containsAll(weightedMembers));
+ assertThat("Group member instances must be equal",
+ memberInstances.containsAll(expectedMemberInstances)
+ && expectedMemberInstances.containsAll(memberInstances));
+
}
}
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 4a48897..d614d6e 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
@@ -19,14 +19,12 @@
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Striped;
import org.onlab.util.SharedExecutors;
import org.onosproject.drivers.p4runtime.mirror.P4RuntimeActionProfileGroupMirror;
import org.onosproject.drivers.p4runtime.mirror.P4RuntimeActionProfileMemberMirror;
import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
-import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DeviceId;
import org.onosproject.net.group.DefaultGroup;
import org.onosproject.net.group.DefaultGroupDescription;
@@ -36,10 +34,8 @@
import org.onosproject.net.group.GroupOperations;
import org.onosproject.net.group.GroupProgrammable;
import org.onosproject.net.group.GroupStore;
-import org.onosproject.net.pi.model.PiActionId;
import org.onosproject.net.pi.model.PiActionProfileId;
import org.onosproject.net.pi.model.PiActionProfileModel;
-import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionProfileGroup;
import org.onosproject.net.pi.runtime.PiActionProfileGroupHandle;
import org.onosproject.net.pi.runtime.PiActionProfileMember;
@@ -59,8 +55,9 @@
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toMap;
import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.DELETE;
import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.INSERT;
import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.MODIFY;
@@ -77,7 +74,6 @@
// the ONOS store.
private static final String READ_ACTION_GROUPS_FROM_MIRROR = "actionGroupReadFromMirror";
private static final boolean DEFAULT_READ_ACTION_GROUPS_FROM_MIRROR = false;
- private static final String MAX_MEM_SIZE = "maxMemSize";
protected GroupStore groupStore;
private P4RuntimeActionProfileGroupMirror groupMirror;
@@ -86,7 +82,6 @@
// Needed to synchronize operations over the same group.
private static final Striped<Lock> STRIPED_LOCKS = Striped.lock(30);
- private static final int GROUP_MEMBERS_BUFFER_SIZE = 3;
@Override
protected boolean setupBehaviour() {
@@ -110,13 +105,20 @@
groupOps.operations().stream()
.filter(op -> !op.groupType().equals(GroupDescription.Type.ALL))
.forEach(op -> {
- // ONOS-7785 We need app cookie (action profile id) from the group
+ // ONOS-7785 We need the group app cookie (which includes
+ // the action profile ID) but this is not part of the
+ // GroupDescription.
Group groupOnStore = groupStore.getGroup(deviceId, op.groupId());
+ if (groupOnStore == null) {
+ log.warn("Unable to find group {} in store, aborting {} operation",
+ op.groupId(), op.opType());
+ return;
+ }
GroupDescription groupDesc = new DefaultGroupDescription(
deviceId, op.groupType(), op.buckets(), groupOnStore.appCookie(),
op.groupId().id(), groupOnStore.appId());
DefaultGroup groupToApply = new DefaultGroup(op.groupId(), groupDesc);
- processGroupOperation(groupToApply, op.opType());
+ processPdGroup(groupToApply, op.opType());
});
}
@@ -125,89 +127,66 @@
if (!setupBehaviour()) {
return Collections.emptyList();
}
- return getActionGroups();
- }
-
- private Collection<Group> getActionGroups() {
if (driverBoolProperty(READ_ACTION_GROUPS_FROM_MIRROR,
DEFAULT_READ_ACTION_GROUPS_FROM_MIRROR)) {
- return getActionGroupsFromMirror();
+ return getGroupsFromMirror();
}
- final Collection<PiActionProfileId> actionProfileIds = pipeconf.pipelineModel()
+ // Dump groups and members from device for all action profiles.
+ final Set<PiActionProfileId> actionProfileIds = pipeconf.pipelineModel()
.actionProfiles()
.stream()
.map(PiActionProfileModel::id)
- .collect(Collectors.toList());
- final List<PiActionProfileGroup> groupsOnDevice = actionProfileIds.stream()
- .flatMap(this::streamGroupsFromDevice)
- .collect(Collectors.toList());
- final Set<PiActionProfileMemberHandle> membersOnDevice = actionProfileIds
- .stream()
- .flatMap(actProfId -> getMembersFromDevice(actProfId)
- .stream()
- .map(memberId -> PiActionProfileMemberHandle.of(
- deviceId, actProfId, memberId)))
.collect(Collectors.toSet());
-
- if (groupsOnDevice.isEmpty()) {
- return Collections.emptyList();
- }
+ final Map<PiActionProfileGroupHandle, PiActionProfileGroup>
+ groupsOnDevice = dumpAllGroupsFromDevice(actionProfileIds);
+ final Map<PiActionProfileMemberHandle, PiActionProfileMember> membersOnDevice =
+ dumpAllMembersFromDevice(actionProfileIds);
// Sync mirrors.
- syncGroupMirror(groupsOnDevice);
- syncMemberMirror(membersOnDevice);
+ groupMirror.sync(deviceId, groupsOnDevice);
+ memberMirror.sync(deviceId, membersOnDevice);
+ // Retrieve the original PD group before translation.
final List<Group> result = Lists.newArrayList();
- final List<PiActionProfileGroup> inconsistentGroups = Lists.newArrayList();
- final List<PiActionProfileGroup> validGroups = Lists.newArrayList();
-
- for (PiActionProfileGroup piGroup : groupsOnDevice) {
- final Group pdGroup = forgeGroupEntry(piGroup);
+ final List<PiActionProfileGroup> groupsToRemove = Lists.newArrayList();
+ final Set<PiActionProfileMemberHandle> memberHandlesToKeep = Sets.newHashSet();
+ for (PiActionProfileGroup piGroup : groupsOnDevice.values()) {
+ final Group pdGroup = checkAndForgeGroupEntry(piGroup, membersOnDevice);
if (pdGroup == null) {
// Entry is on device but unknown to translation service or
// device mirror. Inconsistent. Mark for removal.
- inconsistentGroups.add(piGroup);
+ groupsToRemove.add(piGroup);
} else {
- validGroups.add(piGroup);
result.add(pdGroup);
+ // Keep track of member handles used in groups.
+ piGroup.members().stream()
+ .map(m -> PiActionProfileMemberHandle.of(
+ deviceId, piGroup.actionProfile(), m.id()))
+ .forEach(memberHandlesToKeep::add);
}
}
- // Trigger clean up of inconsistent groups and members. This will also
- // remove all members that are not used by any group, and update the
- // mirror accordingly.
- final Set<PiActionProfileMemberHandle> membersToKeep = validGroups.stream()
- .flatMap(g -> g.members().stream())
- .map(m -> PiActionProfileMemberHandle.of(deviceId, m))
- .collect(Collectors.toSet());
- final Set<PiActionProfileMemberHandle> inconsistentMembers = Sets.difference(
- membersOnDevice, membersToKeep);
+ // Trigger clean up of inconsistent groups and members. This will update
+ // the mirror accordingly.
+ final Set<PiActionProfileMemberHandle> memberHandlesToRemove = Sets.difference(
+ membersOnDevice.keySet(), memberHandlesToKeep);
SharedExecutors.getSingleThreadExecutor().execute(
() -> cleanUpInconsistentGroupsAndMembers(
- inconsistentGroups, inconsistentMembers));
+ groupsToRemove, memberHandlesToRemove));
+ // Done.
return result;
}
- private void syncGroupMirror(Collection<PiActionProfileGroup> groups) {
- Map<PiActionProfileGroupHandle, PiActionProfileGroup> handleMap = Maps.newHashMap();
- groups.forEach(g -> handleMap.put(PiActionProfileGroupHandle.of(deviceId, g), g));
- groupMirror.sync(deviceId, handleMap);
- }
-
- private void syncMemberMirror(Collection<PiActionProfileMemberHandle> memberHandles) {
- Map<PiActionProfileMemberHandle, PiActionProfileMember> handleMap = Maps.newHashMap();
- memberHandles.forEach(handle -> handleMap.put(
- handle, dummyMember(handle.actionProfileId(), handle.memberId())));
- memberMirror.sync(deviceId, handleMap);
- }
-
- private Collection<Group> getActionGroupsFromMirror() {
+ private Collection<Group> getGroupsFromMirror() {
+ final Map<PiActionProfileMemberHandle, PiActionProfileMember> members =
+ memberMirror.deviceHandleMap(deviceId);
return groupMirror.getAll(deviceId).stream()
.map(TimedEntry::entry)
- .map(this::forgeGroupEntry)
+ .map(g -> checkAndForgeGroupEntry(
+ g, members))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
@@ -219,7 +198,7 @@
groupsToRemove.size(), deviceId);
groupsToRemove.forEach(piGroup -> {
log.debug(piGroup.toString());
- processGroup(piGroup, null, Operation.REMOVE);
+ processPiGroup(piGroup, null, Operation.REMOVE);
});
}
if (!membersToRemove.isEmpty()) {
@@ -244,44 +223,77 @@
}
}
- private Stream<PiActionProfileGroup> streamGroupsFromDevice(PiActionProfileId actProfId) {
+ private Map<PiActionProfileGroupHandle, PiActionProfileGroup> dumpAllGroupsFromDevice(
+ Set<PiActionProfileId> actProfIds) {
// TODO: implement P4Runtime client call to read all groups with one call
// Good if pipeline has multiple action profiles.
- final Collection<PiActionProfileGroup> groups = getFutureWithDeadline(
- client.dumpActionProfileGroups(actProfId, pipeconf),
- "dumping groups", Collections.emptyList());
- return groups.stream();
+ return actProfIds.stream()
+ .flatMap(actProfId -> getFutureWithDeadline(
+ client.dumpActionProfileGroups(actProfId, pipeconf),
+ "dumping groups", Collections.emptyList()).stream())
+ .collect(toMap(g -> PiActionProfileGroupHandle.of(deviceId, g), g -> g));
}
- private List<PiActionProfileMemberId> getMembersFromDevice(PiActionProfileId actProfId) {
+ private Map<PiActionProfileMemberHandle, PiActionProfileMember> dumpAllMembersFromDevice(
+ Set<PiActionProfileId> actProfIds) {
// TODO: implement P4Runtime client call to read all members with one call
// Good if pipeline has multiple action profiles.
- return getFutureWithDeadline(
- client.dumpActionProfileMemberIds(actProfId, pipeconf),
- "dumping action profile ids", Collections.emptyList());
+ return actProfIds.stream()
+ .flatMap(actProfId -> getFutureWithDeadline(
+ client.dumpActionProfileMembers(actProfId, pipeconf),
+ "dumping members", Collections.emptyList()).stream())
+ .collect(toMap(m -> PiActionProfileMemberHandle.of(deviceId, m), m -> m));
}
- private Group forgeGroupEntry(PiActionProfileGroup piGroup) {
- final PiActionProfileGroupHandle handle = PiActionProfileGroupHandle.of(deviceId, piGroup);
+ private Group checkAndForgeGroupEntry(
+ PiActionProfileGroup piGroupOnDevice,
+ Map<PiActionProfileMemberHandle, PiActionProfileMember> membersOnDevice) {
+ final PiActionProfileGroupHandle handle = PiActionProfileGroupHandle.of(
+ deviceId, piGroupOnDevice);
final Optional<PiTranslatedEntity<Group, PiActionProfileGroup>>
translatedEntity = groupTranslator.lookup(handle);
- final TimedEntry<PiActionProfileGroup> timedEntry = groupMirror.get(handle);
- // Is entry consistent with our state?
+ final TimedEntry<PiActionProfileGroup> mirrorEntry = groupMirror.get(handle);
+ // Check that entry obtained from device is consistent with what is known
+ // by the translation store.
if (!translatedEntity.isPresent()) {
- log.warn("Group handle not found in translation store: {}", handle);
+ log.warn("Group not found in translation store: {}", handle);
return null;
}
- if (!translatedEntity.get().translated().equals(piGroup)) {
- log.warn("Group obtained from device {} is different from the one in " +
- "translation store: device={}, store={}",
- deviceId, piGroup, translatedEntity.get().translated());
+ final PiActionProfileGroup piGroupFromStore = translatedEntity.get().translated();
+ if (!piGroupFromStore.equals(piGroupOnDevice)) {
+ log.warn("Group on device {} is different from the one in " +
+ "translation store: {} [device={}, store={}]",
+ deviceId, handle, piGroupOnDevice, piGroupFromStore);
return null;
}
- if (timedEntry == null) {
+ // Groups in P4Runtime contains only a reference to members. Check that
+ // the actual member instances in the translation store are the same
+ // found on the device.
+ if (!validateMembers(piGroupFromStore, membersOnDevice)) {
+ log.warn("Group on device {} refers to members that are different " +
+ "than those found in translation store: {}", handle);
+ return null;
+ }
+ if (mirrorEntry == null) {
log.warn("Group handle not found in device mirror: {}", handle);
return null;
}
- return addedGroup(translatedEntity.get().original(), timedEntry.lifeSec());
+ // Check that members from device are the same as in the translated group.
+ return addedGroup(translatedEntity.get().original(), mirrorEntry.lifeSec());
+ }
+
+ private boolean validateMembers(
+ PiActionProfileGroup piGroupFromStore,
+ Map<PiActionProfileMemberHandle, PiActionProfileMember> membersOnDevice) {
+ final Collection<PiActionProfileMember> groupMembers =
+ extractAllMemberInstancesOrNull(piGroupFromStore);
+ if (groupMembers == null) {
+ return false;
+ }
+ return groupMembers.stream().allMatch(
+ memberFromStore -> memberFromStore.equals(
+ membersOnDevice.get(
+ PiActionProfileMemberHandle.of(deviceId, memberFromStore))));
}
private Group addedGroup(Group original, long life) {
@@ -291,7 +303,7 @@
return forgedGroup;
}
- private void processGroupOperation(Group pdGroup, GroupOperation.Type opType) {
+ private void processPdGroup(Group pdGroup, GroupOperation.Type opType) {
final PiActionProfileGroup piGroup;
try {
piGroup = groupTranslator.translate(pdGroup, pipeconf);
@@ -302,12 +314,12 @@
}
final Operation operation = opType.equals(GroupOperation.Type.DELETE)
? Operation.REMOVE : Operation.APPLY;
- processGroup(piGroup, pdGroup, operation);
+ processPiGroup(piGroup, pdGroup, operation);
}
- private void processGroup(PiActionProfileGroup groupToApply,
- Group pdGroup,
- Operation operation) {
+ private void processPiGroup(PiActionProfileGroup groupToApply,
+ Group pdGroup,
+ Operation operation) {
final PiActionProfileGroupHandle handle = PiActionProfileGroupHandle.of(deviceId, groupToApply);
STRIPED_LOCKS.get(handle).lock();
try {
@@ -334,55 +346,45 @@
private boolean applyGroupWithMembersOrNothing(PiActionProfileGroup group, PiActionProfileGroupHandle handle) {
// First apply members, then group, if fails, delete members.
- if (!applyAllMembersOrNothing(group.members())) {
+ Collection<PiActionProfileMember> members = extractAllMemberInstancesOrNull(group);
+ if (members == null) {
+ return false;
+ }
+ if (!applyAllMembersOrNothing(members)) {
return false;
}
if (!applyGroup(group, handle)) {
- deleteMembers(group.members());
+ deleteMembers(handles(members));
return false;
}
return true;
}
- private boolean applyGroup(PiActionProfileGroup group, PiActionProfileGroupHandle handle) {
- final int currentMemberSize = group.members().size();
- if (groupMirror.get(handle) != null) {
- String maxMemSize = "";
- if (groupMirror.annotations(handle) != null &&
- groupMirror.annotations(handle).value(MAX_MEM_SIZE) != null) {
- maxMemSize = groupMirror.annotations(handle).value(MAX_MEM_SIZE);
- }
- if (maxMemSize.equals("") || currentMemberSize > Integer.parseInt(maxMemSize)) {
- deleteGroup(group, handle);
- }
+ private boolean applyGroup(PiActionProfileGroup groupToApply, PiActionProfileGroupHandle handle) {
+ final TimedEntry<PiActionProfileGroup> groupOnDevice = groupMirror.get(handle);
+ final P4RuntimeClient.WriteOperationType opType =
+ groupOnDevice == null ? INSERT : MODIFY;
+ if (opType.equals(MODIFY) && groupToApply.equals(groupOnDevice.entry())) {
+ // Skip writing, group is unchanged.
+ return true;
}
-
- P4RuntimeClient.WriteOperationType opType =
- groupMirror.get(handle) == null ? INSERT : MODIFY;
- int currentMaxMemberSize = opType == INSERT ? (currentMemberSize + GROUP_MEMBERS_BUFFER_SIZE) : 0;
-
final boolean success = getFutureWithDeadline(
- client.writeActionProfileGroup(group, opType, pipeconf, currentMaxMemberSize),
+ client.writeActionProfileGroup(groupToApply, opType, pipeconf),
"performing action profile group " + opType, false);
if (success) {
- groupMirror.put(handle, group);
- if (opType == INSERT) {
- groupMirror.putAnnotations(handle, DefaultAnnotations
- .builder()
- .set(MAX_MEM_SIZE, Integer.toString(currentMaxMemberSize))
- .build());
- }
+ groupMirror.put(handle, groupToApply);
}
return success;
}
private boolean deleteGroup(PiActionProfileGroup group, PiActionProfileGroupHandle handle) {
final boolean success = getFutureWithDeadline(
- client.writeActionProfileGroup(group, DELETE, pipeconf, 0),
+ client.writeActionProfileGroup(group, DELETE, pipeconf),
"performing action profile group " + DELETE, false);
if (success) {
groupMirror.remove(handle);
}
+ // Orphan members will be removed at the next reconciliation cycle.
return success;
}
@@ -391,7 +393,7 @@
if (appliedMembers.size() == members.size()) {
return true;
} else {
- deleteMembers(appliedMembers);
+ deleteMembers(handles(appliedMembers));
return false;
}
}
@@ -403,52 +405,67 @@
.collect(Collectors.toList());
}
- private boolean applyMember(PiActionProfileMember member) {
- // If exists, modify, otherwise insert
+ private boolean applyMember(PiActionProfileMember memberToApply) {
+ // If exists, modify, otherwise insert.
final PiActionProfileMemberHandle handle = PiActionProfileMemberHandle.of(
- deviceId, member);
+ deviceId, memberToApply);
+ final TimedEntry<PiActionProfileMember> memberOnDevice = memberMirror.get(handle);
final P4RuntimeClient.WriteOperationType opType =
- memberMirror.get(handle) == null ? INSERT : MODIFY;
+ memberOnDevice == null ? INSERT : MODIFY;
+ if (opType.equals(MODIFY) && memberToApply.equals(memberOnDevice.entry())) {
+ // Skip writing if member is unchanged.
+ return true;
+ }
final boolean success = getFutureWithDeadline(
- client.writeActionProfileMembers(Collections.singletonList(member),
- opType, pipeconf),
+ client.writeActionProfileMembers(
+ singletonList(memberToApply), opType, pipeconf),
"performing action profile member " + opType, false);
if (success) {
- memberMirror.put(handle, dummyMember(member.actionProfile(), member.id()));
+ memberMirror.put(handle, memberToApply);
}
return success;
}
- private void deleteMembers(Collection<PiActionProfileMember> members) {
- members.forEach(this::deleteMember);
+ private void deleteMembers(Collection<PiActionProfileMemberHandle> handles) {
+ // TODO: improve by batching deletes.
+ handles.forEach(this::deleteMember);
}
- private void deleteMember(PiActionProfileMember member) {
- final PiActionProfileMemberHandle handle = PiActionProfileMemberHandle.of(
- deviceId, member);
+ private void deleteMember(PiActionProfileMemberHandle handle) {
final boolean success = getFutureWithDeadline(
- client.writeActionProfileMembers(Collections.singletonList(member),
- DELETE, pipeconf),
- "performing action profile member " + DELETE, false);
+ client.removeActionProfileMembers(
+ handle.actionProfileId(),
+ singletonList(handle.memberId()), pipeconf),
+ "performing action profile member " + DELETE,
+ Collections.emptyList())
+ // Successful if the only member passed has been removed.
+ .size() == 1;
if (success) {
memberMirror.remove(handle);
}
}
- // FIXME: this is nasty, we have to rely on a dummy member of the mirror
- // because the PiActionProfileMember abstraction is broken, since it includes
- // attributes that are not part of a P4Runtime member, e.g. weight.
- // We should remove weight from the class, and have client methods that
- // return the full PiActionProfileMember, not just the IDs.
- private PiActionProfileMember dummyMember(
- PiActionProfileId actionProfileId, PiActionProfileMemberId memberId) {
- return PiActionProfileMember.builder()
- .forActionProfile(actionProfileId)
- .withId(memberId)
- .withAction(PiAction.builder()
- .withId(PiActionId.of("dummy"))
- .build())
- .build();
+ private Collection<PiActionProfileMemberHandle> handles(
+ Collection<PiActionProfileMember> members) {
+ return members.stream()
+ .map(m -> PiActionProfileMemberHandle.of(
+ deviceId, m.actionProfile(), m.id()))
+ .collect(Collectors.toList());
+ }
+
+ private Collection<PiActionProfileMember> extractAllMemberInstancesOrNull(
+ PiActionProfileGroup group) {
+ final Collection<PiActionProfileMember> instances = group.members().stream()
+ .map(PiActionProfileGroup.WeightedMember::instance)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ if (instances.size() != group.members().size()) {
+ log.error("PiActionProfileGroup has {} member references, " +
+ "but only {} instances were found",
+ group.members().size(), instances.size());
+ return null;
+ }
+ return instances;
}
enum Operation {
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/AbstractDistributedP4RuntimeMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/AbstractDistributedP4RuntimeMirror.java
index f45a4e5..e1fc1e9 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/AbstractDistributedP4RuntimeMirror.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/AbstractDistributedP4RuntimeMirror.java
@@ -160,7 +160,7 @@
@Override
public void sync(DeviceId deviceId, Map<H, E> deviceState) {
checkNotNull(deviceId);
- final Map<H, E> localState = getMirrorMapForDevice(deviceId);
+ final Map<H, E> localState = deviceHandleMap(deviceId);
final AtomicInteger removeCount = new AtomicInteger(0);
final AtomicInteger updateCount = new AtomicInteger(0);
@@ -202,7 +202,8 @@
.collect(Collectors.toSet());
}
- private Map<H, E> getMirrorMapForDevice(DeviceId deviceId) {
+ @Override
+ public Map<H, E> deviceHandleMap(DeviceId deviceId) {
final Map<H, E> deviceMap = Maps.newHashMap();
mirrorMap.entrySet().stream()
.filter(e -> e.getKey().deviceId().equals(deviceId))
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMirror.java
index 51b31aa..d62bbb8 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMirror.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMirror.java
@@ -75,10 +75,19 @@
void remove(H handle);
/**
+ * Returns a map of handles and corresponding PI entities for the given
+ * device.
+ *
+ * @param deviceId device ID
+ * @return map of handles and corresponding PI entities
+ */
+ Map<H, E> deviceHandleMap(DeviceId deviceId);
+
+ /**
* Stores the given annotations associating it to the given handle.
*
- * @param handle handle
- * @param annotations entry
+ * @param handle handle
+ * @param annotations entry
*/
void putAnnotations(H handle, Annotations annotations);
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 5d7e93e..ff00a1a 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
@@ -188,9 +188,9 @@
* Performs the given write operation for the given action profile members
* and pipeconf.
*
- * @param members action profile members
- * @param opType write operation type
- * @param pipeconf the pipeconf currently deployed on the device
+ * @param members action profile members
+ * @param opType write operation type
+ * @param pipeconf the pipeconf currently deployed on the device
* @return true if the operation was successful, false otherwise
*/
CompletableFuture<Boolean> writeActionProfileMembers(
@@ -204,19 +204,15 @@
* @param group the action profile group
* @param opType write operation type
* @param pipeconf the pipeconf currently deployed on the device
- * @param maxMemberSize the maximum number of members that can be added to
- * the group. This is meaningful only if it's an INSERT
- * operation, otherwise its value should be 0
* @return true if the operation was successful, false otherwise
*/
CompletableFuture<Boolean> writeActionProfileGroup(
PiActionProfileGroup group,
WriteOperationType opType,
- PiPipeconf pipeconf,
- int maxMemberSize);
+ PiPipeconf pipeconf);
/**
- * Dumps all groups currently installed in the given action profile.
+ * Dumps all groups for a given action profile.
*
* @param actionProfileId the action profile id
* @param pipeconf the pipeconf currently deployed on the device
@@ -226,13 +222,13 @@
PiActionProfileId actionProfileId, PiPipeconf pipeconf);
/**
- * Dumps all action profile member IDs for a given action profile.
+ * Dumps all members for a given action profile.
*
* @param actionProfileId action profile ID
* @param pipeconf pipeconf
* @return future of list of action profile member ID
*/
- CompletableFuture<List<PiActionProfileMemberId>> dumpActionProfileMemberIds(
+ CompletableFuture<List<PiActionProfileMember>> dumpActionProfileMembers(
PiActionProfileId actionProfileId, PiPipeconf pipeconf);
/**
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/AbstractP4RuntimeCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/AbstractP4RuntimeCodec.java
new file mode 100644
index 0000000..a76ab7c
--- /dev/null
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/AbstractP4RuntimeCodec.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2019-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 com.google.protobuf.Message;
+import com.google.protobuf.TextFormat;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Abstract implementation of a codec that translates PI entities into P4Runtime
+ * protobuf messages and vice versa.
+ *
+ * @param <P> PI entity class
+ * @param <M> P4Runtime protobuf message class
+ */
+abstract class AbstractP4RuntimeCodec<P extends PiEntity, M extends Message> {
+
+ protected final Logger log = getLogger(this.getClass());
+
+ protected abstract M encode(P piEntity, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException;
+
+ protected abstract P decode(M message, PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException;
+
+ /**
+ * Returns a P4Runtime protobuf message that is equivalent to the given PI
+ * entity for the given pipeconf.
+ *
+ * @param piEntity PI entity instance
+ * @param pipeconf pipeconf
+ * @return P4Runtime protobuf message
+ * @throws CodecException if the given PI entity cannot be encoded (see
+ * exception message)
+ */
+ public M encode(P piEntity, PiPipeconf pipeconf)
+ throws CodecException {
+ try {
+ return encode(piEntity, pipeconf, browserOrFail(pipeconf));
+ } catch (P4InfoBrowser.NotFoundException e) {
+ throw new CodecException(e.getMessage());
+ }
+ }
+
+ /**
+ * Returns a PI entity instance that is equivalent to the P4Runtime protobuf
+ * message for the given pipeconf.
+ *
+ * @param message P4Runtime protobuf message
+ * @param pipeconf pipeconf pipeconf
+ * @return PI entity instance
+ * @throws CodecException if the given protobuf message cannot be decoded
+ * (see exception message)
+ */
+ public P decode(M message, PiPipeconf pipeconf)
+ throws CodecException {
+ try {
+ return decode(message, pipeconf, browserOrFail(pipeconf));
+ } catch (P4InfoBrowser.NotFoundException e) {
+ throw new CodecException(e.getMessage());
+ }
+ }
+
+ /**
+ * Same as {@link #encode(PiEntity, PiPipeconf)} but returns null in case of
+ * exceptions, while the error message is logged.
+ *
+ * @param piEntity PI entity instance
+ * @param pipeconf pipeconf
+ * @return P4Runtime protobuf message
+ */
+ public M encodeOrNull(P piEntity, PiPipeconf pipeconf) {
+ try {
+ return encode(piEntity, pipeconf);
+ } catch (CodecException e) {
+ log.error("Unable to encode {}: {} [{}]",
+ piEntity.getClass().getSimpleName(),
+ e.getMessage(), piEntity.toString());
+ return null;
+ }
+ }
+
+ /**
+ * Same as {@link #decode(Message, PiPipeconf)} but returns null in case of
+ * exceptions, while the error message is logged.
+ *
+ * @param message P4Runtime protobuf message
+ * @param pipeconf pipeconf pipeconf
+ * @return PI entity instance
+ */
+ public P decodeOrNull(M message, PiPipeconf pipeconf) {
+ try {
+ return decode(message, pipeconf);
+ } catch (CodecException e) {
+ log.error("Unable to decode {}: {} [{}]",
+ message.getClass().getSimpleName(),
+ e.getMessage(), TextFormat.shortDebugString(message));
+ return null;
+ }
+ }
+
+ /**
+ * Encodes the given list of PI entities, skipping those that cannot be
+ * encoded, in which case an error message is logged. For this reason, the
+ * returned list might have different size than the returned one.
+ *
+ * @param piEntities list of PI entities
+ * @param pipeconf pipeconf
+ * @return list of P4Runtime protobuf messages
+ */
+ public List<M> encodeAll(List<P> piEntities, PiPipeconf pipeconf) {
+ checkNotNull(piEntities);
+ return piEntities.stream()
+ .map(p -> encodeOrNull(p, pipeconf))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Decodes the given list of P4Runtime protobuf messages, skipping those
+ * that cannot be decoded, on which case an error message is logged. For
+ * this reason, the returned list might have different size than the
+ * returned one.
+ *
+ * @param messages list of protobuf messages
+ * @param pipeconf pipeconf
+ * @return list of PI entities
+ */
+ public List<P> decodeAll(List<M> messages, PiPipeconf pipeconf) {
+ checkNotNull(messages);
+ return messages.stream()
+ .map(m -> decodeOrNull(m, pipeconf))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Same as {@link #encodeAll(List, PiPipeconf)} but throws an exception if
+ * one or more of the given PI entities cannot be encoded. The returned list
+ * is guaranteed to have same size and order as the given one.
+ *
+ * @param piEntities list of PI entities
+ * @param pipeconf pipeconf
+ * @return list of protobuf messages
+ * @throws CodecException if one or more of the given PI entities cannot be
+ * encoded
+ */
+ public List<M> encodeAllOrFail(List<P> piEntities, PiPipeconf pipeconf)
+ throws CodecException {
+ final List<M> messages = encodeAll(piEntities, pipeconf);
+ if (piEntities.size() != messages.size()) {
+ throw new CodecException(format(
+ "Unable to encode %d entities of %d given " +
+ "(see previous logs for details)",
+ piEntities.size() - messages.size(), piEntities.size()));
+ }
+ return messages;
+ }
+
+ /**
+ * Same as {@link #decodeAll(List, PiPipeconf)} but throws an exception if
+ * one or more of the given protobuf messages cannot be decoded. The
+ * returned list is guaranteed to have same size and order as the given
+ * one.
+ *
+ * @param messages list of protobuf messages
+ * @param pipeconf pipeconf
+ * @return list of PI entities
+ * @throws CodecException if one or more of the given protobuf messages
+ * cannot be decoded
+ */
+ public List<P> decodeAllOrFail(List<M> messages, PiPipeconf pipeconf)
+ throws CodecException {
+ final List<P> piEntities = decodeAll(messages, pipeconf);
+ if (messages.size() != piEntities.size()) {
+ throw new CodecException(format(
+ "Unable to decode %d messages of %d given " +
+ "(see previous logs for details)",
+ messages.size() - piEntities.size(), messages.size()));
+ }
+ return piEntities;
+ }
+
+ private P4InfoBrowser browserOrFail(PiPipeconf pipeconf) throws CodecException {
+ final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+ if (browser == null) {
+ throw new CodecException(format(
+ "Unable to get P4InfoBrowser for pipeconf %s", pipeconf.id()));
+ }
+ return browser;
+ }
+}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileGroupCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileGroupCodec.java
new file mode 100644
index 0000000..684ef04
--- /dev/null
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileGroupCodec.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019-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.PiActionProfileId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiActionProfileGroup;
+import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
+import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
+import p4.v1.P4RuntimeOuterClass.ActionProfileGroup;
+
+/**
+ * Codec for P4Runtime ActionProfileGroup.
+ */
+final class ActionProfileGroupCodec
+ extends AbstractP4RuntimeCodec<PiActionProfileGroup, ActionProfileGroup> {
+
+ @Override
+ public ActionProfileGroup encode(
+ PiActionProfileGroup piGroup, PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException {
+
+ final int p4ActionProfileId = browser.actionProfiles()
+ .getByName(piGroup.actionProfile().id())
+ .getPreamble().getId();
+ final ActionProfileGroup.Builder msgBuilder = ActionProfileGroup.newBuilder()
+ .setGroupId(piGroup.id().id())
+ .setActionProfileId(p4ActionProfileId)
+ .setMaxSize(piGroup.maxSize());
+ piGroup.members().forEach(m -> {
+ // TODO: currently we don't set "watch" field
+ ActionProfileGroup.Member member = ActionProfileGroup.Member.newBuilder()
+ .setMemberId(m.id().id())
+ .setWeight(m.weight())
+ .build();
+ msgBuilder.addMembers(member);
+ });
+
+ return msgBuilder.build();
+ }
+
+ @Override
+ public PiActionProfileGroup decode(
+ ActionProfileGroup msg, PiPipeconf pipeconf, P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+
+ final PiActionProfileGroup.Builder piGroupBuilder = PiActionProfileGroup.builder()
+ .withActionProfileId(PiActionProfileId.of(
+ browser.actionProfiles()
+ .getById(msg.getActionProfileId())
+ .getPreamble().getName()))
+ .withId(PiActionProfileGroupId.of(msg.getGroupId()))
+ .withMaxSize(msg.getMaxSize());
+
+ msg.getMembersList().forEach(m -> {
+ int weight = m.getWeight();
+ if (weight < 1) {
+ // FIXME: currently PI has a bug which will always return weight 0
+ // ONOS won't accept group buckets with weight 0
+ log.warn("Decoding ActionProfileGroup with 'weight' " +
+ "field {}, will set to 1", weight);
+ weight = 1;
+ }
+ piGroupBuilder.addMember(PiActionProfileMemberId.of(
+ m.getMemberId()), weight);
+ });
+ return piGroupBuilder.build();
+ }
+}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileGroupEncoder.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileGroupEncoder.java
deleted file mode 100644
index 414a0e6..0000000
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileGroupEncoder.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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 com.google.common.collect.Maps;
-import org.onosproject.net.pi.model.PiActionProfileId;
-import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.runtime.PiActionProfileGroup;
-import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
-import p4.config.v1.P4InfoOuterClass;
-import p4.v1.P4RuntimeOuterClass.ActionProfileGroup;
-import p4.v1.P4RuntimeOuterClass.ActionProfileGroup.Member;
-import p4.v1.P4RuntimeOuterClass.ActionProfileMember;
-
-import java.util.Collection;
-import java.util.Map;
-
-import static java.lang.String.format;
-
-/**
- * Encoder/Decoder for action profile group.
- */
-final class ActionProfileGroupEncoder {
-
- private ActionProfileGroupEncoder() {
- // hide default constructor
- }
-
- /**
- * Encode a PI action profile group to a action profile group.
- *
- * @param piActionGroup the action profile group
- * @param pipeconf the pipeconf
- * @param maxMemberSize the max member size of action group
- * @return a action profile group encoded from PI action profile group
- * @throws P4InfoBrowser.NotFoundException if can't find action profile from
- * P4Info browser
- * @throws EncodeException if can't find P4Info from
- * pipeconf
- */
- static ActionProfileGroup encode(PiActionProfileGroup piActionGroup, PiPipeconf pipeconf, int maxMemberSize)
- throws P4InfoBrowser.NotFoundException, EncodeException {
- P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
-
- if (browser == null) {
- throw new EncodeException(format("Can't get P4 info browser from pipeconf %s", pipeconf));
- }
-
- PiActionProfileId piActionProfileId = piActionGroup.actionProfileId();
- P4InfoOuterClass.ActionProfile actionProfile = browser.actionProfiles()
- .getByName(piActionProfileId.id());
- int actionProfileId = actionProfile.getPreamble().getId();
- ActionProfileGroup.Builder actionProfileGroupBuilder = ActionProfileGroup.newBuilder()
- .setGroupId(piActionGroup.id().id())
- .setActionProfileId(actionProfileId);
-
- piActionGroup.members().forEach(m -> {
- // TODO: currently we don't set "watch" field of member
- Member member = Member.newBuilder()
- .setMemberId(m.id().id())
- .setWeight(m.weight())
- .build();
- actionProfileGroupBuilder.addMembers(member);
- });
-
- if (maxMemberSize > 0) {
- actionProfileGroupBuilder.setMaxSize(maxMemberSize);
- }
-
- return actionProfileGroupBuilder.build();
- }
-
- /**
- * Decode an action profile group with members information to a PI action
- * profile group.
- *
- * @param actionProfileGroup the action profile group
- * @param members members of the action profile group
- * @param pipeconf the pipeconf
- * @return decoded PI action profile group
- * @throws P4InfoBrowser.NotFoundException if can't find action profile from
- * P4Info browser
- * @throws EncodeException if can't find P4Info from
- * pipeconf
- */
- static PiActionProfileGroup decode(ActionProfileGroup actionProfileGroup,
- Collection<ActionProfileMember> members,
- PiPipeconf pipeconf)
- throws P4InfoBrowser.NotFoundException, EncodeException {
- P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
- if (browser == null) {
- throw new EncodeException(format("Can't get P4 info browser from pipeconf %s", pipeconf));
- }
- PiActionProfileGroup.Builder piActionGroupBuilder = PiActionProfileGroup.builder();
-
- P4InfoOuterClass.ActionProfile actionProfile = browser.actionProfiles()
- .getById(actionProfileGroup.getActionProfileId());
- PiActionProfileId piActionProfileId = PiActionProfileId.of(actionProfile.getPreamble().getName());
-
- piActionGroupBuilder
- .withActionProfileId(piActionProfileId)
- .withId(PiActionProfileGroupId.of(actionProfileGroup.getGroupId()));
-
- Map<Integer, Integer> memberWeights = Maps.newHashMap();
- actionProfileGroup.getMembersList().forEach(member -> {
- int weight = member.getWeight();
- if (weight < 1) {
- // FIXME: currently PI has a bug which will always return weight 0
- // ONOS won't accept group buckets with weight 0
- weight = 1;
- }
- memberWeights.put(member.getMemberId(), weight);
- });
-
- for (ActionProfileMember member : members) {
- if (!memberWeights.containsKey(member.getMemberId())) {
- // Not a member of this group, ignore.
- continue;
- }
- int weight = memberWeights.get(member.getMemberId());
- piActionGroupBuilder.addMember(ActionProfileMemberEncoder.decode(member, weight, pipeconf));
- }
-
- return piActionGroupBuilder.build();
- }
-}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileMemberCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileMemberCodec.java
new file mode 100644
index 0000000..9567b0a
--- /dev/null
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileMemberCodec.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2019-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.PiActionProfileId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiActionProfileMember;
+import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
+import p4.config.v1.P4InfoOuterClass;
+import p4.v1.P4RuntimeOuterClass;
+import p4.v1.P4RuntimeOuterClass.ActionProfileMember;
+
+import static org.onosproject.p4runtime.ctl.TableEntryEncoder.decodeActionMsg;
+import static org.onosproject.p4runtime.ctl.TableEntryEncoder.encodePiAction;
+/**
+ * Codec for P4Runtime ActionProfileMember.
+ */
+final class ActionProfileMemberCodec
+ extends AbstractP4RuntimeCodec<PiActionProfileMember, ActionProfileMember> {
+
+ @Override
+ public ActionProfileMember encode(PiActionProfileMember piEntity,
+ PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ final ActionProfileMember.Builder actionProfileMemberBuilder =
+ ActionProfileMember.newBuilder();
+ // Member ID
+ actionProfileMemberBuilder.setMemberId(piEntity.id().id());
+ // Action profile ID
+ P4InfoOuterClass.ActionProfile actionProfile =
+ browser.actionProfiles().getByName(piEntity.actionProfile().id());
+ final int actionProfileId = actionProfile.getPreamble().getId();
+ actionProfileMemberBuilder.setActionProfileId(actionProfileId);
+ // Action
+ final P4RuntimeOuterClass.Action action = encodePiAction(piEntity.action(), browser);
+ actionProfileMemberBuilder.setAction(action);
+ return actionProfileMemberBuilder.build();
+ }
+
+ @Override
+ public PiActionProfileMember decode(ActionProfileMember message,
+ PiPipeconf pipeconf,
+ P4InfoBrowser browser)
+ throws CodecException, P4InfoBrowser.NotFoundException {
+ final PiActionProfileId actionProfileId = PiActionProfileId.of(
+ browser.actionProfiles()
+ .getById(message.getActionProfileId())
+ .getPreamble()
+ .getName());
+ return PiActionProfileMember.builder()
+ .forActionProfile(actionProfileId)
+ .withId(PiActionProfileMemberId.of(message.getMemberId()))
+ .withAction(decodeActionMsg(message.getAction(), browser))
+ .build();
+ }
+}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileMemberEncoder.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileMemberEncoder.java
deleted file mode 100644
index 30dd43c..0000000
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/ActionProfileMemberEncoder.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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.PiActionProfileId;
-import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.runtime.PiActionProfileMember;
-import org.onosproject.net.pi.runtime.PiActionProfileMemberId;
-import p4.config.v1.P4InfoOuterClass;
-import p4.v1.P4RuntimeOuterClass;
-import p4.v1.P4RuntimeOuterClass.ActionProfileMember;
-
-import static java.lang.String.format;
-import static org.onosproject.p4runtime.ctl.TableEntryEncoder.decodeActionMsg;
-import static org.onosproject.p4runtime.ctl.TableEntryEncoder.encodePiAction;
-
-/**
- * Encoder/Decoder of action profile member.
- */
-final class ActionProfileMemberEncoder {
- private ActionProfileMemberEncoder() {
- // Hide default constructor
- }
-
- /**
- * Encode a PiActionProfileMember to a ActionProfileMember.
- *
- * @param member the member to encode
- * @param pipeconf the pipeconf, as encode spec
- * @return encoded member
- * @throws P4InfoBrowser.NotFoundException can't find action profile from
- * P4Info browser
- * @throws EncodeException can't find P4Info from pipeconf
- */
- static ActionProfileMember encode(PiActionProfileMember member,
- PiPipeconf pipeconf)
- throws P4InfoBrowser.NotFoundException, EncodeException {
-
- P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
-
- if (browser == null) {
- throw new EncodeException(format("Can't get P4 info browser from pipeconf %s", pipeconf));
- }
-
- ActionProfileMember.Builder actionProfileMemberBuilder =
- ActionProfileMember.newBuilder();
-
- // member id
- actionProfileMemberBuilder.setMemberId(member.id().id());
-
- // action profile id
- P4InfoOuterClass.ActionProfile actionProfile =
- browser.actionProfiles().getByName(member.actionProfile().id());
-
- int actionProfileId = actionProfile.getPreamble().getId();
- actionProfileMemberBuilder.setActionProfileId(actionProfileId);
-
- // Action
- P4RuntimeOuterClass.Action action = encodePiAction(member.action(), browser);
- actionProfileMemberBuilder.setAction(action);
-
- return actionProfileMemberBuilder.build();
- }
-
- /**
- * Decode an action profile member to PI action profile member.
- *
- * @param member the action profile member
- * @param weight the weight of the member
- * @param pipeconf the pipeconf, as decode spec
- * @return decoded PI action profile member
- * @throws P4InfoBrowser.NotFoundException can't find definition of action
- * from P4 info
- * @throws EncodeException can't get P4 info browser from
- * pipeconf
- */
- static PiActionProfileMember decode(ActionProfileMember member,
- int weight,
- PiPipeconf pipeconf)
- throws P4InfoBrowser.NotFoundException, EncodeException {
- P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
- if (browser == null) {
- throw new EncodeException(format("Can't get P4 info browser from pipeconf %s", pipeconf));
- }
-
- final PiActionProfileId actionProfileId = PiActionProfileId.of(
- browser.actionProfiles()
- .getById(member.getActionProfileId())
- .getPreamble()
- .getName());
-
- return PiActionProfileMember.builder()
- .forActionProfile(actionProfileId)
- .withId(PiActionProfileMemberId.of(member.getMemberId()))
- .withWeight(weight)
- .withAction(decodeActionMsg(member.getAction(), browser))
- .build();
- }
-}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/EncodeException.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CodecException.java
similarity index 65%
rename from protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/EncodeException.java
rename to protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CodecException.java
index 51abbb5..89d5510 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/EncodeException.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CodecException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-present Open Networking Foundation
+ * Copyright 2019-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.
@@ -17,11 +17,15 @@
package org.onosproject.p4runtime.ctl;
/**
- * Signals that the proto message cannot be build.
+ * Signals an error during encoding/decoding of a PI entity/protobuf message.
*/
-final class EncodeException extends Exception {
+public final class CodecException extends Exception {
- EncodeException(String explanation) {
+ /**
+ * Ceeates anew exception with the given explanation message.
+ * @param explanation explanation
+ */
+ public CodecException(String explanation) {
super(explanation);
}
}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
index 6c29062..3765d24 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
@@ -80,7 +80,7 @@
.map(cellId -> {
try {
return encodePiCounterCellId(cellId, pipeconf, browser);
- } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+ } catch (P4InfoBrowser.NotFoundException | CodecException e) {
log.warn("Unable to encode PI counter cell id: {}", e.getMessage());
return null;
}
@@ -114,7 +114,7 @@
.map(counterId -> {
try {
return readAllCellsEntity(counterId, pipeconf, browser);
- } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+ } catch (P4InfoBrowser.NotFoundException | CodecException e) {
log.warn("Unable to encode counter ID to read-all-cells entity: {}",
e.getMessage());
return null;
@@ -152,7 +152,7 @@
.map(entity -> {
try {
return decodeCounterEntity(entity, pipeconf, browser);
- } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
+ } catch (CodecException | P4InfoBrowser.NotFoundException e) {
log.warn("Unable to decode counter entity message: {}",
e.getMessage());
return null;
@@ -165,7 +165,7 @@
private static Entity encodePiCounterCellId(PiCounterCellId cellId,
PiPipeconf pipeconf,
P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
int counterId;
Entity entity;
@@ -193,7 +193,7 @@
.build();
break;
default:
- throw new EncodeException(format(
+ throw new CodecException(format(
"Unrecognized PI counter cell ID type '%s'",
cellId.counterType()));
}
@@ -204,10 +204,10 @@
private static Entity readAllCellsEntity(PiCounterId counterId,
PiPipeconf pipeconf,
P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
if (!pipeconf.pipelineModel().counter(counterId).isPresent()) {
- throw new EncodeException(format(
+ throw new CodecException(format(
"not such counter '%s' in pipeline model", counterId));
}
final PiCounterType counterType = pipeconf.pipelineModel()
@@ -228,7 +228,7 @@
final PiTableId tableId = pipeconf.pipelineModel()
.counter(counterId).get().table();
if (tableId == null) {
- throw new EncodeException(format(
+ throw new CodecException(format(
"null table for direct counter '%s'", counterId));
}
final int p4TableId = browser.tables().getByName(tableId.id())
@@ -243,7 +243,7 @@
.build())
.build();
default:
- throw new EncodeException(format(
+ throw new CodecException(format(
"unrecognized PI counter type '%s'", counterType));
}
}
@@ -251,7 +251,7 @@
private static PiCounterCell decodeCounterEntity(Entity entity,
PiPipeconf pipeconf,
P4InfoBrowser browser)
- throws EncodeException, P4InfoBrowser.NotFoundException {
+ throws CodecException, P4InfoBrowser.NotFoundException {
CounterData counterData;
PiCounterCellId piCellId;
@@ -271,7 +271,7 @@
piCellId = PiCounterCellId.ofDirect(piTableEntry);
counterData = entity.getDirectCounterEntry().getData();
} else {
- throw new EncodeException(format(
+ throw new CodecException(format(
"Unrecognized entity type '%s' in P4Runtime message",
entity.getEntityCase().name()));
}
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
index a825488..44f27d1 100644
--- 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
@@ -80,7 +80,7 @@
.map(cellConfig -> {
try {
return encodePiMeterCellConfig(cellConfig, pipeconf, browser);
- } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+ } catch (P4InfoBrowser.NotFoundException | CodecException e) {
log.warn("Unable to encode PI meter cell id: {}", e.getMessage());
log.debug("exception", e);
return null;
@@ -115,7 +115,7 @@
.map(meterId -> {
try {
return readAllCellsEntity(meterId, pipeconf, browser);
- } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+ } catch (P4InfoBrowser.NotFoundException | CodecException e) {
log.warn("Unable to encode meter ID to read-all-cells entity: {}",
e.getMessage());
return null;
@@ -152,7 +152,7 @@
.map(entity -> {
try {
return decodeMeterEntity(entity, pipeconf, browser);
- } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
+ } catch (CodecException | P4InfoBrowser.NotFoundException e) {
log.warn("Unable to decode meter entity message: {}", e.getMessage());
return null;
}
@@ -164,7 +164,7 @@
private static Entity encodePiMeterCellConfig(PiMeterCellConfig config,
PiPipeconf pipeconf,
P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
int meterId;
Entity entity;
@@ -198,7 +198,7 @@
// When reading meter cells.
meterConfig = null;
} else {
- throw new EncodeException("number of meter bands should be either 2 or 0");
+ throw new CodecException("number of meter bands should be either 2 or 0");
}
switch (config.cellId().meterType()) {
@@ -226,8 +226,8 @@
.setDirectMeterEntry(dirEntryBuilder.build()).build();
break;
default:
- throw new EncodeException(format("unrecognized PI meter type '%s'",
- config.cellId().meterType()));
+ throw new CodecException(format("unrecognized PI meter type '%s'",
+ config.cellId().meterType()));
}
return entity;
@@ -236,10 +236,10 @@
private static Entity readAllCellsEntity(PiMeterId meterId,
PiPipeconf pipeconf,
P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
if (!pipeconf.pipelineModel().meter(meterId).isPresent()) {
- throw new EncodeException(format(
+ throw new CodecException(format(
"not such meter '%s' in pipeline model", meterId));
}
final PiMeterType meterType = pipeconf.pipelineModel()
@@ -260,7 +260,7 @@
final PiTableId tableId = pipeconf.pipelineModel()
.meter(meterId).get().table();
if (tableId == null) {
- throw new EncodeException(format(
+ throw new CodecException(format(
"null table for direct meter '%s'", meterId));
}
final int p4TableId = browser.tables().getByName(tableId.id())
@@ -275,7 +275,7 @@
.build())
.build();
default:
- throw new EncodeException(format(
+ throw new CodecException(format(
"unrecognized PI meter type '%s'", meterType));
}
}
@@ -283,7 +283,7 @@
private static PiMeterCellConfig decodeMeterEntity(Entity entity,
PiPipeconf pipeconf,
P4InfoBrowser browser)
- throws EncodeException, P4InfoBrowser.NotFoundException {
+ throws CodecException, P4InfoBrowser.NotFoundException {
MeterConfig meterConfig;
PiMeterCellId piCellId;
@@ -304,7 +304,7 @@
piCellId = PiMeterCellId.ofDirect(piTableEntry);
meterConfig = entity.getDirectMeterEntry().getConfig();
} else {
- throw new EncodeException(format(
+ throw new CodecException(format(
"unrecognized entity type '%s' in P4Runtime message",
entity.getEntityCase().name()));
}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MulticastGroupEntryCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MulticastGroupEntryCodec.java
index f2ececb..5f55c1f 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MulticastGroupEntryCodec.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MulticastGroupEntryCodec.java
@@ -40,9 +40,9 @@
*
* @param piEntry PiMulticastGroupEntry
* @return P4Runtime MulticastGroupEntry message
- * @throws EncodeException if the PiMulticastGroupEntry cannot be encoded.
+ * @throws CodecException if the PiMulticastGroupEntry cannot be encoded.
*/
- static MulticastGroupEntry encode(PiMulticastGroupEntry piEntry) throws EncodeException {
+ static MulticastGroupEntry encode(PiMulticastGroupEntry piEntry) throws CodecException {
final MulticastGroupEntry.Builder msgBuilder = MulticastGroupEntry.newBuilder();
msgBuilder.setMulticastGroupId(piEntry.groupId());
for (PiPreReplica replica : piEntry.replicas()) {
@@ -50,7 +50,7 @@
try {
p4PortId = Math.toIntExact(replica.egressPort().toLong());
} catch (ArithmeticException e) {
- throw new EncodeException(format(
+ throw new CodecException(format(
"Cannot cast 64bit port value '%s' to 32bit",
replica.egressPort()));
}
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 78ea9d9..6ac478f 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
@@ -16,10 +16,8 @@
package org.onosproject.p4runtime.ctl;
-import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
@@ -86,14 +84,21 @@
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
+import java.util.stream.Stream;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static org.onosproject.p4runtime.ctl.P4RuntimeCodecs.CODECS;
import static p4.v1.P4RuntimeOuterClass.Entity.EntityCase.ACTION_PROFILE_GROUP;
import static p4.v1.P4RuntimeOuterClass.Entity.EntityCase.ACTION_PROFILE_MEMBER;
+import static p4.v1.P4RuntimeOuterClass.Entity.EntityCase.COUNTER_ENTRY;
+import static p4.v1.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_COUNTER_ENTRY;
+import static p4.v1.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_METER_ENTRY;
+import static p4.v1.P4RuntimeOuterClass.Entity.EntityCase.METER_ENTRY;
import static p4.v1.P4RuntimeOuterClass.Entity.EntityCase.PACKET_REPLICATION_ENGINE_ENTRY;
import static p4.v1.P4RuntimeOuterClass.Entity.EntityCase.TABLE_ENTRY;
import static p4.v1.P4RuntimeOuterClass.PacketIn;
@@ -106,10 +111,13 @@
*/
final class P4RuntimeClientImpl extends AbstractGrpcClient implements P4RuntimeClient {
+ private static final String MISSING_P4INFO_BROWSER = "Unable to get a P4Info browser for pipeconf {}";
+
private static final Metadata.Key<com.google.rpc.Status> STATUS_DETAILS_KEY =
- Metadata.Key.of("grpc-status-details-bin",
- ProtoLiteUtils.metadataMarshaller(
- com.google.rpc.Status.getDefaultInstance()));
+ Metadata.Key.of(
+ "grpc-status-details-bin",
+ ProtoLiteUtils.metadataMarshaller(
+ com.google.rpc.Status.getDefaultInstance()));
private static final Map<WriteOperationType, Update.Type> UPDATE_TYPES = ImmutableMap.of(
WriteOperationType.UNSPECIFIED, Update.Type.UNSPECIFIED,
@@ -229,9 +237,8 @@
@Override
public CompletableFuture<Boolean> writeActionProfileGroup(PiActionProfileGroup group,
WriteOperationType opType,
- PiPipeconf pipeconf,
- int maxMemberSize) {
- return supplyInContext(() -> doWriteActionProfileGroup(group, opType, pipeconf, maxMemberSize),
+ PiPipeconf pipeconf) {
+ return supplyInContext(() -> doWriteActionProfileGroup(group, opType, pipeconf),
"writeActionProfileGroup-" + opType.name());
}
@@ -243,10 +250,10 @@
}
@Override
- public CompletableFuture<List<PiActionProfileMemberId>> dumpActionProfileMemberIds(
+ public CompletableFuture<List<PiActionProfileMember>> dumpActionProfileMembers(
PiActionProfileId actionProfileId, PiPipeconf pipeconf) {
- return supplyInContext(() -> doDumpActionProfileMemberIds(actionProfileId, pipeconf),
- "dumpActionProfileMemberIds-" + actionProfileId.id());
+ return supplyInContext(() -> doDumpActionProfileMembers(actionProfileId, pipeconf),
+ "dumpActionProfileMembers-" + actionProfileId.id());
}
@Override
@@ -484,8 +491,8 @@
.build())
.setType(UPDATE_TYPES.get(opType))
.build())
- .collect(Collectors.toList());
- } catch (EncodeException e) {
+ .collect(toList());
+ } catch (CodecException e) {
log.error("Unable to encode table entries, aborting {} operation: {}",
opType.name(), e.getMessage());
return false;
@@ -507,7 +514,7 @@
} else {
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
- log.warn("Unable to get a P4Info browser for pipeconf {}", pipeconf);
+ log.error(MISSING_P4INFO_BROWSER, pipeconf);
return Collections.emptyList();
}
piTableIds.forEach(piTableId -> {
@@ -523,36 +530,18 @@
return Collections.emptyList();
}
- ReadRequest.Builder requestMsgBuilder = ReadRequest.newBuilder()
- .setDeviceId(p4DeviceId);
- tableIds.forEach(tableId -> requestMsgBuilder.addEntities(
- Entity.newBuilder()
- .setTableEntry(
- TableEntry.newBuilder()
- .setTableId(tableId)
- .setIsDefaultAction(defaultEntries)
- .setCounterData(P4RuntimeOuterClass.CounterData.getDefaultInstance())
- .build())
+ final List<Entity> entities = tableIds.stream()
+ .map(tableId -> TableEntry.newBuilder()
+ .setTableId(tableId)
+ .setIsDefaultAction(defaultEntries)
+ .setCounterData(P4RuntimeOuterClass.CounterData.getDefaultInstance())
.build())
- .build());
+ .map(e -> Entity.newBuilder().setTableEntry(e).build())
+ .collect(toList());
- Iterator<ReadResponse> responses;
- try {
- responses = blockingStub.read(requestMsgBuilder.build());
- } catch (StatusRuntimeException e) {
- checkGrpcException(e);
- log.warn("Unable to dump tables from {}: {}", deviceId, e.getMessage());
- return Collections.emptyList();
- }
-
- Iterable<ReadResponse> responseIterable = () -> responses;
- List<TableEntry> tableEntryMsgs = StreamSupport
- .stream(responseIterable.spliterator(), false)
- .map(ReadResponse::getEntitiesList)
- .flatMap(List::stream)
- .filter(entity -> entity.getEntityCase() == TABLE_ENTRY)
+ final List<TableEntry> tableEntryMsgs = blockingRead(entities, TABLE_ENTRY)
.map(Entity::getTableEntry)
- .collect(Collectors.toList());
+ .collect(toList());
log.debug("Retrieved {} entries from {} tables on {}...",
tableEntryMsgs.size(), tableIds.size(), deviceId);
@@ -641,61 +630,32 @@
private List<PiCounterCell> doReadCounterEntities(
List<Entity> counterEntities, PiPipeconf pipeconf) {
- if (counterEntities.size() == 0) {
- return Collections.emptyList();
- }
-
- final ReadRequest request = ReadRequest.newBuilder()
- .setDeviceId(p4DeviceId)
- .addAllEntities(counterEntities)
- .build();
-
- final Iterable<ReadResponse> responses;
- try {
- responses = () -> blockingStub.read(request);
- } catch (StatusRuntimeException e) {
- checkGrpcException(e);
- log.warn("Unable to read counter cells from {}: {}", deviceId, e.getMessage());
- return Collections.emptyList();
- }
-
- List<Entity> entities = StreamSupport.stream(responses.spliterator(), false)
- .map(ReadResponse::getEntitiesList)
- .flatMap(List::stream)
- .collect(Collectors.toList());
+ final List<Entity> entities = blockingRead(
+ counterEntities, COUNTER_ENTRY, DIRECT_COUNTER_ENTRY)
+ .collect(toList());
return CounterEntryCodec.decodeCounterEntities(entities, pipeconf);
}
private boolean doWriteActionProfileMembers(List<PiActionProfileMember> members,
WriteOperationType opType, PiPipeconf pipeconf) {
- final List<ActionProfileMember> actionProfileMembers = Lists.newArrayList();
-
- for (PiActionProfileMember member : members) {
- try {
- actionProfileMembers.add(ActionProfileMemberEncoder.encode(member, pipeconf));
- } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
- log.warn("Unable to encode action profile member, aborting {} operation: {} [{}]",
- opType.name(), e.getMessage(), member.toString());
- return false;
- }
+ final List<ActionProfileMember> actionProfileMembers;
+ try {
+ actionProfileMembers = CODECS.actionProfileMember()
+ .encodeAllOrFail(members, pipeconf);
+ } catch (CodecException e) {
+ log.warn("Unable to {} action profile members: {}",
+ opType.name(), e.getMessage());
+ return false;
}
-
final List<Update> updateMsgs = actionProfileMembers.stream()
- .map(actionProfileMember ->
- Update.newBuilder()
- .setEntity(Entity.newBuilder()
- .setActionProfileMember(actionProfileMember)
- .build())
- .setType(UPDATE_TYPES.get(opType))
- .build())
- .collect(Collectors.toList());
-
- if (updateMsgs.size() == 0) {
- // Nothing to update.
- return true;
- }
-
+ .map(m -> Update.newBuilder()
+ .setEntity(Entity.newBuilder()
+ .setActionProfileMember(m)
+ .build())
+ .setType(UPDATE_TYPES.get(opType))
+ .build())
+ .collect(toList());
return write(updateMsgs, members, opType, "action profile member");
}
@@ -705,7 +665,7 @@
final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
- log.warn("Unable to get a P4Info browser for pipeconf {}, aborting dump action profile", pipeconf);
+ log.warn(MISSING_P4INFO_BROWSER, pipeconf);
return Collections.emptyList();
}
@@ -721,117 +681,29 @@
return Collections.emptyList();
}
- // Prepare read request to read all groups from the given action profile.
- final ReadRequest groupRequestMsg = ReadRequest.newBuilder()
- .setDeviceId(p4DeviceId)
- .addEntities(Entity.newBuilder()
- .setActionProfileGroup(
- ActionProfileGroup.newBuilder()
- .setActionProfileId(actionProfileId)
- .build())
- .build())
+ // Read all groups from the given action profile.
+ final Entity entityToRead = Entity.newBuilder()
+ .setActionProfileGroup(
+ ActionProfileGroup.newBuilder()
+ .setActionProfileId(actionProfileId)
+ .build())
.build();
-
- // Read groups.
- final Iterator<ReadResponse> groupResponses;
- try {
- groupResponses = blockingStub.read(groupRequestMsg);
- } catch (StatusRuntimeException e) {
- checkGrpcException(e);
- log.warn("Unable to dump action profile {} from {}: {}", piActionProfileId, deviceId, e.getMessage());
- return Collections.emptyList();
- }
-
- final List<ActionProfileGroup> groupMsgs = Tools.stream(() -> groupResponses)
- .map(ReadResponse::getEntitiesList)
- .flatMap(List::stream)
- .filter(entity -> entity.getEntityCase() == ACTION_PROFILE_GROUP)
+ final List<ActionProfileGroup> groupMsgs = blockingRead(entityToRead, ACTION_PROFILE_GROUP)
.map(Entity::getActionProfileGroup)
- .collect(Collectors.toList());
+ .collect(toList());
log.debug("Retrieved {} groups from action profile {} on {}...",
groupMsgs.size(), piActionProfileId.id(), deviceId);
- // Returned groups contain only a minimal description of their members.
- // We need to issue a new request to get the full description of each member.
-
- // Keep a map of all member IDs for each group ID, will need it later.
- final Multimap<Integer, Integer> groupIdToMemberIdsMap = HashMultimap.create();
- groupMsgs.forEach(g -> groupIdToMemberIdsMap.putAll(
- g.getGroupId(),
- g.getMembersList().stream()
- .map(ActionProfileGroup.Member::getMemberId)
- .collect(Collectors.toList())));
-
- // Prepare one big read request to read all members in one shot.
- final Set<Entity> entityMsgs = groupMsgs.stream()
- .flatMap(g -> g.getMembersList().stream())
- .map(ActionProfileGroup.Member::getMemberId)
- // Prevent issuing many read requests for the same member.
- .distinct()
- .map(id -> ActionProfileMember.newBuilder()
- .setActionProfileId(actionProfileId)
- .setMemberId(id)
- .build())
- .map(m -> Entity.newBuilder()
- .setActionProfileMember(m)
- .build())
- .collect(Collectors.toSet());
- final ReadRequest memberRequestMsg = ReadRequest.newBuilder().setDeviceId(p4DeviceId)
- .addAllEntities(entityMsgs)
- .build();
-
- // Read members.
- final Iterator<ReadResponse> memberResponses;
- try {
- memberResponses = blockingStub.read(memberRequestMsg);
- } catch (StatusRuntimeException e) {
- checkGrpcException(e);
- log.warn("Unable to read members of action profile {} from {}: {}",
- piActionProfileId, deviceId, e.getMessage());
- return Collections.emptyList();
- }
-
- final Multimap<Integer, ActionProfileMember> groupIdToMembersMap = HashMultimap.create();
- Tools.stream(() -> memberResponses)
- .map(ReadResponse::getEntitiesList)
- .flatMap(List::stream)
- .filter(e -> e.getEntityCase() == ACTION_PROFILE_MEMBER)
- .map(Entity::getActionProfileMember)
- .forEach(member -> groupIdToMemberIdsMap.asMap()
- // Get all group IDs that contain this member.
- .entrySet()
- .stream()
- .filter(entry -> entry.getValue().contains(member.getMemberId()))
- .map(Map.Entry::getKey)
- .forEach(gid -> groupIdToMembersMap.put(gid, member)));
-
- log.debug("Retrieved {} members from action profile {} on {}...",
- groupIdToMembersMap.size(), piActionProfileId.id(), deviceId);
-
- return groupMsgs.stream()
- .map(groupMsg -> {
- try {
- return ActionProfileGroupEncoder.decode(groupMsg,
- groupIdToMembersMap.get(groupMsg.getGroupId()),
- pipeconf);
- } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
- log.warn("Unable to decode group: {}\n {}", e.getMessage(), groupMsg);
- return null;
- }
- })
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
+ return CODECS.actionProfileGroup().decodeAll(groupMsgs, pipeconf);
}
- private List<PiActionProfileMemberId> doDumpActionProfileMemberIds(
+ private List<PiActionProfileMember> doDumpActionProfileMembers(
PiActionProfileId actionProfileId, PiPipeconf pipeconf) {
final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
- log.warn("Unable to get a P4Info browser for pipeconf {}, " +
- "aborting cleanup of action profile members",
- pipeconf);
+ log.error(MISSING_P4INFO_BROWSER, pipeconf);
return Collections.emptyList();
}
@@ -843,40 +715,24 @@
.getPreamble()
.getId();
} catch (P4InfoBrowser.NotFoundException e) {
- log.warn("Unable to cleanup action profile members: {}", e.getMessage());
+ log.warn("Unable to dump action profile members: {}", e.getMessage());
return Collections.emptyList();
}
- final ReadRequest memberRequestMsg = ReadRequest.newBuilder()
- .setDeviceId(p4DeviceId)
- .addEntities(Entity.newBuilder().setActionProfileMember(
+ Entity entityToRead = Entity.newBuilder()
+ .setActionProfileMember(
ActionProfileMember.newBuilder()
.setActionProfileId(p4ActProfId)
- .build()).build())
+ .build())
.build();
-
- // Read members.
- final Iterator<ReadResponse> memberResponses;
- try {
- memberResponses = blockingStub.read(memberRequestMsg);
- } catch (StatusRuntimeException e) {
- checkGrpcException(e);
- log.warn("Unable to read members of action profile {} from {}: {}",
- actionProfileId, deviceId, e.getMessage());
- return Collections.emptyList();
- }
-
- return Tools.stream(() -> memberResponses)
- .map(ReadResponse::getEntitiesList)
- .flatMap(List::stream)
- .filter(e -> e.getEntityCase() == ACTION_PROFILE_MEMBER)
+ final List<ActionProfileMember> memberMsgs = blockingRead(entityToRead, ACTION_PROFILE_MEMBER)
.map(Entity::getActionProfileMember)
- // Perhaps not needed, but better to double check to avoid
- // removing members of other groups.
- .filter(m -> m.getActionProfileId() == p4ActProfId)
- .map(ActionProfileMember::getMemberId)
- .map(PiActionProfileMemberId::of)
- .collect(Collectors.toList());
+ .collect(toList());
+
+ log.debug("Retrieved {} members from action profile {} on {}...",
+ memberMsgs.size(), actionProfileId.id(), deviceId);
+
+ return CODECS.actionProfileMember().decodeAll(memberMsgs, pipeconf);
}
private List<PiActionProfileMemberId> doRemoveActionProfileMembers(
@@ -890,9 +746,7 @@
final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
- log.warn("Unable to get a P4Info browser for pipeconf {}, " +
- "aborting cleanup of action profile members",
- pipeconf);
+ log.error(MISSING_P4INFO_BROWSER, pipeconf);
return Collections.emptyList();
}
@@ -912,7 +766,7 @@
.map(m -> Entity.newBuilder().setActionProfileMember(m).build())
.map(e -> Update.newBuilder().setEntity(e)
.setType(Update.Type.DELETE).build())
- .collect(Collectors.toList());
+ .collect(toList());
log.debug("Removing {} members of action profile '{}'...",
memberIds.size(), actionProfileId);
@@ -923,23 +777,19 @@
}
private boolean doWriteActionProfileGroup(
- PiActionProfileGroup group, WriteOperationType opType, PiPipeconf pipeconf,
- int maxMemberSize) {
- final ActionProfileGroup actionProfileGroup;
- if (opType == P4RuntimeClient.WriteOperationType.INSERT && maxMemberSize < group.members().size()) {
- log.warn("Unable to encode group, since group member larger than maximum member size");
- return false;
- }
+ PiActionProfileGroup group, WriteOperationType opType, PiPipeconf pipeconf) {
+ final ActionProfileGroup groupMsg;
try {
- actionProfileGroup = ActionProfileGroupEncoder.encode(group, pipeconf, maxMemberSize);
- } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
- log.warn("Unable to encode group, aborting {} operation: {}", e.getMessage(), opType.name());
+ groupMsg = CODECS.actionProfileGroup().encode(group, pipeconf);
+ } catch (CodecException e) {
+ log.warn("Unable to encode group, aborting {} operation: {}",
+ opType.name(), e.getMessage());
return false;
}
final Update updateMsg = Update.newBuilder()
.setEntity(Entity.newBuilder()
- .setActionProfileGroup(actionProfileGroup)
+ .setActionProfileGroup(groupMsg)
.build())
.setType(UPDATE_TYPES.get(opType))
.build();
@@ -961,7 +811,7 @@
.map(cellId -> PiMeterCellConfig.builder()
.withMeterCellId(cellId)
.build())
- .collect(Collectors.toList());
+ .collect(toList());
return doReadMeterEntities(MeterEntryCodec.encodePiMeterCellConfigs(
piMeterCellConfigs, pipeconf), pipeconf);
@@ -970,30 +820,9 @@
private List<PiMeterCellConfig> doReadMeterEntities(
List<Entity> entitiesToRead, PiPipeconf pipeconf) {
- if (entitiesToRead.size() == 0) {
- return Collections.emptyList();
- }
-
- final ReadRequest request = ReadRequest.newBuilder()
- .setDeviceId(p4DeviceId)
- .addAllEntities(entitiesToRead)
- .build();
-
- final Iterable<ReadResponse> responses;
- try {
- responses = () -> blockingStub.read(request);
- } catch (StatusRuntimeException e) {
- checkGrpcException(e);
- log.warn("Unable to read meter cells: {}", e.getMessage());
- log.debug("exception", e);
- return Collections.emptyList();
- }
-
- List<Entity> responseEntities = StreamSupport
- .stream(responses.spliterator(), false)
- .map(ReadResponse::getEntitiesList)
- .flatMap(List::stream)
- .collect(Collectors.toList());
+ final List<Entity> responseEntities = blockingRead(
+ entitiesToRead, METER_ENTRY, DIRECT_METER_ENTRY)
+ .collect(toList());
return MeterEntryCodec.decodeMeterEntities(responseEntities, pipeconf);
}
@@ -1007,7 +836,7 @@
.setEntity(meterEntryMsg)
.setType(UPDATE_TYPES.get(WriteOperationType.MODIFY))
.build())
- .collect(Collectors.toList());
+ .collect(toList());
if (updateMsgs.size() == 0) {
return true;
@@ -1024,7 +853,7 @@
.map(piEntry -> {
try {
return MulticastGroupEntryCodec.encode(piEntry);
- } catch (EncodeException e) {
+ } catch (CodecException e) {
log.warn("Unable to encode PiMulticastGroupEntry: {}", e.getMessage());
return null;
}
@@ -1040,7 +869,7 @@
.setEntity(entityMsg)
.setType(UPDATE_TYPES.get(opType))
.build())
- .collect(Collectors.toList());
+ .collect(toList());
return write(updateMsgs, entries, opType, "multicast group entry");
}
@@ -1055,32 +884,12 @@
.build())
.build();
- final ReadRequest req = ReadRequest.newBuilder()
- .setDeviceId(p4DeviceId)
- .addEntities(entity)
- .build();
-
- Iterator<ReadResponse> responses;
- try {
- responses = blockingStub.read(req);
- } catch (StatusRuntimeException e) {
- checkGrpcException(e);
- log.warn("Unable to read multicast group entries from {}: {}", deviceId, e.getMessage());
- return Collections.emptyList();
- }
-
- Iterable<ReadResponse> responseIterable = () -> responses;
- final List<PiMulticastGroupEntry> mcEntries = StreamSupport
- .stream(responseIterable.spliterator(), false)
- .map(ReadResponse::getEntitiesList)
- .flatMap(List::stream)
- .filter(e -> e.getEntityCase()
- .equals(PACKET_REPLICATION_ENGINE_ENTRY))
+ final List<PiMulticastGroupEntry> mcEntries = blockingRead(entity, PACKET_REPLICATION_ENGINE_ENTRY)
.map(Entity::getPacketReplicationEngineEntry)
.filter(e -> e.getTypeCase().equals(MULTICAST_GROUP_ENTRY))
.map(PacketReplicationEngineEntry::getMulticastGroupEntry)
.map(MulticastGroupEntryCodec::decode)
- .collect(Collectors.toList());
+ .collect(toList());
log.debug("Retrieved {} multicast group entries from {}...",
mcEntries.size(), deviceId);
@@ -1126,6 +935,46 @@
.build();
}
+ private Stream<Entity> blockingRead(Entity entity, Entity.EntityCase entityCase) {
+ return blockingRead(singletonList(entity), entityCase);
+ }
+
+ private Stream<Entity> blockingRead(Iterable<Entity> entities,
+ Entity.EntityCase... entityCases) {
+ // Build read request making sure we are reading what declared.
+ final ReadRequest.Builder reqBuilder = ReadRequest.newBuilder()
+ .setDeviceId(p4DeviceId);
+ final Set<Entity.EntityCase> entityCaseSet = Sets.newHashSet(entityCases);
+ for (Entity e : entities) {
+ checkArgument(entityCaseSet.contains(e.getEntityCase()),
+ "Entity case mismatch");
+ reqBuilder.addEntities(e);
+ }
+ final ReadRequest readRequest = reqBuilder.build();
+ if (readRequest.getEntitiesCount() == 0) {
+ return Stream.empty();
+ }
+ // Issue read.
+ final Iterator<ReadResponse> responseIterator;
+ try {
+ responseIterator = blockingStub.read(readRequest);
+ } catch (StatusRuntimeException e) {
+ checkGrpcException(e);
+ final String caseString = entityCaseSet.stream()
+ .map(Entity.EntityCase::name)
+ .collect(joining("/"));
+ log.warn("Unable to read {} from {}: {}",
+ caseString, deviceId, e.getMessage());
+ log.debug("Exception during read", e);
+ return Stream.empty();
+ }
+ // Filter results.
+ return Tools.stream(() -> responseIterator)
+ .map(ReadResponse::getEntitiesList)
+ .flatMap(List::stream)
+ .filter(e -> entityCaseSet.contains(e.getEntityCase()));
+ }
+
protected Void doShutdown() {
streamChannelManager.complete();
return super.doShutdown();
@@ -1195,7 +1044,7 @@
}
})
.filter(Objects::nonNull)
- .collect(Collectors.toList());
+ .collect(toList());
}
private String parseP4Error(P4RuntimeOuterClass.Error err) {
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeCodecs.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeCodecs.java
new file mode 100644
index 0000000..1126fef
--- /dev/null
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeCodecs.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019-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;
+
+/**
+ * Utility class that provides access to P4Runtime codec instances.
+ */
+final class P4RuntimeCodecs {
+
+ static final P4RuntimeCodecs CODECS = new P4RuntimeCodecs();
+
+ private final ActionProfileMemberCodec actionProfileMember;
+ private final ActionProfileGroupCodec actionProfileGroup;
+
+ private P4RuntimeCodecs() {
+ this.actionProfileMember = new ActionProfileMemberCodec();
+ this.actionProfileGroup = new ActionProfileGroupCodec();
+ }
+
+ ActionProfileMemberCodec actionProfileMember() {
+ return actionProfileMember;
+ }
+
+ ActionProfileGroupCodec actionProfileGroup() {
+ return actionProfileGroup;
+ }
+}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java
index 5dafe2b..604b1f0 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java
@@ -31,20 +31,21 @@
}
static void assertSize(String entityDescr, ByteString value, int bitWidth)
- throws EncodeException {
+ throws CodecException {
int byteWidth = (int) Math.ceil((float) bitWidth / 8);
if (value.size() != byteWidth) {
- throw new EncodeException(format("Wrong size for %s, expected %d bytes, but found %d",
- entityDescr, byteWidth, value.size()));
+ throw new CodecException(format(
+ "Wrong size for %s, expected %d bytes, but found %d",
+ entityDescr, byteWidth, value.size()));
}
}
static void assertPrefixLen(String entityDescr, int prefixLength, int bitWidth)
- throws EncodeException {
+ throws CodecException {
if (prefixLength > bitWidth) {
- throw new EncodeException(format(
+ throw new CodecException(format(
"wrong prefix length for %s, field size is %d bits, but found one is %d",
entityDescr, bitWidth, prefixLength));
}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
index 3294a6d..357e41d 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
@@ -81,16 +81,16 @@
* @param piTableEntries PI table entries
* @param pipeconf PI pipeconf
* @return collection of P4Runtime table entry protobuf messages
- * @throws EncodeException if a PI table entry cannot be encoded
+ * @throws CodecException if a PI table entry cannot be encoded
*/
static List<TableEntry> encode(List<PiTableEntry> piTableEntries,
PiPipeconf pipeconf)
- throws EncodeException {
+ throws CodecException {
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
- throw new EncodeException(format(
+ throw new CodecException(format(
"Unable to get a P4Info browser for pipeconf %s", pipeconf.id()));
}
@@ -100,7 +100,7 @@
try {
tableEntryMsgListBuilder.add(encodePiTableEntry(piTableEntry, browser));
} catch (P4InfoBrowser.NotFoundException e) {
- throw new EncodeException(e.getMessage());
+ throw new CodecException(e.getMessage());
}
}
@@ -113,15 +113,15 @@
* @param piTableEntry table entry
* @param pipeconf pipeconf
* @return encoded table entry message
- * @throws EncodeException if entry cannot be encoded
+ * @throws CodecException if entry cannot be encoded
* @throws P4InfoBrowser.NotFoundException if the required information cannot be find in the pipeconf's P4info
*/
static TableEntry encode(PiTableEntry piTableEntry, PiPipeconf pipeconf)
- throws EncodeException, P4InfoBrowser.NotFoundException {
+ throws CodecException, P4InfoBrowser.NotFoundException {
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
- throw new EncodeException(format("Unable to get a P4Info browser for pipeconf %s", pipeconf.id()));
+ throw new CodecException(format("Unable to get a P4Info browser for pipeconf %s", pipeconf.id()));
}
return encodePiTableEntry(piTableEntry, browser);
@@ -152,7 +152,7 @@
for (TableEntry tableEntryMsg : tableEntryMsgs) {
try {
piTableEntryListBuilder.add(decodeTableEntryMsg(tableEntryMsg, browser));
- } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+ } catch (P4InfoBrowser.NotFoundException | CodecException e) {
log.error("Unable to decode table entry message: {}", e.getMessage());
}
}
@@ -166,15 +166,15 @@
* @param tableEntryMsg table entry message
* @param pipeconf pipeconf
* @return decoded PI table entry
- * @throws EncodeException if message cannot be decoded
+ * @throws CodecException if message cannot be decoded
* @throws P4InfoBrowser.NotFoundException if the required information cannot be find in the pipeconf's P4info
*/
static PiTableEntry decode(TableEntry tableEntryMsg, PiPipeconf pipeconf)
- throws EncodeException, P4InfoBrowser.NotFoundException {
+ throws CodecException, P4InfoBrowser.NotFoundException {
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
- throw new EncodeException(format("Unable to get a P4Info browser for pipeconf %s", pipeconf.id()));
+ throw new CodecException(format("Unable to get a P4Info browser for pipeconf %s", pipeconf.id()));
}
return decodeTableEntryMsg(tableEntryMsg, browser);
}
@@ -188,11 +188,11 @@
* @param matchKey match key
* @param pipeconf pipeconf
* @return table entry message
- * @throws EncodeException if message cannot be encoded
+ * @throws CodecException if message cannot be encoded
* @throws P4InfoBrowser.NotFoundException if the required information cannot be find in the pipeconf's P4info
*/
static TableEntry encode(PiTableId tableId, PiMatchKey matchKey, PiPipeconf pipeconf)
- throws EncodeException, P4InfoBrowser.NotFoundException {
+ throws CodecException, P4InfoBrowser.NotFoundException {
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
TableEntry.Builder tableEntryMsgBuilder = TableEntry.newBuilder();
@@ -215,7 +215,7 @@
}
private static TableEntry encodePiTableEntry(PiTableEntry piTableEntry, P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
TableEntry.Builder tableEntryMsgBuilder = TableEntry.newBuilder();
@@ -259,7 +259,7 @@
}
private static PiTableEntry decodeTableEntryMsg(TableEntry tableEntryMsg, P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
PiTableEntry.Builder piTableEntryBuilder = PiTableEntry.builder();
@@ -295,7 +295,7 @@
private static FieldMatch encodePiFieldMatch(PiFieldMatch piFieldMatch, P4InfoOuterClass.Table tableInfo,
P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
FieldMatch.Builder fieldMatchMsgBuilder = FieldMatch.newBuilder();
@@ -359,7 +359,7 @@
.build())
.build();
default:
- throw new EncodeException(format(
+ throw new CodecException(format(
"Building of match type %s not implemented", piFieldMatch.type()));
}
}
@@ -370,11 +370,11 @@
* @param tableEntryMsg table entry message
* @param pipeconf pipeconf
* @return PI match key
- * @throws EncodeException if message cannot be decoded
+ * @throws CodecException if message cannot be decoded
* @throws P4InfoBrowser.NotFoundException if the required information cannot be find in the pipeconf's P4info
*/
static PiMatchKey decodeMatchKey(TableEntry tableEntryMsg, PiPipeconf pipeconf)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
P4InfoOuterClass.Table tableInfo = browser.tables().getById(tableEntryMsg.getTableId());
if (tableEntryMsg.getMatchCount() == 0) {
@@ -386,7 +386,7 @@
private static PiMatchKey decodeFieldMatchMsgs(List<FieldMatch> fieldMatchs, P4InfoOuterClass.Table tableInfo,
P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
// Match key for field matches.
PiMatchKey.Builder piMatchKeyBuilder = PiMatchKey.builder();
for (FieldMatch fieldMatchMsg : fieldMatchs) {
@@ -397,7 +397,7 @@
private static PiFieldMatch decodeFieldMatchMsg(FieldMatch fieldMatchMsg, P4InfoOuterClass.Table tableInfo,
P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
int tableId = tableInfo.getPreamble().getId();
String fieldMatchName = browser.matchFields(tableId).getById(fieldMatchMsg.getFieldId()).getName();
@@ -426,13 +426,13 @@
ImmutableByteSequence rangeLowValue = copyFrom(rangeFieldMatch.getLow().asReadOnlyByteBuffer());
return new PiRangeFieldMatch(headerFieldId, rangeLowValue, rangeHighValue);
default:
- throw new EncodeException(format(
+ throw new CodecException(format(
"Decoding of field match type '%s' not implemented", typeCase.name()));
}
}
static TableAction encodePiTableAction(PiTableAction piTableAction, P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
checkNotNull(piTableAction, "Cannot encode null PiTableAction");
TableAction.Builder tableActionMsgBuilder = TableAction.newBuilder();
@@ -451,7 +451,7 @@
tableActionMsgBuilder.setActionProfileMemberId(actionProfileMemberId.id());
break;
default:
- throw new EncodeException(
+ throw new CodecException(
format("Building of table action type %s not implemented", piTableAction.type()));
}
@@ -459,7 +459,7 @@
}
static PiTableAction decodeTableActionMsg(TableAction tableActionMsg, P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
TableAction.TypeCase typeCase = tableActionMsg.getTypeCase();
switch (typeCase) {
case ACTION:
@@ -470,13 +470,13 @@
case ACTION_PROFILE_MEMBER_ID:
return PiActionProfileMemberId.of(tableActionMsg.getActionProfileMemberId());
default:
- throw new EncodeException(
+ throw new CodecException(
format("Decoding of table action type %s not implemented", typeCase.name()));
}
}
static Action encodePiAction(PiAction piAction, P4InfoBrowser browser)
- throws P4InfoBrowser.NotFoundException, EncodeException {
+ throws P4InfoBrowser.NotFoundException, CodecException {
int actionId = browser.actions().getByName(piAction.id().toString()).getPreamble().getId();
diff --git a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/P4RuntimeGroupTest.java b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/P4RuntimeGroupTest.java
index b51a2c3..a7f7183 100644
--- a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/P4RuntimeGroupTest.java
+++ b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/P4RuntimeGroupTest.java
@@ -84,15 +84,19 @@
private static final PiActionParamId PORT_PARAM_ID = PiActionParamId.of("port");
private static final int BASE_MEM_ID = 65535;
private static final List<Integer> MEMBER_IDS = ImmutableList.of(65536, 65537, 65538);
- private static final List<PiActionProfileMember> GROUP_MEMBERS =
+ private static final List<PiActionProfileMember> GROUP_MEMBER_INSTANCES =
Lists.newArrayList(
outputMember((short) 1),
outputMember((short) 2),
outputMember((short) 3)
);
+ private static final List<PiActionProfileGroup.WeightedMember> GROUP_WEIGHTED_MEMBERS =
+ GROUP_MEMBER_INSTANCES.stream()
+ .map(m -> new PiActionProfileGroup.WeightedMember(m, DEFAULT_MEMBER_WEIGHT))
+ .collect(Collectors.toList());
private static final PiActionProfileGroup GROUP = PiActionProfileGroup.builder()
.withId(GROUP_ID)
- .addMembers(GROUP_MEMBERS)
+ .addMembers(GROUP_MEMBER_INSTANCES)
.withActionProfileId(ACT_PROF_ID)
.build();
private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:p4runtime:1");
@@ -121,7 +125,6 @@
.forActionProfile(ACT_PROF_ID)
.withAction(piAction)
.withId(PiActionProfileMemberId.of(BASE_MEM_ID + portNum))
- .withWeight(DEFAULT_MEMBER_WEIGHT)
.build();
}
@@ -163,7 +166,7 @@
@Test
public void testInsertPiActionProfileGroup() throws Exception {
CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(1);
- client.writeActionProfileGroup(GROUP, INSERT, PIPECONF, 3);
+ client.writeActionProfileGroup(GROUP, INSERT, PIPECONF);
complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
WriteRequest result = p4RuntimeServerImpl.getWriteReqs().get(0);
assertEquals(1, result.getDeviceId());
@@ -192,7 +195,7 @@
@Test
public void testInsertPiActionMembers() throws Exception {
CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(1);
- client.writeActionProfileMembers(GROUP_MEMBERS, INSERT, PIPECONF);
+ client.writeActionProfileMembers(GROUP_MEMBER_INSTANCES, INSERT, PIPECONF);
complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
WriteRequest result = p4RuntimeServerImpl.getWriteReqs().get(0);
assertEquals(1, result.getDeviceId());
@@ -224,15 +227,41 @@
.setGroupId(GROUP_ID.id())
.setActionProfileId(P4_INFO_ACT_PROF_ID);
- List<ActionProfileMember> members = Lists.newArrayList();
-
MEMBER_IDS.forEach(id -> {
ActionProfileGroup.Member member = ActionProfileGroup.Member.newBuilder()
.setMemberId(id)
.setWeight(DEFAULT_MEMBER_WEIGHT)
.build();
group.addMembers(member);
+ });
+ List<ReadResponse> responses = Lists.newArrayList();
+ responses.add(ReadResponse.newBuilder()
+ .addEntities(Entity.newBuilder().setActionProfileGroup(group))
+ .build()
+ );
+
+ p4RuntimeServerImpl.willReturnReadResult(responses);
+ CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(1);
+ CompletableFuture<List<PiActionProfileGroup>> groupsComplete = client.dumpActionProfileGroups(
+ ACT_PROF_ID, PIPECONF);
+ complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
+
+ Collection<PiActionProfileGroup> groups = groupsComplete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
+ assertEquals(1, groups.size());
+ PiActionProfileGroup piActionGroup = groups.iterator().next();
+ assertEquals(ACT_PROF_ID, piActionGroup.actionProfile());
+ assertEquals(GROUP_ID, piActionGroup.id());
+ assertEquals(3, piActionGroup.members().size());
+ assertTrue(GROUP_WEIGHTED_MEMBERS.containsAll(piActionGroup.members()));
+ assertTrue(piActionGroup.members().containsAll(GROUP_WEIGHTED_MEMBERS));
+ }
+
+ @Test
+ public void testReadMembers() throws Exception {
+ List<ActionProfileMember> members = Lists.newArrayList();
+
+ MEMBER_IDS.forEach(id -> {
byte outPort = (byte) (id - BASE_MEM_ID);
ByteString bs = ByteString.copyFrom(new byte[]{0, outPort});
Action.Param param = Action.Param.newBuilder()
@@ -256,11 +285,6 @@
List<ReadResponse> responses = Lists.newArrayList();
responses.add(ReadResponse.newBuilder()
- .addEntities(Entity.newBuilder().setActionProfileGroup(group))
- .build()
- );
-
- responses.add(ReadResponse.newBuilder()
.addAllEntities(members.stream()
.map(m -> Entity.newBuilder()
.setActionProfileMember(m).build())
@@ -268,18 +292,14 @@
.build());
p4RuntimeServerImpl.willReturnReadResult(responses);
- CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(2);
- CompletableFuture<List<PiActionProfileGroup>> groupsComplete = client.dumpActionProfileGroups(
+ CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(1);
+ CompletableFuture<List<PiActionProfileMember>> membersComplete = client.dumpActionProfileMembers(
ACT_PROF_ID, PIPECONF);
complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
- Collection<PiActionProfileGroup> groups = groupsComplete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
- assertEquals(1, groups.size());
- PiActionProfileGroup piActionGroup = groups.iterator().next();
- assertEquals(ACT_PROF_ID, piActionGroup.actionProfileId());
- assertEquals(GROUP_ID, piActionGroup.id());
- assertEquals(3, piActionGroup.members().size());
- assertTrue(GROUP_MEMBERS.containsAll(piActionGroup.members()));
- assertTrue(piActionGroup.members().containsAll(GROUP_MEMBERS));
+ Collection<PiActionProfileMember> piMembers = membersComplete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
+ assertEquals(3, piMembers.size());
+ assertTrue(GROUP_MEMBER_INSTANCES.containsAll(piMembers));
+ assertTrue(piMembers.containsAll(GROUP_MEMBER_INSTANCES));
}
}
diff --git a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionProfileModel.java b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionProfileModel.java
index 5417d44..443d407 100644
--- a/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionProfileModel.java
+++ b/protocols/p4runtime/model/src/main/java/org/onosproject/p4runtime/model/P4ActionProfileModel.java
@@ -32,14 +32,17 @@
private final PiActionProfileId id;
private final ImmutableSet<PiTableId> tables;
private final boolean hasSelector;
- private final long maxSize;
+ private final long size;
+ private final int maxGroupSize;
P4ActionProfileModel(PiActionProfileId id,
- ImmutableSet<PiTableId> tables, boolean hasSelector, long maxSize) {
+ ImmutableSet<PiTableId> tables, boolean hasSelector,
+ long size, int maxGroupSize) {
this.id = id;
this.tables = tables;
this.hasSelector = hasSelector;
- this.maxSize = maxSize;
+ this.size = size;
+ this.maxGroupSize = maxGroupSize;
}
@Override
@@ -58,13 +61,18 @@
}
@Override
- public long maxSize() {
- return maxSize;
+ public long size() {
+ return size;
+ }
+
+ @Override
+ public int maxGroupSize() {
+ return maxGroupSize;
}
@Override
public int hashCode() {
- return Objects.hash(id, tables, hasSelector, maxSize);
+ return Objects.hash(id, tables, hasSelector, size, maxGroupSize);
}
@Override
@@ -79,6 +87,7 @@
return Objects.equals(this.id, other.id)
&& Objects.equals(this.tables, other.tables)
&& Objects.equals(this.hasSelector, other.hasSelector)
- && Objects.equals(this.maxSize, other.maxSize);
+ && Objects.equals(this.size, other.size)
+ && Objects.equals(this.maxGroupSize, other.maxGroupSize);
}
}
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 ff49928..ef8cb36 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
@@ -332,7 +332,8 @@
PiActionProfileId.of(actProfileMsg.getPreamble().getName()),
tableIdSetBuilder.build(),
actProfileMsg.getWithSelector(),
- actProfileMsg.getSize()));
+ actProfileMsg.getSize(),
+ actProfileMsg.getMaxGroupSize()));
}
return actProfileMap;
}
diff --git a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4ActionProfileModelTest.java b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4ActionProfileModelTest.java
index 3042414..021d341 100644
--- a/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4ActionProfileModelTest.java
+++ b/protocols/p4runtime/model/src/test/java/org/onosproject/p4runtime/model/P4ActionProfileModelTest.java
@@ -21,7 +21,6 @@
import org.onosproject.net.pi.model.PiActionProfileId;
import org.onosproject.net.pi.model.PiTableId;
-
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
/**
@@ -52,17 +51,17 @@
private final PiActionProfileId id2 = PiActionProfileId.of("name2");
private final P4ActionProfileModel metadataModel = new P4ActionProfileModel(id, tables,
- true, 64);
+ true, 64, 10);
private final P4ActionProfileModel sameAsMetadataModel = new P4ActionProfileModel(id, sameAsTables,
- true, 64);
+ true, 64, 10);
private final P4ActionProfileModel metadataModel2 = new P4ActionProfileModel(id, tables2,
- true, 64);
+ true, 64, 10);
private final P4ActionProfileModel metadataModel3 = new P4ActionProfileModel(id2, tables,
- true, 64);
+ true, 64, 10);
private final P4ActionProfileModel metadataModel4 = new P4ActionProfileModel(id, tables,
- false, 64);
+ false, 64, 10);
private final P4ActionProfileModel metadataModel5 = new P4ActionProfileModel(id, tables,
- true, 32);
+ true, 32, 5);
/**
* Checks that the P4ActionProfileModel class is immutable.
@@ -85,4 +84,4 @@
.addEqualityGroup(metadataModel5)
.testEquals();
}
-}
\ No newline at end of file
+}
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 3a803d4..9d696e2 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
@@ -70,6 +70,7 @@
private static final Long DEFAULT_MAX_TABLE_SIZE = 1024L;
private static final Long DEFAULT_MAX_ACTION_PROFILE_SIZE = 64L;
+ private static final int DEFAULT_MAX_GROUP_SIZE = 0;
/**
* Tests parse method.
@@ -205,7 +206,8 @@
ImmutableSet<PiTableId> tableIds = new ImmutableSet.Builder<PiTableId>().add(tableId).build();
PiActionProfileId actionProfileId = PiActionProfileId.of("wcmp_control.wcmp_selector");
PiActionProfileModel wcmpSelector3 = new P4ActionProfileModel(actionProfileId, tableIds,
- true, DEFAULT_MAX_ACTION_PROFILE_SIZE);
+ true, DEFAULT_MAX_ACTION_PROFILE_SIZE,
+ DEFAULT_MAX_GROUP_SIZE);
PiActionProfileModel wcmpSelector = model.actionProfiles(actionProfileId).orElse(null);
PiActionProfileModel wcmpSelector2 = model2.actionProfiles(actionProfileId).orElse(null);
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 da10762..8576e84 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
@@ -75,12 +75,17 @@
private static final long ACTION_MAX_SIZE_1 = 100;
private static final long ACTION_MAX_SIZE_2 = 200;
+ private static final int ACTION_MAX_GROUP_SIZE_1 = 10;
+ private static final int ACTION_MAX_GROUP_SIZE_2 = 20;
+
private static final PiActionProfileModel P4_ACTION_PROFILE_MODEL_1 =
new P4ActionProfileModel(PI_ACTION_PROFILE_ID_1, ACTION_TABLES_1,
- ACTION_HAS_SELECTOR_1, ACTION_MAX_SIZE_1);
+ ACTION_HAS_SELECTOR_1, ACTION_MAX_SIZE_1,
+ ACTION_MAX_GROUP_SIZE_1);
private static final PiActionProfileModel P4_ACTION_PROFILE_MODEL_2 =
new P4ActionProfileModel(PI_ACTION_PROFILE_ID_2, ACTION_TABLES_2,
- ACTION_HAS_SELECTOR_2, ACTION_MAX_SIZE_2);
+ ACTION_HAS_SELECTOR_2, ACTION_MAX_SIZE_2,
+ ACTION_MAX_GROUP_SIZE_2);
/* Counters */
private static final PiCounterId PI_COUNTER_ID_1 = PiCounterId.of("Counter1");
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 89b10d0..797abe4 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
@@ -67,12 +67,17 @@
private static final long ACTION_MAX_SIZE_1 = 100;
private static final long ACTION_MAX_SIZE_2 = 200;
+ private static final int ACTION_MAX_GROUP_SIZE_1 = 10;
+ private static final int ACTION_MAX_GROUP_SIZE_2 = 20;
+
private static final PiActionProfileModel P4_ACTION_PROFILE_MODEL_1 =
new P4ActionProfileModel(PI_ACTION_PROFILE_ID_1, ACTION_TABLES_1,
- ACTION_HAS_SELECTOR_1, ACTION_MAX_SIZE_1);
+ ACTION_HAS_SELECTOR_1, ACTION_MAX_SIZE_1,
+ ACTION_MAX_GROUP_SIZE_1);
private static final PiActionProfileModel P4_ACTION_PROFILE_MODEL_2 =
new P4ActionProfileModel(PI_ACTION_PROFILE_ID_2, ACTION_TABLES_2,
- ACTION_HAS_SELECTOR_2, ACTION_MAX_SIZE_2);
+ ACTION_HAS_SELECTOR_2, ACTION_MAX_SIZE_2,
+ ACTION_MAX_GROUP_SIZE_2);
/* Counters */
private static final PiCounterId PI_COUNTER_ID_1 = PiCounterId.of("Counter1");