blob: 744cd9d44885767263442210d65e8add0b3f3d52 [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;
Carmelo Cascone2388cc12021-05-26 19:30:30 +020083import static org.onosproject.pipelines.fabric.impl.behaviour.pipeliner.FilteringObjectiveTranslator.FWD_IPV4_ROUTING;
Yi Tseng0b809722017-11-03 10:23:26 -070084import static org.slf4j.LoggerFactory.getLogger;
85
86/**
Carmelo Casconeb5324e72018-11-25 02:26:32 -080087 * Pipeliner implementation for fabric pipeline which uses ObjectiveTranslator
88 * implementations to translate flow objectives for the different blocks,
89 * filtering, forwarding and next.
Yi Tseng0b809722017-11-03 10:23:26 -070090 */
Carmelo Casconeb5324e72018-11-25 02:26:32 -080091public class FabricPipeliner extends AbstractFabricHandlerBehavior
92 implements Pipeliner {
93
Yi Tseng0b809722017-11-03 10:23:26 -070094 private static final Logger log = getLogger(FabricPipeliner.class);
Carmelo Cascone2388cc12021-05-26 19:30:30 +020095 private static final int DEFAULT_FLOW_PRIORITY = 100;
96 public static final int DEFAULT_VLAN = 4094;
Yi Tseng0b809722017-11-03 10:23:26 -070097
98 protected static final KryoNamespace KRYO = new KryoNamespace.Builder()
99 .register(KryoNamespaces.API)
100 .register(FabricNextGroup.class)
101 .build("FabricPipeliner");
102
Yi Tseng0b809722017-11-03 10:23:26 -0700103 protected DeviceId deviceId;
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200104 protected ApplicationId appId;
Yi Tseng0b809722017-11-03 10:23:26 -0700105 protected FlowRuleService flowRuleService;
106 protected GroupService groupService;
107 protected FlowObjectiveStore flowObjectiveStore;
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200108 protected CoreService coreService;
Yi Tseng0b809722017-11-03 10:23:26 -0700109
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800110 private FilteringObjectiveTranslator filteringTranslator;
111 private ForwardingObjectiveTranslator forwardingTranslator;
112 private NextObjectiveTranslator nextTranslator;
Charles Chan91ea9722018-08-30 15:56:32 -0700113
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800114 private final ExecutorService callbackExecutor = SharedExecutors.getPoolThreadExecutor();
Yi Tseng0b809722017-11-03 10:23:26 -0700115
Daniele Morof51d0c12019-07-30 10:43:10 -0700116 /**
117 * Creates a new instance of this behavior with the given capabilities.
118 *
119 * @param capabilities capabilities
120 */
121 public FabricPipeliner(FabricCapabilities capabilities) {
122 super(capabilities);
123 }
124
125 /**
126 * Create a new instance of this behaviour. Used by the abstract projectable
127 * model (i.e., {@link org.onosproject.net.Device#as(Class)}.
128 */
129 public FabricPipeliner() {
130 super();
131 }
132
Yi Tseng0b809722017-11-03 10:23:26 -0700133 @Override
134 public void init(DeviceId deviceId, PipelinerContext context) {
135 this.deviceId = deviceId;
136 this.flowRuleService = context.directory().get(FlowRuleService.class);
137 this.groupService = context.directory().get(GroupService.class);
138 this.flowObjectiveStore = context.directory().get(FlowObjectiveStore.class);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800139 this.filteringTranslator = new FilteringObjectiveTranslator(deviceId, capabilities);
140 this.forwardingTranslator = new ForwardingObjectiveTranslator(deviceId, capabilities);
141 this.nextTranslator = new NextObjectiveTranslator(deviceId, capabilities);
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200142 this.coreService = context.directory().get(CoreService.class);
143 this.appId = coreService.getAppId(FabricPipeconfLoader.PIPELINE_APP_NAME);
Carmelo Cascone2a308ff2021-06-01 18:31:57 -0700144
145 initializePipeline();
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200146 }
147
148 protected void initializePipeline() {
149 // Set up rules for packet-out forwarding. We support only IPv4 routing.
150 final int cpuPort = capabilities.cpuPort().get();
151 flowRuleService.applyFlowRules(
152 ingressVlanRule(cpuPort, false, DEFAULT_VLAN),
153 fwdClassifierRule(cpuPort, null, Ethernet.TYPE_IPV4, FWD_IPV4_ROUTING,
154 DEFAULT_FLOW_PRIORITY));
Yi Tseng0b809722017-11-03 10:23:26 -0700155 }
156
157 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800158 public void filter(FilteringObjective obj) {
159 final ObjectiveTranslation result = filteringTranslator.translate(obj);
160 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700161 }
162
163 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800164 public void forward(ForwardingObjective obj) {
165 final ObjectiveTranslation result = forwardingTranslator.translate(obj);
166 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700167 }
168
169 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800170 public void next(NextObjective obj) {
171 if (obj.op() == Objective.Operation.VERIFY) {
pierventrea137fba2021-06-14 20:28:35 +0200172 if (obj.type() != NextObjective.Type.HASHED) {
173 log.warn("VERIFY operation not yet supported for NextObjective {}, will return failure :(",
174 obj.type());
175 fail(obj, ObjectiveError.UNSUPPORTED);
176 return;
177 }
178
179 if (log.isTraceEnabled()) {
180 log.trace("Verify NextObjective {} in dev {}", obj, deviceId);
181 }
182 ObjectiveError error = handleVerify(obj);
183 if (error == null) {
184 success(obj);
185 } else {
186 fail(obj, error);
187 }
Yi Tseng1b154bd2017-11-20 17:48:19 -0800188 return;
189 }
190
pierventre4e02fb52020-08-13 16:35:15 +0200191 if (obj.op() == Objective.Operation.MODIFY && obj.type() != SIMPLE) {
192 log.warn("MODIFY operation not yet supported for NextObjective {}, will return failure :(",
193 obj.type());
194 if (log.isTraceEnabled()) {
195 log.trace("Objective {}", obj);
196 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800197 fail(obj, ObjectiveError.UNSUPPORTED);
Charles Chan91ea9722018-08-30 15:56:32 -0700198 return;
199 }
200
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800201 final ObjectiveTranslation result = nextTranslator.translate(obj);
202 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700203 }
204
205 @Override
206 public List<String> getNextMappings(NextGroup nextGroup) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800207 final FabricNextGroup fabricNextGroup = KRYO.deserialize(nextGroup.data());
208 return fabricNextGroup.nextMappings().stream()
209 .map(m -> format("%s -> %s", fabricNextGroup.type(), m))
Yi Tseng1b154bd2017-11-20 17:48:19 -0800210 .collect(Collectors.toList());
Yi Tseng0b809722017-11-03 10:23:26 -0700211 }
212
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800213 private void handleResult(Objective obj, ObjectiveTranslation result) {
214 if (result.error().isPresent()) {
215 fail(obj, result.error().get());
216 return;
217 }
218 processGroups(obj, result.groups());
219 processFlows(obj, result.flowRules());
220 if (obj instanceof NextObjective) {
221 handleNextGroup((NextObjective) obj);
222 }
223 success(obj);
Yi Tseng0b809722017-11-03 10:23:26 -0700224 }
225
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800226 private void handleNextGroup(NextObjective obj) {
pierventrea137fba2021-06-14 20:28:35 +0200227 // FIXME SDFAB-250 ADD_TO and REMOVE_FROM should update the content
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800228 switch (obj.op()) {
229 case REMOVE:
230 removeNextGroup(obj);
231 break;
232 case ADD:
233 case ADD_TO_EXISTING:
234 case REMOVE_FROM_EXISTING:
235 case MODIFY:
236 putNextGroup(obj);
237 break;
238 case VERIFY:
239 break;
240 default:
241 log.error("Unknown NextObjective operation '{}'", obj.op());
242 }
243 }
244
245 private void processFlows(Objective objective, Collection<FlowRule> flowRules) {
Yi Tseng0b809722017-11-03 10:23:26 -0700246 if (flowRules.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800247 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700248 }
pierventre4e02fb52020-08-13 16:35:15 +0200249
250 if (log.isTraceEnabled()) {
251 log.trace("Objective {} -> Flows {}", objective, flowRules);
252 }
253
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800254 final FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
255 switch (objective.op()) {
256 case ADD:
257 case ADD_TO_EXISTING:
pierventre4e02fb52020-08-13 16:35:15 +0200258 case MODIFY:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800259 flowRules.forEach(ops::add);
260 break;
261 case REMOVE:
262 case REMOVE_FROM_EXISTING:
263 flowRules.forEach(ops::remove);
264 break;
265 default:
pierventre4e02fb52020-08-13 16:35:15 +0200266 log.warn("Unsupported Objective operation {}", objective.op());
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800267 return;
wu914ed232018-10-23 11:19:53 +0800268 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800269 flowRuleService.apply(ops.build());
Yi Tseng0b809722017-11-03 10:23:26 -0700270 }
271
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800272 private void processGroups(Objective objective, Collection<GroupDescription> groups) {
Yi Tseng0b809722017-11-03 10:23:26 -0700273 if (groups.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800274 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700275 }
pierventre4e02fb52020-08-13 16:35:15 +0200276
277 if (log.isTraceEnabled()) {
278 log.trace("Objective {} -> Groups {}", objective, groups);
279 }
280
Yi Tseng0b809722017-11-03 10:23:26 -0700281 switch (objective.op()) {
282 case ADD:
283 groups.forEach(groupService::addGroup);
284 break;
285 case REMOVE:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800286 groups.forEach(group -> groupService.removeGroup(
287 deviceId, group.appCookie(), objective.appId()));
Yi Tseng0b809722017-11-03 10:23:26 -0700288 break;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800289 case ADD_TO_EXISTING:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800290 groups.forEach(group -> groupService.addBucketsToGroup(
291 deviceId, group.appCookie(), group.buckets(),
292 group.appCookie(), group.appId())
Charles Chan91ea9722018-08-30 15:56:32 -0700293 );
Yi Tseng1b154bd2017-11-20 17:48:19 -0800294 break;
295 case REMOVE_FROM_EXISTING:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800296 groups.forEach(group -> groupService.removeBucketsFromGroup(
297 deviceId, group.appCookie(), group.buckets(),
298 group.appCookie(), group.appId())
Charles Chan91ea9722018-08-30 15:56:32 -0700299 );
Yi Tseng1b154bd2017-11-20 17:48:19 -0800300 break;
pierventre4e02fb52020-08-13 16:35:15 +0200301 case MODIFY:
302 // Modify is only supported for simple next objective
303 // Replace group bucket directly
304 groups.forEach(group -> groupService.setBucketsForGroup(
305 deviceId, group.appCookie(), group.buckets(),
306 group.appCookie(), group.appId())
307 );
308 break;
Yi Tseng0b809722017-11-03 10:23:26 -0700309 default:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800310 log.warn("Unsupported Objective operation {}", objective.op());
Yi Tseng0b809722017-11-03 10:23:26 -0700311 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800312 }
Yi Tseng0b809722017-11-03 10:23:26 -0700313
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800314 private void fail(Objective objective, ObjectiveError error) {
315 CompletableFuture.runAsync(
316 () -> objective.context().ifPresent(
317 ctx -> ctx.onError(objective, error)), callbackExecutor);
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800318
Yi Tseng0b809722017-11-03 10:23:26 -0700319 }
320
Charles Chan91ea9722018-08-30 15:56:32 -0700321
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800322 private void success(Objective objective) {
323 CompletableFuture.runAsync(
324 () -> objective.context().ifPresent(
325 ctx -> ctx.onSuccess(objective)), callbackExecutor);
Yi Tseng0b809722017-11-03 10:23:26 -0700326 }
327
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800328 private void removeNextGroup(NextObjective obj) {
329 final NextGroup removed = flowObjectiveStore.removeNextGroup(obj.id());
330 if (removed == null) {
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200331 log.debug("NextGroup {} was not found in FlowObjectiveStore", obj);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800332 }
Charles Chan91ea9722018-08-30 15:56:32 -0700333 }
334
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800335 private void putNextGroup(NextObjective obj) {
336 final List<String> nextMappings = obj.nextTreatments().stream()
337 .map(this::nextTreatmentToMappingString)
338 .filter(Objects::nonNull)
339 .collect(Collectors.toList());
340 final FabricNextGroup nextGroup = new FabricNextGroup(obj.type(), nextMappings);
341 flowObjectiveStore.putNextGroup(obj.id(), nextGroup);
342 }
343
344 private String nextTreatmentToMappingString(NextTreatment n) {
345 switch (n.type()) {
346 case TREATMENT:
347 final PortNumber p = outputPort(n);
348 return p == null ? "UNKNOWN"
349 : format("OUTPUT:%s", p.toString());
350 case ID:
351 final IdNextTreatment id = (IdNextTreatment) n;
352 return format("NEXT_ID:%d", id.nextId());
Yi Tseng0b809722017-11-03 10:23:26 -0700353 default:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800354 log.warn("Unknown NextTreatment type '{}'", n.type());
355 return "???";
Yi Tseng0b809722017-11-03 10:23:26 -0700356 }
Yi Tseng0b809722017-11-03 10:23:26 -0700357 }
358
Carmelo Cascone2388cc12021-05-26 19:30:30 +0200359 public FlowRule ingressVlanRule(long port, boolean vlanValid, int vlanId) {
360 final TrafficSelector selector = DefaultTrafficSelector.builder()
361 .add(Criteria.matchInPort(PortNumber.portNumber(port)))
362 .add(PiCriterion.builder()
363 .matchExact(FabricConstants.HDR_VLAN_IS_VALID, vlanValid ? ONE : ZERO)
364 .build())
365 .build();
366 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
367 .piTableAction(PiAction.builder()
368 .withId(vlanValid ? FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT
369 : FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
370 .withParameter(new PiActionParam(FabricConstants.VLAN_ID, vlanId))
371 .build())
372 .build();
373 return DefaultFlowRule.builder()
374 .withSelector(selector)
375 .withTreatment(treatment)
376 .forTable(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN)
377 .makePermanent()
378 .withPriority(DEFAULT_FLOW_PRIORITY)
379 .forDevice(deviceId)
380 .fromApp(appId)
381 .build();
382 }
383
384 public FlowRule fwdClassifierRule(int port, Short ethType, short ipEthType, byte fwdType, int priority) {
385 final TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
386 .matchInPort(PortNumber.portNumber(port))
387 .matchPi(PiCriterion.builder()
388 .matchExact(FabricConstants.HDR_IP_ETH_TYPE, ipEthType)
389 .build());
390 if (ethType != null) {
391 selectorBuilder.matchEthType(ethType);
392 }
393 final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
394 .piTableAction(PiAction.builder()
395 .withId(FabricConstants.FABRIC_INGRESS_FILTERING_SET_FORWARDING_TYPE)
396 .withParameter(new PiActionParam(FabricConstants.FWD_TYPE, fwdType))
397 .build())
398 .build();
399 return DefaultFlowRule.builder()
400 .withSelector(selectorBuilder.build())
401 .withTreatment(treatment)
402 .forTable(FabricConstants.FABRIC_INGRESS_FILTERING_FWD_CLASSIFIER)
403 .makePermanent()
404 .withPriority(priority)
405 .forDevice(deviceId)
406 .fromApp(appId)
407 .build();
408 }
409
pierventrea137fba2021-06-14 20:28:35 +0200410 private ObjectiveError handleVerify(NextObjective nextObjective) {
411 Map<GroupBucket, FlowRule> bucketsToFlows = getBucketToFlowMapping(nextObjective);
412 if (bucketsToFlows.isEmpty() && !nextObjective.nextTreatments().isEmpty()) {
413 log.warn("VERIFY failed due to translation error, bucketsToFlows is empty");
414 return ObjectiveError.BADPARAMS;
415 }
416
417 if (log.isTraceEnabled()) {
418 log.trace("Mapping bucketsToFlows {} has been generated ", bucketsToFlows);
419 }
420
421 GroupKey groupKey = nextTranslator.getGroupKey(nextObjective);
422 if (groupKey == null) {
423 log.warn("VERIFY failed due to translation error, unable to determine group key");
424 return ObjectiveError.BADPARAMS;
425 }
426 Group groupFromStore = groupService.getGroup(deviceId, groupKey);
427 if (groupFromStore == null) {
428 log.warn("VERIFY failed due to missing group in the store");
429 return ObjectiveError.GROUPMISSING;
430 }
431
432 // Looking for duplicate buckets - remove them by using a set and comparing size after/before
433 Set<GroupBucket> bucketsFromStore = Sets.newHashSet(groupFromStore.buckets().buckets());
434 if (groupFromStore.buckets().buckets().size() > bucketsFromStore.size()) {
435 log.warn("Duplicated buckets detected in device:{}, nextId:{}, before-size" +
436 ":{} after-size:{} .. correcting", deviceId,
437 nextObjective.id(), groupFromStore.buckets().buckets().size(), bucketsFromStore.size());
438 final GroupBuckets bucketToSet = new GroupBuckets(Lists.newArrayList(bucketsFromStore));
439 groupService.setBucketsForGroup(deviceId, groupKey, bucketToSet, groupKey, nextObjective.appId());
440 // Forge temporary the group to avoid race condition with the store
441 groupFromStore = new DefaultGroup(groupFromStore.id(), deviceId, groupFromStore.type(), bucketToSet);
442 }
443
444 // Looking for buckets missing in the group but defined in the next
445 Map<GroupBucket, FlowRule> toAdd = Maps.newHashMap();
446 for (Map.Entry<GroupBucket, FlowRule> entry : bucketsToFlows.entrySet()) {
447 if (!groupFromStore.buckets().buckets().contains(entry.getKey())) {
448 toAdd.put(entry.getKey(), entry.getValue());
449 }
450 }
451
452 // Looking for buckets missing in the next but defined in the group
453 // FIXME SDFAB-250 we cannot remove associated egress flows
454 List<GroupBucket> toRemove = Lists.newArrayList();
455 groupFromStore.buckets().buckets().forEach(bucket -> {
456 if (!bucketsToFlows.containsKey(bucket)) {
457 toRemove.add(bucket);
458 }
459 });
460
461 if (!toAdd.isEmpty() || !toRemove.isEmpty()) {
462 log.warn("Mismatch detected in device:{}, nextId:{}, groupFromTranslation-size:{} " +
463 "groupFromStore-size:{} toAdd-size:{} toRemove-size: {} .. correcting",
464 deviceId, nextObjective.id(), bucketsToFlows.size(), groupFromStore.buckets().buckets().size(),
465 toAdd.size(), toRemove.size());
466 }
467
468 if (!toAdd.isEmpty()) {
469 if (log.isTraceEnabled()) {
470 log.trace("Adding missing buckets {} and flows {}", toAdd.keySet(), toAdd.values());
471 }
472 final FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
473 final FlowRule dummyFlow = getDummyFlow(nextObjective);
474 toAdd.values().stream()
475 .filter(flowRule -> !flowRule.equals(dummyFlow))
476 .forEach(ops::add);
477 final GroupBuckets bucketsToAdd = new GroupBuckets(Lists.newArrayList(toAdd.keySet()));
478 groupService.addBucketsToGroup(deviceId, groupKey, bucketsToAdd, groupKey, nextObjective.appId());
479 flowRuleService.apply(ops.build());
480 }
481
482 if (!toRemove.isEmpty()) {
483 if (log.isTraceEnabled()) {
484 log.trace("Removing stale buckets {}", toRemove);
485 }
486 final GroupBuckets bucketsToRemove = new GroupBuckets(toRemove);
487 groupService.removeBucketsFromGroup(deviceId, groupKey, bucketsToRemove, groupKey,
488 nextObjective.appId());
489 }
490
491 return null;
492 }
493
494 private Map<GroupBucket, FlowRule> getBucketToFlowMapping(NextObjective nextObjective) {
495 Map<GroupBucket, FlowRule> mapping = Maps.newHashMap();
496 NextObjective newNextObjective;
497 ObjectiveTranslation result;
498 FlowRule dummyFlow = getDummyFlow(nextObjective);
499 FlowRule egFlow;
500 GroupBucket groupBucket;
501 GroupDescription group;
502 for (NextTreatment nextTreatment : nextObjective.nextTreatments()) {
503 newNextObjective = DefaultNextObjective.builder()
504 .withId(nextObjective.id())
505 .withType(nextObjective.type())
506 .fromApp(nextObjective.appId())
507 .withMeta(nextObjective.meta())
508 .addTreatment(nextTreatment)
509 .verify();
510 result = nextTranslator.translate(newNextObjective);
511 if ((result.groups().isEmpty() && result.flowRules().isEmpty()) ||
512 result.groups().size() > 1) {
513 return Collections.emptyMap();
514 }
515 group = result.groups().iterator().next();
516 egFlow = result.flowRules().stream()
517 .filter(flowRule -> flowRule.table().equals(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN))
518 .findFirst()
519 .orElse(null);
520 if (group.buckets().buckets().isEmpty() || group.buckets().buckets().size() > 1) {
521 return Collections.emptyMap();
522 }
523 groupBucket = group.buckets().buckets().iterator().next();
524 if (egFlow == null) {
525 mapping.put(groupBucket, dummyFlow);
526 } else {
527 mapping.put(groupBucket, egFlow);
528 }
529 }
530 return mapping;
531 }
532
533 private FlowRule getDummyFlow(NextObjective nextObjective) {
534 return DefaultFlowRule.builder()
535 .forDevice(deviceId)
536 .forTable(0)
537 .fromApp(nextObjective.appId())
538 .withPriority(1)
539 .withSelector(DefaultTrafficSelector.emptySelector())
540 .makePermanent()
541 .build();
542 }
543
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800544 /**
545 * NextGroup implementation.
546 */
547 private static class FabricNextGroup implements NextGroup {
pierventrea137fba2021-06-14 20:28:35 +0200548 // FIXME SDFAB-250 they are not very useful nor technically correct
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800549 private final NextObjective.Type type;
550 private final List<String> nextMappings;
551
552 FabricNextGroup(NextObjective.Type type, List<String> nextMappings) {
Yi Tseng0b809722017-11-03 10:23:26 -0700553 this.type = type;
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800554 this.nextMappings = ImmutableList.copyOf(nextMappings);
Yi Tseng0b809722017-11-03 10:23:26 -0700555 }
556
Charles Chan91ea9722018-08-30 15:56:32 -0700557 NextObjective.Type type() {
Yi Tseng0b809722017-11-03 10:23:26 -0700558 return type;
559 }
560
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800561 Collection<String> nextMappings() {
562 return nextMappings;
Yi Tseng0b809722017-11-03 10:23:26 -0700563 }
564
565 @Override
566 public byte[] data() {
567 return KRYO.serialize(this);
568 }
569 }
Yi Tseng0b809722017-11-03 10:23:26 -0700570}