blob: d0928cef18afd5610b20cdf07e5feb3bb478de57 [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;
Andreas Papazoisf00087c2016-11-23 11:36:55 +020031import org.onosproject.net.domain.DomainIntent;
32import org.onosproject.net.domain.DomainIntentOperations;
33import org.onosproject.net.domain.DomainIntentOperationsContext;
34import org.onosproject.net.domain.DomainIntentService;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080035import org.onosproject.net.flow.FlowRule;
36import org.onosproject.net.flow.FlowRuleOperations;
37import org.onosproject.net.flow.FlowRuleOperationsContext;
38import org.onosproject.net.flow.FlowRuleService;
Yi Tseng38fc71e2017-02-03 14:50:47 -080039import org.onosproject.net.flowobjective.FilteringObjective;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080040import org.onosproject.net.flowobjective.FlowObjectiveService;
Yi Tseng38fc71e2017-02-03 14:50:47 -080041import org.onosproject.net.flowobjective.ForwardingObjective;
42import org.onosproject.net.flowobjective.NextObjective;
Thomas Vachuska2980c972016-02-23 20:58:49 -080043import org.onosproject.net.flowobjective.Objective;
44import org.onosproject.net.flowobjective.ObjectiveContext;
45import org.onosproject.net.flowobjective.ObjectiveError;
46import org.onosproject.net.intent.FlowObjectiveIntent;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080047import org.onosproject.net.intent.FlowRuleIntent;
48import org.onosproject.net.intent.Intent;
49import org.onosproject.net.intent.IntentData;
50import org.onosproject.net.intent.IntentStore;
Andreas Papazois05548962016-11-23 11:36:55 +020051import org.onosproject.net.intent.ProtectionEndpointIntent;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080052import org.slf4j.Logger;
53
Thomas Vachuska2980c972016-02-23 20:58:49 -080054import java.util.ArrayList;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080055import java.util.Collection;
Brian O'Connora78f0602016-09-22 10:56:08 -070056import java.util.Collections;
helenyrwue6aaa332016-08-05 15:41:42 -070057import java.util.Iterator;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080058import java.util.List;
59import java.util.Optional;
60import java.util.Set;
Andreas Papazois05548962016-11-23 11:36:55 +020061import java.util.concurrent.CompletableFuture;
62import java.util.concurrent.CopyOnWriteArrayList;
Yi Tseng38fc71e2017-02-03 14:50:47 -080063import java.util.concurrent.atomic.AtomicInteger;
Thomas Vachuska2980c972016-02-23 20:58:49 -080064import java.util.function.Consumer;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080065import java.util.stream.Collectors;
Yuta HIGUCHI3026c9b2017-01-30 13:28:49 -080066import java.util.stream.Stream;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080067
Andreas Papazois05548962016-11-23 11:36:55 +020068import static com.google.common.base.Preconditions.checkNotNull;
Thomas Vachuska2980c972016-02-23 20:58:49 -080069import static com.google.common.base.Preconditions.checkState;
Yi Tseng38fc71e2017-02-03 14:50:47 -080070import static org.onosproject.net.flowobjective.ObjectiveError.INSTALLATIONTHRESHOLDEXCEEDED;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080071import static org.onosproject.net.intent.IntentState.*;
72import static org.slf4j.LoggerFactory.getLogger;
73
74/**
75 * Auxiliary entity responsible for installing the intents into the environment.
76 */
77class IntentInstaller {
78
Brian O'Connorc590ebb2016-12-08 18:16:41 -080079 private static final Logger log = getLogger(IntentInstaller.class);
Yi Tseng38fc71e2017-02-03 14:50:47 -080080 private static final long OBJECTIVE_RETRY_THRESHOLD = 5;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080081
82 private IntentStore store;
83 private ObjectiveTrackerService trackerService;
84 private FlowRuleService flowRuleService;
85 private FlowObjectiveService flowObjectiveService;
Andreas Papazois05548962016-11-23 11:36:55 +020086 private NetworkConfigService networkConfigService;
Andreas Papazoisf00087c2016-11-23 11:36:55 +020087 private DomainIntentService domainIntentService;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -080088
89 private enum Direction {
90 ADD,
91 REMOVE
92 }
93
94 /**
95 * Initializes the installer with references to required services.
96 *
97 * @param intentStore intent store
98 * @param trackerService objective tracking service
99 * @param flowRuleService flow rule service
100 * @param flowObjectiveService flow objective service
Andreas Papazois05548962016-11-23 11:36:55 +0200101 * @param networkConfigService network configuration service
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200102 * @param domainIntentService domain intent service
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800103 */
104 void init(IntentStore intentStore, ObjectiveTrackerService trackerService,
Andreas Papazois05548962016-11-23 11:36:55 +0200105 FlowRuleService flowRuleService, FlowObjectiveService flowObjectiveService,
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200106 NetworkConfigService networkConfigService, DomainIntentService domainIntentService) {
107
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800108 this.store = intentStore;
109 this.trackerService = trackerService;
Andreas Papazois05548962016-11-23 11:36:55 +0200110 //TODO Various services should be plugged to the intent installer instead of being hardcoded
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800111 this.flowRuleService = flowRuleService;
112 this.flowObjectiveService = flowObjectiveService;
Andreas Papazois05548962016-11-23 11:36:55 +0200113 this.networkConfigService = networkConfigService;
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200114 this.domainIntentService = domainIntentService;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800115 }
116
Thomas Vachuska2980c972016-02-23 20:58:49 -0800117 // FIXME: Intent Manager should have never become dependent on a specific intent type(s).
118 // This will be addressed in intent domains work; not now.
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800119
120 /**
121 * Applies the specified intent updates to the environment by uninstalling
122 * and installing the intents and updating the store references appropriately.
123 *
124 * @param toUninstall optional intent to uninstall
125 * @param toInstall optional intent to install
126 */
127 void apply(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
Andreas Papazois05548962016-11-23 11:36:55 +0200128 // Hook for handling success at intent installation level.
129 Consumer<IntentInstallationContext> successConsumer = (ctx) -> {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800130 if (toInstall.isPresent()) {
131 IntentData installData = toInstall.get();
132 log.debug("Completed installing: {}", installData.key());
133 installData.setState(INSTALLED);
134 store.write(installData);
135 } else if (toUninstall.isPresent()) {
136 IntentData uninstallData = toUninstall.get();
137 log.debug("Completed withdrawing: {}", uninstallData.key());
138 switch (uninstallData.request()) {
139 case INSTALL_REQ:
Yuta HIGUCHI0164c1c2017-05-04 15:43:55 -0700140 // illegal state?
141 log.warn("{} was requested to withdraw during installation?",
142 uninstallData.intent());
Thomas Vachuska2980c972016-02-23 20:58:49 -0800143 uninstallData.setState(FAILED);
144 break;
145 case WITHDRAW_REQ:
146 default: //TODO "default" case should not happen
147 uninstallData.setState(WITHDRAWN);
148 break;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800149 }
Brian O'Connora78f0602016-09-22 10:56:08 -0700150 // Intent has been withdrawn; we can clear the installables
151 store.write(new IntentData(uninstallData, Collections.emptyList()));
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800152 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800153 };
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800154
Andreas Papazois05548962016-11-23 11:36:55 +0200155 // Hook for handling errors at intent installation level
156 Consumer<IntentInstallationContext> errorConsumer = (ctx) -> {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800157 // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
158 if (toInstall.isPresent()) {
159 IntentData installData = toInstall.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800160 installData.setState(CORRUPT);
161 installData.incrementErrorCount();
162 store.write(installData);
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800163 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800164 // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
165 if (toUninstall.isPresent()) {
166 IntentData uninstallData = toUninstall.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800167 uninstallData.setState(CORRUPT);
168 uninstallData.incrementErrorCount();
169 store.write(uninstallData);
170 }
171 };
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800172
Andreas Papazois05548962016-11-23 11:36:55 +0200173 // Hooks at operation level
174 Consumer<OperationContext> successOperationConsumer = (ctx) -> {
175 ctx.intentContext.finishContext(ctx);
176 };
177 Consumer<OperationContext> errorOperationConsumer = (ctx) -> {
178 if (ctx.toInstall.isPresent()) {
179 IntentData installData = toInstall.get();
180 log.warn("Failed installation operation for: {} {} due to {}",
181 installData.key(), installData.intent(), ctx.error());
182 }
183 if (ctx.toUninstall.isPresent()) {
184 IntentData uninstallData = toUninstall.get();
185 log.warn("Failed withdrawal operation for: {} {} due to {}",
186 uninstallData.key(), uninstallData.intent(), ctx.error());
187 }
188 ctx.intentContext.handleError(ctx);
189 };
190
Thomas Vachuska2980c972016-02-23 20:58:49 -0800191 // Create a context for tracking the backing operations for applying
192 // the intents to the environment.
Andreas Papazois05548962016-11-23 11:36:55 +0200193 IntentInstallationContext intentContext =
194 new IntentInstallationContext(successConsumer, errorConsumer);
195 Set<OperationContext> contexts = createContext(intentContext, toUninstall, toInstall);
196 intentContext.pendingContexts = contexts;
197 contexts.forEach(ctx -> {
198 ctx.prepare(toUninstall, toInstall, successOperationConsumer, errorOperationConsumer);
199 ctx.apply();
200 });
Thomas Vachuska2980c972016-02-23 20:58:49 -0800201 }
202
Andreas Papazois05548962016-11-23 11:36:55 +0200203 // Context for applying and tracking multiple kinds of operation contexts
204 // related to specific intent data.
205 private final class IntentInstallationContext {
206 private Set<OperationContext> pendingContexts = Sets.newHashSet();
207 private Set<OperationContext> errorContexts = Sets.newHashSet();
208 private Consumer<IntentInstallationContext> successConsumer;
209 private Consumer<IntentInstallationContext> errorConsumer;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800210
Andreas Papazois05548962016-11-23 11:36:55 +0200211 private IntentInstallationContext(Consumer<IntentInstallationContext> succesConsumer,
212 Consumer<IntentInstallationContext> errorConsumer) {
213 this.successConsumer = succesConsumer;
214 this.errorConsumer = errorConsumer;
215 }
216
217 private void handleError(OperationContext ctx) {
218 errorContexts.add(ctx);
219 finishContext(ctx);
220 }
221
222 private void finishContext(OperationContext ctx) {
223 synchronized (pendingContexts) {
224 pendingContexts.remove(ctx);
225 if (pendingContexts.isEmpty()) {
226 if (errorContexts.isEmpty()) {
227 successConsumer.accept(IntentInstallationContext.this);
228 } else {
229 errorConsumer.accept(IntentInstallationContext.this);
230 }
231 }
232 }
233 }
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800234
235 @Override
236 public String toString() {
237 return MoreObjects.toStringHelper(this)
238 .add("pendingContexts", pendingContexts)
239 .add("errorContexts", errorContexts)
240 .toString();
241 }
Andreas Papazois05548962016-11-23 11:36:55 +0200242 }
243
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200244 // --- Utilities to support FlowRule vs. FlowObjective vs. DomainIntent behavior ----
Andreas Papazois05548962016-11-23 11:36:55 +0200245
246 // Creates the set of contexts appropriate for tracking operations of the
Thomas Vachuska2980c972016-02-23 20:58:49 -0800247 // the specified intents.
Andreas Papazois05548962016-11-23 11:36:55 +0200248 private Set<OperationContext> createContext(IntentInstallationContext intentContext,
249 Optional<IntentData> toUninstall,
250 Optional<IntentData> toInstall) {
251
252 Set<OperationContext> contexts = Sets.newConcurrentHashSet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800253 if (isInstallable(toUninstall, toInstall, FlowRuleIntent.class)) {
Andreas Papazois05548962016-11-23 11:36:55 +0200254 contexts.add(new FlowRuleOperationContext(intentContext));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800255 }
256 if (isInstallable(toUninstall, toInstall, FlowObjectiveIntent.class)) {
Andreas Papazois05548962016-11-23 11:36:55 +0200257 contexts.add(new FlowObjectiveOperationContext(intentContext));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800258 }
Andreas Papazois05548962016-11-23 11:36:55 +0200259 if (isInstallable(toUninstall, toInstall, ProtectionEndpointIntent.class)) {
260 contexts.add(new ProtectionConfigOperationContext(intentContext));
261 }
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200262 if (isInstallable(toUninstall, toInstall, DomainIntent.class)) {
263 contexts.add(new DomainIntentOperationContext(intentContext));
264 }
Andreas Papazois05548962016-11-23 11:36:55 +0200265
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800266 if (contexts.isEmpty()) {
267 log.warn("{} did not contain installable Intents", intentContext);
268 return ImmutableSet.of(new ErrorContext(intentContext));
269 }
270
271 return contexts;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800272 }
273
Yuta HIGUCHI3026c9b2017-01-30 13:28:49 -0800274 /**
275 * Tests if one of {@code toUninstall} or {@code toInstall} contains
276 * installable Intent of type specified by {@code intentClass}.
277 *
278 * @param toUninstall IntentData to test
279 * @param toInstall IntentData to test
280 * @param intentClass installable Intent class
281 * @return true if at least one of IntentData contains installable specified.
282 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800283 private boolean isInstallable(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
284 Class<? extends Intent> intentClass) {
Yuta HIGUCHI3026c9b2017-01-30 13:28:49 -0800285
286 return Stream.concat(toInstall
287 .map(IntentData::installables)
288 .map(Collection::stream)
289 .orElse(Stream.empty()),
290 toUninstall
291 .map(IntentData::installables)
292 .map(Collection::stream)
293 .orElse(Stream.empty()))
294 .anyMatch(i -> intentClass.isAssignableFrom(i.getClass()));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800295 }
296
297 // Base context for applying and tracking operations related to installable intents.
298 private abstract class OperationContext {
Andreas Papazois05548962016-11-23 11:36:55 +0200299 protected IntentInstallationContext intentContext;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800300 protected Optional<IntentData> toUninstall;
301 protected Optional<IntentData> toInstall;
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700302 /**
303 * Implementation of {@link OperationContext} should call this on success.
304 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800305 protected Consumer<OperationContext> successConsumer;
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700306 /**
307 * Implementation of {@link OperationContext} should call this on error.
308 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800309 protected Consumer<OperationContext> errorConsumer;
310
Andreas Papazois05548962016-11-23 11:36:55 +0200311 protected OperationContext(IntentInstallationContext context) {
312 this.intentContext = context;
313 }
314
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700315 /**
316 * Applies the Intents specified by
317 * {@link #prepareIntents(List, Direction)} call(s) prior to this call.
318 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800319 abstract void apply();
320
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700321 /**
322 * Returns error state of the context.
323 * <p>
324 * Used for error logging purpose.
325 * Returned Object should have reasonable toString() implementation.
326 * @return context state, describing current error state
327 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800328 abstract Object error();
329
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700330 /**
331 * Prepares Intent(s) to {@link #apply() apply} in this operation.
332 * <p>
333 * Intents specified by {@code intentsToApply} in a single call
334 * can be applied to the Devices in arbitrary order.
335 * But group of Intents specified in consecutive {@link #prepareIntents(List, Direction)}
336 * calls must be applied in order. (e.g., guarded by barrier)
337 *
338 * @param intentsToApply {@link Intent}s to apply
339 * @param direction of operation
340 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800341 abstract void prepareIntents(List<Intent> intentsToApply, Direction direction);
342
343 void prepare(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
344 Consumer<OperationContext> successConsumer,
345 Consumer<OperationContext> errorConsumer) {
346 this.toUninstall = toUninstall;
347 this.toInstall = toInstall;
348 this.successConsumer = successConsumer;
349 this.errorConsumer = errorConsumer;
helenyrwue6aaa332016-08-05 15:41:42 -0700350 prepareIntentData(toUninstall, toInstall);
351 }
352
353 private void prepareIntentData(Optional<IntentData> uninstallData,
354 Optional<IntentData> installData) {
355 if (!installData.isPresent() && !uninstallData.isPresent()) {
356 return;
357 } else if (!installData.isPresent()) {
358 prepareIntentData(uninstallData, Direction.REMOVE);
359 } else if (!uninstallData.isPresent()) {
360 prepareIntentData(installData, Direction.ADD);
361 } else {
362 IntentData uninstall = uninstallData.get();
363 IntentData install = installData.get();
Brian O'Connor09d90f02016-09-13 11:06:14 -0700364 List<Intent> uninstallIntents = Lists.newArrayList(uninstall.installables());
365 List<Intent> installIntents = Lists.newArrayList(install.installables());
helenyrwue6aaa332016-08-05 15:41:42 -0700366
367 checkState(uninstallIntents.stream().allMatch(this::isSupported),
Yuta HIGUCHId5324f32017-01-27 14:35:13 -0800368 "Unsupported installable intents detected: %s", uninstallIntents);
helenyrwue6aaa332016-08-05 15:41:42 -0700369 checkState(installIntents.stream().allMatch(this::isSupported),
Yuta HIGUCHId5324f32017-01-27 14:35:13 -0800370 "Unsupported installable intents detected: %s", installIntents);
helenyrwue6aaa332016-08-05 15:41:42 -0700371
372 //TODO: Filter FlowObjective intents
373 // Filter out same intents and intents with same flow rules
374 Iterator<Intent> iterator = installIntents.iterator();
375 while (iterator.hasNext()) {
376 Intent installIntent = iterator.next();
377 uninstallIntents.stream().filter(uIntent -> {
378 if (uIntent.equals(installIntent)) {
379 return true;
380 } else if (uIntent instanceof FlowRuleIntent && installIntent instanceof FlowRuleIntent) {
Brian O'Connor09d90f02016-09-13 11:06:14 -0700381 //FIXME we can further optimize this by doing the filtering on a flow-by-flow basis
382 // (direction can be implied from intent state)
Yi Tseng424bfa72017-04-14 13:43:26 -0700383 return !flowRuleIntentChanged(((FlowRuleIntent) uIntent),
384 ((FlowRuleIntent) installIntent));
helenyrwue6aaa332016-08-05 15:41:42 -0700385 } else {
386 return false;
387 }
388 }).findFirst().ifPresent(common -> {
389 uninstallIntents.remove(common);
Brian O'Connor09d90f02016-09-13 11:06:14 -0700390 if (INSTALLED.equals(uninstall.state())) {
391 // only remove the install intent if the existing
392 // intent (i.e. the uninstall one) is already
393 // installed or installing
394 iterator.remove();
395 }
helenyrwue6aaa332016-08-05 15:41:42 -0700396 });
397 }
398
399 final IntentData newUninstall = new IntentData(uninstall, uninstallIntents);
400 final IntentData newInstall = new IntentData(install, installIntents);
401
402 trackerService.removeTrackedResources(newUninstall.key(), newUninstall.intent().resources());
403 uninstallIntents.forEach(installable ->
404 trackerService.removeTrackedResources(newUninstall.intent().key(),
405 installable.resources()));
406 trackerService.addTrackedResources(newInstall.key(), newInstall.intent().resources());
407 installIntents.forEach(installable ->
408 trackerService.addTrackedResources(newInstall.key(),
409 installable.resources()));
410 prepareIntents(uninstallIntents, Direction.REMOVE);
411 prepareIntents(installIntents, Direction.ADD);
412 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800413 }
414
Thomas Vachuska2980c972016-02-23 20:58:49 -0800415 /**
Yi Tseng424bfa72017-04-14 13:43:26 -0700416 * Determines whether there is any flow rule changed
417 * (i.e., different set of flow rules or different treatments)
418 * between FlowRuleIntents to be uninstalled and to be installed.
419 *
420 * @param uninstallIntent FlowRuleIntent to uninstall
421 * @param installIntent FlowRuleIntent to install
422 * @return true if flow rules which to be uninstalled
423 * contains all flow rules which to be installed.
424 */
425 private boolean flowRuleIntentChanged(FlowRuleIntent uninstallIntent,
426 FlowRuleIntent installIntent) {
427 Collection<FlowRule> flowRulesToUninstall = uninstallIntent.flowRules();
428 Collection<FlowRule> flowRulesToInstall = installIntent.flowRules();
429
430 // Check if any flow rule changed
431 for (FlowRule flowRuleToInstall : flowRulesToInstall) {
432 if (flowRulesToUninstall.stream().noneMatch(flowRuleToInstall::exactMatch)) {
433 return true;
434 }
435 }
436 return false;
437 }
438
439 /**
Thomas Vachuska2980c972016-02-23 20:58:49 -0800440 * Applies the specified intent data, if present, to the network using the
441 * specified context.
442 *
443 * @param intentData optional intent data; no-op if not present
444 * @param direction indicates adding or removal
445 */
446 private void prepareIntentData(Optional<IntentData> intentData, Direction direction) {
447 if (!intentData.isPresent()) {
448 return;
449 }
450
451 IntentData data = intentData.get();
452 List<Intent> intentsToApply = data.installables();
453 checkState(intentsToApply.stream().allMatch(this::isSupported),
Yuta HIGUCHId5324f32017-01-27 14:35:13 -0800454 "Unsupported installable intents detected: %s", intentsToApply);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800455
456 if (direction == Direction.ADD) {
457 trackerService.addTrackedResources(data.key(), data.intent().resources());
458 intentsToApply.forEach(installable ->
459 trackerService.addTrackedResources(data.key(),
460 installable.resources()));
461 } else {
462 trackerService.removeTrackedResources(data.key(), data.intent().resources());
463 intentsToApply.forEach(installable ->
464 trackerService.removeTrackedResources(data.intent().key(),
465 installable.resources()));
466 }
467
468 prepareIntents(intentsToApply, direction);
469 }
470
471 private boolean isSupported(Intent intent) {
Andreas Papazois05548962016-11-23 11:36:55 +0200472 return intent instanceof FlowRuleIntent ||
473 intent instanceof FlowObjectiveIntent ||
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200474 intent instanceof ProtectionEndpointIntent ||
475 intent instanceof DomainIntent;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800476 }
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800477
478 protected ToStringHelper toStringHelper() {
479 return MoreObjects.toStringHelper(this)
480 .add("intentContext", intentContext)
481 .add("toUninstall", toUninstall)
482 .add("toInstall", toInstall);
483 }
484
485 @Override
486 public String toString() {
487 return toStringHelper()
488 .toString();
489 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800490 }
491
492
Andreas Papazois05548962016-11-23 11:36:55 +0200493 // Context for applying and tracking operations related to flow rule intents.
Thomas Vachuska2980c972016-02-23 20:58:49 -0800494 private class FlowRuleOperationContext extends OperationContext {
495 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
496 FlowRuleOperationsContext flowRuleOperationsContext;
497
Andreas Papazois05548962016-11-23 11:36:55 +0200498 FlowRuleOperationContext(IntentInstallationContext context) {
499 super(context);
500 }
501
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700502 @Override
Thomas Vachuska2980c972016-02-23 20:58:49 -0800503 void apply() {
504 flowRuleOperationsContext = new FlowRuleOperationsContext() {
505 @Override
506 public void onSuccess(FlowRuleOperations ops) {
507 successConsumer.accept(FlowRuleOperationContext.this);
508 }
509
510 @Override
511 public void onError(FlowRuleOperations ops) {
512 errorConsumer.accept(FlowRuleOperationContext.this);
513 }
514 };
515 FlowRuleOperations operations = builder.build(flowRuleOperationsContext);
516
517 if (log.isTraceEnabled()) {
518 log.trace("applying intent {} -> {} with {} rules: {}",
519 toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
520 toInstall.map(x -> x.key().toString()).orElse("<empty>"),
521 operations.stages().stream().mapToLong(Set::size).sum(),
522 operations.stages());
523 }
524
525 flowRuleService.apply(operations);
526 }
527
528 @Override
529 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
530 // FIXME do FlowRuleIntents have stages??? Can we do uninstall work in parallel? I think so.
531 builder.newStage();
532
533 List<Collection<FlowRule>> stages = intentsToApply.stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200534 .filter(x -> x instanceof FlowRuleIntent)
Thomas Vachuska2980c972016-02-23 20:58:49 -0800535 .map(x -> (FlowRuleIntent) x)
536 .map(FlowRuleIntent::flowRules)
537 .collect(Collectors.toList());
538
539 for (Collection<FlowRule> rules : stages) {
540 if (direction == Direction.ADD) {
541 rules.forEach(builder::add);
542 } else {
543 rules.forEach(builder::remove);
544 }
545 }
546
547 }
548
549 @Override
550 public Object error() {
551 return flowRuleOperationsContext;
552 }
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800553
554 @Override
555 protected ToStringHelper toStringHelper() {
556 return super.toStringHelper()
557 .omitNullValues()
558 .add("flowRuleOperationsContext", flowRuleOperationsContext);
559 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800560 }
561
562 // Context for applying and tracking operations related to flow objective intents.
563 private class FlowObjectiveOperationContext extends OperationContext {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800564 private static final String UNSUPPORT_OBJ = "unsupported objective {}";
565 final List<ObjectiveContext> contexts = Lists.newArrayList();
566
567 final Set<ObjectiveContext> pendingContexts = Sets.newConcurrentHashSet();
568
569 // Second stage of pending contexts
570 final Set<ObjectiveContext> nextPendingContexts = Sets.newConcurrentHashSet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800571 final Set<ObjectiveContext> errorContexts = Sets.newConcurrentHashSet();
572
Andreas Papazois05548962016-11-23 11:36:55 +0200573 FlowObjectiveOperationContext(IntentInstallationContext context) {
574 super(context);
575 }
576
Thomas Vachuska2980c972016-02-23 20:58:49 -0800577 @Override
578 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800579 intentsToApply
580 .stream()
581 .filter(intent -> intent instanceof FlowObjectiveIntent)
582 .map(intent -> buildObjectiveContexts((FlowObjectiveIntent) intent, direction))
583 .flatMap(Collection::stream)
584 .forEach(contexts::add);
585
586 // Two stage for different direction context
587 // We will apply REMOVE context first, and apply ADD context.
588 contexts.forEach(context -> {
589 switch (direction) {
590 case REMOVE:
591 pendingContexts.add(context);
592 break;
593 case ADD:
594 nextPendingContexts.add(context);
595 break;
596 default:
597 break;
598 }
599 });
Thomas Vachuska2980c972016-02-23 20:58:49 -0800600 }
601
602 // Builds the specified objective in the appropriate direction
Yi Tseng38fc71e2017-02-03 14:50:47 -0800603 private Set<? extends ObjectiveContext> buildObjectiveContexts(FlowObjectiveIntent intent,
604 Direction direction) {
605 Set<FlowObjectiveInstallationContext> contexts = Sets.newHashSet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800606 int size = intent.objectives().size();
Yi Tseng38fc71e2017-02-03 14:50:47 -0800607 List<Objective> objectives = intent.objectives();
608 List<DeviceId> deviceIds = intent.devices();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800609
Yi Tseng38fc71e2017-02-03 14:50:47 -0800610 if (direction == Direction.ADD) {
611 for (int i = 0; i < size; i++) {
612 Objective objective = objectives.get(i);
613 DeviceId deviceId = deviceIds.get(i);
614 FlowObjectiveInstallationContext ctx =
615 buildObjectiveContext(objective, deviceId, direction);
616 contexts.add(ctx);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800617 }
Yi Tseng38fc71e2017-02-03 14:50:47 -0800618 return contexts;
619 } else {
620 // we need to care about ordering here
621 // basic idea is to chain objective contexts
622 for (int i = 0; i < size; i++) {
623 Objective objective = intent.objectives().get(i);
624 DeviceId deviceId = intent.devices().get(i);
625
626 if (objective instanceof FilteringObjective) {
627 // don't need to care ordering of filtering objective
628 FlowObjectiveInstallationContext ctx =
629 buildObjectiveContext(objective, deviceId, direction);
630 contexts.add(ctx);
631 } else if (objective instanceof NextObjective) {
632 // need to removed after forwarding objective
633 // nothing to do here
634 } else if (objective instanceof ForwardingObjective) {
635 // forwarding objective, also find next objective if
636 // exist
637 FlowObjectiveInstallationContext fwdCtx =
638 buildObjectiveContext(objective, deviceId, direction);
639 ForwardingObjective fwd = (ForwardingObjective) objective;
640 NextObjective nxt = null;
641 Integer nextId = fwd.nextId();
642 if (nextId != null) {
643 for (int j = 0; j < size; j++) {
644 if (objectives.get(j).id() == nextId) {
645 nxt = (NextObjective) objectives.get(j);
646 break;
647 }
648 }
649 // if a next objective exists in the Intent
650 if (nxt != null) {
651 FlowObjectiveInstallationContext nxtCtx =
652 buildObjectiveContext(nxt, deviceId, direction);
653 fwdCtx.nextContext(nxtCtx);
654 }
655 }
656 contexts.add(fwdCtx);
657 } else {
658 // possible here?
659 log.warn(UNSUPPORT_OBJ, objective);
660 }
661 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800662 }
663 return contexts;
664 }
665
Yi Tseng38fc71e2017-02-03 14:50:47 -0800666 private FlowObjectiveInstallationContext buildObjectiveContext(Objective objective,
667 DeviceId deviceId,
668 Direction direction) {
669 Objective.Builder builder = objective.copy();
670 FlowObjectiveInstallationContext ctx = new FlowObjectiveInstallationContext();
671 switch (direction) {
672 case ADD:
673 objective = builder.add(ctx);
674 break;
675 case REMOVE:
676 objective = builder.remove(ctx);
677 break;
678 default:
679 break;
680 }
681 ctx.setObjective(objective, deviceId);
682 return ctx;
683 }
684
Thomas Vachuska2980c972016-02-23 20:58:49 -0800685 @Override
686 void apply() {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800687 // If there is no pending contexts, try apply second stage
688 // pending contexts
689 if (pendingContexts.isEmpty()) {
690 pendingContexts.addAll(nextPendingContexts);
691 nextPendingContexts.clear();
692 }
693 final Set<ObjectiveContext> contextsToApply = Sets.newHashSet(pendingContexts);
694 contextsToApply.forEach(ctx -> {
695 FlowObjectiveInstallationContext foiCtx =
696 (FlowObjectiveInstallationContext) ctx;
697
698 flowObjectiveService.apply(foiCtx.deviceId, foiCtx.objective);
699 });
Thomas Vachuska2980c972016-02-23 20:58:49 -0800700 }
701
702 @Override
703 public Object error() {
704 return errorContexts;
705 }
706
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800707 @Override
708 protected ToStringHelper toStringHelper() {
709 return super.toStringHelper()
710 .add("contexts", contexts)
711 .add("pendingContexts", pendingContexts)
712 .add("errorContexts", errorContexts);
713 }
714
Thomas Vachuska2980c972016-02-23 20:58:49 -0800715 private class FlowObjectiveInstallationContext implements ObjectiveContext {
716 Objective objective;
717 DeviceId deviceId;
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700718 ObjectiveError error;
Yi Tseng38fc71e2017-02-03 14:50:47 -0800719 AtomicInteger retry;
720 FlowObjectiveInstallationContext nextContext;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800721
722 void setObjective(Objective objective, DeviceId deviceId) {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800723 // init function
Thomas Vachuska2980c972016-02-23 20:58:49 -0800724 this.objective = objective;
725 this.deviceId = deviceId;
Yi Tseng38fc71e2017-02-03 14:50:47 -0800726 this.error = null;
727 this.retry = new AtomicInteger(0);
728 this.nextContext = null;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800729 }
730
Yi Tseng38fc71e2017-02-03 14:50:47 -0800731 int retryTimes() {
732 return this.retry.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800733 }
734
Yi Tseng38fc71e2017-02-03 14:50:47 -0800735 void increaseRetryValue() {
736 this.retry.incrementAndGet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800737 }
738
Yi Tseng38fc71e2017-02-03 14:50:47 -0800739 private void finished(ObjectiveError error) {
740
Thomas Vachuska2980c972016-02-23 20:58:49 -0800741 synchronized (pendingContexts) {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800742 if (error != null) {
743 this.error = error;
744 handleObjectiveError(this, error);
745 } else {
746 // apply next context if exist
747 if (nextContext != null) {
748 pendingContexts.add(nextContext);
749 flowObjectiveService.apply(nextContext.deviceId,
750 nextContext.objective);
751 pendingContexts.remove(this);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800752 } else {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800753 pendingContexts.remove(this);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800754 }
755 }
Yi Tseng38fc71e2017-02-03 14:50:47 -0800756 if (!pendingContexts.isEmpty()) {
757 return;
758 }
759 // Apply second stage pending contexts if it is not empty
760 if (!nextPendingContexts.isEmpty()) {
761 pendingContexts.addAll(nextPendingContexts);
762 nextPendingContexts.clear();
763 final Set<ObjectiveContext> contextsToApply =
764 Sets.newHashSet(pendingContexts);
765 contextsToApply.forEach(ctx -> {
766 FlowObjectiveInstallationContext foiCtx =
767 (FlowObjectiveInstallationContext) ctx;
768 flowObjectiveService.apply(foiCtx.deviceId,
769 foiCtx.objective);
770 });
771 return;
772 }
773 if (errorContexts.isEmpty()) {
774 successConsumer.accept(FlowObjectiveOperationContext.this);
775 } else {
776 errorConsumer.accept(FlowObjectiveOperationContext.this);
777 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800778 }
779 }
780
781 @Override
Yi Tseng38fc71e2017-02-03 14:50:47 -0800782 public void onSuccess(Objective objective) {
783 finished(null);
784 }
785
786 @Override
787 public void onError(Objective objective, ObjectiveError error) {
788 finished(error);
789 }
790
791 @Override
Thomas Vachuska2980c972016-02-23 20:58:49 -0800792 public String toString() {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700793 return String.format("(%s on %s for %s)", error, deviceId, objective);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800794 }
Yi Tseng38fc71e2017-02-03 14:50:47 -0800795
796 public void nextContext(FlowObjectiveInstallationContext nextContext) {
797 this.nextContext = nextContext;
798 }
799 }
800
801 private void handleObjectiveError(FlowObjectiveInstallationContext ctx,
802 ObjectiveError error) {
803 log.debug("Got error(s) when install objective: {}, error: {}, retry: {}",
804 ctx.objective, ctx.error, ctx.retry);
805 if (ctx.retryTimes() > OBJECTIVE_RETRY_THRESHOLD) {
806 ctx.error = INSTALLATIONTHRESHOLDEXCEEDED;
807 errorContexts.add(ctx);
808 return;
809 }
810 // reset error
811 ctx.error = null;
812 // strategies for errors
813 switch (error) {
814 case GROUPEXISTS:
815 if (ctx.objective.op() == Objective.Operation.ADD) {
816 // Next group exists
817 // build new objective with new op ADD_TO_EXIST
818 NextObjective newObj =
819 ((NextObjective.Builder) ctx.objective.copy()).addToExisting(ctx);
820 ctx.setObjective(newObj, ctx.deviceId);
821 ctx.increaseRetryValue();
822 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
823 } else {
824 pendingContexts.remove(ctx);
825 errorContexts.add(ctx);
826 }
827 break;
828 case GROUPINSTALLATIONFAILED:
829 // Group install failed, retry again
830 ctx.increaseRetryValue();
831 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
832 break;
833 case GROUPMISSING:
834 if (ctx.objective.op() == Objective.Operation.ADD_TO_EXISTING) {
835 // Next group not exist, but we want to add new buckets
836 // build new objective with new op ADD
837 NextObjective newObj = (NextObjective) ctx.objective.copy().add(ctx);
838 ctx.setObjective(newObj, ctx.deviceId);
839 ctx.increaseRetryValue();
840 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
841 } else if (ctx.objective.op() == Objective.Operation.REMOVE ||
842 ctx.objective.op() == Objective.Operation.REMOVE_FROM_EXISTING) {
843 // Already removed, no need to do anything
844 ctx.error = null;
845 pendingContexts.remove(ctx);
846 return;
847 } else {
848 // Next chaining group missing, try again.
849 ctx.increaseRetryValue();
850 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
851 }
852 break;
853 case FLOWINSTALLATIONFAILED:
854 case GROUPREMOVALFAILED:
855 case INSTALLATIONTIMEOUT:
856 // Retry
857 ctx.increaseRetryValue();
858 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
859 break;
860 default:
861 pendingContexts.remove(ctx);
862 errorContexts.add(ctx);
863 break;
864 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800865 }
866 }
867
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200868 // Context for applying and tracking operations related to domain intents.
869 private class DomainIntentOperationContext extends OperationContext {
870 DomainIntentOperations.Builder builder = DomainIntentOperations.builder();
871 DomainIntentOperationsContext domainOperationsContext;
872
873 DomainIntentOperationContext(IntentInstallationContext context) {
874 super(context);
875 }
876 @Override
877 void apply() {
878 domainOperationsContext = new DomainIntentOperationsContext() {
879 @Override
880 public void onSuccess(DomainIntentOperations ops) {
881 successConsumer.accept(DomainIntentOperationContext.this);
882 }
883
884 @Override
885 public void onError(DomainIntentOperations ops) {
886 errorConsumer.accept(DomainIntentOperationContext.this);
887 }
888 };
889 DomainIntentOperations operations = builder.build(domainOperationsContext);
890
891 if (log.isTraceEnabled()) {
892 log.trace("submitting domain intent {} -> {}",
893 toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
894 toInstall.map(x -> x.key().toString()).orElse("<empty>"));
895 }
896 domainIntentService.sumbit(operations);
897 }
898
899 @Override
900 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
901 List<DomainIntent> intents = intentsToApply.stream()
902 .filter(x -> x instanceof DomainIntent)
903 .map(x -> (DomainIntent) x)
904 .collect(Collectors.toList());
905
906 for (DomainIntent intent : intents) {
907 if (direction == Direction.ADD) {
908 builder.add(intent);
909 } else {
910 builder.remove(intent);
911 }
912 }
913 }
914
915 @Override
916 public Object error() {
917 return domainOperationsContext;
918 }
919 }
920
Thomas Vachuska2980c972016-02-23 20:58:49 -0800921 private class ErrorContext extends OperationContext {
Andreas Papazois05548962016-11-23 11:36:55 +0200922 ErrorContext(IntentInstallationContext context) {
923 super(context);
924 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800925 @Override
926 void apply() {
927 throw new UnsupportedOperationException("Unsupported installable intent");
928 }
929
930 @Override
931 Object error() {
932 return null;
933 }
934
935 @Override
936 void prepareIntents(List<Intent> intentsToApply, Direction direction) {
937 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800938 }
Andreas Papazois05548962016-11-23 11:36:55 +0200939
940
941 /**
942 * Context for applying and tracking operations related to
943 * {@link ProtectionEndpointIntent}.
944 */
945 @Beta
946 private class ProtectionConfigOperationContext extends OperationContext {
947
948 ProtectionConfigOperationContext(IntentInstallationContext context) {
949 super(context);
950 }
951
952 /**
953 * Stage of installable Intents which can be processed in parallel.
954 */
955 private final class Stage {
956 // should it have progress state, how far it went?
957 private final Collection<Pair<ProtectionEndpointIntent, Direction>> ops;
958
959 Stage(Collection<Pair<ProtectionEndpointIntent, Direction>> ops) {
960 this.ops = checkNotNull(ops);
961 }
962
963 CompletableFuture<Void> apply() {
964 return ops.stream()
965 .map(op -> applyOp(op.getRight(), op.getLeft()))
966 .reduce(CompletableFuture.completedFuture(null),
967 (l, r) -> {
968 l.join();
969 return r;
970 });
971 }
972
973 private CompletableFuture<Void> applyOp(Direction dir, ProtectionEndpointIntent intent) {
974 log.trace("applying {}: {}", dir, intent);
975 if (dir == Direction.REMOVE) {
976 networkConfigService.removeConfig(intent.deviceId(), ProtectionConfig.class);
977 } else if (dir == Direction.ADD) {
978 ProtectedTransportEndpointDescription description = intent.description();
979
980 // Can't do following. Will trigger empty CONFIG_ADDED
981 //ProtectionConfig cfg = networkConfigService.addConfig(intent.deviceId(),
982 // ProtectionConfig.class);
983 ProtectionConfig cfg = new ProtectionConfig(intent.deviceId());
984 cfg.fingerprint(description.fingerprint());
985 cfg.peer(description.peer());
986 cfg.paths(description.paths());
987 //cfg.apply();
988
989 networkConfigService.applyConfig(intent.deviceId(),
990 ProtectionConfig.class,
991 cfg.node());
992 }
993 // TODO Should monitor progress and complete only after it's
994 // actually done.
995 return CompletableFuture.completedFuture(null);
996 }
997
998 @Override
999 public String toString() {
1000 return ops.toString();
1001 }
1002 }
1003
1004 /**
1005 * List of Stages which must be executed in order.
1006 */
1007 private final List<Stage> stages = new ArrayList<>();
1008
1009 private final List<Stage> failed = new CopyOnWriteArrayList<>();
1010
1011 @Override
1012 synchronized void apply() {
1013 for (Stage stage : stages) {
1014 log.trace("applying Stage {}", stage);
1015 CompletableFuture<Void> result = stage.apply();
1016 // wait for stage completion
1017 result.join();
1018 if (result.isCompletedExceptionally()) {
1019 log.error("Stage {} failed", stage);
1020 failed.add(stage);
1021 errorConsumer.accept(ProtectionConfigOperationContext.this);
1022 return;
1023 }
1024 }
1025 successConsumer.accept(ProtectionConfigOperationContext.this);
1026 }
1027
1028 @Override
1029 Object error() {
1030 // Something to represent error state
1031 return failed;
1032 }
1033
1034 @Override
1035 synchronized void prepareIntents(List<Intent> intentsToApply,
1036 Direction direction) {
1037
1038 stages.add(new Stage(intentsToApply.stream()
1039 .filter(i -> i instanceof ProtectionEndpointIntent)
1040 .map(i -> Pair.of((ProtectionEndpointIntent) i, direction))
1041 .collect(Collectors.toList())));
1042 }
1043
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -08001044 @Override
1045 protected ToStringHelper toStringHelper() {
1046 return super.toStringHelper()
1047 .add("stages", stages)
1048 .add("failed", failed);
1049 }
Andreas Papazois05548962016-11-23 11:36:55 +02001050 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -08001051}