blob: 8614527d5ea8a12a79ef204834ed5820f31fec36 [file] [log] [blame]
/*
* Copyright 2019-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.p4runtime.ctl.codec;
import com.google.common.testing.EqualsTester;
import org.easymock.EasyMock;
import org.junit.Test;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.net.pi.model.DefaultPiPipeconf;
import org.onosproject.net.pi.model.PiActionId;
import org.onosproject.net.pi.model.PiActionParamId;
import org.onosproject.net.pi.model.PiMatchFieldId;
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.model.PiTableId;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiExactFieldMatch;
import org.onosproject.net.pi.runtime.PiMatchKey;
import org.onosproject.net.pi.runtime.PiOptionalFieldMatch;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
import org.onosproject.p4runtime.ctl.utils.P4InfoBrowser;
import org.onosproject.p4runtime.ctl.utils.PipeconfHelper;
import p4.v1.P4RuntimeOuterClass;
import p4.v1.P4RuntimeOuterClass.Action;
import p4.v1.P4RuntimeOuterClass.CounterData;
import p4.v1.P4RuntimeOuterClass.TableEntry;
import java.net.URL;
import java.util.Random;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.hasItem;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onlab.util.ImmutableByteSequence.ofOnes;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT;
/**
* Test for P4 runtime table entry encoder.
*/
public class TableEntryEncoderTest {
private static final String DOT = ".";
private static final String TABLE_0 = "table0";
private static final String TABLE_ECMP = "ecmp";
private static final String SET_EGRESS_PORT = "set_egress_port";
private static final String PORT = "port";
private static final String HDR = "hdr";
private static final String META = "meta";
private static final String ETHERNET = "ethernet";
private static final String DST_ADDR = "dstAddr";
private static final String SRC_ADDR = "srcAddr";
private static final String STANDARD_METADATA = "standard_metadata";
private static final String LOCAL_METADATA = "local_metadata";
private static final String ECMP_METADATA = "ecmp_metadata";
private static final String INGRESS_PORT = "ingress_port";
private static final String ETHER_TYPE = "etherType";
private static final String ECMP_GROUP_ID = "ecmp_group_id";
private static final long PACKETS = 10;
private static final long BYTES = 100;
private final Random rand = new Random();
private final URL p4InfoUrl = this.getClass().getResource("/test.p4info");
private final URL p4InfoUrl2 = this.getClass().getResource("/test_p4runtime_translation_p4info.txt");
private final PiPipeconf defaultPipeconf = DefaultPiPipeconf.builder()
.withId(new PiPipeconfId("mock"))
.withPipelineModel(EasyMock.niceMock(PiPipelineModel.class))
.addExtension(P4_INFO_TEXT, p4InfoUrl)
.build();
private final PiPipeconf defaultPipeconf2 = DefaultPiPipeconf.builder()
.withId(new PiPipeconfId("mock"))
.withPipelineModel(EasyMock.niceMock(PiPipelineModel.class))
.addExtension(P4_INFO_TEXT, p4InfoUrl2)
.build();
private final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(defaultPipeconf);
private final P4InfoBrowser browser2 = PipeconfHelper.getP4InfoBrowser(defaultPipeconf2);
private final ImmutableByteSequence ethAddr = copyFrom(rand.nextInt()).fit(48);
private final ImmutableByteSequence ethAddrString = ImmutableByteSequence.copyFrom(
"00:11:22:33:44:55:66".getBytes());
private final ImmutableByteSequence portValue = copyFrom((short) rand.nextInt());
private final ImmutableByteSequence portValueString = ImmutableByteSequence.copyFrom(
String.format("Ethernet%d", rand.nextInt()).getBytes());
private final ImmutableByteSequence portValue32Bit = copyFrom((short) rand.nextInt()).fit(32);
private final PiMatchFieldId ethDstAddrFieldId = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + DST_ADDR);
private final PiMatchFieldId ethSrcAddrFieldId = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + SRC_ADDR);
private final PiMatchFieldId inPortFieldId = PiMatchFieldId.of(STANDARD_METADATA + DOT + INGRESS_PORT);
private final PiMatchFieldId inPortFieldId2 = PiMatchFieldId.of(LOCAL_METADATA + DOT + INGRESS_PORT);
private final PiMatchFieldId ethTypeFieldId = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + ETHER_TYPE);
private final PiMatchFieldId ecmpGroupFieldId =
PiMatchFieldId.of(META + DOT + ECMP_METADATA + DOT + ECMP_GROUP_ID);
private final PiActionParamId portParamId = PiActionParamId.of(PORT);
private final PiActionId outActionId = PiActionId.of(SET_EGRESS_PORT);
private final PiActionId outActionId2 = PiActionId.of(SET_EGRESS_PORT + "2");
private final PiTableId tableId = PiTableId.of(TABLE_0);
private final PiTableId ecmpTableId = PiTableId.of(TABLE_ECMP);
private final PiCounterCellData counterCellData = new PiCounterCellData(PACKETS, BYTES);
private final PiTableEntry piTableEntry = PiTableEntry
.builder()
.forTable(tableId)
.withMatchKey(PiMatchKey.builder()
.addFieldMatch(new PiTernaryFieldMatch(ethDstAddrFieldId, ethAddr, ofOnes(6)))
.addFieldMatch(new PiTernaryFieldMatch(ethSrcAddrFieldId, ethAddr, ofOnes(6)))
.addFieldMatch(new PiTernaryFieldMatch(inPortFieldId, portValue, ofOnes(2)))
.addFieldMatch(new PiTernaryFieldMatch(ethTypeFieldId, portValue, ofOnes(2)))
.build())
.withAction(PiAction
.builder()
.withId(outActionId)
.withParameter(new PiActionParam(portParamId, portValue))
.build())
.withPriority(1)
.withCookie(2)
.withCounterCellData(counterCellData)
.build();
private final PiTableEntry piTableEntry2 = PiTableEntry
.builder()
.forTable(tableId)
.withMatchKey(PiMatchKey.builder()
.addFieldMatch(new PiExactFieldMatch(inPortFieldId2, portValue32Bit))
.addFieldMatch(new PiExactFieldMatch(ethDstAddrFieldId, ethAddrString))
.addFieldMatch(new PiExactFieldMatch(ethSrcAddrFieldId, ethAddrString))
.addFieldMatch(new PiOptionalFieldMatch(ethTypeFieldId, portValue))
.build())
.withAction(PiAction
.builder()
.withId(outActionId)
.withParameter(new PiActionParam(portParamId, portValueString))
.build())
.withPriority(1)
.withCookie(2)
.build();
private final PiTableEntry piTableEntry3 = PiTableEntry
.builder()
.forTable(tableId)
.withMatchKey(PiMatchKey.builder()
.addFieldMatch(new PiExactFieldMatch(inPortFieldId2, portValue32Bit))
.addFieldMatch(new PiExactFieldMatch(ethDstAddrFieldId, ethAddrString))
.addFieldMatch(new PiExactFieldMatch(ethSrcAddrFieldId, ethAddrString))
.addFieldMatch(new PiOptionalFieldMatch(ethTypeFieldId, portValue))
.build())
.withAction(PiAction
.builder()
.withId(outActionId2)
.withParameter(new PiActionParam(portParamId, portValue32Bit))
.build())
.withPriority(1)
.withCookie(2)
.build();
private final PiTableEntry piTableEntryWithoutOptionalField = PiTableEntry
.builder()
.forTable(tableId)
.withMatchKey(PiMatchKey.builder()
.addFieldMatch(new PiExactFieldMatch(inPortFieldId2, portValue32Bit))
.addFieldMatch(new PiExactFieldMatch(ethDstAddrFieldId, ethAddrString))
.addFieldMatch(new PiExactFieldMatch(ethSrcAddrFieldId, ethAddrString))
.build())
.withAction(PiAction
.builder()
.withId(outActionId)
.withParameter(new PiActionParam(portParamId, portValueString))
.build())
.withPriority(1)
.withCookie(2)
.build();
private final PiTableEntry piTableEntryWithoutAction = PiTableEntry
.builder()
.forTable(tableId)
.withMatchKey(PiMatchKey.builder()
.addFieldMatch(new PiTernaryFieldMatch(ethDstAddrFieldId, ethAddr, ofOnes(6)))
.addFieldMatch(new PiTernaryFieldMatch(ethSrcAddrFieldId, ethAddr, ofOnes(6)))
.addFieldMatch(new PiTernaryFieldMatch(inPortFieldId, portValue, ofOnes(2)))
.addFieldMatch(new PiTernaryFieldMatch(ethTypeFieldId, portValue, ofOnes(2)))
.build())
.withPriority(1)
.withCookie(2)
.withCounterCellData(counterCellData)
.build();
private final PiTableEntry piTableEntryWithGroupAction = PiTableEntry
.builder()
.forTable(ecmpTableId)
.withMatchKey(PiMatchKey.builder()
.addFieldMatch(new PiExactFieldMatch(ecmpGroupFieldId, ofOnes(1)))
.build())
.withAction(PiActionProfileGroupId.of(1))
.withPriority(1)
.withCookie(2)
.withCounterCellData(counterCellData)
.build();
public TableEntryEncoderTest() throws ImmutableByteSequence.ByteSequenceTrimException {
}
@Test
public void testP4InfoBrowser() throws Exception {
P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(defaultPipeconf);
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();
assertThat(browser.matchFields(tableId).hasName(STANDARD_METADATA + DOT + INGRESS_PORT), is(true));
assertThat(browser.actionParams(actionId).hasName(PORT), is(true));
// TODO: improve, assert browsing other entities (counters, meters, etc.)
}
@Test
public void testTableEntryEncoder() throws Exception {
TableEntry tableEntryMsg = Codecs.CODECS.tableEntry().encode(
piTableEntry, null, defaultPipeconf);
PiTableEntry decodedPiTableEntry = Codecs.CODECS.tableEntry().decode(
tableEntryMsg, null, defaultPipeconf);
// Test equality for decoded entry.
new EqualsTester()
.addEqualityGroup(piTableEntry, 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()));
Action actionMsg = tableEntryMsg.getAction().getAction();
// Action ID.
int p4InfoActionId = browser.actions().getByName(outActionId.toString()).getPreamble().getId();
int encodedActionId = actionMsg.getActionId();
assertThat(encodedActionId, is(p4InfoActionId));
// Action param ID.
int p4InfoActionParamId = browser.actionParams(p4InfoActionId).getByName(portParamId.toString()).getId();
int encodedActionParamId = actionMsg.getParams(0).getParamId();
assertThat(encodedActionParamId, is(p4InfoActionParamId));
// Action param value.
byte[] encodedActionParam = actionMsg.getParams(0).getValue().toByteArray();
assertThat(encodedActionParam, is(portValue.asArray()));
// 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)
}
@Test
public void testTableEntryEncoderWithTranslations() throws Exception {
TableEntry tableEntryMsg = Codecs.CODECS.tableEntry().encode(
piTableEntry2, null, defaultPipeconf2);
PiTableEntry decodedPiTableEntry = Codecs.CODECS.tableEntry().decode(
tableEntryMsg, null, defaultPipeconf2);
// Test equality for decoded entry.
new EqualsTester()
.addEqualityGroup(piTableEntry2, decodedPiTableEntry)
.testEquals();
// Check the exact match with string
byte[] encodedExactMatchValueString = tableEntryMsg.getMatch(1).getExact().getValue().toByteArray();
assertThat(encodedExactMatchValueString, is(ethAddrString.asArray()));
Action actionMsg = tableEntryMsg.getAction().getAction();
// Check action param value with string
byte[] encodedActionParamString = actionMsg.getParams(0).getValue().toByteArray();
assertThat(encodedActionParamString, is(portValueString.asArray()));
TableEntry tableEntryMsg1 = Codecs.CODECS.tableEntry().encode(
piTableEntry3, null, defaultPipeconf2);
PiTableEntry decodedPiTableEntry1 = Codecs.CODECS.tableEntry().decode(
tableEntryMsg1, null, defaultPipeconf2);
// Test equality for decoded entry.
new EqualsTester()
.addEqualityGroup(piTableEntry3, decodedPiTableEntry1)
.testEquals();
}
@Test
public void testTableEntryEncoderWithoutOptionalField() throws Exception {
TableEntry tableEntryMsg = Codecs.CODECS.tableEntry().encode(
piTableEntryWithoutOptionalField, null, defaultPipeconf2);
PiTableEntry decodedPiTableEntry = Codecs.CODECS.tableEntry().decode(
tableEntryMsg, null, defaultPipeconf2);
// Table ID.
int p4InfoTableId = browser2.tables().getByName(tableId.id()).getPreamble().getId();
int encodedTableId = tableEntryMsg.getTableId();
assertThat(encodedTableId, is(p4InfoTableId));
// Test equality for decoded entry.
new EqualsTester()
.addEqualityGroup(piTableEntryWithoutOptionalField, decodedPiTableEntry)
.testEquals();
// no optional field
assertThat(tableEntryMsg.getMatchCount(), is(3));
assertThat(tableEntryMsg.getMatchList().stream()
.map(P4RuntimeOuterClass.FieldMatch::getFieldMatchTypeCase)
.collect(Collectors.toList()),
not(hasItem(P4RuntimeOuterClass.FieldMatch.FieldMatchTypeCase.OPTIONAL)));
}
@Test
public void testActopProfileGroup() throws Exception {
TableEntry tableEntryMsg = Codecs.CODECS.tableEntry().encode(
piTableEntryWithGroupAction, null, defaultPipeconf);
PiTableEntry decodedPiTableEntry = Codecs.CODECS.tableEntry().decode(
tableEntryMsg, null, defaultPipeconf);
// Test equality for decoded entry.
new EqualsTester()
.addEqualityGroup(piTableEntryWithGroupAction, decodedPiTableEntry)
.testEquals();
// Table ID.
int p4InfoTableId = browser.tables().getByName(ecmpTableId.id()).getPreamble().getId();
int encodedTableId = tableEntryMsg.getTableId();
assertThat(encodedTableId, is(p4InfoTableId));
// Exact match.
byte[] encodedTernaryMatchValue = tableEntryMsg.getMatch(0).getExact().getValue().toByteArray();
assertThat(encodedTernaryMatchValue, is(new byte[]{(byte) 0xff}));
// Action profile group id
int actionProfileGroupId = tableEntryMsg.getAction().getActionProfileGroupId();
assertThat(actionProfileGroupId, is(1));
}
@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)
}
}