blob: 8a50580f82e2254b539fabedff022b00772f6e9b [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;
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -080021import com.google.common.base.MoreObjects;
22import com.google.common.base.MoreObjects.ToStringHelper;
Ray Milkeyfd724402016-05-26 14:45:46 -070023import com.google.common.collect.Lists;
Thomas Vachuska2980c972016-02-23 20:58:49 -080024import com.google.common.collect.Sets;
Andreas Papazois05548962016-11-23 11:36:55 +020025
26import org.apache.commons.lang3.tuple.Pair;
Thomas Vachuska2980c972016-02-23 20:58:49 -080027import org.onosproject.net.DeviceId;
Andreas Papazois05548962016-11-23 11:36:55 +020028import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription;
29import org.onosproject.net.behaviour.protection.ProtectionConfig;
30import org.onosproject.net.config.NetworkConfigService;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080031import org.onosproject.net.flow.FlowRule;
32import org.onosproject.net.flow.FlowRuleOperations;
33import org.onosproject.net.flow.FlowRuleOperationsContext;
34import org.onosproject.net.flow.FlowRuleService;
35import org.onosproject.net.flowobjective.FlowObjectiveService;
Thomas Vachuska2980c972016-02-23 20:58:49 -080036import org.onosproject.net.flowobjective.Objective;
37import org.onosproject.net.flowobjective.ObjectiveContext;
38import org.onosproject.net.flowobjective.ObjectiveError;
39import org.onosproject.net.intent.FlowObjectiveIntent;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080040import org.onosproject.net.intent.FlowRuleIntent;
41import org.onosproject.net.intent.Intent;
42import org.onosproject.net.intent.IntentData;
43import org.onosproject.net.intent.IntentStore;
Andreas Papazois05548962016-11-23 11:36:55 +020044import org.onosproject.net.intent.ProtectionEndpointIntent;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080045import org.slf4j.Logger;
46
Thomas Vachuska2980c972016-02-23 20:58:49 -080047import java.util.ArrayList;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080048import java.util.Collection;
Brian O'Connora78f0602016-09-22 10:56:08 -070049import java.util.Collections;
helenyrwue6aaa332016-08-05 15:41:42 -070050import java.util.Iterator;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080051import java.util.List;
52import java.util.Optional;
53import java.util.Set;
Andreas Papazois05548962016-11-23 11:36:55 +020054import java.util.concurrent.CompletableFuture;
55import java.util.concurrent.CopyOnWriteArrayList;
Thomas Vachuska2980c972016-02-23 20:58:49 -080056import java.util.function.Consumer;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080057import java.util.stream.Collectors;
58
Andreas Papazois05548962016-11-23 11:36:55 +020059import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuska2980c972016-02-23 20:58:49 -080060import static com.google.common.base.Preconditions.checkState;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080061import static org.onosproject.net.intent.IntentState.*;
62import static org.slf4j.LoggerFactory.getLogger;
63
64/**
65 * Auxiliary entity responsible for installing the intents into the environment.
66 */
67class IntentInstaller {
68
Brian O'Connorc590ebb2016-12-08 18:16:41 -080069 private static final Logger log = getLogger(IntentInstaller.class);
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080070
71 private IntentStore store;
72 private ObjectiveTrackerService trackerService;
73 private FlowRuleService flowRuleService;
74 private FlowObjectiveService flowObjectiveService;
Andreas Papazois05548962016-11-23 11:36:55 +020075 private NetworkConfigService networkConfigService;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080076
77 private enum Direction {
78 ADD,
79 REMOVE
80 }
81
82 /**
83 * Initializes the installer with references to required services.
84 *
85 * @param intentStore intent store
86 * @param trackerService objective tracking service
87 * @param flowRuleService flow rule service
88 * @param flowObjectiveService flow objective service
Andreas Papazois05548962016-11-23 11:36:55 +020089 * @param networkConfigService network configuration service
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080090 */
91 void init(IntentStore intentStore, ObjectiveTrackerService trackerService,
Andreas Papazois05548962016-11-23 11:36:55 +020092 FlowRuleService flowRuleService, FlowObjectiveService flowObjectiveService,
93 NetworkConfigService networkConfigService) {
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080094 this.store = intentStore;
95 this.trackerService = trackerService;
Andreas Papazois05548962016-11-23 11:36:55 +020096 //TODO Various services should be plugged to the intent installer instead of being hardcoded
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080097 this.flowRuleService = flowRuleService;
98 this.flowObjectiveService = flowObjectiveService;
Andreas Papazois05548962016-11-23 11:36:55 +020099 this.networkConfigService = networkConfigService;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800100 }
101
Thomas Vachuska2980c972016-02-23 20:58:49 -0800102 // FIXME: Intent Manager should have never become dependent on a specific intent type(s).
103 // This will be addressed in intent domains work; not now.
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800104
105 /**
106 * Applies the specified intent updates to the environment by uninstalling
107 * and installing the intents and updating the store references appropriately.
108 *
109 * @param toUninstall optional intent to uninstall
110 * @param toInstall optional intent to install
111 */
112 void apply(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
Andreas Papazois05548962016-11-23 11:36:55 +0200113 // Hook for handling success at intent installation level.
114 Consumer<IntentInstallationContext> successConsumer = (ctx) -> {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800115 if (toInstall.isPresent()) {
116 IntentData installData = toInstall.get();
117 log.debug("Completed installing: {}", installData.key());
118 installData.setState(INSTALLED);
119 store.write(installData);
120 } else if (toUninstall.isPresent()) {
121 IntentData uninstallData = toUninstall.get();
122 log.debug("Completed withdrawing: {}", uninstallData.key());
123 switch (uninstallData.request()) {
124 case INSTALL_REQ:
125 uninstallData.setState(FAILED);
126 break;
127 case WITHDRAW_REQ:
128 default: //TODO "default" case should not happen
129 uninstallData.setState(WITHDRAWN);
130 break;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800131 }
Brian O'Connora78f0602016-09-22 10:56:08 -0700132 // Intent has been withdrawn; we can clear the installables
133 store.write(new IntentData(uninstallData, Collections.emptyList()));
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800134 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800135 };
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800136
Andreas Papazois05548962016-11-23 11:36:55 +0200137 // Hook for handling errors at intent installation level
138 Consumer<IntentInstallationContext> errorConsumer = (ctx) -> {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800139 // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
140 if (toInstall.isPresent()) {
141 IntentData installData = toInstall.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800142 installData.setState(CORRUPT);
143 installData.incrementErrorCount();
144 store.write(installData);
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800145 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800146 // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
147 if (toUninstall.isPresent()) {
148 IntentData uninstallData = toUninstall.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800149 uninstallData.setState(CORRUPT);
150 uninstallData.incrementErrorCount();
151 store.write(uninstallData);
152 }
153 };
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800154
Andreas Papazois05548962016-11-23 11:36:55 +0200155 // Hooks at operation level
156 Consumer<OperationContext> successOperationConsumer = (ctx) -> {
157 ctx.intentContext.finishContext(ctx);
158 };
159 Consumer<OperationContext> errorOperationConsumer = (ctx) -> {
160 if (ctx.toInstall.isPresent()) {
161 IntentData installData = toInstall.get();
162 log.warn("Failed installation operation for: {} {} due to {}",
163 installData.key(), installData.intent(), ctx.error());
164 }
165 if (ctx.toUninstall.isPresent()) {
166 IntentData uninstallData = toUninstall.get();
167 log.warn("Failed withdrawal operation for: {} {} due to {}",
168 uninstallData.key(), uninstallData.intent(), ctx.error());
169 }
170 ctx.intentContext.handleError(ctx);
171 };
172
Thomas Vachuska2980c972016-02-23 20:58:49 -0800173 // Create a context for tracking the backing operations for applying
174 // the intents to the environment.
Andreas Papazois05548962016-11-23 11:36:55 +0200175 IntentInstallationContext intentContext =
176 new IntentInstallationContext(successConsumer, errorConsumer);
177 Set<OperationContext> contexts = createContext(intentContext, toUninstall, toInstall);
178 intentContext.pendingContexts = contexts;
179 contexts.forEach(ctx -> {
180 ctx.prepare(toUninstall, toInstall, successOperationConsumer, errorOperationConsumer);
181 ctx.apply();
182 });
Thomas Vachuska2980c972016-02-23 20:58:49 -0800183 }
184
Andreas Papazois05548962016-11-23 11:36:55 +0200185 // Context for applying and tracking multiple kinds of operation contexts
186 // related to specific intent data.
187 private final class IntentInstallationContext {
188 private Set<OperationContext> pendingContexts = Sets.newHashSet();
189 private Set<OperationContext> errorContexts = Sets.newHashSet();
190 private Consumer<IntentInstallationContext> successConsumer;
191 private Consumer<IntentInstallationContext> errorConsumer;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800192
Andreas Papazois05548962016-11-23 11:36:55 +0200193 private IntentInstallationContext(Consumer<IntentInstallationContext> succesConsumer,
194 Consumer<IntentInstallationContext> errorConsumer) {
195 this.successConsumer = succesConsumer;
196 this.errorConsumer = errorConsumer;
197 }
198
199 private void handleError(OperationContext ctx) {
200 errorContexts.add(ctx);
201 finishContext(ctx);
202 }
203
204 private void finishContext(OperationContext ctx) {
205 synchronized (pendingContexts) {
206 pendingContexts.remove(ctx);
207 if (pendingContexts.isEmpty()) {
208 if (errorContexts.isEmpty()) {
209 successConsumer.accept(IntentInstallationContext.this);
210 } else {
211 errorConsumer.accept(IntentInstallationContext.this);
212 }
213 }
214 }
215 }
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800216
217 @Override
218 public String toString() {
219 return MoreObjects.toStringHelper(this)
220 .add("pendingContexts", pendingContexts)
221 .add("errorContexts", errorContexts)
222 .toString();
223 }
Andreas Papazois05548962016-11-23 11:36:55 +0200224 }
225
226 // --- Utilities to support various installable Intent ----
227
228 // Creates the set of contexts appropriate for tracking operations of the
Thomas Vachuska2980c972016-02-23 20:58:49 -0800229 // the specified intents.
Andreas Papazois05548962016-11-23 11:36:55 +0200230 private Set<OperationContext> createContext(IntentInstallationContext intentContext,
231 Optional<IntentData> toUninstall,
232 Optional<IntentData> toInstall) {
233
234 Set<OperationContext> contexts = Sets.newConcurrentHashSet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800235 if (isInstallable(toUninstall, toInstall, FlowRuleIntent.class)) {
Andreas Papazois05548962016-11-23 11:36:55 +0200236 contexts.add(new FlowRuleOperationContext(intentContext));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800237 }
238 if (isInstallable(toUninstall, toInstall, FlowObjectiveIntent.class)) {
Andreas Papazois05548962016-11-23 11:36:55 +0200239 contexts.add(new FlowObjectiveOperationContext(intentContext));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800240 }
Andreas Papazois05548962016-11-23 11:36:55 +0200241 if (isInstallable(toUninstall, toInstall, ProtectionEndpointIntent.class)) {
242 contexts.add(new ProtectionConfigOperationContext(intentContext));
243 }
244
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800245 if (contexts.isEmpty()) {
246 log.warn("{} did not contain installable Intents", intentContext);
247 return ImmutableSet.of(new ErrorContext(intentContext));
248 }
249
250 return contexts;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800251 }
252
253 private boolean isInstallable(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
254 Class<? extends Intent> intentClass) {
255 boolean notBothNull = false;
256 if (toInstall.isPresent()) {
257 notBothNull = true;
258 if (!toInstall.get().installables().stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200259 .anyMatch(i -> intentClass.isAssignableFrom(i.getClass()))) {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800260 return false;
261 }
262 }
263 if (toUninstall.isPresent()) {
264 notBothNull = true;
265 if (!toUninstall.get().installables().stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200266 .anyMatch(i -> intentClass.isAssignableFrom(i.getClass()))) {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800267 return false;
268 }
269 }
270 return notBothNull;
271 }
272
273 // Base context for applying and tracking operations related to installable intents.
274 private abstract class OperationContext {
Andreas Papazois05548962016-11-23 11:36:55 +0200275 protected IntentInstallationContext intentContext;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800276 protected Optional<IntentData> toUninstall;
277 protected Optional<IntentData> toInstall;
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700278 /**
279 * Implementation of {@link OperationContext} should call this on success.
280 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800281 protected Consumer<OperationContext> successConsumer;
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700282 /**
283 * Implementation of {@link OperationContext} should call this on error.
284 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800285 protected Consumer<OperationContext> errorConsumer;
286
Andreas Papazois05548962016-11-23 11:36:55 +0200287 protected OperationContext(IntentInstallationContext context) {
288 this.intentContext = context;
289 }
290
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700291 /**
292 * Applies the Intents specified by
293 * {@link #prepareIntents(List, Direction)} call(s) prior to this call.
294 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800295 abstract void apply();
296
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700297 /**
298 * Returns error state of the context.
299 * <p>
300 * Used for error logging purpose.
301 * Returned Object should have reasonable toString() implementation.
302 * @return context state, describing current error state
303 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800304 abstract Object error();
305
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700306 /**
307 * Prepares Intent(s) to {@link #apply() apply} in this operation.
308 * <p>
309 * Intents specified by {@code intentsToApply} in a single call
310 * can be applied to the Devices in arbitrary order.
311 * But group of Intents specified in consecutive {@link #prepareIntents(List, Direction)}
312 * calls must be applied in order. (e.g., guarded by barrier)
313 *
314 * @param intentsToApply {@link Intent}s to apply
315 * @param direction of operation
316 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800317 abstract void prepareIntents(List<Intent> intentsToApply, Direction direction);
318
319 void prepare(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
320 Consumer<OperationContext> successConsumer,
321 Consumer<OperationContext> errorConsumer) {
322 this.toUninstall = toUninstall;
323 this.toInstall = toInstall;
324 this.successConsumer = successConsumer;
325 this.errorConsumer = errorConsumer;
helenyrwue6aaa332016-08-05 15:41:42 -0700326 prepareIntentData(toUninstall, toInstall);
327 }
328
329 private void prepareIntentData(Optional<IntentData> uninstallData,
330 Optional<IntentData> installData) {
331 if (!installData.isPresent() && !uninstallData.isPresent()) {
332 return;
333 } else if (!installData.isPresent()) {
334 prepareIntentData(uninstallData, Direction.REMOVE);
335 } else if (!uninstallData.isPresent()) {
336 prepareIntentData(installData, Direction.ADD);
337 } else {
338 IntentData uninstall = uninstallData.get();
339 IntentData install = installData.get();
Brian O'Connor09d90f02016-09-13 11:06:14 -0700340 List<Intent> uninstallIntents = Lists.newArrayList(uninstall.installables());
341 List<Intent> installIntents = Lists.newArrayList(install.installables());
helenyrwue6aaa332016-08-05 15:41:42 -0700342
343 checkState(uninstallIntents.stream().allMatch(this::isSupported),
Yuta HIGUCHId5324f32017-01-27 14:35:13 -0800344 "Unsupported installable intents detected: %s", uninstallIntents);
helenyrwue6aaa332016-08-05 15:41:42 -0700345 checkState(installIntents.stream().allMatch(this::isSupported),
Yuta HIGUCHId5324f32017-01-27 14:35:13 -0800346 "Unsupported installable intents detected: %s", installIntents);
helenyrwue6aaa332016-08-05 15:41:42 -0700347
348 //TODO: Filter FlowObjective intents
349 // Filter out same intents and intents with same flow rules
350 Iterator<Intent> iterator = installIntents.iterator();
351 while (iterator.hasNext()) {
352 Intent installIntent = iterator.next();
353 uninstallIntents.stream().filter(uIntent -> {
354 if (uIntent.equals(installIntent)) {
355 return true;
356 } else if (uIntent instanceof FlowRuleIntent && installIntent instanceof FlowRuleIntent) {
Brian O'Connor09d90f02016-09-13 11:06:14 -0700357 //FIXME we can further optimize this by doing the filtering on a flow-by-flow basis
358 // (direction can be implied from intent state)
helenyrwue6aaa332016-08-05 15:41:42 -0700359 return ((FlowRuleIntent) uIntent).flowRules()
360 .containsAll(((FlowRuleIntent) installIntent).flowRules());
361 } else {
362 return false;
363 }
364 }).findFirst().ifPresent(common -> {
365 uninstallIntents.remove(common);
Brian O'Connor09d90f02016-09-13 11:06:14 -0700366 if (INSTALLED.equals(uninstall.state())) {
367 // only remove the install intent if the existing
368 // intent (i.e. the uninstall one) is already
369 // installed or installing
370 iterator.remove();
371 }
helenyrwue6aaa332016-08-05 15:41:42 -0700372 });
373 }
374
375 final IntentData newUninstall = new IntentData(uninstall, uninstallIntents);
376 final IntentData newInstall = new IntentData(install, installIntents);
377
378 trackerService.removeTrackedResources(newUninstall.key(), newUninstall.intent().resources());
379 uninstallIntents.forEach(installable ->
380 trackerService.removeTrackedResources(newUninstall.intent().key(),
381 installable.resources()));
382 trackerService.addTrackedResources(newInstall.key(), newInstall.intent().resources());
383 installIntents.forEach(installable ->
384 trackerService.addTrackedResources(newInstall.key(),
385 installable.resources()));
386 prepareIntents(uninstallIntents, Direction.REMOVE);
387 prepareIntents(installIntents, Direction.ADD);
388 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800389 }
390
Thomas Vachuska2980c972016-02-23 20:58:49 -0800391 /**
392 * Applies the specified intent data, if present, to the network using the
393 * specified context.
394 *
395 * @param intentData optional intent data; no-op if not present
396 * @param direction indicates adding or removal
397 */
398 private void prepareIntentData(Optional<IntentData> intentData, Direction direction) {
399 if (!intentData.isPresent()) {
400 return;
401 }
402
403 IntentData data = intentData.get();
404 List<Intent> intentsToApply = data.installables();
405 checkState(intentsToApply.stream().allMatch(this::isSupported),
Yuta HIGUCHId5324f32017-01-27 14:35:13 -0800406 "Unsupported installable intents detected: %s", intentsToApply);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800407
408 if (direction == Direction.ADD) {
409 trackerService.addTrackedResources(data.key(), data.intent().resources());
410 intentsToApply.forEach(installable ->
411 trackerService.addTrackedResources(data.key(),
412 installable.resources()));
413 } else {
414 trackerService.removeTrackedResources(data.key(), data.intent().resources());
415 intentsToApply.forEach(installable ->
416 trackerService.removeTrackedResources(data.intent().key(),
417 installable.resources()));
418 }
419
420 prepareIntents(intentsToApply, direction);
421 }
422
423 private boolean isSupported(Intent intent) {
Andreas Papazois05548962016-11-23 11:36:55 +0200424 return intent instanceof FlowRuleIntent ||
425 intent instanceof FlowObjectiveIntent ||
426 intent instanceof ProtectionEndpointIntent;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800427 }
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800428
429 protected ToStringHelper toStringHelper() {
430 return MoreObjects.toStringHelper(this)
431 .add("intentContext", intentContext)
432 .add("toUninstall", toUninstall)
433 .add("toInstall", toInstall);
434 }
435
436 @Override
437 public String toString() {
438 return toStringHelper()
439 .toString();
440 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800441 }
442
443
Andreas Papazois05548962016-11-23 11:36:55 +0200444 // Context for applying and tracking operations related to flow rule intents.
Thomas Vachuska2980c972016-02-23 20:58:49 -0800445 private class FlowRuleOperationContext extends OperationContext {
446 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
447 FlowRuleOperationsContext flowRuleOperationsContext;
448
Andreas Papazois05548962016-11-23 11:36:55 +0200449 FlowRuleOperationContext(IntentInstallationContext context) {
450 super(context);
451 }
452
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700453 @Override
Thomas Vachuska2980c972016-02-23 20:58:49 -0800454 void apply() {
455 flowRuleOperationsContext = new FlowRuleOperationsContext() {
456 @Override
457 public void onSuccess(FlowRuleOperations ops) {
458 successConsumer.accept(FlowRuleOperationContext.this);
459 }
460
461 @Override
462 public void onError(FlowRuleOperations ops) {
463 errorConsumer.accept(FlowRuleOperationContext.this);
464 }
465 };
466 FlowRuleOperations operations = builder.build(flowRuleOperationsContext);
467
468 if (log.isTraceEnabled()) {
469 log.trace("applying intent {} -> {} with {} rules: {}",
470 toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
471 toInstall.map(x -> x.key().toString()).orElse("<empty>"),
472 operations.stages().stream().mapToLong(Set::size).sum(),
473 operations.stages());
474 }
475
476 flowRuleService.apply(operations);
477 }
478
479 @Override
480 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
481 // FIXME do FlowRuleIntents have stages??? Can we do uninstall work in parallel? I think so.
482 builder.newStage();
483
484 List<Collection<FlowRule>> stages = intentsToApply.stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200485 .filter(x -> x instanceof FlowRuleIntent)
Thomas Vachuska2980c972016-02-23 20:58:49 -0800486 .map(x -> (FlowRuleIntent) x)
487 .map(FlowRuleIntent::flowRules)
488 .collect(Collectors.toList());
489
490 for (Collection<FlowRule> rules : stages) {
491 if (direction == Direction.ADD) {
492 rules.forEach(builder::add);
493 } else {
494 rules.forEach(builder::remove);
495 }
496 }
497
498 }
499
500 @Override
501 public Object error() {
502 return flowRuleOperationsContext;
503 }
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800504
505 @Override
506 protected ToStringHelper toStringHelper() {
507 return super.toStringHelper()
508 .omitNullValues()
509 .add("flowRuleOperationsContext", flowRuleOperationsContext);
510 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800511 }
512
513 // Context for applying and tracking operations related to flow objective intents.
514 private class FlowObjectiveOperationContext extends OperationContext {
Ray Milkeyfd724402016-05-26 14:45:46 -0700515 List<FlowObjectiveInstallationContext> contexts = Lists.newLinkedList();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800516 final Set<ObjectiveContext> pendingContexts = Sets.newHashSet();
517 final Set<ObjectiveContext> errorContexts = Sets.newConcurrentHashSet();
518
Andreas Papazois05548962016-11-23 11:36:55 +0200519 FlowObjectiveOperationContext(IntentInstallationContext context) {
520 super(context);
521 }
522
Thomas Vachuska2980c972016-02-23 20:58:49 -0800523 @Override
524 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
Ray Milkeyfd724402016-05-26 14:45:46 -0700525 intentsToApply.stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200526 .filter(x -> x instanceof FlowObjectiveIntent)
Thomas Vachuska2980c972016-02-23 20:58:49 -0800527 .flatMap(x -> buildObjectiveContexts((FlowObjectiveIntent) x, direction).stream())
Ray Milkeyfd724402016-05-26 14:45:46 -0700528 .forEach(contexts::add);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800529 }
530
531 // Builds the specified objective in the appropriate direction
532 private List<FlowObjectiveInstallationContext> buildObjectiveContexts(FlowObjectiveIntent intent,
533 Direction direction) {
534 int size = intent.objectives().size();
535 List<FlowObjectiveInstallationContext> contexts = new ArrayList<>(size);
536 for (int i = 0; i < size; i++) {
537 DeviceId deviceId = intent.devices().get(i);
538 Objective.Builder builder = intent.objectives().get(i).copy();
539 FlowObjectiveInstallationContext context = new FlowObjectiveInstallationContext();
540
541 final Objective objective;
542 switch (direction) {
543 case ADD:
544 objective = builder.add(context);
545 break;
546 case REMOVE:
547 objective = builder.remove(context);
548 break;
549 default:
550 throw new UnsupportedOperationException("Unsupported direction " + direction);
551 }
552 context.setObjective(objective, deviceId);
553 contexts.add(context);
554 }
555 return contexts;
556 }
557
558 @Override
559 void apply() {
Pier Ventre2c433ce2016-12-13 13:23:02 -0800560 pendingContexts.addAll(contexts);
561 contexts.forEach(objectiveContext ->
Thomas Vachuska2980c972016-02-23 20:58:49 -0800562 flowObjectiveService.apply(objectiveContext.deviceId,
Pier Ventre2c433ce2016-12-13 13:23:02 -0800563 objectiveContext.objective)
564 );
Thomas Vachuska2980c972016-02-23 20:58:49 -0800565 }
566
567 @Override
568 public Object error() {
569 return errorContexts;
570 }
571
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800572 @Override
573 protected ToStringHelper toStringHelper() {
574 return super.toStringHelper()
575 .add("contexts", contexts)
576 .add("pendingContexts", pendingContexts)
577 .add("errorContexts", errorContexts);
578 }
579
580
Thomas Vachuska2980c972016-02-23 20:58:49 -0800581 private class FlowObjectiveInstallationContext implements ObjectiveContext {
582 Objective objective;
583 DeviceId deviceId;
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700584 ObjectiveError error;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800585
586 void setObjective(Objective objective, DeviceId deviceId) {
587 this.objective = objective;
588 this.deviceId = deviceId;
589 }
590
591 @Override
592 public void onSuccess(Objective objective) {
593 finish();
594 }
595
596 @Override
597 public void onError(Objective objective, ObjectiveError error) {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700598 this.error = error;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800599 errorContexts.add(this);
600 finish();
601 }
602
603 private void finish() {
604 synchronized (pendingContexts) {
605 pendingContexts.remove(this);
606 if (pendingContexts.isEmpty()) {
607 if (errorContexts.isEmpty()) {
608 successConsumer.accept(FlowObjectiveOperationContext.this);
609 } else {
610 errorConsumer.accept(FlowObjectiveOperationContext.this);
611 }
612 }
613 }
614 }
615
616 @Override
617 public String toString() {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700618 return String.format("(%s on %s for %s)", error, deviceId, objective);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800619 }
620 }
621 }
622
623 private class ErrorContext extends OperationContext {
Andreas Papazois05548962016-11-23 11:36:55 +0200624 ErrorContext(IntentInstallationContext context) {
625 super(context);
626 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800627 @Override
628 void apply() {
629 throw new UnsupportedOperationException("Unsupported installable intent");
630 }
631
632 @Override
633 Object error() {
634 return null;
635 }
636
637 @Override
638 void prepareIntents(List<Intent> intentsToApply, Direction direction) {
639 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800640 }
Andreas Papazois05548962016-11-23 11:36:55 +0200641
642
643 /**
644 * Context for applying and tracking operations related to
645 * {@link ProtectionEndpointIntent}.
646 */
647 @Beta
648 private class ProtectionConfigOperationContext extends OperationContext {
649
650 ProtectionConfigOperationContext(IntentInstallationContext context) {
651 super(context);
652 }
653
654 /**
655 * Stage of installable Intents which can be processed in parallel.
656 */
657 private final class Stage {
658 // should it have progress state, how far it went?
659 private final Collection<Pair<ProtectionEndpointIntent, Direction>> ops;
660
661 Stage(Collection<Pair<ProtectionEndpointIntent, Direction>> ops) {
662 this.ops = checkNotNull(ops);
663 }
664
665 CompletableFuture<Void> apply() {
666 return ops.stream()
667 .map(op -> applyOp(op.getRight(), op.getLeft()))
668 .reduce(CompletableFuture.completedFuture(null),
669 (l, r) -> {
670 l.join();
671 return r;
672 });
673 }
674
675 private CompletableFuture<Void> applyOp(Direction dir, ProtectionEndpointIntent intent) {
676 log.trace("applying {}: {}", dir, intent);
677 if (dir == Direction.REMOVE) {
678 networkConfigService.removeConfig(intent.deviceId(), ProtectionConfig.class);
679 } else if (dir == Direction.ADD) {
680 ProtectedTransportEndpointDescription description = intent.description();
681
682 // Can't do following. Will trigger empty CONFIG_ADDED
683 //ProtectionConfig cfg = networkConfigService.addConfig(intent.deviceId(),
684 // ProtectionConfig.class);
685 ProtectionConfig cfg = new ProtectionConfig(intent.deviceId());
686 cfg.fingerprint(description.fingerprint());
687 cfg.peer(description.peer());
688 cfg.paths(description.paths());
689 //cfg.apply();
690
691 networkConfigService.applyConfig(intent.deviceId(),
692 ProtectionConfig.class,
693 cfg.node());
694 }
695 // TODO Should monitor progress and complete only after it's
696 // actually done.
697 return CompletableFuture.completedFuture(null);
698 }
699
700 @Override
701 public String toString() {
702 return ops.toString();
703 }
704 }
705
706 /**
707 * List of Stages which must be executed in order.
708 */
709 private final List<Stage> stages = new ArrayList<>();
710
711 private final List<Stage> failed = new CopyOnWriteArrayList<>();
712
713 @Override
714 synchronized void apply() {
715 for (Stage stage : stages) {
716 log.trace("applying Stage {}", stage);
717 CompletableFuture<Void> result = stage.apply();
718 // wait for stage completion
719 result.join();
720 if (result.isCompletedExceptionally()) {
721 log.error("Stage {} failed", stage);
722 failed.add(stage);
723 errorConsumer.accept(ProtectionConfigOperationContext.this);
724 return;
725 }
726 }
727 successConsumer.accept(ProtectionConfigOperationContext.this);
728 }
729
730 @Override
731 Object error() {
732 // Something to represent error state
733 return failed;
734 }
735
736 @Override
737 synchronized void prepareIntents(List<Intent> intentsToApply,
738 Direction direction) {
739
740 stages.add(new Stage(intentsToApply.stream()
741 .filter(i -> i instanceof ProtectionEndpointIntent)
742 .map(i -> Pair.of((ProtectionEndpointIntent) i, direction))
743 .collect(Collectors.toList())));
744 }
745
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800746 @Override
747 protected ToStringHelper toStringHelper() {
748 return super.toStringHelper()
749 .add("stages", stages)
750 .add("failed", failed);
751 }
Andreas Papazois05548962016-11-23 11:36:55 +0200752 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800753}