blob: 628e848da4fa66a8d63f5b769d7f22427262d4d0 [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'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.intent.impl;
Brian O'Connor66630c82014-10-02 21:08:19 -070017
Brian O'Connor03406a42015-02-03 17:28:57 -080018import java.util.ArrayList;
19import java.util.Collection;
20import java.util.Collections;
21import java.util.EnumSet;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.Map;
25import java.util.Optional;
26import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.ConcurrentMap;
28import java.util.concurrent.ExecutionException;
29import java.util.concurrent.ExecutorService;
30import java.util.concurrent.Future;
31import java.util.concurrent.TimeUnit;
32import java.util.concurrent.TimeoutException;
33import java.util.stream.Collectors;
34
Brian O'Connor66630c82014-10-02 21:08:19 -070035import org.apache.felix.scr.annotations.Activate;
36import org.apache.felix.scr.annotations.Component;
37import org.apache.felix.scr.annotations.Deactivate;
38import org.apache.felix.scr.annotations.Reference;
39import org.apache.felix.scr.annotations.ReferenceCardinality;
40import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.core.CoreService;
42import org.onosproject.core.IdGenerator;
43import org.onosproject.event.AbstractListenerRegistry;
44import org.onosproject.event.EventDeliveryService;
45import org.onosproject.net.flow.CompletedBatchOperation;
46import org.onosproject.net.flow.FlowRuleBatchOperation;
47import org.onosproject.net.flow.FlowRuleService;
Brian O'Connorb499b352015-02-03 16:46:15 -080048import org.onosproject.net.intent.BatchWrite;
Brian O'Connorabafb502014-12-02 22:26:20 -080049import org.onosproject.net.intent.Intent;
50import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080051import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080052import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080053import org.onosproject.net.intent.IntentEvent;
54import org.onosproject.net.intent.IntentException;
55import org.onosproject.net.intent.IntentExtensionService;
56import org.onosproject.net.intent.IntentId;
57import org.onosproject.net.intent.IntentInstaller;
58import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080059import org.onosproject.net.intent.IntentService;
60import org.onosproject.net.intent.IntentState;
61import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080062import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070063import org.slf4j.Logger;
64
Brian O'Connor03406a42015-02-03 17:28:57 -080065import com.google.common.collect.ImmutableList;
66import com.google.common.collect.ImmutableMap;
67import com.google.common.collect.Lists;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070068
Brian O'Connorfa81eae2014-10-30 13:20:05 -070069import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080070import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080071import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070072import static org.onlab.util.Tools.namedThreads;
Brian O'Connor03406a42015-02-03 17:28:57 -080073import static org.onosproject.net.intent.IntentState.FAILED;
74import static org.onosproject.net.intent.IntentState.INSTALLED;
75import static org.onosproject.net.intent.IntentState.INSTALLING;
76import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
77import static org.onosproject.net.intent.IntentState.WITHDRAWN;
78import static org.onosproject.net.intent.IntentState.WITHDRAW_REQ;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070079import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070080
81/**
82 * An implementation of Intent Manager.
83 */
84@Component(immediate = true)
85@Service
86public class IntentManager
87 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080088 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070089
90 public static final String INTENT_NULL = "Intent cannot be null";
91 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
92
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080093 private static final int NUM_THREADS = 12;
94
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080095 private static final EnumSet<IntentState> RECOMPILE
96 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080097
98
Brian O'Connor66630c82014-10-02 21:08:19 -070099 // Collections for compiler, installer, and listener are ONOS instance local
100 private final ConcurrentMap<Class<? extends Intent>,
101 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700102 private final ConcurrentMap<Class<? extends Intent>,
103 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700104
105 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700106 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700107
Brian O'Connor520c0522014-11-23 23:50:47 -0800108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected IntentStore store;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700115 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700118 protected EventDeliveryService eventDispatcher;
119
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected FlowRuleService flowRuleService;
122
Brian O'Connor520c0522014-11-23 23:50:47 -0800123
124 private ExecutorService executor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800125
126 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
127 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
128 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
129 private IdGenerator idGenerator;
130
Brian O'Connorb499b352015-02-03 16:46:15 -0800131 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800132
Brian O'Connor66630c82014-10-02 21:08:19 -0700133 @Activate
134 public void activate() {
135 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700136 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700137 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Yuta HIGUCHI7a9fddd2014-12-03 15:30:25 -0800138 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800139 idGenerator = coreService.getIdGenerator("intent-ids");
140 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700141 log.info("Started");
142 }
143
144 @Deactivate
145 public void deactivate() {
146 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700147 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700148 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700149 executor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800150 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700151 log.info("Stopped");
152 }
153
154 @Override
155 public void submit(Intent intent) {
156 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800157 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
158 //FIXME timestamp?
159 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700160 }
161
162 @Override
163 public void withdraw(Intent intent) {
164 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800165 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
166 //FIXME timestamp?
167 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700168 }
169
Brian O'Connor66630c82014-10-02 21:08:19 -0700170 @Override
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700171 public void replace(IntentId oldIntentId, Intent newIntent) {
Brian O'Connorcff03322015-02-03 15:28:59 -0800172 throw new UnsupportedOperationException("replace is not implemented");
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700173 }
174
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700175 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700176 public Iterable<Intent> getIntents() {
177 return store.getIntents();
178 }
179
180 @Override
181 public long getIntentCount() {
182 return store.getIntentCount();
183 }
184
185 @Override
186 public Intent getIntent(IntentId id) {
187 checkNotNull(id, INTENT_ID_NULL);
188 return store.getIntent(id);
189 }
190
191 @Override
192 public IntentState getIntentState(IntentId id) {
193 checkNotNull(id, INTENT_ID_NULL);
194 return store.getIntentState(id);
195 }
196
197 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700198 public List<Intent> getInstallableIntents(IntentId intentId) {
199 checkNotNull(intentId, INTENT_ID_NULL);
200 return store.getInstallableIntents(intentId);
201 }
202
203 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700204 public void addListener(IntentListener listener) {
205 listenerRegistry.addListener(listener);
206 }
207
208 @Override
209 public void removeListener(IntentListener listener) {
210 listenerRegistry.removeListener(listener);
211 }
212
213 @Override
214 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
215 compilers.put(cls, compiler);
216 }
217
218 @Override
219 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
220 compilers.remove(cls);
221 }
222
223 @Override
224 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
225 return ImmutableMap.copyOf(compilers);
226 }
227
228 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700229 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700230 installers.put(cls, installer);
231 }
232
233 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700234 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700235 installers.remove(cls);
236 }
237
238 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700239 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700240 return ImmutableMap.copyOf(installers);
241 }
242
243 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700244 * Returns the corresponding intent compiler to the specified intent.
245 *
246 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700247 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700248 * @return intent compiler corresponding to the specified intent
249 */
250 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
251 @SuppressWarnings("unchecked")
252 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
253 if (compiler == null) {
254 throw new IntentException("no compiler for class " + intent.getClass());
255 }
256 return compiler;
257 }
258
259 /**
260 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700261 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700262 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700263 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700264 * @return intent installer corresponding to the specified installable intent
265 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700266 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700267 @SuppressWarnings("unchecked")
268 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
269 if (installer == null) {
270 throw new IntentException("no installer for class " + intent.getClass());
271 }
272 return installer;
273 }
274
275 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700276 * Compiles an intent recursively.
277 *
278 * @param intent intent
279 * @return result of compilation
280 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800281 private List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700282 if (intent.isInstallable()) {
283 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700284 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700285
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700286 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700287 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700288 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800289 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
290 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700291 }
tom85258ee2014-10-07 00:10:02 -0700292 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700293 }
294
295 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700296 * Uninstalls all installable intents associated with the given intent.
297 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800298 * @param intent intent
299 * @param installables installable intents
300 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700301 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800302 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700303 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800304 for (Intent installable : installables) {
305 trackerService.removeTrackedResources(intent.id(),
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800306 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700307 try {
308 batches.addAll(getInstaller(installable).uninstall(installable));
309 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800310 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700311 // TODO: this should never happen. but what if it does?
312 }
313 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800314 return batches;
tom85258ee2014-10-07 00:10:02 -0700315 }
316
317 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700318 * Registers an intent compiler of the specified intent if an intent compiler
319 * for the intent is not registered. This method traverses the class hierarchy of
320 * the intent. Once an intent compiler for a parent type is found, this method
321 * registers the found intent compiler.
322 *
323 * @param intent intent
324 */
325 private void registerSubclassCompilerIfNeeded(Intent intent) {
326 if (!compilers.containsKey(intent.getClass())) {
327 Class<?> cls = intent.getClass();
328 while (cls != Object.class) {
329 // As long as we're within the Intent class descendants
330 if (Intent.class.isAssignableFrom(cls)) {
331 IntentCompiler<?> compiler = compilers.get(cls);
332 if (compiler != null) {
333 compilers.put(intent.getClass(), compiler);
334 return;
335 }
336 }
337 cls = cls.getSuperclass();
338 }
339 }
340 }
341
342 /**
343 * Registers an intent installer of the specified intent if an intent installer
344 * for the intent is not registered. This method traverses the class hierarchy of
345 * the intent. Once an intent installer for a parent type is found, this method
346 * registers the found intent installer.
347 *
348 * @param intent intent
349 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700350 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700351 if (!installers.containsKey(intent.getClass())) {
352 Class<?> cls = intent.getClass();
353 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700354 // As long as we're within the Intent class descendants
355 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700356 IntentInstaller<?> installer = installers.get(cls);
357 if (installer != null) {
358 installers.put(intent.getClass(), installer);
359 return;
360 }
361 }
362 cls = cls.getSuperclass();
363 }
364 }
365 }
366
Brian O'Connor66630c82014-10-02 21:08:19 -0700367 // Store delegate to re-post events emitted from the store.
368 private class InternalStoreDelegate implements IntentStoreDelegate {
369 @Override
370 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700371 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700372 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800373
374 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800375 public void process(IntentData data) {
376 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800377 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700378 }
379
Brian O'Connor72a034c2014-11-26 18:24:23 -0800380 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
381 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800382 // Attempt recompilation of the specified intents first.
383 for (IntentId id : intentIds) {
384 Intent intent = store.getIntent(id);
385 if (intent == null) {
386 continue;
387 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800388 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800389 }
390
391 if (compileAllFailed) {
392 // If required, compile all currently failed intents.
393 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800394 IntentState state = getIntentState(intent.id());
395 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800396 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800397 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800398 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800399 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800400 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800401 }
402 }
403 }
404
Brian O'Connorb499b352015-02-03 16:46:15 -0800405 //FIXME
406// for (ApplicationId appId : batches.keySet()) {
407// if (batchService.isLocalLeader(appId)) {
408// execute(batches.get(appId).build());
409// }
410// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800411 }
412
tom95329eb2014-10-06 08:40:06 -0700413 // Topology change delegate
414 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
415 @Override
tom85258ee2014-10-07 00:10:02 -0700416 public void triggerCompile(Iterable<IntentId> intentIds,
417 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800418 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700419 }
tom95329eb2014-10-06 08:40:06 -0700420 }
tom85258ee2014-10-07 00:10:02 -0700421
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800422 // TODO: simplify the branching statements
Brian O'Connorb499b352015-02-03 16:46:15 -0800423 private IntentUpdate createIntentUpdate(IntentData intentData) {
424 IntentData currentState = store.getIntentData(intentData.key());
425 switch (intentData.state()) {
426 case INSTALL_REQ:
427 return new InstallRequest(intentData.intent(), currentState);
428 case WITHDRAW_REQ:
429 return new WithdrawRequest(intentData.intent(), currentState);
430 // fallthrough
431 case COMPILING:
432 case INSTALLING:
433 case INSTALLED:
434 case RECOMPILING:
435 case WITHDRAWING:
436 case WITHDRAWN:
437 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800438 default:
439 // illegal state
440 return new DoNothing();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700441 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700442 }
443
Brian O'Connorcff03322015-02-03 15:28:59 -0800444 // TODO pull out the IntentUpdate inner classes
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800445 private class InstallRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800446
447 private final Intent intent;
Brian O'Connorb499b352015-02-03 16:46:15 -0800448 private final IntentData currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800449
Brian O'Connorb499b352015-02-03 16:46:15 -0800450 InstallRequest(Intent intent, IntentData currentState) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800451 this.intent = checkNotNull(intent);
Brian O'Connorb499b352015-02-03 16:46:15 -0800452 this.currentState = currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800453 }
454
455 @Override
456 public void writeBeforeExecution(BatchWrite batchWrite) {
457 // TODO consider only "creating" intent if it does not exist
458 // Note: We need to set state to INSTALL_REQ regardless.
459 batchWrite.createIntent(intent);
460 }
461
462 @Override
463 public Optional<IntentUpdate> execute() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800464 return Optional.of(new Compiling(intent)); //FIXME
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800465 }
466 }
467
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800468 private class WithdrawRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800469
470 private final Intent intent;
Brian O'Connorb499b352015-02-03 16:46:15 -0800471 private final IntentData currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800472
Brian O'Connorb499b352015-02-03 16:46:15 -0800473 WithdrawRequest(Intent intent, IntentData currentState) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800474 this.intent = checkNotNull(intent);
Brian O'Connorb499b352015-02-03 16:46:15 -0800475 this.currentState = currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800476 }
477
478 @Override
479 public void writeBeforeExecution(BatchWrite batchWrite) {
480 batchWrite.setState(intent, WITHDRAW_REQ);
481 }
482
483 @Override
484 public Optional<IntentUpdate> execute() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800485 return Optional.of(new Withdrawing(intent, currentState.installables())); //FIXME
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800486 }
487 }
488
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800489 private class ReplaceRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800490
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700491 private final Intent newIntent;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800492 private final Intent oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700493 private final List<Intent> oldInstallables;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700494
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800495 ReplaceRequest(Intent newIntent, Intent oldIntent, List<Intent> oldInstallables) {
496 this.newIntent = checkNotNull(newIntent);
497 this.oldIntent = checkNotNull(oldIntent);
498 this.oldInstallables = ImmutableList.copyOf(oldInstallables);
499 }
500
501 @Override
502 public void writeBeforeExecution(BatchWrite batchWrite) {
503 // TODO consider only "creating" intent if it does not exist
504 // Note: We need to set state to INSTALL_REQ regardless.
505 batchWrite.createIntent(newIntent);
506 }
507
508 @Override
509 public Optional<IntentUpdate> execute() {
510 try {
511 List<Intent> installables = compileIntent(newIntent, oldInstallables);
512 return Optional.of(new Replacing(newIntent, oldIntent, installables, oldInstallables));
513 } catch (PathNotFoundException e) {
514 log.debug("Path not found for intent {}", newIntent);
515 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
516 } catch (IntentException e) {
517 log.warn("Unable to compile intent {} due to:", newIntent.id(), e);
518 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700519 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800520 }
521 }
522
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800523 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800524 private class WithdrawStateChange1 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800525
526 private final Intent intent;
527
528 WithdrawStateChange1(Intent intent) {
529 this.intent = checkNotNull(intent);
530 }
531
532 @Override
533 public void writeBeforeExecution(BatchWrite batchWrite) {
534 batchWrite.setState(intent, WITHDRAW_REQ);
535 }
536
537 @Override
538 public void writeAfterExecution(BatchWrite batchWrite) {
539 batchWrite.setState(intent, WITHDRAWN);
540 batchWrite.removeInstalledIntents(intent.id());
541 batchWrite.removeIntent(intent.id());
542 }
543 }
544
545 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800546 private class WithdrawStateChange2 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800547
548 private final Intent intent;
549
550 WithdrawStateChange2(Intent intent) {
551 this.intent = checkNotNull(intent);
552 }
553
554 @Override
555 public void writeBeforeExecution(BatchWrite batchWrite) {
556 // TODO consider only "creating" intent if it does not exist
557 // Note: We need to set state to INSTALL_REQ regardless.
558 batchWrite.createIntent(intent);
559 }
560
561 @Override
562 public void writeAfterExecution(BatchWrite batchWrite) {
563 batchWrite.setState(intent, WITHDRAWN);
564 batchWrite.removeInstalledIntents(intent.id());
565 batchWrite.removeIntent(intent.id());
566 }
567 }
568
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800569 private class Compiling implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800570
571 private final Intent intent;
572
573 Compiling(Intent intent) {
574 this.intent = checkNotNull(intent);
575 }
576
577 @Override
578 public Optional<IntentUpdate> execute() {
579 try {
580 // Compile the intent into installable derivatives.
581 // If all went well, associate the resulting list of installable
582 // intents with the top-level intent and proceed to install.
583 return Optional.of(new Installing(intent, compileIntent(intent, null)));
584 } catch (PathNotFoundException e) {
Sho SHIMIZUfd0cd8c2015-01-28 08:16:58 -0800585 log.debug("Path not found for intent {}", intent);
586 return Optional.of(new CompilingFailed(intent));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800587 } catch (IntentException e) {
Sho SHIMIZUfd0cd8c2015-01-28 08:16:58 -0800588 log.warn("Unable to compile intent {} due to:", intent.id(), e);
589 return Optional.of(new CompilingFailed(intent));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800590 }
591 }
592 }
593
594 // TODO: better naming because install() method actually generate FlowRuleBatchOperations
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800595 private class Installing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800596
597 private final Intent intent;
598 private final List<Intent> installables;
599
600 Installing(Intent intent, List<Intent> installables) {
601 this.intent = checkNotNull(intent);
602 this.installables = ImmutableList.copyOf(checkNotNull(installables));
603 }
604
605 @Override
606 public Optional<IntentUpdate> execute() {
607 Exception exception = null;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800608
609 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
610 for (Intent installable : installables) {
611 registerSubclassInstallerIfNeeded(installable);
612 trackerService.addTrackedResources(intent.id(), installable.resources());
613 try {
614 batches.addAll(getInstaller(installable).install(installable));
615 } catch (Exception e) { // TODO this should be IntentException
616 log.warn("Unable to install intent {} due to:", intent.id(), e);
617 trackerService.removeTrackedResources(intent.id(), installable.resources());
618 //TODO we failed; intent should be recompiled
619 exception = e;
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800620 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700621 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700622
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800623 if (exception != null) {
624 return Optional.of(new InstallingFailed(intent, installables, batches));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800625 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800626
627 return Optional.of(new Installed(intent, installables, batches));
628 }
629 }
630
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800631 private class Withdrawing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800632
633 private final Intent intent;
634 private final List<Intent> installables;
635
636 Withdrawing(Intent intent, List<Intent> installables) {
637 this.intent = checkNotNull(intent);
638 this.installables = ImmutableList.copyOf(installables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800639 }
640
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800641 @Override
642 public Optional<IntentUpdate> execute() {
643 List<FlowRuleBatchOperation> batches = uninstallIntent(intent, installables);
644
645 return Optional.of(new Withdrawn(intent, installables, batches));
646 }
647 }
648
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800649 private class Replacing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800650
651 private final Intent newIntent;
652 private final Intent oldIntent;
653 private final List<Intent> newInstallables;
654 private final List<Intent> oldInstallables;
655
656 private Exception exception;
657
658 Replacing(Intent newIntent, Intent oldIntent,
659 List<Intent> newInstallables, List<Intent> oldInstallables) {
660 this.newIntent = checkNotNull(newIntent);
661 this.oldIntent = checkNotNull(oldIntent);
662 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
663 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700664 }
665
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800666 @Override
667 public Optional<IntentUpdate> execute() {
668 List<FlowRuleBatchOperation> batches = replace();
669
670 if (exception == null) {
671 return Optional.of(
672 new Replaced(newIntent, oldIntent, newInstallables, oldInstallables, batches));
673 }
674
675 return Optional.of(
676 new ReplacingFailed(newIntent, oldIntent, newInstallables, oldInstallables, batches));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700677 }
678
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800679 protected List<FlowRuleBatchOperation> replace() {
680 checkState(oldInstallables.size() == newInstallables.size(),
681 "Old and New Intent must have equivalent installable intents.");
682
683 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
684 for (int i = 0; i < oldInstallables.size(); i++) {
685 Intent oldInstallable = oldInstallables.get(i);
686 Intent newInstallable = newInstallables.get(i);
687 //FIXME revisit this
688// if (oldInstallable.equals(newInstallable)) {
689// continue;
690// }
691 checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
692 "Installable Intent type mismatch.");
693 trackerService.removeTrackedResources(oldIntent.id(), oldInstallable.resources());
694 trackerService.addTrackedResources(newIntent.id(), newInstallable.resources());
695 try {
696 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
697 } catch (IntentException e) {
698 log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
699 //FIXME... we failed. need to uninstall (if same) or revert (if different)
700 trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
701 exception = e;
702 batches = uninstallIntent(oldIntent, oldInstallables);
703 }
704 }
705 return batches;
706 }
707 }
708
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800709 private class Installed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800710
711 private final Intent intent;
712 private final List<Intent> installables;
713 private IntentState intentState;
714 private final List<FlowRuleBatchOperation> batches;
715 private int currentBatch = 0;
716
717 Installed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
718 this.intent = checkNotNull(intent);
719 this.installables = ImmutableList.copyOf(checkNotNull(installables));
720 this.batches = new LinkedList<>(checkNotNull(batches));
721 this.intentState = INSTALLING;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700722 }
723
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800724 @Override
725 public void batchSuccess() {
Ray Milkey93508c22014-12-02 11:35:56 -0800726 currentBatch++;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800727 }
728
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800729 @Override
730 public List<Intent> allInstallables() {
731 return installables;
732 }
733
734 @Override
735 public void writeAfterExecution(BatchWrite batchWrite) {
736 switch (intentState) {
737 case INSTALLING:
738 batchWrite.setState(intent, INSTALLED);
739 batchWrite.setInstallableIntents(intent.id(), this.installables);
740 break;
741 case FAILED:
742 batchWrite.setState(intent, FAILED);
743 batchWrite.removeInstalledIntents(intent.id());
744 break;
745 default:
746 break;
747 }
748 }
749
750 @Override
751 public FlowRuleBatchOperation currentBatch() {
752 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
753 }
754
755 @Override
756 public void batchFailed() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800757 // the current batch has failed, so recompile
758 // remove the current batch and all remaining
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800759 for (int i = batches.size() - 1; i >= currentBatch; i--) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800760 batches.remove(i);
761 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800762 intentState = FAILED;
763 batches.addAll(uninstallIntent(intent, installables));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800764
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800765 // TODO we might want to try to recompile the new intent
Brian O'Connor427a1762014-11-19 18:40:32 -0800766 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800767 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800768
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800769 private class Withdrawn implements CompletedIntentUpdate {
Brian O'Connor427a1762014-11-19 18:40:32 -0800770
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800771 private final Intent intent;
772 private final List<Intent> installables;
773 private final List<FlowRuleBatchOperation> batches;
774 private int currentBatch;
775
776 Withdrawn(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
777 this.intent = checkNotNull(intent);
778 this.installables = ImmutableList.copyOf(installables);
779 this.batches = new LinkedList<>(batches);
780 this.currentBatch = 0;
781 }
782
783 @Override
784 public List<Intent> allInstallables() {
785 return installables;
786 }
787
788 @Override
789 public void batchSuccess() {
790 currentBatch++;
791 }
792
793 @Override
794 public void writeAfterExecution(BatchWrite batchWrite) {
795 batchWrite.setState(intent, WITHDRAWN);
796 batchWrite.removeInstalledIntents(intent.id());
797 batchWrite.removeIntent(intent.id());
798 }
799
800 @Override
801 public FlowRuleBatchOperation currentBatch() {
802 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
803 }
804
805 @Override
806 public void batchFailed() {
807 // the current batch has failed, so recompile
808 // remove the current batch and all remaining
809 for (int i = batches.size() - 1; i >= currentBatch; i--) {
810 batches.remove(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800811 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800812 batches.addAll(uninstallIntent(intent, installables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700813 }
814 }
815
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800816 private class Replaced implements CompletedIntentUpdate {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700817
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800818 private final Intent newIntent;
819 private final Intent oldIntent;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700820
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800821 private final List<Intent> newInstallables;
822 private final List<Intent> oldInstallables;
823 private final List<FlowRuleBatchOperation> batches;
824 private int currentBatch;
Brian O'Connor427a1762014-11-19 18:40:32 -0800825
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800826 Replaced(Intent newIntent, Intent oldIntent,
827 List<Intent> newInstallables, List<Intent> oldInstallables,
828 List<FlowRuleBatchOperation> batches) {
829 this.newIntent = checkNotNull(newIntent);
830 this.oldIntent = checkNotNull(oldIntent);
831 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
832 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
833 this.batches = new LinkedList<>(batches);
834 this.currentBatch = 0;
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800835 }
836
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800837 @Override
838 public List<Intent> allInstallables() {
839 LinkedList<Intent> allInstallables = new LinkedList<>();
840 allInstallables.addAll(newInstallables);
841 allInstallables.addAll(oldInstallables);
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800842
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800843 return allInstallables;
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800844 }
845
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800846 @Override
847 public void batchSuccess() {
848 currentBatch++;
Brian O'Connor427a1762014-11-19 18:40:32 -0800849 }
850
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800851 @Override
852 public void writeAfterExecution(BatchWrite batchWrite) {
853 batchWrite.setState(newIntent, INSTALLED);
854 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800855
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800856 batchWrite.setState(oldIntent, WITHDRAWN);
857 batchWrite.removeInstalledIntents(oldIntent.id());
858 batchWrite.removeIntent(oldIntent.id());
859 }
860
861 @Override
862 public FlowRuleBatchOperation currentBatch() {
863 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
864 }
865
866 @Override
867 public void batchFailed() {
868 // the current batch has failed, so recompile
869 // remove the current batch and all remaining
870 for (int i = batches.size() - 1; i >= currentBatch; i--) {
871 batches.remove(i);
872 }
873 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
874
875 batches.addAll(uninstallIntent(newIntent, newInstallables));
876
877 // TODO we might want to try to recompile the new intent
878 }
879 }
880
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800881 private class InstallingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800882
883 private final Intent intent;
884 private final List<Intent> installables;
885 private final List<FlowRuleBatchOperation> batches;
886 private int currentBatch = 0;
887
888 InstallingFailed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
889 this.intent = checkNotNull(intent);
890 this.installables = ImmutableList.copyOf(checkNotNull(installables));
891 this.batches = new LinkedList<>(checkNotNull(batches));
892 }
893
894 @Override
895 public List<Intent> allInstallables() {
896 return installables;
897 }
898
899 @Override
900 public void batchSuccess() {
901 currentBatch++;
902 }
903
904 @Override
905 public void writeAfterExecution(BatchWrite batchWrite) {
906 batchWrite.setState(intent, FAILED);
907 batchWrite.removeInstalledIntents(intent.id());
908 }
909
910 @Override
911 public FlowRuleBatchOperation currentBatch() {
912 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
913 }
914
915 @Override
916 public void batchFailed() {
917 // the current batch has failed, so recompile
918 // remove the current batch and all remaining
919 for (int i = batches.size() - 1; i >= currentBatch; i--) {
920 batches.remove(i);
921 }
922 batches.addAll(uninstallIntent(intent, installables));
923
924 // TODO we might want to try to recompile the new intent
925 }
926 }
927
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800928 private class ReplacingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800929
930 private final Intent newIntent;
931 private final Intent oldIntent;
932 private final List<Intent> newInstallables;
933 private final List<Intent> oldInstallables;
934 private final List<FlowRuleBatchOperation> batches;
935 private int currentBatch;
936
937 ReplacingFailed(Intent newIntent, Intent oldIntent,
938 List<Intent> newInstallables, List<Intent> oldInstallables,
939 List<FlowRuleBatchOperation> batches) {
940 this.newIntent = checkNotNull(newIntent);
941 this.oldIntent = checkNotNull(oldIntent);
942 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
943 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
944 this.batches = new LinkedList<>(batches);
945 this.currentBatch = 0;
946 }
947
948 @Override
949 public List<Intent> allInstallables() {
950 LinkedList<Intent> allInstallables = new LinkedList<>();
951 allInstallables.addAll(newInstallables);
952 allInstallables.addAll(oldInstallables);
953
954 return allInstallables;
955 }
956
957 @Override
958 public void batchSuccess() {
959 currentBatch++;
960 }
961
962 @Override
963 public void writeAfterExecution(BatchWrite batchWrite) {
964 batchWrite.setState(newIntent, FAILED);
965 batchWrite.removeInstalledIntents(newIntent.id());
966
967 batchWrite.setState(oldIntent, WITHDRAWN);
968 batchWrite.removeInstalledIntents(oldIntent.id());
969 batchWrite.removeIntent(oldIntent.id());
970 }
971
972 @Override
973 public FlowRuleBatchOperation currentBatch() {
974 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
975 }
976
977 @Override
978 public void batchFailed() {
979 // the current batch has failed, so recompile
980 // remove the current batch and all remaining
981 for (int i = batches.size() - 1; i >= currentBatch; i--) {
982 batches.remove(i);
983 }
984 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
985
986 batches.addAll(uninstallIntent(newIntent, newInstallables));
987
988 // TODO we might want to try to recompile the new intent
989 }
990 }
991
992 private class IntentBatchPreprocess implements Runnable {
993
994 // TODO make this configurable
995 private static final int TIMEOUT_PER_OP = 500; // ms
996 protected static final int MAX_ATTEMPTS = 3;
997
Brian O'Connorb499b352015-02-03 16:46:15 -0800998 protected final Collection<IntentData> ops;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800999
1000 // future holding current FlowRuleBatch installation result
1001 protected final long startTime = System.currentTimeMillis();
1002 protected final long endTime;
1003
Brian O'Connorb499b352015-02-03 16:46:15 -08001004 private IntentBatchPreprocess(Collection<IntentData> ops, long endTime) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001005 this.ops = checkNotNull(ops);
1006 this.endTime = endTime;
1007 }
1008
Brian O'Connorb499b352015-02-03 16:46:15 -08001009 public IntentBatchPreprocess(Collection<IntentData> ops) {
1010 this(ops, System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001011 }
1012
1013 // FIXME compute reasonable timeouts
1014 protected long calculateTimeoutLimit() {
Brian O'Connorb499b352015-02-03 16:46:15 -08001015 return System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001016 }
1017
1018 @Override
1019 public void run() {
1020 try {
1021 // this should only be called on the first iteration
1022 // note: this a "expensive", so it is not done in the constructor
1023
1024 // - creates per Intent installation context (IntentUpdate)
1025 // - write Intents to store
1026 // - process (compile, install, etc.) each Intents
1027 // - generate FlowRuleBatch for this phase
1028 // build IntentUpdates
1029 List<IntentUpdate> updates = createIntentUpdates();
1030
1031 // Write batch information
1032 BatchWrite batchWrite = createBatchWrite(updates);
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001033 store.batchWrite(batchWrite);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001034
1035 new IntentBatchApplyFirst(ops, processIntentUpdates(updates), endTime, 0, null).run();
1036 } catch (Exception e) {
1037 log.error("Error submitting batches:", e);
1038 // FIXME incomplete Intents should be cleaned up
1039 // (transition to FAILED, etc.)
1040
1041 // TODO: remove duplicate due to inlining
1042 // the batch has failed
1043 // TODO: maybe we should do more?
1044 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -08001045 //FIXME
1046// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001047 }
1048 }
1049
1050 private List<IntentUpdate> createIntentUpdates() {
Brian O'Connorb499b352015-02-03 16:46:15 -08001051 return ops.stream()
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001052 .map(IntentManager.this::createIntentUpdate)
1053 .collect(Collectors.toList());
1054 }
1055
1056 private BatchWrite createBatchWrite(List<IntentUpdate> updates) {
1057 BatchWrite batchWrite = BatchWrite.newInstance();
1058 updates.forEach(update -> update.writeBeforeExecution(batchWrite));
1059 return batchWrite;
1060 }
1061
1062 private List<CompletedIntentUpdate> processIntentUpdates(List<IntentUpdate> updates) {
1063 // start processing each Intents
1064 List<CompletedIntentUpdate> completed = new ArrayList<>();
1065 for (IntentUpdate update : updates) {
1066 Optional<IntentUpdate> phase = Optional.of(update);
1067 IntentUpdate previous = update;
1068 while (true) {
1069 if (!phase.isPresent()) {
1070 // FIXME: not type safe cast
1071 completed.add((CompletedIntentUpdate) previous);
1072 break;
1073 }
1074 previous = phase.get();
1075 phase = previous.execute();
1076 }
1077 }
1078
1079 return completed;
1080 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001081 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001082
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001083 // TODO: better naming
1084 private class IntentBatchApplyFirst extends IntentBatchPreprocess {
1085
1086 protected final List<CompletedIntentUpdate> intentUpdates;
1087 protected final int installAttempt;
1088 protected Future<CompletedBatchOperation> future;
1089
Brian O'Connorb499b352015-02-03 16:46:15 -08001090 IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001091 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1092 super(operations, endTime);
1093 this.intentUpdates = ImmutableList.copyOf(intentUpdates);
1094 this.future = future;
1095 this.installAttempt = installAttempt;
1096 }
1097
1098 @Override
1099 public void run() {
1100 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1101 new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future).run();
Brian O'Connorcb900f42014-10-07 21:55:33 -07001102 }
1103
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001104 /**
Brian O'Connor427a1762014-11-19 18:40:32 -08001105 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001106 *
1107 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001108 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001109 protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001110 //TODO test this. (also, maybe save this batch)
Brian O'Connor72cb19a2015-01-16 16:14:41 -08001111
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001112 FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
Ray Milkey93508c22014-12-02 11:35:56 -08001113 if (batch.size() > 0) {
1114 //FIXME apply batch might throw an exception
1115 return flowRuleService.applyBatch(batch);
1116 } else {
1117 // there are no flow rule batches; finalize the intent update
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001118 BatchWrite batchWrite = createFinalizedBatchWrite(updates);
1119
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001120 store.batchWrite(batchWrite);
Ray Milkey93508c22014-12-02 11:35:56 -08001121 return null;
1122 }
1123 }
1124
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001125 private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -08001126 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001127 for (CompletedIntentUpdate update : intentUpdates) {
1128 FlowRuleBatchOperation currentBatch = update.currentBatch();
1129 if (currentBatch != null) {
1130 batch.addAll(currentBatch);
1131 }
1132 }
1133 return batch;
1134 }
1135
1136 private BatchWrite createFinalizedBatchWrite(List<CompletedIntentUpdate> intentUpdates) {
1137 BatchWrite batchWrite = BatchWrite.newInstance();
1138 for (CompletedIntentUpdate update : intentUpdates) {
1139 update.writeAfterExecution(batchWrite);
1140 }
1141 return batchWrite;
1142 }
1143
1144 protected void abandonShip() {
1145 // the batch has failed
1146 // TODO: maybe we should do more?
1147 log.error("Walk the plank, matey...");
1148 future = null;
Brian O'Connorb499b352015-02-03 16:46:15 -08001149 //FIXME
1150// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001151 }
1152 }
1153
1154 // TODO: better naming
1155 private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
1156
Brian O'Connorb499b352015-02-03 16:46:15 -08001157 IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001158 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1159 super(operations, intentUpdates, endTime, installAttempt, future);
1160 }
1161
1162 @Override
1163 public void run() {
1164 try {
1165 // - peek if current FlowRuleBatch is complete
1166 // -- If complete OK:
1167 // step each IntentUpdate forward
1168 // If phase left: generate next FlowRuleBatch
1169 // If no more phase: write parking states
1170 // -- If complete FAIL:
1171 // Intent which failed: transition Intent to FAILED
1172 // Other Intents: resubmit same FlowRuleBatch for this phase
1173 Future<CompletedBatchOperation> future = processFutures();
1174 if (future == null) {
1175 // there are no outstanding batches; we are done
Brian O'Connorb499b352015-02-03 16:46:15 -08001176 //FIXME
1177 return; //?
1178// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001179 } else if (System.currentTimeMillis() > endTime) {
1180 // - cancel current FlowRuleBatch and resubmit again
1181 retry();
1182 } else {
1183 // we are not done yet, yield the thread by resubmitting ourselves
1184 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future));
1185 }
1186 } catch (Exception e) {
1187 log.error("Error submitting batches:", e);
1188 // FIXME incomplete Intents should be cleaned up
1189 // (transition to FAILED, etc.)
1190 abandonShip();
1191 }
1192 }
1193
1194 /**
1195 * Iterate through the pending futures, and remove them when they have completed.
1196 */
1197 private Future<CompletedBatchOperation> processFutures() {
1198 try {
1199 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
1200 updateBatches(completed);
1201 return applyNextBatch(intentUpdates);
1202 } catch (TimeoutException | InterruptedException te) {
1203 log.trace("Installation of intents are still pending: {}", ops);
1204 return future;
1205 } catch (ExecutionException e) {
1206 log.warn("Execution of batch failed: {}", ops, e);
1207 abandonShip();
1208 return future;
1209 }
1210 }
1211
Ray Milkey93508c22014-12-02 11:35:56 -08001212 private void updateBatches(CompletedBatchOperation completed) {
1213 if (completed.isSuccess()) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001214 for (CompletedIntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -08001215 update.batchSuccess();
1216 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001217 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -08001218 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -08001219 log.debug("Failed items: {}", completed.failedItems());
1220 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -08001221
1222 for (Long id : completed.failedIds()) {
1223 IntentId targetId = IntentId.valueOf(id);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001224 for (CompletedIntentUpdate update : intentUpdates) {
1225 for (Intent intent : update.allInstallables()) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001226 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001227 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001228 break;
1229 }
1230 }
1231 }
1232 // don't increment the non-failed items, as they have been reverted.
1233 }
1234 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001235 }
1236
Brian O'Connor427a1762014-11-19 18:40:32 -08001237 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -08001238 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001239 if (future.cancel(true)) { // cancel success; batch is reverted
1240 // reset the timer
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001241 long timeLimit = calculateTimeoutLimit();
1242 int attempts = installAttempt + 1;
1243 if (attempts == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001244 log.warn("Install request timed out: {}", ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001245 for (CompletedIntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001246 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001247 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001248 } else if (attempts > MAX_ATTEMPTS) {
Ray Milkey93508c22014-12-02 11:35:56 -08001249 abandonShip();
1250 return;
Brian O'Connor427a1762014-11-19 18:40:32 -08001251 } // else just resubmit the work
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001252 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1253 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, timeLimit, attempts, future));
Brian O'Connor427a1762014-11-19 18:40:32 -08001254 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001255 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001256 // FIXME
1257 // cancel failed... batch is broken; shouldn't happen!
1258 // we could manually reverse everything
1259 // ... or just core dump and send email to Ali
Ray Milkey93508c22014-12-02 11:35:56 -08001260 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -08001261 }
1262 }
Brian O'Connorcb900f42014-10-07 21:55:33 -07001263 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001264
1265 private class InternalBatchDelegate implements IntentBatchDelegate {
1266 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -08001267 public void execute(Collection<IntentData> operations) {
1268 log.info("Execute {} operation(s).", operations.size());
1269 log.debug("Execute operations: {}", operations);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001270 executor.execute(new IntentBatchPreprocess(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001271 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001272 }
Brian O'Connor66630c82014-10-02 21:08:19 -07001273}