blob: c69cd0b97af67413af9a2f297abfa0cf96c73258 [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;
Carmelo Cascone6af4e172018-06-15 16:01:30 +020050import p4.config.v1.P4InfoOuterClass;
51import p4.config.v1.P4InfoOuterClass.Action;
52import p4.config.v1.P4InfoOuterClass.ActionProfile;
53import p4.config.v1.P4InfoOuterClass.ActionRef;
54import p4.config.v1.P4InfoOuterClass.ControllerPacketMetadata;
55import p4.config.v1.P4InfoOuterClass.Counter;
56import p4.config.v1.P4InfoOuterClass.CounterSpec;
57import p4.config.v1.P4InfoOuterClass.DirectCounter;
58import p4.config.v1.P4InfoOuterClass.DirectMeter;
59import p4.config.v1.P4InfoOuterClass.MatchField;
60import p4.config.v1.P4InfoOuterClass.Meter;
61import p4.config.v1.P4InfoOuterClass.MeterSpec;
62import p4.config.v1.P4InfoOuterClass.P4Info;
63import p4.config.v1.P4InfoOuterClass.Table;
Carmelo Cascone87892e22017-11-13 16:01:29 -080064
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>()
Carmelo Cascone87892e22017-11-13 16:01:29 -0800106 .put(MatchField.MatchType.EXACT, PiMatchType.EXACT)
107 .put(MatchField.MatchType.LPM, PiMatchType.LPM)
108 .put(MatchField.MatchType.TERNARY, PiMatchType.TERNARY)
109 .put(MatchField.MatchType.RANGE, PiMatchType.RANGE)
110 // Don't map UNSPECIFIED as we don't support it at the moment.
111 .build();
112 public static final int NO_SIZE = -1;
113
114 private P4InfoParser() {
115 // Utility class, hides constructor.
116 }
117
118 /**
119 * Parse the given URL pointing to a P4Info file (in text format) to a PI pipeline model.
120 *
121 * @param p4InfoUrl URL to P4Info in text form
122 * @return PI pipeline model
123 * @throws P4InfoParserException if the P4Info file cannot be parsed (see message)
124 */
125 public static PiPipelineModel parse(URL p4InfoUrl) throws P4InfoParserException {
126
127 final P4Info p4info;
128 try {
129 p4info = getP4InfoMessage(p4InfoUrl);
130 } catch (IOException e) {
131 throw new P4InfoParserException("Unable to parse protobuf " + p4InfoUrl.toString(), e);
132 }
133
134 // Start by parsing and mapping instances to to their integer P4Info IDs.
135 // Convenient to build the table model at the end.
136
137 // Counters.
138 final Map<Integer, PiCounterModel> counterMap = Maps.newHashMap();
139 counterMap.putAll(parseCounters(p4info));
140 counterMap.putAll(parseDirectCounters(p4info));
141
142 // Meters.
143 final Map<Integer, PiMeterModel> meterMap = Maps.newHashMap();
144 meterMap.putAll(parseMeters(p4info));
145 meterMap.putAll(parseDirectMeters(p4info));
146
FrankWang2674e452018-05-24 17:13:35 +0800147 // Registers.
148 final Map<Integer, PiRegisterModel> registerMap = Maps.newHashMap();
149 registerMap.putAll(parseRegisters(p4info));
150
Carmelo Cascone87892e22017-11-13 16:01:29 -0800151 // Action profiles.
152 final Map<Integer, PiActionProfileModel> actProfileMap = parseActionProfiles(p4info);
153
154 // Actions.
155 final Map<Integer, PiActionModel> actionMap = parseActions(p4info);
156
157 // Controller packet metadatas.
158 final Map<PiPacketOperationType, PiPacketOperationModel> pktOpMap = parseCtrlPktMetadatas(p4info);
159
160 // Finally, parse tables.
161 final ImmutableMap.Builder<PiTableId, PiTableModel> tableImmMapBuilder =
162 ImmutableMap.builder();
163 for (Table tableMsg : p4info.getTablesList()) {
164 final PiTableId tableId = PiTableId.of(tableMsg.getPreamble().getName());
165 // Parse match fields.
166 final ImmutableMap.Builder<PiMatchFieldId, PiMatchFieldModel> tableFieldMapBuilder =
167 ImmutableMap.builder();
168 for (MatchField fieldMsg : tableMsg.getMatchFieldsList()) {
169 final PiMatchFieldId fieldId = PiMatchFieldId.of(fieldMsg.getName());
170 tableFieldMapBuilder.put(
171 fieldId,
172 new P4MatchFieldModel(fieldId,
173 fieldMsg.getBitwidth(),
174 mapMatchFieldType(fieldMsg.getMatchType())));
175
176 }
177 // Retrieve action models by inter IDs.
178 final ImmutableMap.Builder<PiActionId, PiActionModel> tableActionMapBuilder =
179 ImmutableMap.builder();
180 tableMsg.getActionRefsList().stream()
181 .map(ActionRef::getId)
182 .map(actionMap::get)
183 .forEach(actionModel -> tableActionMapBuilder.put(actionModel.id(), actionModel));
184 // Retrieve direct meters by integer IDs.
185 final ImmutableMap.Builder<PiMeterId, PiMeterModel> tableMeterMapBuilder =
186 ImmutableMap.builder();
187 tableMsg.getDirectResourceIdsList()
188 .stream()
189 .map(meterMap::get)
190 // Direct resource ID might be that of a counter.
191 // Filter out missed mapping.
192 .filter(Objects::nonNull)
193 .forEach(meterModel -> tableMeterMapBuilder.put(meterModel.id(), meterModel));
194 // Retrieve direct counters by integer IDs.
195 final ImmutableMap.Builder<PiCounterId, PiCounterModel> tableCounterMapBuilder =
196 ImmutableMap.builder();
197 tableMsg.getDirectResourceIdsList()
198 .stream()
199 .map(counterMap::get)
200 // As before, resource ID might be that of a meter.
201 // Filter out missed mapping.
202 .filter(Objects::nonNull)
203 .forEach(counterModel -> tableCounterMapBuilder.put(counterModel.id(), counterModel));
204 tableImmMapBuilder.put(
205 tableId,
206 new P4TableModel(
207 PiTableId.of(tableMsg.getPreamble().getName()),
208 tableMsg.getImplementationId() == 0 ? PiTableType.DIRECT : PiTableType.INDIRECT,
209 actProfileMap.get(tableMsg.getImplementationId()),
210 tableMsg.getSize(),
211 tableCounterMapBuilder.build(),
212 tableMeterMapBuilder.build(),
Carmelo Cascone6af4e172018-06-15 16:01:30 +0200213 !tableMsg.getIdleTimeoutBehavior()
214 .equals(Table.IdleTimeoutBehavior.NO_TIMEOUT),
Carmelo Cascone87892e22017-11-13 16:01:29 -0800215 tableFieldMapBuilder.build(),
216 tableActionMapBuilder.build(),
217 actionMap.get(tableMsg.getConstDefaultActionId()),
Carmelo Cascone33b27bc2018-09-09 22:56:14 -0700218 tableMsg.getIsConstTable()));
Carmelo Cascone87892e22017-11-13 16:01:29 -0800219
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
Carmelo Cascone6af4e172018-06-15 16:01:30 +0200310 private static Map<Integer, PiRegisterModel> parseRegisters(P4Info p4info) {
FrankWang2674e452018-05-24 17:13:35 +0800311 final Map<Integer, PiRegisterModel> registerMap = Maps.newHashMap();
312 for (P4InfoOuterClass.Register registerMsg : p4info.getRegistersList()) {
313 registerMap.put(registerMsg.getPreamble().getId(),
314 new P4RegisterModel(PiRegisterId.of(registerMsg.getPreamble().getName()),
315 registerMsg.getSize()));
316 }
317 return registerMap;
318 }
319
Carmelo Cascone87892e22017-11-13 16:01:29 -0800320 private static Map<Integer, PiActionProfileModel> parseActionProfiles(P4Info p4info)
321 throws P4InfoParserException {
322 final Map<Integer, PiActionProfileModel> actProfileMap = Maps.newHashMap();
323 for (ActionProfile actProfileMsg : p4info.getActionProfilesList()) {
324 final ImmutableSet.Builder<PiTableId> tableIdSetBuilder = ImmutableSet.builder();
325 for (int tableId : actProfileMsg.getTableIdsList()) {
326 tableIdSetBuilder.add(PiTableId.of(getTableName(tableId, p4info)));
327 }
328 actProfileMap.put(
329 actProfileMsg.getPreamble().getId(),
330 new P4ActionProfileModel(
331 PiActionProfileId.of(actProfileMsg.getPreamble().getName()),
332 tableIdSetBuilder.build(),
333 actProfileMsg.getWithSelector(),
Carmelo Cascone99c59db2019-01-17 15:39:35 -0800334 actProfileMsg.getSize(),
335 actProfileMsg.getMaxGroupSize()));
Carmelo Cascone87892e22017-11-13 16:01:29 -0800336 }
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}