blob: 378b9b8ffff590080073ac490b22034615f0660d [file] [log] [blame]
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -08003 *
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;
18
Andreas Papazois05548962016-11-23 11:36:55 +020019import com.google.common.collect.ImmutableSet;
20import com.google.common.annotations.Beta;
Ray Milkeyfd724402016-05-26 14:45:46 -070021import com.google.common.collect.Lists;
Thomas Vachuska2980c972016-02-23 20:58:49 -080022import com.google.common.collect.Sets;
Andreas Papazois05548962016-11-23 11:36:55 +020023
24import org.apache.commons.lang3.tuple.Pair;
Thomas Vachuska2980c972016-02-23 20:58:49 -080025import org.onosproject.net.DeviceId;
Andreas Papazois05548962016-11-23 11:36:55 +020026import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription;
27import org.onosproject.net.behaviour.protection.ProtectionConfig;
28import org.onosproject.net.config.NetworkConfigService;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080029import org.onosproject.net.flow.FlowRule;
30import org.onosproject.net.flow.FlowRuleOperations;
31import org.onosproject.net.flow.FlowRuleOperationsContext;
32import org.onosproject.net.flow.FlowRuleService;
33import org.onosproject.net.flowobjective.FlowObjectiveService;
Thomas Vachuska2980c972016-02-23 20:58:49 -080034import org.onosproject.net.flowobjective.Objective;
35import org.onosproject.net.flowobjective.ObjectiveContext;
36import org.onosproject.net.flowobjective.ObjectiveError;
37import org.onosproject.net.intent.FlowObjectiveIntent;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080038import org.onosproject.net.intent.FlowRuleIntent;
39import org.onosproject.net.intent.Intent;
40import org.onosproject.net.intent.IntentData;
41import org.onosproject.net.intent.IntentStore;
Andreas Papazois05548962016-11-23 11:36:55 +020042import org.onosproject.net.intent.ProtectionEndpointIntent;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080043import org.slf4j.Logger;
44
Thomas Vachuska2980c972016-02-23 20:58:49 -080045import java.util.ArrayList;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080046import java.util.Collection;
Brian O'Connora78f0602016-09-22 10:56:08 -070047import java.util.Collections;
helenyrwue6aaa332016-08-05 15:41:42 -070048import java.util.Iterator;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080049import java.util.List;
50import java.util.Optional;
51import java.util.Set;
Andreas Papazois05548962016-11-23 11:36:55 +020052import java.util.concurrent.CompletableFuture;
53import java.util.concurrent.CopyOnWriteArrayList;
Thomas Vachuska2980c972016-02-23 20:58:49 -080054import java.util.function.Consumer;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080055import java.util.stream.Collectors;
56
Andreas Papazois05548962016-11-23 11:36:55 +020057import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuska2980c972016-02-23 20:58:49 -080058import static com.google.common.base.Preconditions.checkState;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080059import static org.onosproject.net.intent.IntentState.*;
60import static org.slf4j.LoggerFactory.getLogger;
61
62/**
63 * Auxiliary entity responsible for installing the intents into the environment.
64 */
65class IntentInstaller {
66
Brian O'Connorc590ebb2016-12-08 18:16:41 -080067 private static final Logger log = getLogger(IntentInstaller.class);
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080068
69 private IntentStore store;
70 private ObjectiveTrackerService trackerService;
71 private FlowRuleService flowRuleService;
72 private FlowObjectiveService flowObjectiveService;
Andreas Papazois05548962016-11-23 11:36:55 +020073 private NetworkConfigService networkConfigService;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080074
75 private enum Direction {
76 ADD,
77 REMOVE
78 }
79
80 /**
81 * Initializes the installer with references to required services.
82 *
83 * @param intentStore intent store
84 * @param trackerService objective tracking service
85 * @param flowRuleService flow rule service
86 * @param flowObjectiveService flow objective service
Andreas Papazois05548962016-11-23 11:36:55 +020087 * @param networkConfigService network configuration service
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080088 */
89 void init(IntentStore intentStore, ObjectiveTrackerService trackerService,
Andreas Papazois05548962016-11-23 11:36:55 +020090 FlowRuleService flowRuleService, FlowObjectiveService flowObjectiveService,
91 NetworkConfigService networkConfigService) {
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080092 this.store = intentStore;
93 this.trackerService = trackerService;
Andreas Papazois05548962016-11-23 11:36:55 +020094 //TODO Various services should be plugged to the intent installer instead of being hardcoded
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080095 this.flowRuleService = flowRuleService;
96 this.flowObjectiveService = flowObjectiveService;
Andreas Papazois05548962016-11-23 11:36:55 +020097 this.networkConfigService = networkConfigService;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080098 }
99
Thomas Vachuska2980c972016-02-23 20:58:49 -0800100 // FIXME: Intent Manager should have never become dependent on a specific intent type(s).
101 // This will be addressed in intent domains work; not now.
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800102
103 /**
104 * Applies the specified intent updates to the environment by uninstalling
105 * and installing the intents and updating the store references appropriately.
106 *
107 * @param toUninstall optional intent to uninstall
108 * @param toInstall optional intent to install
109 */
110 void apply(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
Andreas Papazois05548962016-11-23 11:36:55 +0200111 // Hook for handling success at intent installation level.
112 Consumer<IntentInstallationContext> successConsumer = (ctx) -> {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800113 if (toInstall.isPresent()) {
114 IntentData installData = toInstall.get();
115 log.debug("Completed installing: {}", installData.key());
116 installData.setState(INSTALLED);
117 store.write(installData);
118 } else if (toUninstall.isPresent()) {
119 IntentData uninstallData = toUninstall.get();
120 log.debug("Completed withdrawing: {}", uninstallData.key());
121 switch (uninstallData.request()) {
122 case INSTALL_REQ:
123 uninstallData.setState(FAILED);
124 break;
125 case WITHDRAW_REQ:
126 default: //TODO "default" case should not happen
127 uninstallData.setState(WITHDRAWN);
128 break;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800129 }
Brian O'Connora78f0602016-09-22 10:56:08 -0700130 // Intent has been withdrawn; we can clear the installables
131 store.write(new IntentData(uninstallData, Collections.emptyList()));
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800132 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800133 };
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800134
Andreas Papazois05548962016-11-23 11:36:55 +0200135 // Hook for handling errors at intent installation level
136 Consumer<IntentInstallationContext> errorConsumer = (ctx) -> {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800137 // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
138 if (toInstall.isPresent()) {
139 IntentData installData = toInstall.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800140 installData.setState(CORRUPT);
141 installData.incrementErrorCount();
142 store.write(installData);
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800143 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800144 // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
145 if (toUninstall.isPresent()) {
146 IntentData uninstallData = toUninstall.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800147 uninstallData.setState(CORRUPT);
148 uninstallData.incrementErrorCount();
149 store.write(uninstallData);
150 }
151 };
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800152
Andreas Papazois05548962016-11-23 11:36:55 +0200153 // Hooks at operation level
154 Consumer<OperationContext> successOperationConsumer = (ctx) -> {
155 ctx.intentContext.finishContext(ctx);
156 };
157 Consumer<OperationContext> errorOperationConsumer = (ctx) -> {
158 if (ctx.toInstall.isPresent()) {
159 IntentData installData = toInstall.get();
160 log.warn("Failed installation operation for: {} {} due to {}",
161 installData.key(), installData.intent(), ctx.error());
162 }
163 if (ctx.toUninstall.isPresent()) {
164 IntentData uninstallData = toUninstall.get();
165 log.warn("Failed withdrawal operation for: {} {} due to {}",
166 uninstallData.key(), uninstallData.intent(), ctx.error());
167 }
168 ctx.intentContext.handleError(ctx);
169 };
170
Thomas Vachuska2980c972016-02-23 20:58:49 -0800171 // Create a context for tracking the backing operations for applying
172 // the intents to the environment.
Andreas Papazois05548962016-11-23 11:36:55 +0200173 IntentInstallationContext intentContext =
174 new IntentInstallationContext(successConsumer, errorConsumer);
175 Set<OperationContext> contexts = createContext(intentContext, toUninstall, toInstall);
176 intentContext.pendingContexts = contexts;
177 contexts.forEach(ctx -> {
178 ctx.prepare(toUninstall, toInstall, successOperationConsumer, errorOperationConsumer);
179 ctx.apply();
180 });
Thomas Vachuska2980c972016-02-23 20:58:49 -0800181 }
182
Andreas Papazois05548962016-11-23 11:36:55 +0200183 // Context for applying and tracking multiple kinds of operation contexts
184 // related to specific intent data.
185 private final class IntentInstallationContext {
186 private Set<OperationContext> pendingContexts = Sets.newHashSet();
187 private Set<OperationContext> errorContexts = Sets.newHashSet();
188 private Consumer<IntentInstallationContext> successConsumer;
189 private Consumer<IntentInstallationContext> errorConsumer;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800190
Andreas Papazois05548962016-11-23 11:36:55 +0200191 private IntentInstallationContext(Consumer<IntentInstallationContext> succesConsumer,
192 Consumer<IntentInstallationContext> errorConsumer) {
193 this.successConsumer = succesConsumer;
194 this.errorConsumer = errorConsumer;
195 }
196
197 private void handleError(OperationContext ctx) {
198 errorContexts.add(ctx);
199 finishContext(ctx);
200 }
201
202 private void finishContext(OperationContext ctx) {
203 synchronized (pendingContexts) {
204 pendingContexts.remove(ctx);
205 if (pendingContexts.isEmpty()) {
206 if (errorContexts.isEmpty()) {
207 successConsumer.accept(IntentInstallationContext.this);
208 } else {
209 errorConsumer.accept(IntentInstallationContext.this);
210 }
211 }
212 }
213 }
214 }
215
216 // --- Utilities to support various installable Intent ----
217
218 // Creates the set of contexts appropriate for tracking operations of the
Thomas Vachuska2980c972016-02-23 20:58:49 -0800219 // the specified intents.
Andreas Papazois05548962016-11-23 11:36:55 +0200220 private Set<OperationContext> createContext(IntentInstallationContext intentContext,
221 Optional<IntentData> toUninstall,
222 Optional<IntentData> toInstall) {
223
224 Set<OperationContext> contexts = Sets.newConcurrentHashSet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800225 if (isInstallable(toUninstall, toInstall, FlowRuleIntent.class)) {
Andreas Papazois05548962016-11-23 11:36:55 +0200226 contexts.add(new FlowRuleOperationContext(intentContext));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800227 }
228 if (isInstallable(toUninstall, toInstall, FlowObjectiveIntent.class)) {
Andreas Papazois05548962016-11-23 11:36:55 +0200229 contexts.add(new FlowObjectiveOperationContext(intentContext));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800230 }
Andreas Papazois05548962016-11-23 11:36:55 +0200231 if (isInstallable(toUninstall, toInstall, ProtectionEndpointIntent.class)) {
232 contexts.add(new ProtectionConfigOperationContext(intentContext));
233 }
234
235 return contexts.isEmpty() ? ImmutableSet.of(new ErrorContext(intentContext)) : contexts;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800236 }
237
238 private boolean isInstallable(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
239 Class<? extends Intent> intentClass) {
240 boolean notBothNull = false;
241 if (toInstall.isPresent()) {
242 notBothNull = true;
243 if (!toInstall.get().installables().stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200244 .anyMatch(i -> intentClass.isAssignableFrom(i.getClass()))) {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800245 return false;
246 }
247 }
248 if (toUninstall.isPresent()) {
249 notBothNull = true;
250 if (!toUninstall.get().installables().stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200251 .anyMatch(i -> intentClass.isAssignableFrom(i.getClass()))) {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800252 return false;
253 }
254 }
255 return notBothNull;
256 }
257
258 // Base context for applying and tracking operations related to installable intents.
259 private abstract class OperationContext {
Andreas Papazois05548962016-11-23 11:36:55 +0200260 protected IntentInstallationContext intentContext;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800261 protected Optional<IntentData> toUninstall;
262 protected Optional<IntentData> toInstall;
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700263 /**
264 * Implementation of {@link OperationContext} should call this on success.
265 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800266 protected Consumer<OperationContext> successConsumer;
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700267 /**
268 * Implementation of {@link OperationContext} should call this on error.
269 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800270 protected Consumer<OperationContext> errorConsumer;
271
Andreas Papazois05548962016-11-23 11:36:55 +0200272 protected OperationContext(IntentInstallationContext context) {
273 this.intentContext = context;
274 }
275
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700276 /**
277 * Applies the Intents specified by
278 * {@link #prepareIntents(List, Direction)} call(s) prior to this call.
279 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800280 abstract void apply();
281
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700282 /**
283 * Returns error state of the context.
284 * <p>
285 * Used for error logging purpose.
286 * Returned Object should have reasonable toString() implementation.
287 * @return context state, describing current error state
288 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800289 abstract Object error();
290
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700291 /**
292 * Prepares Intent(s) to {@link #apply() apply} in this operation.
293 * <p>
294 * Intents specified by {@code intentsToApply} in a single call
295 * can be applied to the Devices in arbitrary order.
296 * But group of Intents specified in consecutive {@link #prepareIntents(List, Direction)}
297 * calls must be applied in order. (e.g., guarded by barrier)
298 *
299 * @param intentsToApply {@link Intent}s to apply
300 * @param direction of operation
301 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800302 abstract void prepareIntents(List<Intent> intentsToApply, Direction direction);
303
304 void prepare(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
305 Consumer<OperationContext> successConsumer,
306 Consumer<OperationContext> errorConsumer) {
307 this.toUninstall = toUninstall;
308 this.toInstall = toInstall;
309 this.successConsumer = successConsumer;
310 this.errorConsumer = errorConsumer;
helenyrwue6aaa332016-08-05 15:41:42 -0700311 prepareIntentData(toUninstall, toInstall);
312 }
313
314 private void prepareIntentData(Optional<IntentData> uninstallData,
315 Optional<IntentData> installData) {
316 if (!installData.isPresent() && !uninstallData.isPresent()) {
317 return;
318 } else if (!installData.isPresent()) {
319 prepareIntentData(uninstallData, Direction.REMOVE);
320 } else if (!uninstallData.isPresent()) {
321 prepareIntentData(installData, Direction.ADD);
322 } else {
323 IntentData uninstall = uninstallData.get();
324 IntentData install = installData.get();
Brian O'Connor09d90f02016-09-13 11:06:14 -0700325 List<Intent> uninstallIntents = Lists.newArrayList(uninstall.installables());
326 List<Intent> installIntents = Lists.newArrayList(install.installables());
helenyrwue6aaa332016-08-05 15:41:42 -0700327
328 checkState(uninstallIntents.stream().allMatch(this::isSupported),
329 "Unsupported installable intents detected");
330 checkState(installIntents.stream().allMatch(this::isSupported),
331 "Unsupported installable intents detected");
332
333 //TODO: Filter FlowObjective intents
334 // Filter out same intents and intents with same flow rules
335 Iterator<Intent> iterator = installIntents.iterator();
336 while (iterator.hasNext()) {
337 Intent installIntent = iterator.next();
338 uninstallIntents.stream().filter(uIntent -> {
339 if (uIntent.equals(installIntent)) {
340 return true;
341 } else if (uIntent instanceof FlowRuleIntent && installIntent instanceof FlowRuleIntent) {
Brian O'Connor09d90f02016-09-13 11:06:14 -0700342 //FIXME we can further optimize this by doing the filtering on a flow-by-flow basis
343 // (direction can be implied from intent state)
helenyrwue6aaa332016-08-05 15:41:42 -0700344 return ((FlowRuleIntent) uIntent).flowRules()
345 .containsAll(((FlowRuleIntent) installIntent).flowRules());
346 } else {
347 return false;
348 }
349 }).findFirst().ifPresent(common -> {
350 uninstallIntents.remove(common);
Brian O'Connor09d90f02016-09-13 11:06:14 -0700351 if (INSTALLED.equals(uninstall.state())) {
352 // only remove the install intent if the existing
353 // intent (i.e. the uninstall one) is already
354 // installed or installing
355 iterator.remove();
356 }
helenyrwue6aaa332016-08-05 15:41:42 -0700357 });
358 }
359
360 final IntentData newUninstall = new IntentData(uninstall, uninstallIntents);
361 final IntentData newInstall = new IntentData(install, installIntents);
362
363 trackerService.removeTrackedResources(newUninstall.key(), newUninstall.intent().resources());
364 uninstallIntents.forEach(installable ->
365 trackerService.removeTrackedResources(newUninstall.intent().key(),
366 installable.resources()));
367 trackerService.addTrackedResources(newInstall.key(), newInstall.intent().resources());
368 installIntents.forEach(installable ->
369 trackerService.addTrackedResources(newInstall.key(),
370 installable.resources()));
371 prepareIntents(uninstallIntents, Direction.REMOVE);
372 prepareIntents(installIntents, Direction.ADD);
373 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800374 }
375
Thomas Vachuska2980c972016-02-23 20:58:49 -0800376 /**
377 * Applies the specified intent data, if present, to the network using the
378 * specified context.
379 *
380 * @param intentData optional intent data; no-op if not present
381 * @param direction indicates adding or removal
382 */
383 private void prepareIntentData(Optional<IntentData> intentData, Direction direction) {
384 if (!intentData.isPresent()) {
385 return;
386 }
387
388 IntentData data = intentData.get();
389 List<Intent> intentsToApply = data.installables();
390 checkState(intentsToApply.stream().allMatch(this::isSupported),
391 "Unsupported installable intents detected");
392
393 if (direction == Direction.ADD) {
394 trackerService.addTrackedResources(data.key(), data.intent().resources());
395 intentsToApply.forEach(installable ->
396 trackerService.addTrackedResources(data.key(),
397 installable.resources()));
398 } else {
399 trackerService.removeTrackedResources(data.key(), data.intent().resources());
400 intentsToApply.forEach(installable ->
401 trackerService.removeTrackedResources(data.intent().key(),
402 installable.resources()));
403 }
404
405 prepareIntents(intentsToApply, direction);
406 }
407
408 private boolean isSupported(Intent intent) {
Andreas Papazois05548962016-11-23 11:36:55 +0200409 return intent instanceof FlowRuleIntent ||
410 intent instanceof FlowObjectiveIntent ||
411 intent instanceof ProtectionEndpointIntent;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800412 }
413 }
414
415
Andreas Papazois05548962016-11-23 11:36:55 +0200416 // Context for applying and tracking operations related to flow rule intents.
Thomas Vachuska2980c972016-02-23 20:58:49 -0800417 private class FlowRuleOperationContext extends OperationContext {
418 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
419 FlowRuleOperationsContext flowRuleOperationsContext;
420
Andreas Papazois05548962016-11-23 11:36:55 +0200421 FlowRuleOperationContext(IntentInstallationContext context) {
422 super(context);
423 }
424
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700425 @Override
Thomas Vachuska2980c972016-02-23 20:58:49 -0800426 void apply() {
427 flowRuleOperationsContext = new FlowRuleOperationsContext() {
428 @Override
429 public void onSuccess(FlowRuleOperations ops) {
430 successConsumer.accept(FlowRuleOperationContext.this);
431 }
432
433 @Override
434 public void onError(FlowRuleOperations ops) {
435 errorConsumer.accept(FlowRuleOperationContext.this);
436 }
437 };
438 FlowRuleOperations operations = builder.build(flowRuleOperationsContext);
439
440 if (log.isTraceEnabled()) {
441 log.trace("applying intent {} -> {} with {} rules: {}",
442 toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
443 toInstall.map(x -> x.key().toString()).orElse("<empty>"),
444 operations.stages().stream().mapToLong(Set::size).sum(),
445 operations.stages());
446 }
447
448 flowRuleService.apply(operations);
449 }
450
451 @Override
452 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
453 // FIXME do FlowRuleIntents have stages??? Can we do uninstall work in parallel? I think so.
454 builder.newStage();
455
456 List<Collection<FlowRule>> stages = intentsToApply.stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200457 .filter(x -> x instanceof FlowRuleIntent)
Thomas Vachuska2980c972016-02-23 20:58:49 -0800458 .map(x -> (FlowRuleIntent) x)
459 .map(FlowRuleIntent::flowRules)
460 .collect(Collectors.toList());
461
462 for (Collection<FlowRule> rules : stages) {
463 if (direction == Direction.ADD) {
464 rules.forEach(builder::add);
465 } else {
466 rules.forEach(builder::remove);
467 }
468 }
469
470 }
471
472 @Override
473 public Object error() {
474 return flowRuleOperationsContext;
475 }
476 }
477
478 // Context for applying and tracking operations related to flow objective intents.
479 private class FlowObjectiveOperationContext extends OperationContext {
Ray Milkeyfd724402016-05-26 14:45:46 -0700480 List<FlowObjectiveInstallationContext> contexts = Lists.newLinkedList();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800481 final Set<ObjectiveContext> pendingContexts = Sets.newHashSet();
482 final Set<ObjectiveContext> errorContexts = Sets.newConcurrentHashSet();
483
Andreas Papazois05548962016-11-23 11:36:55 +0200484 FlowObjectiveOperationContext(IntentInstallationContext context) {
485 super(context);
486 }
487
Thomas Vachuska2980c972016-02-23 20:58:49 -0800488 @Override
489 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
Ray Milkeyfd724402016-05-26 14:45:46 -0700490 intentsToApply.stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200491 .filter(x -> x instanceof FlowObjectiveIntent)
Thomas Vachuska2980c972016-02-23 20:58:49 -0800492 .flatMap(x -> buildObjectiveContexts((FlowObjectiveIntent) x, direction).stream())
Ray Milkeyfd724402016-05-26 14:45:46 -0700493 .forEach(contexts::add);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800494 }
495
496 // Builds the specified objective in the appropriate direction
497 private List<FlowObjectiveInstallationContext> buildObjectiveContexts(FlowObjectiveIntent intent,
498 Direction direction) {
499 int size = intent.objectives().size();
500 List<FlowObjectiveInstallationContext> contexts = new ArrayList<>(size);
501 for (int i = 0; i < size; i++) {
502 DeviceId deviceId = intent.devices().get(i);
503 Objective.Builder builder = intent.objectives().get(i).copy();
504 FlowObjectiveInstallationContext context = new FlowObjectiveInstallationContext();
505
506 final Objective objective;
507 switch (direction) {
508 case ADD:
509 objective = builder.add(context);
510 break;
511 case REMOVE:
512 objective = builder.remove(context);
513 break;
514 default:
515 throw new UnsupportedOperationException("Unsupported direction " + direction);
516 }
517 context.setObjective(objective, deviceId);
518 contexts.add(context);
519 }
520 return contexts;
521 }
522
523 @Override
524 void apply() {
Pier Ventre2c433ce2016-12-13 13:23:02 -0800525 pendingContexts.addAll(contexts);
526 contexts.forEach(objectiveContext ->
Thomas Vachuska2980c972016-02-23 20:58:49 -0800527 flowObjectiveService.apply(objectiveContext.deviceId,
Pier Ventre2c433ce2016-12-13 13:23:02 -0800528 objectiveContext.objective)
529 );
Thomas Vachuska2980c972016-02-23 20:58:49 -0800530 }
531
532 @Override
533 public Object error() {
534 return errorContexts;
535 }
536
537 private class FlowObjectiveInstallationContext implements ObjectiveContext {
538 Objective objective;
539 DeviceId deviceId;
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700540 ObjectiveError error;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800541
542 void setObjective(Objective objective, DeviceId deviceId) {
543 this.objective = objective;
544 this.deviceId = deviceId;
545 }
546
547 @Override
548 public void onSuccess(Objective objective) {
549 finish();
550 }
551
552 @Override
553 public void onError(Objective objective, ObjectiveError error) {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700554 this.error = error;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800555 errorContexts.add(this);
556 finish();
557 }
558
559 private void finish() {
560 synchronized (pendingContexts) {
561 pendingContexts.remove(this);
562 if (pendingContexts.isEmpty()) {
563 if (errorContexts.isEmpty()) {
564 successConsumer.accept(FlowObjectiveOperationContext.this);
565 } else {
566 errorConsumer.accept(FlowObjectiveOperationContext.this);
567 }
568 }
569 }
570 }
571
572 @Override
573 public String toString() {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700574 return String.format("(%s on %s for %s)", error, deviceId, objective);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800575 }
576 }
577 }
578
579 private class ErrorContext extends OperationContext {
Andreas Papazois05548962016-11-23 11:36:55 +0200580 ErrorContext(IntentInstallationContext context) {
581 super(context);
582 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800583 @Override
584 void apply() {
585 throw new UnsupportedOperationException("Unsupported installable intent");
586 }
587
588 @Override
589 Object error() {
590 return null;
591 }
592
593 @Override
594 void prepareIntents(List<Intent> intentsToApply, Direction direction) {
595 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800596 }
Andreas Papazois05548962016-11-23 11:36:55 +0200597
598
599 /**
600 * Context for applying and tracking operations related to
601 * {@link ProtectionEndpointIntent}.
602 */
603 @Beta
604 private class ProtectionConfigOperationContext extends OperationContext {
605
606 ProtectionConfigOperationContext(IntentInstallationContext context) {
607 super(context);
608 }
609
610 /**
611 * Stage of installable Intents which can be processed in parallel.
612 */
613 private final class Stage {
614 // should it have progress state, how far it went?
615 private final Collection<Pair<ProtectionEndpointIntent, Direction>> ops;
616
617 Stage(Collection<Pair<ProtectionEndpointIntent, Direction>> ops) {
618 this.ops = checkNotNull(ops);
619 }
620
621 CompletableFuture<Void> apply() {
622 return ops.stream()
623 .map(op -> applyOp(op.getRight(), op.getLeft()))
624 .reduce(CompletableFuture.completedFuture(null),
625 (l, r) -> {
626 l.join();
627 return r;
628 });
629 }
630
631 private CompletableFuture<Void> applyOp(Direction dir, ProtectionEndpointIntent intent) {
632 log.trace("applying {}: {}", dir, intent);
633 if (dir == Direction.REMOVE) {
634 networkConfigService.removeConfig(intent.deviceId(), ProtectionConfig.class);
635 } else if (dir == Direction.ADD) {
636 ProtectedTransportEndpointDescription description = intent.description();
637
638 // Can't do following. Will trigger empty CONFIG_ADDED
639 //ProtectionConfig cfg = networkConfigService.addConfig(intent.deviceId(),
640 // ProtectionConfig.class);
641 ProtectionConfig cfg = new ProtectionConfig(intent.deviceId());
642 cfg.fingerprint(description.fingerprint());
643 cfg.peer(description.peer());
644 cfg.paths(description.paths());
645 //cfg.apply();
646
647 networkConfigService.applyConfig(intent.deviceId(),
648 ProtectionConfig.class,
649 cfg.node());
650 }
651 // TODO Should monitor progress and complete only after it's
652 // actually done.
653 return CompletableFuture.completedFuture(null);
654 }
655
656 @Override
657 public String toString() {
658 return ops.toString();
659 }
660 }
661
662 /**
663 * List of Stages which must be executed in order.
664 */
665 private final List<Stage> stages = new ArrayList<>();
666
667 private final List<Stage> failed = new CopyOnWriteArrayList<>();
668
669 @Override
670 synchronized void apply() {
671 for (Stage stage : stages) {
672 log.trace("applying Stage {}", stage);
673 CompletableFuture<Void> result = stage.apply();
674 // wait for stage completion
675 result.join();
676 if (result.isCompletedExceptionally()) {
677 log.error("Stage {} failed", stage);
678 failed.add(stage);
679 errorConsumer.accept(ProtectionConfigOperationContext.this);
680 return;
681 }
682 }
683 successConsumer.accept(ProtectionConfigOperationContext.this);
684 }
685
686 @Override
687 Object error() {
688 // Something to represent error state
689 return failed;
690 }
691
692 @Override
693 synchronized void prepareIntents(List<Intent> intentsToApply,
694 Direction direction) {
695
696 stages.add(new Stage(intentsToApply.stream()
697 .filter(i -> i instanceof ProtectionEndpointIntent)
698 .map(i -> Pair.of((ProtectionEndpointIntent) i, direction))
699 .collect(Collectors.toList())));
700 }
701
702 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800703}