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