blob: 56ea65092a57c0d6effad6c0dd2f218feddfba28 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connor66630c82014-10-02 21:08:19 -070016package org.onlab.onos.net.intent.impl;
17
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableMap;
20import com.google.common.collect.Lists;
21import com.google.common.collect.Maps;
Brian O'Connor66630c82014-10-02 21:08:19 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.apache.felix.scr.annotations.Service;
Brian O'Connor72a034c2014-11-26 18:24:23 -080028import org.onlab.onos.core.ApplicationId;
Brian O'Connor520c0522014-11-23 23:50:47 -080029import org.onlab.onos.core.CoreService;
30import org.onlab.onos.core.IdGenerator;
Brian O'Connor66630c82014-10-02 21:08:19 -070031import org.onlab.onos.event.AbstractListenerRegistry;
32import org.onlab.onos.event.EventDeliveryService;
Brian O'Connorcb900f42014-10-07 21:55:33 -070033import org.onlab.onos.net.flow.CompletedBatchOperation;
Brian O'Connorf2dbde52014-10-10 16:20:24 -070034import org.onlab.onos.net.flow.FlowRuleBatchOperation;
35import org.onlab.onos.net.flow.FlowRuleService;
Brian O'Connor66630c82014-10-02 21:08:19 -070036import org.onlab.onos.net.intent.Intent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070037import org.onlab.onos.net.intent.IntentBatchDelegate;
38import org.onlab.onos.net.intent.IntentBatchService;
Brian O'Connor66630c82014-10-02 21:08:19 -070039import org.onlab.onos.net.intent.IntentCompiler;
40import org.onlab.onos.net.intent.IntentEvent;
41import org.onlab.onos.net.intent.IntentException;
42import org.onlab.onos.net.intent.IntentExtensionService;
43import org.onlab.onos.net.intent.IntentId;
44import org.onlab.onos.net.intent.IntentInstaller;
45import org.onlab.onos.net.intent.IntentListener;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070046import org.onlab.onos.net.intent.IntentOperation;
Brian O'Connor66630c82014-10-02 21:08:19 -070047import org.onlab.onos.net.intent.IntentOperations;
48import org.onlab.onos.net.intent.IntentService;
49import org.onlab.onos.net.intent.IntentState;
50import org.onlab.onos.net.intent.IntentStore;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080051import org.onlab.onos.net.intent.IntentStore.BatchWrite;
Brian O'Connor66630c82014-10-02 21:08:19 -070052import org.onlab.onos.net.intent.IntentStoreDelegate;
53import org.slf4j.Logger;
54
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080055import java.util.ArrayList;
56import java.util.Collections;
57import java.util.EnumSet;
58import java.util.List;
59import java.util.Map;
60import java.util.concurrent.ConcurrentHashMap;
61import java.util.concurrent.ConcurrentMap;
62import java.util.concurrent.ExecutionException;
63import java.util.concurrent.ExecutorService;
64import java.util.concurrent.Future;
65import java.util.concurrent.TimeUnit;
66import java.util.concurrent.TimeoutException;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070067
68import static com.google.common.base.Preconditions.checkArgument;
69import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080070import static java.util.concurrent.Executors.newFixedThreadPool;
71import static org.onlab.onos.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070072import static org.onlab.util.Tools.namedThreads;
73import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070074
75/**
76 * An implementation of Intent Manager.
77 */
78@Component(immediate = true)
79@Service
80public class IntentManager
81 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080082 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070083
84 public static final String INTENT_NULL = "Intent cannot be null";
85 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
86
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080087 private static final int NUM_THREADS = 12;
88
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080089 private static final EnumSet<IntentState> RECOMPILE
90 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
91 private static final EnumSet<IntentState> NON_PARKED_OR_FAILED
92 = EnumSet.complementOf(EnumSet.of(INSTALL_REQ, INSTALLED, WITHDRAW_REQ, WITHDRAWN));
93
94
Brian O'Connor66630c82014-10-02 21:08:19 -070095 // Collections for compiler, installer, and listener are ONOS instance local
96 private final ConcurrentMap<Class<? extends Intent>,
97 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070098 private final ConcurrentMap<Class<? extends Intent>,
99 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700100
101 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700102 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700103
Brian O'Connor520c0522014-11-23 23:50:47 -0800104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected IntentStore store;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700111 protected IntentBatchService batchService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700114 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700117 protected EventDeliveryService eventDispatcher;
118
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected FlowRuleService flowRuleService;
121
Brian O'Connor520c0522014-11-23 23:50:47 -0800122
123 private ExecutorService executor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800124
125 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
126 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
127 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
128 private IdGenerator idGenerator;
129
Brian O'Connor66630c82014-10-02 21:08:19 -0700130 @Activate
131 public void activate() {
132 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700133 trackerService.setDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700134 batchService.setDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700135 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
alshabiba9819bf2014-11-30 18:15:52 -0800136 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800137 idGenerator = coreService.getIdGenerator("intent-ids");
138 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700139 log.info("Started");
140 }
141
142 @Deactivate
143 public void deactivate() {
144 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700145 trackerService.unsetDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700146 batchService.unsetDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700147 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700148 executor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800149 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700150 log.info("Stopped");
151 }
152
153 @Override
154 public void submit(Intent intent) {
155 checkNotNull(intent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800156 execute(IntentOperations.builder(intent.appId())
157 .addSubmitOperation(intent).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700158 }
159
160 @Override
161 public void withdraw(Intent intent) {
162 checkNotNull(intent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800163 execute(IntentOperations.builder(intent.appId())
164 .addWithdrawOperation(intent.id()).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700165 }
166
Brian O'Connor66630c82014-10-02 21:08:19 -0700167 @Override
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700168 public void replace(IntentId oldIntentId, Intent newIntent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700169 checkNotNull(oldIntentId, INTENT_ID_NULL);
170 checkNotNull(newIntent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800171 execute(IntentOperations.builder(newIntent.appId())
Ray Milkeye97ede92014-11-20 10:43:12 -0800172 .addReplaceOperation(oldIntentId, newIntent)
173 .build());
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700174 }
175
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700176 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700177 public void execute(IntentOperations operations) {
Brian O'Connore2ff25a2014-11-18 19:25:43 -0800178 if (operations.operations().isEmpty()) {
179 return;
180 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700181 batchService.addIntentOperations(operations);
Brian O'Connor66630c82014-10-02 21:08:19 -0700182 }
183
184 @Override
185 public Iterable<Intent> getIntents() {
186 return store.getIntents();
187 }
188
189 @Override
190 public long getIntentCount() {
191 return store.getIntentCount();
192 }
193
194 @Override
195 public Intent getIntent(IntentId id) {
196 checkNotNull(id, INTENT_ID_NULL);
197 return store.getIntent(id);
198 }
199
200 @Override
201 public IntentState getIntentState(IntentId id) {
202 checkNotNull(id, INTENT_ID_NULL);
203 return store.getIntentState(id);
204 }
205
206 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700207 public List<Intent> getInstallableIntents(IntentId intentId) {
208 checkNotNull(intentId, INTENT_ID_NULL);
209 return store.getInstallableIntents(intentId);
210 }
211
212 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700213 public void addListener(IntentListener listener) {
214 listenerRegistry.addListener(listener);
215 }
216
217 @Override
218 public void removeListener(IntentListener listener) {
219 listenerRegistry.removeListener(listener);
220 }
221
222 @Override
223 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
224 compilers.put(cls, compiler);
225 }
226
227 @Override
228 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
229 compilers.remove(cls);
230 }
231
232 @Override
233 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
234 return ImmutableMap.copyOf(compilers);
235 }
236
237 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700238 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700239 installers.put(cls, installer);
240 }
241
242 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700243 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700244 installers.remove(cls);
245 }
246
247 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700248 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700249 return ImmutableMap.copyOf(installers);
250 }
251
252 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 * Returns the corresponding intent compiler to the specified intent.
254 *
255 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700256 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700257 * @return intent compiler corresponding to the specified intent
258 */
259 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
260 @SuppressWarnings("unchecked")
261 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
262 if (compiler == null) {
263 throw new IntentException("no compiler for class " + intent.getClass());
264 }
265 return compiler;
266 }
267
268 /**
269 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700270 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700271 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700272 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700273 * @return intent installer corresponding to the specified installable intent
274 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700275 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700276 @SuppressWarnings("unchecked")
277 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
278 if (installer == null) {
279 throw new IntentException("no installer for class " + intent.getClass());
280 }
281 return installer;
282 }
283
284 /**
tom85258ee2014-10-07 00:10:02 -0700285 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700286 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700287 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700288 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700289 private void executeCompilingPhase(IntentUpdate update) {
290 Intent intent = update.newIntent();
tom85258ee2014-10-07 00:10:02 -0700291 // Indicate that the intent is entering the compiling phase.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800292 update.setInflightState(intent, COMPILING);
tom85258ee2014-10-07 00:10:02 -0700293
294 try {
295 // Compile the intent into installable derivatives.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700296 List<Intent> installables = compileIntent(intent, update);
tom85258ee2014-10-07 00:10:02 -0700297
298 // If all went well, associate the resulting list of installable
299 // intents with the top-level intent and proceed to install.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700300 update.setInstallables(installables);
Brian O'Connora44dda52014-12-01 17:42:41 -0800301 } catch (PathNotFoundException e) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800302 log.debug("Path not found for intent {}", intent);
Ray Milkey93508c22014-12-02 11:35:56 -0800303 update.setInflightState(intent, FAILED);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700304 } catch (IntentException e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700305 log.warn("Unable to compile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700306
tom85258ee2014-10-07 00:10:02 -0700307 // If compilation failed, mark the intent as failed.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800308 update.setInflightState(intent, FAILED);
tom85258ee2014-10-07 00:10:02 -0700309 }
310 }
311
Brian O'Connorcb900f42014-10-07 21:55:33 -0700312 /**
313 * Compiles an intent recursively.
314 *
315 * @param intent intent
316 * @return result of compilation
317 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700318 private List<Intent> compileIntent(Intent intent, IntentUpdate update) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700319 if (intent.isInstallable()) {
320 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700321 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700322
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700323 registerSubclassCompilerIfNeeded(intent);
324 List<Intent> previous = update.oldInstallables();
325 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700326 List<Intent> installable = new ArrayList<>();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700327 for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) {
328 installable.addAll(compileIntent(compiled, update));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700329 }
tom85258ee2014-10-07 00:10:02 -0700330 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700331 }
332
333 /**
tom85258ee2014-10-07 00:10:02 -0700334 * Installs all installable intents associated with the specified top-level
335 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700336 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700337 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700338 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700339 private void executeInstallingPhase(IntentUpdate update) {
340 if (update.newInstallables() == null) {
341 //no failed intents allowed past this point...
342 return;
tom85258ee2014-10-07 00:10:02 -0700343 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700344 // Indicate that the intent is entering the installing phase.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800345 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700346
347 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
348 for (Intent installable : update.newInstallables()) {
349 registerSubclassInstallerIfNeeded(installable);
350 trackerService.addTrackedResources(update.newIntent().id(),
351 installable.resources());
352 try {
353 batches.addAll(getInstaller(installable).install(installable));
Ray Milkey93508c22014-12-02 11:35:56 -0800354 } catch (Exception e) { // TODO this should be IntentException
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700355 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
Brian O'Connor427a1762014-11-19 18:40:32 -0800356 trackerService.removeTrackedResources(update.newIntent().id(),
357 installable.resources());
Ray Milkey93508c22014-12-02 11:35:56 -0800358 //TODO we failed; intent should be recompiled
359 update.setInflightState(update.newIntent(), FAILED);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700360 }
361 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800362 update.addBatches(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700363 }
364
365 /**
366 * Uninstalls the specified intent by uninstalling all of its associated
367 * installable derivatives.
368 *
369 * @param update intent update
370 */
371 private void executeWithdrawingPhase(IntentUpdate update) {
372 if (!update.oldIntent().equals(update.newIntent())) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800373 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700374 } // else newIntent is FAILED
Brian O'Connor427a1762014-11-19 18:40:32 -0800375 update.addBatches(uninstallIntent(update.oldIntent(), update.oldInstallables()));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700376 }
377
378 /**
379 * Uninstalls all installable intents associated with the given intent.
380 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800381 * @param intent intent
382 * @param installables installable intents
383 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700384 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800385 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
386 if (installables == null) {
387 return Collections.emptyList();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700388 }
389 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800390 for (Intent installable : installables) {
391 trackerService.removeTrackedResources(intent.id(),
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700392 installable.resources());
393 try {
394 batches.addAll(getInstaller(installable).uninstall(installable));
395 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800396 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700397 // TODO: this should never happen. but what if it does?
398 }
399 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800400 return batches;
tom85258ee2014-10-07 00:10:02 -0700401 }
402
403 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700404 * Withdraws the old intent and installs the new intent as one operation.
tom85258ee2014-10-07 00:10:02 -0700405 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700406 * @param update intent update
tom85258ee2014-10-07 00:10:02 -0700407 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700408 private void executeReplacementPhase(IntentUpdate update) {
409 checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
410 "Old and New Intent must have equivalent installable intents.");
411 if (!update.oldIntent().equals(update.newIntent())) {
412 // only set the old intent's state if it is different
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800413 update.setInflightState(update.oldIntent(), WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700414 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800415 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700416
417 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
418 for (int i = 0; i < update.oldInstallables().size(); i++) {
419 Intent oldInstallable = update.oldInstallables().get(i);
420 Intent newInstallable = update.newInstallables().get(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800421 //FIXME revisit this
422// if (oldInstallable.equals(newInstallable)) {
423// continue;
424// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700425 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
426 "Installable Intent type mismatch.");
427 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
428 trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
429 try {
430 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
431 } catch (IntentException e) {
432 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
433 //FIXME... we failed. need to uninstall (if same) or revert (if different)
Brian O'Connor427a1762014-11-19 18:40:32 -0800434 trackerService.removeTrackedResources(update.newIntent().id(), newInstallable.resources());
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800435 update.setInflightState(update.newIntent(), FAILED);
Brian O'Connor427a1762014-11-19 18:40:32 -0800436 batches = uninstallIntent(update.oldIntent(), update.oldInstallables());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700437 }
438 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800439 update.addBatches(batches);
tom53945d52014-10-07 11:01:36 -0700440 }
441
442 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700443 * Registers an intent compiler of the specified intent if an intent compiler
444 * for the intent is not registered. This method traverses the class hierarchy of
445 * the intent. Once an intent compiler for a parent type is found, this method
446 * registers the found intent compiler.
447 *
448 * @param intent intent
449 */
450 private void registerSubclassCompilerIfNeeded(Intent intent) {
451 if (!compilers.containsKey(intent.getClass())) {
452 Class<?> cls = intent.getClass();
453 while (cls != Object.class) {
454 // As long as we're within the Intent class descendants
455 if (Intent.class.isAssignableFrom(cls)) {
456 IntentCompiler<?> compiler = compilers.get(cls);
457 if (compiler != null) {
458 compilers.put(intent.getClass(), compiler);
459 return;
460 }
461 }
462 cls = cls.getSuperclass();
463 }
464 }
465 }
466
467 /**
468 * Registers an intent installer of the specified intent if an intent installer
469 * for the intent is not registered. This method traverses the class hierarchy of
470 * the intent. Once an intent installer for a parent type is found, this method
471 * registers the found intent installer.
472 *
473 * @param intent intent
474 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700475 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700476 if (!installers.containsKey(intent.getClass())) {
477 Class<?> cls = intent.getClass();
478 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700479 // As long as we're within the Intent class descendants
480 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700481 IntentInstaller<?> installer = installers.get(cls);
482 if (installer != null) {
483 installers.put(intent.getClass(), installer);
484 return;
485 }
486 }
487 cls = cls.getSuperclass();
488 }
489 }
490 }
491
Brian O'Connor66630c82014-10-02 21:08:19 -0700492 // Store delegate to re-post events emitted from the store.
493 private class InternalStoreDelegate implements IntentStoreDelegate {
494 @Override
495 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700496 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700497 }
498 }
499
Brian O'Connor72a034c2014-11-26 18:24:23 -0800500 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
501 boolean compileAllFailed) {
502 Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
503 // Attempt recompilation of the specified intents first.
504 for (IntentId id : intentIds) {
505 Intent intent = store.getIntent(id);
506 if (intent == null) {
507 continue;
508 }
509 IntentOperations.Builder builder = batches.get(intent.appId());
510 if (builder == null) {
511 builder = IntentOperations.builder(intent.appId());
512 batches.put(intent.appId(), builder);
513 }
514 builder.addUpdateOperation(id);
515 }
516
517 if (compileAllFailed) {
518 // If required, compile all currently failed intents.
519 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800520 IntentState state = getIntentState(intent.id());
521 if (RECOMPILE.contains(state)) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800522 IntentOperations.Builder builder = batches.get(intent.appId());
523 if (builder == null) {
524 builder = IntentOperations.builder(intent.appId());
525 batches.put(intent.appId(), builder);
526 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800527 if (state == WITHDRAW_REQ) {
528 builder.addWithdrawOperation(intent.id());
529 } else {
530 builder.addUpdateOperation(intent.id());
531 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800532 }
533 }
534 }
535
536 for (ApplicationId appId : batches.keySet()) {
537 if (batchService.isLocalLeader(appId)) {
538 execute(batches.get(appId).build());
539 }
540 }
541 }
542
tom95329eb2014-10-06 08:40:06 -0700543 // Topology change delegate
544 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
545 @Override
tom85258ee2014-10-07 00:10:02 -0700546 public void triggerCompile(Iterable<IntentId> intentIds,
547 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800548 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700549 }
tom95329eb2014-10-06 08:40:06 -0700550 }
tom85258ee2014-10-07 00:10:02 -0700551
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800552 // TODO move this inside IntentUpdate?
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700553 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800554 * TODO. rename this...
555 * @param update intent update
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700556 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800557 private void processIntentUpdate(IntentUpdate update) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700558
Brian O'Connor427a1762014-11-19 18:40:32 -0800559 // check to see if the intent needs to be compiled or recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700560 if (update.newIntent() != null) {
561 executeCompilingPhase(update);
562 }
563
564 if (update.oldInstallables() != null && update.newInstallables() != null) {
565 executeReplacementPhase(update);
566 } else if (update.newInstallables() != null) {
567 executeInstallingPhase(update);
568 } else if (update.oldInstallables() != null) {
569 executeWithdrawingPhase(update);
570 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800571 if (update.oldIntent() != null &&
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800572 !update.oldIntent().equals(update.newIntent())) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800573 // removing failed intent
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800574 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700575 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800576// if (update.newIntent() != null) {
577// // TODO assert that next state is failed
578// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700579 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700580 }
581
582 // TODO comments...
583 private class IntentUpdate {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700584 private final Intent oldIntent;
585 private final Intent newIntent;
586 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
587
588 private final List<Intent> oldInstallables;
589 private List<Intent> newInstallables;
Brian O'Connor427a1762014-11-19 18:40:32 -0800590 private final List<FlowRuleBatchOperation> batches = Lists.newLinkedList();
591 private int currentBatch = 0; // TODO: maybe replace with an iterator
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700592
593 IntentUpdate(IntentOperation op) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700594 switch (op.type()) {
595 case SUBMIT:
596 newIntent = op.intent();
597 oldIntent = null;
598 break;
599 case WITHDRAW:
600 newIntent = null;
601 oldIntent = store.getIntent(op.intentId());
602 break;
603 case REPLACE:
604 newIntent = op.intent();
605 oldIntent = store.getIntent(op.intentId());
606 break;
607 case UPDATE:
608 oldIntent = store.getIntent(op.intentId());
Brian O'Connor427a1762014-11-19 18:40:32 -0800609 newIntent = oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700610 break;
611 default:
612 oldIntent = null;
613 newIntent = null;
614 break;
615 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700616 // fetch the old intent's installables from the store
617 if (oldIntent != null) {
618 oldInstallables = store.getInstallableIntents(oldIntent.id());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700619 } else {
620 oldInstallables = null;
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800621 if (newIntent == null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800622 log.info("Ignoring {} for missing Intent {}", op.type(), op.intentId());
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800623 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700624 }
625 }
626
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800627 void init(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800628 if (newIntent != null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800629 // TODO consider only "creating" intent if it does not exist
630 // Note: We need to set state to INSTALL_REQ regardless.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800631 batchWrite.createIntent(newIntent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800632 } else if (oldIntent != null && !oldIntent.equals(newIntent)) {
633 batchWrite.setState(oldIntent, WITHDRAW_REQ);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800634 }
635 }
636
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700637 Intent oldIntent() {
638 return oldIntent;
639 }
640
641 Intent newIntent() {
642 return newIntent;
643 }
644
645 List<Intent> oldInstallables() {
646 return oldInstallables;
647 }
648
649 List<Intent> newInstallables() {
650 return newInstallables;
651 }
652
653 void setInstallables(List<Intent> installables) {
654 newInstallables = installables;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700655 }
656
Brian O'Connor427a1762014-11-19 18:40:32 -0800657 boolean isComplete() {
658 return currentBatch >= batches.size();
659 }
660
661 FlowRuleBatchOperation currentBatch() {
662 return !isComplete() ? batches.get(currentBatch) : null;
663 }
664
Ray Milkey93508c22014-12-02 11:35:56 -0800665 void batchSuccess() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800666 // move on to next Batch
Ray Milkey93508c22014-12-02 11:35:56 -0800667 currentBatch++;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800668 }
669
670 void batchFailed() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800671 // the current batch has failed, so recompile
672 // remove the current batch and all remaining
673 for (int i = currentBatch; i < batches.size(); i++) {
674 batches.remove(i);
675 }
676 if (oldIntent != null) {
677 executeWithdrawingPhase(this); // remove the old intent
678 }
679 if (newIntent != null) {
680 setInflightState(newIntent, FAILED);
681 batches.addAll(uninstallIntent(newIntent, newInstallables()));
682 }
683
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800684 // TODO we might want to try to recompile the new intent
Brian O'Connor427a1762014-11-19 18:40:32 -0800685 }
686
alshabiba9819bf2014-11-30 18:15:52 -0800687 private void finalizeStates(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800688 // events to be triggered on successful write
Brian O'Connor427a1762014-11-19 18:40:32 -0800689 for (Intent intent : stateMap.keySet()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800690 switch (getInflightState(intent)) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800691 case INSTALLING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800692 batchWrite.setState(intent, INSTALLED);
693 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Brian O'Connor427a1762014-11-19 18:40:32 -0800694 break;
695 case WITHDRAWING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800696 batchWrite.setState(intent, WITHDRAWN);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800697 batchWrite.removeInstalledIntents(intent.id());
698 batchWrite.removeIntent(intent.id());
Brian O'Connor427a1762014-11-19 18:40:32 -0800699 break;
700 case FAILED:
Ray Milkey93508c22014-12-02 11:35:56 -0800701 batchWrite.setState(intent, FAILED);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800702 batchWrite.removeInstalledIntents(intent.id());
Brian O'Connor427a1762014-11-19 18:40:32 -0800703 break;
704
705 // FALLTHROUGH to default from here
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800706 case INSTALL_REQ:
Brian O'Connor427a1762014-11-19 18:40:32 -0800707 case COMPILING:
708 case RECOMPILING:
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800709 case WITHDRAW_REQ:
Brian O'Connor427a1762014-11-19 18:40:32 -0800710 case WITHDRAWN:
711 case INSTALLED:
712 default:
713 //FIXME clean this up (we shouldn't ever get here)
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800714 log.warn("Bad state: {} for {}", getInflightState(intent), intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800715 break;
716 }
717 }
718 }
719
Brian O'Connor427a1762014-11-19 18:40:32 -0800720 void addBatches(List<FlowRuleBatchOperation> batches) {
721 this.batches.addAll(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700722 }
723
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800724 IntentState getInflightState(Intent intent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700725 return stateMap.get(intent);
726 }
727
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800728 // set transient state during intent update process
729 void setInflightState(Intent intent, IntentState newState) {
730 // This method should be called for
731 // transition to non-parking or Failed only
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800732 if (!NON_PARKED_OR_FAILED.contains(newState)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800733 log.error("Unexpected transition to {}", newState);
734 }
735
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700736 IntentState oldState = stateMap.get(intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800737 log.debug("intent id: {}, old state: {}, new state: {}",
Ray Milkeye97ede92014-11-20 10:43:12 -0800738 intent.id(), oldState, newState);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700739
740 stateMap.put(intent, newState);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700741 }
742 }
743
Brian O'Connorcb900f42014-10-07 21:55:33 -0700744 private class IntentInstallMonitor implements Runnable {
745
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800746 // TODO make this configurable
747 private static final int TIMEOUT_PER_OP = 500; // ms
Brian O'Connor427a1762014-11-19 18:40:32 -0800748 private static final int MAX_ATTEMPTS = 3;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700749
Brian O'Connor427a1762014-11-19 18:40:32 -0800750 private final IntentOperations ops;
751 private final List<IntentUpdate> intentUpdates = Lists.newArrayList();
752
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800753 // future holding current FlowRuleBatch installation result
Brian O'Connor427a1762014-11-19 18:40:32 -0800754 private Future<CompletedBatchOperation> future;
755 private long startTime = System.currentTimeMillis();
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800756 private long endTime;
Brian O'Connor427a1762014-11-19 18:40:32 -0800757 private int installAttempt;
758
759 public IntentInstallMonitor(IntentOperations ops) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700760 this.ops = ops;
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800761 resetTimeoutLimit();
762 }
763
764 private void resetTimeoutLimit() {
765 // FIXME compute reasonable timeouts
766 this.endTime = System.currentTimeMillis()
767 + ops.operations().size() * TIMEOUT_PER_OP;
Brian O'Connor427a1762014-11-19 18:40:32 -0800768 }
769
770 private void buildIntentUpdates() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800771 BatchWrite batchWrite = store.newBatchWrite();
772
773 // create context and record new request to store
Brian O'Connor427a1762014-11-19 18:40:32 -0800774 for (IntentOperation op : ops.operations()) {
775 IntentUpdate update = new IntentUpdate(op);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800776 update.init(batchWrite);
Brian O'Connor427a1762014-11-19 18:40:32 -0800777 intentUpdates.add(update);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800778 }
779
780 if (!batchWrite.isEmpty()) {
781 store.batchWrite(batchWrite);
782 }
783
784 // start processing each Intents
785 for (IntentUpdate update : intentUpdates) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800786 processIntentUpdate(update);
787 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700788 future = applyNextBatch();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700789 }
790
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700791 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800792 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700793 *
794 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700795 */
796 private Future<CompletedBatchOperation> applyNextBatch() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800797 //TODO test this. (also, maybe save this batch)
798 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList());
799 for (IntentUpdate update : intentUpdates) {
800 if (!update.isComplete()) {
801 batch.addAll(update.currentBatch());
802 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700803 }
Ray Milkey93508c22014-12-02 11:35:56 -0800804 if (batch.size() > 0) {
805 //FIXME apply batch might throw an exception
806 return flowRuleService.applyBatch(batch);
807 } else {
808 // there are no flow rule batches; finalize the intent update
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800809 BatchWrite batchWrite = store.newBatchWrite();
Brian O'Connor427a1762014-11-19 18:40:32 -0800810 for (IntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -0800811 update.finalizeStates(batchWrite);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800812 }
813 if (!batchWrite.isEmpty()) {
814 store.batchWrite(batchWrite);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700815 }
Ray Milkey93508c22014-12-02 11:35:56 -0800816 return null;
817 }
818 }
819
820 private void updateBatches(CompletedBatchOperation completed) {
821 if (completed.isSuccess()) {
822 for (IntentUpdate update : intentUpdates) {
823 update.batchSuccess();
824 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700825 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800826 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -0800827 log.debug("Failed items: {}", completed.failedItems());
828 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -0800829
830 for (Long id : completed.failedIds()) {
831 IntentId targetId = IntentId.valueOf(id);
832 for (IntentUpdate update : intentUpdates) {
833 List<Intent> installables = Lists.newArrayList(update.newInstallables());
Ray Milkey93508c22014-12-02 11:35:56 -0800834 if (update.oldInstallables() != null) {
835 installables.addAll(update.oldInstallables());
836 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800837 for (Intent intent : installables) {
838 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800839 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800840 break;
841 }
842 }
843 }
844 // don't increment the non-failed items, as they have been reverted.
845 }
846 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700847 }
848
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800849 private void abandonShip() {
850 // the batch has failed
851 // TODO: maybe we should do more?
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800852 log.error("Walk the plank, matey...");
Ray Milkey93508c22014-12-02 11:35:56 -0800853 future = null;
854 batchService.removeIntentOperations(ops);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800855 }
856
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700857 /**
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700858 * Iterate through the pending futures, and remove them when they have completed.
859 */
860 private void processFutures() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700861 if (future == null) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800862 // we are done if the future is null
863 return;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700864 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700865 try {
866 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
Brian O'Connor427a1762014-11-19 18:40:32 -0800867 updateBatches(completed);
868 future = applyNextBatch();
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800869 } catch (TimeoutException | InterruptedException te) {
870 log.trace("Installation of intents are still pending: {}", ops);
871 } catch (ExecutionException e) {
872 log.warn("Execution of batch failed: {}", ops, e);
873 abandonShip();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700874 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700875 }
876
Brian O'Connor427a1762014-11-19 18:40:32 -0800877 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800878 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800879 if (future.cancel(true)) { // cancel success; batch is reverted
880 // reset the timer
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800881 resetTimeoutLimit();
Ray Milkey93508c22014-12-02 11:35:56 -0800882 installAttempt++;
883 if (installAttempt == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800884 log.warn("Install request timed out: {}", ops);
885 for (IntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800886 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800887 }
Ray Milkey93508c22014-12-02 11:35:56 -0800888 } else if (installAttempt > MAX_ATTEMPTS) {
889 abandonShip();
890 return;
Brian O'Connor427a1762014-11-19 18:40:32 -0800891 } // else just resubmit the work
892 future = applyNextBatch();
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800893 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -0800894 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800895 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800896 // FIXME
897 // cancel failed... batch is broken; shouldn't happen!
898 // we could manually reverse everything
899 // ... or just core dump and send email to Ali
Ray Milkey93508c22014-12-02 11:35:56 -0800900 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -0800901 }
902 }
903
904 boolean isComplete() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800905 return future == null;
906 }
907
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700908 @Override
909 public void run() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800910 try {
911 if (intentUpdates.isEmpty()) {
912 // this should only be called on the first iteration
913 // note: this a "expensive", so it is not done in the constructor
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800914
915 // - creates per Intent installation context (IntentUpdate)
916 // - write Intents to store
917 // - process (compile, install, etc.) each Intents
918 // - generate FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800919 buildIntentUpdates();
920 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800921
922 // - peek if current FlowRuleBatch is complete
923 // -- If complete OK:
924 // step each IntentUpdate forward
925 // If phase left: generate next FlowRuleBatch
926 // If no more phase: write parking states
927 // -- If complete FAIL:
928 // Intent which failed: transition Intent to FAILED
929 // Other Intents: resubmit same FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800930 processFutures();
931 if (isComplete()) {
932 // there are no outstanding batches; we are done
933 batchService.removeIntentOperations(ops);
934 } else if (endTime < System.currentTimeMillis()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800935 // - cancel current FlowRuleBatch and resubmit again
Brian O'Connor427a1762014-11-19 18:40:32 -0800936 retry();
937 } else {
938 // we are not done yet, yield the thread by resubmitting ourselves
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800939 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -0800940 }
941 } catch (Exception e) {
942 log.error("Error submitting batches:", e);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800943 // FIXME incomplete Intents should be cleaned up
944 // (transition to FAILED, etc.)
Ray Milkey93508c22014-12-02 11:35:56 -0800945 abandonShip();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700946 }
947 }
948 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700949
950 private class InternalBatchDelegate implements IntentBatchDelegate {
951 @Override
952 public void execute(IntentOperations operations) {
Yuta HIGUCHIfe4367a2014-11-24 19:19:09 -0800953 log.info("Execute {} operation(s).", operations.operations().size());
954 log.debug("Execute operations: {}", operations.operations());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700955 //FIXME: perhaps we want to track this task so that we can cancel it.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800956 executor.execute(new IntentInstallMonitor(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700957 }
958
959 @Override
960 public void cancel(IntentOperations operations) {
961 //FIXME: implement this
962 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
963 }
964 }
Ray Milkeye97ede92014-11-20 10:43:12 -0800965
Brian O'Connor66630c82014-10-02 21:08:19 -0700966}