blob: d307978e4ebf4298dddfa8dda26d086e014601cd [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);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700303 } catch (IntentException e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700304 log.warn("Unable to compile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700305
tom85258ee2014-10-07 00:10:02 -0700306 // If compilation failed, mark the intent as failed.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800307 update.setInflightState(intent, FAILED);
tom85258ee2014-10-07 00:10:02 -0700308 }
309 }
310
Brian O'Connorcb900f42014-10-07 21:55:33 -0700311 /**
312 * Compiles an intent recursively.
313 *
314 * @param intent intent
315 * @return result of compilation
316 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700317 private List<Intent> compileIntent(Intent intent, IntentUpdate update) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700318 if (intent.isInstallable()) {
319 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700320 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700321
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700322 registerSubclassCompilerIfNeeded(intent);
323 List<Intent> previous = update.oldInstallables();
324 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700325 List<Intent> installable = new ArrayList<>();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700326 for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) {
327 installable.addAll(compileIntent(compiled, update));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700328 }
tom85258ee2014-10-07 00:10:02 -0700329 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700330 }
331
332 /**
tom85258ee2014-10-07 00:10:02 -0700333 * Installs all installable intents associated with the specified top-level
334 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700335 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700336 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700337 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700338 private void executeInstallingPhase(IntentUpdate update) {
339 if (update.newInstallables() == null) {
340 //no failed intents allowed past this point...
341 return;
tom85258ee2014-10-07 00:10:02 -0700342 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700343 // Indicate that the intent is entering the installing phase.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800344 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700345
346 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
347 for (Intent installable : update.newInstallables()) {
348 registerSubclassInstallerIfNeeded(installable);
349 trackerService.addTrackedResources(update.newIntent().id(),
350 installable.resources());
351 try {
352 batches.addAll(getInstaller(installable).install(installable));
353 } catch (IntentException e) {
354 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
Brian O'Connor427a1762014-11-19 18:40:32 -0800355 trackerService.removeTrackedResources(update.newIntent().id(),
356 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700357 //FIXME we failed... intent should be recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700358 }
359 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800360 update.addBatches(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700361 }
362
363 /**
364 * Uninstalls the specified intent by uninstalling all of its associated
365 * installable derivatives.
366 *
367 * @param update intent update
368 */
369 private void executeWithdrawingPhase(IntentUpdate update) {
370 if (!update.oldIntent().equals(update.newIntent())) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800371 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700372 } // else newIntent is FAILED
Brian O'Connor427a1762014-11-19 18:40:32 -0800373 update.addBatches(uninstallIntent(update.oldIntent(), update.oldInstallables()));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700374 }
375
376 /**
377 * Uninstalls all installable intents associated with the given intent.
378 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800379 * @param intent intent
380 * @param installables installable intents
381 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700382 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800383 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
384 if (installables == null) {
385 return Collections.emptyList();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700386 }
387 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800388 for (Intent installable : installables) {
389 trackerService.removeTrackedResources(intent.id(),
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700390 installable.resources());
391 try {
392 batches.addAll(getInstaller(installable).uninstall(installable));
393 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800394 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700395 // TODO: this should never happen. but what if it does?
396 }
397 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800398 return batches;
tom85258ee2014-10-07 00:10:02 -0700399 }
400
401 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700402 * Withdraws the old intent and installs the new intent as one operation.
tom85258ee2014-10-07 00:10:02 -0700403 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700404 * @param update intent update
tom85258ee2014-10-07 00:10:02 -0700405 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700406 private void executeReplacementPhase(IntentUpdate update) {
407 checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
408 "Old and New Intent must have equivalent installable intents.");
409 if (!update.oldIntent().equals(update.newIntent())) {
410 // only set the old intent's state if it is different
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800411 update.setInflightState(update.oldIntent(), WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700412 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800413 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700414
415 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
416 for (int i = 0; i < update.oldInstallables().size(); i++) {
417 Intent oldInstallable = update.oldInstallables().get(i);
418 Intent newInstallable = update.newInstallables().get(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800419 //FIXME revisit this
420// if (oldInstallable.equals(newInstallable)) {
421// continue;
422// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700423 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
424 "Installable Intent type mismatch.");
425 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
426 trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
427 try {
428 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
429 } catch (IntentException e) {
430 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
431 //FIXME... we failed. need to uninstall (if same) or revert (if different)
Brian O'Connor427a1762014-11-19 18:40:32 -0800432 trackerService.removeTrackedResources(update.newIntent().id(), newInstallable.resources());
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800433 update.setInflightState(update.newIntent(), FAILED);
Brian O'Connor427a1762014-11-19 18:40:32 -0800434 batches = uninstallIntent(update.oldIntent(), update.oldInstallables());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700435 }
436 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800437 update.addBatches(batches);
tom53945d52014-10-07 11:01:36 -0700438 }
439
440 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700441 * Registers an intent compiler of the specified intent if an intent compiler
442 * for the intent is not registered. This method traverses the class hierarchy of
443 * the intent. Once an intent compiler for a parent type is found, this method
444 * registers the found intent compiler.
445 *
446 * @param intent intent
447 */
448 private void registerSubclassCompilerIfNeeded(Intent intent) {
449 if (!compilers.containsKey(intent.getClass())) {
450 Class<?> cls = intent.getClass();
451 while (cls != Object.class) {
452 // As long as we're within the Intent class descendants
453 if (Intent.class.isAssignableFrom(cls)) {
454 IntentCompiler<?> compiler = compilers.get(cls);
455 if (compiler != null) {
456 compilers.put(intent.getClass(), compiler);
457 return;
458 }
459 }
460 cls = cls.getSuperclass();
461 }
462 }
463 }
464
465 /**
466 * Registers an intent installer of the specified intent if an intent installer
467 * for the intent is not registered. This method traverses the class hierarchy of
468 * the intent. Once an intent installer for a parent type is found, this method
469 * registers the found intent installer.
470 *
471 * @param intent intent
472 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700473 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700474 if (!installers.containsKey(intent.getClass())) {
475 Class<?> cls = intent.getClass();
476 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700477 // As long as we're within the Intent class descendants
478 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700479 IntentInstaller<?> installer = installers.get(cls);
480 if (installer != null) {
481 installers.put(intent.getClass(), installer);
482 return;
483 }
484 }
485 cls = cls.getSuperclass();
486 }
487 }
488 }
489
Brian O'Connor66630c82014-10-02 21:08:19 -0700490 // Store delegate to re-post events emitted from the store.
491 private class InternalStoreDelegate implements IntentStoreDelegate {
492 @Override
493 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700494 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700495 }
496 }
497
Brian O'Connor72a034c2014-11-26 18:24:23 -0800498 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
499 boolean compileAllFailed) {
500 Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
501 // Attempt recompilation of the specified intents first.
502 for (IntentId id : intentIds) {
503 Intent intent = store.getIntent(id);
504 if (intent == null) {
505 continue;
506 }
507 IntentOperations.Builder builder = batches.get(intent.appId());
508 if (builder == null) {
509 builder = IntentOperations.builder(intent.appId());
510 batches.put(intent.appId(), builder);
511 }
512 builder.addUpdateOperation(id);
513 }
514
515 if (compileAllFailed) {
516 // If required, compile all currently failed intents.
517 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800518 IntentState state = getIntentState(intent.id());
519 if (RECOMPILE.contains(state)) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800520 IntentOperations.Builder builder = batches.get(intent.appId());
521 if (builder == null) {
522 builder = IntentOperations.builder(intent.appId());
523 batches.put(intent.appId(), builder);
524 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800525 if (state == WITHDRAW_REQ) {
526 builder.addWithdrawOperation(intent.id());
527 } else {
528 builder.addUpdateOperation(intent.id());
529 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800530 }
531 }
532 }
533
534 for (ApplicationId appId : batches.keySet()) {
535 if (batchService.isLocalLeader(appId)) {
536 execute(batches.get(appId).build());
537 }
538 }
539 }
540
tom95329eb2014-10-06 08:40:06 -0700541 // Topology change delegate
542 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
543 @Override
tom85258ee2014-10-07 00:10:02 -0700544 public void triggerCompile(Iterable<IntentId> intentIds,
545 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800546 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700547 }
tom95329eb2014-10-06 08:40:06 -0700548 }
tom85258ee2014-10-07 00:10:02 -0700549
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800550 // TODO move this inside IntentUpdate?
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700551 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800552 * TODO. rename this...
553 * @param update intent update
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700554 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800555 private void processIntentUpdate(IntentUpdate update) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700556
Brian O'Connor427a1762014-11-19 18:40:32 -0800557 // check to see if the intent needs to be compiled or recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700558 if (update.newIntent() != null) {
559 executeCompilingPhase(update);
560 }
561
562 if (update.oldInstallables() != null && update.newInstallables() != null) {
563 executeReplacementPhase(update);
564 } else if (update.newInstallables() != null) {
565 executeInstallingPhase(update);
566 } else if (update.oldInstallables() != null) {
567 executeWithdrawingPhase(update);
568 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800569 if (update.oldIntent() != null &&
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800570 !update.oldIntent().equals(update.newIntent())) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800571 // removing failed intent
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800572 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700573 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800574// if (update.newIntent() != null) {
575// // TODO assert that next state is failed
576// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700577 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700578 }
579
580 // TODO comments...
581 private class IntentUpdate {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700582 private final Intent oldIntent;
583 private final Intent newIntent;
584 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
585
586 private final List<Intent> oldInstallables;
587 private List<Intent> newInstallables;
Brian O'Connor427a1762014-11-19 18:40:32 -0800588 private final List<FlowRuleBatchOperation> batches = Lists.newLinkedList();
589 private int currentBatch = 0; // TODO: maybe replace with an iterator
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700590
591 IntentUpdate(IntentOperation op) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700592 switch (op.type()) {
593 case SUBMIT:
594 newIntent = op.intent();
595 oldIntent = null;
596 break;
597 case WITHDRAW:
598 newIntent = null;
599 oldIntent = store.getIntent(op.intentId());
600 break;
601 case REPLACE:
602 newIntent = op.intent();
603 oldIntent = store.getIntent(op.intentId());
604 break;
605 case UPDATE:
606 oldIntent = store.getIntent(op.intentId());
Brian O'Connor427a1762014-11-19 18:40:32 -0800607 newIntent = oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700608 break;
609 default:
610 oldIntent = null;
611 newIntent = null;
612 break;
613 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700614 // fetch the old intent's installables from the store
615 if (oldIntent != null) {
616 oldInstallables = store.getInstallableIntents(oldIntent.id());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700617 } else {
618 oldInstallables = null;
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800619 if (newIntent == null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800620 log.info("Ignoring {} for missing Intent {}", op.type(), op.intentId());
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800621 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700622 }
623 }
624
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800625 void init(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800626 if (newIntent != null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800627 // TODO consider only "creating" intent if it does not exist
628 // Note: We need to set state to INSTALL_REQ regardless.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800629 batchWrite.createIntent(newIntent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800630 } else if (oldIntent != null && !oldIntent.equals(newIntent)) {
631 batchWrite.setState(oldIntent, WITHDRAW_REQ);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800632 }
633 }
634
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700635 Intent oldIntent() {
636 return oldIntent;
637 }
638
639 Intent newIntent() {
640 return newIntent;
641 }
642
643 List<Intent> oldInstallables() {
644 return oldInstallables;
645 }
646
647 List<Intent> newInstallables() {
648 return newInstallables;
649 }
650
651 void setInstallables(List<Intent> installables) {
652 newInstallables = installables;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700653 }
654
Brian O'Connor427a1762014-11-19 18:40:32 -0800655 boolean isComplete() {
656 return currentBatch >= batches.size();
657 }
658
659 FlowRuleBatchOperation currentBatch() {
660 return !isComplete() ? batches.get(currentBatch) : null;
661 }
662
alshabiba9819bf2014-11-30 18:15:52 -0800663 void batchSuccess(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800664 // move on to next Batch
665 if (++currentBatch == batches.size()) {
alshabiba9819bf2014-11-30 18:15:52 -0800666 finalizeStates(batchWrite);
Brian O'Connor427a1762014-11-19 18:40:32 -0800667 }
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
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800687 // make sure this is called!!!
alshabiba9819bf2014-11-30 18:15:52 -0800688 private void finalizeStates(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800689 // events to be triggered on successful write
Brian O'Connor427a1762014-11-19 18:40:32 -0800690 for (Intent intent : stateMap.keySet()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800691 switch (getInflightState(intent)) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800692 case INSTALLING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800693 batchWrite.setState(intent, INSTALLED);
694 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Brian O'Connor427a1762014-11-19 18:40:32 -0800695 break;
696 case WITHDRAWING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800697 batchWrite.setState(intent, WITHDRAWN);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800698 batchWrite.removeInstalledIntents(intent.id());
699 batchWrite.removeIntent(intent.id());
Brian O'Connor427a1762014-11-19 18:40:32 -0800700 break;
701 case 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 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800804 return (batch.size() > 0) ? flowRuleService.applyBatch(batch) : null;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700805 }
806
Brian O'Connor427a1762014-11-19 18:40:32 -0800807 private void updateBatches(CompletedBatchOperation completed) {
808 if (completed.isSuccess()) {
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) {
alshabiba9819bf2014-11-30 18:15:52 -0800811 update.batchSuccess(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 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700816 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800817 // entire batch has been reverted...
818 log.warn("Failed items: {}", completed.failedItems());
819
820 for (Long id : completed.failedIds()) {
821 IntentId targetId = IntentId.valueOf(id);
822 for (IntentUpdate update : intentUpdates) {
823 List<Intent> installables = Lists.newArrayList(update.newInstallables());
824 installables.addAll(update.oldInstallables());
825 for (Intent intent : installables) {
826 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800827 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800828 break;
829 }
830 }
831 }
832 // don't increment the non-failed items, as they have been reverted.
833 }
834 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700835 }
836
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800837 private void abandonShip() {
838 // the batch has failed
839 // TODO: maybe we should do more?
840 future = null;
841 log.error("Walk the plank, matey...");
842 }
843
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700844 /**
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700845 * Iterate through the pending futures, and remove them when they have completed.
846 */
847 private void processFutures() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700848 if (future == null) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800849 // we are done if the future is null
850 return;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700851 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700852 try {
853 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
Brian O'Connor427a1762014-11-19 18:40:32 -0800854 updateBatches(completed);
855 future = applyNextBatch();
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800856 } catch (TimeoutException | InterruptedException te) {
857 log.trace("Installation of intents are still pending: {}", ops);
858 } catch (ExecutionException e) {
859 log.warn("Execution of batch failed: {}", ops, e);
860 abandonShip();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700861 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700862 }
863
Brian O'Connor427a1762014-11-19 18:40:32 -0800864 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800865 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800866 if (future.cancel(true)) { // cancel success; batch is reverted
867 // reset the timer
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800868 resetTimeoutLimit();
Brian O'Connor427a1762014-11-19 18:40:32 -0800869 if (installAttempt++ >= MAX_ATTEMPTS) {
870 log.warn("Install request timed out: {}", ops);
871 for (IntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800872 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800873 }
874 } // else just resubmit the work
875 future = applyNextBatch();
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800876 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -0800877 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800878 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800879 // FIXME
880 // cancel failed... batch is broken; shouldn't happen!
881 // we could manually reverse everything
882 // ... or just core dump and send email to Ali
883 batchService.removeIntentOperations(ops);
884 }
885 }
886
887 boolean isComplete() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800888 return future == null;
889 }
890
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700891 @Override
892 public void run() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800893 try {
894 if (intentUpdates.isEmpty()) {
895 // this should only be called on the first iteration
896 // note: this a "expensive", so it is not done in the constructor
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800897
898 // - creates per Intent installation context (IntentUpdate)
899 // - write Intents to store
900 // - process (compile, install, etc.) each Intents
901 // - generate FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800902 buildIntentUpdates();
903 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800904
905 // - peek if current FlowRuleBatch is complete
906 // -- If complete OK:
907 // step each IntentUpdate forward
908 // If phase left: generate next FlowRuleBatch
909 // If no more phase: write parking states
910 // -- If complete FAIL:
911 // Intent which failed: transition Intent to FAILED
912 // Other Intents: resubmit same FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800913 processFutures();
914 if (isComplete()) {
915 // there are no outstanding batches; we are done
916 batchService.removeIntentOperations(ops);
917 } else if (endTime < System.currentTimeMillis()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800918 // - cancel current FlowRuleBatch and resubmit again
Brian O'Connor427a1762014-11-19 18:40:32 -0800919 retry();
920 } else {
921 // we are not done yet, yield the thread by resubmitting ourselves
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800922 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -0800923 }
924 } catch (Exception e) {
925 log.error("Error submitting batches:", e);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800926 // FIXME incomplete Intents should be cleaned up
927 // (transition to FAILED, etc.)
Brian O'Connorcb900f42014-10-07 21:55:33 -0700928 }
929 }
930 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700931
932 private class InternalBatchDelegate implements IntentBatchDelegate {
933 @Override
934 public void execute(IntentOperations operations) {
Yuta HIGUCHIfe4367a2014-11-24 19:19:09 -0800935 log.info("Execute {} operation(s).", operations.operations().size());
936 log.debug("Execute operations: {}", operations.operations());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700937 //FIXME: perhaps we want to track this task so that we can cancel it.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800938 executor.execute(new IntentInstallMonitor(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700939 }
940
941 @Override
942 public void cancel(IntentOperations operations) {
943 //FIXME: implement this
944 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
945 }
946 }
Ray Milkeye97ede92014-11-20 10:43:12 -0800947
Brian O'Connor66630c82014-10-02 21:08:19 -0700948}