blob: e60f25fac7ef0681fdf36b47ea133c5b31f3369d [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()),
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
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(),
334 actProfileMsg.getSize()));
335 }
336 return actProfileMap;
337 }
338
339 private static Map<Integer, PiActionModel> parseActions(P4Info p4info) {
340 final Map<Integer, PiActionModel> actionMap = Maps.newHashMap();
341 for (Action actionMsg : p4info.getActionsList()) {
342 final ImmutableMap.Builder<PiActionParamId, PiActionParamModel> paramMapBuilder =
343 ImmutableMap.builder();
344 actionMsg.getParamsList().forEach(paramMsg -> {
345 final PiActionParamId paramId = PiActionParamId.of(paramMsg.getName());
346 paramMapBuilder.put(paramId,
347 new P4ActionParamModel(PiActionParamId.of(paramMsg.getName()),
348 paramMsg.getBitwidth()));
349 });
350 actionMap.put(
351 actionMsg.getPreamble().getId(),
352 new P4ActionModel(
353 PiActionId.of(actionMsg.getPreamble().getName()),
354 paramMapBuilder.build()));
355
356 }
357 return actionMap;
358 }
359
360 private static Map<PiPacketOperationType, PiPacketOperationModel> parseCtrlPktMetadatas(P4Info p4info)
361 throws P4InfoParserException {
362 final Map<PiPacketOperationType, PiPacketOperationModel> packetOpMap = Maps.newHashMap();
363 for (ControllerPacketMetadata ctrlPktMetaMsg : p4info.getControllerPacketMetadataList()) {
364 final ImmutableList.Builder<PiControlMetadataModel> metadataListBuilder =
365 ImmutableList.builder();
366 ctrlPktMetaMsg.getMetadataList().forEach(metadataMsg -> metadataListBuilder.add(
367 new P4ControlMetadataModel(PiControlMetadataId.of(metadataMsg.getName()),
368 metadataMsg.getBitwidth())));
369 packetOpMap.put(
370 mapPacketOpType(ctrlPktMetaMsg.getPreamble().getName()),
371 new P4PacketOperationModel(mapPacketOpType(ctrlPktMetaMsg.getPreamble().getName()),
372 metadataListBuilder.build()));
373
374 }
375 return packetOpMap;
376 }
377
378 private static P4Info getP4InfoMessage(URL p4InfoUrl) throws IOException {
379 InputStream p4InfoStream = p4InfoUrl.openStream();
380 P4Info.Builder p4InfoBuilder = P4Info.newBuilder();
381 TextFormat.getParser().merge(new InputStreamReader(p4InfoStream),
382 ExtensionRegistry.getEmptyRegistry(),
383 p4InfoBuilder);
384 return p4InfoBuilder.build();
385 }
386
387 private static String getTableName(int id, P4Info p4info)
388 throws P4InfoParserException {
389 return p4info.getTablesList().stream()
390 .filter(t -> t.getPreamble().getId() == id)
391 .findFirst()
392 .orElseThrow(() -> new P4InfoParserException(format(
393 "Not such table with ID %d", id)))
394 .getPreamble()
395 .getName();
396 }
397
398 private static PiCounterModel.Unit mapCounterSpecUnit(CounterSpec spec)
399 throws P4InfoParserException {
400 if (!COUNTER_UNIT_MAP.containsKey(spec.getUnit())) {
401 throw new P4InfoParserException(format(
402 "Unrecognized counter unit '%s'", spec.getUnit()));
403 }
404 return COUNTER_UNIT_MAP.get(spec.getUnit());
405 }
406
407 private static PiMeterModel.Unit mapMeterSpecUnit(MeterSpec spec)
408 throws P4InfoParserException {
409 if (!METER_UNIT_MAP.containsKey(spec.getUnit())) {
410 throw new P4InfoParserException(format(
411 "Unrecognized meter unit '%s'", spec.getUnit()));
412 }
413 return METER_UNIT_MAP.get(spec.getUnit());
414 }
415
416 private static PiPacketOperationType mapPacketOpType(String name)
417 throws P4InfoParserException {
418 if (!PACKET_OPERATION_TYPE_MAP.containsKey(name)) {
419 throw new P4InfoParserException(format(
420 "Unrecognized controller packet metadata name '%s'", name));
421 }
422 return PACKET_OPERATION_TYPE_MAP.get(name);
423 }
424
425 private static PiMatchType mapMatchFieldType(MatchField.MatchType type)
426 throws P4InfoParserException {
427 if (!MATCH_TYPE_MAP.containsKey(type)) {
428 throw new P4InfoParserException(format(
429 "Unrecognized match field type '%s'", type));
430 }
431 return MATCH_TYPE_MAP.get(type);
432 }
433}