blob: 7c3bb3e3627b4157fefe950c88c544a84b493980 [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;
36import org.onosproject.net.intent.Intent;
37import org.onosproject.net.intent.IntentBatchDelegate;
38import org.onosproject.net.intent.IntentBatchService;
39import org.onosproject.net.intent.IntentCompiler;
40import org.onosproject.net.intent.IntentEvent;
41import org.onosproject.net.intent.IntentException;
42import org.onosproject.net.intent.IntentExtensionService;
43import org.onosproject.net.intent.IntentId;
44import org.onosproject.net.intent.IntentInstaller;
45import org.onosproject.net.intent.IntentListener;
46import org.onosproject.net.intent.IntentOperation;
47import org.onosproject.net.intent.IntentOperations;
48import org.onosproject.net.intent.IntentService;
49import org.onosproject.net.intent.IntentState;
50import org.onosproject.net.intent.IntentStore;
Sho SHIMIZU64ae11c2014-12-03 15:17:47 -080051import org.onosproject.net.intent.BatchWrite;
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;
56import java.util.Collections;
57import java.util.EnumSet;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080058import java.util.LinkedList;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080059import java.util.List;
60import java.util.Map;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080061import java.util.Optional;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080062import java.util.concurrent.ConcurrentHashMap;
63import java.util.concurrent.ConcurrentMap;
64import java.util.concurrent.ExecutionException;
65import java.util.concurrent.ExecutorService;
66import java.util.concurrent.Future;
67import java.util.concurrent.TimeUnit;
68import java.util.concurrent.TimeoutException;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080069import java.util.stream.Collectors;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070070
Brian O'Connorfa81eae2014-10-30 13:20:05 -070071import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080072import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080073import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connorabafb502014-12-02 22:26:20 -080074import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070075import static org.onlab.util.Tools.namedThreads;
76import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070077
78/**
79 * An implementation of Intent Manager.
80 */
81@Component(immediate = true)
82@Service
83public class IntentManager
84 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080085 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070086
87 public static final String INTENT_NULL = "Intent cannot be null";
88 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
89
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080090 private static final int NUM_THREADS = 12;
91
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080092 private static final EnumSet<IntentState> RECOMPILE
93 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080094
95
Brian O'Connor66630c82014-10-02 21:08:19 -070096 // Collections for compiler, installer, and listener are ONOS instance local
97 private final ConcurrentMap<Class<? extends Intent>,
98 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070099 private final ConcurrentMap<Class<? extends Intent>,
100 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700101
102 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700103 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700104
Brian O'Connor520c0522014-11-23 23:50:47 -0800105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected IntentStore store;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700112 protected IntentBatchService batchService;
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'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'Connorfa81eae2014-10-30 13:20:05 -0700135 batchService.setDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700136 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Yuta HIGUCHI7a9fddd2014-12-03 15:30:25 -0800137 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800138 idGenerator = coreService.getIdGenerator("intent-ids");
139 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700140 log.info("Started");
141 }
142
143 @Deactivate
144 public void deactivate() {
145 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700146 trackerService.unsetDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700147 batchService.unsetDelegate(batchDelegate);
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'Connor72a034c2014-11-26 18:24:23 -0800157 execute(IntentOperations.builder(intent.appId())
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800158 .addSubmitOperation(intent).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700159 }
160
161 @Override
162 public void withdraw(Intent intent) {
163 checkNotNull(intent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800164 execute(IntentOperations.builder(intent.appId())
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800165 .addWithdrawOperation(intent.id()).build());
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'Connorfa81eae2014-10-30 13:20:05 -0700170 checkNotNull(oldIntentId, INTENT_ID_NULL);
171 checkNotNull(newIntent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800172 execute(IntentOperations.builder(newIntent.appId())
Ray Milkeye97ede92014-11-20 10:43:12 -0800173 .addReplaceOperation(oldIntentId, newIntent)
174 .build());
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700175 }
176
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700177 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700178 public void execute(IntentOperations operations) {
Brian O'Connore2ff25a2014-11-18 19:25:43 -0800179 if (operations.operations().isEmpty()) {
180 return;
181 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700182 batchService.addIntentOperations(operations);
Brian O'Connor66630c82014-10-02 21:08:19 -0700183 }
184
185 @Override
186 public Iterable<Intent> getIntents() {
187 return store.getIntents();
188 }
189
190 @Override
191 public long getIntentCount() {
192 return store.getIntentCount();
193 }
194
195 @Override
196 public Intent getIntent(IntentId id) {
197 checkNotNull(id, INTENT_ID_NULL);
198 return store.getIntent(id);
199 }
200
201 @Override
202 public IntentState getIntentState(IntentId id) {
203 checkNotNull(id, INTENT_ID_NULL);
204 return store.getIntentState(id);
205 }
206
207 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700208 public List<Intent> getInstallableIntents(IntentId intentId) {
209 checkNotNull(intentId, INTENT_ID_NULL);
210 return store.getInstallableIntents(intentId);
211 }
212
213 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700214 public void addListener(IntentListener listener) {
215 listenerRegistry.addListener(listener);
216 }
217
218 @Override
219 public void removeListener(IntentListener listener) {
220 listenerRegistry.removeListener(listener);
221 }
222
223 @Override
224 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
225 compilers.put(cls, compiler);
226 }
227
228 @Override
229 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
230 compilers.remove(cls);
231 }
232
233 @Override
234 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
235 return ImmutableMap.copyOf(compilers);
236 }
237
238 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700239 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700240 installers.put(cls, installer);
241 }
242
243 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700244 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700245 installers.remove(cls);
246 }
247
248 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700249 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700250 return ImmutableMap.copyOf(installers);
251 }
252
253 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700254 * Returns the corresponding intent compiler to the specified intent.
255 *
256 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700257 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700258 * @return intent compiler corresponding to the specified intent
259 */
260 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
261 @SuppressWarnings("unchecked")
262 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
263 if (compiler == null) {
264 throw new IntentException("no compiler for class " + intent.getClass());
265 }
266 return compiler;
267 }
268
269 /**
270 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700271 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700272 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700273 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700274 * @return intent installer corresponding to the specified installable intent
275 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700276 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700277 @SuppressWarnings("unchecked")
278 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
279 if (installer == null) {
280 throw new IntentException("no installer for class " + intent.getClass());
281 }
282 return installer;
283 }
284
285 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700286 * Compiles an intent recursively.
287 *
288 * @param intent intent
289 * @return result of compilation
290 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800291 private List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700292 if (intent.isInstallable()) {
293 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700294 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700295
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700296 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700297 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700298 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800299 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
300 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700301 }
tom85258ee2014-10-07 00:10:02 -0700302 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700303 }
304
305 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700306 * Uninstalls all installable intents associated with the given intent.
307 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800308 * @param intent intent
309 * @param installables installable intents
310 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700311 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800312 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700313 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800314 for (Intent installable : installables) {
315 trackerService.removeTrackedResources(intent.id(),
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800316 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700317 try {
318 batches.addAll(getInstaller(installable).uninstall(installable));
319 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800320 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700321 // TODO: this should never happen. but what if it does?
322 }
323 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800324 return batches;
tom85258ee2014-10-07 00:10:02 -0700325 }
326
327 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700328 * Registers an intent compiler of the specified intent if an intent compiler
329 * for the intent is not registered. This method traverses the class hierarchy of
330 * the intent. Once an intent compiler for a parent type is found, this method
331 * registers the found intent compiler.
332 *
333 * @param intent intent
334 */
335 private void registerSubclassCompilerIfNeeded(Intent intent) {
336 if (!compilers.containsKey(intent.getClass())) {
337 Class<?> cls = intent.getClass();
338 while (cls != Object.class) {
339 // As long as we're within the Intent class descendants
340 if (Intent.class.isAssignableFrom(cls)) {
341 IntentCompiler<?> compiler = compilers.get(cls);
342 if (compiler != null) {
343 compilers.put(intent.getClass(), compiler);
344 return;
345 }
346 }
347 cls = cls.getSuperclass();
348 }
349 }
350 }
351
352 /**
353 * Registers an intent installer of the specified intent if an intent installer
354 * for the intent is not registered. This method traverses the class hierarchy of
355 * the intent. Once an intent installer for a parent type is found, this method
356 * registers the found intent installer.
357 *
358 * @param intent intent
359 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700360 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700361 if (!installers.containsKey(intent.getClass())) {
362 Class<?> cls = intent.getClass();
363 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700364 // As long as we're within the Intent class descendants
365 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700366 IntentInstaller<?> installer = installers.get(cls);
367 if (installer != null) {
368 installers.put(intent.getClass(), installer);
369 return;
370 }
371 }
372 cls = cls.getSuperclass();
373 }
374 }
375 }
376
Brian O'Connor66630c82014-10-02 21:08:19 -0700377 // Store delegate to re-post events emitted from the store.
378 private class InternalStoreDelegate implements IntentStoreDelegate {
379 @Override
380 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700381 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700382 }
383 }
384
Brian O'Connor72a034c2014-11-26 18:24:23 -0800385 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
386 boolean compileAllFailed) {
387 Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
388 // Attempt recompilation of the specified intents first.
389 for (IntentId id : intentIds) {
390 Intent intent = store.getIntent(id);
391 if (intent == null) {
392 continue;
393 }
394 IntentOperations.Builder builder = batches.get(intent.appId());
395 if (builder == null) {
396 builder = IntentOperations.builder(intent.appId());
397 batches.put(intent.appId(), builder);
398 }
399 builder.addUpdateOperation(id);
400 }
401
402 if (compileAllFailed) {
403 // If required, compile all currently failed intents.
404 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800405 IntentState state = getIntentState(intent.id());
406 if (RECOMPILE.contains(state)) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800407 IntentOperations.Builder builder = batches.get(intent.appId());
408 if (builder == null) {
409 builder = IntentOperations.builder(intent.appId());
410 batches.put(intent.appId(), builder);
411 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800412 if (state == WITHDRAW_REQ) {
413 builder.addWithdrawOperation(intent.id());
414 } else {
415 builder.addUpdateOperation(intent.id());
416 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800417 }
418 }
419 }
420
421 for (ApplicationId appId : batches.keySet()) {
422 if (batchService.isLocalLeader(appId)) {
423 execute(batches.get(appId).build());
424 }
425 }
426 }
427
tom95329eb2014-10-06 08:40:06 -0700428 // Topology change delegate
429 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
430 @Override
tom85258ee2014-10-07 00:10:02 -0700431 public void triggerCompile(Iterable<IntentId> intentIds,
432 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800433 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700434 }
tom95329eb2014-10-06 08:40:06 -0700435 }
tom85258ee2014-10-07 00:10:02 -0700436
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800437 // TODO: simplify the branching statements
438 private IntentUpdate createIntentUpdate(IntentOperation operation) {
439 switch (operation.type()) {
440 case SUBMIT:
441 return new InstallRequest(operation.intent());
442 case WITHDRAW: {
443 Intent oldIntent = store.getIntent(operation.intentId());
444 if (oldIntent == null) {
445 return new DoNothing();
446 }
447 List<Intent> installables = store.getInstallableIntents(oldIntent.id());
448 if (installables == null) {
449 return new WithdrawStateChange1(oldIntent);
450 }
451 return new WithdrawRequest(oldIntent, installables);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700452 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800453 case REPLACE: {
454 Intent newIntent = operation.intent();
455 Intent oldIntent = store.getIntent(operation.intentId());
456 if (oldIntent == null) {
457 return new InstallRequest(newIntent);
458 }
459 List<Intent> installables = store.getInstallableIntents(oldIntent.id());
460 if (installables == null) {
461 if (newIntent.equals(oldIntent)) {
462 return new InstallRequest(newIntent);
463 } else {
464 return new WithdrawStateChange2(oldIntent);
465 }
466 }
467 return new ReplaceRequest(newIntent, oldIntent, installables);
468 }
469 case UPDATE: {
470 Intent oldIntent = store.getIntent(operation.intentId());
471 if (oldIntent == null) {
472 return new DoNothing();
473 }
474 List<Intent> installables = getInstallableIntents(oldIntent.id());
475 if (installables == null) {
476 return new InstallRequest(oldIntent);
477 }
478 return new ReplaceRequest(oldIntent, oldIntent, installables);
479 }
480 default:
481 // illegal state
482 return new DoNothing();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700483 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700484 }
485
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800486 private interface IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800487
488 /**
489 * Execute the procedure represented by the instance
490 * and generates the next update instance.
491 *
492 * @return next update
493 */
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800494 default Optional<IntentUpdate> execute() {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800495 return Optional.empty();
496 }
497
498 /**
499 * Write data to the specified BatchWrite before execution() is called.
500 *
501 * @param batchWrite batchWrite
502 */
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800503 default void writeBeforeExecution(BatchWrite batchWrite) {}
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800504 }
505
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800506 private interface CompletedIntentUpdate extends IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800507
508 /**
509 * Write data to the specified BatchWrite after execution() is called.
510 *
511 * @param batchWrite batchWrite
512 */
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800513 default void writeAfterExecution(BatchWrite batchWrite) {}
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800514
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800515 default void batchSuccess() {}
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800516
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800517 default void batchFailed() {}
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800518
519 /**
520 * Returns the current FlowRuleBatchOperation.
521 *
522 * @return current FlowRuleBatchOperation
523 */
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800524 default FlowRuleBatchOperation currentBatch() {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800525 return null;
526 }
527
528 /**
529 * Returns all of installable intents this instance holds.
530 *
531 * @return all of installable intents
532 */
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800533 default List<Intent> allInstallables() {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800534 return Collections.emptyList();
535 }
536 }
537
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800538 private class InstallRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800539
540 private final Intent intent;
541
542 InstallRequest(Intent intent) {
543 this.intent = checkNotNull(intent);
544 }
545
546 @Override
547 public void writeBeforeExecution(BatchWrite batchWrite) {
548 // TODO consider only "creating" intent if it does not exist
549 // Note: We need to set state to INSTALL_REQ regardless.
550 batchWrite.createIntent(intent);
551 }
552
553 @Override
554 public Optional<IntentUpdate> execute() {
555 return Optional.of(new Compiling(intent));
556 }
557 }
558
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800559 private class WithdrawRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800560
561 private final Intent intent;
562 private final List<Intent> installables;
563
564 WithdrawRequest(Intent intent, List<Intent> installables) {
565 this.intent = checkNotNull(intent);
566 this.installables = ImmutableList.copyOf(checkNotNull(installables));
567 }
568
569 @Override
570 public void writeBeforeExecution(BatchWrite batchWrite) {
571 batchWrite.setState(intent, WITHDRAW_REQ);
572 }
573
574 @Override
575 public Optional<IntentUpdate> execute() {
576 return Optional.of(new Withdrawing(intent, installables));
577 }
578 }
579
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800580 private class ReplaceRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800581
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700582 private final Intent newIntent;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800583 private final Intent oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700584 private final List<Intent> oldInstallables;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700585
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800586 ReplaceRequest(Intent newIntent, Intent oldIntent, List<Intent> oldInstallables) {
587 this.newIntent = checkNotNull(newIntent);
588 this.oldIntent = checkNotNull(oldIntent);
589 this.oldInstallables = ImmutableList.copyOf(oldInstallables);
590 }
591
592 @Override
593 public void writeBeforeExecution(BatchWrite batchWrite) {
594 // TODO consider only "creating" intent if it does not exist
595 // Note: We need to set state to INSTALL_REQ regardless.
596 batchWrite.createIntent(newIntent);
597 }
598
599 @Override
600 public Optional<IntentUpdate> execute() {
601 try {
602 List<Intent> installables = compileIntent(newIntent, oldInstallables);
603 return Optional.of(new Replacing(newIntent, oldIntent, installables, oldInstallables));
604 } catch (PathNotFoundException e) {
605 log.debug("Path not found for intent {}", newIntent);
606 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
607 } catch (IntentException e) {
608 log.warn("Unable to compile intent {} due to:", newIntent.id(), e);
609 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700610 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800611 }
612 }
613
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800614 private class DoNothing implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800615 }
616
617 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800618 private class WithdrawStateChange1 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800619
620 private final Intent intent;
621
622 WithdrawStateChange1(Intent intent) {
623 this.intent = checkNotNull(intent);
624 }
625
626 @Override
627 public void writeBeforeExecution(BatchWrite batchWrite) {
628 batchWrite.setState(intent, WITHDRAW_REQ);
629 }
630
631 @Override
632 public void writeAfterExecution(BatchWrite batchWrite) {
633 batchWrite.setState(intent, WITHDRAWN);
634 batchWrite.removeInstalledIntents(intent.id());
635 batchWrite.removeIntent(intent.id());
636 }
637 }
638
639 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800640 private class WithdrawStateChange2 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800641
642 private final Intent intent;
643
644 WithdrawStateChange2(Intent intent) {
645 this.intent = checkNotNull(intent);
646 }
647
648 @Override
649 public void writeBeforeExecution(BatchWrite batchWrite) {
650 // TODO consider only "creating" intent if it does not exist
651 // Note: We need to set state to INSTALL_REQ regardless.
652 batchWrite.createIntent(intent);
653 }
654
655 @Override
656 public void writeAfterExecution(BatchWrite batchWrite) {
657 batchWrite.setState(intent, WITHDRAWN);
658 batchWrite.removeInstalledIntents(intent.id());
659 batchWrite.removeIntent(intent.id());
660 }
661 }
662
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800663 private class Compiling implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800664
665 private final Intent intent;
666
667 Compiling(Intent intent) {
668 this.intent = checkNotNull(intent);
669 }
670
671 @Override
672 public Optional<IntentUpdate> execute() {
673 try {
674 // Compile the intent into installable derivatives.
675 // If all went well, associate the resulting list of installable
676 // intents with the top-level intent and proceed to install.
677 return Optional.of(new Installing(intent, compileIntent(intent, null)));
678 } catch (PathNotFoundException e) {
679 return Optional.of(new CompilingFailed(intent, e));
680 } catch (IntentException e) {
681 return Optional.of(new CompilingFailed(intent, e));
682 }
683 }
684 }
685
686 // TODO: better naming because install() method actually generate FlowRuleBatchOperations
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800687 private class Installing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800688
689 private final Intent intent;
690 private final List<Intent> installables;
691
692 Installing(Intent intent, List<Intent> installables) {
693 this.intent = checkNotNull(intent);
694 this.installables = ImmutableList.copyOf(checkNotNull(installables));
695 }
696
697 @Override
698 public Optional<IntentUpdate> execute() {
699 Exception exception = null;
700 // Indicate that the intent is entering the installing phase.
701
702 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
703 for (Intent installable : installables) {
704 registerSubclassInstallerIfNeeded(installable);
705 trackerService.addTrackedResources(intent.id(), installable.resources());
706 try {
707 batches.addAll(getInstaller(installable).install(installable));
708 } catch (Exception e) { // TODO this should be IntentException
709 log.warn("Unable to install intent {} due to:", intent.id(), e);
710 trackerService.removeTrackedResources(intent.id(), installable.resources());
711 //TODO we failed; intent should be recompiled
712 exception = e;
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800713 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700714 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700715
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800716 if (exception != null) {
717 return Optional.of(new InstallingFailed(intent, installables, batches));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800718 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800719
720 return Optional.of(new Installed(intent, installables, batches));
721 }
722 }
723
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800724 private class Withdrawing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800725
726 private final Intent intent;
727 private final List<Intent> installables;
728
729 Withdrawing(Intent intent, List<Intent> installables) {
730 this.intent = checkNotNull(intent);
731 this.installables = ImmutableList.copyOf(installables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800732 }
733
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800734 @Override
735 public Optional<IntentUpdate> execute() {
736 List<FlowRuleBatchOperation> batches = uninstallIntent(intent, installables);
737
738 return Optional.of(new Withdrawn(intent, installables, batches));
739 }
740 }
741
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800742 private class Replacing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800743
744 private final Intent newIntent;
745 private final Intent oldIntent;
746 private final List<Intent> newInstallables;
747 private final List<Intent> oldInstallables;
748
749 private Exception exception;
750
751 Replacing(Intent newIntent, Intent oldIntent,
752 List<Intent> newInstallables, List<Intent> oldInstallables) {
753 this.newIntent = checkNotNull(newIntent);
754 this.oldIntent = checkNotNull(oldIntent);
755 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
756 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700757 }
758
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800759 @Override
760 public Optional<IntentUpdate> execute() {
761 List<FlowRuleBatchOperation> batches = replace();
762
763 if (exception == null) {
764 return Optional.of(
765 new Replaced(newIntent, oldIntent, newInstallables, oldInstallables, batches));
766 }
767
768 return Optional.of(
769 new ReplacingFailed(newIntent, oldIntent, newInstallables, oldInstallables, batches));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700770 }
771
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800772 protected List<FlowRuleBatchOperation> replace() {
773 checkState(oldInstallables.size() == newInstallables.size(),
774 "Old and New Intent must have equivalent installable intents.");
775
776 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
777 for (int i = 0; i < oldInstallables.size(); i++) {
778 Intent oldInstallable = oldInstallables.get(i);
779 Intent newInstallable = newInstallables.get(i);
780 //FIXME revisit this
781// if (oldInstallable.equals(newInstallable)) {
782// continue;
783// }
784 checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
785 "Installable Intent type mismatch.");
786 trackerService.removeTrackedResources(oldIntent.id(), oldInstallable.resources());
787 trackerService.addTrackedResources(newIntent.id(), newInstallable.resources());
788 try {
789 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
790 } catch (IntentException e) {
791 log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
792 //FIXME... we failed. need to uninstall (if same) or revert (if different)
793 trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
794 exception = e;
795 batches = uninstallIntent(oldIntent, oldInstallables);
796 }
797 }
798 return batches;
799 }
800 }
801
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800802 private class Installed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800803
804 private final Intent intent;
805 private final List<Intent> installables;
806 private IntentState intentState;
807 private final List<FlowRuleBatchOperation> batches;
808 private int currentBatch = 0;
809
810 Installed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
811 this.intent = checkNotNull(intent);
812 this.installables = ImmutableList.copyOf(checkNotNull(installables));
813 this.batches = new LinkedList<>(checkNotNull(batches));
814 this.intentState = INSTALLING;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700815 }
816
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800817 @Override
818 public void batchSuccess() {
Ray Milkey93508c22014-12-02 11:35:56 -0800819 currentBatch++;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800820 }
821
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800822 @Override
823 public List<Intent> allInstallables() {
824 return installables;
825 }
826
827 @Override
828 public void writeAfterExecution(BatchWrite batchWrite) {
829 switch (intentState) {
830 case INSTALLING:
831 batchWrite.setState(intent, INSTALLED);
832 batchWrite.setInstallableIntents(intent.id(), this.installables);
833 break;
834 case FAILED:
835 batchWrite.setState(intent, FAILED);
836 batchWrite.removeInstalledIntents(intent.id());
837 break;
838 default:
839 break;
840 }
841 }
842
843 @Override
844 public FlowRuleBatchOperation currentBatch() {
845 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
846 }
847
848 @Override
849 public void batchFailed() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800850 // the current batch has failed, so recompile
851 // remove the current batch and all remaining
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800852 for (int i = batches.size() - 1; i >= currentBatch; i--) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800853 batches.remove(i);
854 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800855 intentState = FAILED;
856 batches.addAll(uninstallIntent(intent, installables));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800857
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800858 // TODO we might want to try to recompile the new intent
Brian O'Connor427a1762014-11-19 18:40:32 -0800859 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800860 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800861
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800862 private class Withdrawn implements CompletedIntentUpdate {
Brian O'Connor427a1762014-11-19 18:40:32 -0800863
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800864 private final Intent intent;
865 private final List<Intent> installables;
866 private final List<FlowRuleBatchOperation> batches;
867 private int currentBatch;
868
869 Withdrawn(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
870 this.intent = checkNotNull(intent);
871 this.installables = ImmutableList.copyOf(installables);
872 this.batches = new LinkedList<>(batches);
873 this.currentBatch = 0;
874 }
875
876 @Override
877 public List<Intent> allInstallables() {
878 return installables;
879 }
880
881 @Override
882 public void batchSuccess() {
883 currentBatch++;
884 }
885
886 @Override
887 public void writeAfterExecution(BatchWrite batchWrite) {
888 batchWrite.setState(intent, WITHDRAWN);
889 batchWrite.removeInstalledIntents(intent.id());
890 batchWrite.removeIntent(intent.id());
891 }
892
893 @Override
894 public FlowRuleBatchOperation currentBatch() {
895 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
896 }
897
898 @Override
899 public void batchFailed() {
900 // the current batch has failed, so recompile
901 // remove the current batch and all remaining
902 for (int i = batches.size() - 1; i >= currentBatch; i--) {
903 batches.remove(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800904 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800905 batches.addAll(uninstallIntent(intent, installables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700906 }
907 }
908
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800909 private class Replaced implements CompletedIntentUpdate {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700910
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800911 private final Intent newIntent;
912 private final Intent oldIntent;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700913
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800914 private final List<Intent> newInstallables;
915 private final List<Intent> oldInstallables;
916 private final List<FlowRuleBatchOperation> batches;
917 private int currentBatch;
Brian O'Connor427a1762014-11-19 18:40:32 -0800918
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800919 Replaced(Intent newIntent, Intent oldIntent,
920 List<Intent> newInstallables, List<Intent> oldInstallables,
921 List<FlowRuleBatchOperation> batches) {
922 this.newIntent = checkNotNull(newIntent);
923 this.oldIntent = checkNotNull(oldIntent);
924 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
925 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
926 this.batches = new LinkedList<>(batches);
927 this.currentBatch = 0;
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800928 }
929
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800930 @Override
931 public List<Intent> allInstallables() {
932 LinkedList<Intent> allInstallables = new LinkedList<>();
933 allInstallables.addAll(newInstallables);
934 allInstallables.addAll(oldInstallables);
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800935
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800936 return allInstallables;
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800937 }
938
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800939 @Override
940 public void batchSuccess() {
941 currentBatch++;
Brian O'Connor427a1762014-11-19 18:40:32 -0800942 }
943
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800944 @Override
945 public void writeAfterExecution(BatchWrite batchWrite) {
946 batchWrite.setState(newIntent, INSTALLED);
947 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800948
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800949 batchWrite.setState(oldIntent, WITHDRAWN);
950 batchWrite.removeInstalledIntents(oldIntent.id());
951 batchWrite.removeIntent(oldIntent.id());
952 }
953
954 @Override
955 public FlowRuleBatchOperation currentBatch() {
956 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
957 }
958
959 @Override
960 public void batchFailed() {
961 // the current batch has failed, so recompile
962 // remove the current batch and all remaining
963 for (int i = batches.size() - 1; i >= currentBatch; i--) {
964 batches.remove(i);
965 }
966 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
967
968 batches.addAll(uninstallIntent(newIntent, newInstallables));
969
970 // TODO we might want to try to recompile the new intent
971 }
972 }
973
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800974 private class CompilingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800975
976 private final Intent intent;
977 private final IntentException exception;
978
979 CompilingFailed(Intent intent, IntentException exception) {
980 this.intent = checkNotNull(intent);
981 this.exception = checkNotNull(exception);
982 }
983
984 @Override
985 public Optional<IntentUpdate> execute() {
986 if (exception instanceof PathNotFoundException) {
987 log.debug("Path not found for intent {}", intent);
988 } else {
989 log.warn("Unable to compile intent {} due to:", intent.id(), exception);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800990 }
991
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800992 return Optional.empty();
993 }
994
995 @Override
996 public void writeAfterExecution(BatchWrite batchWrite) {
997 batchWrite.setState(intent, FAILED);
998 batchWrite.removeInstalledIntents(intent.id());
999 }
1000 }
1001
Sho SHIMIZU28e72712015-01-27 17:03:41 -08001002 private class InstallingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001003
1004 private final Intent intent;
1005 private final List<Intent> installables;
1006 private final List<FlowRuleBatchOperation> batches;
1007 private int currentBatch = 0;
1008
1009 InstallingFailed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
1010 this.intent = checkNotNull(intent);
1011 this.installables = ImmutableList.copyOf(checkNotNull(installables));
1012 this.batches = new LinkedList<>(checkNotNull(batches));
1013 }
1014
1015 @Override
1016 public List<Intent> allInstallables() {
1017 return installables;
1018 }
1019
1020 @Override
1021 public void batchSuccess() {
1022 currentBatch++;
1023 }
1024
1025 @Override
1026 public void writeAfterExecution(BatchWrite batchWrite) {
1027 batchWrite.setState(intent, FAILED);
1028 batchWrite.removeInstalledIntents(intent.id());
1029 }
1030
1031 @Override
1032 public FlowRuleBatchOperation currentBatch() {
1033 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
1034 }
1035
1036 @Override
1037 public void batchFailed() {
1038 // the current batch has failed, so recompile
1039 // remove the current batch and all remaining
1040 for (int i = batches.size() - 1; i >= currentBatch; i--) {
1041 batches.remove(i);
1042 }
1043 batches.addAll(uninstallIntent(intent, installables));
1044
1045 // TODO we might want to try to recompile the new intent
1046 }
1047 }
1048
Sho SHIMIZU28e72712015-01-27 17:03:41 -08001049 private class ReplacingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001050
1051 private final Intent newIntent;
1052 private final Intent oldIntent;
1053 private final List<Intent> newInstallables;
1054 private final List<Intent> oldInstallables;
1055 private final List<FlowRuleBatchOperation> batches;
1056 private int currentBatch;
1057
1058 ReplacingFailed(Intent newIntent, Intent oldIntent,
1059 List<Intent> newInstallables, List<Intent> oldInstallables,
1060 List<FlowRuleBatchOperation> batches) {
1061 this.newIntent = checkNotNull(newIntent);
1062 this.oldIntent = checkNotNull(oldIntent);
1063 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
1064 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
1065 this.batches = new LinkedList<>(batches);
1066 this.currentBatch = 0;
1067 }
1068
1069 @Override
1070 public List<Intent> allInstallables() {
1071 LinkedList<Intent> allInstallables = new LinkedList<>();
1072 allInstallables.addAll(newInstallables);
1073 allInstallables.addAll(oldInstallables);
1074
1075 return allInstallables;
1076 }
1077
1078 @Override
1079 public void batchSuccess() {
1080 currentBatch++;
1081 }
1082
1083 @Override
1084 public void writeAfterExecution(BatchWrite batchWrite) {
1085 batchWrite.setState(newIntent, FAILED);
1086 batchWrite.removeInstalledIntents(newIntent.id());
1087
1088 batchWrite.setState(oldIntent, WITHDRAWN);
1089 batchWrite.removeInstalledIntents(oldIntent.id());
1090 batchWrite.removeIntent(oldIntent.id());
1091 }
1092
1093 @Override
1094 public FlowRuleBatchOperation currentBatch() {
1095 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
1096 }
1097
1098 @Override
1099 public void batchFailed() {
1100 // the current batch has failed, so recompile
1101 // remove the current batch and all remaining
1102 for (int i = batches.size() - 1; i >= currentBatch; i--) {
1103 batches.remove(i);
1104 }
1105 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
1106
1107 batches.addAll(uninstallIntent(newIntent, newInstallables));
1108
1109 // TODO we might want to try to recompile the new intent
1110 }
1111 }
1112
1113 private class IntentBatchPreprocess implements Runnable {
1114
1115 // TODO make this configurable
1116 private static final int TIMEOUT_PER_OP = 500; // ms
1117 protected static final int MAX_ATTEMPTS = 3;
1118
1119 protected final IntentOperations ops;
1120
1121 // future holding current FlowRuleBatch installation result
1122 protected final long startTime = System.currentTimeMillis();
1123 protected final long endTime;
1124
1125 private IntentBatchPreprocess(IntentOperations ops, long endTime) {
1126 this.ops = checkNotNull(ops);
1127 this.endTime = endTime;
1128 }
1129
1130 public IntentBatchPreprocess(IntentOperations ops) {
1131 this(ops, System.currentTimeMillis() + ops.operations().size() * TIMEOUT_PER_OP);
1132 }
1133
1134 // FIXME compute reasonable timeouts
1135 protected long calculateTimeoutLimit() {
1136 return System.currentTimeMillis() + ops.operations().size() * TIMEOUT_PER_OP;
1137 }
1138
1139 @Override
1140 public void run() {
1141 try {
1142 // this should only be called on the first iteration
1143 // note: this a "expensive", so it is not done in the constructor
1144
1145 // - creates per Intent installation context (IntentUpdate)
1146 // - write Intents to store
1147 // - process (compile, install, etc.) each Intents
1148 // - generate FlowRuleBatch for this phase
1149 // build IntentUpdates
1150 List<IntentUpdate> updates = createIntentUpdates();
1151
1152 // Write batch information
1153 BatchWrite batchWrite = createBatchWrite(updates);
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001154 store.batchWrite(batchWrite);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001155
1156 new IntentBatchApplyFirst(ops, processIntentUpdates(updates), endTime, 0, null).run();
1157 } catch (Exception e) {
1158 log.error("Error submitting batches:", e);
1159 // FIXME incomplete Intents should be cleaned up
1160 // (transition to FAILED, etc.)
1161
1162 // TODO: remove duplicate due to inlining
1163 // the batch has failed
1164 // TODO: maybe we should do more?
1165 log.error("Walk the plank, matey...");
1166 batchService.removeIntentOperations(ops);
1167 }
1168 }
1169
1170 private List<IntentUpdate> createIntentUpdates() {
1171 return ops.operations().stream()
1172 .map(IntentManager.this::createIntentUpdate)
1173 .collect(Collectors.toList());
1174 }
1175
1176 private BatchWrite createBatchWrite(List<IntentUpdate> updates) {
1177 BatchWrite batchWrite = BatchWrite.newInstance();
1178 updates.forEach(update -> update.writeBeforeExecution(batchWrite));
1179 return batchWrite;
1180 }
1181
1182 private List<CompletedIntentUpdate> processIntentUpdates(List<IntentUpdate> updates) {
1183 // start processing each Intents
1184 List<CompletedIntentUpdate> completed = new ArrayList<>();
1185 for (IntentUpdate update : updates) {
1186 Optional<IntentUpdate> phase = Optional.of(update);
1187 IntentUpdate previous = update;
1188 while (true) {
1189 if (!phase.isPresent()) {
1190 // FIXME: not type safe cast
1191 completed.add((CompletedIntentUpdate) previous);
1192 break;
1193 }
1194 previous = phase.get();
1195 phase = previous.execute();
1196 }
1197 }
1198
1199 return completed;
1200 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001201 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001202
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001203 // TODO: better naming
1204 private class IntentBatchApplyFirst extends IntentBatchPreprocess {
1205
1206 protected final List<CompletedIntentUpdate> intentUpdates;
1207 protected final int installAttempt;
1208 protected Future<CompletedBatchOperation> future;
1209
1210 IntentBatchApplyFirst(IntentOperations operations, List<CompletedIntentUpdate> intentUpdates,
1211 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1212 super(operations, endTime);
1213 this.intentUpdates = ImmutableList.copyOf(intentUpdates);
1214 this.future = future;
1215 this.installAttempt = installAttempt;
1216 }
1217
1218 @Override
1219 public void run() {
1220 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1221 new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future).run();
Brian O'Connorcb900f42014-10-07 21:55:33 -07001222 }
1223
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001224 /**
Brian O'Connor427a1762014-11-19 18:40:32 -08001225 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001226 *
1227 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001228 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001229 protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001230 //TODO test this. (also, maybe save this batch)
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001231 FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
Ray Milkey93508c22014-12-02 11:35:56 -08001232 if (batch.size() > 0) {
1233 //FIXME apply batch might throw an exception
1234 return flowRuleService.applyBatch(batch);
1235 } else {
1236 // there are no flow rule batches; finalize the intent update
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001237 BatchWrite batchWrite = createFinalizedBatchWrite(updates);
1238
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001239 store.batchWrite(batchWrite);
Ray Milkey93508c22014-12-02 11:35:56 -08001240 return null;
1241 }
1242 }
1243
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001244 private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
1245 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList());
1246 for (CompletedIntentUpdate update : intentUpdates) {
1247 FlowRuleBatchOperation currentBatch = update.currentBatch();
1248 if (currentBatch != null) {
1249 batch.addAll(currentBatch);
1250 }
1251 }
1252 return batch;
1253 }
1254
1255 private BatchWrite createFinalizedBatchWrite(List<CompletedIntentUpdate> intentUpdates) {
1256 BatchWrite batchWrite = BatchWrite.newInstance();
1257 for (CompletedIntentUpdate update : intentUpdates) {
1258 update.writeAfterExecution(batchWrite);
1259 }
1260 return batchWrite;
1261 }
1262
1263 protected void abandonShip() {
1264 // the batch has failed
1265 // TODO: maybe we should do more?
1266 log.error("Walk the plank, matey...");
1267 future = null;
1268 batchService.removeIntentOperations(ops);
1269 }
1270 }
1271
1272 // TODO: better naming
1273 private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
1274
1275 IntentBatchProcessFutures(IntentOperations operations, List<CompletedIntentUpdate> intentUpdates,
1276 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1277 super(operations, intentUpdates, endTime, installAttempt, future);
1278 }
1279
1280 @Override
1281 public void run() {
1282 try {
1283 // - peek if current FlowRuleBatch is complete
1284 // -- If complete OK:
1285 // step each IntentUpdate forward
1286 // If phase left: generate next FlowRuleBatch
1287 // If no more phase: write parking states
1288 // -- If complete FAIL:
1289 // Intent which failed: transition Intent to FAILED
1290 // Other Intents: resubmit same FlowRuleBatch for this phase
1291 Future<CompletedBatchOperation> future = processFutures();
1292 if (future == null) {
1293 // there are no outstanding batches; we are done
1294 batchService.removeIntentOperations(ops);
1295 } else if (System.currentTimeMillis() > endTime) {
1296 // - cancel current FlowRuleBatch and resubmit again
1297 retry();
1298 } else {
1299 // we are not done yet, yield the thread by resubmitting ourselves
1300 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future));
1301 }
1302 } catch (Exception e) {
1303 log.error("Error submitting batches:", e);
1304 // FIXME incomplete Intents should be cleaned up
1305 // (transition to FAILED, etc.)
1306 abandonShip();
1307 }
1308 }
1309
1310 /**
1311 * Iterate through the pending futures, and remove them when they have completed.
1312 */
1313 private Future<CompletedBatchOperation> processFutures() {
1314 try {
1315 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
1316 updateBatches(completed);
1317 return applyNextBatch(intentUpdates);
1318 } catch (TimeoutException | InterruptedException te) {
1319 log.trace("Installation of intents are still pending: {}", ops);
1320 return future;
1321 } catch (ExecutionException e) {
1322 log.warn("Execution of batch failed: {}", ops, e);
1323 abandonShip();
1324 return future;
1325 }
1326 }
1327
Ray Milkey93508c22014-12-02 11:35:56 -08001328 private void updateBatches(CompletedBatchOperation completed) {
1329 if (completed.isSuccess()) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001330 for (CompletedIntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -08001331 update.batchSuccess();
1332 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001333 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -08001334 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -08001335 log.debug("Failed items: {}", completed.failedItems());
1336 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -08001337
1338 for (Long id : completed.failedIds()) {
1339 IntentId targetId = IntentId.valueOf(id);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001340 for (CompletedIntentUpdate update : intentUpdates) {
1341 for (Intent intent : update.allInstallables()) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001342 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001343 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001344 break;
1345 }
1346 }
1347 }
1348 // don't increment the non-failed items, as they have been reverted.
1349 }
1350 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001351 }
1352
Brian O'Connor427a1762014-11-19 18:40:32 -08001353 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -08001354 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001355 if (future.cancel(true)) { // cancel success; batch is reverted
1356 // reset the timer
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001357 long timeLimit = calculateTimeoutLimit();
1358 int attempts = installAttempt + 1;
1359 if (attempts == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001360 log.warn("Install request timed out: {}", ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001361 for (CompletedIntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001362 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001363 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001364 } else if (attempts > MAX_ATTEMPTS) {
Ray Milkey93508c22014-12-02 11:35:56 -08001365 abandonShip();
1366 return;
Brian O'Connor427a1762014-11-19 18:40:32 -08001367 } // else just resubmit the work
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001368 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1369 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, timeLimit, attempts, future));
Brian O'Connor427a1762014-11-19 18:40:32 -08001370 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001371 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001372 // FIXME
1373 // cancel failed... batch is broken; shouldn't happen!
1374 // we could manually reverse everything
1375 // ... or just core dump and send email to Ali
Ray Milkey93508c22014-12-02 11:35:56 -08001376 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -08001377 }
1378 }
Brian O'Connorcb900f42014-10-07 21:55:33 -07001379 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001380
1381 private class InternalBatchDelegate implements IntentBatchDelegate {
1382 @Override
1383 public void execute(IntentOperations operations) {
Yuta HIGUCHIfe4367a2014-11-24 19:19:09 -08001384 log.info("Execute {} operation(s).", operations.operations().size());
1385 log.debug("Execute operations: {}", operations.operations());
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001386 //FIXME: perhaps we want to track this task so that we can cancel it.
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001387 executor.execute(new IntentBatchPreprocess(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001388 }
1389
1390 @Override
1391 public void cancel(IntentOperations operations) {
1392 //FIXME: implement this
1393 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
1394 }
1395 }
Brian O'Connor66630c82014-10-02 21:08:19 -07001396}