blob: 3785c96245dfe8411396c2ea61cd9e0acb0630b3 [file] [log] [blame]
Ekber Aziz123ad5d2017-11-27 12:36:35 -08001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.p4runtime.model;
17
18import com.google.common.collect.ImmutableMap;
19import com.google.common.collect.ImmutableSet;
20import com.google.common.testing.EqualsTester;
21import com.google.protobuf.ExtensionRegistry;
22import com.google.protobuf.TextFormat;
23import org.hamcrest.collection.IsIterableContainingInAnyOrder;
24import org.hamcrest.collection.IsIterableContainingInOrder;
Carmelo Cascone6af4e172018-06-15 16:01:30 +020025import org.junit.Assert;
Ekber Aziz123ad5d2017-11-27 12:36:35 -080026import org.junit.Test;
27import org.onosproject.net.pi.model.PiActionId;
28import org.onosproject.net.pi.model.PiActionModel;
29import org.onosproject.net.pi.model.PiActionParamId;
30import org.onosproject.net.pi.model.PiActionParamModel;
31import org.onosproject.net.pi.model.PiActionProfileId;
32import org.onosproject.net.pi.model.PiActionProfileModel;
33import org.onosproject.net.pi.model.PiCounterId;
34import org.onosproject.net.pi.model.PiCounterModel;
35import org.onosproject.net.pi.model.PiMatchFieldId;
36import org.onosproject.net.pi.model.PiMatchFieldModel;
37import org.onosproject.net.pi.model.PiMatchType;
38import org.onosproject.net.pi.model.PiMeterModel;
39import org.onosproject.net.pi.model.PiPacketOperationModel;
40import org.onosproject.net.pi.model.PiPacketOperationType;
41import org.onosproject.net.pi.model.PiPipelineModel;
42import org.onosproject.net.pi.model.PiTableId;
43import org.onosproject.net.pi.model.PiTableModel;
Carmelo Cascone6af4e172018-06-15 16:01:30 +020044import p4.config.v1.P4InfoOuterClass.ActionRef;
45import p4.config.v1.P4InfoOuterClass.MatchField;
46import p4.config.v1.P4InfoOuterClass.P4Info;
47import p4.config.v1.P4InfoOuterClass.Table;
Ekber Aziz123ad5d2017-11-27 12:36:35 -080048
49import java.io.IOException;
50import java.io.InputStream;
51import java.io.InputStreamReader;
Ekber Aziz123ad5d2017-11-27 12:36:35 -080052import java.net.URL;
53import java.util.ArrayList;
54import java.util.Collection;
55import java.util.List;
56
57import static org.hamcrest.MatcherAssert.assertThat;
Carmelo Cascone6af4e172018-06-15 16:01:30 +020058import static org.hamcrest.Matchers.containsInAnyOrder;
59import static org.hamcrest.Matchers.equalTo;
60import static org.hamcrest.Matchers.is;
Ekber Aziz123ad5d2017-11-27 12:36:35 -080061import static org.hamcrest.core.IsNull.notNullValue;
62
63/**
64 * Test for P4Info Parser.
65 */
66public class P4InfoParserTest {
67 private static final String PATH = "basic.p4info";
Daniele Morof178b0a2020-12-15 14:13:51 +010068 private static final String PATH2 = "test_p4runtime_translation_p4info.txt";
Ekber Aziz123ad5d2017-11-27 12:36:35 -080069
70 private final URL p4InfoUrl = P4InfoParserTest.class.getResource(PATH);
Daniele Morof178b0a2020-12-15 14:13:51 +010071 private final URL p4InfoUrl2 = P4InfoParserTest.class.getResource(PATH2);
Ekber Aziz123ad5d2017-11-27 12:36:35 -080072
73 private static final Long DEFAULT_MAX_TABLE_SIZE = 1024L;
74 private static final Long DEFAULT_MAX_ACTION_PROFILE_SIZE = 64L;
Carmelo Casconea3635ab2019-03-19 12:55:34 -070075 private static final int DEFAULT_MAX_GROUP_SIZE = 16;
Ekber Aziz123ad5d2017-11-27 12:36:35 -080076
Ekber Aziz123ad5d2017-11-27 12:36:35 -080077 /**
78 * Tests parse method.
79 * @throws Exception if equality group objects dose not match as expected
80 */
81 @Test
82 public void testParse() throws Exception {
83 // Generate two PiPipelineModels from p4Info file
84 PiPipelineModel model = P4InfoParser.parse(p4InfoUrl);
85 PiPipelineModel model2 = P4InfoParser.parse(p4InfoUrl);
Ekber Aziz123ad5d2017-11-27 12:36:35 -080086 // Check equality
87 new EqualsTester().addEqualityGroup(model, model2).testEquals();
Ekber Aziz123ad5d2017-11-27 12:36:35 -080088 // Generate a P4Info object from the file
89 final P4Info p4info;
90 try {
91 p4info = getP4InfoMessage(p4InfoUrl);
92 } catch (IOException e) {
93 throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
94 }
Ekber Aziz123ad5d2017-11-27 12:36:35 -080095 List<Table> tableMsgs = p4info.getTablesList();
96 PiTableId table0Id = PiTableId.of(tableMsgs.get(0).getPreamble().getName());
97 PiTableId wcmpTableId = PiTableId.of(tableMsgs.get(1).getPreamble().getName());
Daniele Morod900fe42021-02-11 16:12:57 +010098 PiTableId wcmpTableOneShotId = PiTableId.of(tableMsgs.get(2).getPreamble().getName());
Ekber Aziz123ad5d2017-11-27 12:36:35 -080099 //parse tables
100 PiTableModel table0Model = model.table(table0Id).orElse(null);
101 PiTableModel wcmpTableModel = model.table(wcmpTableId).orElse(null);
Daniele Morod900fe42021-02-11 16:12:57 +0100102 PiTableModel wcmpTableOneShotModel = model.table(wcmpTableOneShotId).orElse(null);
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800103 PiTableModel table0Model2 = model2.table(table0Id).orElse(null);
104 PiTableModel wcmpTableModel2 = model2.table(wcmpTableId).orElse(null);
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800105 new EqualsTester().addEqualityGroup(table0Model, table0Model2)
106 .addEqualityGroup(wcmpTableModel, wcmpTableModel2).testEquals();
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800107 // Check existence
108 assertThat("model parsed value is null", table0Model, notNullValue());
109 assertThat("model parsed value is null", wcmpTableModel, notNullValue());
Daniele Morod900fe42021-02-11 16:12:57 +0100110 assertThat("model parsed value is null", wcmpTableOneShotModel, notNullValue());
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800111 assertThat("Incorrect size for table0 size", table0Model.maxSize(), is(equalTo(DEFAULT_MAX_TABLE_SIZE)));
112 assertThat("Incorrect size for wcmp_table size", wcmpTableModel.maxSize(), is(equalTo(DEFAULT_MAX_TABLE_SIZE)));
Daniele Morod900fe42021-02-11 16:12:57 +0100113 assertThat("Incorrect size for wcmp_table_one_shot size", wcmpTableOneShotModel.maxSize(),
114 is(equalTo(DEFAULT_MAX_TABLE_SIZE)));
115 // Check one-shot annotation
116 assertThat("error parsing one-shot annotation", wcmpTableModel.oneShotOnly(), is(false));
117 assertThat("error parsing one-shot annotation", wcmpTableOneShotModel.oneShotOnly(), is(true));
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800118
119 // Check matchFields
120 List<MatchField> matchFieldList = tableMsgs.get(0).getMatchFieldsList();
121 List<PiMatchFieldModel> piMatchFieldList = new ArrayList<>();
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800122 for (MatchField matchFieldIter : matchFieldList) {
Carmelo Cascone6af4e172018-06-15 16:01:30 +0200123 MatchField.MatchType matchType = matchFieldIter.getMatchType();
124 PiMatchType piMatchType;
125 switch (matchType) {
126 case EXACT: piMatchType = PiMatchType.EXACT; break;
127 case LPM: piMatchType = PiMatchType.LPM; break;
128 case TERNARY: piMatchType = PiMatchType.TERNARY; break;
129 case RANGE: piMatchType = PiMatchType.RANGE; break;
130 default: Assert.fail(); return;
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800131 }
132 piMatchFieldList.add(new P4MatchFieldModel(PiMatchFieldId.of(matchFieldIter.getName()),
133 matchFieldIter.getBitwidth(), piMatchType));
134 }
135 // Check MatchFields size
136 assertThat("Incorrect size for matchFields", table0Model.matchFields().size(), is(equalTo(9)));
137 // Check if matchFields are in order
138 assertThat("Incorrect order for matchFields", table0Model.matchFields(), IsIterableContainingInOrder.contains(
139 piMatchFieldList.get(0), piMatchFieldList.get(1),
140 piMatchFieldList.get(2), piMatchFieldList.get(3),
141 piMatchFieldList.get(4), piMatchFieldList.get(5),
142 piMatchFieldList.get(6), piMatchFieldList.get(7),
143 piMatchFieldList.get(8)));
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800144 assertThat("Incorrect size for matchFields", wcmpTableModel.matchFields().size(), is(equalTo(1)));
145
146 // check if matchFields are in order
147 matchFieldList = tableMsgs.get(1).getMatchFieldsList();
148 assertThat("Incorrect order for matchFields",
149 wcmpTableModel.matchFields(), IsIterableContainingInOrder.contains(
150 new P4MatchFieldModel(PiMatchFieldId.of(matchFieldList.get(0).getName()),
151 matchFieldList.get(0).getBitwidth(), PiMatchType.EXACT)));
152
153 //check table0 actionsRefs
154 List<ActionRef> actionRefList = tableMsgs.get(0).getActionRefsList();
155 assertThat("Incorrect size for actionRefs", actionRefList.size(), is(equalTo(4)));
156
157 //create action instances
158 PiActionId actionId = PiActionId.of("set_egress_port");
159 PiActionParamId piActionParamId = PiActionParamId.of("port");
160 int bitWitdth = 9;
161 PiActionParamModel actionParamModel = new P4ActionParamModel(piActionParamId, bitWitdth);
162 ImmutableMap<PiActionParamId, PiActionParamModel> params = new
163 ImmutableMap.Builder<PiActionParamId, PiActionParamModel>()
164 .put(piActionParamId, actionParamModel).build();
165
166 PiActionModel setEgressPortAction = new P4ActionModel(actionId, params);
167
168 actionId = PiActionId.of("send_to_cpu");
169 PiActionModel sendToCpuAction =
170 new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
171
172 actionId = PiActionId.of("_drop");
173 PiActionModel dropAction =
174 new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
175
176 actionId = PiActionId.of("NoAction");
177 PiActionModel noAction =
178 new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
179
180 actionId = PiActionId.of("table0_control.set_next_hop_id");
181 piActionParamId = PiActionParamId.of("next_hop_id");
182 bitWitdth = 16;
183 actionParamModel = new P4ActionParamModel(piActionParamId, bitWitdth);
184 params = new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>()
185 .put(piActionParamId, actionParamModel).build();
186
187 PiActionModel setNextHopIdAction = new P4ActionModel(actionId, params);
188
189 //check table0 actions
190 assertThat("action dose not match",
191 table0Model.actions(), IsIterableContainingInAnyOrder.containsInAnyOrder(
192 setEgressPortAction, sendToCpuAction, setNextHopIdAction, dropAction));
193
194 //check wcmp_table actions
195 assertThat("actions dose not match",
196 wcmpTableModel.actions(), IsIterableContainingInAnyOrder.containsInAnyOrder(
197 setEgressPortAction, noAction));
198
Carmelo Cascone50d195f2018-09-11 13:26:38 -0700199 PiActionModel table0DefaultAction = table0Model.constDefaultAction().orElse(null);
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800200
201 new EqualsTester().addEqualityGroup(table0DefaultAction, dropAction).testEquals();
202
203 // Check existence
204 assertThat("model parsed value is null", table0DefaultAction, notNullValue());
205
206 //parse action profiles
207 PiTableId tableId = PiTableId.of("wcmp_control.wcmp_table");
208 ImmutableSet<PiTableId> tableIds = new ImmutableSet.Builder<PiTableId>().add(tableId).build();
209 PiActionProfileId actionProfileId = PiActionProfileId.of("wcmp_control.wcmp_selector");
210 PiActionProfileModel wcmpSelector3 = new P4ActionProfileModel(actionProfileId, tableIds,
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800211 true, DEFAULT_MAX_ACTION_PROFILE_SIZE,
212 DEFAULT_MAX_GROUP_SIZE);
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800213 PiActionProfileModel wcmpSelector = model.actionProfiles(actionProfileId).orElse(null);
214 PiActionProfileModel wcmpSelector2 = model2.actionProfiles(actionProfileId).orElse(null);
215
216 new EqualsTester().addEqualityGroup(wcmpSelector, wcmpSelector2, wcmpSelector3).testEquals();
217
218 // Check existence
219 assertThat("model parsed value is null", wcmpSelector, notNullValue());
220 assertThat("Incorrect value for actions profiles", model.actionProfiles(), containsInAnyOrder(wcmpSelector));
221 // ActionProfiles size
222 assertThat("Incorrect size for action profiles", model.actionProfiles().size(), is(equalTo(1)));
223
224 //parse counters
225 PiCounterModel ingressPortCounterModel =
226 model.counter(PiCounterId.of("port_counters_ingress.ingress_port_counter")).orElse(null);
227 PiCounterModel egressPortCounterModel =
228 model.counter(PiCounterId.of("port_counters_egress.egress_port_counter")).orElse(null);
229 PiCounterModel table0CounterModel =
230 model.counter(PiCounterId.of("table0_control.table0_counter")).orElse(null);
231 PiCounterModel wcmpTableCounterModel =
232 model.counter(PiCounterId.of("wcmp_control.wcmp_table_counter")).orElse(null);
233
234 PiCounterModel ingressPortCounterModel2 =
235 model2.counter(PiCounterId.of("port_counters_ingress.ingress_port_counter")).orElse(null);
236 PiCounterModel egressPortCounterModel2 =
237 model2.counter(PiCounterId.of("port_counters_egress.egress_port_counter")).orElse(null);
238 PiCounterModel table0CounterModel2 =
239 model2.counter(PiCounterId.of("table0_control.table0_counter")).orElse(null);
240 PiCounterModel wcmpTableCounterModel2 =
241 model2.counter(PiCounterId.of("wcmp_control.wcmp_table_counter")).orElse(null);
242
243 new EqualsTester()
244 .addEqualityGroup(ingressPortCounterModel, ingressPortCounterModel2)
245 .addEqualityGroup(egressPortCounterModel, egressPortCounterModel2)
246 .addEqualityGroup(table0CounterModel, table0CounterModel2)
247 .addEqualityGroup(wcmpTableCounterModel, wcmpTableCounterModel2)
248 .testEquals();
249
250 assertThat("model parsed value is null", ingressPortCounterModel, notNullValue());
251 assertThat("model parsed value is null", egressPortCounterModel, notNullValue());
252 assertThat("model parsed value is null", table0CounterModel, notNullValue());
253 assertThat("model parsed value is null", wcmpTableCounterModel, notNullValue());
254
255 //Parse meters
256 Collection<PiMeterModel> meterModel = model.meters();
257 Collection<PiMeterModel> meterModel2 = model2.meters();
258
Daniele Morod900fe42021-02-11 16:12:57 +0100259 assertThat("model parsed meter collection should be empty", meterModel.isEmpty(), is(true));
260 assertThat("model parsed meter collection should be empty", meterModel2.isEmpty(), is(true));
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800261
262 //parse packet operations
263 PiPacketOperationModel packetInOperationalModel =
264 model.packetOperationModel(PiPacketOperationType.PACKET_IN).orElse(null);
265 PiPacketOperationModel packetOutOperationalModel =
266 model.packetOperationModel(PiPacketOperationType.PACKET_OUT).orElse(null);
267
268 PiPacketOperationModel packetInOperationalModel2 =
269 model2.packetOperationModel(PiPacketOperationType.PACKET_IN).orElse(null);
270 PiPacketOperationModel packetOutOperationalModel2 =
271 model2.packetOperationModel(PiPacketOperationType.PACKET_OUT).orElse(null);
272
273 new EqualsTester()
274 .addEqualityGroup(packetInOperationalModel, packetInOperationalModel2)
275 .addEqualityGroup(packetOutOperationalModel, packetOutOperationalModel2)
276 .testEquals();
277
278 // Check existence
279 assertThat("model parsed value is null", packetInOperationalModel, notNullValue());
280 assertThat("model parsed value is null", packetOutOperationalModel, notNullValue());
281 }
282
283 /**
Daniele Moroc6f2f7f2020-12-18 10:55:57 +0100284 * Tests parse method with P4Runtime translation fields and optional fields.
Daniele Morof178b0a2020-12-15 14:13:51 +0100285 * @throws Exception if equality group objects dose not match as expected
286 */
287 @Test
Daniele Moroc6f2f7f2020-12-18 10:55:57 +0100288 public void testParseP4RuntimeTranslationAndOptional() throws Exception {
Daniele Morof178b0a2020-12-15 14:13:51 +0100289 PiPipelineModel model = P4InfoParser.parse(p4InfoUrl2);
290 // Generate a P4Info object from the file
291 final P4Info p4info;
292 try {
293 p4info = getP4InfoMessage(p4InfoUrl2);
294 } catch (IOException e) {
295 throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
296 }
297 List<Table> tableMsgs = p4info.getTablesList();
298 PiTableId table0Id = PiTableId.of(tableMsgs.get(0).getPreamble().getName());
299
300 //parse tables
301 PiTableModel table0Model = model.table(table0Id).orElse(null);
302
303 // Check matchFields
304 List<MatchField> matchFieldList = tableMsgs.get(0).getMatchFieldsList();
305 List<PiMatchFieldModel> piMatchFieldList = new ArrayList<>();
306
307 for (MatchField matchFieldIter : matchFieldList) {
308 MatchField.MatchType matchType = matchFieldIter.getMatchType();
309 PiMatchType piMatchType;
310 switch (matchType) {
311 case EXACT: piMatchType = PiMatchType.EXACT; break;
312 case LPM: piMatchType = PiMatchType.LPM; break;
313 case TERNARY: piMatchType = PiMatchType.TERNARY; break;
314 case RANGE: piMatchType = PiMatchType.RANGE; break;
Daniele Moroc6f2f7f2020-12-18 10:55:57 +0100315 case OPTIONAL: piMatchType = PiMatchType.OPTIONAL; break;
Daniele Morof178b0a2020-12-15 14:13:51 +0100316 default: Assert.fail(); return;
317 }
318 if (matchFieldIter.getTypeName().getName().equals("mac_addr_t")) {
319 piMatchFieldList.add(new P4MatchFieldModel(PiMatchFieldId.of(matchFieldIter.getName()),
320 P4MatchFieldModel.BIT_WIDTH_UNDEFINED, piMatchType));
321 } else {
322 piMatchFieldList.add(new P4MatchFieldModel(PiMatchFieldId.of(matchFieldIter.getName()),
323 matchFieldIter.getBitwidth(), piMatchType));
324 }
325 }
326 assertThat("Incorrect order for matchFields",
327 table0Model.matchFields(),
328 IsIterableContainingInOrder.contains(
329 piMatchFieldList.get(0), piMatchFieldList.get(1),
330 piMatchFieldList.get(2), piMatchFieldList.get(3)));
331
332 //check table0 actionsRefs
333 List<ActionRef> actionRefList = tableMsgs.get(0).getActionRefsList();
334 assertThat("Incorrect size for actionRefs",
335 actionRefList.size(),
336 is(equalTo(4)));
337
338 //create action instances
339 // Set egress with string as parameter
340 PiActionId actionId = PiActionId.of("set_egress_port");
341 PiActionParamId piActionParamId = PiActionParamId.of("port");
342 PiActionParamModel actionParamModel = new P4ActionParamModel(
343 piActionParamId, P4ActionParamModel.BIT_WIDTH_UNDEFINED);
344 ImmutableMap<PiActionParamId, PiActionParamModel> params = new
345 ImmutableMap.Builder<PiActionParamId, PiActionParamModel>()
346 .put(piActionParamId, actionParamModel).build();
347 PiActionModel setEgressPortAction = new P4ActionModel(actionId, params);
348
349 // Set egress with 32 bit as parameter
350 actionId = PiActionId.of("set_egress_port2");
351 piActionParamId = PiActionParamId.of("port");
352 int bitWitdth = 32;
353 actionParamModel = new P4ActionParamModel(
354 piActionParamId, bitWitdth);
355 params = new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>()
356 .put(piActionParamId, actionParamModel).build();
357
358 PiActionModel setEgressPortAction2 = new P4ActionModel(actionId, params);
359
360 actionId = PiActionId.of("send_to_cpu");
361 PiActionModel sendToCpuAction =
362 new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
363
364 actionId = PiActionId.of("drop");
365 PiActionModel dropAction =
366 new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
367
368 //check table0 actions
369 assertThat("action dose not match",
370 table0Model.actions(), IsIterableContainingInAnyOrder.containsInAnyOrder(
371 setEgressPortAction, setEgressPortAction2, sendToCpuAction, dropAction));
372 }
373
374 /**
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800375 * Gets P4Info message from the URL.
376 * @param p4InfoUrl link to the p4Info file
377 * @return a P4Info object
378 * @throws IOException if any problem occurs while reading from the URL connection.
379 */
380 private P4Info getP4InfoMessage(URL p4InfoUrl) throws IOException {
381 InputStream p4InfoStream = p4InfoUrl.openStream();
382 P4Info.Builder p4InfoBuilder = P4Info.newBuilder();
383 TextFormat.getParser().merge(new InputStreamReader(p4InfoStream),
384 ExtensionRegistry.getEmptyRegistry(),
385 p4InfoBuilder);
386 return p4InfoBuilder.build();
387 }
388}