blob: e23a49cfe090885433989d832f1d33376d2201c7 [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";
68
69 private final URL p4InfoUrl = P4InfoParserTest.class.getResource(PATH);
70
71 private static final Long DEFAULT_MAX_TABLE_SIZE = 1024L;
72 private static final Long DEFAULT_MAX_ACTION_PROFILE_SIZE = 64L;
73
Ekber Aziz123ad5d2017-11-27 12:36:35 -080074 /**
75 * Tests parse method.
76 * @throws Exception if equality group objects dose not match as expected
77 */
78 @Test
79 public void testParse() throws Exception {
80 // Generate two PiPipelineModels from p4Info file
81 PiPipelineModel model = P4InfoParser.parse(p4InfoUrl);
82 PiPipelineModel model2 = P4InfoParser.parse(p4InfoUrl);
83
84 // Check equality
85 new EqualsTester().addEqualityGroup(model, model2).testEquals();
86
87 // Generate a P4Info object from the file
88 final P4Info p4info;
89 try {
90 p4info = getP4InfoMessage(p4InfoUrl);
91 } catch (IOException e) {
92 throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
93 }
94
95 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());
98
99 //parse tables
100 PiTableModel table0Model = model.table(table0Id).orElse(null);
101 PiTableModel wcmpTableModel = model.table(wcmpTableId).orElse(null);
102 PiTableModel table0Model2 = model2.table(table0Id).orElse(null);
103 PiTableModel wcmpTableModel2 = model2.table(wcmpTableId).orElse(null);
104
105 new EqualsTester().addEqualityGroup(table0Model, table0Model2)
106 .addEqualityGroup(wcmpTableModel, wcmpTableModel2).testEquals();
107
108 // Check existence
109 assertThat("model parsed value is null", table0Model, notNullValue());
110 assertThat("model parsed value is null", wcmpTableModel, notNullValue());
111 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)));
113
114 // Check matchFields
115 List<MatchField> matchFieldList = tableMsgs.get(0).getMatchFieldsList();
116 List<PiMatchFieldModel> piMatchFieldList = new ArrayList<>();
117
118 for (MatchField matchFieldIter : matchFieldList) {
Carmelo Cascone6af4e172018-06-15 16:01:30 +0200119 MatchField.MatchType matchType = matchFieldIter.getMatchType();
120 PiMatchType piMatchType;
121 switch (matchType) {
122 case EXACT: piMatchType = PiMatchType.EXACT; break;
123 case LPM: piMatchType = PiMatchType.LPM; break;
124 case TERNARY: piMatchType = PiMatchType.TERNARY; break;
125 case RANGE: piMatchType = PiMatchType.RANGE; break;
126 default: Assert.fail(); return;
Ekber Aziz123ad5d2017-11-27 12:36:35 -0800127 }
128 piMatchFieldList.add(new P4MatchFieldModel(PiMatchFieldId.of(matchFieldIter.getName()),
129 matchFieldIter.getBitwidth(), piMatchType));
130 }
131 // Check MatchFields size
132 assertThat("Incorrect size for matchFields", table0Model.matchFields().size(), is(equalTo(9)));
133 // Check if matchFields are in order
134 assertThat("Incorrect order for matchFields", table0Model.matchFields(), IsIterableContainingInOrder.contains(
135 piMatchFieldList.get(0), piMatchFieldList.get(1),
136 piMatchFieldList.get(2), piMatchFieldList.get(3),
137 piMatchFieldList.get(4), piMatchFieldList.get(5),
138 piMatchFieldList.get(6), piMatchFieldList.get(7),
139 piMatchFieldList.get(8)));
140
141 assertThat("Incorrect size for matchFields", wcmpTableModel.matchFields().size(), is(equalTo(1)));
142
143 // check if matchFields are in order
144 matchFieldList = tableMsgs.get(1).getMatchFieldsList();
145 assertThat("Incorrect order for matchFields",
146 wcmpTableModel.matchFields(), IsIterableContainingInOrder.contains(
147 new P4MatchFieldModel(PiMatchFieldId.of(matchFieldList.get(0).getName()),
148 matchFieldList.get(0).getBitwidth(), PiMatchType.EXACT)));
149
150 //check table0 actionsRefs
151 List<ActionRef> actionRefList = tableMsgs.get(0).getActionRefsList();
152 assertThat("Incorrect size for actionRefs", actionRefList.size(), is(equalTo(4)));
153
154 //create action instances
155 PiActionId actionId = PiActionId.of("set_egress_port");
156 PiActionParamId piActionParamId = PiActionParamId.of("port");
157 int bitWitdth = 9;
158 PiActionParamModel actionParamModel = new P4ActionParamModel(piActionParamId, bitWitdth);
159 ImmutableMap<PiActionParamId, PiActionParamModel> params = new
160 ImmutableMap.Builder<PiActionParamId, PiActionParamModel>()
161 .put(piActionParamId, actionParamModel).build();
162
163 PiActionModel setEgressPortAction = new P4ActionModel(actionId, params);
164
165 actionId = PiActionId.of("send_to_cpu");
166 PiActionModel sendToCpuAction =
167 new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
168
169 actionId = PiActionId.of("_drop");
170 PiActionModel dropAction =
171 new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
172
173 actionId = PiActionId.of("NoAction");
174 PiActionModel noAction =
175 new P4ActionModel(actionId, new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>().build());
176
177 actionId = PiActionId.of("table0_control.set_next_hop_id");
178 piActionParamId = PiActionParamId.of("next_hop_id");
179 bitWitdth = 16;
180 actionParamModel = new P4ActionParamModel(piActionParamId, bitWitdth);
181 params = new ImmutableMap.Builder<PiActionParamId, PiActionParamModel>()
182 .put(piActionParamId, actionParamModel).build();
183
184 PiActionModel setNextHopIdAction = new P4ActionModel(actionId, params);
185
186 //check table0 actions
187 assertThat("action dose not match",
188 table0Model.actions(), IsIterableContainingInAnyOrder.containsInAnyOrder(
189 setEgressPortAction, sendToCpuAction, setNextHopIdAction, dropAction));
190
191 //check wcmp_table actions
192 assertThat("actions dose not match",
193 wcmpTableModel.actions(), IsIterableContainingInAnyOrder.containsInAnyOrder(
194 setEgressPortAction, noAction));
195
196 PiActionModel table0DefaultAction = table0Model.defaultAction().orElse(null);
197
198 new EqualsTester().addEqualityGroup(table0DefaultAction, dropAction).testEquals();
199
200 // Check existence
201 assertThat("model parsed value is null", table0DefaultAction, notNullValue());
202
203 //parse action profiles
204 PiTableId tableId = PiTableId.of("wcmp_control.wcmp_table");
205 ImmutableSet<PiTableId> tableIds = new ImmutableSet.Builder<PiTableId>().add(tableId).build();
206 PiActionProfileId actionProfileId = PiActionProfileId.of("wcmp_control.wcmp_selector");
207 PiActionProfileModel wcmpSelector3 = new P4ActionProfileModel(actionProfileId, tableIds,
208 true, DEFAULT_MAX_ACTION_PROFILE_SIZE);
209 PiActionProfileModel wcmpSelector = model.actionProfiles(actionProfileId).orElse(null);
210 PiActionProfileModel wcmpSelector2 = model2.actionProfiles(actionProfileId).orElse(null);
211
212 new EqualsTester().addEqualityGroup(wcmpSelector, wcmpSelector2, wcmpSelector3).testEquals();
213
214 // Check existence
215 assertThat("model parsed value is null", wcmpSelector, notNullValue());
216 assertThat("Incorrect value for actions profiles", model.actionProfiles(), containsInAnyOrder(wcmpSelector));
217 // ActionProfiles size
218 assertThat("Incorrect size for action profiles", model.actionProfiles().size(), is(equalTo(1)));
219
220 //parse counters
221 PiCounterModel ingressPortCounterModel =
222 model.counter(PiCounterId.of("port_counters_ingress.ingress_port_counter")).orElse(null);
223 PiCounterModel egressPortCounterModel =
224 model.counter(PiCounterId.of("port_counters_egress.egress_port_counter")).orElse(null);
225 PiCounterModel table0CounterModel =
226 model.counter(PiCounterId.of("table0_control.table0_counter")).orElse(null);
227 PiCounterModel wcmpTableCounterModel =
228 model.counter(PiCounterId.of("wcmp_control.wcmp_table_counter")).orElse(null);
229
230 PiCounterModel ingressPortCounterModel2 =
231 model2.counter(PiCounterId.of("port_counters_ingress.ingress_port_counter")).orElse(null);
232 PiCounterModel egressPortCounterModel2 =
233 model2.counter(PiCounterId.of("port_counters_egress.egress_port_counter")).orElse(null);
234 PiCounterModel table0CounterModel2 =
235 model2.counter(PiCounterId.of("table0_control.table0_counter")).orElse(null);
236 PiCounterModel wcmpTableCounterModel2 =
237 model2.counter(PiCounterId.of("wcmp_control.wcmp_table_counter")).orElse(null);
238
239 new EqualsTester()
240 .addEqualityGroup(ingressPortCounterModel, ingressPortCounterModel2)
241 .addEqualityGroup(egressPortCounterModel, egressPortCounterModel2)
242 .addEqualityGroup(table0CounterModel, table0CounterModel2)
243 .addEqualityGroup(wcmpTableCounterModel, wcmpTableCounterModel2)
244 .testEquals();
245
246 assertThat("model parsed value is null", ingressPortCounterModel, notNullValue());
247 assertThat("model parsed value is null", egressPortCounterModel, notNullValue());
248 assertThat("model parsed value is null", table0CounterModel, notNullValue());
249 assertThat("model parsed value is null", wcmpTableCounterModel, notNullValue());
250
251 //Parse meters
252 Collection<PiMeterModel> meterModel = model.meters();
253 Collection<PiMeterModel> meterModel2 = model2.meters();
254
255 assertThat("model pased meter collaction should be empty", meterModel.isEmpty(), is(true));
256 assertThat("model pased meter collaction should be empty", meterModel2.isEmpty(), is(true));
257
258 //parse packet operations
259 PiPacketOperationModel packetInOperationalModel =
260 model.packetOperationModel(PiPacketOperationType.PACKET_IN).orElse(null);
261 PiPacketOperationModel packetOutOperationalModel =
262 model.packetOperationModel(PiPacketOperationType.PACKET_OUT).orElse(null);
263
264 PiPacketOperationModel packetInOperationalModel2 =
265 model2.packetOperationModel(PiPacketOperationType.PACKET_IN).orElse(null);
266 PiPacketOperationModel packetOutOperationalModel2 =
267 model2.packetOperationModel(PiPacketOperationType.PACKET_OUT).orElse(null);
268
269 new EqualsTester()
270 .addEqualityGroup(packetInOperationalModel, packetInOperationalModel2)
271 .addEqualityGroup(packetOutOperationalModel, packetOutOperationalModel2)
272 .testEquals();
273
274 // Check existence
275 assertThat("model parsed value is null", packetInOperationalModel, notNullValue());
276 assertThat("model parsed value is null", packetOutOperationalModel, notNullValue());
277 }
278
279 /**
280 * Gets P4Info message from the URL.
281 * @param p4InfoUrl link to the p4Info file
282 * @return a P4Info object
283 * @throws IOException if any problem occurs while reading from the URL connection.
284 */
285 private P4Info getP4InfoMessage(URL p4InfoUrl) throws IOException {
286 InputStream p4InfoStream = p4InfoUrl.openStream();
287 P4Info.Builder p4InfoBuilder = P4Info.newBuilder();
288 TextFormat.getParser().merge(new InputStreamReader(p4InfoStream),
289 ExtensionRegistry.getEmptyRegistry(),
290 p4InfoBuilder);
291 return p4InfoBuilder.build();
292 }
293}