use of org.onosproject.net.pi.runtime.PiTableEntry 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.runtime.PiTableEntry in project onos by opennetworkinglab.
the class PiFlowRuleTranslatorImplTest method testTranslateFlowRules.
@Test
public void testTranslateFlowRules() throws Exception {
ApplicationId appId = new DefaultApplicationId(1, "test");
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();
TrafficSelector emptySelector = DefaultTrafficSelector.builder().build();
TrafficTreatment outPort2 = DefaultTrafficTreatment.builder().setOutput(PortNumber.portNumber(outPort)).build();
FlowRule rule1 = DefaultFlowRule.builder().forDevice(DEVICE_ID).forTable(INGRESS_TABLE0_CONTROL_TABLE0).fromApp(appId).withSelector(matchInPort1).withTreatment(outPort2).makeTemporary(timeout).withPriority(priority).build();
FlowRule rule2 = DefaultFlowRule.builder().forDevice(DEVICE_ID).forTable(INGRESS_TABLE0_CONTROL_TABLE0).fromApp(appId).withSelector(matchInPort1).withTreatment(outPort2).makeTemporary(timeout).withPriority(priority).build();
FlowRule defActionRule = DefaultFlowRule.builder().forDevice(DEVICE_ID).forTable(INGRESS_TABLE0_CONTROL_TABLE0).fromApp(appId).withSelector(emptySelector).withTreatment(outPort2).makeTemporary(timeout).withPriority(priority).build();
PiTableEntry entry1 = PiFlowRuleTranslatorImpl.translate(rule1, pipeconf, null);
PiTableEntry entry2 = PiFlowRuleTranslatorImpl.translate(rule2, pipeconf, null);
PiTableEntry defActionEntry = PiFlowRuleTranslatorImpl.translate(defActionRule, pipeconf, null);
// check equality, i.e. same rules must produce same entries
new EqualsTester().addEqualityGroup(rule1, rule2).addEqualityGroup(entry1, entry2).testEquals();
// parse values stored in entry1
PiTernaryFieldMatch inPortParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_STANDARD_METADATA_INGRESS_PORT).get();
PiTernaryFieldMatch ethDstParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_HDR_ETHERNET_DST_ADDR).get();
PiTernaryFieldMatch ethSrcParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_HDR_ETHERNET_SRC_ADDR).get();
PiTernaryFieldMatch ethTypeParam = (PiTernaryFieldMatch) entry1.matchKey().fieldMatch(HDR_HDR_ETHERNET_ETHER_TYPE).get();
Optional<Double> expectedTimeout = pipeconf.pipelineModel().table(INGRESS_TABLE0_CONTROL_TABLE0).get().supportsAging() ? Optional.of((double) rule1.timeout()) : Optional.empty();
// 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)));
// FIXME: re-enable when P4Runtime priority handling will be moved out of transltion service
// see PiFlowRuleTranslatorImpl
// assertThat("Incorrect priority value",
// entry1.priority().get(), is(equalTo(MAX_PI_PRIORITY - rule1.priority())));
assertThat("Incorrect timeout value", entry1.timeout(), is(equalTo(expectedTimeout)));
assertThat("Match key should be empty", defActionEntry.matchKey(), is(equalTo(PiMatchKey.EMPTY)));
assertThat("Priority should not be set", !defActionEntry.priority().isPresent());
}
use of org.onosproject.net.pi.runtime.PiTableEntry in project onos by opennetworkinglab.
the class P4RuntimeFlowRuleProgrammable method getFlowEntries.
@Override
public Collection<FlowEntry> getFlowEntries() {
if (!setupBehaviour("getFlowEntries()")) {
return Collections.emptyList();
}
if (driverBoolProperty(READ_FROM_MIRROR, DEFAULT_READ_FROM_MIRROR)) {
return getFlowEntriesFromMirror();
}
final ImmutableList.Builder<FlowEntry> result = ImmutableList.builder();
final List<PiTableEntry> inconsistentEntries = Lists.newArrayList();
// Read table entries from device.
final Collection<PiTableEntry> deviceEntries = getAllTableEntriesFromDevice();
if (deviceEntries == null) {
// Potential error at the client level.
return Collections.emptyList();
}
// Synchronize mirror with the device state.
tableMirror.sync(deviceId, deviceEntries);
if (deviceEntries.isEmpty()) {
// Nothing to do.
return Collections.emptyList();
}
final Map<PiTableEntryHandle, PiCounterCellData> counterCellMap = readEntryCounters(deviceEntries);
// Forge flow entries with counter values.
for (PiTableEntry entry : deviceEntries) {
final PiTableEntryHandle handle = entry.handle(deviceId);
final FlowEntry flowEntry = forgeFlowEntry(entry, handle, counterCellMap.get(handle));
if (flowEntry == null) {
// program via default_action, which cannot be removed.)
if (!isOriginalDefaultEntry(entry)) {
inconsistentEntries.add(entry);
}
} else {
result.add(flowEntry);
}
}
// Default entries need to be treated in a different way according to the spec:
// the client can modify (reset or update) them but cannot remove the entries
List<PiTableEntry> inconsistentDefaultEntries = Lists.newArrayList();
List<PiTableEntry> tempDefaultEntries = inconsistentEntries.stream().filter(PiTableEntry::isDefaultAction).collect(Collectors.toList());
inconsistentEntries.removeAll(tempDefaultEntries);
// Once we have removed the default entry from inconsistentEntries we need to
// craft for each default entry a copy without the action field. According to
// the spec leaving the action field unset will reset the original default entry.
tempDefaultEntries.forEach(piTableEntry -> {
PiTableEntry resetEntry = PiTableEntry.builder().forTable(piTableEntry.table()).build();
inconsistentDefaultEntries.add(resetEntry);
});
// Clean up of inconsistent entries.
if (!inconsistentEntries.isEmpty() || !inconsistentDefaultEntries.isEmpty()) {
WriteRequest writeRequest = client.write(p4DeviceId, pipeconf);
// Trigger remove of inconsistent entries.
if (!inconsistentEntries.isEmpty()) {
log.warn("Found {} inconsistent table entries on {}, removing them...", inconsistentEntries.size(), deviceId);
writeRequest = writeRequest.entities(inconsistentEntries, DELETE);
}
// Trigger reset of inconsistent default entries.
if (!inconsistentDefaultEntries.isEmpty()) {
log.warn("Found {} inconsistent default table entries on {}, resetting them...", inconsistentDefaultEntries.size(), deviceId);
writeRequest = writeRequest.entities(inconsistentDefaultEntries, MODIFY);
}
// Submit delete request for non-default entries and modify request
// for default entries. Updates mirror when done.
writeRequest.submit().whenComplete((response, ex) -> {
if (ex != null) {
log.error("Exception removing inconsistent table entries", ex);
} else {
log.debug("Successfully removed {} out of {} inconsistent entries", response.success().size(), response.all().size());
}
// We can use the entity as the handle does not contain the action field
// so the key will be removed even if the table entry is different
response.success().forEach(entity -> tableMirror.remove((PiTableEntryHandle) entity.handle()));
});
}
return result.build();
}
use of org.onosproject.net.pi.runtime.PiTableEntry in project onos by opennetworkinglab.
the class P4RuntimeFlowRuleProgrammable method getOriginalDefaultEntry.
private PiTableEntry getOriginalDefaultEntry(PiTableId tableId) {
final PiTableEntryHandle handle = PiTableEntry.builder().forTable(tableId).withMatchKey(PiMatchKey.EMPTY).build().handle(deviceId);
final TimedEntry<PiTableEntry> originalDefaultEntry = defaultEntryMirror.get(handle);
if (originalDefaultEntry != null) {
return originalDefaultEntry.entry();
}
return null;
}
use of org.onosproject.net.pi.runtime.PiTableEntry in project onos by opennetworkinglab.
the class TableEntryEncoderTest method testEncodeWithNoAction.
@Test
public void testEncodeWithNoAction() throws Exception {
TableEntry tableEntryMsg = Codecs.CODECS.tableEntry().encode(piTableEntryWithoutAction, null, defaultPipeconf);
PiTableEntry decodedPiTableEntry = Codecs.CODECS.tableEntry().decode(tableEntryMsg, null, defaultPipeconf);
// Test equality for decoded entry.
new EqualsTester().addEqualityGroup(piTableEntryWithoutAction, decodedPiTableEntry).testEquals();
// Table ID.
int p4InfoTableId = browser.tables().getByName(tableId.id()).getPreamble().getId();
int encodedTableId = tableEntryMsg.getTableId();
assertThat(encodedTableId, is(p4InfoTableId));
// Ternary match.
byte[] encodedTernaryMatchValue = tableEntryMsg.getMatch(0).getTernary().getValue().toByteArray();
assertThat(encodedTernaryMatchValue, is(ethAddr.asArray()));
// no action
assertThat(tableEntryMsg.hasAction(), is(false));
// Counter
CounterData counterData = tableEntryMsg.getCounterData();
PiCounterCellData encodedCounterData = new PiCounterCellData(counterData.getPacketCount(), counterData.getByteCount());
assertThat(encodedCounterData, is(counterCellData));
// TODO: improve, assert other field match types (ternary, LPM)
}
Aggregations