blob: 79045f104a6d062132f03e828bcfaf69d1807f05 [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;
Yi Tseng0b809722017-11-03 10:23:26 -070020import org.onlab.util.KryoNamespace;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080021import org.onlab.util.SharedExecutors;
Yi Tseng0b809722017-11-03 10:23:26 -070022import org.onosproject.net.DeviceId;
23import org.onosproject.net.PortNumber;
24import org.onosproject.net.behaviour.NextGroup;
25import org.onosproject.net.behaviour.Pipeliner;
26import org.onosproject.net.behaviour.PipelinerContext;
Yi Tseng0b809722017-11-03 10:23:26 -070027import org.onosproject.net.flow.FlowRule;
28import org.onosproject.net.flow.FlowRuleOperations;
Yi Tseng0b809722017-11-03 10:23:26 -070029import org.onosproject.net.flow.FlowRuleService;
Yi Tseng0b809722017-11-03 10:23:26 -070030import org.onosproject.net.flowobjective.FilteringObjective;
31import org.onosproject.net.flowobjective.FlowObjectiveStore;
32import org.onosproject.net.flowobjective.ForwardingObjective;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080033import org.onosproject.net.flowobjective.IdNextTreatment;
Yi Tseng0b809722017-11-03 10:23:26 -070034import org.onosproject.net.flowobjective.NextObjective;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080035import org.onosproject.net.flowobjective.NextTreatment;
Yi Tseng0b809722017-11-03 10:23:26 -070036import org.onosproject.net.flowobjective.Objective;
37import org.onosproject.net.flowobjective.ObjectiveError;
38import org.onosproject.net.group.GroupDescription;
Yi Tseng0b809722017-11-03 10:23:26 -070039import org.onosproject.net.group.GroupService;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070040import org.onosproject.pipelines.fabric.impl.behaviour.AbstractFabricHandlerBehavior;
41import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities;
Yi Tseng0b809722017-11-03 10:23:26 -070042import org.onosproject.store.serializers.KryoNamespaces;
43import org.slf4j.Logger;
44
45import java.util.Collection;
46import java.util.List;
Carmelo Casconeb5324e72018-11-25 02:26:32 -080047import java.util.Objects;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080048import java.util.concurrent.CompletableFuture;
Yi Tsengfe13f3e2018-08-19 03:09:54 +080049import java.util.concurrent.ExecutorService;
Yi Tseng1b154bd2017-11-20 17:48:19 -080050import java.util.stream.Collectors;
Yi Tseng0b809722017-11-03 10:23:26 -070051
Carmelo Casconeb5324e72018-11-25 02:26:32 -080052import static java.lang.String.format;
pierventre4e02fb52020-08-13 16:35:15 +020053import static org.onosproject.net.flowobjective.NextObjective.Type.SIMPLE;
Carmelo Cascone356ab8b2019-09-25 01:02:53 -070054import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.outputPort;
Yi Tseng0b809722017-11-03 10:23:26 -070055import static org.slf4j.LoggerFactory.getLogger;
56
57/**
Carmelo Casconeb5324e72018-11-25 02:26:32 -080058 * Pipeliner implementation for fabric pipeline which uses ObjectiveTranslator
59 * implementations to translate flow objectives for the different blocks,
60 * filtering, forwarding and next.
Yi Tseng0b809722017-11-03 10:23:26 -070061 */
Carmelo Casconeb5324e72018-11-25 02:26:32 -080062public class FabricPipeliner extends AbstractFabricHandlerBehavior
63 implements Pipeliner {
64
Yi Tseng0b809722017-11-03 10:23:26 -070065 private static final Logger log = getLogger(FabricPipeliner.class);
66
67 protected static final KryoNamespace KRYO = new KryoNamespace.Builder()
68 .register(KryoNamespaces.API)
69 .register(FabricNextGroup.class)
70 .build("FabricPipeliner");
71
Yi Tseng0b809722017-11-03 10:23:26 -070072 protected DeviceId deviceId;
73 protected FlowRuleService flowRuleService;
74 protected GroupService groupService;
75 protected FlowObjectiveStore flowObjectiveStore;
Yi Tseng0b809722017-11-03 10:23:26 -070076
Carmelo Casconeb5324e72018-11-25 02:26:32 -080077 private FilteringObjectiveTranslator filteringTranslator;
78 private ForwardingObjectiveTranslator forwardingTranslator;
79 private NextObjectiveTranslator nextTranslator;
Charles Chan91ea9722018-08-30 15:56:32 -070080
Carmelo Casconeb5324e72018-11-25 02:26:32 -080081 private final ExecutorService callbackExecutor = SharedExecutors.getPoolThreadExecutor();
Yi Tseng0b809722017-11-03 10:23:26 -070082
Daniele Morof51d0c12019-07-30 10:43:10 -070083 /**
84 * Creates a new instance of this behavior with the given capabilities.
85 *
86 * @param capabilities capabilities
87 */
88 public FabricPipeliner(FabricCapabilities capabilities) {
89 super(capabilities);
90 }
91
92 /**
93 * Create a new instance of this behaviour. Used by the abstract projectable
94 * model (i.e., {@link org.onosproject.net.Device#as(Class)}.
95 */
96 public FabricPipeliner() {
97 super();
98 }
99
Yi Tseng0b809722017-11-03 10:23:26 -0700100 @Override
101 public void init(DeviceId deviceId, PipelinerContext context) {
102 this.deviceId = deviceId;
103 this.flowRuleService = context.directory().get(FlowRuleService.class);
104 this.groupService = context.directory().get(GroupService.class);
105 this.flowObjectiveStore = context.directory().get(FlowObjectiveStore.class);
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800106 this.filteringTranslator = new FilteringObjectiveTranslator(deviceId, capabilities);
107 this.forwardingTranslator = new ForwardingObjectiveTranslator(deviceId, capabilities);
108 this.nextTranslator = new NextObjectiveTranslator(deviceId, capabilities);
Yi Tseng0b809722017-11-03 10:23:26 -0700109 }
110
111 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800112 public void filter(FilteringObjective obj) {
113 final ObjectiveTranslation result = filteringTranslator.translate(obj);
114 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700115 }
116
117 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800118 public void forward(ForwardingObjective obj) {
119 final ObjectiveTranslation result = forwardingTranslator.translate(obj);
120 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700121 }
122
123 @Override
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800124 public void next(NextObjective obj) {
125 if (obj.op() == Objective.Operation.VERIFY) {
Yi Tseng1b154bd2017-11-20 17:48:19 -0800126 // TODO: support VERIFY operation
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800127 log.debug("VERIFY operation not yet supported for NextObjective, will return success");
128 success(obj);
Yi Tseng1b154bd2017-11-20 17:48:19 -0800129 return;
130 }
131
pierventre4e02fb52020-08-13 16:35:15 +0200132 if (obj.op() == Objective.Operation.MODIFY && obj.type() != SIMPLE) {
133 log.warn("MODIFY operation not yet supported for NextObjective {}, will return failure :(",
134 obj.type());
135 if (log.isTraceEnabled()) {
136 log.trace("Objective {}", obj);
137 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800138 fail(obj, ObjectiveError.UNSUPPORTED);
Charles Chan91ea9722018-08-30 15:56:32 -0700139 return;
140 }
141
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800142 final ObjectiveTranslation result = nextTranslator.translate(obj);
143 handleResult(obj, result);
Yi Tseng0b809722017-11-03 10:23:26 -0700144 }
145
146 @Override
147 public List<String> getNextMappings(NextGroup nextGroup) {
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800148 final FabricNextGroup fabricNextGroup = KRYO.deserialize(nextGroup.data());
149 return fabricNextGroup.nextMappings().stream()
150 .map(m -> format("%s -> %s", fabricNextGroup.type(), m))
Yi Tseng1b154bd2017-11-20 17:48:19 -0800151 .collect(Collectors.toList());
Yi Tseng0b809722017-11-03 10:23:26 -0700152 }
153
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800154 private void handleResult(Objective obj, ObjectiveTranslation result) {
155 if (result.error().isPresent()) {
156 fail(obj, result.error().get());
157 return;
158 }
159 processGroups(obj, result.groups());
160 processFlows(obj, result.flowRules());
161 if (obj instanceof NextObjective) {
162 handleNextGroup((NextObjective) obj);
163 }
164 success(obj);
Yi Tseng0b809722017-11-03 10:23:26 -0700165 }
166
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800167 private void handleNextGroup(NextObjective obj) {
168 switch (obj.op()) {
169 case REMOVE:
170 removeNextGroup(obj);
171 break;
172 case ADD:
173 case ADD_TO_EXISTING:
174 case REMOVE_FROM_EXISTING:
175 case MODIFY:
176 putNextGroup(obj);
177 break;
178 case VERIFY:
179 break;
180 default:
181 log.error("Unknown NextObjective operation '{}'", obj.op());
182 }
183 }
184
185 private void processFlows(Objective objective, Collection<FlowRule> flowRules) {
Yi Tseng0b809722017-11-03 10:23:26 -0700186 if (flowRules.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800187 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700188 }
pierventre4e02fb52020-08-13 16:35:15 +0200189
190 if (log.isTraceEnabled()) {
191 log.trace("Objective {} -> Flows {}", objective, flowRules);
192 }
193
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800194 final FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
195 switch (objective.op()) {
196 case ADD:
197 case ADD_TO_EXISTING:
pierventre4e02fb52020-08-13 16:35:15 +0200198 case MODIFY:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800199 flowRules.forEach(ops::add);
200 break;
201 case REMOVE:
202 case REMOVE_FROM_EXISTING:
203 flowRules.forEach(ops::remove);
204 break;
205 default:
pierventre4e02fb52020-08-13 16:35:15 +0200206 log.warn("Unsupported Objective operation {}", objective.op());
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800207 return;
wu914ed232018-10-23 11:19:53 +0800208 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800209 flowRuleService.apply(ops.build());
Yi Tseng0b809722017-11-03 10:23:26 -0700210 }
211
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800212 private void processGroups(Objective objective, Collection<GroupDescription> groups) {
Yi Tseng0b809722017-11-03 10:23:26 -0700213 if (groups.isEmpty()) {
Yi Tsengf78e1742018-04-08 19:57:17 +0800214 return;
Yi Tseng0b809722017-11-03 10:23:26 -0700215 }
pierventre4e02fb52020-08-13 16:35:15 +0200216
217 if (log.isTraceEnabled()) {
218 log.trace("Objective {} -> Groups {}", objective, groups);
219 }
220
Yi Tseng0b809722017-11-03 10:23:26 -0700221 switch (objective.op()) {
222 case ADD:
223 groups.forEach(groupService::addGroup);
224 break;
225 case REMOVE:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800226 groups.forEach(group -> groupService.removeGroup(
227 deviceId, group.appCookie(), objective.appId()));
Yi Tseng0b809722017-11-03 10:23:26 -0700228 break;
Yi Tseng1b154bd2017-11-20 17:48:19 -0800229 case ADD_TO_EXISTING:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800230 groups.forEach(group -> groupService.addBucketsToGroup(
231 deviceId, group.appCookie(), group.buckets(),
232 group.appCookie(), group.appId())
Charles Chan91ea9722018-08-30 15:56:32 -0700233 );
Yi Tseng1b154bd2017-11-20 17:48:19 -0800234 break;
235 case REMOVE_FROM_EXISTING:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800236 groups.forEach(group -> groupService.removeBucketsFromGroup(
237 deviceId, group.appCookie(), group.buckets(),
238 group.appCookie(), group.appId())
Charles Chan91ea9722018-08-30 15:56:32 -0700239 );
Yi Tseng1b154bd2017-11-20 17:48:19 -0800240 break;
pierventre4e02fb52020-08-13 16:35:15 +0200241 case MODIFY:
242 // Modify is only supported for simple next objective
243 // Replace group bucket directly
244 groups.forEach(group -> groupService.setBucketsForGroup(
245 deviceId, group.appCookie(), group.buckets(),
246 group.appCookie(), group.appId())
247 );
248 break;
Yi Tseng0b809722017-11-03 10:23:26 -0700249 default:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800250 log.warn("Unsupported Objective operation {}", objective.op());
Yi Tseng0b809722017-11-03 10:23:26 -0700251 }
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800252 }
Yi Tseng0b809722017-11-03 10:23:26 -0700253
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800254 private void fail(Objective objective, ObjectiveError error) {
255 CompletableFuture.runAsync(
256 () -> objective.context().ifPresent(
257 ctx -> ctx.onError(objective, error)), callbackExecutor);
Yi Tsengfe13f3e2018-08-19 03:09:54 +0800258
Yi Tseng0b809722017-11-03 10:23:26 -0700259 }
260
Charles Chan91ea9722018-08-30 15:56:32 -0700261
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800262 private void success(Objective objective) {
263 CompletableFuture.runAsync(
264 () -> objective.context().ifPresent(
265 ctx -> ctx.onSuccess(objective)), callbackExecutor);
Yi Tseng0b809722017-11-03 10:23:26 -0700266 }
267
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800268 private void removeNextGroup(NextObjective obj) {
269 final NextGroup removed = flowObjectiveStore.removeNextGroup(obj.id());
270 if (removed == null) {
271 log.debug("NextGroup {} was not found in FlowObjectiveStore");
272 }
Charles Chan91ea9722018-08-30 15:56:32 -0700273 }
274
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800275 private void putNextGroup(NextObjective obj) {
276 final List<String> nextMappings = obj.nextTreatments().stream()
277 .map(this::nextTreatmentToMappingString)
278 .filter(Objects::nonNull)
279 .collect(Collectors.toList());
280 final FabricNextGroup nextGroup = new FabricNextGroup(obj.type(), nextMappings);
281 flowObjectiveStore.putNextGroup(obj.id(), nextGroup);
282 }
283
284 private String nextTreatmentToMappingString(NextTreatment n) {
285 switch (n.type()) {
286 case TREATMENT:
287 final PortNumber p = outputPort(n);
288 return p == null ? "UNKNOWN"
289 : format("OUTPUT:%s", p.toString());
290 case ID:
291 final IdNextTreatment id = (IdNextTreatment) n;
292 return format("NEXT_ID:%d", id.nextId());
Yi Tseng0b809722017-11-03 10:23:26 -0700293 default:
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800294 log.warn("Unknown NextTreatment type '{}'", n.type());
295 return "???";
Yi Tseng0b809722017-11-03 10:23:26 -0700296 }
Yi Tseng0b809722017-11-03 10:23:26 -0700297 }
298
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800299 /**
300 * NextGroup implementation.
301 */
302 private static class FabricNextGroup implements NextGroup {
Yi Tseng0b809722017-11-03 10:23:26 -0700303
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800304 private final NextObjective.Type type;
305 private final List<String> nextMappings;
306
307 FabricNextGroup(NextObjective.Type type, List<String> nextMappings) {
Yi Tseng0b809722017-11-03 10:23:26 -0700308 this.type = type;
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800309 this.nextMappings = ImmutableList.copyOf(nextMappings);
Yi Tseng0b809722017-11-03 10:23:26 -0700310 }
311
Charles Chan91ea9722018-08-30 15:56:32 -0700312 NextObjective.Type type() {
Yi Tseng0b809722017-11-03 10:23:26 -0700313 return type;
314 }
315
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800316 Collection<String> nextMappings() {
317 return nextMappings;
Yi Tseng0b809722017-11-03 10:23:26 -0700318 }
319
320 @Override
321 public byte[] data() {
322 return KRYO.serialize(this);
323 }
324 }
Yi Tseng0b809722017-11-03 10:23:26 -0700325}