blob: 8703e01cc6712c15b6521c8aa22cd0631107c30f [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 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800383
384 @Override
385 public void process(IntentOperation op) {
386 //FIXME
387 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700388 }
389
Brian O'Connor72a034c2014-11-26 18:24:23 -0800390 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
391 boolean compileAllFailed) {
392 Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
393 // Attempt recompilation of the specified intents first.
394 for (IntentId id : intentIds) {
395 Intent intent = store.getIntent(id);
396 if (intent == null) {
397 continue;
398 }
399 IntentOperations.Builder builder = batches.get(intent.appId());
400 if (builder == null) {
401 builder = IntentOperations.builder(intent.appId());
402 batches.put(intent.appId(), builder);
403 }
404 builder.addUpdateOperation(id);
405 }
406
407 if (compileAllFailed) {
408 // If required, compile all currently failed intents.
409 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800410 IntentState state = getIntentState(intent.id());
411 if (RECOMPILE.contains(state)) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800412 IntentOperations.Builder builder = batches.get(intent.appId());
413 if (builder == null) {
414 builder = IntentOperations.builder(intent.appId());
415 batches.put(intent.appId(), builder);
416 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800417 if (state == WITHDRAW_REQ) {
418 builder.addWithdrawOperation(intent.id());
419 } else {
420 builder.addUpdateOperation(intent.id());
421 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800422 }
423 }
424 }
425
426 for (ApplicationId appId : batches.keySet()) {
427 if (batchService.isLocalLeader(appId)) {
428 execute(batches.get(appId).build());
429 }
430 }
431 }
432
tom95329eb2014-10-06 08:40:06 -0700433 // Topology change delegate
434 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
435 @Override
tom85258ee2014-10-07 00:10:02 -0700436 public void triggerCompile(Iterable<IntentId> intentIds,
437 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800438 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700439 }
tom95329eb2014-10-06 08:40:06 -0700440 }
tom85258ee2014-10-07 00:10:02 -0700441
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800442 // TODO: simplify the branching statements
443 private IntentUpdate createIntentUpdate(IntentOperation operation) {
444 switch (operation.type()) {
445 case SUBMIT:
446 return new InstallRequest(operation.intent());
447 case WITHDRAW: {
448 Intent oldIntent = store.getIntent(operation.intentId());
449 if (oldIntent == null) {
450 return new DoNothing();
451 }
452 List<Intent> installables = store.getInstallableIntents(oldIntent.id());
453 if (installables == null) {
454 return new WithdrawStateChange1(oldIntent);
455 }
456 return new WithdrawRequest(oldIntent, installables);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700457 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800458 case REPLACE: {
459 Intent newIntent = operation.intent();
460 Intent oldIntent = store.getIntent(operation.intentId());
461 if (oldIntent == null) {
462 return new InstallRequest(newIntent);
463 }
464 List<Intent> installables = store.getInstallableIntents(oldIntent.id());
465 if (installables == null) {
466 if (newIntent.equals(oldIntent)) {
467 return new InstallRequest(newIntent);
468 } else {
469 return new WithdrawStateChange2(oldIntent);
470 }
471 }
472 return new ReplaceRequest(newIntent, oldIntent, installables);
473 }
474 case UPDATE: {
475 Intent oldIntent = store.getIntent(operation.intentId());
476 if (oldIntent == null) {
477 return new DoNothing();
478 }
479 List<Intent> installables = getInstallableIntents(oldIntent.id());
480 if (installables == null) {
481 return new InstallRequest(oldIntent);
482 }
483 return new ReplaceRequest(oldIntent, oldIntent, installables);
484 }
485 default:
486 // illegal state
487 return new DoNothing();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700488 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700489 }
490
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800491 private class InstallRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800492
493 private final Intent intent;
494
495 InstallRequest(Intent intent) {
496 this.intent = checkNotNull(intent);
497 }
498
499 @Override
500 public void writeBeforeExecution(BatchWrite batchWrite) {
501 // TODO consider only "creating" intent if it does not exist
502 // Note: We need to set state to INSTALL_REQ regardless.
503 batchWrite.createIntent(intent);
504 }
505
506 @Override
507 public Optional<IntentUpdate> execute() {
508 return Optional.of(new Compiling(intent));
509 }
510 }
511
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800512 private class WithdrawRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800513
514 private final Intent intent;
515 private final List<Intent> installables;
516
517 WithdrawRequest(Intent intent, List<Intent> installables) {
518 this.intent = checkNotNull(intent);
519 this.installables = ImmutableList.copyOf(checkNotNull(installables));
520 }
521
522 @Override
523 public void writeBeforeExecution(BatchWrite batchWrite) {
524 batchWrite.setState(intent, WITHDRAW_REQ);
525 }
526
527 @Override
528 public Optional<IntentUpdate> execute() {
529 return Optional.of(new Withdrawing(intent, installables));
530 }
531 }
532
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800533 private class ReplaceRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800534
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700535 private final Intent newIntent;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800536 private final Intent oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700537 private final List<Intent> oldInstallables;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700538
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800539 ReplaceRequest(Intent newIntent, Intent oldIntent, List<Intent> oldInstallables) {
540 this.newIntent = checkNotNull(newIntent);
541 this.oldIntent = checkNotNull(oldIntent);
542 this.oldInstallables = ImmutableList.copyOf(oldInstallables);
543 }
544
545 @Override
546 public void writeBeforeExecution(BatchWrite batchWrite) {
547 // TODO consider only "creating" intent if it does not exist
548 // Note: We need to set state to INSTALL_REQ regardless.
549 batchWrite.createIntent(newIntent);
550 }
551
552 @Override
553 public Optional<IntentUpdate> execute() {
554 try {
555 List<Intent> installables = compileIntent(newIntent, oldInstallables);
556 return Optional.of(new Replacing(newIntent, oldIntent, installables, oldInstallables));
557 } catch (PathNotFoundException e) {
558 log.debug("Path not found for intent {}", newIntent);
559 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
560 } catch (IntentException e) {
561 log.warn("Unable to compile intent {} due to:", newIntent.id(), e);
562 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700563 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800564 }
565 }
566
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800567 private class DoNothing implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800568 }
569
570 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800571 private class WithdrawStateChange1 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800572
573 private final Intent intent;
574
575 WithdrawStateChange1(Intent intent) {
576 this.intent = checkNotNull(intent);
577 }
578
579 @Override
580 public void writeBeforeExecution(BatchWrite batchWrite) {
581 batchWrite.setState(intent, WITHDRAW_REQ);
582 }
583
584 @Override
585 public void writeAfterExecution(BatchWrite batchWrite) {
586 batchWrite.setState(intent, WITHDRAWN);
587 batchWrite.removeInstalledIntents(intent.id());
588 batchWrite.removeIntent(intent.id());
589 }
590 }
591
592 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800593 private class WithdrawStateChange2 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800594
595 private final Intent intent;
596
597 WithdrawStateChange2(Intent intent) {
598 this.intent = checkNotNull(intent);
599 }
600
601 @Override
602 public void writeBeforeExecution(BatchWrite batchWrite) {
603 // TODO consider only "creating" intent if it does not exist
604 // Note: We need to set state to INSTALL_REQ regardless.
605 batchWrite.createIntent(intent);
606 }
607
608 @Override
609 public void writeAfterExecution(BatchWrite batchWrite) {
610 batchWrite.setState(intent, WITHDRAWN);
611 batchWrite.removeInstalledIntents(intent.id());
612 batchWrite.removeIntent(intent.id());
613 }
614 }
615
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800616 private class Compiling implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800617
618 private final Intent intent;
619
620 Compiling(Intent intent) {
621 this.intent = checkNotNull(intent);
622 }
623
624 @Override
625 public Optional<IntentUpdate> execute() {
626 try {
627 // Compile the intent into installable derivatives.
628 // If all went well, associate the resulting list of installable
629 // intents with the top-level intent and proceed to install.
630 return Optional.of(new Installing(intent, compileIntent(intent, null)));
631 } catch (PathNotFoundException e) {
632 return Optional.of(new CompilingFailed(intent, e));
633 } catch (IntentException e) {
634 return Optional.of(new CompilingFailed(intent, e));
635 }
636 }
637 }
638
639 // TODO: better naming because install() method actually generate FlowRuleBatchOperations
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800640 private class Installing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800641
642 private final Intent intent;
643 private final List<Intent> installables;
644
645 Installing(Intent intent, List<Intent> installables) {
646 this.intent = checkNotNull(intent);
647 this.installables = ImmutableList.copyOf(checkNotNull(installables));
648 }
649
650 @Override
651 public Optional<IntentUpdate> execute() {
652 Exception exception = null;
653 // Indicate that the intent is entering the installing phase.
654
655 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
656 for (Intent installable : installables) {
657 registerSubclassInstallerIfNeeded(installable);
658 trackerService.addTrackedResources(intent.id(), installable.resources());
659 try {
660 batches.addAll(getInstaller(installable).install(installable));
661 } catch (Exception e) { // TODO this should be IntentException
662 log.warn("Unable to install intent {} due to:", intent.id(), e);
663 trackerService.removeTrackedResources(intent.id(), installable.resources());
664 //TODO we failed; intent should be recompiled
665 exception = e;
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800666 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700667 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700668
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800669 if (exception != null) {
670 return Optional.of(new InstallingFailed(intent, installables, batches));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800671 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800672
673 return Optional.of(new Installed(intent, installables, batches));
674 }
675 }
676
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800677 private class Withdrawing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800678
679 private final Intent intent;
680 private final List<Intent> installables;
681
682 Withdrawing(Intent intent, List<Intent> installables) {
683 this.intent = checkNotNull(intent);
684 this.installables = ImmutableList.copyOf(installables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800685 }
686
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800687 @Override
688 public Optional<IntentUpdate> execute() {
689 List<FlowRuleBatchOperation> batches = uninstallIntent(intent, installables);
690
691 return Optional.of(new Withdrawn(intent, installables, batches));
692 }
693 }
694
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800695 private class Replacing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800696
697 private final Intent newIntent;
698 private final Intent oldIntent;
699 private final List<Intent> newInstallables;
700 private final List<Intent> oldInstallables;
701
702 private Exception exception;
703
704 Replacing(Intent newIntent, Intent oldIntent,
705 List<Intent> newInstallables, List<Intent> oldInstallables) {
706 this.newIntent = checkNotNull(newIntent);
707 this.oldIntent = checkNotNull(oldIntent);
708 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
709 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700710 }
711
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800712 @Override
713 public Optional<IntentUpdate> execute() {
714 List<FlowRuleBatchOperation> batches = replace();
715
716 if (exception == null) {
717 return Optional.of(
718 new Replaced(newIntent, oldIntent, newInstallables, oldInstallables, batches));
719 }
720
721 return Optional.of(
722 new ReplacingFailed(newIntent, oldIntent, newInstallables, oldInstallables, batches));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700723 }
724
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800725 protected List<FlowRuleBatchOperation> replace() {
726 checkState(oldInstallables.size() == newInstallables.size(),
727 "Old and New Intent must have equivalent installable intents.");
728
729 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
730 for (int i = 0; i < oldInstallables.size(); i++) {
731 Intent oldInstallable = oldInstallables.get(i);
732 Intent newInstallable = newInstallables.get(i);
733 //FIXME revisit this
734// if (oldInstallable.equals(newInstallable)) {
735// continue;
736// }
737 checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
738 "Installable Intent type mismatch.");
739 trackerService.removeTrackedResources(oldIntent.id(), oldInstallable.resources());
740 trackerService.addTrackedResources(newIntent.id(), newInstallable.resources());
741 try {
742 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
743 } catch (IntentException e) {
744 log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
745 //FIXME... we failed. need to uninstall (if same) or revert (if different)
746 trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
747 exception = e;
748 batches = uninstallIntent(oldIntent, oldInstallables);
749 }
750 }
751 return batches;
752 }
753 }
754
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800755 private class Installed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800756
757 private final Intent intent;
758 private final List<Intent> installables;
759 private IntentState intentState;
760 private final List<FlowRuleBatchOperation> batches;
761 private int currentBatch = 0;
762
763 Installed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
764 this.intent = checkNotNull(intent);
765 this.installables = ImmutableList.copyOf(checkNotNull(installables));
766 this.batches = new LinkedList<>(checkNotNull(batches));
767 this.intentState = INSTALLING;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700768 }
769
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800770 @Override
771 public void batchSuccess() {
Ray Milkey93508c22014-12-02 11:35:56 -0800772 currentBatch++;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800773 }
774
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800775 @Override
776 public List<Intent> allInstallables() {
777 return installables;
778 }
779
780 @Override
781 public void writeAfterExecution(BatchWrite batchWrite) {
782 switch (intentState) {
783 case INSTALLING:
784 batchWrite.setState(intent, INSTALLED);
785 batchWrite.setInstallableIntents(intent.id(), this.installables);
786 break;
787 case FAILED:
788 batchWrite.setState(intent, FAILED);
789 batchWrite.removeInstalledIntents(intent.id());
790 break;
791 default:
792 break;
793 }
794 }
795
796 @Override
797 public FlowRuleBatchOperation currentBatch() {
798 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
799 }
800
801 @Override
802 public void batchFailed() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800803 // the current batch has failed, so recompile
804 // remove the current batch and all remaining
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800805 for (int i = batches.size() - 1; i >= currentBatch; i--) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800806 batches.remove(i);
807 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800808 intentState = FAILED;
809 batches.addAll(uninstallIntent(intent, installables));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800810
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800811 // TODO we might want to try to recompile the new intent
Brian O'Connor427a1762014-11-19 18:40:32 -0800812 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800813 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800814
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800815 private class Withdrawn implements CompletedIntentUpdate {
Brian O'Connor427a1762014-11-19 18:40:32 -0800816
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800817 private final Intent intent;
818 private final List<Intent> installables;
819 private final List<FlowRuleBatchOperation> batches;
820 private int currentBatch;
821
822 Withdrawn(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
823 this.intent = checkNotNull(intent);
824 this.installables = ImmutableList.copyOf(installables);
825 this.batches = new LinkedList<>(batches);
826 this.currentBatch = 0;
827 }
828
829 @Override
830 public List<Intent> allInstallables() {
831 return installables;
832 }
833
834 @Override
835 public void batchSuccess() {
836 currentBatch++;
837 }
838
839 @Override
840 public void writeAfterExecution(BatchWrite batchWrite) {
841 batchWrite.setState(intent, WITHDRAWN);
842 batchWrite.removeInstalledIntents(intent.id());
843 batchWrite.removeIntent(intent.id());
844 }
845
846 @Override
847 public FlowRuleBatchOperation currentBatch() {
848 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
849 }
850
851 @Override
852 public void batchFailed() {
853 // the current batch has failed, so recompile
854 // remove the current batch and all remaining
855 for (int i = batches.size() - 1; i >= currentBatch; i--) {
856 batches.remove(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800857 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800858 batches.addAll(uninstallIntent(intent, installables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700859 }
860 }
861
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800862 private class Replaced implements CompletedIntentUpdate {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700863
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800864 private final Intent newIntent;
865 private final Intent oldIntent;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700866
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800867 private final List<Intent> newInstallables;
868 private final List<Intent> oldInstallables;
869 private final List<FlowRuleBatchOperation> batches;
870 private int currentBatch;
Brian O'Connor427a1762014-11-19 18:40:32 -0800871
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800872 Replaced(Intent newIntent, Intent oldIntent,
873 List<Intent> newInstallables, List<Intent> oldInstallables,
874 List<FlowRuleBatchOperation> batches) {
875 this.newIntent = checkNotNull(newIntent);
876 this.oldIntent = checkNotNull(oldIntent);
877 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
878 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
879 this.batches = new LinkedList<>(batches);
880 this.currentBatch = 0;
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800881 }
882
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800883 @Override
884 public List<Intent> allInstallables() {
885 LinkedList<Intent> allInstallables = new LinkedList<>();
886 allInstallables.addAll(newInstallables);
887 allInstallables.addAll(oldInstallables);
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800888
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800889 return allInstallables;
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800890 }
891
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800892 @Override
893 public void batchSuccess() {
894 currentBatch++;
Brian O'Connor427a1762014-11-19 18:40:32 -0800895 }
896
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800897 @Override
898 public void writeAfterExecution(BatchWrite batchWrite) {
899 batchWrite.setState(newIntent, INSTALLED);
900 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800901
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800902 batchWrite.setState(oldIntent, WITHDRAWN);
903 batchWrite.removeInstalledIntents(oldIntent.id());
904 batchWrite.removeIntent(oldIntent.id());
905 }
906
907 @Override
908 public FlowRuleBatchOperation currentBatch() {
909 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
910 }
911
912 @Override
913 public void batchFailed() {
914 // the current batch has failed, so recompile
915 // remove the current batch and all remaining
916 for (int i = batches.size() - 1; i >= currentBatch; i--) {
917 batches.remove(i);
918 }
919 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
920
921 batches.addAll(uninstallIntent(newIntent, newInstallables));
922
923 // TODO we might want to try to recompile the new intent
924 }
925 }
926
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800927 private class CompilingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800928
929 private final Intent intent;
930 private final IntentException exception;
931
932 CompilingFailed(Intent intent, IntentException exception) {
933 this.intent = checkNotNull(intent);
934 this.exception = checkNotNull(exception);
935 }
936
937 @Override
938 public Optional<IntentUpdate> execute() {
939 if (exception instanceof PathNotFoundException) {
940 log.debug("Path not found for intent {}", intent);
941 } else {
942 log.warn("Unable to compile intent {} due to:", intent.id(), exception);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800943 }
944
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800945 return Optional.empty();
946 }
947
948 @Override
949 public void writeAfterExecution(BatchWrite batchWrite) {
950 batchWrite.setState(intent, FAILED);
951 batchWrite.removeInstalledIntents(intent.id());
952 }
953 }
954
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800955 private class InstallingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800956
957 private final Intent intent;
958 private final List<Intent> installables;
959 private final List<FlowRuleBatchOperation> batches;
960 private int currentBatch = 0;
961
962 InstallingFailed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
963 this.intent = checkNotNull(intent);
964 this.installables = ImmutableList.copyOf(checkNotNull(installables));
965 this.batches = new LinkedList<>(checkNotNull(batches));
966 }
967
968 @Override
969 public List<Intent> allInstallables() {
970 return installables;
971 }
972
973 @Override
974 public void batchSuccess() {
975 currentBatch++;
976 }
977
978 @Override
979 public void writeAfterExecution(BatchWrite batchWrite) {
980 batchWrite.setState(intent, FAILED);
981 batchWrite.removeInstalledIntents(intent.id());
982 }
983
984 @Override
985 public FlowRuleBatchOperation currentBatch() {
986 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
987 }
988
989 @Override
990 public void batchFailed() {
991 // the current batch has failed, so recompile
992 // remove the current batch and all remaining
993 for (int i = batches.size() - 1; i >= currentBatch; i--) {
994 batches.remove(i);
995 }
996 batches.addAll(uninstallIntent(intent, installables));
997
998 // TODO we might want to try to recompile the new intent
999 }
1000 }
1001
Sho SHIMIZU28e72712015-01-27 17:03:41 -08001002 private class ReplacingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001003
1004 private final Intent newIntent;
1005 private final Intent oldIntent;
1006 private final List<Intent> newInstallables;
1007 private final List<Intent> oldInstallables;
1008 private final List<FlowRuleBatchOperation> batches;
1009 private int currentBatch;
1010
1011 ReplacingFailed(Intent newIntent, Intent oldIntent,
1012 List<Intent> newInstallables, List<Intent> oldInstallables,
1013 List<FlowRuleBatchOperation> batches) {
1014 this.newIntent = checkNotNull(newIntent);
1015 this.oldIntent = checkNotNull(oldIntent);
1016 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
1017 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
1018 this.batches = new LinkedList<>(batches);
1019 this.currentBatch = 0;
1020 }
1021
1022 @Override
1023 public List<Intent> allInstallables() {
1024 LinkedList<Intent> allInstallables = new LinkedList<>();
1025 allInstallables.addAll(newInstallables);
1026 allInstallables.addAll(oldInstallables);
1027
1028 return allInstallables;
1029 }
1030
1031 @Override
1032 public void batchSuccess() {
1033 currentBatch++;
1034 }
1035
1036 @Override
1037 public void writeAfterExecution(BatchWrite batchWrite) {
1038 batchWrite.setState(newIntent, FAILED);
1039 batchWrite.removeInstalledIntents(newIntent.id());
1040
1041 batchWrite.setState(oldIntent, WITHDRAWN);
1042 batchWrite.removeInstalledIntents(oldIntent.id());
1043 batchWrite.removeIntent(oldIntent.id());
1044 }
1045
1046 @Override
1047 public FlowRuleBatchOperation currentBatch() {
1048 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
1049 }
1050
1051 @Override
1052 public void batchFailed() {
1053 // the current batch has failed, so recompile
1054 // remove the current batch and all remaining
1055 for (int i = batches.size() - 1; i >= currentBatch; i--) {
1056 batches.remove(i);
1057 }
1058 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
1059
1060 batches.addAll(uninstallIntent(newIntent, newInstallables));
1061
1062 // TODO we might want to try to recompile the new intent
1063 }
1064 }
1065
1066 private class IntentBatchPreprocess implements Runnable {
1067
1068 // TODO make this configurable
1069 private static final int TIMEOUT_PER_OP = 500; // ms
1070 protected static final int MAX_ATTEMPTS = 3;
1071
1072 protected final IntentOperations ops;
1073
1074 // future holding current FlowRuleBatch installation result
1075 protected final long startTime = System.currentTimeMillis();
1076 protected final long endTime;
1077
1078 private IntentBatchPreprocess(IntentOperations ops, long endTime) {
1079 this.ops = checkNotNull(ops);
1080 this.endTime = endTime;
1081 }
1082
1083 public IntentBatchPreprocess(IntentOperations ops) {
1084 this(ops, System.currentTimeMillis() + ops.operations().size() * TIMEOUT_PER_OP);
1085 }
1086
1087 // FIXME compute reasonable timeouts
1088 protected long calculateTimeoutLimit() {
1089 return System.currentTimeMillis() + ops.operations().size() * TIMEOUT_PER_OP;
1090 }
1091
1092 @Override
1093 public void run() {
1094 try {
1095 // this should only be called on the first iteration
1096 // note: this a "expensive", so it is not done in the constructor
1097
1098 // - creates per Intent installation context (IntentUpdate)
1099 // - write Intents to store
1100 // - process (compile, install, etc.) each Intents
1101 // - generate FlowRuleBatch for this phase
1102 // build IntentUpdates
1103 List<IntentUpdate> updates = createIntentUpdates();
1104
1105 // Write batch information
1106 BatchWrite batchWrite = createBatchWrite(updates);
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001107 store.batchWrite(batchWrite);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001108
1109 new IntentBatchApplyFirst(ops, processIntentUpdates(updates), endTime, 0, null).run();
1110 } catch (Exception e) {
1111 log.error("Error submitting batches:", e);
1112 // FIXME incomplete Intents should be cleaned up
1113 // (transition to FAILED, etc.)
1114
1115 // TODO: remove duplicate due to inlining
1116 // the batch has failed
1117 // TODO: maybe we should do more?
1118 log.error("Walk the plank, matey...");
1119 batchService.removeIntentOperations(ops);
1120 }
1121 }
1122
1123 private List<IntentUpdate> createIntentUpdates() {
1124 return ops.operations().stream()
1125 .map(IntentManager.this::createIntentUpdate)
1126 .collect(Collectors.toList());
1127 }
1128
1129 private BatchWrite createBatchWrite(List<IntentUpdate> updates) {
1130 BatchWrite batchWrite = BatchWrite.newInstance();
1131 updates.forEach(update -> update.writeBeforeExecution(batchWrite));
1132 return batchWrite;
1133 }
1134
1135 private List<CompletedIntentUpdate> processIntentUpdates(List<IntentUpdate> updates) {
1136 // start processing each Intents
1137 List<CompletedIntentUpdate> completed = new ArrayList<>();
1138 for (IntentUpdate update : updates) {
1139 Optional<IntentUpdate> phase = Optional.of(update);
1140 IntentUpdate previous = update;
1141 while (true) {
1142 if (!phase.isPresent()) {
1143 // FIXME: not type safe cast
1144 completed.add((CompletedIntentUpdate) previous);
1145 break;
1146 }
1147 previous = phase.get();
1148 phase = previous.execute();
1149 }
1150 }
1151
1152 return completed;
1153 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001154 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001155
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001156 // TODO: better naming
1157 private class IntentBatchApplyFirst extends IntentBatchPreprocess {
1158
1159 protected final List<CompletedIntentUpdate> intentUpdates;
1160 protected final int installAttempt;
1161 protected Future<CompletedBatchOperation> future;
1162
1163 IntentBatchApplyFirst(IntentOperations operations, List<CompletedIntentUpdate> intentUpdates,
1164 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1165 super(operations, endTime);
1166 this.intentUpdates = ImmutableList.copyOf(intentUpdates);
1167 this.future = future;
1168 this.installAttempt = installAttempt;
1169 }
1170
1171 @Override
1172 public void run() {
1173 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1174 new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future).run();
Brian O'Connorcb900f42014-10-07 21:55:33 -07001175 }
1176
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001177 /**
Brian O'Connor427a1762014-11-19 18:40:32 -08001178 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001179 *
1180 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001181 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001182 protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001183 //TODO test this. (also, maybe save this batch)
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001184 FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
Ray Milkey93508c22014-12-02 11:35:56 -08001185 if (batch.size() > 0) {
1186 //FIXME apply batch might throw an exception
1187 return flowRuleService.applyBatch(batch);
1188 } else {
1189 // there are no flow rule batches; finalize the intent update
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001190 BatchWrite batchWrite = createFinalizedBatchWrite(updates);
1191
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001192 store.batchWrite(batchWrite);
Ray Milkey93508c22014-12-02 11:35:56 -08001193 return null;
1194 }
1195 }
1196
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001197 private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
1198 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList());
1199 for (CompletedIntentUpdate update : intentUpdates) {
1200 FlowRuleBatchOperation currentBatch = update.currentBatch();
1201 if (currentBatch != null) {
1202 batch.addAll(currentBatch);
1203 }
1204 }
1205 return batch;
1206 }
1207
1208 private BatchWrite createFinalizedBatchWrite(List<CompletedIntentUpdate> intentUpdates) {
1209 BatchWrite batchWrite = BatchWrite.newInstance();
1210 for (CompletedIntentUpdate update : intentUpdates) {
1211 update.writeAfterExecution(batchWrite);
1212 }
1213 return batchWrite;
1214 }
1215
1216 protected void abandonShip() {
1217 // the batch has failed
1218 // TODO: maybe we should do more?
1219 log.error("Walk the plank, matey...");
1220 future = null;
1221 batchService.removeIntentOperations(ops);
1222 }
1223 }
1224
1225 // TODO: better naming
1226 private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
1227
1228 IntentBatchProcessFutures(IntentOperations operations, List<CompletedIntentUpdate> intentUpdates,
1229 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1230 super(operations, intentUpdates, endTime, installAttempt, future);
1231 }
1232
1233 @Override
1234 public void run() {
1235 try {
1236 // - peek if current FlowRuleBatch is complete
1237 // -- If complete OK:
1238 // step each IntentUpdate forward
1239 // If phase left: generate next FlowRuleBatch
1240 // If no more phase: write parking states
1241 // -- If complete FAIL:
1242 // Intent which failed: transition Intent to FAILED
1243 // Other Intents: resubmit same FlowRuleBatch for this phase
1244 Future<CompletedBatchOperation> future = processFutures();
1245 if (future == null) {
1246 // there are no outstanding batches; we are done
1247 batchService.removeIntentOperations(ops);
1248 } else if (System.currentTimeMillis() > endTime) {
1249 // - cancel current FlowRuleBatch and resubmit again
1250 retry();
1251 } else {
1252 // we are not done yet, yield the thread by resubmitting ourselves
1253 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future));
1254 }
1255 } catch (Exception e) {
1256 log.error("Error submitting batches:", e);
1257 // FIXME incomplete Intents should be cleaned up
1258 // (transition to FAILED, etc.)
1259 abandonShip();
1260 }
1261 }
1262
1263 /**
1264 * Iterate through the pending futures, and remove them when they have completed.
1265 */
1266 private Future<CompletedBatchOperation> processFutures() {
1267 try {
1268 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
1269 updateBatches(completed);
1270 return applyNextBatch(intentUpdates);
1271 } catch (TimeoutException | InterruptedException te) {
1272 log.trace("Installation of intents are still pending: {}", ops);
1273 return future;
1274 } catch (ExecutionException e) {
1275 log.warn("Execution of batch failed: {}", ops, e);
1276 abandonShip();
1277 return future;
1278 }
1279 }
1280
Ray Milkey93508c22014-12-02 11:35:56 -08001281 private void updateBatches(CompletedBatchOperation completed) {
1282 if (completed.isSuccess()) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001283 for (CompletedIntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -08001284 update.batchSuccess();
1285 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001286 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -08001287 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -08001288 log.debug("Failed items: {}", completed.failedItems());
1289 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -08001290
1291 for (Long id : completed.failedIds()) {
1292 IntentId targetId = IntentId.valueOf(id);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001293 for (CompletedIntentUpdate update : intentUpdates) {
1294 for (Intent intent : update.allInstallables()) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001295 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001296 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001297 break;
1298 }
1299 }
1300 }
1301 // don't increment the non-failed items, as they have been reverted.
1302 }
1303 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001304 }
1305
Brian O'Connor427a1762014-11-19 18:40:32 -08001306 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -08001307 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001308 if (future.cancel(true)) { // cancel success; batch is reverted
1309 // reset the timer
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001310 long timeLimit = calculateTimeoutLimit();
1311 int attempts = installAttempt + 1;
1312 if (attempts == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001313 log.warn("Install request timed out: {}", ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001314 for (CompletedIntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001315 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001316 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001317 } else if (attempts > MAX_ATTEMPTS) {
Ray Milkey93508c22014-12-02 11:35:56 -08001318 abandonShip();
1319 return;
Brian O'Connor427a1762014-11-19 18:40:32 -08001320 } // else just resubmit the work
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001321 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1322 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, timeLimit, attempts, future));
Brian O'Connor427a1762014-11-19 18:40:32 -08001323 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001324 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001325 // FIXME
1326 // cancel failed... batch is broken; shouldn't happen!
1327 // we could manually reverse everything
1328 // ... or just core dump and send email to Ali
Ray Milkey93508c22014-12-02 11:35:56 -08001329 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -08001330 }
1331 }
Brian O'Connorcb900f42014-10-07 21:55:33 -07001332 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001333
1334 private class InternalBatchDelegate implements IntentBatchDelegate {
1335 @Override
1336 public void execute(IntentOperations operations) {
Yuta HIGUCHIfe4367a2014-11-24 19:19:09 -08001337 log.info("Execute {} operation(s).", operations.operations().size());
1338 log.debug("Execute operations: {}", operations.operations());
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001339 //FIXME: perhaps we want to track this task so that we can cancel it.
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001340 executor.execute(new IntentBatchPreprocess(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001341 }
1342
1343 @Override
1344 public void cancel(IntentOperations operations) {
1345 //FIXME: implement this
1346 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
1347 }
1348 }
Brian O'Connor66630c82014-10-02 21:08:19 -07001349}