blob: a8c4c6f1c2f04dda931b14d7a8a6e1331899d122 [file] [log] [blame]
Carmelo Casconeb5324e72018-11-25 02:26:32 -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
Carmelo Cascone36d5e7a2019-09-25 01:02:53 -070017package org.onosproject.pipelines.fabric.impl.behaviour.pipeliner;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080018
19import com.google.common.collect.Lists;
Carmelo Casconed06a8512018-12-02 16:34:20 -080020import org.onlab.packet.VlanId;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080021import org.onosproject.net.DeviceId;
22import org.onosproject.net.PortNumber;
23import org.onosproject.net.flow.DefaultTrafficSelector;
24import org.onosproject.net.flow.DefaultTrafficTreatment;
25import org.onosproject.net.flow.TrafficSelector;
26import org.onosproject.net.flow.TrafficTreatment;
27import org.onosproject.net.flow.criteria.Criterion;
28import org.onosproject.net.flow.criteria.PiCriterion;
29import org.onosproject.net.flow.criteria.VlanIdCriterion;
30import org.onosproject.net.flow.instructions.Instruction;
Carmelo Casconed06a8512018-12-02 16:34:20 -080031import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080032import org.onosproject.net.flowobjective.DefaultNextTreatment;
33import org.onosproject.net.flowobjective.NextObjective;
34import org.onosproject.net.flowobjective.NextTreatment;
35import org.onosproject.net.flowobjective.Objective;
36import org.onosproject.net.flowobjective.ObjectiveError;
37import org.onosproject.net.group.DefaultGroupBucket;
38import org.onosproject.net.group.DefaultGroupDescription;
39import org.onosproject.net.group.DefaultGroupKey;
40import org.onosproject.net.group.GroupBucket;
41import org.onosproject.net.group.GroupBuckets;
42import org.onosproject.net.group.GroupDescription;
43import org.onosproject.net.group.GroupKey;
44import org.onosproject.net.pi.model.PiTableId;
45import org.onosproject.net.pi.runtime.PiAction;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080046import org.onosproject.net.pi.runtime.PiActionParam;
Carmelo Casconecb4327a2018-09-11 15:17:23 -070047import org.onosproject.net.pi.runtime.PiActionProfileGroupId;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080048import org.onosproject.net.pi.runtime.PiGroupKey;
Carmelo Cascone36d5e7a2019-09-25 01:02:53 -070049import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities;
Carmelo Cascone8f6b5cd2020-06-30 18:56:12 -070050import org.onosproject.pipelines.fabric.FabricConstants;
Carmelo Cascone36d5e7a2019-09-25 01:02:53 -070051import org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080052
53import java.util.Collection;
54import java.util.List;
55import java.util.Objects;
56import java.util.Set;
57import java.util.stream.Collectors;
58
59import static java.lang.String.format;
Carmelo Casconed06a8512018-12-02 16:34:20 -080060import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080061import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
Carmelo Cascone36d5e7a2019-09-25 01:02:53 -070062import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.criterion;
63import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instruction;
64import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.l2Instructions;
65import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.outputPort;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080066
67/**
68 * ObjectiveTranslator implementation for NextObjective.
69 */
70class NextObjectiveTranslator
71 extends AbstractObjectiveTranslator<NextObjective> {
72
Carmelo Cascone45cc0862018-11-26 11:50:41 -080073 private static final String XCONNECT = "xconnect";
74
Carmelo Casconeb5324e72018-11-25 02:26:32 -080075 NextObjectiveTranslator(DeviceId deviceId, FabricCapabilities capabilities) {
76 super(deviceId, capabilities);
77 }
78
79 @Override
80 public ObjectiveTranslation doTranslate(NextObjective obj)
81 throws FabricPipelinerException {
82
83 final ObjectiveTranslation.Builder resultBuilder =
84 ObjectiveTranslation.builder();
85
86 switch (obj.type()) {
87 case SIMPLE:
88 simpleNext(obj, resultBuilder, false);
89 break;
90 case HASHED:
91 hashedNext(obj, resultBuilder);
92 break;
93 case BROADCAST:
Carmelo Cascone45cc0862018-11-26 11:50:41 -080094 if (isXconnect(obj)) {
95 xconnectNext(obj, resultBuilder);
96 } else {
97 multicastNext(obj, resultBuilder);
98 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -080099 break;
100 default:
101 log.warn("Unsupported NextObjective type '{}'", obj);
102 return ObjectiveTranslation.ofError(ObjectiveError.UNSUPPORTED);
103 }
104
105 if (!isGroupModifyOp(obj)) {
106 // Generate next VLAN rules.
107 nextVlan(obj, resultBuilder);
108 }
109
110 return resultBuilder.build();
111 }
112
113 private void nextVlan(NextObjective obj,
114 ObjectiveTranslation.Builder resultBuilder)
115 throws FabricPipelinerException {
Daniele Moro77ef35c2019-07-30 10:43:10 -0700116 // We expect NextObjective treatments to contain one or two VLAN instructions.
117 // If two, this treatment should be mapped to an action for double-vlan push.
118 // In fabric.p4, mapping next IDs to VLAN IDs is done by a direct table (next_vlan),
119 // for this reason, we also make sure that all treatments in the NextObjective
120 // have exactly the same VLAN instructions, as they will be mapped to a single action
Carmelo Casconed06a8512018-12-02 16:34:20 -0800121
Daniele Moro77ef35c2019-07-30 10:43:10 -0700122 // Try to extract VLAN instructions in the treatment,
123 // later we check if we support multiple VLAN termination.
124 final List<List<ModVlanIdInstruction>> vlanInstructions = defaultNextTreatments(
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700125 obj.nextTreatments(), false).stream()
Daniele Moro77ef35c2019-07-30 10:43:10 -0700126 .map(defaultNextTreatment ->
127 l2Instructions(defaultNextTreatment.treatment(), VLAN_ID)
128 .stream().map(v -> (ModVlanIdInstruction) v)
129 .collect(Collectors.toList()))
130 .filter(l -> !l.isEmpty())
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700131 .collect(Collectors.toList());
Daniele Moro77ef35c2019-07-30 10:43:10 -0700132
Carmelo Casconed06a8512018-12-02 16:34:20 -0800133 final VlanIdCriterion vlanIdCriterion = obj.meta() == null ? null
134 : (VlanIdCriterion) criterion(obj.meta().criteria(), Criterion.Type.VLAN_VID);
Carmelo Casconed06a8512018-12-02 16:34:20 -0800135
Daniele Moro77ef35c2019-07-30 10:43:10 -0700136 final List<VlanId> vlanIdList;
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700137 if (vlanInstructions.isEmpty() && vlanIdCriterion == null) {
138 // No VLAN_ID to apply.
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800139 return;
Daniele Moro77ef35c2019-07-30 10:43:10 -0700140 }
141 if (!vlanInstructions.isEmpty()) {
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700142 // Give priority to what found in the instructions.
Daniele Moro77ef35c2019-07-30 10:43:10 -0700143 // Expect the same VLAN ID (or two VLAN IDs in the same order) for all instructions.
144 final Set<List<VlanId>> vlanIds = vlanInstructions.stream()
145 .map(l -> l.stream().map(ModVlanIdInstruction::vlanId).collect(Collectors.toList()))
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700146 .collect(Collectors.toSet());
147 if (obj.nextTreatments().size() != vlanInstructions.size() ||
148 vlanIds.size() != 1) {
Carmelo Casconed06a8512018-12-02 16:34:20 -0800149 throw new FabricPipelinerException(
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700150 "Inconsistent VLAN_ID instructions, cannot process " +
151 "next_vlan rule. It is required that all " +
Daniele Moro77ef35c2019-07-30 10:43:10 -0700152 "treatments have the same VLAN_ID instructions.");
Carmelo Casconed06a8512018-12-02 16:34:20 -0800153 }
Daniele Moro77ef35c2019-07-30 10:43:10 -0700154 vlanIdList = vlanIds.iterator().next();
Carmelo Cascone1c8a4ed2019-04-08 15:37:03 -0700155 } else {
156 // Use the value in meta.
157 // FIXME: there should be no need to generate a next_vlan rule for
158 // the value found in meta. Meta describes the fields that were
159 // expected to be matched in previous pipeline stages, i.e.
160 // existing packet fields. But, for some reason, if we remove this
161 // rule, traffic is not forwarded at spines. We might need to look
162 // at the way default VLANs are handled in fabric.p4.
Daniele Moro77ef35c2019-07-30 10:43:10 -0700163 vlanIdList = List.of(vlanIdCriterion.vlanId());
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800164 }
165
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800166 final TrafficSelector selector = nextIdSelector(obj.id());
Daniele Moro77ef35c2019-07-30 10:43:10 -0700167 final TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
168 vlanIdList.stream().forEach(vlanId -> treatmentBuilder.setVlanId(vlanId));
169 final TrafficTreatment treatment = treatmentBuilder.build();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800170
171 resultBuilder.addFlowRule(flowRule(
172 obj, FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN,
173 selector, treatment));
174 }
175
176 private void simpleNext(NextObjective obj,
177 ObjectiveTranslation.Builder resultBuilder,
178 boolean forceSimple)
179 throws FabricPipelinerException {
180
181 if (capabilities.hasHashedTable()) {
182 // Use hashed table when possible.
183 hashedNext(obj, resultBuilder);
184 return;
185 }
186
187 if (obj.nextTreatments().isEmpty()) {
188 // Do nothing.
189 return;
190 } else if (!forceSimple && obj.nextTreatments().size() != 1) {
191 throw new FabricPipelinerException(format(
192 "SIMPLE NextObjective should contain only 1 treatment, found %d",
193 obj.nextTreatments().size()), ObjectiveError.BADPARAMS);
194 }
195
196 final TrafficSelector selector = nextIdSelector(obj.id());
197
Carmelo Casconed06a8512018-12-02 16:34:20 -0800198 final List<DefaultNextTreatment> treatments = defaultNextTreatments(
199 obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800200
201 if (forceSimple && treatments.size() > 1) {
pierventre592d10c2020-08-13 16:35:15 +0200202 log.warn("Forcing SIMPLE behavior for NextObjective with {} treatments {}",
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800203 treatments.size(), obj);
204 }
205
206 // If not forcing, we are essentially extracting the only available treatment.
Carmelo Casconed06a8512018-12-02 16:34:20 -0800207 final TrafficTreatment treatment = defaultNextTreatments(
208 obj.nextTreatments(), true).get(0).treatment();
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800209
210 resultBuilder.addFlowRule(flowRule(
211 obj, FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE,
212 selector, treatment));
213
214 handleEgress(obj, treatment, resultBuilder, false);
215 }
216
217 private void hashedNext(NextObjective obj,
218 ObjectiveTranslation.Builder resultBuilder)
219 throws FabricPipelinerException {
220
221 if (!capabilities.hasHashedTable()) {
222 simpleNext(obj, resultBuilder, true);
223 return;
224 }
225
226 // Updated result builder with hashed group.
227 final int groupId = selectGroup(obj, resultBuilder);
228
229 if (isGroupModifyOp(obj)) {
230 // No changes to flow rules.
231 return;
232 }
233
234 final TrafficSelector selector = nextIdSelector(obj.id());
235 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Carmelo Casconecb4327a2018-09-11 15:17:23 -0700236 .piTableAction(PiActionProfileGroupId.of(groupId))
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800237 .build();
238
239 resultBuilder.addFlowRule(flowRule(
240 obj, FabricConstants.FABRIC_INGRESS_NEXT_HASHED,
241 selector, treatment));
242 }
243
244 private void handleEgress(NextObjective obj, TrafficTreatment treatment,
245 ObjectiveTranslation.Builder resultBuilder,
246 boolean strict)
247 throws FabricPipelinerException {
248 final PortNumber outPort = outputPort(treatment);
249 final Instruction popVlanInst = l2Instruction(treatment, VLAN_POP);
pierventre31440602020-12-15 17:34:54 +0100250 if (outPort != null) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800251 if (strict && treatment.allInstructions().size() > 2) {
252 throw new FabricPipelinerException(
253 "Treatment contains instructions other " +
254 "than OUTPUT and VLAN_POP, cannot generate " +
255 "egress rules");
256 }
pierventre31440602020-12-15 17:34:54 +0100257 // We cannot program if there are no proper metadata in the objective
258 if (obj.meta() != null && obj.meta().getCriterion(Criterion.Type.VLAN_VID) != null) {
259 egressVlan(outPort, obj, popVlanInst, resultBuilder);
260 } else {
261 log.warn("NextObjective {} is trying to program {} without {} information",
262 obj, FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
263 obj.meta() == null ? "metadata" : "vlanId");
264 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800265 }
266 }
267
pierventre31440602020-12-15 17:34:54 +0100268 private void egressVlan(PortNumber outPort, NextObjective obj, Instruction popVlanInst,
269 ObjectiveTranslation.Builder resultBuilder)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800270 throws FabricPipelinerException {
271
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800272 final VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) criterion(
273 obj.meta(), Criterion.Type.VLAN_VID);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800274
275 final PiCriterion egressVlanTableMatch = PiCriterion.builder()
276 .matchExact(FabricConstants.HDR_EG_PORT, outPort.toLong())
277 .build();
278 final TrafficSelector selector = DefaultTrafficSelector.builder()
279 .matchPi(egressVlanTableMatch)
280 .matchVlanId(vlanIdCriterion.vlanId())
281 .build();
pierventre31440602020-12-15 17:34:54 +0100282 final TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
283 if (popVlanInst == null) {
284 treatmentBuilder.pushVlan();
285 } else {
286 treatmentBuilder.popVlan();
287 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800288
289 resultBuilder.addFlowRule(flowRule(
290 obj, FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
pierventre31440602020-12-15 17:34:54 +0100291 selector, treatmentBuilder.build()));
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800292 }
293
294 private TrafficSelector nextIdSelector(int nextId) {
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800295 return nextIdSelectorBuilder(nextId).build();
296 }
297
298 private TrafficSelector.Builder nextIdSelectorBuilder(int nextId) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800299 final PiCriterion nextIdCriterion = PiCriterion.builder()
300 .matchExact(FabricConstants.HDR_NEXT_ID, nextId)
301 .build();
302 return DefaultTrafficSelector.builder()
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800303 .matchPi(nextIdCriterion);
304 }
305
306 private void xconnectNext(NextObjective obj, ObjectiveTranslation.Builder resultBuilder)
307 throws FabricPipelinerException {
308
309 final Collection<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800310 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800311
312 final List<PortNumber> outPorts = defaultNextTreatments.stream()
313 .map(DefaultNextTreatment::treatment)
314 .map(FabricUtils::outputPort)
315 .filter(Objects::nonNull)
316 .collect(Collectors.toList());
317
318 if (outPorts.size() != 2) {
319 throw new FabricPipelinerException(format(
320 "Handling XCONNECT with %d treatments (ports), but expected is 2",
321 defaultNextTreatments.size()), ObjectiveError.UNSUPPORTED);
322 }
323
324 final PortNumber port1 = outPorts.get(0);
325 final PortNumber port2 = outPorts.get(1);
326 final TrafficSelector selector1 = nextIdSelectorBuilder(obj.id())
327 .matchInPort(port1)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800328 .build();
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800329 final TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
330 .setOutput(port2)
331 .build();
332 final TrafficSelector selector2 = nextIdSelectorBuilder(obj.id())
333 .matchInPort(port2)
334 .build();
335 final TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
336 .setOutput(port1)
337 .build();
338
339 resultBuilder.addFlowRule(flowRule(
340 obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
341 selector1, treatment1));
342 resultBuilder.addFlowRule(flowRule(
343 obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
344 selector2, treatment2));
345
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800346 }
347
348 private void multicastNext(NextObjective obj,
349 ObjectiveTranslation.Builder resultBuilder)
350 throws FabricPipelinerException {
351
352 // Create ALL group that will be translated to a PRE multicast entry.
353 final int groupId = allGroup(obj, resultBuilder);
354
355 if (isGroupModifyOp(obj)) {
356 // No changes to flow rules.
357 return;
358 }
359
360 final TrafficSelector selector = nextIdSelector(obj.id());
361 final PiActionParam groupIdParam = new PiActionParam(
362 FabricConstants.GROUP_ID, groupId);
363 final PiAction setMcGroupAction = PiAction.builder()
364 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_MCAST_GROUP_ID)
365 .withParameter(groupIdParam)
366 .build();
367 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
368 .piTableAction(setMcGroupAction)
369 .build();
370
371 resultBuilder.addFlowRule(flowRule(
372 obj, FabricConstants.FABRIC_INGRESS_NEXT_MULTICAST,
373 selector, treatment));
374 }
375
376 private int selectGroup(NextObjective obj,
377 ObjectiveTranslation.Builder resultBuilder)
378 throws FabricPipelinerException {
379
380 final PiTableId hashedTableId = FabricConstants.FABRIC_INGRESS_NEXT_HASHED;
381 final List<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800382 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800383 final List<TrafficTreatment> piTreatments = Lists.newArrayList();
384
385 for (DefaultNextTreatment t : defaultNextTreatments) {
386 // Map treatment to PI...
387 piTreatments.add(mapTreatmentToPiIfNeeded(t.treatment(), hashedTableId));
388 // ...and handle egress if necessary.
389 handleEgress(obj, t.treatment(), resultBuilder, false);
390 }
391
392 final List<GroupBucket> bucketList = piTreatments.stream()
393 .map(DefaultGroupBucket::createSelectGroupBucket)
394 .collect(Collectors.toList());
395
396 final int groupId = obj.id();
397 final PiGroupKey groupKey = new PiGroupKey(
398 hashedTableId,
399 FabricConstants.FABRIC_INGRESS_NEXT_HASHED_SELECTOR,
400 groupId);
401
402 resultBuilder.addGroup(new DefaultGroupDescription(
403 deviceId,
404 GroupDescription.Type.SELECT,
405 new GroupBuckets(bucketList),
406 groupKey,
407 groupId,
408 obj.appId()));
409
410 return groupId;
411 }
412
413 private int allGroup(NextObjective obj,
414 ObjectiveTranslation.Builder resultBuilder)
415 throws FabricPipelinerException {
416
417 final Collection<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800418 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800419 // No need to map treatments to PI as translation of ALL groups to PRE
420 // multicast entries is based solely on the output port.
421 for (DefaultNextTreatment t : defaultNextTreatments) {
422 handleEgress(obj, t.treatment(), resultBuilder, true);
423 }
424
425 // FIXME: this implementation supports only the case in which each
426 // switch interface is associated with only one VLAN, otherwise we would
427 // need to support replicating multiple times the same packet for the
428 // same port while setting different VLAN IDs. Hence, collect in a set.
429 final Set<PortNumber> outPorts = defaultNextTreatments.stream()
430 .map(DefaultNextTreatment::treatment)
431 .map(FabricUtils::outputPort)
432 .filter(Objects::nonNull)
433 .collect(Collectors.toSet());
434
435 if (outPorts.size() != defaultNextTreatments.size()) {
436 throw new FabricPipelinerException(format(
437 "Found BROADCAST NextObjective with %d treatments but " +
438 "found only %d distinct OUTPUT port numbers, cannot " +
439 "translate to ALL groups",
440 defaultNextTreatments.size(), outPorts.size()),
441 ObjectiveError.UNSUPPORTED);
442 }
443
444 final List<GroupBucket> bucketList = outPorts.stream()
445 .map(p -> DefaultTrafficTreatment.builder().setOutput(p).build())
446 .map(DefaultGroupBucket::createAllGroupBucket)
447 .collect(Collectors.toList());
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800448
449 final int groupId = obj.id();
450 // Use DefaultGroupKey instead of PiGroupKey as we don't have any
451 // action profile to apply to the groups of ALL type.
452 final GroupKey groupKey = new DefaultGroupKey(
453 FabricPipeliner.KRYO.serialize(groupId));
454
455 resultBuilder.addGroup(
456 new DefaultGroupDescription(
457 deviceId,
458 GroupDescription.Type.ALL,
459 new GroupBuckets(bucketList),
460 groupKey,
461 groupId,
462 obj.appId()));
463
464 return groupId;
465 }
466
Carmelo Casconed06a8512018-12-02 16:34:20 -0800467 private List<DefaultNextTreatment> defaultNextTreatments(
468 Collection<NextTreatment> nextTreatments, boolean strict)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800469 throws FabricPipelinerException {
470 final List<DefaultNextTreatment> defaultNextTreatments = Lists.newArrayList();
471 final List<NextTreatment> unsupportedNextTreatments = Lists.newArrayList();
472 for (NextTreatment n : nextTreatments) {
473 if (n.type() == NextTreatment.Type.TREATMENT) {
474 defaultNextTreatments.add((DefaultNextTreatment) n);
475 } else {
476 unsupportedNextTreatments.add(n);
477 }
478 }
Carmelo Casconed06a8512018-12-02 16:34:20 -0800479 if (strict && !unsupportedNextTreatments.isEmpty()) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800480 throw new FabricPipelinerException(format(
481 "Unsupported NextTreatments: %s",
482 unsupportedNextTreatments));
483 }
484 return defaultNextTreatments;
485 }
486
Carmelo Casconed06a8512018-12-02 16:34:20 -0800487 private TrafficTreatment getFirstDefaultNextTreatmentIfAny(
488 Collection<NextTreatment> nextTreatments)
489 throws FabricPipelinerException {
490 final Collection<DefaultNextTreatment> nexts = defaultNextTreatments(nextTreatments, false);
491 return nexts.isEmpty() ? null : nexts.iterator().next().treatment();
492 }
493
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800494 private boolean isGroupModifyOp(NextObjective obj) {
pierventre592d10c2020-08-13 16:35:15 +0200495 // If operation is ADD_TO_EXIST, REMOVE_FROM_EXIST or MODIFY, it means we modify
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800496 // group buckets only, no changes for flow rules.
pierventre592d10c2020-08-13 16:35:15 +0200497 // FIXME Please note that for MODIFY op this could not apply in future if we extend the scope of MODIFY
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800498 return obj.op() == Objective.Operation.ADD_TO_EXISTING ||
pierventre592d10c2020-08-13 16:35:15 +0200499 obj.op() == Objective.Operation.REMOVE_FROM_EXISTING ||
500 obj.op() == Objective.Operation.MODIFY;
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800501 }
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800502
503 private boolean isXconnect(NextObjective obj) {
504 return obj.appId().name().contains(XCONNECT);
505 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800506}