Various improvements to PI group handling
- Moved group translation logic to core service
- Removed dependency on KRYO
- Fixed bug where tratments with PI instructions where not supported if
an interpreter was present
- Fixed bug where action profile name was not found during protobuf
encoding (always perform P4Info lookup by name and alias)
- Improved reading of members by issuing one big request for all
groups
Change-Id: Ifcf8380b09293e70be15cf4999bd2845caf5d01e
diff --git a/apps/p4runtime-test/src/test/java/org/onosproject/p4runtime/test/P4RuntimeTest.java b/apps/p4runtime-test/src/test/java/org/onosproject/p4runtime/test/P4RuntimeTest.java
index 45a8716..da1e36f 100644
--- a/apps/p4runtime-test/src/test/java/org/onosproject/p4runtime/test/P4RuntimeTest.java
+++ b/apps/p4runtime-test/src/test/java/org/onosproject/p4runtime/test/P4RuntimeTest.java
@@ -270,7 +270,7 @@
.withId(groupId)
.addMembers(members)
.build();
- CompletableFuture<Boolean> success = client.writeActionGroupMembers(actionGroup, members,
+ CompletableFuture<Boolean> success = client.writeActionGroupMembers(actionGroup,
P4RuntimeClient.WriteOperationType.INSERT,
bmv2DefaultPipeconf);
assert (success.get());
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiFlowRuleTranslationService.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiFlowRuleTranslationService.java
deleted file mode 100644
index 4948ebb..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiFlowRuleTranslationService.java
+++ /dev/null
@@ -1,55 +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.net.pi.runtime;
-
-import com.google.common.annotations.Beta;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.pi.model.PiPipeconf;
-
-/**
- * A service to translate ONOS flow rules to table entries of a protocol-independent pipeline.
- */
-@Beta
-public interface PiFlowRuleTranslationService {
-
- /**
- * Returns a table entry equivalent to the given flow rule for the given protocol-independent
- * pipeline configuration.
- *
- * @param rule a flow rule
- * @param pipeconf a pipeline configuration
- * @return a table entry
- * @throws PiFlowRuleTranslationException if the flow rule cannot be translated
- */
- PiTableEntry translate(FlowRule rule, PiPipeconf pipeconf)
- throws PiFlowRuleTranslationException;
-
- /**
- * Signals that an error was encountered while translating flow rule.
- */
- class PiFlowRuleTranslationException extends Exception {
-
- /**
- * Creates a new exception with the given message.
- *
- * @param message a message
- */
- public PiFlowRuleTranslationException(String message) {
- super(message);
- }
- }
-}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiGroupKey.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiGroupKey.java
new file mode 100644
index 0000000..598f3e7
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiGroupKey.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.base.Objects;
+import org.onosproject.net.group.GroupKey;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of GroupKey for the case of a protocol-independent pipeline.
+ */
+public final class PiGroupKey implements GroupKey {
+
+ private final PiTableId tableId;
+ private final PiActionProfileId piActionProfileId;
+ private final int groupId;
+
+ /**
+ * Returns a new group key for the given table ID, action profile ID, and group ID.
+ *
+ * @param tableId table ID
+ * @param piActionProfileId action profile ID
+ * @param groupId group ID
+ */
+ public PiGroupKey(PiTableId tableId, PiActionProfileId piActionProfileId, int groupId) {
+ this.tableId = checkNotNull(tableId);
+ this.piActionProfileId = checkNotNull(piActionProfileId);
+ this.groupId = groupId;
+ }
+
+ /**
+ * Returns the table ID defined by this key.
+ *
+ * @return table ID
+ */
+ public PiTableId tableId() {
+ return tableId;
+ }
+
+ /**
+ * Returns the group ID defined by this key.
+ *
+ * @return group ID
+ */
+ public int groupId() {
+ return groupId;
+ }
+
+ /**
+ * Returns the action profile ID defined by this key.
+ *
+ * @return action profile ID
+ */
+ public PiActionProfileId actionProfileId() {
+ return piActionProfileId;
+ }
+
+ @Override
+ public byte[] key() {
+ return toString().getBytes();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof PiGroupKey)) {
+ return false;
+ }
+ PiGroupKey that = (PiGroupKey) o;
+ return groupId == that.groupId &&
+ Objects.equal(tableId, that.tableId) &&
+ Objects.equal(piActionProfileId, that.piActionProfileId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(tableId, piActionProfileId, groupId);
+ }
+
+ @Override
+ public String toString() {
+ return tableId.id() + "-" + piActionProfileId.id() + "-" + String.valueOf(groupId);
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTranslationService.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTranslationService.java
new file mode 100644
index 0000000..a0c1101
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTranslationService.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.pi.model.PiPipeconf;
+
+/**
+ * A service to translate ONOS entities to protocol-independent ones.
+ */
+@Beta
+public interface PiTranslationService {
+
+ /**
+ * Returns a PI table entry equivalent to the given flow rule for the given protocol-independent pipeline
+ * configuration.
+ *
+ * @param rule a flow rule
+ * @param pipeconf a pipeline configuration
+ * @return a table entry
+ * @throws PiTranslationException if the flow rule cannot be translated
+ */
+ PiTableEntry translateFlowRule(FlowRule rule, PiPipeconf pipeconf)
+ throws PiTranslationException;
+
+ /**
+ * Returns a PI action group equivalent to the given group for the given protocol-independent pipeline
+ * configuration.
+ *
+ * @param group a group
+ * @param pipeconf a pipeline configuration
+ * @return a PI action group
+ * @throws PiTranslationException if the group cannot be translated
+ */
+ PiActionGroup translateGroup(Group group, PiPipeconf pipeconf)
+ throws PiTranslationException;
+
+ /**
+ * Signals that an error was encountered while translating an entity.
+ */
+ class PiTranslationException extends Exception {
+
+ /**
+ * Creates a new exception with the given message.
+ *
+ * @param message a message
+ */
+ public PiTranslationException(String message) {
+ super(message);
+ }
+ }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java b/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java
index df73a65..4e0fd16 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java
@@ -92,7 +92,7 @@
import static java.lang.String.format;
import static org.onlab.util.ImmutableByteSequence.ByteSequenceTrimException;
import static org.onosproject.net.pi.impl.CriterionTranslator.CriterionTranslatorException;
-import static org.onosproject.net.pi.runtime.PiFlowRuleTranslationService.PiFlowRuleTranslationException;
+import static org.onosproject.net.pi.runtime.PiTranslationService.PiTranslationException;
/**
* Helper class to translate criterion instances to PI field matches.
@@ -145,14 +145,14 @@
* @param matchType match type
* @param bitWidth size of the field match in bits
* @return a PI field match
- * @throws PiFlowRuleTranslationException if the criterion cannot be translated (see exception message)
+ * @throws PiTranslationException if the criterion cannot be translated (see exception message)
*/
static PiFieldMatch translateCriterion(Criterion criterion, PiHeaderFieldId fieldId, PiMatchType matchType,
int bitWidth)
- throws PiFlowRuleTranslationException {
+ throws PiTranslationException {
if (!TRANSLATORS.containsKey(criterion.getClass())) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Translation of criterion class %s is not implemented.",
criterion.getClass().getSimpleName()));
}
@@ -171,15 +171,15 @@
Pair<ImmutableByteSequence, Integer> lp = translator.lpmMatch();
return new PiLpmFieldMatch(fieldId, lp.getLeft(), lp.getRight());
default:
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Translation of criterion %s (%s class) to match type %s is not implemented.",
criterion.type().name(), criterion.getClass().getSimpleName(), matchType.name()));
}
} catch (ByteSequenceTrimException e) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Size mismatch for criterion %s: %s", criterion.type(), e.getMessage()));
} catch (CriterionTranslatorException e) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Unable to translate criterion %s: %s", criterion.type(), e.getMessage()));
}
}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslator.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslator.java
index fbfd9b7..c0fd846 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslator.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslator.java
@@ -21,7 +21,6 @@
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.net.Device;
import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.IndexTableId;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
@@ -61,7 +60,9 @@
import static org.onlab.util.ImmutableByteSequence.fit;
import static org.onosproject.net.flow.criteria.Criterion.Type.PROTOCOL_INDEPENDENT;
import static org.onosproject.net.pi.impl.CriterionTranslatorHelper.translateCriterion;
-import static org.onosproject.net.pi.runtime.PiFlowRuleTranslationService.PiFlowRuleTranslationException;
+import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
+import static org.onosproject.net.pi.impl.PiUtils.translateTableId;
+import static org.onosproject.net.pi.runtime.PiTranslationService.PiTranslationException;
/**
* Implementation of flow rule translation logic.
@@ -69,77 +70,43 @@
final class PiFlowRuleTranslator {
public static final int MAX_PI_PRIORITY = (int) Math.pow(2, 24);
- private static final Logger log = LoggerFactory.getLogger(PiFlowRuleTranslationServiceImpl.class);
+ private static final Logger log = LoggerFactory.getLogger(PiFlowRuleTranslator.class);
private PiFlowRuleTranslator() {
// Hide constructor.
}
- static PiTableEntry translateFlowRule(FlowRule rule, PiPipeconf pipeconf, Device device)
- throws PiFlowRuleTranslationException {
+ /**
+ * Returns a PI table entry equivalent to the given flow rule, for the given pipeconf and device.
+ *
+ * @param rule flow rule
+ * @param pipeconf pipeconf
+ * @param device device
+ * @return PI table entry
+ * @throws PiTranslationException if the flow rule cannot be translated
+ */
+ static PiTableEntry translate(FlowRule rule, PiPipeconf pipeconf, Device device)
+ throws PiTranslationException {
PiPipelineModel pipelineModel = pipeconf.pipelineModel();
// Retrieve interpreter, if any.
- final PiPipelineInterpreter interpreter;
+ final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf);
+ // Get table model.
+ final PiTableId piTableId = translateTableId(rule.table(), interpreter);
+ final PiTableModel tableModel = getTableModel(piTableId, pipelineModel);
+ // Translate selector.
+ final Collection<PiFieldMatch> fieldMatches = translateFieldMatches(interpreter, rule.selector(), tableModel);
+ // Translate treatment.
+ final PiTableAction piTableAction = translateTreatment(rule.treatment(), interpreter, piTableId, pipelineModel);
- if (device != null) {
- interpreter = device.is(PiPipelineInterpreter.class) ? device.as(PiPipelineInterpreter.class) : null;
- } else {
- // The case of device == null should be admitted only during unit testing.
- // In any other case, the interpreter should be constructed using the device.as() method to make sure that
- // behaviour's handler/data attributes are correctly populated.
- // FIXME: modify test class PiFlowRuleTranslatorTest to avoid passing null device
- // I.e. we need to create a device object that supports is/as method for obtaining the interpreter.
- log.warn("translateFlowRule() called with device == null, is this a unit test?");
- try {
- interpreter = (PiPipelineInterpreter) pipeconf.implementation(PiPipelineInterpreter.class)
- .orElse(null)
- .newInstance();
- } catch (InstantiationException | IllegalAccessException e) {
- throw new RuntimeException(format("Unable to instantiate interpreter of pipeconf %s", pipeconf.id()));
- }
- }
-
- PiTableId piTableId;
- switch (rule.table().type()) {
- case PIPELINE_INDEPENDENT:
- piTableId = (PiTableId) rule.table();
- break;
- case INDEX:
- IndexTableId indexId = (IndexTableId) rule.table();
- if (interpreter == null) {
- throw new PiFlowRuleTranslationException(format(
- "Unable to map table ID '%d' from index to PI: missing interpreter", indexId.id()));
- } else if (!interpreter.mapFlowRuleTableId(indexId.id()).isPresent()) {
- throw new PiFlowRuleTranslationException(format(
- "Unable to map table ID '%d' from index to PI: missing ID in interpreter", indexId.id()));
- } else {
- piTableId = interpreter.mapFlowRuleTableId(indexId.id()).get();
- }
- break;
- default:
- throw new PiFlowRuleTranslationException(format(
- "Unrecognized table ID type %s", rule.table().type().name()));
- }
-
- PiTableModel table = pipelineModel.table(piTableId.toString())
- .orElseThrow(() -> new PiFlowRuleTranslationException(format(
- "Not such a table in pipeline model: %s", piTableId)));
-
- /* Translate selector */
- Collection<PiFieldMatch> fieldMatches = buildFieldMatches(interpreter, rule.selector(), table);
-
- /* Translate treatment */
- PiTableAction piTableAction = buildAction(rule.treatment(), interpreter, piTableId);
- piTableAction = typeCheckAction(piTableAction, table);
-
- PiTableEntry.Builder tableEntryBuilder = PiTableEntry.builder();
+ // Build PI entry.
+ final PiTableEntry.Builder tableEntryBuilder = PiTableEntry.builder();
// In the P4 world 0 is the highest priority, in ONOS the lowest one.
// FIXME: move priority conversion to the driver, where different constraints might apply
// e.g. less bits for encoding priority in TCAM-based implementations.
- int newPriority;
+ final int newPriority;
if (rule.priority() > MAX_PI_PRIORITY) {
log.warn("Flow rule priority too big, setting translated priority to max value {}: {}",
MAX_PI_PRIORITY, rule);
@@ -157,11 +124,11 @@
.withAction(piTableAction);
if (!rule.isPermanent()) {
- if (table.supportsAging()) {
+ if (tableModel.supportsAging()) {
tableEntryBuilder.withTimeout((double) rule.timeout());
} else {
log.warn("Flow rule is temporary, but table '{}' doesn't support " +
- "aging, translating to permanent.", table.name());
+ "aging, translating to permanent.", tableModel.name());
}
}
@@ -169,12 +136,40 @@
return tableEntryBuilder.build();
}
+
+ /**
+ * Returns a PI action equivalent to the given treatment, optionally using the given interpreter. This method also
+ * checks that the produced PI table action is suitable for the given table ID and pipeline model. If suitable, the
+ * returned action instance will have parameters well-sized, according to the table model.
+ *
+ * @param treatment traffic treatment
+ * @param interpreter interpreter
+ * @param tableId PI table ID
+ * @param pipelineModel pipeline model
+ * @return PI table action
+ * @throws PiTranslationException if the treatment cannot be translated or if the PI action is not suitable for the
+ * given pipeline model
+ */
+ static PiTableAction translateTreatment(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
+ PiTableId tableId, PiPipelineModel pipelineModel)
+ throws PiTranslationException {
+ PiTableModel tableModel = getTableModel(tableId, pipelineModel);
+ return typeCheckAction(buildAction(treatment, interpreter, tableId), tableModel);
+ }
+
+ private static PiTableModel getTableModel(PiTableId piTableId, PiPipelineModel pipelineModel)
+ throws PiTranslationException {
+ return pipelineModel.table(piTableId.toString())
+ .orElseThrow(() -> new PiTranslationException(format(
+ "Not such a table in pipeline model: %s", piTableId)));
+ }
+
/**
* Builds a PI action out of the given treatment, optionally using the given interpreter.
*/
private static PiTableAction buildAction(TrafficTreatment treatment, PiPipelineInterpreter interpreter,
- PiTableId tableId)
- throws PiFlowRuleTranslationException {
+ PiTableId tableId)
+ throws PiTranslationException {
PiTableAction piTableAction = null;
@@ -184,7 +179,7 @@
if (treatment.allInstructions().size() == 1) {
piTableAction = ((PiInstruction) inst).action();
} else {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Unable to translate treatment, found multiple instructions " +
"of which one is protocol-independent: %s", treatment));
}
@@ -196,14 +191,14 @@
try {
piTableAction = interpreter.mapTreatment(treatment, tableId);
} catch (PiPipelineInterpreter.PiInterpreterException e) {
- throw new PiFlowRuleTranslationException(
+ throw new PiTranslationException(
"Interpreter was unable to translate treatment. " + e.getMessage());
}
}
if (piTableAction == null) {
// No PiInstruction, no interpreter. It's time to give up.
- throw new PiFlowRuleTranslationException(
+ throw new PiTranslationException(
"Unable to translate treatment, neither an interpreter or a "
+ "protocol-independent instruction were provided.");
}
@@ -211,13 +206,8 @@
return piTableAction;
}
- /**
- * Checks that the given PI table action is suitable for the given table
- * model and returns a new action instance with parameters well-sized,
- * according to the table model. If not suitable, throws an exception explaining why.
- */
private static PiTableAction typeCheckAction(PiTableAction piTableAction, PiTableModel table)
- throws PiFlowRuleTranslationException {
+ throws PiTranslationException {
switch (piTableAction.type()) {
case ACTION:
return checkPiAction((PiAction) piTableAction, table);
@@ -229,15 +219,15 @@
}
private static PiTableAction checkPiAction(PiAction piAction, PiTableModel table)
- throws PiFlowRuleTranslationException {
+ throws PiTranslationException {
// Table supports this action?
PiActionModel actionModel = table.action(piAction.id().name()).orElseThrow(
- () -> new PiFlowRuleTranslationException(format("Not such action '%s' for table '%s'",
- piAction.id(), table.name())));
+ () -> new PiTranslationException(format("Not such action '%s' for table '%s'",
+ piAction.id(), table.name())));
// Is the number of runtime parameters correct?
if (actionModel.params().size() != piAction.parameters().size()) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Wrong number of runtime parameters for action '%s', expected %d but found %d",
actionModel.name(), actionModel.params().size(), piAction.parameters().size()));
}
@@ -247,13 +237,13 @@
PiAction.Builder newActionBuilder = PiAction.builder().withId(piAction.id());
for (PiActionParam param : piAction.parameters()) {
PiActionParamModel paramModel = actionModel.param(param.id().name())
- .orElseThrow(() -> new PiFlowRuleTranslationException(format(
+ .orElseThrow(() -> new PiTranslationException(format(
"Not such parameter '%s' for action '%s'", param.id(), actionModel.name())));
try {
newActionBuilder.withParameter(new PiActionParam(param.id(),
fit(param.value(), paramModel.bitWidth())));
} catch (ByteSequenceTrimException e) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Size mismatch for parameter '%s' of action '%s': %s",
param.id(), piAction.id(), e.getMessage()));
}
@@ -266,9 +256,9 @@
* Builds a collection of PI field matches out of the given selector, optionally using the given interpreter. The
* field matches returned are guaranteed to be compatible for the given table model.
*/
- private static Collection<PiFieldMatch> buildFieldMatches(PiPipelineInterpreter interpreter,
- TrafficSelector selector, PiTableModel tableModel)
- throws PiFlowRuleTranslationException {
+ private static Collection<PiFieldMatch> translateFieldMatches(PiPipelineInterpreter interpreter,
+ TrafficSelector selector, PiTableModel tableModel)
+ throws PiTranslationException {
Map<PiHeaderFieldId, PiFieldMatch> fieldMatches = Maps.newHashMap();
@@ -325,7 +315,7 @@
break;
// FIXME: Can we handle the case of RANGE or VALID match?
default:
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"No value found for required match field '%s'", fieldId));
}
// Next field.
@@ -339,7 +329,7 @@
try {
fieldMatch = translateCriterion(criterion, fieldId, fieldModel.matchType(), bitWidth);
translatedCriteria.add(criterion);
- } catch (PiFlowRuleTranslationException ex) {
+ } catch (PiTranslationException ex) {
// Ignore exception if the same field was found in PiCriterion.
if (piCriterionFields.containsKey(fieldId)) {
ignoredCriteria.add(criterion);
@@ -355,7 +345,7 @@
// Field was already translated from other criterion.
// Throw exception only if we are trying to match on different values of the same field...
if (!fieldMatch.equals(piCriterionFields.get(fieldId))) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Duplicate match field '%s': instance translated from criterion '%s' is different to " +
"what found in PiCriterion.", fieldId, criterion.type()));
}
@@ -377,7 +367,7 @@
.filter(c -> !translatedCriteria.contains(c) && !ignoredCriteria.contains(c))
.forEach(c -> skippedCriteriaJoiner.add(c.type().name()));
if (skippedCriteriaJoiner.length() > 0) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"The following criteria cannot be translated for table '%s': %s",
tableModel.name(), skippedCriteriaJoiner.toString()));
}
@@ -388,7 +378,7 @@
.filter(k -> !usedPiCriterionFields.contains(k) && !ignoredPiCriterionFields.contains(k))
.forEach(k -> skippedPiFieldsJoiner.add(k.id()));
if (skippedPiFieldsJoiner.length() > 0) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"The following PiCriterion field matches are not supported in table '%s': %s",
tableModel.name(), skippedPiFieldsJoiner.toString()));
}
@@ -397,11 +387,11 @@
}
private static PiFieldMatch typeCheckFieldMatch(PiFieldMatch fieldMatch, PiTableMatchFieldModel fieldModel)
- throws PiFlowRuleTranslationException {
+ throws PiTranslationException {
// Check parameter type and size
if (!fieldModel.matchType().equals(fieldMatch.type())) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Wrong match type for field '%s', expected %s, but found %s",
fieldMatch.fieldId(), fieldModel.matchType().name(), fieldMatch.type().name()));
}
@@ -427,7 +417,7 @@
case LPM:
PiLpmFieldMatch lpmfield = (PiLpmFieldMatch) fieldMatch;
if (lpmfield.prefixLength() > modelBitWidth) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Invalid prefix length for LPM field '%s', found %d but field has bit-width %d",
fieldMatch.fieldId(), lpmfield.prefixLength(), modelBitWidth));
}
@@ -446,7 +436,7 @@
"Unrecognized match type " + fieldModel.matchType().name());
}
} catch (ByteSequenceTrimException e) {
- throw new PiFlowRuleTranslationException(format(
+ throw new PiTranslationException(format(
"Size mismatch for field %s: %s", fieldMatch.fieldId(), e.getMessage()));
}
}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslator.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslator.java
new file mode 100644
index 0000000..7b798c0
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslator.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.impl;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.runtime.PiActionGroupId;
+import org.onosproject.net.pi.runtime.PiActionGroupMember;
+import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
+import org.onosproject.net.pi.runtime.PiGroupKey;
+import org.onosproject.net.pi.runtime.PiTableAction;
+import org.onosproject.net.pi.runtime.PiTranslationService.PiTranslationException;
+
+import java.nio.ByteBuffer;
+
+import static java.lang.String.format;
+import static org.onosproject.net.pi.impl.PiFlowRuleTranslator.translateTreatment;
+import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
+import static org.onosproject.net.pi.runtime.PiTableAction.Type.ACTION;
+
+/**
+ * Implementation of group translation logic.
+ */
+final class PiGroupTranslator {
+
+ private PiGroupTranslator() {
+ // Hides constructor.
+ }
+
+ /**
+ * Returns a PI action group equivalent to the given group, for the given pipeconf and device.
+ *
+ * @param group group
+ * @param pipeconf pipeconf
+ * @param device device
+ * @return PI action group
+ * @throws PiTranslationException if the group cannot be translated
+ */
+ static PiActionGroup translate(Group group, PiPipeconf pipeconf, Device device) throws PiTranslationException {
+
+ final PiPipelineInterpreter interpreter = getInterpreterOrNull(device, pipeconf);
+
+ final PiActionGroup.Builder piActionGroupBuilder = PiActionGroup.builder()
+ .withId(PiActionGroupId.of(group.id().id()));
+
+ switch (group.type()) {
+ case SELECT:
+ piActionGroupBuilder.withType(PiActionGroup.Type.SELECT);
+ break;
+ default:
+ throw new PiTranslationException(format("Group type %s not supported", group.type()));
+ }
+
+ if (!(group.appCookie() instanceof PiGroupKey)) {
+ throw new PiTranslationException("Group app cookie is not PI (class should be PiGroupKey)");
+ }
+ final PiGroupKey groupKey = (PiGroupKey) group.appCookie();
+
+ piActionGroupBuilder.withActionProfileId(groupKey.actionProfileId());
+
+ // Translate group buckets to PI group members
+ short bucketIdx = 0;
+ for (GroupBucket bucket : group.buckets().buckets()) {
+ /*
+ FIXME: the way member IDs are computed can cause collisions!
+ Problem:
+ In P4Runtime action group members, i.e. action buckets, are associated to a numeric ID chosen
+ at member insertion time. This ID must be unique for the whole action profile (i.e. the group table in
+ OpenFlow). In ONOS, GroupBucket doesn't specify any ID.
+
+ Solutions:
+ - Change GroupBucket API to force application wanting to perform group operations to specify a member id.
+ - Maintain state to dynamically allocate/deallocate member IDs, e.g. in a dedicated service, or in a
+ P4Runtime Group Provider.
+
+ Hack:
+ Statically derive member ID by combining groupId and position of the bucket in the list.
+ */
+ ByteBuffer bb = ByteBuffer.allocate(4)
+ .putShort((short) (group.id().id() & 0xffff))
+ .putShort(bucketIdx);
+ bb.rewind();
+ int memberId = bb.getInt();
+ bucketIdx++;
+
+ final PiTableAction tableAction = translateTreatment(bucket.treatment(), interpreter, groupKey.tableId(),
+ pipeconf.pipelineModel());
+
+ if (tableAction.type() != ACTION) {
+ throw new PiTranslationException(format(
+ "PI table action of type %s is not supported in groups", tableAction.type()));
+ }
+
+ piActionGroupBuilder.addMember(PiActionGroupMember.builder()
+ .withId(PiActionGroupMemberId.of(memberId))
+ .withAction((PiAction) tableAction)
+ .withWeight(bucket.weight())
+ .build());
+ }
+
+ return piActionGroupBuilder.build();
+ }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslationServiceImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
similarity index 62%
rename from core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslationServiceImpl.java
rename to core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
index c55a57c..68a2d20 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslationServiceImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
@@ -23,26 +23,27 @@
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.group.Group;
import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.runtime.PiFlowRuleTranslationService;
+import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.onosproject.net.pi.runtime.PiTranslationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.onosproject.net.pi.impl.PiFlowRuleTranslator.translateFlowRule;
-
/**
- * Implementation of the protocol-independent flow rule translation service.
+ * Implementation of the protocol-independent translation service.
*/
@Component(immediate = true)
@Service
-public class PiFlowRuleTranslationServiceImpl implements PiFlowRuleTranslationService {
+public class PiTranslationServiceImpl implements PiTranslationService {
private final Logger log = LoggerFactory.getLogger(this.getClass());
- // TODO: implement cache to speed up translation of flow rules.
+ // TODO: implement cache to speed up translation.
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@@ -58,15 +59,21 @@
}
@Override
- public PiTableEntry translate(FlowRule rule, PiPipeconf pipeconf)
- throws PiFlowRuleTranslationException {
+ public PiTableEntry translateFlowRule(FlowRule rule, PiPipeconf pipeconf) throws PiTranslationException {
+ return PiFlowRuleTranslator.translate(rule, pipeconf, getDevice(rule.deviceId()));
+ }
- final Device device = deviceService.getDevice(rule.deviceId());
+ @Override
+ public PiActionGroup translateGroup(Group group, PiPipeconf pipeconf) throws PiTranslationException {
+ return PiGroupTranslator.translate(group, pipeconf, getDevice(group.deviceId()));
+ }
+
+ private Device getDevice(DeviceId deviceId) throws PiTranslationException {
+ final Device device = deviceService.getDevice(deviceId);
if (device == null) {
- throw new PiFlowRuleTranslationException("Unable to get device " + rule.deviceId());
+ throw new PiTranslationException("Unable to get device " + deviceId);
}
-
- return translateFlowRule(rule, pipeconf, device);
+ return device;
}
}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiUtils.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiUtils.java
new file mode 100644
index 0000000..aab8b40
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiUtils.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.impl;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.flow.IndexTableId;
+import org.onosproject.net.flow.TableId;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
+import org.onosproject.net.pi.runtime.PiTableId;
+import org.onosproject.net.pi.runtime.PiTranslationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.lang.String.format;
+
+/**
+ * PI utility class.
+ */
+final class PiUtils {
+
+ private static final Logger log = LoggerFactory.getLogger(PiUtils.class);
+
+ private PiUtils() {
+ // Hides constructor.
+ }
+
+ static PiPipelineInterpreter getInterpreterOrNull(Device device, PiPipeconf pipeconf) {
+ if (device != null) {
+ return device.is(PiPipelineInterpreter.class) ? device.as(PiPipelineInterpreter.class) : null;
+ } else {
+ // The case of device == null should be admitted only during unit testing.
+ // In any other case, the interpreter should be constructed using the device.as() method to make sure that
+ // behaviour's handler/data attributes are correctly populated.
+ // FIXME: modify test class PiFlowRuleTranslatorTest to avoid passing null device
+ // I.e. we need to create a device object that supports is/as method for obtaining the interpreter.
+ log.warn("getInterpreterOrNull() called with device == null, is this a unit test?");
+ try {
+ return (PiPipelineInterpreter) pipeconf.implementation(PiPipelineInterpreter.class)
+ .orElse(null)
+ .newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new RuntimeException(format("Unable to instantiate interpreter of pipeconf %s", pipeconf.id()));
+ }
+ }
+ }
+
+ static PiTableId translateTableId(TableId tableId, PiPipelineInterpreter interpreter)
+ throws PiTranslationService.PiTranslationException {
+ switch (tableId.type()) {
+ case PIPELINE_INDEPENDENT:
+ return (PiTableId) tableId;
+ case INDEX:
+ IndexTableId indexId = (IndexTableId) tableId;
+ if (interpreter == null) {
+ throw new PiTranslationService.PiTranslationException(format(
+ "Unable to map table ID '%d' from index to PI: missing interpreter", indexId.id()));
+ } else if (!interpreter.mapFlowRuleTableId(indexId.id()).isPresent()) {
+ throw new PiTranslationService.PiTranslationException(format(
+ "Unable to map table ID '%d' from index to PI: missing ID in interpreter", indexId.id()));
+ } else {
+ return interpreter.mapFlowRuleTableId(indexId.id()).get();
+ }
+ default:
+ throw new PiTranslationService.PiTranslationException(format(
+ "Unrecognized table ID type %s", tableId.type().name()));
+ }
+ }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorTest.java
deleted file mode 100644
index 6874292..0000000
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorTest.java
+++ /dev/null
@@ -1,164 +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.net.pi.impl;
-
-import com.google.common.testing.EqualsTester;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.MacAddress;
-import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.DefaultApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultFlowRule;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.pi.model.DefaultPiPipeconf;
-import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.model.PiPipeconfId;
-import org.onosproject.net.pi.model.PiPipelineInterpreter;
-import org.onosproject.net.pi.runtime.PiTableEntry;
-import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
-
-import java.util.Optional;
-import java.util.Random;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.onosproject.net.pi.impl.MockInterpreter.*;
-import static org.onosproject.net.pi.impl.PiFlowRuleTranslator.MAX_PI_PRIORITY;
-
-/**
- * Tests for {@link PiFlowRuleTranslator}.
- */
-@SuppressWarnings("ConstantConditions")
-public class PiFlowRuleTranslatorTest {
-
- private static final String BMV2_JSON_PATH = "/org/onosproject/net/pi/impl/default.json";
- private static final short IN_PORT_MASK = 0x01ff; // 9-bit mask
- private static final short ETH_TYPE_MASK = (short) 0xffff;
-
- private Random random = new Random();
- private PiPipeconf pipeconf;
-
- @Before
- public void setUp() throws Exception {
- pipeconf = DefaultPiPipeconf.builder()
- .withId(new PiPipeconfId("mock-pipeconf"))
- .withPipelineModel(Bmv2PipelineModelParser.parse(this.getClass().getResource(BMV2_JSON_PATH)))
- .addBehaviour(PiPipelineInterpreter.class, MockInterpreter.class)
- .build();
- }
-
- @Test
- public void testTranslate() throws Exception {
-
- DeviceId deviceId = DeviceId.NONE;
- ApplicationId appId = new DefaultApplicationId(1, "test");
- int tableId = 0;
- MacAddress ethDstMac = MacAddress.valueOf(random.nextLong());
- MacAddress ethSrcMac = MacAddress.valueOf(random.nextLong());
- short ethType = (short) (0x0000FFFF & random.nextInt());
- short outPort = (short) random.nextInt(65);
- short inPort = (short) random.nextInt(65);
- int timeout = random.nextInt(100);
- int priority = random.nextInt(100);
-
- TrafficSelector matchInPort1 = DefaultTrafficSelector
- .builder()
- .matchInPort(PortNumber.portNumber(inPort))
- .matchEthDst(ethDstMac)
- .matchEthSrc(ethSrcMac)
- .matchEthType(ethType)
- .build();
-
- TrafficTreatment outPort2 = DefaultTrafficTreatment
- .builder()
- .setOutput(PortNumber.portNumber(outPort))
- .build();
-
- FlowRule rule1 = DefaultFlowRule.builder()
- .forDevice(deviceId)
- .forTable(tableId)
- .fromApp(appId)
- .withSelector(matchInPort1)
- .withTreatment(outPort2)
- .makeTemporary(timeout)
- .withPriority(priority)
- .build();
-
- FlowRule rule2 = DefaultFlowRule.builder()
- .forDevice(deviceId)
- .forTable(tableId)
- .fromApp(appId)
- .withSelector(matchInPort1)
- .withTreatment(outPort2)
- .makeTemporary(timeout)
- .withPriority(priority)
- .build();
-
- PiTableEntry entry1 = PiFlowRuleTranslator.translateFlowRule(rule1, pipeconf, null);
- PiTableEntry entry2 = PiFlowRuleTranslator.translateFlowRule(rule1, pipeconf, null);
-
- // check equality, i.e. same rules must produce same entries
- new EqualsTester()
- .addEqualityGroup(rule1, rule2)
- .addEqualityGroup(entry1, entry2)
- .testEquals();
-
- int numMatchParams = pipeconf.pipelineModel().table(TABLE0).get().matchFields().size();
- // parse values stored in entry1
- PiTernaryFieldMatch inPortParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(IN_PORT_ID).get();
- PiTernaryFieldMatch ethDstParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_DST_ID).get();
- PiTernaryFieldMatch ethSrcParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_SRC_ID).get();
- PiTernaryFieldMatch ethTypeParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_TYPE_ID).get();
- Optional<Double> expectedTimeout = pipeconf.pipelineModel().table(TABLE0).get().supportsAging()
- ? Optional.of((double) rule1.timeout()) : Optional.empty();
-
- // check that the number of parameters in the entry is the same as the number of table keys
- assertThat("Incorrect number of match parameters",
- entry1.matchKey().fieldMatches().size(), is(equalTo(numMatchParams)));
-
- // check that values stored in entry are the same used for the flow rule
- assertThat("Incorrect inPort match param value",
- inPortParam.value().asReadOnlyBuffer().getShort(), is(equalTo(inPort)));
- assertThat("Incorrect inPort match param mask",
- inPortParam.mask().asReadOnlyBuffer().getShort(), is(equalTo(IN_PORT_MASK)));
- assertThat("Incorrect ethDestMac match param value",
- ethDstParam.value().asArray(), is(equalTo(ethDstMac.toBytes())));
- assertThat("Incorrect ethDestMac match param mask",
- ethDstParam.mask().asArray(), is(equalTo(MacAddress.BROADCAST.toBytes())));
- assertThat("Incorrect ethSrcMac match param value",
- ethSrcParam.value().asArray(), is(equalTo(ethSrcMac.toBytes())));
- assertThat("Incorrect ethSrcMac match param mask",
- ethSrcParam.mask().asArray(), is(equalTo(MacAddress.BROADCAST.toBytes())));
- assertThat("Incorrect ethType match param value",
- ethTypeParam.value().asReadOnlyBuffer().getShort(), is(equalTo(ethType)));
- assertThat("Incorrect ethType match param mask",
- ethTypeParam.mask().asReadOnlyBuffer().getShort(), is(equalTo(ETH_TYPE_MASK)));
- assertThat("Incorrect priority value",
- entry1.priority().get(), is(equalTo(MAX_PI_PRIORITY - rule1.priority())));
- assertThat("Incorrect timeout value",
- entry1.timeout(), is(equalTo(expectedTimeout)));
-
- }
-}
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
new file mode 100644
index 0000000..2e6fa5f
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.testing.EqualsTester;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.TestApplicationId;
+import org.onosproject.bmv2.model.Bmv2PipelineModelParser;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.pi.model.DefaultPiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.runtime.PiActionGroupMember;
+import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
+import org.onosproject.net.pi.runtime.PiActionId;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.net.pi.runtime.PiActionParamId;
+import org.onosproject.net.pi.runtime.PiActionProfileId;
+import org.onosproject.net.pi.runtime.PiGroupKey;
+import org.onosproject.net.pi.runtime.PiTableAction;
+import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.onosproject.net.pi.runtime.PiTableId;
+import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Random;
+
+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.onlab.util.ImmutableByteSequence.fit;
+import static org.onosproject.net.group.GroupDescription.Type.SELECT;
+import static org.onosproject.net.pi.impl.MockInterpreter.*;
+import static org.onosproject.net.pi.impl.PiFlowRuleTranslator.MAX_PI_PRIORITY;
+
+/**
+ * Tests for {@link PiFlowRuleTranslator}.
+ */
+@SuppressWarnings("ConstantConditions")
+public class PiTranslatorServiceTest {
+
+ private static final String BMV2_JSON_PATH = "/org/onosproject/net/pi/impl/default.json";
+ private static final short IN_PORT_MASK = 0x01ff; // 9-bit mask
+ private static final short ETH_TYPE_MASK = (short) 0xffff;
+ private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:dummy:1");
+ private static final ApplicationId APP_ID = TestApplicationId.create("dummy");
+ private static final PiTableId ECMP_TABLE_ID = PiTableId.of("ecmp");
+ private static final PiActionProfileId ACT_PROF_ID = PiActionProfileId.of("ecmp_selector");
+ private static final GroupId GROUP_ID = GroupId.valueOf(1);
+ private static final PiActionId EGRESS_PORT_ACTION_ID = PiActionId.of("set_egress_port");
+ private static final int PORT_BITWIDTH = 9;
+ private static final PiActionParamId PORT_PARAM_ID = PiActionParamId.of("port");
+ private static final List<GroupBucket> BUCKET_LIST = ImmutableList.of(outputBucket(1),
+ outputBucket(2),
+ outputBucket(3)
+ );
+ private static final PiGroupKey GROUP_KEY = new PiGroupKey(ECMP_TABLE_ID, ACT_PROF_ID, GROUP_ID.id());
+ private static final GroupBuckets BUCKETS = new GroupBuckets(BUCKET_LIST);
+ private static final GroupDescription GROUP_DESC =
+ new DefaultGroupDescription(DEVICE_ID, SELECT, BUCKETS, GROUP_KEY, GROUP_ID.id(), APP_ID);
+ private static final Group GROUP = new DefaultGroup(GROUP_ID, GROUP_DESC);
+ private static final int DEFAULT_MEMBER_WEIGHT = 1;
+ private static final int BASE_MEM_ID = 65535;
+ private Collection<PiActionGroupMember> expectedMembers;
+
+ private Random random = new Random();
+ private PiPipeconf pipeconf;
+
+ @Before
+ public void setUp() throws Exception {
+ pipeconf = DefaultPiPipeconf.builder()
+ .withId(new PiPipeconfId("mock-pipeconf"))
+ .withPipelineModel(Bmv2PipelineModelParser.parse(this.getClass().getResource(BMV2_JSON_PATH)))
+ .addBehaviour(PiPipelineInterpreter.class, MockInterpreter.class)
+ .build();
+
+ expectedMembers = ImmutableSet.of(outputMember(1),
+ outputMember(2),
+ outputMember(3));
+ }
+
+ @Test
+ public void testTranslateFlowRules() throws Exception {
+
+ ApplicationId appId = new DefaultApplicationId(1, "test");
+ int tableId = 0;
+ MacAddress ethDstMac = MacAddress.valueOf(random.nextLong());
+ MacAddress ethSrcMac = MacAddress.valueOf(random.nextLong());
+ short ethType = (short) (0x0000FFFF & random.nextInt());
+ short outPort = (short) random.nextInt(65);
+ short inPort = (short) random.nextInt(65);
+ int timeout = random.nextInt(100);
+ int priority = random.nextInt(100);
+
+ TrafficSelector matchInPort1 = DefaultTrafficSelector
+ .builder()
+ .matchInPort(PortNumber.portNumber(inPort))
+ .matchEthDst(ethDstMac)
+ .matchEthSrc(ethSrcMac)
+ .matchEthType(ethType)
+ .build();
+
+ TrafficTreatment outPort2 = DefaultTrafficTreatment
+ .builder()
+ .setOutput(PortNumber.portNumber(outPort))
+ .build();
+
+ FlowRule rule1 = DefaultFlowRule.builder()
+ .forDevice(DEVICE_ID)
+ .forTable(tableId)
+ .fromApp(appId)
+ .withSelector(matchInPort1)
+ .withTreatment(outPort2)
+ .makeTemporary(timeout)
+ .withPriority(priority)
+ .build();
+
+ FlowRule rule2 = DefaultFlowRule.builder()
+ .forDevice(DEVICE_ID)
+ .forTable(tableId)
+ .fromApp(appId)
+ .withSelector(matchInPort1)
+ .withTreatment(outPort2)
+ .makeTemporary(timeout)
+ .withPriority(priority)
+ .build();
+
+ PiTableEntry entry1 = PiFlowRuleTranslator.translate(rule1, pipeconf, null);
+ PiTableEntry entry2 = PiFlowRuleTranslator.translate(rule1, pipeconf, null);
+
+ // check equality, i.e. same rules must produce same entries
+ new EqualsTester()
+ .addEqualityGroup(rule1, rule2)
+ .addEqualityGroup(entry1, entry2)
+ .testEquals();
+
+ int numMatchParams = pipeconf.pipelineModel().table(TABLE0).get().matchFields().size();
+ // parse values stored in entry1
+ PiTernaryFieldMatch inPortParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(IN_PORT_ID).get();
+ PiTernaryFieldMatch ethDstParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_DST_ID).get();
+ PiTernaryFieldMatch ethSrcParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_SRC_ID).get();
+ PiTernaryFieldMatch ethTypeParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(ETH_TYPE_ID).get();
+ Optional<Double> expectedTimeout = pipeconf.pipelineModel().table(TABLE0).get().supportsAging()
+ ? Optional.of((double) rule1.timeout()) : Optional.empty();
+
+ // check that the number of parameters in the entry is the same as the number of table keys
+ assertThat("Incorrect number of match parameters",
+ entry1.matchKey().fieldMatches().size(), is(equalTo(numMatchParams)));
+
+ // check that values stored in entry are the same used for the flow rule
+ assertThat("Incorrect inPort match param value",
+ inPortParam.value().asReadOnlyBuffer().getShort(), is(equalTo(inPort)));
+ assertThat("Incorrect inPort match param mask",
+ inPortParam.mask().asReadOnlyBuffer().getShort(), is(equalTo(IN_PORT_MASK)));
+ assertThat("Incorrect ethDestMac match param value",
+ ethDstParam.value().asArray(), is(equalTo(ethDstMac.toBytes())));
+ assertThat("Incorrect ethDestMac match param mask",
+ ethDstParam.mask().asArray(), is(equalTo(MacAddress.BROADCAST.toBytes())));
+ assertThat("Incorrect ethSrcMac match param value",
+ ethSrcParam.value().asArray(), is(equalTo(ethSrcMac.toBytes())));
+ assertThat("Incorrect ethSrcMac match param mask",
+ ethSrcParam.mask().asArray(), is(equalTo(MacAddress.BROADCAST.toBytes())));
+ assertThat("Incorrect ethType match param value",
+ ethTypeParam.value().asReadOnlyBuffer().getShort(), is(equalTo(ethType)));
+ assertThat("Incorrect ethType match param mask",
+ ethTypeParam.mask().asReadOnlyBuffer().getShort(), is(equalTo(ETH_TYPE_MASK)));
+ assertThat("Incorrect priority value",
+ entry1.priority().get(), is(equalTo(MAX_PI_PRIORITY - rule1.priority())));
+ assertThat("Incorrect timeout value",
+ entry1.timeout(), is(equalTo(expectedTimeout)));
+
+ }
+
+ private static GroupBucket outputBucket(int portNum) {
+ ImmutableByteSequence paramVal = copyFrom(portNum);
+ PiActionParam param = new PiActionParam(PiActionParamId.of(PORT_PARAM_ID.name()), paramVal);
+ PiTableAction action = PiAction.builder().withId(EGRESS_PORT_ACTION_ID).withParameter(param).build();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .add(Instructions.piTableAction(action))
+ .build();
+ return DefaultGroupBucket.createSelectGroupBucket(treatment);
+ }
+
+ private static PiActionGroupMember outputMember(int portNum)
+ throws ImmutableByteSequence.ByteSequenceTrimException {
+ PiActionParam param = new PiActionParam(PORT_PARAM_ID, fit(copyFrom(portNum), PORT_BITWIDTH));
+ PiAction piAction = PiAction.builder()
+ .withId(EGRESS_PORT_ACTION_ID)
+ .withParameter(param).build();
+ return PiActionGroupMember.builder()
+ .withAction(piAction)
+ .withId(PiActionGroupMemberId.of(BASE_MEM_ID + portNum))
+ .withWeight(DEFAULT_MEMBER_WEIGHT)
+ .build();
+ }
+
+ /**
+ * Test add group with buckets.
+ */
+ @Test
+ public void testTranslateGroups() throws Exception {
+
+ PiActionGroup piGroup1 = PiGroupTranslator.translate(GROUP, pipeconf, null);
+ PiActionGroup piGroup2 = PiGroupTranslator.translate(GROUP, pipeconf, null);
+
+ new EqualsTester()
+ .addEqualityGroup(piGroup1, piGroup2)
+ .testEquals();
+
+ assertThat("Group ID must be equal",
+ piGroup1.id().id(), is(equalTo(GROUP_ID.id())));
+ assertThat("Group type must be SELECT",
+ piGroup1.type(), is(equalTo(PiActionGroup.Type.SELECT)));
+ assertThat("Action profile ID must be equal",
+ piGroup1.actionProfileId(), is(equalTo(ACT_PROF_ID)));
+
+ // members installed
+ Collection<PiActionGroupMember> members = piGroup1.members();
+ 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));
+ }
+}
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index 54dd14c..2de7dcc 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -213,6 +213,7 @@
import org.onosproject.net.pi.runtime.PiExactFieldMatch;
import org.onosproject.net.pi.runtime.PiFieldMatch;
import org.onosproject.net.pi.runtime.PiActionProfileId;
+import org.onosproject.net.pi.runtime.PiGroupKey;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
import org.onosproject.net.pi.runtime.PiMatchKey;
@@ -607,6 +608,7 @@
PiActionParamId.class,
PiExactFieldMatch.class,
PiFieldMatch.class,
+ PiGroupKey.class,
PiHeaderFieldId.class,
PiLpmFieldMatch.class,
PiMatchKey.class,
diff --git a/drivers/p4runtime/BUCK b/drivers/p4runtime/BUCK
index 5c50f12..b8167e8 100644
--- a/drivers/p4runtime/BUCK
+++ b/drivers/p4runtime/BUCK
@@ -5,22 +5,14 @@
'//protocols/p4runtime/api:onos-protocols-p4runtime-api',
'//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER,
'//lib:grpc-netty-' + GRPC_VER,
- '//core/store/serializers:onos-core-serializers',
- '//lib:KRYO',
]
BUNDLES = [
':onos-drivers-p4runtime',
]
-TEST_DEPS = [
- '//lib:TEST_ADAPTERS',
- '//core/api:onos-api-tests',
-]
-
-osgi_jar_with_tests (
+osgi_jar (
deps = COMPILE_DEPS,
- test_deps = TEST_DEPS,
)
onos_app (
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/AbstractP4RuntimeHandlerBehaviour.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/AbstractP4RuntimeHandlerBehaviour.java
index 8a5a2dd..35de9ed 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/AbstractP4RuntimeHandlerBehaviour.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/AbstractP4RuntimeHandlerBehaviour.java
@@ -24,6 +24,7 @@
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.runtime.PiPipeconfService;
+import org.onosproject.net.pi.runtime.PiTranslationService;
import org.onosproject.p4runtime.api.P4RuntimeClient;
import org.onosproject.p4runtime.api.P4RuntimeController;
import org.slf4j.Logger;
@@ -47,6 +48,7 @@
protected P4RuntimeController controller;
protected PiPipeconf pipeconf;
protected P4RuntimeClient client;
+ protected PiTranslationService piTranslationService;
/**
* Initializes this behaviour attributes. Returns true if the operation was successful, false otherwise. This method
@@ -80,6 +82,8 @@
}
pipeconf = piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).get();
+ piTranslationService = handler().get(PiTranslationService.class);
+
return true;
}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
index c2cc0b5..c9fe49d83 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
@@ -31,7 +31,7 @@
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.runtime.PiCounterId;
import org.onosproject.net.pi.runtime.PiDirectCounterCellId;
-import org.onosproject.net.pi.runtime.PiFlowRuleTranslationService;
+import org.onosproject.net.pi.runtime.PiTranslationService;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTableId;
import org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType;
@@ -95,7 +95,6 @@
private PiPipelineModel pipelineModel;
private PiPipelineInterpreter interpreter;
- private PiFlowRuleTranslationService piFlowRuleTranslationService;
@Override
protected boolean setupBehaviour() {
@@ -110,7 +109,6 @@
}
interpreter = device.as(PiPipelineInterpreter.class);
pipelineModel = pipeconf.pipelineModel();
- piFlowRuleTranslationService = handler().get(PiFlowRuleTranslationService.class);
return true;
}
@@ -249,8 +247,8 @@
PiTableEntry piTableEntry;
try {
- piTableEntry = piFlowRuleTranslationService.translate(rule, pipeconf);
- } catch (PiFlowRuleTranslationService.PiFlowRuleTranslationException e) {
+ piTableEntry = piTranslationService.translateFlowRule(rule, pipeconf);
+ } catch (PiTranslationService.PiTranslationException e) {
log.warn("Unable to translate flow rule: {} - {}", e.getMessage(), rule);
continue; // next rule
}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java
index e108123..b9f9766 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java
@@ -16,42 +16,28 @@
package org.onosproject.drivers.p4runtime;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
-import org.onlab.util.KryoNamespace;
import org.onosproject.core.GroupId;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.PiInstruction;
import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupBucket;
import org.onosproject.net.group.GroupOperation;
import org.onosproject.net.group.GroupOperations;
import org.onosproject.net.group.GroupProgrammable;
import org.onosproject.net.group.GroupStore;
-import org.onosproject.net.pi.model.PiPipelineInterpreter;
-import org.onosproject.net.pi.runtime.PiActionProfileId;
-import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiActionGroupId;
-import org.onosproject.net.pi.runtime.PiActionGroupMember;
-import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
-import org.onosproject.net.pi.runtime.PiTableAction;
-import org.onosproject.net.pi.runtime.PiTableId;
+import org.onosproject.net.pi.runtime.PiActionProfileId;
+import org.onosproject.net.pi.runtime.PiTranslationService;
import org.onosproject.p4runtime.api.P4RuntimeClient;
import org.onosproject.p4runtime.api.P4RuntimeGroupReference;
import org.onosproject.p4runtime.api.P4RuntimeGroupWrapper;
-import org.onosproject.store.serializers.KryoNamespaces;
import org.slf4j.Logger;
-import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -70,11 +56,6 @@
private static final String ACT_GRP = "action group";
private static final String INSERT = "insert";
private static final Logger log = getLogger(P4RuntimeGroupProgrammable.class);
- private static final int GROUP_ID_MASK = 0xffff;
- public static final KryoNamespace KRYO = new KryoNamespace.Builder()
- .register(KryoNamespaces.API)
- .register(DefaultP4RuntimeGroupCookie.class)
- .build("P4RuntimeGroupProgrammable");
/*
* About action groups in P4runtime:
@@ -102,28 +83,12 @@
// TODO: replace with distribute store
private static final Map<P4RuntimeGroupReference, P4RuntimeGroupWrapper> GROUP_STORE = Maps.newConcurrentMap();
- private PiPipelineInterpreter interpreter;
-
- protected boolean init() {
- if (!setupBehaviour()) {
- return false;
- }
- Device device = deviceService.getDevice(deviceId);
- // Need an interpreter to map the bucket treatment to a PI action
- if (!device.is(PiPipelineInterpreter.class)) {
- log.warn("Can't find interpreter for device {}", device.id());
- } else {
- interpreter = device.as(PiPipelineInterpreter.class);
- }
- return true;
- }
-
@Override
public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
- if (!init()) {
- // Ignore group operation of not initialized.
+ if (!setupBehaviour()) {
return;
}
+
Device device = handler().get(DeviceService.class).getDevice(deviceId);
for (GroupOperation groupOp : groupOps.operations()) {
@@ -136,71 +101,38 @@
GroupStore groupStore = handler().get(GroupStore.class);
Group group = groupStore.getGroup(device.id(), groupId);
- // Most of this logic can go in a core service, e.g. PiGroupTranslationService
- // From a P4Runtime perspective, we need first to insert members, then the group.
- PiActionGroupId piActionGroupId = PiActionGroupId.of(groupOp.groupId().id());
-
- PiActionGroup.Builder piActionGroupBuilder = PiActionGroup.builder()
- .withId(piActionGroupId);
-
- switch (group.type()) {
- case SELECT:
- piActionGroupBuilder.withType(PiActionGroup.Type.SELECT);
- break;
- default:
- log.warn("Group type {} not supported, ignore group {}.", group.type(), groupId);
- return;
- }
- /*
- Problem:
- In P4Runtime, action profiles (i.e. group tables) are specific to one or more tables.
- Mapping of treatments depends on the target table. How do we derive the target table from here?
-
- Solution:
- - Add table information into app cookie and put into group description
- */
- // TODO: notify group service if we get deserialize error
- DefaultP4RuntimeGroupCookie defaultP4RuntimeGroupCookie = KRYO.deserialize(group.appCookie().key());
- PiTableId piTableId = defaultP4RuntimeGroupCookie.tableId();
- PiActionProfileId piActionProfileId = defaultP4RuntimeGroupCookie.actionProfileId();
- piActionGroupBuilder.withActionProfileId(piActionProfileId);
-
- List<PiActionGroupMember> members = buildMembers(group, piActionGroupId, piTableId);
- if (members == null) {
- log.warn("Can't build members for group {} on {}", group, device.id());
+ PiActionGroup piActionGroup;
+ try {
+ piActionGroup = piTranslationService.translateGroup(group, pipeconf);
+ } catch (PiTranslationService.PiTranslationException e) {
+ log.warn("Unable translate group, aborting group operation {}: {}", groupOp.opType(), e.getMessage());
return;
}
- piActionGroupBuilder.addMembers(members);
- PiActionGroup piActionGroup = piActionGroupBuilder.build();
+ P4RuntimeGroupReference groupRef = new P4RuntimeGroupReference(deviceId, piActionGroup.actionProfileId(),
+ piActionGroup.id());
- P4RuntimeGroupReference groupRef =
- new P4RuntimeGroupReference(deviceId, piActionProfileId, piActionGroupId);
Lock lock = GROUP_LOCKS.computeIfAbsent(groupRef, k -> new ReentrantLock());
lock.lock();
-
try {
P4RuntimeGroupWrapper oldGroupWrapper = GROUP_STORE.get(groupRef);
- P4RuntimeGroupWrapper newGroupWrapper =
- new P4RuntimeGroupWrapper(piActionGroup, group, System.currentTimeMillis());
- boolean success;
+ P4RuntimeGroupWrapper newGroupWrapper = new P4RuntimeGroupWrapper(piActionGroup, group,
+ System.currentTimeMillis());
switch (groupOp.opType()) {
case ADD:
case MODIFY:
- success = writeGroupToDevice(oldGroupWrapper, piActionGroup, members);
- if (success) {
+ if (writeGroupToDevice(oldGroupWrapper, piActionGroup)) {
GROUP_STORE.put(groupRef, newGroupWrapper);
}
break;
case DELETE:
- success = deleteGroupFromDevice(piActionGroup, members);
- if (success) {
+ if (deleteGroupFromDevice(piActionGroup)) {
GROUP_STORE.remove(groupRef);
}
break;
default:
- throw new UnsupportedOperationException();
+ log.warn("Group operation {} not supported", groupOp.opType());
}
} finally {
lock.unlock();
@@ -211,13 +143,10 @@
* Installs action group and members to device via client interface.
*
* @param oldGroupWrapper old group wrapper for the group; null if not exists
- * @param piActionGroup the action group to be installed
- * @param members members of the action group
+ * @param piActionGroup the action group to be installed
* @return true if install success; false otherwise
*/
- private boolean writeGroupToDevice(P4RuntimeGroupWrapper oldGroupWrapper,
- PiActionGroup piActionGroup,
- Collection<PiActionGroupMember> members) {
+ private boolean writeGroupToDevice(P4RuntimeGroupWrapper oldGroupWrapper, PiActionGroup piActionGroup) {
boolean success = true;
CompletableFuture<Boolean> writeSuccess;
if (checkStoreBeforeUpdate && oldGroupWrapper != null &&
@@ -226,11 +155,9 @@
return true;
}
if (deleteBeforeUpdate && oldGroupWrapper != null) {
- success = deleteGroupFromDevice(oldGroupWrapper.piActionGroup(),
- oldGroupWrapper.piActionGroup().members());
+ success = deleteGroupFromDevice(oldGroupWrapper.piActionGroup());
}
writeSuccess = client.writeActionGroupMembers(piActionGroup,
- members,
P4RuntimeClient.WriteOperationType.INSERT,
pipeconf);
success = success && completeSuccess(writeSuccess, ACT_GRP_MEMS, INSERT);
@@ -242,18 +169,16 @@
return success;
}
- private boolean deleteGroupFromDevice(PiActionGroup piActionGroup,
- Collection<PiActionGroupMember> members) {
+ private boolean deleteGroupFromDevice(PiActionGroup piActionGroup) {
boolean success;
CompletableFuture<Boolean> writeSuccess;
writeSuccess = client.writeActionGroup(piActionGroup,
- P4RuntimeClient.WriteOperationType.DELETE,
- pipeconf);
+ P4RuntimeClient.WriteOperationType.DELETE,
+ pipeconf);
success = completeSuccess(writeSuccess, ACT_GRP, DELETE);
writeSuccess = client.writeActionGroupMembers(piActionGroup,
- members,
- P4RuntimeClient.WriteOperationType.DELETE,
- pipeconf);
+ P4RuntimeClient.WriteOperationType.DELETE,
+ pipeconf);
success = success && completeSuccess(writeSuccess, ACT_GRP_MEMS, DELETE);
return success;
}
@@ -268,98 +193,16 @@
}
}
- /**
- * Build pi action group members from group.
- *
- * @param group the group
- * @param piActionGroupId the PI action group id of the group
- * @param piTableId the PI table related to the group
- * @return list of PI action group members; null if can't build member list
- */
- private List<PiActionGroupMember> buildMembers(Group group, PiActionGroupId piActionGroupId, PiTableId piTableId) {
- GroupId groupId = group.id();
- ImmutableList.Builder<PiActionGroupMember> membersBuilder = ImmutableList.builder();
-
- int bucketIdx = 0;
- for (GroupBucket bucket : group.buckets().buckets()) {
- /*
- Problem:
- In P4Runtime action group members, i.e. action buckets, are associated to a numeric ID chosen
- at member insertion time. This ID must be unique for the whole action profile (i.e. the group table in
- OpenFlow). In ONOS, GroupBucket doesn't specify any ID.
-
- Solutions:
- - Change GroupBucket API to force application wanting to perform group operations to specify a member id.
- - Maintain state to dynamically allocate/deallocate member IDs, e.g. in a dedicated service, or in a
- P4Runtime Group Provider.
-
- Hack:
- Statically derive member ID by combining groupId and position of the bucket in the list.
- */
- ByteBuffer bb = ByteBuffer.allocate(4)
- .putShort((short) (piActionGroupId.id() & GROUP_ID_MASK))
- .putShort((short) bucketIdx);
- bb.rewind();
- int memberId = bb.getInt();
-
- bucketIdx++;
- PiAction action;
- if (interpreter != null) {
- // if we have interpreter, use interpreter
- try {
- action = interpreter.mapTreatment(bucket.treatment(), piTableId);
- } catch (PiPipelineInterpreter.PiInterpreterException e) {
- log.warn("Can't map treatment {} to action due to {}, ignore group {}",
- bucket.treatment(), e.getMessage(), groupId);
- return null;
- }
- } else {
- // if we don't have interpreter, accept PiInstruction only
- TrafficTreatment treatment = bucket.treatment();
-
- if (treatment.allInstructions().size() > 1) {
- log.warn("Treatment {} has multiple instructions, ignore group {}",
- treatment, groupId);
- return null;
- }
- Instruction instruction = treatment.allInstructions().get(0);
- if (instruction.type() != Instruction.Type.PROTOCOL_INDEPENDENT) {
- log.warn("Instruction {} is not a PROTOCOL_INDEPENDENT type, ignore group {}",
- instruction, groupId);
- return null;
- }
-
- PiInstruction piInstruction = (PiInstruction) instruction;
- if (piInstruction.action().type() != PiTableAction.Type.ACTION) {
- log.warn("Action {} is not an ACTION type, ignore group {}",
- piInstruction.action(), groupId);
- return null;
- }
- action = (PiAction) piInstruction.action();
- }
-
- PiActionGroupMember member = PiActionGroupMember.builder()
- .withId(PiActionGroupMemberId.of(memberId))
- .withAction(action)
- .withWeight(bucket.weight())
- .build();
-
- membersBuilder.add(member);
- }
- return membersBuilder.build();
- }
-
@Override
public Collection<Group> getGroups() {
- if (!init()) {
- return Collections.emptySet();
+ if (!setupBehaviour()) {
+ return Collections.emptyList();
}
Collection<Group> result = Sets.newHashSet();
Collection<PiActionProfileId> piActionProfileIds = Sets.newHashSet();
- // Collection action profile Ids
- // TODO: find better way to get all action profile ids....
+ // TODO: find better way to get all action profile ids. e.g. by providing them in the interpreter
GROUP_STORE.forEach((groupRef, wrapper) -> piActionProfileIds.add(groupRef.actionProfileId()));
AtomicBoolean success = new AtomicBoolean(true);
@@ -409,32 +252,4 @@
}
}
- /**
- * P4Runtime app cookie for group.
- */
- public static class DefaultP4RuntimeGroupCookie {
- private PiTableId tableId;
- private PiActionProfileId piActionProfileId;
- private Integer groupId;
-
- public DefaultP4RuntimeGroupCookie(PiTableId tableId,
- PiActionProfileId piActionProfileId,
- Integer groupId) {
- this.tableId = tableId;
- this.piActionProfileId = piActionProfileId;
- this.groupId = groupId;
- }
-
- public PiTableId tableId() {
- return tableId;
- }
-
- public PiActionProfileId actionProfileId() {
- return piActionProfileId;
- }
-
- public Integer groupId() {
- return groupId;
- }
- }
}
diff --git a/drivers/p4runtime/src/test/java/org/onosproject/drivers/p4runtime/P4runtimeGroupProgrammableTest.java b/drivers/p4runtime/src/test/java/org/onosproject/drivers/p4runtime/P4runtimeGroupProgrammableTest.java
deleted file mode 100644
index c334218..0000000
--- a/drivers/p4runtime/src/test/java/org/onosproject/drivers/p4runtime/P4runtimeGroupProgrammableTest.java
+++ /dev/null
@@ -1,291 +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.drivers.p4runtime;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import org.easymock.Capture;
-import org.easymock.EasyMock;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.util.ImmutableByteSequence;
-import org.onosproject.TestApplicationId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.GroupId;
-import org.onosproject.drivers.p4runtime.P4RuntimeGroupProgrammable.DefaultP4RuntimeGroupCookie;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.driver.DriverData;
-import org.onosproject.net.driver.DriverHandler;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.group.DefaultGroup;
-import org.onosproject.net.group.DefaultGroupBucket;
-import org.onosproject.net.group.DefaultGroupDescription;
-import org.onosproject.net.group.DefaultGroupKey;
-import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupBucket;
-import org.onosproject.net.group.GroupBuckets;
-import org.onosproject.net.group.GroupDescription;
-import org.onosproject.net.group.GroupKey;
-import org.onosproject.net.group.GroupOperation;
-import org.onosproject.net.group.GroupOperations;
-import org.onosproject.net.group.GroupStore;
-import org.onosproject.net.pi.model.DefaultPiPipeconf;
-import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.model.PiPipeconfId;
-import org.onosproject.net.pi.model.PiPipelineInterpreter;
-import org.onosproject.net.pi.model.PiPipelineModel;
-import org.onosproject.net.pi.runtime.PiActionProfileId;
-import org.onosproject.net.pi.runtime.PiAction;
-import org.onosproject.net.pi.runtime.PiActionGroup;
-import org.onosproject.net.pi.runtime.PiActionGroupMember;
-import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
-import org.onosproject.net.pi.runtime.PiActionId;
-import org.onosproject.net.pi.runtime.PiActionParam;
-import org.onosproject.net.pi.runtime.PiActionParamId;
-import org.onosproject.net.pi.runtime.PiPipeconfService;
-import org.onosproject.net.pi.runtime.PiTableAction;
-import org.onosproject.net.pi.runtime.PiTableId;
-import org.onosproject.p4runtime.api.P4RuntimeClient;
-import org.onosproject.p4runtime.api.P4RuntimeController;
-
-import java.net.URL;
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-
-import static org.easymock.EasyMock.*;
-import static org.junit.Assert.assertEquals;
-import static org.onosproject.net.group.GroupDescription.Type.SELECT;
-import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
-import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.DELETE;
-import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.INSERT;
-
-public class P4runtimeGroupProgrammableTest {
- private static final String P4INFO_PATH = "/default.p4info";
- private static final DeviceId DEVICE_ID = DeviceId.deviceId("device:p4runtime:1");
- private static final PiPipeconfId PIPECONF_ID = new PiPipeconfId("p4runtime-mock-pipeconf");
- private static final PiPipeconf PIPECONF = buildPipeconf();
- private static final PiTableId ECMP_TABLE_ID = PiTableId.of("ecmp");
- private static final PiActionProfileId ACT_PROF_ID = PiActionProfileId.of("ecmp_selector");
- private static final ApplicationId APP_ID = TestApplicationId.create("P4runtimeGroupProgrammableTest");
- private static final GroupId GROUP_ID = GroupId.valueOf(1);
- private static final PiActionId EGRESS_PORT_ACTION_ID = PiActionId.of("set_egress_port");
- private static final PiActionParamId PORT_PARAM_ID = PiActionParamId.of("port");
- private static final List<GroupBucket> BUCKET_LIST = ImmutableList.of(
- outputBucket(1),
- outputBucket(2),
- outputBucket(3)
- );
- private static final DefaultP4RuntimeGroupCookie COOKIE =
- new DefaultP4RuntimeGroupCookie(ECMP_TABLE_ID, ACT_PROF_ID, GROUP_ID.id());
- private static final GroupKey GROUP_KEY =
- new DefaultGroupKey(P4RuntimeGroupProgrammable.KRYO.serialize(COOKIE));
- private static final GroupBuckets BUCKETS = new GroupBuckets(BUCKET_LIST);
- private static final GroupDescription GROUP_DESC =
- new DefaultGroupDescription(DEVICE_ID,
- SELECT,
- BUCKETS,
- GROUP_KEY,
- GROUP_ID.id(),
- APP_ID);
- private static final Group GROUP = new DefaultGroup(GROUP_ID, GROUP_DESC);
- private static final int DEFAULT_MEMBER_WEIGHT = 1;
- private static final int BASE_MEM_ID = 65535;
- private static final Collection<PiActionGroupMember> EXPECTED_MEMBERS =
- ImmutableSet.of(
- outputMember(1),
- outputMember(2),
- outputMember(3)
- );
-
- private P4RuntimeGroupProgrammable programmable;
- private DriverHandler driverHandler;
- private DriverData driverData;
- private P4RuntimeController controller;
- private P4RuntimeClient client;
- private PiPipeconfService piPipeconfService;
- private DeviceService deviceService;
- private Device device;
- private GroupStore groupStore;
-
- private static PiPipeconf buildPipeconf() {
- final URL p4InfoUrl = P4runtimeGroupProgrammableTest.class.getResource(P4INFO_PATH);
- return DefaultPiPipeconf.builder()
- .withId(PIPECONF_ID)
- .withPipelineModel(niceMock(PiPipelineModel.class))
- .addExtension(P4_INFO_TEXT, p4InfoUrl)
- .build();
- }
-
- private static GroupBucket outputBucket(int portNum) {
- ImmutableByteSequence paramVal = ImmutableByteSequence.copyFrom(portNum);
- PiActionParam param = new PiActionParam(PiActionParamId.of(PORT_PARAM_ID.name()), paramVal);
- PiTableAction action = PiAction.builder().withId(EGRESS_PORT_ACTION_ID).withParameter(param).build();
-
- TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .add(Instructions.piTableAction(action))
- .build();
-
- return DefaultGroupBucket.createSelectGroupBucket(treatment);
- }
-
- private static PiActionGroupMember outputMember(int portNum) {
- PiActionParam param = new PiActionParam(PORT_PARAM_ID,
- ImmutableByteSequence.copyFrom(portNum));
- PiAction piAction = PiAction.builder()
- .withId(EGRESS_PORT_ACTION_ID)
- .withParameter(param).build();
-
- return PiActionGroupMember.builder()
- .withAction(piAction)
- .withId(PiActionGroupMemberId.of(BASE_MEM_ID + portNum))
- .withWeight(DEFAULT_MEMBER_WEIGHT)
- .build();
- }
-
- @Before
- public void setup() {
- driverHandler = EasyMock.niceMock(DriverHandler.class);
- driverData = EasyMock.niceMock(DriverData.class);
- controller = EasyMock.niceMock(P4RuntimeController.class);
- client = EasyMock.niceMock(P4RuntimeClient.class);
- piPipeconfService = EasyMock.niceMock(PiPipeconfService.class);
- deviceService = EasyMock.niceMock(DeviceService.class);
- device = EasyMock.niceMock(Device.class);
- groupStore = EasyMock.niceMock(GroupStore.class);
-
- expect(controller.hasClient(DEVICE_ID)).andReturn(true).anyTimes();
- expect(controller.getClient(DEVICE_ID)).andReturn(client).anyTimes();
- expect(device.is(PiPipelineInterpreter.class)).andReturn(true).anyTimes();
- expect(device.id()).andReturn(DEVICE_ID).anyTimes();
- expect(deviceService.getDevice(DEVICE_ID)).andReturn(device).anyTimes();
- expect(driverData.deviceId()).andReturn(DEVICE_ID).anyTimes();
- expect(groupStore.getGroup(DEVICE_ID, GROUP_ID)).andReturn(GROUP).anyTimes();
- expect(piPipeconfService.ofDevice(DEVICE_ID)).andReturn(Optional.of(PIPECONF_ID)).anyTimes();
- expect(piPipeconfService.getPipeconf(PIPECONF_ID)).andReturn(Optional.of(PIPECONF)).anyTimes();
- expect(driverHandler.data()).andReturn(driverData).anyTimes();
- expect(driverHandler.get(P4RuntimeController.class)).andReturn(controller).anyTimes();
- expect(driverHandler.get(PiPipeconfService.class)).andReturn(piPipeconfService).anyTimes();
- expect(driverHandler.get(DeviceService.class)).andReturn(deviceService).anyTimes();
- expect(driverHandler.get(GroupStore.class)).andReturn(groupStore).anyTimes();
-
- programmable = new P4RuntimeGroupProgrammable();
- programmable.setHandler(driverHandler);
- programmable.setData(driverData);
- EasyMock.replay(driverHandler, driverData, controller, piPipeconfService,
- deviceService, device, groupStore);
- }
-
- /**
- * Test init function.
- */
- @Test
- public void testInit() {
- programmable.init();
- }
-
- /**
- * Test add group with buckets.
- */
- @Test
- public void testAddGroup() {
- List<GroupOperation> ops = Lists.newArrayList();
- ops.add(GroupOperation.createAddGroupOperation(GROUP_ID, SELECT, BUCKETS));
- GroupOperations groupOps = new GroupOperations(ops);
- CompletableFuture<Boolean> completeTrue = new CompletableFuture<>();
- completeTrue.complete(true);
-
- Capture<PiActionGroup> groupCapture1 = EasyMock.newCapture();
- expect(client.writeActionGroup(EasyMock.capture(groupCapture1), EasyMock.eq(INSERT), EasyMock.eq(PIPECONF)))
- .andReturn(completeTrue).anyTimes();
-
- Capture<PiActionGroup> groupCapture2 = EasyMock.newCapture();
- Capture<Collection<PiActionGroupMember>> membersCapture = EasyMock.newCapture();
- expect(client.writeActionGroupMembers(EasyMock.capture(groupCapture2),
- EasyMock.capture(membersCapture),
- EasyMock.eq(INSERT),
- EasyMock.eq(PIPECONF)))
- .andReturn(completeTrue).anyTimes();
-
- EasyMock.replay(client);
- programmable.performGroupOperation(DEVICE_ID, groupOps);
-
- // verify group installed by group programmable
- PiActionGroup group1 = groupCapture1.getValue();
- PiActionGroup group2 = groupCapture2.getValue();
- assertEquals("Groups should be equal", group1, group2);
- assertEquals(GROUP_ID.id(), group1.id().id());
- assertEquals(PiActionGroup.Type.SELECT, group1.type());
- assertEquals(ACT_PROF_ID, group1.actionProfileId());
-
- // members installed
- Collection<PiActionGroupMember> members = group1.members();
- assertEquals(3, members.size());
-
- Assert.assertTrue(EXPECTED_MEMBERS.containsAll(members));
- Assert.assertTrue(members.containsAll(EXPECTED_MEMBERS));
- }
-
- /**
- * Test remove group with buckets.
- */
- @Test
- public void testDelGroup() {
- List<GroupOperation> ops = Lists.newArrayList();
- ops.add(GroupOperation.createDeleteGroupOperation(GROUP_ID, SELECT));
- GroupOperations groupOps = new GroupOperations(ops);
- CompletableFuture<Boolean> completeTrue = new CompletableFuture<>();
- completeTrue.complete(true);
-
- Capture<PiActionGroup> groupCapture1 = EasyMock.newCapture();
- expect(client.writeActionGroup(EasyMock.capture(groupCapture1), EasyMock.eq(DELETE), EasyMock.eq(PIPECONF)))
- .andReturn(completeTrue).anyTimes();
-
- Capture<PiActionGroup> groupCapture2 = EasyMock.newCapture();
- Capture<Collection<PiActionGroupMember>> membersCapture = EasyMock.newCapture();
- expect(client.writeActionGroupMembers(EasyMock.capture(groupCapture2),
- EasyMock.capture(membersCapture),
- EasyMock.eq(DELETE),
- EasyMock.eq(PIPECONF)))
- .andReturn(completeTrue).anyTimes();
-
- EasyMock.replay(client);
- programmable.performGroupOperation(DEVICE_ID, groupOps);
-
- // verify group installed by group programmable
- PiActionGroup group1 = groupCapture1.getValue();
- PiActionGroup group2 = groupCapture2.getValue();
- assertEquals("Groups should be equal", group1, group2);
- assertEquals(GROUP_ID.id(), group1.id().id());
- assertEquals(PiActionGroup.Type.SELECT, group1.type());
- assertEquals(ACT_PROF_ID, group1.actionProfileId());
-
- // members installed
- Collection<PiActionGroupMember> members = group1.members();
- assertEquals(3, members.size());
-
- Assert.assertTrue(EXPECTED_MEMBERS.containsAll(members));
- Assert.assertTrue(members.containsAll(EXPECTED_MEMBERS));
- }
-}
diff --git a/drivers/p4runtime/src/test/resources/default.p4info b/drivers/p4runtime/src/test/resources/default.p4info
deleted file mode 120000
index 8f71cbe..0000000
--- a/drivers/p4runtime/src/test/resources/default.p4info
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../tools/test/p4src/p4-16/p4c-out/default.p4info
\ No newline at end of file
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 3b7f3d3..b2743ac 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
@@ -18,12 +18,11 @@
import com.google.common.annotations.Beta;
import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiActionProfileId;
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.runtime.PiCounterId;
-import org.onosproject.net.pi.runtime.PiActionGroup;
-import org.onosproject.net.pi.runtime.PiActionGroupMember;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTableId;
@@ -121,13 +120,11 @@
* Performs the given write operation for the given action group members and pipeconf.
*
* @param group action group
- * @param members the collection of action group 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> writeActionGroupMembers(PiActionGroup group,
- Collection<PiActionGroupMember> members,
WriteOperationType opType,
PiPipeconf pipeconf);
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
index 472bf8e..c3783c1 100644
--- 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
@@ -18,9 +18,9 @@
import com.google.common.collect.Maps;
import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.runtime.PiActionProfileId;
import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiActionGroupId;
+import org.onosproject.net.pi.runtime.PiActionProfileId;
import p4.P4RuntimeOuterClass.ActionProfileGroup;
import p4.P4RuntimeOuterClass.ActionProfileGroup.Member;
import p4.P4RuntimeOuterClass.ActionProfileMember;
@@ -57,12 +57,10 @@
}
PiActionProfileId piActionProfileId = piActionGroup.actionProfileId();
- int actionProfileId;
- P4InfoOuterClass.ActionProfile actionProfile =
- browser.actionProfiles().getByName(piActionProfileId.id());
- actionProfileId = actionProfile.getPreamble().getId();
- ActionProfileGroup.Builder actionProfileGroupBuilder =
- ActionProfileGroup.newBuilder()
+ P4InfoOuterClass.ActionProfile actionProfile = browser.actionProfiles()
+ .getByNameOrAlias(piActionProfileId.id());
+ int actionProfileId = actionProfile.getPreamble().getId();
+ ActionProfileGroup.Builder actionProfileGroupBuilder = ActionProfileGroup.newBuilder()
.setGroupId(piActionGroup.id().id())
.setActionProfileId(actionProfileId);
@@ -71,8 +69,7 @@
actionProfileGroupBuilder.setType(ActionProfileGroup.Type.SELECT);
break;
default:
- throw new EncodeException(format("Unsupported pi action group type %s",
- piActionGroup.type()));
+ throw new EncodeException(format("PI action group type %s not supported", piActionGroup.type()));
}
piActionGroup.members().forEach(m -> {
@@ -84,6 +81,8 @@
actionProfileGroupBuilder.addMembers(member);
});
+ actionProfileGroupBuilder.setMaxSize(piActionGroup.members().size());
+
return actionProfileGroupBuilder.build();
}
@@ -98,8 +97,8 @@
* @throws EncodeException if can't find P4Info from pipeconf
*/
static PiActionGroup decode(ActionProfileGroup actionProfileGroup,
- Collection<ActionProfileMember> members,
- PiPipeconf pipeconf)
+ Collection<ActionProfileMember> members,
+ PiPipeconf pipeconf)
throws P4InfoBrowser.NotFoundException, EncodeException {
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
@@ -110,15 +109,19 @@
P4InfoOuterClass.ActionProfile actionProfile = browser.actionProfiles()
.getById(actionProfileGroup.getActionProfileId());
PiActionProfileId piActionProfileId = PiActionProfileId.of(actionProfile.getPreamble().getName());
- piActionGroupBuilder.withActionProfileId(piActionProfileId)
+
+ piActionGroupBuilder
+ .withActionProfileId(piActionProfileId)
.withId(PiActionGroupId.of(actionProfileGroup.getGroupId()));
switch (actionProfileGroup.getType()) {
+ case UNSPECIFIED:
+ // FIXME: PI returns unspecified for select groups. Remove this case when PI bug will be fixed.
case SELECT:
piActionGroupBuilder.withType(PiActionGroup.Type.SELECT);
break;
default:
- throw new EncodeException(format("Unsupported action profile type %s",
+ throw new EncodeException(format("Action profile type %s is not supported",
actionProfileGroup.getType()));
}
@@ -134,9 +137,12 @@
});
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));
+ piActionGroupBuilder.addMember(ActionProfileMemberEncoder.decode(member, weight, pipeconf));
}
return piActionGroupBuilder.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
index 2f08a59..878ede6 100644
--- 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
@@ -73,7 +73,7 @@
// action profile id
P4InfoOuterClass.ActionProfile actionProfile =
- browser.actionProfiles().getByName(group.actionProfileId().id());
+ browser.actionProfiles().getByNameOrAlias(group.actionProfileId().id());
int actionProfileId = actionProfile.getPreamble().getId();
actionProfileMemberBuilder.setActionProfileId(actionProfileId);
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4InfoBrowser.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4InfoBrowser.java
index 609d2d2..177c381 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4InfoBrowser.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4InfoBrowser.java
@@ -303,20 +303,6 @@
}
/**
- * Returns the entity identified by the given name, if present, otherwise, throws an exception.
- *
- * @param name entity name
- * @return entity message
- * @throws NotFoundException if the entity cannot be found
- */
- T getByName(String name) throws NotFoundException {
- if (!hasName(name)) {
- throw new NotFoundException(entityName, name);
- }
- return names.get(name);
- }
-
- /**
* Returns the entity identified by the given name or alias, if present, otherwise, throws an exception.
*
* @param name entity name or alias
@@ -344,20 +330,6 @@
}
/**
- * Returns the entity identified by the given alias, if present, otherwise, throws an exception.
- *
- * @param alias entity alias
- * @return entity message
- * @throws NotFoundException if the entity cannot be found
- */
- T getByAlias(String alias) throws NotFoundException {
- if (!hasName(alias)) {
- throw new NotFoundException(entityName, alias);
- }
- return aliases.get(alias);
- }
-
- /**
* Returns true if the P4Info defines an entity with such id, false otherwise.
*
* @param id entity id
@@ -388,7 +360,7 @@
public static final class NotFoundException extends Exception {
NotFoundException(String entityName, String key) {
- super(format("No such %s in P4Info with name/alias '%s'", entityName, key));
+ super(format("No such %s in P4Info with %name or alias '%s'", entityName, key));
}
NotFoundException(String entityName, int id) {
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 7f22e79..a579e36 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,10 @@
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.Maps;
-import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.protobuf.ByteString;
@@ -32,14 +32,14 @@
import org.onlab.util.Tools;
import org.onosproject.net.DeviceId;
import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.runtime.PiActionGroupMember;
import org.onosproject.net.pi.runtime.PiActionProfileId;
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.runtime.PiCounterId;
import org.onosproject.net.pi.runtime.PiDirectCounterCellId;
import org.onosproject.net.pi.runtime.PiIndirectCounterCellId;
-import org.onosproject.net.pi.runtime.PiActionGroup;
-import org.onosproject.net.pi.runtime.PiActionGroupMember;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiPipeconfService;
import org.onosproject.net.pi.runtime.PiTableEntry;
@@ -62,7 +62,6 @@
import p4.P4RuntimeOuterClass.TableEntry;
import p4.P4RuntimeOuterClass.Update;
import p4.P4RuntimeOuterClass.WriteRequest;
-import p4.config.P4InfoOuterClass;
import p4.config.P4InfoOuterClass.P4Info;
import p4.tmp.P4Config;
@@ -73,14 +72,13 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
@@ -90,9 +88,7 @@
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType;
import static org.slf4j.LoggerFactory.getLogger;
-import static p4.P4RuntimeOuterClass.Entity.EntityCase.ACTION_PROFILE_GROUP;
-import static p4.P4RuntimeOuterClass.Entity.EntityCase.ACTION_PROFILE_MEMBER;
-import static p4.P4RuntimeOuterClass.Entity.EntityCase.TABLE_ENTRY;
+import static p4.P4RuntimeOuterClass.Entity.EntityCase.*;
import static p4.P4RuntimeOuterClass.PacketOut;
import static p4.P4RuntimeOuterClass.SetForwardingPipelineConfigRequest.Action.VERIFY_AND_COMMIT;
@@ -123,9 +119,9 @@
/**
* Default constructor.
*
- * @param deviceId the ONOS device id
+ * @param deviceId the ONOS device id
* @param p4DeviceId the P4 device id
- * @param channel gRPC channel
+ * @param channel gRPC channel
* @param controller runtime client controller
*/
P4RuntimeClientImpl(DeviceId deviceId, long p4DeviceId, ManagedChannel channel,
@@ -239,10 +235,9 @@
@Override
public CompletableFuture<Boolean> writeActionGroupMembers(PiActionGroup group,
- Collection<PiActionGroupMember> members,
WriteOperationType opType,
PiPipeconf pipeconf) {
- return supplyInContext(() -> doWriteActionGroupMembers(group, members, opType, pipeconf),
+ return supplyInContext(() -> doWriteActionGroupMembers(group, opType, pipeconf),
"writeActionGroupMembers-" + opType.name());
}
@@ -394,7 +389,7 @@
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
int tableId;
try {
- tableId = browser.tables().getByName(piTableId.id()).getPreamble().getId();
+ tableId = browser.tables().getByNameOrAlias(piTableId.id()).getPreamble().getId();
} catch (P4InfoBrowser.NotFoundException e) {
log.warn("Unable to dump table: {}", e.getMessage());
return Collections.emptyList();
@@ -509,23 +504,19 @@
return CounterEntryCodec.decodeCounterEntities(entities, counterIdMap, pipeconf);
}
- private boolean doWriteActionGroupMembers(PiActionGroup group, Collection<PiActionGroupMember> members,
- WriteOperationType opType, PiPipeconf pipeconf) {
- WriteRequest.Builder writeRequestBuilder = WriteRequest.newBuilder();
+ private boolean doWriteActionGroupMembers(PiActionGroup group, WriteOperationType opType, PiPipeconf pipeconf) {
- Collection<ActionProfileMember> actionProfileMembers = Lists.newArrayList();
+ final Collection<ActionProfileMember> actionProfileMembers = Lists.newArrayList();
try {
- for (PiActionGroupMember member : members) {
- actionProfileMembers.add(
- ActionProfileMemberEncoder.encode(group, member, pipeconf)
- );
+ for (PiActionGroupMember member : group.members()) {
+ actionProfileMembers.add(ActionProfileMemberEncoder.encode(group, member, pipeconf));
}
} catch (EncodeException | P4InfoBrowser.NotFoundException e) {
- log.warn("Can't encode group member {} due to {}", members, e.getMessage());
+ log.warn("Unable to write ({}) group members: {}", opType, e.getMessage());
return false;
}
- Collection<Update> updateMsgs = actionProfileMembers.stream()
+ final Collection<Update> updateMsgs = actionProfileMembers.stream()
.map(actionProfileMember ->
Update.newBuilder()
.setEntity(Entity.newBuilder()
@@ -536,18 +527,19 @@
.collect(Collectors.toList());
if (updateMsgs.size() == 0) {
- // Nothing to update
+ // Nothing to update.
return true;
}
- writeRequestBuilder
+ WriteRequest writeRequestMsg = WriteRequest.newBuilder()
.setDeviceId(p4DeviceId)
- .addAllUpdates(updateMsgs);
+ .addAllUpdates(updateMsgs)
+ .build();
try {
- blockingStub.write(writeRequestBuilder.build());
+ blockingStub.write(writeRequestMsg);
return true;
} catch (StatusRuntimeException e) {
- log.warn("Unable to write table entries ({}): {}", opType, e.getMessage());
+ log.warn("Unable to write ({}) group members: {}", opType, e.getMessage());
return false;
}
}
@@ -555,141 +547,149 @@
private Collection<PiActionGroup> doDumpGroups(PiActionProfileId piActionProfileId, PiPipeconf pipeconf) {
log.debug("Dumping groups from action profile {} from {} (pipeconf {})...",
piActionProfileId.id(), deviceId, pipeconf.id());
- P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+ final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
if (browser == null) {
- log.warn("Unable to get a P4Info browser for pipeconf {}, skipping dump action profile {}",
- pipeconf, piActionProfileId);
+ log.warn("Unable to get a P4Info browser for pipeconf {}, aborting dump action profile", pipeconf);
return Collections.emptySet();
}
- int actionProfileId;
+ final int actionProfileId;
try {
- P4InfoOuterClass.ActionProfile actionProfile =
- browser.actionProfiles().getByName(piActionProfileId.id());
- actionProfileId = actionProfile.getPreamble().getId();
+ actionProfileId = browser
+ .actionProfiles()
+ .getByNameOrAlias(piActionProfileId.id())
+ .getPreamble()
+ .getId();
} catch (P4InfoBrowser.NotFoundException e) {
- log.warn("Can't find action profile {} from p4info", piActionProfileId);
+ log.warn("Unable to dump groups: {}", e.getMessage());
return Collections.emptySet();
}
- ActionProfileGroup actionProfileGroup =
- ActionProfileGroup.newBuilder()
- .setActionProfileId(actionProfileId)
- .build();
-
- ReadRequest requestMsg = ReadRequest.newBuilder()
+ // Prepare read request to read all groups from the given action profile.
+ final ReadRequest groupRequestMsg = ReadRequest.newBuilder()
.setDeviceId(p4DeviceId)
.addEntities(Entity.newBuilder()
- .setActionProfileGroup(actionProfileGroup)
+ .setActionProfileGroup(
+ ActionProfileGroup.newBuilder()
+ .setActionProfileId(actionProfileId)
+ .build())
.build())
.build();
- Iterator<ReadResponse> responses;
+ // Read groups.
+ final Iterator<ReadResponse> groupResponses;
try {
- responses = blockingStub.read(requestMsg);
+ groupResponses = blockingStub.read(groupRequestMsg);
} catch (StatusRuntimeException e) {
- log.warn("Unable to read action profile {} due to {}", piActionProfileId, e.getMessage());
+ log.warn("Unable dump groups from action profile '{}': {}", piActionProfileId.id(), e.getMessage());
return Collections.emptySet();
}
- List<ActionProfileGroup> actionProfileGroups =
- Tools.stream(() -> responses)
- .map(ReadResponse::getEntitiesList)
- .flatMap(List::stream)
- .filter(entity -> entity.getEntityCase() == ACTION_PROFILE_GROUP)
- .map(Entity::getActionProfileGroup)
- .collect(Collectors.toList());
+ final List<ActionProfileGroup> groupMsgs = Tools.stream(() -> groupResponses)
+ .map(ReadResponse::getEntitiesList)
+ .flatMap(List::stream)
+ .filter(entity -> entity.getEntityCase() == ACTION_PROFILE_GROUP)
+ .map(Entity::getActionProfileGroup)
+ .collect(Collectors.toList());
log.debug("Retrieved {} groups from action profile {} on {}...",
- actionProfileGroups.size(), piActionProfileId.id(), deviceId);
+ groupMsgs.size(), piActionProfileId.id(), deviceId);
- // group id -> members
- Multimap<Integer, ActionProfileMember> actionProfileMemberMap = HashMultimap.create();
- AtomicLong memberCount = new AtomicLong(0);
- AtomicBoolean success = new AtomicBoolean(true);
- actionProfileGroups.forEach(actProfGrp -> {
- actProfGrp.getMembersList().forEach(member -> {
- ActionProfileMember actProfMember =
- ActionProfileMember.newBuilder()
- .setActionProfileId(actProfGrp.getActionProfileId())
- .setMemberId(member.getMemberId())
- .build();
- Entity entity = Entity.newBuilder()
- .setActionProfileMember(actProfMember)
- .build();
+ // 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.
- ReadRequest reqMsg = ReadRequest.newBuilder().setDeviceId(p4DeviceId)
- .addEntities(entity)
- .build();
+ // 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())));
- Iterator<ReadResponse> resps;
- try {
- resps = blockingStub.read(reqMsg);
- } catch (StatusRuntimeException e) {
- log.warn("Unable to read member {} from action profile {} due to {}",
- member, piActionProfileId, e.getMessage());
- success.set(false);
- return;
- }
- Tools.stream(() -> resps)
- .map(ReadResponse::getEntitiesList)
- .flatMap(List::stream)
- .filter(e -> e.getEntityCase() == ACTION_PROFILE_MEMBER)
- .map(Entity::getActionProfileMember)
- .forEach(m -> {
- actionProfileMemberMap.put(actProfGrp.getGroupId(), m);
- memberCount.incrementAndGet();
- });
- });
- });
+ // 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();
- if (!success.get()) {
- // Can't read members
- return Collections.emptySet();
- }
- log.info("Retrieved {} group members from action profile {} on {}...",
- memberCount.get(), piActionProfileId.id(), deviceId);
-
- Collection<PiActionGroup> piActionGroups = Sets.newHashSet();
-
- for (ActionProfileGroup apg : actionProfileGroups) {
- try {
- Collection<ActionProfileMember> members = actionProfileMemberMap.get(apg.getGroupId());
- PiActionGroup decodedGroup =
- ActionProfileGroupEncoder.decode(apg, members, pipeconf);
- piActionGroups.add(decodedGroup);
- } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
- log.warn("Can't decode group {} due to {}", apg, e.getMessage());
- return Collections.emptySet();
- }
+ // Read members.
+ final Iterator<ReadResponse> memberResponses;
+ try {
+ memberResponses = blockingStub.read(memberRequestMsg);
+ } catch (StatusRuntimeException e) {
+ log.warn("Unable to read members from action profile {}: {}", piActionProfileId, e.getMessage());
+ return Collections.emptyList();
}
- return piActionGroups;
+ 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 {} group 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());
}
private boolean doWriteActionGroup(PiActionGroup group, WriteOperationType opType, PiPipeconf pipeconf) {
- WriteRequest.Builder writeRequestBuilder = WriteRequest.newBuilder();
- ActionProfileGroup actionProfileGroup;
+
+ final ActionProfileGroup actionProfileGroup;
try {
actionProfileGroup = ActionProfileGroupEncoder.encode(group, pipeconf);
} catch (EncodeException | P4InfoBrowser.NotFoundException e) {
- log.warn("Can't encode group {} due to {}", e.getMessage());
+ log.warn("Unable to encode group: {}", e.getMessage());
return false;
}
- Update updateMessage = Update.newBuilder()
- .setEntity(Entity.newBuilder()
- .setActionProfileGroup(actionProfileGroup)
- .build())
- .setType(UPDATE_TYPES.get(opType))
- .build();
- writeRequestBuilder
+
+ final WriteRequest writeRequestMsg = WriteRequest.newBuilder()
.setDeviceId(p4DeviceId)
- .addUpdates(updateMessage);
+ .addUpdates(Update.newBuilder()
+ .setEntity(Entity.newBuilder()
+ .setActionProfileGroup(actionProfileGroup)
+ .build())
+ .setType(UPDATE_TYPES.get(opType))
+ .build())
+ .build();
try {
- blockingStub.write(writeRequestBuilder.build());
+ blockingStub.write(writeRequestMsg);
return true;
} catch (StatusRuntimeException e) {
- log.warn("Unable to write table entries ({}): {}", opType, e.getMessage());
+ log.warn("Unable to write groups ({}): {}", opType, e.getMessage());
return false;
}
}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/PacketIOCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/PacketIOCodec.java
index 20219f3..2ffe31b 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/PacketIOCodec.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/PacketIOCodec.java
@@ -33,9 +33,7 @@
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onosproject.p4runtime.ctl.P4InfoBrowser.NotFoundException;
import static org.slf4j.LoggerFactory.getLogger;
-import static p4.P4RuntimeOuterClass.PacketIn;
-import static p4.P4RuntimeOuterClass.PacketMetadata;
-import static p4.P4RuntimeOuterClass.PacketOut;
+import static p4.P4RuntimeOuterClass.*;
/**
* Encoder of packet metadata, from ONOS Pi* format, to P4Runtime protobuf messages, and vice versa.
@@ -75,7 +73,7 @@
//Get the packet out controller packet metadata
P4InfoOuterClass.ControllerPacketMetadata controllerPacketMetadata =
- browser.controllerPacketMetadatas().getByName(PACKET_OUT);
+ browser.controllerPacketMetadatas().getByNameOrAlias(PACKET_OUT);
PacketOut.Builder packetOutBuilder = PacketOut.newBuilder();
//outer controller packet metadata id
@@ -96,7 +94,7 @@
try {
//get each metadata id
int metadataId = browser.packetMetadatas(controllerPacketMetadataId)
- .getByName(metadata.id().name()).getId();
+ .getByNameOrAlias(metadata.id().name()).getId();
//Add the metadata id and it's data the packet out
return PacketMetadata.newBuilder()
@@ -129,7 +127,7 @@
List<PiPacketMetadata> packetMetadatas;
try {
- int controllerPacketMetadataId = browser.controllerPacketMetadatas().getByName(PACKET_IN)
+ int controllerPacketMetadataId = browser.controllerPacketMetadatas().getByNameOrAlias(PACKET_IN)
.getPreamble().getId();
packetMetadatas = decodePacketMetadataIn(packetIn.getMetadataList(), browser,
controllerPacketMetadataId);
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 8278b3e..3890848 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
@@ -39,10 +39,10 @@
import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
import org.onosproject.net.pi.runtime.PiValidFieldMatch;
import org.slf4j.Logger;
+import p4.P4RuntimeOuterClass.Action;
import p4.P4RuntimeOuterClass.FieldMatch;
import p4.P4RuntimeOuterClass.TableAction;
import p4.P4RuntimeOuterClass.TableEntry;
-import p4.P4RuntimeOuterClass.Action;
import p4.config.P4InfoOuterClass;
import java.util.Collection;
@@ -197,7 +197,7 @@
TableEntry.Builder tableEntryMsgBuilder = TableEntry.newBuilder();
//FIXME this throws some kind of NPE
- P4InfoOuterClass.Table tableInfo = browser.tables().getByName(tableId.id());
+ P4InfoOuterClass.Table tableInfo = browser.tables().getByNameOrAlias(tableId.id());
// Table id.
tableEntryMsgBuilder.setTableId(tableInfo.getPreamble().getId());
@@ -216,7 +216,7 @@
TableEntry.Builder tableEntryMsgBuilder = TableEntry.newBuilder();
//FIXME this throws some kind of NPE
- P4InfoOuterClass.Table tableInfo = browser.tables().getByName(piTableEntry.table().id());
+ P4InfoOuterClass.Table tableInfo = browser.tables().getByNameOrAlias(piTableEntry.table().id());
// Table id.
tableEntryMsgBuilder.setTableId(tableInfo.getPreamble().getId());
@@ -469,13 +469,13 @@
static Action encodePiAction(PiAction piAction, P4InfoBrowser browser)
throws P4InfoBrowser.NotFoundException, EncodeException {
- int actionId = browser.actions().getByName(piAction.id().name()).getPreamble().getId();
+ int actionId = browser.actions().getByNameOrAlias(piAction.id().name()).getPreamble().getId();
Action.Builder actionMsgBuilder =
Action.newBuilder().setActionId(actionId);
for (PiActionParam p : piAction.parameters()) {
- P4InfoOuterClass.Action.Param paramInfo = browser.actionParams(actionId).getByName(p.id().name());
+ P4InfoOuterClass.Action.Param paramInfo = browser.actionParams(actionId).getByNameOrAlias(p.id().name());
ByteString paramValue = ByteString.copyFrom(p.value().asReadOnlyBuffer());
assertSize(format("param '%s' of action '%s'", p.id(), piAction.id()),
paramValue, paramInfo.getBitwidth());
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 8932a16..1c9c4ab 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
@@ -36,7 +36,6 @@
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipeconfId;
import org.onosproject.net.pi.model.PiPipelineModel;
-import org.onosproject.net.pi.runtime.PiActionProfileId;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiActionGroupId;
@@ -45,6 +44,7 @@
import org.onosproject.net.pi.runtime.PiActionId;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionParamId;
+import org.onosproject.net.pi.runtime.PiActionProfileId;
import p4.P4RuntimeOuterClass.ActionProfileGroup;
import p4.P4RuntimeOuterClass.ActionProfileMember;
import p4.P4RuntimeOuterClass.Entity;
@@ -57,15 +57,15 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
-import static org.easymock.EasyMock.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.easymock.EasyMock.niceMock;
+import static org.junit.Assert.*;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
import static org.onosproject.net.pi.runtime.PiActionGroup.Type.SELECT;
import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.INSERT;
-import static p4.P4RuntimeOuterClass.*;
+import static p4.P4RuntimeOuterClass.Action;
+import static p4.P4RuntimeOuterClass.ReadResponse;
/**
* Tests for P4 Runtime Action Profile Group support.
@@ -98,7 +98,7 @@
private static final int P4_DEVICE_ID = 1;
private static final int SET_EGRESS_PORT_ID = 16794308;
private static final String GRPC_SERVER_NAME = "P4RuntimeGroupTest";
- private static final long DEFAULT_TIMEOUT_TIME = 5;
+ private static final long DEFAULT_TIMEOUT_TIME = 10;
private P4RuntimeClientImpl client;
private P4RuntimeControllerImpl controller;
@@ -187,7 +187,7 @@
@Test
public void testInsertPiActionMembers() throws Exception {
CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(1);
- client.writeActionGroupMembers(GROUP, GROUP_MEMBERS, INSERT, PIPECONF);
+ client.writeActionGroupMembers(GROUP, INSERT, PIPECONF);
complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
WriteRequest result = p4RuntimeServerImpl.getWriteReqs().get(0);
assertEquals(1, result.getDeviceId());
@@ -240,7 +240,6 @@
.addParams(param)
.build();
-
ActionProfileMember actProfMember =
ActionProfileMember.newBuilder()
.setMemberId(id)
@@ -255,14 +254,14 @@
.build()
);
- members.forEach(m -> {
- responses.add(ReadResponse.newBuilder()
- .addEntities(Entity.newBuilder().setActionProfileMember(m))
- .build());
- });
+ responses.add(ReadResponse.newBuilder()
+ .addAllEntities(members.stream()
+ .map(m -> Entity.newBuilder().setActionProfileMember(m).build())
+ .collect(Collectors.toList()))
+ .build());
p4RuntimeServerImpl.willReturnReadResult(responses);
- CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(4);
+ CompletableFuture<Void> complete = p4RuntimeServerImpl.expectRequests(2);
CompletableFuture<Collection<PiActionGroup>> groupsComplete = client.dumpGroups(ACT_PROF_ID, PIPECONF);
complete.get(DEFAULT_TIMEOUT_TIME, TimeUnit.SECONDS);
diff --git a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java
index b3e9a1e..b434b4d 100644
--- a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java
+++ b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java
@@ -44,9 +44,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
-import static org.onlab.util.ImmutableByteSequence.copyFrom;
-import static org.onlab.util.ImmutableByteSequence.fit;
-import static org.onlab.util.ImmutableByteSequence.ofOnes;
+import static org.onlab.util.ImmutableByteSequence.*;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
import static org.onosproject.p4runtime.ctl.TableEntryEncoder.decode;
import static org.onosproject.p4runtime.ctl.TableEntryEncoder.encode;
@@ -114,8 +112,8 @@
assertThat(browser.tables().hasName(TABLE_0), is(true));
assertThat(browser.actions().hasName(SET_EGRESS_PORT), is(true));
- int tableId = browser.tables().getByName(TABLE_0).getPreamble().getId();
- int actionId = browser.actions().getByName(SET_EGRESS_PORT).getPreamble().getId();
+ int tableId = browser.tables().getByNameOrAlias(TABLE_0).getPreamble().getId();
+ int actionId = browser.actions().getByNameOrAlias(SET_EGRESS_PORT).getPreamble().getId();
assertThat(browser.matchFields(tableId).hasName(STANDARD_METADATA + "." + INGRESS_PORT), is(true));
assertThat(browser.actionParams(actionId).hasName(PORT), is(true));
@@ -141,7 +139,7 @@
.testEquals();
// Table ID.
- int p4InfoTableId = browser.tables().getByName(tableId.id()).getPreamble().getId();
+ int p4InfoTableId = browser.tables().getByNameOrAlias(tableId.id()).getPreamble().getId();
int encodedTableId = tableEntryMsg.getTableId();
assertThat(encodedTableId, is(p4InfoTableId));
@@ -152,12 +150,12 @@
Action actionMsg = tableEntryMsg.getAction().getAction();
// Action ID.
- int p4InfoActionId = browser.actions().getByName(outActionId.name()).getPreamble().getId();
+ int p4InfoActionId = browser.actions().getByNameOrAlias(outActionId.name()).getPreamble().getId();
int encodedActionId = actionMsg.getActionId();
assertThat(encodedActionId, is(p4InfoActionId));
// Action param ID.
- int p4InfoActionParamId = browser.actionParams(p4InfoActionId).getByName(portParamId.name()).getId();
+ int p4InfoActionParamId = browser.actionParams(p4InfoActionId).getByNameOrAlias(portParamId.name()).getId();
int encodedActionParamId = actionMsg.getParams(0).getParamId();
assertThat(encodedActionParamId, is(p4InfoActionParamId));