blob: f8d6082c92afcef10f087cfdf13915a94e873bd4 [file] [log] [blame]
Carmelo Cascone87892e22017-11-13 16:01:29 -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 */
16
17package org.onosproject.p4runtime.model;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.ImmutableMap;
21import com.google.common.collect.ImmutableSet;
22import com.google.common.collect.Maps;
23import com.google.protobuf.ExtensionRegistry;
24import com.google.protobuf.TextFormat;
25import org.onosproject.net.pi.model.PiActionId;
26import org.onosproject.net.pi.model.PiActionModel;
27import org.onosproject.net.pi.model.PiActionParamId;
28import org.onosproject.net.pi.model.PiActionParamModel;
29import org.onosproject.net.pi.model.PiActionProfileId;
30import org.onosproject.net.pi.model.PiActionProfileModel;
31import org.onosproject.net.pi.model.PiControlMetadataId;
32import org.onosproject.net.pi.model.PiControlMetadataModel;
33import org.onosproject.net.pi.model.PiCounterId;
34import org.onosproject.net.pi.model.PiCounterModel;
35import org.onosproject.net.pi.model.PiCounterType;
36import org.onosproject.net.pi.model.PiMatchFieldId;
37import org.onosproject.net.pi.model.PiMatchFieldModel;
38import org.onosproject.net.pi.model.PiMatchType;
39import org.onosproject.net.pi.model.PiMeterId;
40import org.onosproject.net.pi.model.PiMeterModel;
41import org.onosproject.net.pi.model.PiMeterType;
42import org.onosproject.net.pi.model.PiPacketOperationModel;
43import org.onosproject.net.pi.model.PiPacketOperationType;
44import org.onosproject.net.pi.model.PiPipelineModel;
FrankWang2674e452018-05-24 17:13:35 +080045import org.onosproject.net.pi.model.PiRegisterId;
46import org.onosproject.net.pi.model.PiRegisterModel;
Carmelo Cascone87892e22017-11-13 16:01:29 -080047import org.onosproject.net.pi.model.PiTableId;
48import org.onosproject.net.pi.model.PiTableModel;
49import org.onosproject.net.pi.model.PiTableType;
FrankWang2674e452018-05-24 17:13:35 +080050import p4.config.P4InfoOuterClass;
Carmelo Cascone87892e22017-11-13 16:01:29 -080051import p4.config.P4InfoOuterClass.Action;
52import p4.config.P4InfoOuterClass.ActionProfile;
53import p4.config.P4InfoOuterClass.ActionRef;
54import p4.config.P4InfoOuterClass.ControllerPacketMetadata;
55import p4.config.P4InfoOuterClass.Counter;
56import p4.config.P4InfoOuterClass.CounterSpec;
57import p4.config.P4InfoOuterClass.DirectCounter;
58import p4.config.P4InfoOuterClass.DirectMeter;
59import p4.config.P4InfoOuterClass.MatchField;
60import p4.config.P4InfoOuterClass.Meter;
61import p4.config.P4InfoOuterClass.MeterSpec;
62import p4.config.P4InfoOuterClass.P4Info;
63import p4.config.P4InfoOuterClass.Table;
64
65import java.io.IOException;
66import java.io.InputStream;
67import java.io.InputStreamReader;
68import java.net.URL;
69import java.util.Map;
70import java.util.Objects;
71import java.util.stream.Collectors;
72
73import static java.lang.String.format;
74
75/**
76 * Parser of P4Info to PI pipeline model instances.
77 */
78public final class P4InfoParser {
79
80 private static final String PACKET_IN = "packet_in";
81 private static final String PACKET_OUT = "packet_out";
82
83 private static final Map<CounterSpec.Unit, PiCounterModel.Unit> COUNTER_UNIT_MAP =
84 new ImmutableMap.Builder<CounterSpec.Unit, PiCounterModel.Unit>()
85 .put(CounterSpec.Unit.BYTES, PiCounterModel.Unit.BYTES)
86 .put(CounterSpec.Unit.PACKETS, PiCounterModel.Unit.PACKETS)
87 .put(CounterSpec.Unit.BOTH, PiCounterModel.Unit.PACKETS_AND_BYTES)
88 // Don't map UNSPECIFIED as we don't support it at the moment.
89 .build();
90
91 private static final Map<MeterSpec.Unit, PiMeterModel.Unit> METER_UNIT_MAP =
92 new ImmutableMap.Builder<MeterSpec.Unit, PiMeterModel.Unit>()
93 .put(MeterSpec.Unit.BYTES, PiMeterModel.Unit.BYTES)
94 .put(MeterSpec.Unit.PACKETS, PiMeterModel.Unit.PACKETS)
95 // Don't map UNSPECIFIED as we don't support it at the moment.
96 .build();
97
98 private static final Map<String, PiPacketOperationType> PACKET_OPERATION_TYPE_MAP =
99 new ImmutableMap.Builder<String, PiPacketOperationType>()
100 .put(PACKET_IN, PiPacketOperationType.PACKET_IN)
101 .put(PACKET_OUT, PiPacketOperationType.PACKET_OUT)
102 .build();
103
104 private static final Map<MatchField.MatchType, PiMatchType> MATCH_TYPE_MAP =
105 new ImmutableMap.Builder<MatchField.MatchType, PiMatchType>()
106 .put(MatchField.MatchType.VALID, PiMatchType.VALID)
107 .put(MatchField.MatchType.EXACT, PiMatchType.EXACT)
108 .put(MatchField.MatchType.LPM, PiMatchType.LPM)
109 .put(MatchField.MatchType.TERNARY, PiMatchType.TERNARY)
110 .put(MatchField.MatchType.RANGE, PiMatchType.RANGE)
111 // Don't map UNSPECIFIED as we don't support it at the moment.
112 .build();
113 public static final int NO_SIZE = -1;
114
115 private P4InfoParser() {
116 // Utility class, hides constructor.
117 }
118
119 /**
120 * Parse the given URL pointing to a P4Info file (in text format) to a PI pipeline model.
121 *
122 * @param p4InfoUrl URL to P4Info in text form
123 * @return PI pipeline model
124 * @throws P4InfoParserException if the P4Info file cannot be parsed (see message)
125 */
126 public static PiPipelineModel parse(URL p4InfoUrl) throws P4InfoParserException {
127
128 final P4Info p4info;
129 try {
130 p4info = getP4InfoMessage(p4InfoUrl);
131 } catch (IOException e) {
132 throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
133 }
134
135 // Start by parsing and mapping instances to to their integer P4Info IDs.
136 // Convenient to build the table model at the end.
137
138 // Counters.
139 final Map<Integer, PiCounterModel> counterMap = Maps.newHashMap();
140 counterMap.putAll(parseCounters(p4info));
141 counterMap.putAll(parseDirectCounters(p4info));
142
143 // Meters.
144 final Map<Integer, PiMeterModel> meterMap = Maps.newHashMap();
145 meterMap.putAll(parseMeters(p4info));
146 meterMap.putAll(parseDirectMeters(p4info));
147
FrankWang2674e452018-05-24 17:13:35 +0800148 // Registers.
149 final Map<Integer, PiRegisterModel> registerMap = Maps.newHashMap();
150 registerMap.putAll(parseRegisters(p4info));
151
Carmelo Cascone87892e22017-11-13 16:01:29 -0800152 // Action profiles.
153 final Map<Integer, PiActionProfileModel> actProfileMap = parseActionProfiles(p4info);
154
155 // Actions.
156 final Map<Integer, PiActionModel> actionMap = parseActions(p4info);
157
158 // Controller packet metadatas.
159 final Map<PiPacketOperationType, PiPacketOperationModel> pktOpMap = parseCtrlPktMetadatas(p4info);
160
161 // Finally, parse tables.
162 final ImmutableMap.Builder<PiTableId, PiTableModel> tableImmMapBuilder =
163 ImmutableMap.builder();
164 for (Table tableMsg : p4info.getTablesList()) {
165 final PiTableId tableId = PiTableId.of(tableMsg.getPreamble().getName());
166 // Parse match fields.
167 final ImmutableMap.Builder<PiMatchFieldId, PiMatchFieldModel> tableFieldMapBuilder =
168 ImmutableMap.builder();
169 for (MatchField fieldMsg : tableMsg.getMatchFieldsList()) {
170 final PiMatchFieldId fieldId = PiMatchFieldId.of(fieldMsg.getName());
171 tableFieldMapBuilder.put(
172 fieldId,
173 new P4MatchFieldModel(fieldId,
174 fieldMsg.getBitwidth(),
175 mapMatchFieldType(fieldMsg.getMatchType())));
176
177 }
178 // Retrieve action models by inter IDs.
179 final ImmutableMap.Builder<PiActionId, PiActionModel> tableActionMapBuilder =
180 ImmutableMap.builder();
181 tableMsg.getActionRefsList().stream()
182 .map(ActionRef::getId)
183 .map(actionMap::get)
184 .forEach(actionModel -> tableActionMapBuilder.put(actionModel.id(), actionModel));
185 // Retrieve direct meters by integer IDs.
186 final ImmutableMap.Builder<PiMeterId, PiMeterModel> tableMeterMapBuilder =
187 ImmutableMap.builder();
188 tableMsg.getDirectResourceIdsList()
189 .stream()
190 .map(meterMap::get)
191 // Direct resource ID might be that of a counter.
192 // Filter out missed mapping.
193 .filter(Objects::nonNull)
194 .forEach(meterModel -> tableMeterMapBuilder.put(meterModel.id(), meterModel));
195 // Retrieve direct counters by integer IDs.
196 final ImmutableMap.Builder<PiCounterId, PiCounterModel> tableCounterMapBuilder =
197 ImmutableMap.builder();
198 tableMsg.getDirectResourceIdsList()
199 .stream()
200 .map(counterMap::get)
201 // As before, resource ID might be that of a meter.
202 // Filter out missed mapping.
203 .filter(Objects::nonNull)
204 .forEach(counterModel -> tableCounterMapBuilder.put(counterModel.id(), counterModel));
205 tableImmMapBuilder.put(
206 tableId,
207 new P4TableModel(
208 PiTableId.of(tableMsg.getPreamble().getName()),
209 tableMsg.getImplementationId() == 0 ? PiTableType.DIRECT : PiTableType.INDIRECT,
210 actProfileMap.get(tableMsg.getImplementationId()),
211 tableMsg.getSize(),
212 tableCounterMapBuilder.build(),
213 tableMeterMapBuilder.build(),
214 tableMsg.getWithEntryTimeout(),
215 tableFieldMapBuilder.build(),
216 tableActionMapBuilder.build(),
217 actionMap.get(tableMsg.getConstDefaultActionId()),
218 tableMsg.getConstDefaultActionHasMutableParams()));
219
220 }
221
222 // Get a map with proper PI IDs for some of those maps we created at the beginning.
223 ImmutableMap<PiCounterId, PiCounterModel> counterImmMap = ImmutableMap.copyOf(
224 counterMap.values().stream()
225 .collect(Collectors.toMap(PiCounterModel::id, c -> c)));
226 ImmutableMap<PiMeterId, PiMeterModel> meterImmMap = ImmutableMap.copyOf(
227 meterMap.values().stream()
228 .collect(Collectors.toMap(PiMeterModel::id, m -> m)));
FrankWang2674e452018-05-24 17:13:35 +0800229 ImmutableMap<PiRegisterId, PiRegisterModel> registerImmMap = ImmutableMap.copyOf(
230 registerMap.values().stream()
231 .collect(Collectors.toMap(PiRegisterModel::id, r -> r)));
Carmelo Cascone87892e22017-11-13 16:01:29 -0800232 ImmutableMap<PiActionProfileId, PiActionProfileModel> actProfileImmMap = ImmutableMap.copyOf(
233 actProfileMap.values().stream()
234 .collect(Collectors.toMap(PiActionProfileModel::id, a -> a)));
235
236 return new P4PipelineModel(
237 tableImmMapBuilder.build(),
238 counterImmMap,
239 meterImmMap,
FrankWang2674e452018-05-24 17:13:35 +0800240 registerImmMap,
Carmelo Cascone87892e22017-11-13 16:01:29 -0800241 actProfileImmMap,
242 ImmutableMap.copyOf(pktOpMap));
243 }
244
245
246 private static Map<Integer, PiCounterModel> parseCounters(P4Info p4info)
247 throws P4InfoParserException {
248 final Map<Integer, PiCounterModel> counterMap = Maps.newHashMap();
249 for (Counter counterMsg : p4info.getCountersList()) {
250 counterMap.put(
251 counterMsg.getPreamble().getId(),
252 new P4CounterModel(
253 PiCounterId.of(counterMsg.getPreamble().getName()),
254 PiCounterType.INDIRECT,
255 mapCounterSpecUnit(counterMsg.getSpec()),
256 null,
257 counterMsg.getSize()));
258 }
259 return counterMap;
260 }
261
262 private static Map<Integer, PiCounterModel> parseDirectCounters(P4Info p4info)
263 throws P4InfoParserException {
264 final Map<Integer, PiCounterModel> counterMap = Maps.newHashMap();
265 for (DirectCounter dirCounterMsg : p4info.getDirectCountersList()) {
266 counterMap.put(
267 dirCounterMsg.getPreamble().getId(),
268 new P4CounterModel(
269 PiCounterId.of(dirCounterMsg.getPreamble().getName()),
270 PiCounterType.DIRECT,
271 mapCounterSpecUnit(dirCounterMsg.getSpec()),
272 PiTableId.of(getTableName(dirCounterMsg.getDirectTableId(), p4info)),
273 NO_SIZE));
274 }
275 return counterMap;
276 }
277
278 private static Map<Integer, PiMeterModel> parseMeters(P4Info p4info)
279 throws P4InfoParserException {
280 final Map<Integer, PiMeterModel> meterMap = Maps.newHashMap();
281 for (Meter meterMsg : p4info.getMetersList()) {
282 meterMap.put(
283 meterMsg.getPreamble().getId(),
284 new P4MeterModel(
285 PiMeterId.of(meterMsg.getPreamble().getName()),
286 PiMeterType.INDIRECT,
287 mapMeterSpecUnit(meterMsg.getSpec()),
288 null,
289 meterMsg.getSize()));
290 }
291 return meterMap;
292 }
293
294 private static Map<Integer, PiMeterModel> parseDirectMeters(P4Info p4info)
295 throws P4InfoParserException {
296 final Map<Integer, PiMeterModel> meterMap = Maps.newHashMap();
297 for (DirectMeter dirMeterMsg : p4info.getDirectMetersList()) {
298 meterMap.put(
299 dirMeterMsg.getPreamble().getId(),
300 new P4MeterModel(
301 PiMeterId.of(dirMeterMsg.getPreamble().getName()),
302 PiMeterType.DIRECT,
303 mapMeterSpecUnit(dirMeterMsg.getSpec()),
304 PiTableId.of(getTableName(dirMeterMsg.getDirectTableId(), p4info)),
305 NO_SIZE));
306 }
307 return meterMap;
308 }
309
FrankWang2674e452018-05-24 17:13:35 +0800310 private static Map<Integer, PiRegisterModel> parseRegisters(P4Info p4info)
311 throws P4InfoParserException {
312 final Map<Integer, PiRegisterModel> registerMap = Maps.newHashMap();
313 for (P4InfoOuterClass.Register registerMsg : p4info.getRegistersList()) {
314 registerMap.put(registerMsg.getPreamble().getId(),
315 new P4RegisterModel(PiRegisterId.of(registerMsg.getPreamble().getName()),
316 registerMsg.getSize()));
317 }
318 return registerMap;
319 }
320
Carmelo Cascone87892e22017-11-13 16:01:29 -0800321 private static Map<Integer, PiActionProfileModel> parseActionProfiles(P4Info p4info)
322 throws P4InfoParserException {
323 final Map<Integer, PiActionProfileModel> actProfileMap = Maps.newHashMap();
324 for (ActionProfile actProfileMsg : p4info.getActionProfilesList()) {
325 final ImmutableSet.Builder<PiTableId> tableIdSetBuilder = ImmutableSet.builder();
326 for (int tableId : actProfileMsg.getTableIdsList()) {
327 tableIdSetBuilder.add(PiTableId.of(getTableName(tableId, p4info)));
328 }
329 actProfileMap.put(
330 actProfileMsg.getPreamble().getId(),
331 new P4ActionProfileModel(
332 PiActionProfileId.of(actProfileMsg.getPreamble().getName()),
333 tableIdSetBuilder.build(),
334 actProfileMsg.getWithSelector(),
335 actProfileMsg.getSize()));
336 }
337 return actProfileMap;
338 }
339
340 private static Map<Integer, PiActionModel> parseActions(P4Info p4info) {
341 final Map<Integer, PiActionModel> actionMap = Maps.newHashMap();
342 for (Action actionMsg : p4info.getActionsList()) {
343 final ImmutableMap.Builder<PiActionParamId, PiActionParamModel> paramMapBuilder =
344 ImmutableMap.builder();
345 actionMsg.getParamsList().forEach(paramMsg -> {
346 final PiActionParamId paramId = PiActionParamId.of(paramMsg.getName());
347 paramMapBuilder.put(paramId,
348 new P4ActionParamModel(PiActionParamId.of(paramMsg.getName()),
349 paramMsg.getBitwidth()));
350 });
351 actionMap.put(
352 actionMsg.getPreamble().getId(),
353 new P4ActionModel(
354 PiActionId.of(actionMsg.getPreamble().getName()),
355 paramMapBuilder.build()));
356
357 }
358 return actionMap;
359 }
360
361 private static Map<PiPacketOperationType, PiPacketOperationModel> parseCtrlPktMetadatas(P4Info p4info)
362 throws P4InfoParserException {
363 final Map<PiPacketOperationType, PiPacketOperationModel> packetOpMap = Maps.newHashMap();
364 for (ControllerPacketMetadata ctrlPktMetaMsg : p4info.getControllerPacketMetadataList()) {
365 final ImmutableList.Builder<PiControlMetadataModel> metadataListBuilder =
366 ImmutableList.builder();
367 ctrlPktMetaMsg.getMetadataList().forEach(metadataMsg -> metadataListBuilder.add(
368 new P4ControlMetadataModel(PiControlMetadataId.of(metadataMsg.getName()),
369 metadataMsg.getBitwidth())));
370 packetOpMap.put(
371 mapPacketOpType(ctrlPktMetaMsg.getPreamble().getName()),
372 new P4PacketOperationModel(mapPacketOpType(ctrlPktMetaMsg.getPreamble().getName()),
373 metadataListBuilder.build()));
374
375 }
376 return packetOpMap;
377 }
378
379 private static P4Info getP4InfoMessage(URL p4InfoUrl) throws IOException {
380 InputStream p4InfoStream = p4InfoUrl.openStream();
381 P4Info.Builder p4InfoBuilder = P4Info.newBuilder();
382 TextFormat.getParser().merge(new InputStreamReader(p4InfoStream),
383 ExtensionRegistry.getEmptyRegistry(),
384 p4InfoBuilder);
385 return p4InfoBuilder.build();
386 }
387
388 private static String getTableName(int id, P4Info p4info)
389 throws P4InfoParserException {
390 return p4info.getTablesList().stream()
391 .filter(t -> t.getPreamble().getId() == id)
392 .findFirst()
393 .orElseThrow(() -> new P4InfoParserException(format(
394 "Not such table with ID %d", id)))
395 .getPreamble()
396 .getName();
397 }
398
399 private static PiCounterModel.Unit mapCounterSpecUnit(CounterSpec spec)
400 throws P4InfoParserException {
401 if (!COUNTER_UNIT_MAP.containsKey(spec.getUnit())) {
402 throw new P4InfoParserException(format(
403 "Unrecognized counter unit '%s'", spec.getUnit()));
404 }
405 return COUNTER_UNIT_MAP.get(spec.getUnit());
406 }
407
408 private static PiMeterModel.Unit mapMeterSpecUnit(MeterSpec spec)
409 throws P4InfoParserException {
410 if (!METER_UNIT_MAP.containsKey(spec.getUnit())) {
411 throw new P4InfoParserException(format(
412 "Unrecognized meter unit '%s'", spec.getUnit()));
413 }
414 return METER_UNIT_MAP.get(spec.getUnit());
415 }
416
417 private static PiPacketOperationType mapPacketOpType(String name)
418 throws P4InfoParserException {
419 if (!PACKET_OPERATION_TYPE_MAP.containsKey(name)) {
420 throw new P4InfoParserException(format(
421 "Unrecognized controller packet metadata name '%s'", name));
422 }
423 return PACKET_OPERATION_TYPE_MAP.get(name);
424 }
425
426 private static PiMatchType mapMatchFieldType(MatchField.MatchType type)
427 throws P4InfoParserException {
428 if (!MATCH_TYPE_MAP.containsKey(type)) {
429 throw new P4InfoParserException(format(
430 "Unrecognized match field type '%s'", type));
431 }
432 return MATCH_TYPE_MAP.get(type);
433 }
434}