use of org.onosproject.net.pi.model.PiTableModel in project onos by opennetworkinglab.
the class PiFlowRuleTranslatorImpl method translate.
/**
* 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 = getInterpreterOrNull(device, pipeconf);
// Get table model.
final PiTableId piTableId = translateTableId(rule.table(), interpreter);
final PiTableModel tableModel = getTableModel(piTableId, pipelineModel);
// Translate selector.
final PiMatchKey piMatchKey;
final boolean needPriority;
if (rule.selector().criteria().isEmpty()) {
piMatchKey = PiMatchKey.EMPTY;
needPriority = false;
} else {
final Collection<PiFieldMatch> fieldMatches = translateFieldMatches(interpreter, rule.selector(), tableModel);
piMatchKey = PiMatchKey.builder().addFieldMatches(fieldMatches).build();
// FIXME: P4Runtime limit
// Need to ignore priority if no TCAM lookup match field
needPriority = tableModel.matchFields().stream().anyMatch(match -> match.matchType() == PiMatchType.TERNARY || match.matchType() == PiMatchType.RANGE || match.matchType() == PiMatchType.OPTIONAL);
}
// Translate treatment.
final PiTableAction piTableAction = translateTreatment(rule.treatment(), interpreter, piTableId, pipelineModel);
// Build PI entry.
final PiTableEntry.Builder tableEntryBuilder = PiTableEntry.builder();
tableEntryBuilder.forTable(piTableId).withMatchKey(piMatchKey);
if (piTableAction != null) {
tableEntryBuilder.withAction(piTableAction);
}
if (needPriority) {
// FIXME: move priority check to P4Runtime driver.
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);
newPriority = MAX_PI_PRIORITY;
} else {
newPriority = MIN_PI_PRIORITY + rule.priority();
}
tableEntryBuilder.withPriority(newPriority);
}
if (!rule.isPermanent()) {
if (tableModel.supportsAging()) {
tableEntryBuilder.withTimeout(rule.timeout());
} else {
log.debug("Flow rule is temporary, but table '{}' doesn't support " + "aging, translating to permanent.", tableModel.id());
}
}
return tableEntryBuilder.build();
}
use of org.onosproject.net.pi.model.PiTableModel in project onos by opennetworkinglab.
the class P4InfoParserTest method testParseP4RuntimeTranslationAndOptional.
/**
* Tests parse method with P4Runtime translation fields and optional fields.
* @throws Exception if equality group objects dose not match as expected
*/
@Test
public void testParseP4RuntimeTranslationAndOptional() throws Exception {
PiPipelineModel model = P4InfoParser.parse(p4InfoUrl2);
// Generate a P4Info object from the file
final P4Info p4info;
try {
p4info = getP4InfoMessage(p4InfoUrl2);
} catch (IOException e) {
throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
}
List<Table> tableMsgs = p4info.getTablesList();
PiTableId table0Id = PiTableId.of(tableMsgs.get(0).getPreamble().getName());
// parse tables
PiTableModel table0Model = model.table(table0Id).orElse(null);
// Check matchFields
List<MatchField> matchFieldList = tableMsgs.get(0).getMatchFieldsList();
List<PiMatchFieldModel> piMatchFieldList = new ArrayList<>();
for (MatchField matchFieldIter : matchFieldList) {
MatchField.MatchType matchType = matchFieldIter.getMatchType();
PiMatchType piMatchType;
switch(matchType) {
case EXACT:
piMatchType = PiMatchType.EXACT;
break;
case LPM:
piMatchType = PiMatchType.LPM;
break;
case TERNARY:
piMatchType = PiMatchType.TERNARY;
break;
case RANGE:
piMatchType = PiMatchType.RANGE;
break;
case OPTIONAL:
piMatchType = PiMatchType.OPTIONAL;
break;
default:
Assert.fail();
return;
}
if (matchFieldIter.getTypeName().getName().equals("mac_addr_t")) {
piMatchFieldList.add(new P4MatchFieldModel(PiMatchFieldId.of(matchFieldIter.getName()), P4MatchFieldModel.BIT_WIDTH_UNDEFINED, piMatchType));
} else {
piMatchFieldList.add(new P4MatchFieldModel(PiMatchFieldId.of(matchFieldIter.getName()), matchFieldIter.getBitwidth(), piMatchType));
}
}
assertThat("Incorrect order for matchFields", table0Model.matchFields(), IsIterableContainingInOrder.contains(piMatchFieldList.get(0), piMatchFieldList.get(1), piMatchFieldList.get(2), piMatchFieldList.get(3)));
// check table0 actionsRefs
List<ActionRef> actionRefList = tableMsgs.get(0).getActionRefsList();
assertThat("Incorrect size for actionRefs", actionRefList.size(), is(equalTo(4)));
// create action instances
// Set egress with string as parameter
PiActionId actionId = PiActionId.of("set_egress_port");
PiActionParamId piActionParamId = PiActionParamId.of("port");
PiActionParamModel actionParamModel = new P4ActionParamModel(piActionParamId, P4ActionParamModel.BIT_WIDTH_UNDEFINED);
ImmutableMap<PiActionParamId, PiActionParamModel> params = new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().put(piActionParamId, actionParamModel).build();
PiActionModel setEgressPortAction = new P4ActionModel(actionId, params);
// Set egress with 32 bit as parameter
actionId = PiActionId.of("set_egress_port2");
piActionParamId = PiActionParamId.of("port");
int bitWitdth = 32;
actionParamModel = new P4ActionParamModel(piActionParamId, bitWitdth);
params = new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().put(piActionParamId, actionParamModel).build();
PiActionModel setEgressPortAction2 = new P4ActionModel(actionId, params);
actionId = PiActionId.of("send_to_cpu");
PiActionModel sendToCpuAction = new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
actionId = PiActionId.of("drop");
PiActionModel dropAction = new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
// check table0 actions
assertThat("action dose not match", table0Model.actions(), IsIterableContainingInAnyOrder.containsInAnyOrder(setEgressPortAction, setEgressPortAction2, sendToCpuAction, dropAction));
}
use of org.onosproject.net.pi.model.PiTableModel in project onos by opennetworkinglab.
the class PiPipelineModelCodec method encode.
@Override
public ObjectNode encode(PiPipelineModel pipeline, CodecContext context) {
ObjectNode result = context.mapper().createObjectNode();
ArrayNode actions = result.putArray(ACTIONS);
pipeline.tables().stream().flatMap(piTableModel -> piTableModel.actions().stream()).distinct().map(action -> context.encode(action, PiActionModel.class)).forEach(actions::add);
ArrayNode tables = result.putArray(TABLES);
pipeline.tables().stream().map(table -> context.encode(table, PiTableModel.class)).forEach(tables::add);
return result;
}
use of org.onosproject.net.pi.model.PiTableModel in project onos by opennetworkinglab.
the class PiFlowRuleTranslatorImpl method translateFieldMatches.
/**
* 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> translateFieldMatches(PiPipelineInterpreter interpreter, TrafficSelector selector, PiTableModel tableModel) throws PiTranslationException {
Map<PiMatchFieldId, PiFieldMatch> fieldMatches = Maps.newHashMap();
// If present, find a PiCriterion and get its field matches as a map. Otherwise, use an empty map.
Map<PiMatchFieldId, PiFieldMatch> piCriterionFields = selector.criteria().stream().filter(c -> c.type().equals(PROTOCOL_INDEPENDENT)).map(c -> (PiCriterion) c).findFirst().map(PiCriterion::fieldMatches).map(c -> {
Map<PiMatchFieldId, PiFieldMatch> fieldMap = Maps.newHashMap();
c.forEach(fieldMatch -> fieldMap.put(fieldMatch.fieldId(), fieldMatch));
return fieldMap;
}).orElse(Maps.newHashMap());
Set<Criterion> translatedCriteria = Sets.newHashSet();
Set<Criterion> ignoredCriteria = Sets.newHashSet();
Set<PiMatchFieldId> usedPiCriterionFields = Sets.newHashSet();
Set<PiMatchFieldId> ignoredPiCriterionFields = Sets.newHashSet();
Map<PiMatchFieldId, Criterion> criterionMap = Maps.newHashMap();
if (interpreter != null) {
// NOTE: if two criterion types map to the same match field ID, and
// those two criterion types are present in the selector, this won't
// work. This is unlikely to happen since those cases should be
// mutually exclusive:
// e.g. ICMPV6_TYPE -> metadata.my_normalized_icmp_type
// ICMPV4_TYPE -> metadata.my_normalized_icmp_type
// A packet can be either ICMPv6 or ICMPv4 but not both.
selector.criteria().stream().map(Criterion::type).filter(t -> t != PROTOCOL_INDEPENDENT).forEach(t -> {
PiMatchFieldId mfid = interpreter.mapCriterionType(t).orElse(null);
if (mfid != null) {
if (criterionMap.containsKey(mfid)) {
log.warn("Detected criterion mapping " + "conflict for PiMatchFieldId {}", mfid);
}
criterionMap.put(mfid, selector.getCriterion(t));
}
});
}
for (PiMatchFieldModel fieldModel : tableModel.matchFields()) {
PiMatchFieldId fieldId = fieldModel.id();
int bitWidth = fieldModel.bitWidth();
Criterion criterion = criterionMap.get(fieldId);
if (!piCriterionFields.containsKey(fieldId) && criterion == null) {
// Can ignore if match is ternary-like, as it means "don't care".
switch(fieldModel.matchType()) {
case TERNARY:
case LPM:
case RANGE:
case OPTIONAL:
// Skip field.
break;
default:
throw new PiTranslationException(format("No value found for required match field '%s'", fieldId));
}
// Next field.
continue;
}
PiFieldMatch fieldMatch = null;
// TODO: we currently do not support fields with arbitrary bit width
if (criterion != null && fieldModel.hasBitWidth()) {
// Criterion mapping is possible for this field id.
try {
fieldMatch = translateCriterion(criterion, fieldId, fieldModel.matchType(), bitWidth);
translatedCriteria.add(criterion);
} catch (PiTranslationException ex) {
// Ignore exception if the same field was found in PiCriterion.
if (piCriterionFields.containsKey(fieldId)) {
ignoredCriteria.add(criterion);
} else {
throw ex;
}
}
}
if (piCriterionFields.containsKey(fieldId)) {
// Field was found in PiCriterion.
if (fieldMatch != null) {
// Throw exception only if we are trying to match on different values of the same field...
if (!fieldMatch.equals(piCriterionFields.get(fieldId))) {
throw new PiTranslationException(format("Duplicate match field '%s': instance translated from criterion '%s' is different to " + "what found in PiCriterion.", fieldId, criterion.type()));
}
ignoredPiCriterionFields.add(fieldId);
} else {
fieldMatch = piCriterionFields.get(fieldId);
fieldMatch = typeCheckFieldMatch(fieldMatch, fieldModel);
usedPiCriterionFields.add(fieldId);
}
}
fieldMatches.put(fieldId, fieldMatch);
}
// Check if all criteria have been translated.
StringJoiner skippedCriteriaJoiner = new StringJoiner(", ");
selector.criteria().stream().filter(c -> !c.type().equals(PROTOCOL_INDEPENDENT)).filter(c -> !translatedCriteria.contains(c) && !ignoredCriteria.contains(c)).forEach(c -> skippedCriteriaJoiner.add(c.type().name()));
if (skippedCriteriaJoiner.length() > 0) {
throw new PiTranslationException(format("The following criteria cannot be translated for table '%s': %s", tableModel.id(), skippedCriteriaJoiner.toString()));
}
// Check if all fields found in PiCriterion have been used.
StringJoiner skippedPiFieldsJoiner = new StringJoiner(", ");
piCriterionFields.keySet().stream().filter(k -> !usedPiCriterionFields.contains(k) && !ignoredPiCriterionFields.contains(k)).forEach(k -> skippedPiFieldsJoiner.add(k.id()));
if (skippedPiFieldsJoiner.length() > 0) {
throw new PiTranslationException(format("The following PiCriterion field matches are not supported in table '%s': %s", tableModel.id(), skippedPiFieldsJoiner.toString()));
}
return fieldMatches.values();
}
use of org.onosproject.net.pi.model.PiTableModel in project onos by opennetworkinglab.
the class P4InfoParser method parse.
/**
* Parse the given URL pointing to a P4Info file (in text format) to a PI pipeline model.
*
* @param p4InfoUrl URL to P4Info in text form
* @return PI pipeline model
* @throws P4InfoParserException if the P4Info file cannot be parsed (see message)
*/
public static PiPipelineModel parse(URL p4InfoUrl) throws P4InfoParserException {
final P4Info p4info;
try {
p4info = getP4InfoMessage(p4InfoUrl);
} catch (IOException e) {
throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
}
// Generate fingerprint of the pipeline by hashing p4info file
final int fingerprint;
try {
HashingInputStream hin = new HashingInputStream(Hashing.crc32(), p4InfoUrl.openStream());
// noinspection StatementWithEmptyBody
while (hin.read() != -1) {
// Do nothing. Reading all input stream to update hash.
}
fingerprint = hin.hash().asInt();
} catch (IOException e) {
throw new P4InfoParserException("Unable to generate fingerprint " + p4InfoUrl.toString(), e);
}
// Start by parsing and mapping instances to to their integer P4Info IDs.
// Convenient to build the table model at the end.
final String architecture = parseArchitecture(p4info);
// Counters.
final Map<Integer, PiCounterModel> counterMap = Maps.newHashMap();
counterMap.putAll(parseCounters(p4info));
counterMap.putAll(parseDirectCounters(p4info));
// Meters.
final Map<Integer, PiMeterModel> meterMap = Maps.newHashMap();
meterMap.putAll(parseMeters(p4info));
meterMap.putAll(parseDirectMeters(p4info));
// Registers.
final Map<Integer, PiRegisterModel> registerMap = Maps.newHashMap();
registerMap.putAll(parseRegisters(p4info));
// Action profiles.
final Map<Integer, PiActionProfileModel> actProfileMap = parseActionProfiles(p4info);
// Actions.
final Map<Integer, PiActionModel> actionMap = parseActions(p4info);
// Controller packet metadatas.
final Map<PiPacketOperationType, PiPacketOperationModel> pktOpMap = parseCtrlPktMetadatas(p4info);
// Finally, parse tables.
final ImmutableMap.Builder<PiTableId, PiTableModel> tableImmMapBuilder = ImmutableMap.builder();
for (Table tableMsg : p4info.getTablesList()) {
final PiTableId tableId = PiTableId.of(tableMsg.getPreamble().getName());
// Parse match fields.
final ImmutableMap.Builder<PiMatchFieldId, PiMatchFieldModel> tableFieldMapBuilder = ImmutableMap.builder();
for (MatchField fieldMsg : tableMsg.getMatchFieldsList()) {
final PiMatchFieldId fieldId = PiMatchFieldId.of(fieldMsg.getName());
tableFieldMapBuilder.put(fieldId, new P4MatchFieldModel(fieldId, isFieldString(p4info, fieldMsg.getTypeName().getName()) ? P4MatchFieldModel.BIT_WIDTH_UNDEFINED : fieldMsg.getBitwidth(), mapMatchFieldType(fieldMsg.getMatchType())));
}
// Retrieve action models by inter IDs.
final ImmutableMap.Builder<PiActionId, PiActionModel> tableActionMapBuilder = ImmutableMap.builder();
tableMsg.getActionRefsList().stream().map(ActionRef::getId).map(actionMap::get).forEach(actionModel -> tableActionMapBuilder.put(actionModel.id(), actionModel));
// Retrieve direct meters by integer IDs.
final ImmutableMap.Builder<PiMeterId, PiMeterModel> tableMeterMapBuilder = ImmutableMap.builder();
tableMsg.getDirectResourceIdsList().stream().map(meterMap::get).filter(Objects::nonNull).forEach(meterModel -> tableMeterMapBuilder.put(meterModel.id(), meterModel));
// Retrieve direct counters by integer IDs.
final ImmutableMap.Builder<PiCounterId, PiCounterModel> tableCounterMapBuilder = ImmutableMap.builder();
tableMsg.getDirectResourceIdsList().stream().map(counterMap::get).filter(Objects::nonNull).forEach(counterModel -> tableCounterMapBuilder.put(counterModel.id(), counterModel));
// Check if table supports one-shot only
boolean oneShotOnly = isAnnotationPresent(ONE_SHOT_ONLY_ANNOTATION, tableMsg.getPreamble());
tableImmMapBuilder.put(tableId, new P4TableModel(PiTableId.of(tableMsg.getPreamble().getName()), tableMsg.getImplementationId() == 0 ? PiTableType.DIRECT : PiTableType.INDIRECT, actProfileMap.get(tableMsg.getImplementationId()), tableMsg.getSize(), tableCounterMapBuilder.build(), tableMeterMapBuilder.build(), !tableMsg.getIdleTimeoutBehavior().equals(Table.IdleTimeoutBehavior.NO_TIMEOUT), tableFieldMapBuilder.build(), tableActionMapBuilder.build(), actionMap.get(tableMsg.getConstDefaultActionId()), tableMsg.getIsConstTable(), oneShotOnly));
}
// Get a map with proper PI IDs for some of those maps we created at the beginning.
ImmutableMap<PiCounterId, PiCounterModel> counterImmMap = ImmutableMap.copyOf(counterMap.values().stream().collect(Collectors.toMap(PiCounterModel::id, c -> c)));
ImmutableMap<PiMeterId, PiMeterModel> meterImmMap = ImmutableMap.copyOf(meterMap.values().stream().collect(Collectors.toMap(PiMeterModel::id, m -> m)));
ImmutableMap<PiRegisterId, PiRegisterModel> registerImmMap = ImmutableMap.copyOf(registerMap.values().stream().collect(Collectors.toMap(PiRegisterModel::id, r -> r)));
ImmutableMap<PiActionProfileId, PiActionProfileModel> actProfileImmMap = ImmutableMap.copyOf(actProfileMap.values().stream().collect(Collectors.toMap(PiActionProfileModel::id, a -> a)));
return new P4PipelineModel(tableImmMapBuilder.build(), counterImmMap, meterImmMap, registerImmMap, actProfileImmMap, ImmutableMap.copyOf(pktOpMap), architecture, fingerprint);
}
Aggregations