blob: a91fa0a2d19958d46e1f69d51064f7f154acf221 [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
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'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.core.IdGenerator;
31import org.onosproject.event.AbstractListenerRegistry;
32import org.onosproject.event.EventDeliveryService;
33import org.onosproject.net.flow.CompletedBatchOperation;
34import org.onosproject.net.flow.FlowRuleBatchOperation;
35import org.onosproject.net.flow.FlowRuleService;
Brian O'Connorb499b352015-02-03 16:46:15 -080036import org.onosproject.net.intent.BatchWrite;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.net.intent.Intent;
38import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080040import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.intent.IntentEvent;
42import org.onosproject.net.intent.IntentException;
43import org.onosproject.net.intent.IntentExtensionService;
44import org.onosproject.net.intent.IntentId;
45import org.onosproject.net.intent.IntentInstaller;
46import org.onosproject.net.intent.IntentListener;
47import org.onosproject.net.intent.IntentOperation;
48import org.onosproject.net.intent.IntentOperations;
49import org.onosproject.net.intent.IntentService;
50import org.onosproject.net.intent.IntentState;
51import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070053import org.slf4j.Logger;
54
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080055import java.util.ArrayList;
Brian O'Connorb499b352015-02-03 16:46:15 -080056import java.util.Collection;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080057import java.util.Collections;
58import java.util.EnumSet;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080059import java.util.LinkedList;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080060import java.util.List;
61import java.util.Map;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080062import java.util.Optional;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080063import java.util.concurrent.ConcurrentHashMap;
64import java.util.concurrent.ConcurrentMap;
65import java.util.concurrent.ExecutionException;
66import java.util.concurrent.ExecutorService;
67import java.util.concurrent.Future;
68import java.util.concurrent.TimeUnit;
69import java.util.concurrent.TimeoutException;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080070import java.util.stream.Collectors;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070071
Brian O'Connorfa81eae2014-10-30 13:20:05 -070072import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080073import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080074import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070075import static org.onlab.util.Tools.namedThreads;
Brian O'Connorb499b352015-02-03 16:46:15 -080076import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070077import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070078
79/**
80 * An implementation of Intent Manager.
81 */
82@Component(immediate = true)
83@Service
84public class IntentManager
85 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080086 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070087
88 public static final String INTENT_NULL = "Intent cannot be null";
89 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
90
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080091 private static final int NUM_THREADS = 12;
92
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080093 private static final EnumSet<IntentState> RECOMPILE
94 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080095
96
Brian O'Connor66630c82014-10-02 21:08:19 -070097 // Collections for compiler, installer, and listener are ONOS instance local
98 private final ConcurrentMap<Class<? extends Intent>,
99 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700100 private final ConcurrentMap<Class<? extends Intent>,
101 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700102
103 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700104 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700105
Brian O'Connor520c0522014-11-23 23:50:47 -0800106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected IntentStore store;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700113 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700116 protected EventDeliveryService eventDispatcher;
117
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected FlowRuleService flowRuleService;
120
Brian O'Connor520c0522014-11-23 23:50:47 -0800121
122 private ExecutorService executor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800123
124 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
125 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
126 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
127 private IdGenerator idGenerator;
128
Brian O'Connorb499b352015-02-03 16:46:15 -0800129 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800130
Brian O'Connor66630c82014-10-02 21:08:19 -0700131 @Activate
132 public void activate() {
133 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700134 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700135 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Yuta HIGUCHI7a9fddd2014-12-03 15:30:25 -0800136 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-%d"));
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'Connor66630c82014-10-02 21:08:19 -0700146 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700147 executor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800148 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700149 log.info("Stopped");
150 }
151
152 @Override
153 public void submit(Intent intent) {
154 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800155 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
156 //FIXME timestamp?
157 store.addPending(data);
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'Connorcff03322015-02-03 15:28:59 -0800163 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
164 //FIXME timestamp?
165 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700166 }
167
Brian O'Connor66630c82014-10-02 21:08:19 -0700168 @Override
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700169 public void replace(IntentId oldIntentId, Intent newIntent) {
Brian O'Connorcff03322015-02-03 15:28:59 -0800170 throw new UnsupportedOperationException("replace is not implemented");
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700171 }
172
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700173 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700174 public void execute(IntentOperations operations) {
Brian O'Connorcff03322015-02-03 15:28:59 -0800175 for (IntentOperation op : operations.operations()) {
176 switch (op.type()) {
177 case SUBMIT:
178 case UPDATE:
179 submit(op.intent());
180 break;
181 case WITHDRAW:
182 withdraw(op.intent());
183 break;
184 //fallthrough
185 case REPLACE:
186 default:
187 throw new UnsupportedOperationException("replace not supported");
188 }
Brian O'Connore2ff25a2014-11-18 19:25:43 -0800189 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700190 }
191
192 @Override
193 public Iterable<Intent> getIntents() {
194 return store.getIntents();
195 }
196
197 @Override
198 public long getIntentCount() {
199 return store.getIntentCount();
200 }
201
202 @Override
203 public Intent getIntent(IntentId id) {
204 checkNotNull(id, INTENT_ID_NULL);
205 return store.getIntent(id);
206 }
207
208 @Override
209 public IntentState getIntentState(IntentId id) {
210 checkNotNull(id, INTENT_ID_NULL);
211 return store.getIntentState(id);
212 }
213
214 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700215 public List<Intent> getInstallableIntents(IntentId intentId) {
216 checkNotNull(intentId, INTENT_ID_NULL);
217 return store.getInstallableIntents(intentId);
218 }
219
220 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700221 public void addListener(IntentListener listener) {
222 listenerRegistry.addListener(listener);
223 }
224
225 @Override
226 public void removeListener(IntentListener listener) {
227 listenerRegistry.removeListener(listener);
228 }
229
230 @Override
231 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
232 compilers.put(cls, compiler);
233 }
234
235 @Override
236 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
237 compilers.remove(cls);
238 }
239
240 @Override
241 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
242 return ImmutableMap.copyOf(compilers);
243 }
244
245 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700246 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700247 installers.put(cls, installer);
248 }
249
250 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700251 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700252 installers.remove(cls);
253 }
254
255 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700256 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700257 return ImmutableMap.copyOf(installers);
258 }
259
260 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700261 * Returns the corresponding intent compiler to the specified intent.
262 *
263 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700264 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700265 * @return intent compiler corresponding to the specified intent
266 */
267 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
268 @SuppressWarnings("unchecked")
269 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
270 if (compiler == null) {
271 throw new IntentException("no compiler for class " + intent.getClass());
272 }
273 return compiler;
274 }
275
276 /**
277 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700278 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700279 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700280 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700281 * @return intent installer corresponding to the specified installable intent
282 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700283 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700284 @SuppressWarnings("unchecked")
285 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
286 if (installer == null) {
287 throw new IntentException("no installer for class " + intent.getClass());
288 }
289 return installer;
290 }
291
292 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700293 * Compiles an intent recursively.
294 *
295 * @param intent intent
296 * @return result of compilation
297 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800298 private List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700299 if (intent.isInstallable()) {
300 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700301 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700302
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700303 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700304 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700305 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800306 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
307 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700308 }
tom85258ee2014-10-07 00:10:02 -0700309 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700310 }
311
312 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700313 * Uninstalls all installable intents associated with the given intent.
314 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800315 * @param intent intent
316 * @param installables installable intents
317 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700318 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800319 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700320 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800321 for (Intent installable : installables) {
322 trackerService.removeTrackedResources(intent.id(),
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800323 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700324 try {
325 batches.addAll(getInstaller(installable).uninstall(installable));
326 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800327 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700328 // TODO: this should never happen. but what if it does?
329 }
330 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800331 return batches;
tom85258ee2014-10-07 00:10:02 -0700332 }
333
334 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700335 * Registers an intent compiler of the specified intent if an intent compiler
336 * for the intent is not registered. This method traverses the class hierarchy of
337 * the intent. Once an intent compiler for a parent type is found, this method
338 * registers the found intent compiler.
339 *
340 * @param intent intent
341 */
342 private void registerSubclassCompilerIfNeeded(Intent intent) {
343 if (!compilers.containsKey(intent.getClass())) {
344 Class<?> cls = intent.getClass();
345 while (cls != Object.class) {
346 // As long as we're within the Intent class descendants
347 if (Intent.class.isAssignableFrom(cls)) {
348 IntentCompiler<?> compiler = compilers.get(cls);
349 if (compiler != null) {
350 compilers.put(intent.getClass(), compiler);
351 return;
352 }
353 }
354 cls = cls.getSuperclass();
355 }
356 }
357 }
358
359 /**
360 * Registers an intent installer of the specified intent if an intent installer
361 * for the intent is not registered. This method traverses the class hierarchy of
362 * the intent. Once an intent installer for a parent type is found, this method
363 * registers the found intent installer.
364 *
365 * @param intent intent
366 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700367 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700368 if (!installers.containsKey(intent.getClass())) {
369 Class<?> cls = intent.getClass();
370 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700371 // As long as we're within the Intent class descendants
372 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700373 IntentInstaller<?> installer = installers.get(cls);
374 if (installer != null) {
375 installers.put(intent.getClass(), installer);
376 return;
377 }
378 }
379 cls = cls.getSuperclass();
380 }
381 }
382 }
383
Brian O'Connor66630c82014-10-02 21:08:19 -0700384 // Store delegate to re-post events emitted from the store.
385 private class InternalStoreDelegate implements IntentStoreDelegate {
386 @Override
387 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700388 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700389 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800390
391 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800392 public void process(IntentData data) {
393 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800394 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700395 }
396
Brian O'Connor72a034c2014-11-26 18:24:23 -0800397 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
398 boolean compileAllFailed) {
399 Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
400 // Attempt recompilation of the specified intents first.
401 for (IntentId id : intentIds) {
402 Intent intent = store.getIntent(id);
403 if (intent == null) {
404 continue;
405 }
406 IntentOperations.Builder builder = batches.get(intent.appId());
407 if (builder == null) {
408 builder = IntentOperations.builder(intent.appId());
409 batches.put(intent.appId(), builder);
410 }
411 builder.addUpdateOperation(id);
412 }
413
414 if (compileAllFailed) {
415 // If required, compile all currently failed intents.
416 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800417 IntentState state = getIntentState(intent.id());
418 if (RECOMPILE.contains(state)) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800419 IntentOperations.Builder builder = batches.get(intent.appId());
420 if (builder == null) {
421 builder = IntentOperations.builder(intent.appId());
422 batches.put(intent.appId(), builder);
423 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800424 if (state == WITHDRAW_REQ) {
425 builder.addWithdrawOperation(intent.id());
426 } else {
427 builder.addUpdateOperation(intent.id());
428 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800429 }
430 }
431 }
432
Brian O'Connorb499b352015-02-03 16:46:15 -0800433 //FIXME
434// for (ApplicationId appId : batches.keySet()) {
435// if (batchService.isLocalLeader(appId)) {
436// execute(batches.get(appId).build());
437// }
438// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800439 }
440
tom95329eb2014-10-06 08:40:06 -0700441 // Topology change delegate
442 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
443 @Override
tom85258ee2014-10-07 00:10:02 -0700444 public void triggerCompile(Iterable<IntentId> intentIds,
445 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800446 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700447 }
tom95329eb2014-10-06 08:40:06 -0700448 }
tom85258ee2014-10-07 00:10:02 -0700449
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800450 // TODO: simplify the branching statements
Brian O'Connorb499b352015-02-03 16:46:15 -0800451 private IntentUpdate createIntentUpdate(IntentData intentData) {
452 IntentData currentState = store.getIntentData(intentData.key());
453 switch (intentData.state()) {
454 case INSTALL_REQ:
455 return new InstallRequest(intentData.intent(), currentState);
456 case WITHDRAW_REQ:
457 return new WithdrawRequest(intentData.intent(), currentState);
458 // fallthrough
459 case COMPILING:
460 case INSTALLING:
461 case INSTALLED:
462 case RECOMPILING:
463 case WITHDRAWING:
464 case WITHDRAWN:
465 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800466 default:
467 // illegal state
468 return new DoNothing();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700469 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700470 }
471
Brian O'Connorcff03322015-02-03 15:28:59 -0800472 // TODO pull out the IntentUpdate inner classes
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800473 private class InstallRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800474
475 private final Intent intent;
Brian O'Connorb499b352015-02-03 16:46:15 -0800476 private final IntentData currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800477
Brian O'Connorb499b352015-02-03 16:46:15 -0800478 InstallRequest(Intent intent, IntentData currentState) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800479 this.intent = checkNotNull(intent);
Brian O'Connorb499b352015-02-03 16:46:15 -0800480 this.currentState = currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800481 }
482
483 @Override
484 public void writeBeforeExecution(BatchWrite batchWrite) {
485 // TODO consider only "creating" intent if it does not exist
486 // Note: We need to set state to INSTALL_REQ regardless.
487 batchWrite.createIntent(intent);
488 }
489
490 @Override
491 public Optional<IntentUpdate> execute() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800492 return Optional.of(new Compiling(intent)); //FIXME
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800493 }
494 }
495
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800496 private class WithdrawRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800497
498 private final Intent intent;
Brian O'Connorb499b352015-02-03 16:46:15 -0800499 private final IntentData currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800500
Brian O'Connorb499b352015-02-03 16:46:15 -0800501 WithdrawRequest(Intent intent, IntentData currentState) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800502 this.intent = checkNotNull(intent);
Brian O'Connorb499b352015-02-03 16:46:15 -0800503 this.currentState = currentState;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800504 }
505
506 @Override
507 public void writeBeforeExecution(BatchWrite batchWrite) {
508 batchWrite.setState(intent, WITHDRAW_REQ);
509 }
510
511 @Override
512 public Optional<IntentUpdate> execute() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800513 return Optional.of(new Withdrawing(intent, currentState.installables())); //FIXME
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800514 }
515 }
516
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800517 private class ReplaceRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800518
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700519 private final Intent newIntent;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800520 private final Intent oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700521 private final List<Intent> oldInstallables;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700522
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800523 ReplaceRequest(Intent newIntent, Intent oldIntent, List<Intent> oldInstallables) {
524 this.newIntent = checkNotNull(newIntent);
525 this.oldIntent = checkNotNull(oldIntent);
526 this.oldInstallables = ImmutableList.copyOf(oldInstallables);
527 }
528
529 @Override
530 public void writeBeforeExecution(BatchWrite batchWrite) {
531 // TODO consider only "creating" intent if it does not exist
532 // Note: We need to set state to INSTALL_REQ regardless.
533 batchWrite.createIntent(newIntent);
534 }
535
536 @Override
537 public Optional<IntentUpdate> execute() {
538 try {
539 List<Intent> installables = compileIntent(newIntent, oldInstallables);
540 return Optional.of(new Replacing(newIntent, oldIntent, installables, oldInstallables));
541 } catch (PathNotFoundException e) {
542 log.debug("Path not found for intent {}", newIntent);
543 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
544 } catch (IntentException e) {
545 log.warn("Unable to compile intent {} due to:", newIntent.id(), e);
546 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700547 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800548 }
549 }
550
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800551 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800552 private class WithdrawStateChange1 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800553
554 private final Intent intent;
555
556 WithdrawStateChange1(Intent intent) {
557 this.intent = checkNotNull(intent);
558 }
559
560 @Override
561 public void writeBeforeExecution(BatchWrite batchWrite) {
562 batchWrite.setState(intent, WITHDRAW_REQ);
563 }
564
565 @Override
566 public void writeAfterExecution(BatchWrite batchWrite) {
567 batchWrite.setState(intent, WITHDRAWN);
568 batchWrite.removeInstalledIntents(intent.id());
569 batchWrite.removeIntent(intent.id());
570 }
571 }
572
573 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800574 private class WithdrawStateChange2 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800575
576 private final Intent intent;
577
578 WithdrawStateChange2(Intent intent) {
579 this.intent = checkNotNull(intent);
580 }
581
582 @Override
583 public void writeBeforeExecution(BatchWrite batchWrite) {
584 // TODO consider only "creating" intent if it does not exist
585 // Note: We need to set state to INSTALL_REQ regardless.
586 batchWrite.createIntent(intent);
587 }
588
589 @Override
590 public void writeAfterExecution(BatchWrite batchWrite) {
591 batchWrite.setState(intent, WITHDRAWN);
592 batchWrite.removeInstalledIntents(intent.id());
593 batchWrite.removeIntent(intent.id());
594 }
595 }
596
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800597 private class Compiling implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800598
599 private final Intent intent;
600
601 Compiling(Intent intent) {
602 this.intent = checkNotNull(intent);
603 }
604
605 @Override
606 public Optional<IntentUpdate> execute() {
607 try {
608 // Compile the intent into installable derivatives.
609 // If all went well, associate the resulting list of installable
610 // intents with the top-level intent and proceed to install.
611 return Optional.of(new Installing(intent, compileIntent(intent, null)));
612 } catch (PathNotFoundException e) {
Sho SHIMIZUfd0cd8c2015-01-28 08:16:58 -0800613 log.debug("Path not found for intent {}", intent);
614 return Optional.of(new CompilingFailed(intent));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800615 } catch (IntentException e) {
Sho SHIMIZUfd0cd8c2015-01-28 08:16:58 -0800616 log.warn("Unable to compile intent {} due to:", intent.id(), e);
617 return Optional.of(new CompilingFailed(intent));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800618 }
619 }
620 }
621
622 // TODO: better naming because install() method actually generate FlowRuleBatchOperations
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800623 private class Installing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800624
625 private final Intent intent;
626 private final List<Intent> installables;
627
628 Installing(Intent intent, List<Intent> installables) {
629 this.intent = checkNotNull(intent);
630 this.installables = ImmutableList.copyOf(checkNotNull(installables));
631 }
632
633 @Override
634 public Optional<IntentUpdate> execute() {
635 Exception exception = null;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800636
637 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
638 for (Intent installable : installables) {
639 registerSubclassInstallerIfNeeded(installable);
640 trackerService.addTrackedResources(intent.id(), installable.resources());
641 try {
642 batches.addAll(getInstaller(installable).install(installable));
643 } catch (Exception e) { // TODO this should be IntentException
644 log.warn("Unable to install intent {} due to:", intent.id(), e);
645 trackerService.removeTrackedResources(intent.id(), installable.resources());
646 //TODO we failed; intent should be recompiled
647 exception = e;
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800648 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700649 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700650
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800651 if (exception != null) {
652 return Optional.of(new InstallingFailed(intent, installables, batches));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800653 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800654
655 return Optional.of(new Installed(intent, installables, batches));
656 }
657 }
658
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800659 private class Withdrawing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800660
661 private final Intent intent;
662 private final List<Intent> installables;
663
664 Withdrawing(Intent intent, List<Intent> installables) {
665 this.intent = checkNotNull(intent);
666 this.installables = ImmutableList.copyOf(installables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800667 }
668
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800669 @Override
670 public Optional<IntentUpdate> execute() {
671 List<FlowRuleBatchOperation> batches = uninstallIntent(intent, installables);
672
673 return Optional.of(new Withdrawn(intent, installables, batches));
674 }
675 }
676
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800677 private class Replacing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800678
679 private final Intent newIntent;
680 private final Intent oldIntent;
681 private final List<Intent> newInstallables;
682 private final List<Intent> oldInstallables;
683
684 private Exception exception;
685
686 Replacing(Intent newIntent, Intent oldIntent,
687 List<Intent> newInstallables, List<Intent> oldInstallables) {
688 this.newIntent = checkNotNull(newIntent);
689 this.oldIntent = checkNotNull(oldIntent);
690 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
691 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700692 }
693
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800694 @Override
695 public Optional<IntentUpdate> execute() {
696 List<FlowRuleBatchOperation> batches = replace();
697
698 if (exception == null) {
699 return Optional.of(
700 new Replaced(newIntent, oldIntent, newInstallables, oldInstallables, batches));
701 }
702
703 return Optional.of(
704 new ReplacingFailed(newIntent, oldIntent, newInstallables, oldInstallables, batches));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700705 }
706
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800707 protected List<FlowRuleBatchOperation> replace() {
708 checkState(oldInstallables.size() == newInstallables.size(),
709 "Old and New Intent must have equivalent installable intents.");
710
711 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
712 for (int i = 0; i < oldInstallables.size(); i++) {
713 Intent oldInstallable = oldInstallables.get(i);
714 Intent newInstallable = newInstallables.get(i);
715 //FIXME revisit this
716// if (oldInstallable.equals(newInstallable)) {
717// continue;
718// }
719 checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
720 "Installable Intent type mismatch.");
721 trackerService.removeTrackedResources(oldIntent.id(), oldInstallable.resources());
722 trackerService.addTrackedResources(newIntent.id(), newInstallable.resources());
723 try {
724 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
725 } catch (IntentException e) {
726 log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
727 //FIXME... we failed. need to uninstall (if same) or revert (if different)
728 trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
729 exception = e;
730 batches = uninstallIntent(oldIntent, oldInstallables);
731 }
732 }
733 return batches;
734 }
735 }
736
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800737 private class Installed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800738
739 private final Intent intent;
740 private final List<Intent> installables;
741 private IntentState intentState;
742 private final List<FlowRuleBatchOperation> batches;
743 private int currentBatch = 0;
744
745 Installed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
746 this.intent = checkNotNull(intent);
747 this.installables = ImmutableList.copyOf(checkNotNull(installables));
748 this.batches = new LinkedList<>(checkNotNull(batches));
749 this.intentState = INSTALLING;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700750 }
751
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800752 @Override
753 public void batchSuccess() {
Ray Milkey93508c22014-12-02 11:35:56 -0800754 currentBatch++;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800755 }
756
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800757 @Override
758 public List<Intent> allInstallables() {
759 return installables;
760 }
761
762 @Override
763 public void writeAfterExecution(BatchWrite batchWrite) {
764 switch (intentState) {
765 case INSTALLING:
766 batchWrite.setState(intent, INSTALLED);
767 batchWrite.setInstallableIntents(intent.id(), this.installables);
768 break;
769 case FAILED:
770 batchWrite.setState(intent, FAILED);
771 batchWrite.removeInstalledIntents(intent.id());
772 break;
773 default:
774 break;
775 }
776 }
777
778 @Override
779 public FlowRuleBatchOperation currentBatch() {
780 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
781 }
782
783 @Override
784 public void batchFailed() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800785 // the current batch has failed, so recompile
786 // remove the current batch and all remaining
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800787 for (int i = batches.size() - 1; i >= currentBatch; i--) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800788 batches.remove(i);
789 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800790 intentState = FAILED;
791 batches.addAll(uninstallIntent(intent, installables));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800792
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800793 // TODO we might want to try to recompile the new intent
Brian O'Connor427a1762014-11-19 18:40:32 -0800794 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800795 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800796
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800797 private class Withdrawn implements CompletedIntentUpdate {
Brian O'Connor427a1762014-11-19 18:40:32 -0800798
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800799 private final Intent intent;
800 private final List<Intent> installables;
801 private final List<FlowRuleBatchOperation> batches;
802 private int currentBatch;
803
804 Withdrawn(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
805 this.intent = checkNotNull(intent);
806 this.installables = ImmutableList.copyOf(installables);
807 this.batches = new LinkedList<>(batches);
808 this.currentBatch = 0;
809 }
810
811 @Override
812 public List<Intent> allInstallables() {
813 return installables;
814 }
815
816 @Override
817 public void batchSuccess() {
818 currentBatch++;
819 }
820
821 @Override
822 public void writeAfterExecution(BatchWrite batchWrite) {
823 batchWrite.setState(intent, WITHDRAWN);
824 batchWrite.removeInstalledIntents(intent.id());
825 batchWrite.removeIntent(intent.id());
826 }
827
828 @Override
829 public FlowRuleBatchOperation currentBatch() {
830 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
831 }
832
833 @Override
834 public void batchFailed() {
835 // the current batch has failed, so recompile
836 // remove the current batch and all remaining
837 for (int i = batches.size() - 1; i >= currentBatch; i--) {
838 batches.remove(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800839 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800840 batches.addAll(uninstallIntent(intent, installables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700841 }
842 }
843
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800844 private class Replaced implements CompletedIntentUpdate {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700845
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800846 private final Intent newIntent;
847 private final Intent oldIntent;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700848
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800849 private final List<Intent> newInstallables;
850 private final List<Intent> oldInstallables;
851 private final List<FlowRuleBatchOperation> batches;
852 private int currentBatch;
Brian O'Connor427a1762014-11-19 18:40:32 -0800853
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800854 Replaced(Intent newIntent, Intent oldIntent,
855 List<Intent> newInstallables, List<Intent> oldInstallables,
856 List<FlowRuleBatchOperation> batches) {
857 this.newIntent = checkNotNull(newIntent);
858 this.oldIntent = checkNotNull(oldIntent);
859 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
860 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
861 this.batches = new LinkedList<>(batches);
862 this.currentBatch = 0;
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800863 }
864
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800865 @Override
866 public List<Intent> allInstallables() {
867 LinkedList<Intent> allInstallables = new LinkedList<>();
868 allInstallables.addAll(newInstallables);
869 allInstallables.addAll(oldInstallables);
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800870
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800871 return allInstallables;
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800872 }
873
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800874 @Override
875 public void batchSuccess() {
876 currentBatch++;
Brian O'Connor427a1762014-11-19 18:40:32 -0800877 }
878
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800879 @Override
880 public void writeAfterExecution(BatchWrite batchWrite) {
881 batchWrite.setState(newIntent, INSTALLED);
882 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800883
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800884 batchWrite.setState(oldIntent, WITHDRAWN);
885 batchWrite.removeInstalledIntents(oldIntent.id());
886 batchWrite.removeIntent(oldIntent.id());
887 }
888
889 @Override
890 public FlowRuleBatchOperation currentBatch() {
891 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
892 }
893
894 @Override
895 public void batchFailed() {
896 // the current batch has failed, so recompile
897 // remove the current batch and all remaining
898 for (int i = batches.size() - 1; i >= currentBatch; i--) {
899 batches.remove(i);
900 }
901 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
902
903 batches.addAll(uninstallIntent(newIntent, newInstallables));
904
905 // TODO we might want to try to recompile the new intent
906 }
907 }
908
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800909 private class InstallingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800910
911 private final Intent intent;
912 private final List<Intent> installables;
913 private final List<FlowRuleBatchOperation> batches;
914 private int currentBatch = 0;
915
916 InstallingFailed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
917 this.intent = checkNotNull(intent);
918 this.installables = ImmutableList.copyOf(checkNotNull(installables));
919 this.batches = new LinkedList<>(checkNotNull(batches));
920 }
921
922 @Override
923 public List<Intent> allInstallables() {
924 return installables;
925 }
926
927 @Override
928 public void batchSuccess() {
929 currentBatch++;
930 }
931
932 @Override
933 public void writeAfterExecution(BatchWrite batchWrite) {
934 batchWrite.setState(intent, FAILED);
935 batchWrite.removeInstalledIntents(intent.id());
936 }
937
938 @Override
939 public FlowRuleBatchOperation currentBatch() {
940 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
941 }
942
943 @Override
944 public void batchFailed() {
945 // the current batch has failed, so recompile
946 // remove the current batch and all remaining
947 for (int i = batches.size() - 1; i >= currentBatch; i--) {
948 batches.remove(i);
949 }
950 batches.addAll(uninstallIntent(intent, installables));
951
952 // TODO we might want to try to recompile the new intent
953 }
954 }
955
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800956 private class ReplacingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800957
958 private final Intent newIntent;
959 private final Intent oldIntent;
960 private final List<Intent> newInstallables;
961 private final List<Intent> oldInstallables;
962 private final List<FlowRuleBatchOperation> batches;
963 private int currentBatch;
964
965 ReplacingFailed(Intent newIntent, Intent oldIntent,
966 List<Intent> newInstallables, List<Intent> oldInstallables,
967 List<FlowRuleBatchOperation> batches) {
968 this.newIntent = checkNotNull(newIntent);
969 this.oldIntent = checkNotNull(oldIntent);
970 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
971 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
972 this.batches = new LinkedList<>(batches);
973 this.currentBatch = 0;
974 }
975
976 @Override
977 public List<Intent> allInstallables() {
978 LinkedList<Intent> allInstallables = new LinkedList<>();
979 allInstallables.addAll(newInstallables);
980 allInstallables.addAll(oldInstallables);
981
982 return allInstallables;
983 }
984
985 @Override
986 public void batchSuccess() {
987 currentBatch++;
988 }
989
990 @Override
991 public void writeAfterExecution(BatchWrite batchWrite) {
992 batchWrite.setState(newIntent, FAILED);
993 batchWrite.removeInstalledIntents(newIntent.id());
994
995 batchWrite.setState(oldIntent, WITHDRAWN);
996 batchWrite.removeInstalledIntents(oldIntent.id());
997 batchWrite.removeIntent(oldIntent.id());
998 }
999
1000 @Override
1001 public FlowRuleBatchOperation currentBatch() {
1002 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
1003 }
1004
1005 @Override
1006 public void batchFailed() {
1007 // the current batch has failed, so recompile
1008 // remove the current batch and all remaining
1009 for (int i = batches.size() - 1; i >= currentBatch; i--) {
1010 batches.remove(i);
1011 }
1012 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
1013
1014 batches.addAll(uninstallIntent(newIntent, newInstallables));
1015
1016 // TODO we might want to try to recompile the new intent
1017 }
1018 }
1019
1020 private class IntentBatchPreprocess implements Runnable {
1021
1022 // TODO make this configurable
1023 private static final int TIMEOUT_PER_OP = 500; // ms
1024 protected static final int MAX_ATTEMPTS = 3;
1025
Brian O'Connorb499b352015-02-03 16:46:15 -08001026 protected final Collection<IntentData> ops;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001027
1028 // future holding current FlowRuleBatch installation result
1029 protected final long startTime = System.currentTimeMillis();
1030 protected final long endTime;
1031
Brian O'Connorb499b352015-02-03 16:46:15 -08001032 private IntentBatchPreprocess(Collection<IntentData> ops, long endTime) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001033 this.ops = checkNotNull(ops);
1034 this.endTime = endTime;
1035 }
1036
Brian O'Connorb499b352015-02-03 16:46:15 -08001037 public IntentBatchPreprocess(Collection<IntentData> ops) {
1038 this(ops, System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001039 }
1040
1041 // FIXME compute reasonable timeouts
1042 protected long calculateTimeoutLimit() {
Brian O'Connorb499b352015-02-03 16:46:15 -08001043 return System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001044 }
1045
1046 @Override
1047 public void run() {
1048 try {
1049 // this should only be called on the first iteration
1050 // note: this a "expensive", so it is not done in the constructor
1051
1052 // - creates per Intent installation context (IntentUpdate)
1053 // - write Intents to store
1054 // - process (compile, install, etc.) each Intents
1055 // - generate FlowRuleBatch for this phase
1056 // build IntentUpdates
1057 List<IntentUpdate> updates = createIntentUpdates();
1058
1059 // Write batch information
1060 BatchWrite batchWrite = createBatchWrite(updates);
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001061 store.batchWrite(batchWrite);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001062
1063 new IntentBatchApplyFirst(ops, processIntentUpdates(updates), endTime, 0, null).run();
1064 } catch (Exception e) {
1065 log.error("Error submitting batches:", e);
1066 // FIXME incomplete Intents should be cleaned up
1067 // (transition to FAILED, etc.)
1068
1069 // TODO: remove duplicate due to inlining
1070 // the batch has failed
1071 // TODO: maybe we should do more?
1072 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -08001073 //FIXME
1074// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001075 }
1076 }
1077
1078 private List<IntentUpdate> createIntentUpdates() {
Brian O'Connorb499b352015-02-03 16:46:15 -08001079 return ops.stream()
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001080 .map(IntentManager.this::createIntentUpdate)
1081 .collect(Collectors.toList());
1082 }
1083
1084 private BatchWrite createBatchWrite(List<IntentUpdate> updates) {
1085 BatchWrite batchWrite = BatchWrite.newInstance();
1086 updates.forEach(update -> update.writeBeforeExecution(batchWrite));
1087 return batchWrite;
1088 }
1089
1090 private List<CompletedIntentUpdate> processIntentUpdates(List<IntentUpdate> updates) {
1091 // start processing each Intents
1092 List<CompletedIntentUpdate> completed = new ArrayList<>();
1093 for (IntentUpdate update : updates) {
1094 Optional<IntentUpdate> phase = Optional.of(update);
1095 IntentUpdate previous = update;
1096 while (true) {
1097 if (!phase.isPresent()) {
1098 // FIXME: not type safe cast
1099 completed.add((CompletedIntentUpdate) previous);
1100 break;
1101 }
1102 previous = phase.get();
1103 phase = previous.execute();
1104 }
1105 }
1106
1107 return completed;
1108 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001109 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001110
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001111 // TODO: better naming
1112 private class IntentBatchApplyFirst extends IntentBatchPreprocess {
1113
1114 protected final List<CompletedIntentUpdate> intentUpdates;
1115 protected final int installAttempt;
1116 protected Future<CompletedBatchOperation> future;
1117
Brian O'Connorb499b352015-02-03 16:46:15 -08001118 IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001119 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1120 super(operations, endTime);
1121 this.intentUpdates = ImmutableList.copyOf(intentUpdates);
1122 this.future = future;
1123 this.installAttempt = installAttempt;
1124 }
1125
1126 @Override
1127 public void run() {
1128 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1129 new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future).run();
Brian O'Connorcb900f42014-10-07 21:55:33 -07001130 }
1131
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001132 /**
Brian O'Connor427a1762014-11-19 18:40:32 -08001133 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001134 *
1135 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001136 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001137 protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001138 //TODO test this. (also, maybe save this batch)
Brian O'Connor72cb19a2015-01-16 16:14:41 -08001139
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001140 FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
Ray Milkey93508c22014-12-02 11:35:56 -08001141 if (batch.size() > 0) {
1142 //FIXME apply batch might throw an exception
1143 return flowRuleService.applyBatch(batch);
1144 } else {
1145 // there are no flow rule batches; finalize the intent update
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001146 BatchWrite batchWrite = createFinalizedBatchWrite(updates);
1147
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001148 store.batchWrite(batchWrite);
Ray Milkey93508c22014-12-02 11:35:56 -08001149 return null;
1150 }
1151 }
1152
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001153 private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -08001154 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001155 for (CompletedIntentUpdate update : intentUpdates) {
1156 FlowRuleBatchOperation currentBatch = update.currentBatch();
1157 if (currentBatch != null) {
1158 batch.addAll(currentBatch);
1159 }
1160 }
1161 return batch;
1162 }
1163
1164 private BatchWrite createFinalizedBatchWrite(List<CompletedIntentUpdate> intentUpdates) {
1165 BatchWrite batchWrite = BatchWrite.newInstance();
1166 for (CompletedIntentUpdate update : intentUpdates) {
1167 update.writeAfterExecution(batchWrite);
1168 }
1169 return batchWrite;
1170 }
1171
1172 protected void abandonShip() {
1173 // the batch has failed
1174 // TODO: maybe we should do more?
1175 log.error("Walk the plank, matey...");
1176 future = null;
Brian O'Connorb499b352015-02-03 16:46:15 -08001177 //FIXME
1178// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001179 }
1180 }
1181
1182 // TODO: better naming
1183 private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
1184
Brian O'Connorb499b352015-02-03 16:46:15 -08001185 IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001186 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1187 super(operations, intentUpdates, endTime, installAttempt, future);
1188 }
1189
1190 @Override
1191 public void run() {
1192 try {
1193 // - peek if current FlowRuleBatch is complete
1194 // -- If complete OK:
1195 // step each IntentUpdate forward
1196 // If phase left: generate next FlowRuleBatch
1197 // If no more phase: write parking states
1198 // -- If complete FAIL:
1199 // Intent which failed: transition Intent to FAILED
1200 // Other Intents: resubmit same FlowRuleBatch for this phase
1201 Future<CompletedBatchOperation> future = processFutures();
1202 if (future == null) {
1203 // there are no outstanding batches; we are done
Brian O'Connorb499b352015-02-03 16:46:15 -08001204 //FIXME
1205 return; //?
1206// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001207 } else if (System.currentTimeMillis() > endTime) {
1208 // - cancel current FlowRuleBatch and resubmit again
1209 retry();
1210 } else {
1211 // we are not done yet, yield the thread by resubmitting ourselves
1212 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future));
1213 }
1214 } catch (Exception e) {
1215 log.error("Error submitting batches:", e);
1216 // FIXME incomplete Intents should be cleaned up
1217 // (transition to FAILED, etc.)
1218 abandonShip();
1219 }
1220 }
1221
1222 /**
1223 * Iterate through the pending futures, and remove them when they have completed.
1224 */
1225 private Future<CompletedBatchOperation> processFutures() {
1226 try {
1227 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
1228 updateBatches(completed);
1229 return applyNextBatch(intentUpdates);
1230 } catch (TimeoutException | InterruptedException te) {
1231 log.trace("Installation of intents are still pending: {}", ops);
1232 return future;
1233 } catch (ExecutionException e) {
1234 log.warn("Execution of batch failed: {}", ops, e);
1235 abandonShip();
1236 return future;
1237 }
1238 }
1239
Ray Milkey93508c22014-12-02 11:35:56 -08001240 private void updateBatches(CompletedBatchOperation completed) {
1241 if (completed.isSuccess()) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001242 for (CompletedIntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -08001243 update.batchSuccess();
1244 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001245 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -08001246 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -08001247 log.debug("Failed items: {}", completed.failedItems());
1248 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -08001249
1250 for (Long id : completed.failedIds()) {
1251 IntentId targetId = IntentId.valueOf(id);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001252 for (CompletedIntentUpdate update : intentUpdates) {
1253 for (Intent intent : update.allInstallables()) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001254 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001255 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001256 break;
1257 }
1258 }
1259 }
1260 // don't increment the non-failed items, as they have been reverted.
1261 }
1262 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001263 }
1264
Brian O'Connor427a1762014-11-19 18:40:32 -08001265 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -08001266 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001267 if (future.cancel(true)) { // cancel success; batch is reverted
1268 // reset the timer
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001269 long timeLimit = calculateTimeoutLimit();
1270 int attempts = installAttempt + 1;
1271 if (attempts == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001272 log.warn("Install request timed out: {}", ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001273 for (CompletedIntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001274 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001275 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001276 } else if (attempts > MAX_ATTEMPTS) {
Ray Milkey93508c22014-12-02 11:35:56 -08001277 abandonShip();
1278 return;
Brian O'Connor427a1762014-11-19 18:40:32 -08001279 } // else just resubmit the work
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001280 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1281 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, timeLimit, attempts, future));
Brian O'Connor427a1762014-11-19 18:40:32 -08001282 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001283 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001284 // FIXME
1285 // cancel failed... batch is broken; shouldn't happen!
1286 // we could manually reverse everything
1287 // ... or just core dump and send email to Ali
Ray Milkey93508c22014-12-02 11:35:56 -08001288 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -08001289 }
1290 }
Brian O'Connorcb900f42014-10-07 21:55:33 -07001291 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001292
1293 private class InternalBatchDelegate implements IntentBatchDelegate {
1294 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -08001295 public void execute(Collection<IntentData> operations) {
1296 log.info("Execute {} operation(s).", operations.size());
1297 log.debug("Execute operations: {}", operations);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001298 executor.execute(new IntentBatchPreprocess(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001299 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001300 }
Brian O'Connor66630c82014-10-02 21:08:19 -07001301}