blob: 4e3304d63102a5b36844fe6045baf44d7ffc899c [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;
50import org.onosproject.pipelines.fabric.impl.behaviour.FabricConstants;
51import 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) {
202 log.warn("Forcing SIMPLE behavior for NextObjective with {} treatments []",
203 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);
250 if (popVlanInst != null && outPort != null) {
251 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 }
257 egressVlanPop(outPort, obj, resultBuilder);
258 }
259 }
260
261 private void egressVlanPop(PortNumber outPort, NextObjective obj,
262 ObjectiveTranslation.Builder resultBuilder)
263 throws FabricPipelinerException {
264
265 if (obj.meta() == null) {
266 throw new FabricPipelinerException(
267 "Cannot process egress pop VLAN rule, NextObjective has null meta",
268 ObjectiveError.BADPARAMS);
269 }
270
271 final VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) criterion(
272 obj.meta(), Criterion.Type.VLAN_VID);
273 if (vlanIdCriterion == null) {
274 throw new FabricPipelinerException(
275 "Cannot process egress pop VLAN rule, missing VLAN_VID criterion " +
276 "in NextObjective meta",
277 ObjectiveError.BADPARAMS);
278 }
279
280 final PiCriterion egressVlanTableMatch = PiCriterion.builder()
281 .matchExact(FabricConstants.HDR_EG_PORT, outPort.toLong())
282 .build();
283 final TrafficSelector selector = DefaultTrafficSelector.builder()
284 .matchPi(egressVlanTableMatch)
285 .matchVlanId(vlanIdCriterion.vlanId())
286 .build();
287 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
288 .popVlan()
289 .build();
290
291 resultBuilder.addFlowRule(flowRule(
292 obj, FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
293 selector, treatment));
294 }
295
296 private TrafficSelector nextIdSelector(int nextId) {
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800297 return nextIdSelectorBuilder(nextId).build();
298 }
299
300 private TrafficSelector.Builder nextIdSelectorBuilder(int nextId) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800301 final PiCriterion nextIdCriterion = PiCriterion.builder()
302 .matchExact(FabricConstants.HDR_NEXT_ID, nextId)
303 .build();
304 return DefaultTrafficSelector.builder()
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800305 .matchPi(nextIdCriterion);
306 }
307
308 private void xconnectNext(NextObjective obj, ObjectiveTranslation.Builder resultBuilder)
309 throws FabricPipelinerException {
310
311 final Collection<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800312 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800313
314 final List<PortNumber> outPorts = defaultNextTreatments.stream()
315 .map(DefaultNextTreatment::treatment)
316 .map(FabricUtils::outputPort)
317 .filter(Objects::nonNull)
318 .collect(Collectors.toList());
319
320 if (outPorts.size() != 2) {
321 throw new FabricPipelinerException(format(
322 "Handling XCONNECT with %d treatments (ports), but expected is 2",
323 defaultNextTreatments.size()), ObjectiveError.UNSUPPORTED);
324 }
325
326 final PortNumber port1 = outPorts.get(0);
327 final PortNumber port2 = outPorts.get(1);
328 final TrafficSelector selector1 = nextIdSelectorBuilder(obj.id())
329 .matchInPort(port1)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800330 .build();
Carmelo Cascone45cc0862018-11-26 11:50:41 -0800331 final TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
332 .setOutput(port2)
333 .build();
334 final TrafficSelector selector2 = nextIdSelectorBuilder(obj.id())
335 .matchInPort(port2)
336 .build();
337 final TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
338 .setOutput(port1)
339 .build();
340
341 resultBuilder.addFlowRule(flowRule(
342 obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
343 selector1, treatment1));
344 resultBuilder.addFlowRule(flowRule(
345 obj, FabricConstants.FABRIC_INGRESS_NEXT_XCONNECT,
346 selector2, treatment2));
347
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800348 }
349
350 private void multicastNext(NextObjective obj,
351 ObjectiveTranslation.Builder resultBuilder)
352 throws FabricPipelinerException {
353
354 // Create ALL group that will be translated to a PRE multicast entry.
355 final int groupId = allGroup(obj, resultBuilder);
356
357 if (isGroupModifyOp(obj)) {
358 // No changes to flow rules.
359 return;
360 }
361
362 final TrafficSelector selector = nextIdSelector(obj.id());
363 final PiActionParam groupIdParam = new PiActionParam(
364 FabricConstants.GROUP_ID, groupId);
365 final PiAction setMcGroupAction = PiAction.builder()
366 .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_MCAST_GROUP_ID)
367 .withParameter(groupIdParam)
368 .build();
369 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
370 .piTableAction(setMcGroupAction)
371 .build();
372
373 resultBuilder.addFlowRule(flowRule(
374 obj, FabricConstants.FABRIC_INGRESS_NEXT_MULTICAST,
375 selector, treatment));
376 }
377
378 private int selectGroup(NextObjective obj,
379 ObjectiveTranslation.Builder resultBuilder)
380 throws FabricPipelinerException {
381
382 final PiTableId hashedTableId = FabricConstants.FABRIC_INGRESS_NEXT_HASHED;
383 final List<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800384 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800385 final List<TrafficTreatment> piTreatments = Lists.newArrayList();
386
387 for (DefaultNextTreatment t : defaultNextTreatments) {
388 // Map treatment to PI...
389 piTreatments.add(mapTreatmentToPiIfNeeded(t.treatment(), hashedTableId));
390 // ...and handle egress if necessary.
391 handleEgress(obj, t.treatment(), resultBuilder, false);
392 }
393
394 final List<GroupBucket> bucketList = piTreatments.stream()
395 .map(DefaultGroupBucket::createSelectGroupBucket)
396 .collect(Collectors.toList());
397
398 final int groupId = obj.id();
399 final PiGroupKey groupKey = new PiGroupKey(
400 hashedTableId,
401 FabricConstants.FABRIC_INGRESS_NEXT_HASHED_SELECTOR,
402 groupId);
403
404 resultBuilder.addGroup(new DefaultGroupDescription(
405 deviceId,
406 GroupDescription.Type.SELECT,
407 new GroupBuckets(bucketList),
408 groupKey,
409 groupId,
410 obj.appId()));
411
412 return groupId;
413 }
414
415 private int allGroup(NextObjective obj,
416 ObjectiveTranslation.Builder resultBuilder)
417 throws FabricPipelinerException {
418
419 final Collection<DefaultNextTreatment> defaultNextTreatments =
Carmelo Casconed06a8512018-12-02 16:34:20 -0800420 defaultNextTreatments(obj.nextTreatments(), true);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800421 // No need to map treatments to PI as translation of ALL groups to PRE
422 // multicast entries is based solely on the output port.
423 for (DefaultNextTreatment t : defaultNextTreatments) {
424 handleEgress(obj, t.treatment(), resultBuilder, true);
425 }
426
427 // FIXME: this implementation supports only the case in which each
428 // switch interface is associated with only one VLAN, otherwise we would
429 // need to support replicating multiple times the same packet for the
430 // same port while setting different VLAN IDs. Hence, collect in a set.
431 final Set<PortNumber> outPorts = defaultNextTreatments.stream()
432 .map(DefaultNextTreatment::treatment)
433 .map(FabricUtils::outputPort)
434 .filter(Objects::nonNull)
435 .collect(Collectors.toSet());
436
437 if (outPorts.size() != defaultNextTreatments.size()) {
438 throw new FabricPipelinerException(format(
439 "Found BROADCAST NextObjective with %d treatments but " +
440 "found only %d distinct OUTPUT port numbers, cannot " +
441 "translate to ALL groups",
442 defaultNextTreatments.size(), outPorts.size()),
443 ObjectiveError.UNSUPPORTED);
444 }
445
446 final List<GroupBucket> bucketList = outPorts.stream()
447 .map(p -> DefaultTrafficTreatment.builder().setOutput(p).build())
448 .map(DefaultGroupBucket::createAllGroupBucket)
449 .collect(Collectors.toList());
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800450
451 final int groupId = obj.id();
452 // Use DefaultGroupKey instead of PiGroupKey as we don't have any
453 // action profile to apply to the groups of ALL type.
454 final GroupKey groupKey = new DefaultGroupKey(
455 FabricPipeliner.KRYO.serialize(groupId));
456
457 resultBuilder.addGroup(
458 new DefaultGroupDescription(
459 deviceId,
460 GroupDescription.Type.ALL,
461 new GroupBuckets(bucketList),
462 groupKey,
463 groupId,
464 obj.appId()));
465
466 return groupId;
467 }
468
Carmelo Casconed06a8512018-12-02 16:34:20 -0800469 private List<DefaultNextTreatment> defaultNextTreatments(
470 Collection<NextTreatment> nextTreatments, boolean strict)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800471 throws FabricPipelinerException {
472 final List<DefaultNextTreatment> defaultNextTreatments = Lists.newArrayList();
473 final List<NextTreatment> unsupportedNextTreatments = Lists.newArrayList();
474 for (NextTreatment n : nextTreatments) {
475 if (n.type() == NextTreatment.Type.TREATMENT) {
476 defaultNextTreatments.add((DefaultNextTreatment) n);
477 } else {
478 unsupportedNextTreatments.add(n);
479 }
480 }
Carmelo Casconed06a8512018-12-02 16:34:20 -0800481 if (strict && !unsupportedNextTreatments.isEmpty()) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800482 throw new FabricPipelinerException(format(
483 "Unsupported NextTreatments: %s",
484 unsupportedNextTreatments));
485 }
486 return defaultNextTreatments;
487 }
488
Carmelo Casconed06a8512018-12-02 16:34:20 -0800489 private TrafficTreatment getFirstDefaultNextTreatmentIfAny(
490 Collection<NextTreatment> nextTreatments)
491 throws FabricPipelinerException {
492 final Collection<DefaultNextTreatment> nexts = defaultNextTreatments(nextTreatments, false);
493 return nexts.isEmpty() ? null : nexts.iterator().next().treatment();
494 }
495
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800496 private boolean isGroupModifyOp(NextObjective obj) {
497 // If operation is ADD_TO_EXIST or REMOVE_FROM_EXIST, it means we modify
498 // group buckets only, no changes for flow rules.
499 return obj.op() == Objective.Operation.ADD_TO_EXISTING ||
500 obj.op() == Objective.Operation.REMOVE_FROM_EXISTING;
501 }
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}