blob: 3ce320d9735f075461963818980fe79be0a33a8f [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:
140 uninstallData.setState(FAILED);
141 break;
142 case WITHDRAW_REQ:
143 default: //TODO "default" case should not happen
144 uninstallData.setState(WITHDRAWN);
145 break;
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800146 }
Brian O'Connora78f0602016-09-22 10:56:08 -0700147 // Intent has been withdrawn; we can clear the installables
148 store.write(new IntentData(uninstallData, Collections.emptyList()));
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800149 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800150 };
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800151
Andreas Papazois05548962016-11-23 11:36:55 +0200152 // Hook for handling errors at intent installation level
153 Consumer<IntentInstallationContext> errorConsumer = (ctx) -> {
Thomas Vachuska2980c972016-02-23 20:58:49 -0800154 // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
155 if (toInstall.isPresent()) {
156 IntentData installData = toInstall.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800157 installData.setState(CORRUPT);
158 installData.incrementErrorCount();
159 store.write(installData);
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800160 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800161 // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
162 if (toUninstall.isPresent()) {
163 IntentData uninstallData = toUninstall.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800164 uninstallData.setState(CORRUPT);
165 uninstallData.incrementErrorCount();
166 store.write(uninstallData);
167 }
168 };
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800169
Andreas Papazois05548962016-11-23 11:36:55 +0200170 // Hooks at operation level
171 Consumer<OperationContext> successOperationConsumer = (ctx) -> {
172 ctx.intentContext.finishContext(ctx);
173 };
174 Consumer<OperationContext> errorOperationConsumer = (ctx) -> {
175 if (ctx.toInstall.isPresent()) {
176 IntentData installData = toInstall.get();
177 log.warn("Failed installation operation for: {} {} due to {}",
178 installData.key(), installData.intent(), ctx.error());
179 }
180 if (ctx.toUninstall.isPresent()) {
181 IntentData uninstallData = toUninstall.get();
182 log.warn("Failed withdrawal operation for: {} {} due to {}",
183 uninstallData.key(), uninstallData.intent(), ctx.error());
184 }
185 ctx.intentContext.handleError(ctx);
186 };
187
Thomas Vachuska2980c972016-02-23 20:58:49 -0800188 // Create a context for tracking the backing operations for applying
189 // the intents to the environment.
Andreas Papazois05548962016-11-23 11:36:55 +0200190 IntentInstallationContext intentContext =
191 new IntentInstallationContext(successConsumer, errorConsumer);
192 Set<OperationContext> contexts = createContext(intentContext, toUninstall, toInstall);
193 intentContext.pendingContexts = contexts;
194 contexts.forEach(ctx -> {
195 ctx.prepare(toUninstall, toInstall, successOperationConsumer, errorOperationConsumer);
196 ctx.apply();
197 });
Thomas Vachuska2980c972016-02-23 20:58:49 -0800198 }
199
Andreas Papazois05548962016-11-23 11:36:55 +0200200 // Context for applying and tracking multiple kinds of operation contexts
201 // related to specific intent data.
202 private final class IntentInstallationContext {
203 private Set<OperationContext> pendingContexts = Sets.newHashSet();
204 private Set<OperationContext> errorContexts = Sets.newHashSet();
205 private Consumer<IntentInstallationContext> successConsumer;
206 private Consumer<IntentInstallationContext> errorConsumer;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800207
Andreas Papazois05548962016-11-23 11:36:55 +0200208 private IntentInstallationContext(Consumer<IntentInstallationContext> succesConsumer,
209 Consumer<IntentInstallationContext> errorConsumer) {
210 this.successConsumer = succesConsumer;
211 this.errorConsumer = errorConsumer;
212 }
213
214 private void handleError(OperationContext ctx) {
215 errorContexts.add(ctx);
216 finishContext(ctx);
217 }
218
219 private void finishContext(OperationContext ctx) {
220 synchronized (pendingContexts) {
221 pendingContexts.remove(ctx);
222 if (pendingContexts.isEmpty()) {
223 if (errorContexts.isEmpty()) {
224 successConsumer.accept(IntentInstallationContext.this);
225 } else {
226 errorConsumer.accept(IntentInstallationContext.this);
227 }
228 }
229 }
230 }
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800231
232 @Override
233 public String toString() {
234 return MoreObjects.toStringHelper(this)
235 .add("pendingContexts", pendingContexts)
236 .add("errorContexts", errorContexts)
237 .toString();
238 }
Andreas Papazois05548962016-11-23 11:36:55 +0200239 }
240
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200241 // --- Utilities to support FlowRule vs. FlowObjective vs. DomainIntent behavior ----
Andreas Papazois05548962016-11-23 11:36:55 +0200242
243 // Creates the set of contexts appropriate for tracking operations of the
Thomas Vachuska2980c972016-02-23 20:58:49 -0800244 // the specified intents.
Andreas Papazois05548962016-11-23 11:36:55 +0200245 private Set<OperationContext> createContext(IntentInstallationContext intentContext,
246 Optional<IntentData> toUninstall,
247 Optional<IntentData> toInstall) {
248
249 Set<OperationContext> contexts = Sets.newConcurrentHashSet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800250 if (isInstallable(toUninstall, toInstall, FlowRuleIntent.class)) {
Andreas Papazois05548962016-11-23 11:36:55 +0200251 contexts.add(new FlowRuleOperationContext(intentContext));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800252 }
253 if (isInstallable(toUninstall, toInstall, FlowObjectiveIntent.class)) {
Andreas Papazois05548962016-11-23 11:36:55 +0200254 contexts.add(new FlowObjectiveOperationContext(intentContext));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800255 }
Andreas Papazois05548962016-11-23 11:36:55 +0200256 if (isInstallable(toUninstall, toInstall, ProtectionEndpointIntent.class)) {
257 contexts.add(new ProtectionConfigOperationContext(intentContext));
258 }
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200259 if (isInstallable(toUninstall, toInstall, DomainIntent.class)) {
260 contexts.add(new DomainIntentOperationContext(intentContext));
261 }
Andreas Papazois05548962016-11-23 11:36:55 +0200262
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800263 if (contexts.isEmpty()) {
264 log.warn("{} did not contain installable Intents", intentContext);
265 return ImmutableSet.of(new ErrorContext(intentContext));
266 }
267
268 return contexts;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800269 }
270
Yuta HIGUCHI3026c9b2017-01-30 13:28:49 -0800271 /**
272 * Tests if one of {@code toUninstall} or {@code toInstall} contains
273 * installable Intent of type specified by {@code intentClass}.
274 *
275 * @param toUninstall IntentData to test
276 * @param toInstall IntentData to test
277 * @param intentClass installable Intent class
278 * @return true if at least one of IntentData contains installable specified.
279 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800280 private boolean isInstallable(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
281 Class<? extends Intent> intentClass) {
Yuta HIGUCHI3026c9b2017-01-30 13:28:49 -0800282
283 return Stream.concat(toInstall
284 .map(IntentData::installables)
285 .map(Collection::stream)
286 .orElse(Stream.empty()),
287 toUninstall
288 .map(IntentData::installables)
289 .map(Collection::stream)
290 .orElse(Stream.empty()))
291 .anyMatch(i -> intentClass.isAssignableFrom(i.getClass()));
Thomas Vachuska2980c972016-02-23 20:58:49 -0800292 }
293
294 // Base context for applying and tracking operations related to installable intents.
295 private abstract class OperationContext {
Andreas Papazois05548962016-11-23 11:36:55 +0200296 protected IntentInstallationContext intentContext;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800297 protected Optional<IntentData> toUninstall;
298 protected Optional<IntentData> toInstall;
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700299 /**
300 * Implementation of {@link OperationContext} should call this on success.
301 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800302 protected Consumer<OperationContext> successConsumer;
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700303 /**
304 * Implementation of {@link OperationContext} should call this on error.
305 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800306 protected Consumer<OperationContext> errorConsumer;
307
Andreas Papazois05548962016-11-23 11:36:55 +0200308 protected OperationContext(IntentInstallationContext context) {
309 this.intentContext = context;
310 }
311
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700312 /**
313 * Applies the Intents specified by
314 * {@link #prepareIntents(List, Direction)} call(s) prior to this call.
315 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800316 abstract void apply();
317
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700318 /**
319 * Returns error state of the context.
320 * <p>
321 * Used for error logging purpose.
322 * Returned Object should have reasonable toString() implementation.
323 * @return context state, describing current error state
324 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800325 abstract Object error();
326
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700327 /**
328 * Prepares Intent(s) to {@link #apply() apply} in this operation.
329 * <p>
330 * Intents specified by {@code intentsToApply} in a single call
331 * can be applied to the Devices in arbitrary order.
332 * But group of Intents specified in consecutive {@link #prepareIntents(List, Direction)}
333 * calls must be applied in order. (e.g., guarded by barrier)
334 *
335 * @param intentsToApply {@link Intent}s to apply
336 * @param direction of operation
337 */
Thomas Vachuska2980c972016-02-23 20:58:49 -0800338 abstract void prepareIntents(List<Intent> intentsToApply, Direction direction);
339
340 void prepare(Optional<IntentData> toUninstall, Optional<IntentData> toInstall,
341 Consumer<OperationContext> successConsumer,
342 Consumer<OperationContext> errorConsumer) {
343 this.toUninstall = toUninstall;
344 this.toInstall = toInstall;
345 this.successConsumer = successConsumer;
346 this.errorConsumer = errorConsumer;
helenyrwue6aaa332016-08-05 15:41:42 -0700347 prepareIntentData(toUninstall, toInstall);
348 }
349
350 private void prepareIntentData(Optional<IntentData> uninstallData,
351 Optional<IntentData> installData) {
352 if (!installData.isPresent() && !uninstallData.isPresent()) {
353 return;
354 } else if (!installData.isPresent()) {
355 prepareIntentData(uninstallData, Direction.REMOVE);
356 } else if (!uninstallData.isPresent()) {
357 prepareIntentData(installData, Direction.ADD);
358 } else {
359 IntentData uninstall = uninstallData.get();
360 IntentData install = installData.get();
Brian O'Connor09d90f02016-09-13 11:06:14 -0700361 List<Intent> uninstallIntents = Lists.newArrayList(uninstall.installables());
362 List<Intent> installIntents = Lists.newArrayList(install.installables());
helenyrwue6aaa332016-08-05 15:41:42 -0700363
364 checkState(uninstallIntents.stream().allMatch(this::isSupported),
Yuta HIGUCHId5324f32017-01-27 14:35:13 -0800365 "Unsupported installable intents detected: %s", uninstallIntents);
helenyrwue6aaa332016-08-05 15:41:42 -0700366 checkState(installIntents.stream().allMatch(this::isSupported),
Yuta HIGUCHId5324f32017-01-27 14:35:13 -0800367 "Unsupported installable intents detected: %s", installIntents);
helenyrwue6aaa332016-08-05 15:41:42 -0700368
369 //TODO: Filter FlowObjective intents
370 // Filter out same intents and intents with same flow rules
371 Iterator<Intent> iterator = installIntents.iterator();
372 while (iterator.hasNext()) {
373 Intent installIntent = iterator.next();
374 uninstallIntents.stream().filter(uIntent -> {
375 if (uIntent.equals(installIntent)) {
376 return true;
377 } else if (uIntent instanceof FlowRuleIntent && installIntent instanceof FlowRuleIntent) {
Brian O'Connor09d90f02016-09-13 11:06:14 -0700378 //FIXME we can further optimize this by doing the filtering on a flow-by-flow basis
379 // (direction can be implied from intent state)
Yi Tseng424bfa72017-04-14 13:43:26 -0700380 return !flowRuleIntentChanged(((FlowRuleIntent) uIntent),
381 ((FlowRuleIntent) installIntent));
helenyrwue6aaa332016-08-05 15:41:42 -0700382 } else {
383 return false;
384 }
385 }).findFirst().ifPresent(common -> {
386 uninstallIntents.remove(common);
Brian O'Connor09d90f02016-09-13 11:06:14 -0700387 if (INSTALLED.equals(uninstall.state())) {
388 // only remove the install intent if the existing
389 // intent (i.e. the uninstall one) is already
390 // installed or installing
391 iterator.remove();
392 }
helenyrwue6aaa332016-08-05 15:41:42 -0700393 });
394 }
395
396 final IntentData newUninstall = new IntentData(uninstall, uninstallIntents);
397 final IntentData newInstall = new IntentData(install, installIntents);
398
399 trackerService.removeTrackedResources(newUninstall.key(), newUninstall.intent().resources());
400 uninstallIntents.forEach(installable ->
401 trackerService.removeTrackedResources(newUninstall.intent().key(),
402 installable.resources()));
403 trackerService.addTrackedResources(newInstall.key(), newInstall.intent().resources());
404 installIntents.forEach(installable ->
405 trackerService.addTrackedResources(newInstall.key(),
406 installable.resources()));
407 prepareIntents(uninstallIntents, Direction.REMOVE);
408 prepareIntents(installIntents, Direction.ADD);
409 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800410 }
411
Thomas Vachuska2980c972016-02-23 20:58:49 -0800412 /**
Yi Tseng424bfa72017-04-14 13:43:26 -0700413 * Determines whether there is any flow rule changed
414 * (i.e., different set of flow rules or different treatments)
415 * between FlowRuleIntents to be uninstalled and to be installed.
416 *
417 * @param uninstallIntent FlowRuleIntent to uninstall
418 * @param installIntent FlowRuleIntent to install
419 * @return true if flow rules which to be uninstalled
420 * contains all flow rules which to be installed.
421 */
422 private boolean flowRuleIntentChanged(FlowRuleIntent uninstallIntent,
423 FlowRuleIntent installIntent) {
424 Collection<FlowRule> flowRulesToUninstall = uninstallIntent.flowRules();
425 Collection<FlowRule> flowRulesToInstall = installIntent.flowRules();
426
427 // Check if any flow rule changed
428 for (FlowRule flowRuleToInstall : flowRulesToInstall) {
429 if (flowRulesToUninstall.stream().noneMatch(flowRuleToInstall::exactMatch)) {
430 return true;
431 }
432 }
433 return false;
434 }
435
436 /**
Thomas Vachuska2980c972016-02-23 20:58:49 -0800437 * Applies the specified intent data, if present, to the network using the
438 * specified context.
439 *
440 * @param intentData optional intent data; no-op if not present
441 * @param direction indicates adding or removal
442 */
443 private void prepareIntentData(Optional<IntentData> intentData, Direction direction) {
444 if (!intentData.isPresent()) {
445 return;
446 }
447
448 IntentData data = intentData.get();
449 List<Intent> intentsToApply = data.installables();
450 checkState(intentsToApply.stream().allMatch(this::isSupported),
Yuta HIGUCHId5324f32017-01-27 14:35:13 -0800451 "Unsupported installable intents detected: %s", intentsToApply);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800452
453 if (direction == Direction.ADD) {
454 trackerService.addTrackedResources(data.key(), data.intent().resources());
455 intentsToApply.forEach(installable ->
456 trackerService.addTrackedResources(data.key(),
457 installable.resources()));
458 } else {
459 trackerService.removeTrackedResources(data.key(), data.intent().resources());
460 intentsToApply.forEach(installable ->
461 trackerService.removeTrackedResources(data.intent().key(),
462 installable.resources()));
463 }
464
465 prepareIntents(intentsToApply, direction);
466 }
467
468 private boolean isSupported(Intent intent) {
Andreas Papazois05548962016-11-23 11:36:55 +0200469 return intent instanceof FlowRuleIntent ||
470 intent instanceof FlowObjectiveIntent ||
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200471 intent instanceof ProtectionEndpointIntent ||
472 intent instanceof DomainIntent;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800473 }
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800474
475 protected ToStringHelper toStringHelper() {
476 return MoreObjects.toStringHelper(this)
477 .add("intentContext", intentContext)
478 .add("toUninstall", toUninstall)
479 .add("toInstall", toInstall);
480 }
481
482 @Override
483 public String toString() {
484 return toStringHelper()
485 .toString();
486 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800487 }
488
489
Andreas Papazois05548962016-11-23 11:36:55 +0200490 // Context for applying and tracking operations related to flow rule intents.
Thomas Vachuska2980c972016-02-23 20:58:49 -0800491 private class FlowRuleOperationContext extends OperationContext {
492 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
493 FlowRuleOperationsContext flowRuleOperationsContext;
494
Andreas Papazois05548962016-11-23 11:36:55 +0200495 FlowRuleOperationContext(IntentInstallationContext context) {
496 super(context);
497 }
498
Yuta HIGUCHI4d19ab92016-10-27 16:27:15 -0700499 @Override
Thomas Vachuska2980c972016-02-23 20:58:49 -0800500 void apply() {
501 flowRuleOperationsContext = new FlowRuleOperationsContext() {
502 @Override
503 public void onSuccess(FlowRuleOperations ops) {
504 successConsumer.accept(FlowRuleOperationContext.this);
505 }
506
507 @Override
508 public void onError(FlowRuleOperations ops) {
509 errorConsumer.accept(FlowRuleOperationContext.this);
510 }
511 };
512 FlowRuleOperations operations = builder.build(flowRuleOperationsContext);
513
514 if (log.isTraceEnabled()) {
515 log.trace("applying intent {} -> {} with {} rules: {}",
516 toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
517 toInstall.map(x -> x.key().toString()).orElse("<empty>"),
518 operations.stages().stream().mapToLong(Set::size).sum(),
519 operations.stages());
520 }
521
522 flowRuleService.apply(operations);
523 }
524
525 @Override
526 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
527 // FIXME do FlowRuleIntents have stages??? Can we do uninstall work in parallel? I think so.
528 builder.newStage();
529
530 List<Collection<FlowRule>> stages = intentsToApply.stream()
Andreas Papazois05548962016-11-23 11:36:55 +0200531 .filter(x -> x instanceof FlowRuleIntent)
Thomas Vachuska2980c972016-02-23 20:58:49 -0800532 .map(x -> (FlowRuleIntent) x)
533 .map(FlowRuleIntent::flowRules)
534 .collect(Collectors.toList());
535
536 for (Collection<FlowRule> rules : stages) {
537 if (direction == Direction.ADD) {
538 rules.forEach(builder::add);
539 } else {
540 rules.forEach(builder::remove);
541 }
542 }
543
544 }
545
546 @Override
547 public Object error() {
548 return flowRuleOperationsContext;
549 }
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800550
551 @Override
552 protected ToStringHelper toStringHelper() {
553 return super.toStringHelper()
554 .omitNullValues()
555 .add("flowRuleOperationsContext", flowRuleOperationsContext);
556 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800557 }
558
559 // Context for applying and tracking operations related to flow objective intents.
560 private class FlowObjectiveOperationContext extends OperationContext {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800561 private static final String UNSUPPORT_OBJ = "unsupported objective {}";
562 final List<ObjectiveContext> contexts = Lists.newArrayList();
563
564 final Set<ObjectiveContext> pendingContexts = Sets.newConcurrentHashSet();
565
566 // Second stage of pending contexts
567 final Set<ObjectiveContext> nextPendingContexts = Sets.newConcurrentHashSet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800568 final Set<ObjectiveContext> errorContexts = Sets.newConcurrentHashSet();
569
Andreas Papazois05548962016-11-23 11:36:55 +0200570 FlowObjectiveOperationContext(IntentInstallationContext context) {
571 super(context);
572 }
573
Thomas Vachuska2980c972016-02-23 20:58:49 -0800574 @Override
575 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800576 intentsToApply
577 .stream()
578 .filter(intent -> intent instanceof FlowObjectiveIntent)
579 .map(intent -> buildObjectiveContexts((FlowObjectiveIntent) intent, direction))
580 .flatMap(Collection::stream)
581 .forEach(contexts::add);
582
583 // Two stage for different direction context
584 // We will apply REMOVE context first, and apply ADD context.
585 contexts.forEach(context -> {
586 switch (direction) {
587 case REMOVE:
588 pendingContexts.add(context);
589 break;
590 case ADD:
591 nextPendingContexts.add(context);
592 break;
593 default:
594 break;
595 }
596 });
Thomas Vachuska2980c972016-02-23 20:58:49 -0800597 }
598
599 // Builds the specified objective in the appropriate direction
Yi Tseng38fc71e2017-02-03 14:50:47 -0800600 private Set<? extends ObjectiveContext> buildObjectiveContexts(FlowObjectiveIntent intent,
601 Direction direction) {
602 Set<FlowObjectiveInstallationContext> contexts = Sets.newHashSet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800603 int size = intent.objectives().size();
Yi Tseng38fc71e2017-02-03 14:50:47 -0800604 List<Objective> objectives = intent.objectives();
605 List<DeviceId> deviceIds = intent.devices();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800606
Yi Tseng38fc71e2017-02-03 14:50:47 -0800607 if (direction == Direction.ADD) {
608 for (int i = 0; i < size; i++) {
609 Objective objective = objectives.get(i);
610 DeviceId deviceId = deviceIds.get(i);
611 FlowObjectiveInstallationContext ctx =
612 buildObjectiveContext(objective, deviceId, direction);
613 contexts.add(ctx);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800614 }
Yi Tseng38fc71e2017-02-03 14:50:47 -0800615 return contexts;
616 } else {
617 // we need to care about ordering here
618 // basic idea is to chain objective contexts
619 for (int i = 0; i < size; i++) {
620 Objective objective = intent.objectives().get(i);
621 DeviceId deviceId = intent.devices().get(i);
622
623 if (objective instanceof FilteringObjective) {
624 // don't need to care ordering of filtering objective
625 FlowObjectiveInstallationContext ctx =
626 buildObjectiveContext(objective, deviceId, direction);
627 contexts.add(ctx);
628 } else if (objective instanceof NextObjective) {
629 // need to removed after forwarding objective
630 // nothing to do here
631 } else if (objective instanceof ForwardingObjective) {
632 // forwarding objective, also find next objective if
633 // exist
634 FlowObjectiveInstallationContext fwdCtx =
635 buildObjectiveContext(objective, deviceId, direction);
636 ForwardingObjective fwd = (ForwardingObjective) objective;
637 NextObjective nxt = null;
638 Integer nextId = fwd.nextId();
639 if (nextId != null) {
640 for (int j = 0; j < size; j++) {
641 if (objectives.get(j).id() == nextId) {
642 nxt = (NextObjective) objectives.get(j);
643 break;
644 }
645 }
646 // if a next objective exists in the Intent
647 if (nxt != null) {
648 FlowObjectiveInstallationContext nxtCtx =
649 buildObjectiveContext(nxt, deviceId, direction);
650 fwdCtx.nextContext(nxtCtx);
651 }
652 }
653 contexts.add(fwdCtx);
654 } else {
655 // possible here?
656 log.warn(UNSUPPORT_OBJ, objective);
657 }
658 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800659 }
660 return contexts;
661 }
662
Yi Tseng38fc71e2017-02-03 14:50:47 -0800663 private FlowObjectiveInstallationContext buildObjectiveContext(Objective objective,
664 DeviceId deviceId,
665 Direction direction) {
666 Objective.Builder builder = objective.copy();
667 FlowObjectiveInstallationContext ctx = new FlowObjectiveInstallationContext();
668 switch (direction) {
669 case ADD:
670 objective = builder.add(ctx);
671 break;
672 case REMOVE:
673 objective = builder.remove(ctx);
674 break;
675 default:
676 break;
677 }
678 ctx.setObjective(objective, deviceId);
679 return ctx;
680 }
681
Thomas Vachuska2980c972016-02-23 20:58:49 -0800682 @Override
683 void apply() {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800684 // If there is no pending contexts, try apply second stage
685 // pending contexts
686 if (pendingContexts.isEmpty()) {
687 pendingContexts.addAll(nextPendingContexts);
688 nextPendingContexts.clear();
689 }
690 final Set<ObjectiveContext> contextsToApply = Sets.newHashSet(pendingContexts);
691 contextsToApply.forEach(ctx -> {
692 FlowObjectiveInstallationContext foiCtx =
693 (FlowObjectiveInstallationContext) ctx;
694
695 flowObjectiveService.apply(foiCtx.deviceId, foiCtx.objective);
696 });
Thomas Vachuska2980c972016-02-23 20:58:49 -0800697 }
698
699 @Override
700 public Object error() {
701 return errorContexts;
702 }
703
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -0800704 @Override
705 protected ToStringHelper toStringHelper() {
706 return super.toStringHelper()
707 .add("contexts", contexts)
708 .add("pendingContexts", pendingContexts)
709 .add("errorContexts", errorContexts);
710 }
711
Thomas Vachuska2980c972016-02-23 20:58:49 -0800712 private class FlowObjectiveInstallationContext implements ObjectiveContext {
713 Objective objective;
714 DeviceId deviceId;
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700715 ObjectiveError error;
Yi Tseng38fc71e2017-02-03 14:50:47 -0800716 AtomicInteger retry;
717 FlowObjectiveInstallationContext nextContext;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800718
719 void setObjective(Objective objective, DeviceId deviceId) {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800720 // init function
Thomas Vachuska2980c972016-02-23 20:58:49 -0800721 this.objective = objective;
722 this.deviceId = deviceId;
Yi Tseng38fc71e2017-02-03 14:50:47 -0800723 this.error = null;
724 this.retry = new AtomicInteger(0);
725 this.nextContext = null;
Thomas Vachuska2980c972016-02-23 20:58:49 -0800726 }
727
Yi Tseng38fc71e2017-02-03 14:50:47 -0800728 int retryTimes() {
729 return this.retry.get();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800730 }
731
Yi Tseng38fc71e2017-02-03 14:50:47 -0800732 void increaseRetryValue() {
733 this.retry.incrementAndGet();
Thomas Vachuska2980c972016-02-23 20:58:49 -0800734 }
735
Yi Tseng38fc71e2017-02-03 14:50:47 -0800736 private void finished(ObjectiveError error) {
737
Thomas Vachuska2980c972016-02-23 20:58:49 -0800738 synchronized (pendingContexts) {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800739 if (error != null) {
740 this.error = error;
741 handleObjectiveError(this, error);
742 } else {
743 // apply next context if exist
744 if (nextContext != null) {
745 pendingContexts.add(nextContext);
746 flowObjectiveService.apply(nextContext.deviceId,
747 nextContext.objective);
748 pendingContexts.remove(this);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800749 } else {
Yi Tseng38fc71e2017-02-03 14:50:47 -0800750 pendingContexts.remove(this);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800751 }
752 }
Yi Tseng38fc71e2017-02-03 14:50:47 -0800753 if (!pendingContexts.isEmpty()) {
754 return;
755 }
756 // Apply second stage pending contexts if it is not empty
757 if (!nextPendingContexts.isEmpty()) {
758 pendingContexts.addAll(nextPendingContexts);
759 nextPendingContexts.clear();
760 final Set<ObjectiveContext> contextsToApply =
761 Sets.newHashSet(pendingContexts);
762 contextsToApply.forEach(ctx -> {
763 FlowObjectiveInstallationContext foiCtx =
764 (FlowObjectiveInstallationContext) ctx;
765 flowObjectiveService.apply(foiCtx.deviceId,
766 foiCtx.objective);
767 });
768 return;
769 }
770 if (errorContexts.isEmpty()) {
771 successConsumer.accept(FlowObjectiveOperationContext.this);
772 } else {
773 errorConsumer.accept(FlowObjectiveOperationContext.this);
774 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800775 }
776 }
777
778 @Override
Yi Tseng38fc71e2017-02-03 14:50:47 -0800779 public void onSuccess(Objective objective) {
780 finished(null);
781 }
782
783 @Override
784 public void onError(Objective objective, ObjectiveError error) {
785 finished(error);
786 }
787
788 @Override
Thomas Vachuska2980c972016-02-23 20:58:49 -0800789 public String toString() {
Thomas Vachuskad27097c2016-06-14 19:10:41 -0700790 return String.format("(%s on %s for %s)", error, deviceId, objective);
Thomas Vachuska2980c972016-02-23 20:58:49 -0800791 }
Yi Tseng38fc71e2017-02-03 14:50:47 -0800792
793 public void nextContext(FlowObjectiveInstallationContext nextContext) {
794 this.nextContext = nextContext;
795 }
796 }
797
798 private void handleObjectiveError(FlowObjectiveInstallationContext ctx,
799 ObjectiveError error) {
800 log.debug("Got error(s) when install objective: {}, error: {}, retry: {}",
801 ctx.objective, ctx.error, ctx.retry);
802 if (ctx.retryTimes() > OBJECTIVE_RETRY_THRESHOLD) {
803 ctx.error = INSTALLATIONTHRESHOLDEXCEEDED;
804 errorContexts.add(ctx);
805 return;
806 }
807 // reset error
808 ctx.error = null;
809 // strategies for errors
810 switch (error) {
811 case GROUPEXISTS:
812 if (ctx.objective.op() == Objective.Operation.ADD) {
813 // Next group exists
814 // build new objective with new op ADD_TO_EXIST
815 NextObjective newObj =
816 ((NextObjective.Builder) ctx.objective.copy()).addToExisting(ctx);
817 ctx.setObjective(newObj, ctx.deviceId);
818 ctx.increaseRetryValue();
819 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
820 } else {
821 pendingContexts.remove(ctx);
822 errorContexts.add(ctx);
823 }
824 break;
825 case GROUPINSTALLATIONFAILED:
826 // Group install failed, retry again
827 ctx.increaseRetryValue();
828 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
829 break;
830 case GROUPMISSING:
831 if (ctx.objective.op() == Objective.Operation.ADD_TO_EXISTING) {
832 // Next group not exist, but we want to add new buckets
833 // build new objective with new op ADD
834 NextObjective newObj = (NextObjective) ctx.objective.copy().add(ctx);
835 ctx.setObjective(newObj, ctx.deviceId);
836 ctx.increaseRetryValue();
837 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
838 } else if (ctx.objective.op() == Objective.Operation.REMOVE ||
839 ctx.objective.op() == Objective.Operation.REMOVE_FROM_EXISTING) {
840 // Already removed, no need to do anything
841 ctx.error = null;
842 pendingContexts.remove(ctx);
843 return;
844 } else {
845 // Next chaining group missing, try again.
846 ctx.increaseRetryValue();
847 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
848 }
849 break;
850 case FLOWINSTALLATIONFAILED:
851 case GROUPREMOVALFAILED:
852 case INSTALLATIONTIMEOUT:
853 // Retry
854 ctx.increaseRetryValue();
855 flowObjectiveService.apply(ctx.deviceId, ctx.objective);
856 break;
857 default:
858 pendingContexts.remove(ctx);
859 errorContexts.add(ctx);
860 break;
861 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800862 }
863 }
864
Andreas Papazoisf00087c2016-11-23 11:36:55 +0200865 // Context for applying and tracking operations related to domain intents.
866 private class DomainIntentOperationContext extends OperationContext {
867 DomainIntentOperations.Builder builder = DomainIntentOperations.builder();
868 DomainIntentOperationsContext domainOperationsContext;
869
870 DomainIntentOperationContext(IntentInstallationContext context) {
871 super(context);
872 }
873 @Override
874 void apply() {
875 domainOperationsContext = new DomainIntentOperationsContext() {
876 @Override
877 public void onSuccess(DomainIntentOperations ops) {
878 successConsumer.accept(DomainIntentOperationContext.this);
879 }
880
881 @Override
882 public void onError(DomainIntentOperations ops) {
883 errorConsumer.accept(DomainIntentOperationContext.this);
884 }
885 };
886 DomainIntentOperations operations = builder.build(domainOperationsContext);
887
888 if (log.isTraceEnabled()) {
889 log.trace("submitting domain intent {} -> {}",
890 toUninstall.map(x -> x.key().toString()).orElse("<empty>"),
891 toInstall.map(x -> x.key().toString()).orElse("<empty>"));
892 }
893 domainIntentService.sumbit(operations);
894 }
895
896 @Override
897 public void prepareIntents(List<Intent> intentsToApply, Direction direction) {
898 List<DomainIntent> intents = intentsToApply.stream()
899 .filter(x -> x instanceof DomainIntent)
900 .map(x -> (DomainIntent) x)
901 .collect(Collectors.toList());
902
903 for (DomainIntent intent : intents) {
904 if (direction == Direction.ADD) {
905 builder.add(intent);
906 } else {
907 builder.remove(intent);
908 }
909 }
910 }
911
912 @Override
913 public Object error() {
914 return domainOperationsContext;
915 }
916 }
917
Thomas Vachuska2980c972016-02-23 20:58:49 -0800918 private class ErrorContext extends OperationContext {
Andreas Papazois05548962016-11-23 11:36:55 +0200919 ErrorContext(IntentInstallationContext context) {
920 super(context);
921 }
Thomas Vachuska2980c972016-02-23 20:58:49 -0800922 @Override
923 void apply() {
924 throw new UnsupportedOperationException("Unsupported installable intent");
925 }
926
927 @Override
928 Object error() {
929 return null;
930 }
931
932 @Override
933 void prepareIntents(List<Intent> intentsToApply, Direction direction) {
934 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -0800935 }
Andreas Papazois05548962016-11-23 11:36:55 +0200936
937
938 /**
939 * Context for applying and tracking operations related to
940 * {@link ProtectionEndpointIntent}.
941 */
942 @Beta
943 private class ProtectionConfigOperationContext extends OperationContext {
944
945 ProtectionConfigOperationContext(IntentInstallationContext context) {
946 super(context);
947 }
948
949 /**
950 * Stage of installable Intents which can be processed in parallel.
951 */
952 private final class Stage {
953 // should it have progress state, how far it went?
954 private final Collection<Pair<ProtectionEndpointIntent, Direction>> ops;
955
956 Stage(Collection<Pair<ProtectionEndpointIntent, Direction>> ops) {
957 this.ops = checkNotNull(ops);
958 }
959
960 CompletableFuture<Void> apply() {
961 return ops.stream()
962 .map(op -> applyOp(op.getRight(), op.getLeft()))
963 .reduce(CompletableFuture.completedFuture(null),
964 (l, r) -> {
965 l.join();
966 return r;
967 });
968 }
969
970 private CompletableFuture<Void> applyOp(Direction dir, ProtectionEndpointIntent intent) {
971 log.trace("applying {}: {}", dir, intent);
972 if (dir == Direction.REMOVE) {
973 networkConfigService.removeConfig(intent.deviceId(), ProtectionConfig.class);
974 } else if (dir == Direction.ADD) {
975 ProtectedTransportEndpointDescription description = intent.description();
976
977 // Can't do following. Will trigger empty CONFIG_ADDED
978 //ProtectionConfig cfg = networkConfigService.addConfig(intent.deviceId(),
979 // ProtectionConfig.class);
980 ProtectionConfig cfg = new ProtectionConfig(intent.deviceId());
981 cfg.fingerprint(description.fingerprint());
982 cfg.peer(description.peer());
983 cfg.paths(description.paths());
984 //cfg.apply();
985
986 networkConfigService.applyConfig(intent.deviceId(),
987 ProtectionConfig.class,
988 cfg.node());
989 }
990 // TODO Should monitor progress and complete only after it's
991 // actually done.
992 return CompletableFuture.completedFuture(null);
993 }
994
995 @Override
996 public String toString() {
997 return ops.toString();
998 }
999 }
1000
1001 /**
1002 * List of Stages which must be executed in order.
1003 */
1004 private final List<Stage> stages = new ArrayList<>();
1005
1006 private final List<Stage> failed = new CopyOnWriteArrayList<>();
1007
1008 @Override
1009 synchronized void apply() {
1010 for (Stage stage : stages) {
1011 log.trace("applying Stage {}", stage);
1012 CompletableFuture<Void> result = stage.apply();
1013 // wait for stage completion
1014 result.join();
1015 if (result.isCompletedExceptionally()) {
1016 log.error("Stage {} failed", stage);
1017 failed.add(stage);
1018 errorConsumer.accept(ProtectionConfigOperationContext.this);
1019 return;
1020 }
1021 }
1022 successConsumer.accept(ProtectionConfigOperationContext.this);
1023 }
1024
1025 @Override
1026 Object error() {
1027 // Something to represent error state
1028 return failed;
1029 }
1030
1031 @Override
1032 synchronized void prepareIntents(List<Intent> intentsToApply,
1033 Direction direction) {
1034
1035 stages.add(new Stage(intentsToApply.stream()
1036 .filter(i -> i instanceof ProtectionEndpointIntent)
1037 .map(i -> Pair.of((ProtectionEndpointIntent) i, direction))
1038 .collect(Collectors.toList())));
1039 }
1040
Yuta HIGUCHI63bbedd2017-01-30 10:49:29 -08001041 @Override
1042 protected ToStringHelper toStringHelper() {
1043 return super.toStringHelper()
1044 .add("stages", stages)
1045 .add("failed", failed);
1046 }
Andreas Papazois05548962016-11-23 11:36:55 +02001047 }
Thomas Vachuskaf6ec97b2016-02-22 10:59:23 -08001048}