blob: 3105e66209b016382a926d5c33b85c0ea68ea43d [file] [log] [blame]
Yi Tseng0b809722017-11-03 10:23:26 -07001/*
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 Cascone356ab8b2019-09-25 01:02:53 -070017package org.onosproject.pipelines.fabric.impl.behaviour.pipeliner;
Yi Tseng0b809722017-11-03 10:23:26 -070018
19import com.google.common.collect.ImmutableList;
pierventrea137fba2021-06-14 20:28:35 +020020import com.google.common.collect.Lists;
21import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
Carmelo Cascone2388cc12021-05-26 19:30:30 +020023import org.onlab.packet.Ethernet;
Yi Tseng0b809722017-11-03 10:23:26 -070024import org.onlab.util.KryoNamespace;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080025import org.onlab.util.SharedExecutors;
Carmelo Cascone2388cc12021-05-26 19:30:30 +020026import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
Yi Tseng0b809722017-11-03 10:23:26 -070028import org.onosproject.net.DeviceId;
29import org.onosproject.net.PortNumber;
30import org.onosproject.net.behaviour.NextGroup;
31import org.onosproject.net.behaviour.Pipeliner;
32import org.onosproject.net.behaviour.PipelinerContext;
Carmelo Cascone2388cc12021-05-26 19:30:30 +020033import org.onosproject.net.flow.DefaultFlowRule;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
Yi Tseng0b809722017-11-03 10:23:26 -070036import org.onosproject.net.flow.FlowRule;
37import org.onosproject.net.flow.FlowRuleOperations;
Yi Tseng0b809722017-11-03 10:23:26 -070038import org.onosproject.net.flow.FlowRuleService;
Carmelo Cascone2388cc12021-05-26 19:30:30 +020039import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.flow.TrafficTreatment;
41import org.onosproject.net.flow.criteria.Criteria;
42import org.onosproject.net.flow.criteria.PiCriterion;
pierventrea137fba2021-06-14 20:28:35 +020043import org.onosproject.net.flowobjective.DefaultNextObjective;
Yi Tseng0b809722017-11-03 10:23:26 -070044import org.onosproject.net.flowobjective.FilteringObjective;
45import org.onosproject.net.flowobjective.FlowObjectiveStore;
46import org.onosproject.net.flowobjective.ForwardingObjective;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080047import org.onosproject.net.flowobjective.IdNextTreatment;
Yi Tseng0b809722017-11-03 10:23:26 -070048import org.onosproject.net.flowobjective.NextObjective;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080049import org.onosproject.net.flowobjective.NextTreatment;
Yi Tseng0b809722017-11-03 10:23:26 -070050import org.onosproject.net.flowobjective.Objective;
51import org.onosproject.net.flowobjective.ObjectiveError;
pierventrea137fba2021-06-14 20:28:35 +020052import org.onosproject.net.group.DefaultGroup;
53import org.onosproject.net.group.Group;
54import org.onosproject.net.group.GroupBucket;
55import org.onosproject.net.group.GroupBuckets;
Yi Tseng0b809722017-11-03 10:23:26 -070056import org.onosproject.net.group.GroupDescription;
pierventrea137fba2021-06-14 20:28:35 +020057import org.onosproject.net.group.GroupKey;
Yi Tseng0b809722017-11-03 10:23:26 -070058import org.onosproject.net.group.GroupService;
Carmelo Cascone2388cc12021-05-26 19:30:30 +020059import org.onosproject.net.pi.runtime.PiAction;
60import org.onosproject.net.pi.runtime.PiActionParam;
61import org.onosproject.pipelines.fabric.FabricConstants;
62import org.onosproject.pipelines.fabric.impl.FabricPipeconfLoader;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070063import org.onosproject.pipelines.fabric.impl.behaviour.AbstractFabricHandlerBehavior;
64import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities;
Yi Tseng0b809722017-11-03 10:23:26 -070065import org.onosproject.store.serializers.KryoNamespaces;
66import org.slf4j.Logger;
67
68import java.util.Collection;
pierventrea137fba2021-06-14 20:28:35 +020069import java.util.Collections;
Yi Tseng0b809722017-11-03 10:23:26 -070070import java.util.List;
pierventrea137fba2021-06-14 20:28:35 +020071import java.util.Map;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080072import java.util.Objects;
pierventrea137fba2021-06-14 20:28:35 +020073import java.util.Set;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080074import java.util.concurrent.CompletableFuture;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080075import java.util.concurrent.ExecutorService;
Yi Tseng1b154bd2017-11-20 17:48:19 -080076import java.util.stream.Collectors;
Yi Tseng0b809722017-11-03 10:23:26 -070077
Carmelo Casconeb5324e72018-11-25 02:26:32 -080078import static java.lang.String.format;
pierventre4e02fb52020-08-13 16:35:15 +020079import static org.onosproject.net.flowobjective.NextObjective.Type.SIMPLE;
Carmelo Cascone2388cc12021-05-26 19:30:30 +020080import static org.onosproject.pipelines.fabric.impl.behaviour.FabricInterpreter.ONE;
81import static org.onosproject.pipelines.fabric.impl.behaviour.FabricInterpreter.ZERO;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070082import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.outputPort;
Wailok Shumfb7e7872021-06-18 17:30:08 +080083import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.FWD_IPV4_ROUTING;
84import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.PORT_TYPE_INTERNAL;
Yi Tseng0b809722017-11-03 10:23:26 -070085import static org.slf4j.LoggerFactory.getLogger;
86
87/**
Carmelo Casconeb5324e72018-11-25 02:26:32 -080088 * Pipeliner implementation for fabric pipeline which uses ObjectiveTranslator
89 * implementations to translate flow objectives for the different blocks,
90 * filtering, forwarding and next.
Yi Tseng0b809722017-11-03 10:23:26 -070091 */
Carmelo Casconeb5324e72018-11-25 02:26:32 -080092public class FabricPipeliner extends AbstractFabricHandlerBehavior
93 implements Pipeliner {
94
Yi Tseng0b809722017-11-03 10:23:26 -070095 private static final Logger log = getLogger(FabricPipeliner.class);
Carmelo Cascone2388cc12021-05-26 19:30:30 +020096 private static final int DEFAULT_FLOW_PRIORITY = 100;
97 public static final int DEFAULT_VLAN = 4094;
Yi Tseng0b809722017-11-03 10:23:26 -070098
99 protected static final KryoNamespace KRYO = new KryoNamespace.Builder()
100 .register(KryoNamespaces.API)
101 .register(FabricNextGroup.class)
102 .build("FabricPipeliner");
103
Yi Tseng0b809722017-11-03 10:23:26 -0700104 protected DeviceId deviceId;
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200105 protected ApplicationId appId;
Yi Tseng0b809722017-11-03 10:23:26 -0700106 protected FlowRuleService flowRuleService;
107 protected GroupService groupService;
108 protected FlowObjectiveStore flowObjectiveStore;
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200109 protected CoreService coreService;
Yi Tseng0b809722017-11-03 10:23:26 -0700110
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800111 private FilteringObjectiveTranslator filteringTranslator;
112 private ForwardingObjectiveTranslator forwardingTranslator;
113 private NextObjectiveTranslator nextTranslator;
Charles Chan91ea9722018-08-30 15:56:32 -0700114
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800115 private final ExecutorService callbackExecutor = SharedExecutors.getPoolThreadExecutor();
Yi Tseng0b809722017-11-03 10:23:26 -0700116
Daniele Morof51d0c12019-07-30 10:43:10 -0700117 /**
118 * Creates a new instance of this behavior with the given capabilities.
119 *
120 * @param capabilities capabilities
121 */
122 public FabricPipeliner(FabricCapabilities capabilities) {
123 super(capabilities);
124 }
125
126 /**
127 * Create a new instance of this behaviour. Used by the abstract projectable
128 * model (i.e., {@link org.onosproject.net.Device#as(Class)}.
129 */
130 public FabricPipeliner() {
131 super();
132 }
133
Yi Tseng0b809722017-11-03 10:23:26 -0700134 @Override
135 public void init(DeviceId deviceId, PipelinerContext context) {
136 this.deviceId = deviceId;
137 this.flowRuleService = context.directory().get(FlowRuleService.class);
138 this.groupService = context.directory().get(GroupService.class);
139 this.flowObjectiveStore = context.directory().get(FlowObjectiveStore.class);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800140 this.filteringTranslator = new FilteringObjectiveTranslator(deviceId, capabilities);
141 this.forwardingTranslator = new ForwardingObjectiveTranslator(deviceId, capabilities);
142 this.nextTranslator = new NextObjectiveTranslator(deviceId, capabilities);
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200143 this.coreService = context.directory().get(CoreService.class);
144 this.appId = coreService.getAppId(FabricPipeconfLoader.PIPELINE_APP_NAME);
Carmelo Cascone2a308ff2021-06-01 18:31:57 -0700145
146 initializePipeline();
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200147 }
148
149 protected void initializePipeline() {
150 // Set up rules for packet-out forwarding. We support only IPv4 routing.
151 final int cpuPort = capabilities.cpuPort().get();
152 flowRuleService.applyFlowRules(
153 ingressVlanRule(cpuPort, false, DEFAULT_VLAN),
154 fwdClassifierRule(cpuPort, null, Ethernet.TYPE_IPV4, FWD_IPV4_ROUTING,
155 DEFAULT_FLOW_PRIORITY));
Yi Tseng0b809722017-11-03 10:23:26 -0700156 }
157
158 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800159 public void filter(FilteringObjective obj) {
160 final ObjectiveTranslation result = filteringTranslator.translate(obj);
161 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700162 }
163
164 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800165 public void forward(ForwardingObjective obj) {
166 final ObjectiveTranslation result = forwardingTranslator.translate(obj);
167 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700168 }
169
170 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800171 public void next(NextObjective obj) {
172 if (obj.op() == Objective.Operation.VERIFY) {
pierventrea137fba2021-06-14 20:28:35 +0200173 if (obj.type() != NextObjective.Type.HASHED) {
174 log.warn("VERIFY operation not yet supported for NextObjective {}, will return failure :(",
175 obj.type());
176 fail(obj, ObjectiveError.UNSUPPORTED);
177 return;
178 }
179
180 if (log.isTraceEnabled()) {
181 log.trace("Verify NextObjective {} in dev {}", obj, deviceId);
182 }
183 ObjectiveError error = handleVerify(obj);
184 if (error == null) {
185 success(obj);
186 } else {
187 fail(obj, error);
188 }
Yi Tseng1b154bd2017-11-20 17:48:19 -0800189 return;
190 }
191
pierventre4e02fb52020-08-13 16:35:15 +0200192 if (obj.op() == Objective.Operation.MODIFY && obj.type() != SIMPLE) {
193 log.warn("MODIFY operation not yet supported for NextObjective {}, will return failure :(",
194 obj.type());
195 if (log.isTraceEnabled()) {
196 log.trace("Objective {}", obj);
197 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800198 fail(obj, ObjectiveError.UNSUPPORTED);
Charles Chan91ea9722018-08-30 15:56:32 -0700199 return;
200 }
201
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800202 final ObjectiveTranslation result = nextTranslator.translate(obj);
203 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700204 }
205
206 @Override
207 public List<String> getNextMappings(NextGroup nextGroup) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800208 final FabricNextGroup fabricNextGroup = KRYO.deserialize(nextGroup.data());
209 return fabricNextGroup.nextMappings().stream()
210 .map(m -> format("%s -> %s", fabricNextGroup.type(), m))
Yi Tseng1b154bd2017-11-20 17:48:19 -0800211 .collect(Collectors.toList());
Yi Tseng0b809722017-11-03 10:23:26 -0700212 }
213
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800214 private void handleResult(Objective obj, ObjectiveTranslation result) {
215 if (result.error().isPresent()) {
216 fail(obj, result.error().get());
217 return;
218 }
219 processGroups(obj, result.groups());
220 processFlows(obj, result.flowRules());
221 if (obj instanceof NextObjective) {
222 handleNextGroup((NextObjective) obj);
223 }
224 success(obj);
Yi Tseng0b809722017-11-03 10:23:26 -0700225 }
226
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800227 private void handleNextGroup(NextObjective obj) {
pierventrea137fba2021-06-14 20:28:35 +0200228 // FIXME SDFAB-250 ADD_TO and REMOVE_FROM should update the content
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800229 switch (obj.op()) {
230 case REMOVE:
231 removeNextGroup(obj);
232 break;
233 case ADD:
234 case ADD_TO_EXISTING:
235 case REMOVE_FROM_EXISTING:
236 case MODIFY:
237 putNextGroup(obj);
238 break;
239 case VERIFY:
240 break;
241 default:
242 log.error("Unknown NextObjective operation '{}'", obj.op());
243 }
244 }
245
246 private void processFlows(Objective objective, Collection<FlowRule> flowRules) {
Yi Tseng0b809722017-11-03 10:23:26 -0700247 if (flowRules.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800248 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700249 }
pierventre4e02fb52020-08-13 16:35:15 +0200250
251 if (log.isTraceEnabled()) {
252 log.trace("Objective {} -> Flows {}", objective, flowRules);
253 }
254
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800255 final FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
256 switch (objective.op()) {
257 case ADD:
258 case ADD_TO_EXISTING:
pierventre4e02fb52020-08-13 16:35:15 +0200259 case MODIFY:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800260 flowRules.forEach(ops::add);
261 break;
262 case REMOVE:
263 case REMOVE_FROM_EXISTING:
264 flowRules.forEach(ops::remove);
265 break;
266 default:
pierventre4e02fb52020-08-13 16:35:15 +0200267 log.warn("Unsupported Objective operation {}", objective.op());
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800268 return;
wu914ed232018-10-23 11:19:53 +0800269 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800270 flowRuleService.apply(ops.build());
Yi Tseng0b809722017-11-03 10:23:26 -0700271 }
272
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800273 private void processGroups(Objective objective, Collection<GroupDescription> groups) {
Yi Tseng0b809722017-11-03 10:23:26 -0700274 if (groups.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800275 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700276 }
pierventre4e02fb52020-08-13 16:35:15 +0200277
278 if (log.isTraceEnabled()) {
279 log.trace("Objective {} -> Groups {}", objective, groups);
280 }
281
Yi Tseng0b809722017-11-03 10:23:26 -0700282 switch (objective.op()) {
283 case ADD:
284 groups.forEach(groupService::addGroup);
285 break;
286 case REMOVE:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800287 groups.forEach(group -> groupService.removeGroup(
288 deviceId, group.appCookie(), objective.appId()));
Yi Tseng0b809722017-11-03 10:23:26 -0700289 break;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800290 case ADD_TO_EXISTING:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800291 groups.forEach(group -> groupService.addBucketsToGroup(
292 deviceId, group.appCookie(), group.buckets(),
293 group.appCookie(), group.appId())
Charles Chan91ea9722018-08-30 15:56:32 -0700294 );
Yi Tseng1b154bd2017-11-20 17:48:19 -0800295 break;
296 case REMOVE_FROM_EXISTING:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800297 groups.forEach(group -> groupService.removeBucketsFromGroup(
298 deviceId, group.appCookie(), group.buckets(),
299 group.appCookie(), group.appId())
Charles Chan91ea9722018-08-30 15:56:32 -0700300 );
Yi Tseng1b154bd2017-11-20 17:48:19 -0800301 break;
pierventre4e02fb52020-08-13 16:35:15 +0200302 case MODIFY:
303 // Modify is only supported for simple next objective
304 // Replace group bucket directly
305 groups.forEach(group -> groupService.setBucketsForGroup(
306 deviceId, group.appCookie(), group.buckets(),
307 group.appCookie(), group.appId())
308 );
309 break;
Yi Tseng0b809722017-11-03 10:23:26 -0700310 default:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800311 log.warn("Unsupported Objective operation {}", objective.op());
Yi Tseng0b809722017-11-03 10:23:26 -0700312 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800313 }
Yi Tseng0b809722017-11-03 10:23:26 -0700314
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800315 private void fail(Objective objective, ObjectiveError error) {
316 CompletableFuture.runAsync(
317 () -> objective.context().ifPresent(
318 ctx -> ctx.onError(objective, error)), callbackExecutor);
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800319
Yi Tseng0b809722017-11-03 10:23:26 -0700320 }
321
Charles Chan91ea9722018-08-30 15:56:32 -0700322
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800323 private void success(Objective objective) {
324 CompletableFuture.runAsync(
325 () -> objective.context().ifPresent(
326 ctx -> ctx.onSuccess(objective)), callbackExecutor);
Yi Tseng0b809722017-11-03 10:23:26 -0700327 }
328
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800329 private void removeNextGroup(NextObjective obj) {
330 final NextGroup removed = flowObjectiveStore.removeNextGroup(obj.id());
331 if (removed == null) {
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200332 log.debug("NextGroup {} was not found in FlowObjectiveStore", obj);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800333 }
Charles Chan91ea9722018-08-30 15:56:32 -0700334 }
335
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800336 private void putNextGroup(NextObjective obj) {
337 final List<String> nextMappings = obj.nextTreatments().stream()
338 .map(this::nextTreatmentToMappingString)
339 .filter(Objects::nonNull)
340 .collect(Collectors.toList());
341 final FabricNextGroup nextGroup = new FabricNextGroup(obj.type(), nextMappings);
342 flowObjectiveStore.putNextGroup(obj.id(), nextGroup);
343 }
344
345 private String nextTreatmentToMappingString(NextTreatment n) {
346 switch (n.type()) {
347 case TREATMENT:
348 final PortNumber p = outputPort(n);
349 return p == null ? "UNKNOWN"
350 : format("OUTPUT:%s", p.toString());
351 case ID:
352 final IdNextTreatment id = (IdNextTreatment) n;
353 return format("NEXT_ID:%d", id.nextId());
Yi Tseng0b809722017-11-03 10:23:26 -0700354 default:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800355 log.warn("Unknown NextTreatment type '{}'", n.type());
356 return "???";
Yi Tseng0b809722017-11-03 10:23:26 -0700357 }
Yi Tseng0b809722017-11-03 10:23:26 -0700358 }
359
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200360 public FlowRule ingressVlanRule(long port, boolean vlanValid, int vlanId) {
361 final TrafficSelector selector = DefaultTrafficSelector.builder()
362 .add(Criteria.matchInPort(PortNumber.portNumber(port)))
363 .add(PiCriterion.builder()
364 .matchExact(FabricConstants.HDR_VLAN_IS_VALID, vlanValid ? ONE : ZERO)
365 .build())
366 .build();
367 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
368 .piTableAction(PiAction.builder()
369 .withId(vlanValid ? FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT
370 : FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
371 .withParameter(new PiActionParam(FabricConstants.VLAN_ID, vlanId))
Wailok Shumfb7e7872021-06-18 17:30:08 +0800372 .withParameter(new PiActionParam(FabricConstants.PORT_TYPE, PORT_TYPE_INTERNAL))
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200373 .build())
374 .build();
375 return DefaultFlowRule.builder()
376 .withSelector(selector)
377 .withTreatment(treatment)
378 .forTable(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN)
379 .makePermanent()
380 .withPriority(DEFAULT_FLOW_PRIORITY)
381 .forDevice(deviceId)
382 .fromApp(appId)
383 .build();
384 }
385
386 public FlowRule fwdClassifierRule(int port, Short ethType, short ipEthType, byte fwdType, int priority) {
387 final TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
388 .matchInPort(PortNumber.portNumber(port))
389 .matchPi(PiCriterion.builder()
390 .matchExact(FabricConstants.HDR_IP_ETH_TYPE, ipEthType)
391 .build());
392 if (ethType != null) {
393 selectorBuilder.matchEthType(ethType);
394 }
395 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
396 .piTableAction(PiAction.builder()
397 .withId(FabricConstants.FABRIC_INGRESS_FILTERING_SET_FORWARDING_TYPE)
398 .withParameter(new PiActionParam(FabricConstants.FWD_TYPE, fwdType))
399 .build())
400 .build();
401 return DefaultFlowRule.builder()
402 .withSelector(selectorBuilder.build())
403 .withTreatment(treatment)
404 .forTable(FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER)
405 .makePermanent()
406 .withPriority(priority)
407 .forDevice(deviceId)
408 .fromApp(appId)
409 .build();
410 }
411
pierventrea137fba2021-06-14 20:28:35 +0200412 private ObjectiveError handleVerify(NextObjective nextObjective) {
413 Map<GroupBucket, FlowRule> bucketsToFlows = getBucketToFlowMapping(nextObjective);
414 if (bucketsToFlows.isEmpty() && !nextObjective.nextTreatments().isEmpty()) {
415 log.warn("VERIFY failed due to translation error, bucketsToFlows is empty");
416 return ObjectiveError.BADPARAMS;
417 }
418
419 if (log.isTraceEnabled()) {
420 log.trace("Mapping bucketsToFlows {} has been generated ", bucketsToFlows);
421 }
422
423 GroupKey groupKey = nextTranslator.getGroupKey(nextObjective);
424 if (groupKey == null) {
425 log.warn("VERIFY failed due to translation error, unable to determine group key");
426 return ObjectiveError.BADPARAMS;
427 }
428 Group groupFromStore = groupService.getGroup(deviceId, groupKey);
429 if (groupFromStore == null) {
430 log.warn("VERIFY failed due to missing group in the store");
431 return ObjectiveError.GROUPMISSING;
432 }
433
434 // Looking for duplicate buckets - remove them by using a set and comparing size after/before
435 Set<GroupBucket> bucketsFromStore = Sets.newHashSet(groupFromStore.buckets().buckets());
436 if (groupFromStore.buckets().buckets().size() > bucketsFromStore.size()) {
437 log.warn("Duplicated buckets detected in device:{}, nextId:{}, before-size" +
438 ":{} after-size:{} .. correcting", deviceId,
439 nextObjective.id(), groupFromStore.buckets().buckets().size(), bucketsFromStore.size());
440 final GroupBuckets bucketToSet = new GroupBuckets(Lists.newArrayList(bucketsFromStore));
441 groupService.setBucketsForGroup(deviceId, groupKey, bucketToSet, groupKey, nextObjective.appId());
442 // Forge temporary the group to avoid race condition with the store
443 groupFromStore = new DefaultGroup(groupFromStore.id(), deviceId, groupFromStore.type(), bucketToSet);
444 }
445
446 // Looking for buckets missing in the group but defined in the next
447 Map<GroupBucket, FlowRule> toAdd = Maps.newHashMap();
448 for (Map.Entry<GroupBucket, FlowRule> entry : bucketsToFlows.entrySet()) {
449 if (!groupFromStore.buckets().buckets().contains(entry.getKey())) {
450 toAdd.put(entry.getKey(), entry.getValue());
451 }
452 }
453
454 // Looking for buckets missing in the next but defined in the group
455 // FIXME SDFAB-250 we cannot remove associated egress flows
456 List<GroupBucket> toRemove = Lists.newArrayList();
457 groupFromStore.buckets().buckets().forEach(bucket -> {
458 if (!bucketsToFlows.containsKey(bucket)) {
459 toRemove.add(bucket);
460 }
461 });
462
463 if (!toAdd.isEmpty() || !toRemove.isEmpty()) {
464 log.warn("Mismatch detected in device:{}, nextId:{}, groupFromTranslation-size:{} " +
465 "groupFromStore-size:{} toAdd-size:{} toRemove-size: {} .. correcting",
466 deviceId, nextObjective.id(), bucketsToFlows.size(), groupFromStore.buckets().buckets().size(),
467 toAdd.size(), toRemove.size());
468 }
469
470 if (!toAdd.isEmpty()) {
471 if (log.isTraceEnabled()) {
472 log.trace("Adding missing buckets {} and flows {}", toAdd.keySet(), toAdd.values());
473 }
474 final FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
475 final FlowRule dummyFlow = getDummyFlow(nextObjective);
476 toAdd.values().stream()
477 .filter(flowRule -> !flowRule.equals(dummyFlow))
478 .forEach(ops::add);
479 final GroupBuckets bucketsToAdd = new GroupBuckets(Lists.newArrayList(toAdd.keySet()));
480 groupService.addBucketsToGroup(deviceId, groupKey, bucketsToAdd, groupKey, nextObjective.appId());
481 flowRuleService.apply(ops.build());
482 }
483
484 if (!toRemove.isEmpty()) {
485 if (log.isTraceEnabled()) {
486 log.trace("Removing stale buckets {}", toRemove);
487 }
488 final GroupBuckets bucketsToRemove = new GroupBuckets(toRemove);
489 groupService.removeBucketsFromGroup(deviceId, groupKey, bucketsToRemove, groupKey,
490 nextObjective.appId());
491 }
492
493 return null;
494 }
495
496 private Map<GroupBucket, FlowRule> getBucketToFlowMapping(NextObjective nextObjective) {
497 Map<GroupBucket, FlowRule> mapping = Maps.newHashMap();
498 NextObjective newNextObjective;
499 ObjectiveTranslation result;
500 FlowRule dummyFlow = getDummyFlow(nextObjective);
501 FlowRule egFlow;
502 GroupBucket groupBucket;
503 GroupDescription group;
504 for (NextTreatment nextTreatment : nextObjective.nextTreatments()) {
505 newNextObjective = DefaultNextObjective.builder()
506 .withId(nextObjective.id())
507 .withType(nextObjective.type())
508 .fromApp(nextObjective.appId())
509 .withMeta(nextObjective.meta())
510 .addTreatment(nextTreatment)
511 .verify();
512 result = nextTranslator.translate(newNextObjective);
513 if ((result.groups().isEmpty() && result.flowRules().isEmpty()) ||
514 result.groups().size() > 1) {
515 return Collections.emptyMap();
516 }
517 group = result.groups().iterator().next();
518 egFlow = result.flowRules().stream()
519 .filter(flowRule -> flowRule.table().equals(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN))
520 .findFirst()
521 .orElse(null);
522 if (group.buckets().buckets().isEmpty() || group.buckets().buckets().size() > 1) {
523 return Collections.emptyMap();
524 }
525 groupBucket = group.buckets().buckets().iterator().next();
526 if (egFlow == null) {
527 mapping.put(groupBucket, dummyFlow);
528 } else {
529 mapping.put(groupBucket, egFlow);
530 }
531 }
532 return mapping;
533 }
534
535 private FlowRule getDummyFlow(NextObjective nextObjective) {
536 return DefaultFlowRule.builder()
537 .forDevice(deviceId)
538 .forTable(0)
539 .fromApp(nextObjective.appId())
540 .withPriority(1)
541 .withSelector(DefaultTrafficSelector.emptySelector())
542 .makePermanent()
543 .build();
544 }
545
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800546 /**
547 * NextGroup implementation.
548 */
549 private static class FabricNextGroup implements NextGroup {
pierventrea137fba2021-06-14 20:28:35 +0200550 // FIXME SDFAB-250 they are not very useful nor technically correct
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800551 private final NextObjective.Type type;
552 private final List<String> nextMappings;
553
554 FabricNextGroup(NextObjective.Type type, List<String> nextMappings) {
Yi Tseng0b809722017-11-03 10:23:26 -0700555 this.type = type;
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800556 this.nextMappings = ImmutableList.copyOf(nextMappings);
Yi Tseng0b809722017-11-03 10:23:26 -0700557 }
558
Charles Chan91ea9722018-08-30 15:56:32 -0700559 NextObjective.Type type() {
Yi Tseng0b809722017-11-03 10:23:26 -0700560 return type;
561 }
562
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800563 Collection<String> nextMappings() {
564 return nextMappings;
Yi Tseng0b809722017-11-03 10:23:26 -0700565 }
566
567 @Override
568 public byte[] data() {
569 return KRYO.serialize(this);
570 }
571 }
Yi Tseng0b809722017-11-03 10:23:26 -0700572}