blob: a237b8e3e760b36aa589b0decfe44cc6581151af [file] [log] [blame]
Yi Tsengc927a062017-05-02 15:02:37 -07001/*
2 * Copyright 2017-present Open Networking Laboratory
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
17package org.onosproject.net.intent.impl.installer;
18
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Lists;
21import com.google.common.collect.Sets;
22import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.flowobjective.FilteringObjective;
29import org.onosproject.net.flowobjective.FlowObjectiveService;
30import org.onosproject.net.flowobjective.ForwardingObjective;
31import org.onosproject.net.flowobjective.NextObjective;
32import org.onosproject.net.flowobjective.Objective;
33import org.onosproject.net.flowobjective.ObjectiveContext;
34import org.onosproject.net.flowobjective.ObjectiveError;
35import org.onosproject.net.intent.FlowObjectiveIntent;
36import org.onosproject.net.intent.IntentData;
37import org.onosproject.net.intent.IntentExtensionService;
38import org.onosproject.net.intent.IntentInstallCoordinator;
39import org.onosproject.net.intent.IntentOperationContext;
40import org.onosproject.net.intent.IntentInstaller;
41import org.onosproject.net.intent.impl.IntentManager;
Yi Tseng24d9be72017-05-12 11:28:13 -070042import org.onosproject.net.intent.ObjectiveTrackerService;
Yi Tsengc927a062017-05-02 15:02:37 -070043import org.slf4j.Logger;
44
45import java.util.Collection;
46import java.util.List;
47import java.util.Objects;
48import java.util.Optional;
49import java.util.Set;
50import java.util.concurrent.atomic.AtomicInteger;
51
52import static org.onosproject.net.flowobjective.ObjectiveError.INSTALLATIONTHRESHOLDEXCEEDED;
53import static org.onosproject.net.intent.IntentInstaller.Direction.ADD;
54import static org.onosproject.net.intent.IntentInstaller.Direction.REMOVE;
55import static org.slf4j.LoggerFactory.getLogger;
56
57/**
58 * Installer for FlowObjectiveIntent.
59 */
60@Component(immediate = true)
61public class FlowObjectiveIntentInstaller implements IntentInstaller<FlowObjectiveIntent> {
62 private static final int OBJECTIVE_RETRY_THRESHOLD = 5;
63 private static final String UNSUPPORT_OBJ = "unsupported objective {}";
64 private final Logger log = getLogger(IntentManager.class);
65
66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected IntentExtensionService intentExtensionService;
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected ObjectiveTrackerService trackerService;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected IntentInstallCoordinator intentInstallCoordinator;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected FlowObjectiveService flowObjectiveService;
77
78 @Activate
79 public void activate() {
80 intentExtensionService.registerInstaller(FlowObjectiveIntent.class, this);
81 }
82
83 @Deactivate
84 public void deactivated() {
85 intentExtensionService.unregisterInstaller(FlowObjectiveIntent.class);
86 }
87
88 @Override
89 public void apply(IntentOperationContext<FlowObjectiveIntent> intentOperationContext) {
90 Objects.requireNonNull(intentOperationContext);
91 Optional<IntentData> toUninstall = intentOperationContext.toUninstall();
92 Optional<IntentData> toInstall = intentOperationContext.toInstall();
93
94 List<FlowObjectiveIntent> uninstallIntents = intentOperationContext.intentsToUninstall();
95 List<FlowObjectiveIntent> installIntents = intentOperationContext.intentsToInstall();
96
97 if (!toInstall.isPresent() && !toUninstall.isPresent()) {
98 intentInstallCoordinator.intentInstallSuccess(intentOperationContext);
99 return;
100 }
101
102 if (toUninstall.isPresent()) {
103 IntentData intentData = toUninstall.get();
104 trackerService.removeTrackedResources(intentData.key(), intentData.intent().resources());
105 uninstallIntents.forEach(installable ->
106 trackerService.removeTrackedResources(intentData.intent().key(),
107 installable.resources()));
108 }
109
110 if (toInstall.isPresent()) {
111 IntentData intentData = toInstall.get();
112 trackerService.addTrackedResources(intentData.key(), intentData.intent().resources());
113 installIntents.forEach(installable ->
114 trackerService.addTrackedResources(intentData.key(),
115 installable.resources()));
116 }
117
118 FlowObjectiveIntentInstallationContext intentInstallationContext =
119 new FlowObjectiveIntentInstallationContext(intentOperationContext);
120
121 uninstallIntents.stream()
122 .map(intent -> buildObjectiveContexts(intent, REMOVE))
123 .flatMap(Collection::stream)
124 .forEach(context -> {
125 context.intentInstallationContext(intentInstallationContext);
126 intentInstallationContext.addContext(context);
127 intentInstallationContext.addPendingContext(context);
128 });
129
130 installIntents.stream()
131 .map(intent -> buildObjectiveContexts(intent, ADD))
132 .flatMap(Collection::stream)
133 .forEach(context -> {
134 context.intentInstallationContext(intentInstallationContext);
135 intentInstallationContext.addContext(context);
136 intentInstallationContext.addNextPendingContext(context);
137 });
138
139 intentInstallationContext.apply();
140 }
141
142 /**
143 * Builds all objective contexts for a given flow objective Intent with given
144 * operation.
145 *
146 * @param intent the flow objective Intent
147 * @param direction the operation of this Intent
148 * @return all objective context of the Intent with given operation
149 */
150 private Set<FlowObjectiveInstallationContext> buildObjectiveContexts(FlowObjectiveIntent intent,
151 Direction direction) {
152 Objects.requireNonNull(intent);
153 Objects.requireNonNull(direction);
154 Set<FlowObjectiveInstallationContext> contexts = Sets.newHashSet();
155 int size = intent.objectives().size();
156 List<Objective> objectives = intent.objectives();
157 List<DeviceId> deviceIds = intent.devices();
158
159 if (direction == ADD) {
160 // Install objectives
161 // The flow objective system will handle the installation order
162 for (int i = 0; i < size; i++) {
163 Objective objective = objectives.get(i);
164 DeviceId deviceId = deviceIds.get(i);
165 FlowObjectiveInstallationContext ctx = buildObjectiveContext(objective, deviceId, direction);
166 contexts.add(ctx);
167 }
168 return contexts;
169 } else {
170 // Uninstall objecitves
171 // we need to care about ordering here
172 // basic idea is to chain objective contexts
173 for (int i = 0; i < size; i++) {
174 Objective objective = intent.objectives().get(i);
175 DeviceId deviceId = intent.devices().get(i);
176 if (objective instanceof FilteringObjective) {
177 // don't need to care ordering of filtering objective
178 FlowObjectiveInstallationContext ctx =
179 buildObjectiveContext(objective, deviceId, direction);
180 contexts.add(ctx);
181 } else if (objective instanceof NextObjective) {
182 // need to removed after forwarding objective
183 // nothing to do here
184 } else if (objective instanceof ForwardingObjective) {
185 // forwarding objective, also find next objective if
186 // exist
187 FlowObjectiveInstallationContext fwdCtx =
188 buildObjectiveContext(objective, deviceId, direction);
189 ForwardingObjective fwd = (ForwardingObjective) objective;
190 NextObjective nxt = null;
191 Integer nextId = fwd.nextId();
192 if (nextId != null) {
193 for (int j = 0; j < size; j++) {
194 if (objectives.get(j).id() == nextId) {
195 nxt = (NextObjective) objectives.get(j);
196 break;
197 }
198 }
199 // if a next objective exists in the Intent
200 if (nxt != null) {
201 FlowObjectiveInstallationContext nxtCtx =
202 buildObjectiveContext(nxt, deviceId, direction);
203 fwdCtx.nextContext(nxtCtx);
204 }
205 }
206 contexts.add(fwdCtx);
207 } else {
208 // possible here?
209 log.warn(UNSUPPORT_OBJ, objective);
210 }
211 }
212 }
213 return contexts;
214 }
215
216 private FlowObjectiveInstallationContext buildObjectiveContext(Objective objective,
217 DeviceId deviceId,
218 Direction direction) {
219 Objects.requireNonNull(objective);
220 Objects.requireNonNull(deviceId);
221 Objects.requireNonNull(direction);
222 Objective.Builder builder = objective.copy();
223 FlowObjectiveInstallationContext ctx = new FlowObjectiveInstallationContext();
224 switch (direction) {
225 case ADD:
226 objective = builder.add(ctx);
227 break;
228 case REMOVE:
229 objective = builder.remove(ctx);
230 break;
231 default:
232 break;
233 }
234 ctx.setObjective(objective, deviceId);
235 return ctx;
236 }
237
238 /**
239 * Installation context for flow objective.
240 * Manages installation state of a flow objective.
241 */
242 class FlowObjectiveInstallationContext implements ObjectiveContext {
243 private Objective objective;
244 private DeviceId deviceId;
245 private ObjectiveError error;
246 private AtomicInteger retry;
247 private FlowObjectiveInstallationContext nextContext;
248 private FlowObjectiveIntentInstallationContext intentInstallationContext;
249
250 /**
251 * Set flow objective Intent installation context to this context.
252 *
253 * @param intentInstallationContext the Intent installation context
254 */
255 public void intentInstallationContext(FlowObjectiveIntentInstallationContext intentInstallationContext) {
256 Objects.requireNonNull(intentInstallationContext);
257 this.intentInstallationContext = intentInstallationContext;
258
259 // Set Intent installation context to the next context if exists.
260 if (nextContext != null) {
261 nextContext.intentInstallationContext(intentInstallationContext);
262 }
263 }
264
265 /**
266 * Sets next flow objective installation context.
267 *
268 * @param nextContext the next flow objective installation context
269 */
270 public void nextContext(FlowObjectiveInstallationContext nextContext) {
271 Objects.requireNonNull(nextContext);
272 this.nextContext = nextContext;
273 }
274
275 /**
276 * Sets objective and device id to this context; reset error states.
277 *
278 * @param objective the objective
279 * @param deviceId the device id
280 */
281 void setObjective(Objective objective, DeviceId deviceId) {
282 Objects.requireNonNull(objective);
283 Objects.requireNonNull(deviceId);
284 this.objective = objective;
285 this.deviceId = deviceId;
286 this.error = null;
287 this.retry = new AtomicInteger(0);
288 }
289
290 /**
291 * Gets the number of retries.
292 *
293 * @return the retry count
294 */
295 int retryTimes() {
296 return this.retry.get();
297 }
298
299 /**
300 * Increases the number of retries.
301 */
302 void increaseRetryValue() {
303 this.retry.incrementAndGet();
304 }
305
306 /**
307 * Completed this context.
308 *
309 * @param error the error of this context if exist; null otherwise
310 */
311 private void finished(ObjectiveError error) {
312 synchronized (intentInstallationContext) {
313 if (error != null) {
314 this.error = error;
315 intentInstallationContext.handleObjectiveError(this, error);
316 } else {
317 // apply next context if exist
318 if (nextContext != null) {
319 intentInstallationContext.addPendingContext(nextContext);
320 flowObjectiveService.apply(nextContext.deviceId,
321 nextContext.objective);
322 intentInstallationContext.removePendingContext(this);
323 } else {
324 intentInstallationContext.removePendingContext(this);
325 }
326 }
327 if (!intentInstallationContext.pendingContexts().isEmpty()) {
328 return;
329 }
330 // Apply second stage pending contexts if it is not empty
331 if (!intentInstallationContext.nextPendingContexts().isEmpty()) {
332 intentInstallationContext.moveNextPendingToPending();
333 final Set<ObjectiveContext> contextsToApply =
334 Sets.newHashSet(intentInstallationContext.pendingContexts());
335 contextsToApply.forEach(ctx -> {
336 FlowObjectiveInstallationContext foiCtx = (FlowObjectiveInstallationContext) ctx;
337 flowObjectiveService.apply(foiCtx.deviceId,
338 foiCtx.objective);
339 });
340 return;
341 }
342 if (intentInstallationContext.errorContexts().isEmpty()) {
343 intentInstallCoordinator.intentInstallSuccess(intentInstallationContext.intentOperationContext());
344 } else {
345 intentInstallCoordinator.intentInstallFailed(intentInstallationContext.intentOperationContext());
346 }
347 }
348 }
349
350 @Override
351 public void onSuccess(Objective objective) {
352 finished(null);
353 }
354
355 @Override
356 public void onError(Objective objective, ObjectiveError error) {
357 finished(error);
358 }
359
360 @Override
361 public String toString() {
362 return String.format("(%s on %s for %s)", error, deviceId, objective);
363 }
364 }
365
366 /**
367 * Installation context for FlowObjective Intent.
368 * Manages states of pending and error flow objective contexts.
369 */
370 class FlowObjectiveIntentInstallationContext {
371 private final IntentOperationContext<FlowObjectiveIntent> intentOperationContext;
372 final List<ObjectiveContext> contexts = Lists.newArrayList();
373 final Set<ObjectiveContext> errorContexts = Sets.newConcurrentHashSet();
374 final Set<ObjectiveContext> pendingContexts = Sets.newConcurrentHashSet();
375
376 // Second stage of pending contexts
377 final Set<ObjectiveContext> nextPendingContexts = Sets.newConcurrentHashSet();
378
379 /**
380 * Creates a flow objective installation context.
381 *
382 * @param intentOperationContext the flow objective installation context
383 */
384 public FlowObjectiveIntentInstallationContext(
385 IntentOperationContext<FlowObjectiveIntent> intentOperationContext) {
386 Objects.requireNonNull(intentOperationContext);
387 this.intentOperationContext = intentOperationContext;
388 }
389
390 /**
391 * Gets Intent operation context of this context.
392 *
393 * @return the Intent operation context
394 */
395 public IntentOperationContext<FlowObjectiveIntent> intentOperationContext() {
396 return intentOperationContext;
397 }
398
399 /**
400 * Applies all contexts to flow objective service.
401 */
402 public void apply() {
403 if (pendingContexts.isEmpty()) {
404 moveNextPendingToPending();
405 }
406 final Set<ObjectiveContext> contextsToApply = pendingContexts();
407 contextsToApply.forEach(ctx -> {
408 FlowObjectiveInstallationContext foiCtx =
409 (FlowObjectiveInstallationContext) ctx;
410 flowObjectiveService.apply(foiCtx.deviceId, foiCtx.objective);
411 });
412 }
413
414 /**
415 * Gets all error contexts.
416 *
417 * @return the error contexts
418 */
419 public Set<ObjectiveContext> errorContexts() {
420 return ImmutableSet.copyOf(errorContexts);
421 }
422
423 /**
424 * Gets all pending contexts.
425 *
426 * @return the pending contexts
427 */
428 public Set<ObjectiveContext> pendingContexts() {
429 return ImmutableSet.copyOf(pendingContexts);
430 }
431
432 /**
433 * Gets all pending contexts of next stage.
434 *
435 * @return the pending contexts for next stage
436 */
437 public Set<ObjectiveContext> nextPendingContexts() {
438 return ImmutableSet.copyOf(nextPendingContexts);
439 }
440
441 /**
442 * Adds a context.
443 *
444 * @param context the context
445 */
446 public void addContext(ObjectiveContext context) {
447 Objects.requireNonNull(context);
448 contexts.add(context);
449 }
450
451 /**
452 * Adds a context to pending context of next stage.
453 *
454 * @param context the context
455 */
456 public void addNextPendingContext(ObjectiveContext context) {
457 Objects.requireNonNull(context);
458 nextPendingContexts.add(context);
459 }
460
461 /**
462 * Adds a context to pending context.
463 *
464 * @param context the context
465 */
466 public void addPendingContext(ObjectiveContext context) {
467 Objects.requireNonNull(context);
468 pendingContexts.add(context);
469 }
470
471 /**
472 * Removes the pending context.
473 *
474 * @param context the context
475 */
476 public void removePendingContext(ObjectiveContext context) {
477 Objects.requireNonNull(context);
478 pendingContexts.remove(context);
479 }
480
481 /**
482 * Moves pending context from next stage to current stage.
483 */
484 public void moveNextPendingToPending() {
485 pendingContexts.addAll(nextPendingContexts);
486 nextPendingContexts.clear();
487 }
488
489 /**
490 * Handles error of objective context.
491 *
492 * @param ctx the objective context
493 * @param error the error
494 */
495 public void handleObjectiveError(FlowObjectiveInstallationContext ctx,
496 ObjectiveError error) {
497 Objects.requireNonNull(ctx);
498 Objects.requireNonNull(error);
499 log.debug("Got error(s) when install objective: {}, error: {}, retry: {}",
500 ctx.objective, ctx.error, ctx.retry);
501 if (ctx.retryTimes() > OBJECTIVE_RETRY_THRESHOLD) {
502 ctx.error = INSTALLATIONTHRESHOLDEXCEEDED;
503 pendingContexts.remove(ctx);
504 errorContexts.add(ctx);
505 return;
506 }
507 // reset error
508 ctx.error = null;
509 // strategies for errors
510 switch (error) {
511 case GROUPEXISTS:
512 if (ctx.objective.op() == Objective.Operation.ADD &&
513 ctx.objective instanceof NextObjective) {
514 // Next group exists
515 // build new objective with new op ADD_TO_EXIST
516 NextObjective newObj =
517 ((NextObjective.Builder) ctx.objective.copy()).addToExisting(ctx);
518 ctx.setObjective(newObj, ctx.deviceId);
519 ctx.increaseRetryValue();
520 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
521 } else {
522 pendingContexts.remove(ctx);
523 errorContexts.add(ctx);
524 }
525 break;
526 case GROUPINSTALLATIONFAILED:
527 // Group install failed, retry again
528 ctx.increaseRetryValue();
529 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
530 break;
531 case GROUPMISSING:
532 if (ctx.objective.op() == Objective.Operation.ADD_TO_EXISTING) {
533 // Next group not exist, but we want to add new buckets
534 // build new objective with new op ADD
535 NextObjective newObj = (NextObjective) ctx.objective.copy().add(ctx);
536 ctx.setObjective(newObj, ctx.deviceId);
537 ctx.increaseRetryValue();
538 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
539 } else if (ctx.objective.op() == Objective.Operation.REMOVE ||
540 ctx.objective.op() == Objective.Operation.REMOVE_FROM_EXISTING) {
541 // Already removed, no need to do anything
542 ctx.error = null;
543 pendingContexts.remove(ctx);
544 return;
545 } else {
546 // Next chaining group missing, try again.
547 ctx.increaseRetryValue();
548 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
549 }
550 break;
551 case FLOWINSTALLATIONFAILED:
552 case GROUPREMOVALFAILED:
553 case INSTALLATIONTIMEOUT:
554 // Retry
555 ctx.increaseRetryValue();
556 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
557 break;
558 default:
559 pendingContexts.remove(ctx);
560 errorContexts.add(ctx);
561 break;
562 }
563 }
564 }
565}