blob: f94424f416b6e749a89af9181e11e177f6f5475b [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;
Brian O'Connorcff03322015-02-03 15:28:59 -080040import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.intent.IntentEvent;
42import org.onosproject.net.intent.IntentException;
43import org.onosproject.net.intent.IntentExtensionService;
44import org.onosproject.net.intent.IntentId;
45import org.onosproject.net.intent.IntentInstaller;
46import org.onosproject.net.intent.IntentListener;
47import org.onosproject.net.intent.IntentOperation;
48import org.onosproject.net.intent.IntentOperations;
49import org.onosproject.net.intent.IntentService;
50import org.onosproject.net.intent.IntentState;
51import org.onosproject.net.intent.IntentStore;
Sho SHIMIZU64ae11c2014-12-03 15:17:47 -080052import org.onosproject.net.intent.BatchWrite;
Brian O'Connorabafb502014-12-02 22:26:20 -080053import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070054import org.slf4j.Logger;
55
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080056import java.util.ArrayList;
57import java.util.Collections;
58import java.util.EnumSet;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080059import java.util.LinkedList;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080060import java.util.List;
61import java.util.Map;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080062import java.util.Optional;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080063import java.util.concurrent.ConcurrentHashMap;
64import java.util.concurrent.ConcurrentMap;
65import java.util.concurrent.ExecutionException;
66import java.util.concurrent.ExecutorService;
67import java.util.concurrent.Future;
68import java.util.concurrent.TimeUnit;
69import java.util.concurrent.TimeoutException;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080070import java.util.stream.Collectors;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070071
Brian O'Connorfa81eae2014-10-30 13:20:05 -070072import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -080073import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080074import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connorabafb502014-12-02 22:26:20 -080075import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070076import static org.onlab.util.Tools.namedThreads;
77import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070078
79/**
80 * An implementation of Intent Manager.
81 */
82@Component(immediate = true)
83@Service
84public class IntentManager
85 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080086 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070087
88 public static final String INTENT_NULL = "Intent cannot be null";
89 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
90
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080091 private static final int NUM_THREADS = 12;
92
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080093 private static final EnumSet<IntentState> RECOMPILE
94 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080095
96
Brian O'Connor66630c82014-10-02 21:08:19 -070097 // Collections for compiler, installer, and listener are ONOS instance local
98 private final ConcurrentMap<Class<? extends Intent>,
99 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700100 private final ConcurrentMap<Class<? extends Intent>,
101 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700102
103 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700104 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700105
Brian O'Connor520c0522014-11-23 23:50:47 -0800106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected IntentStore store;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700113 protected IntentBatchService batchService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700116 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700119 protected EventDeliveryService eventDispatcher;
120
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected FlowRuleService flowRuleService;
123
Brian O'Connor520c0522014-11-23 23:50:47 -0800124
125 private ExecutorService executor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800126
127 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
128 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
129 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
130 private IdGenerator idGenerator;
131
Brian O'Connorcff03322015-02-03 15:28:59 -0800132 private final IntentAccumulator accumulator = new IntentAccumulator();
133
Brian O'Connor66630c82014-10-02 21:08:19 -0700134 @Activate
135 public void activate() {
136 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700137 trackerService.setDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700138 batchService.setDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700139 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Yuta HIGUCHI7a9fddd2014-12-03 15:30:25 -0800140 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800141 idGenerator = coreService.getIdGenerator("intent-ids");
142 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700143 log.info("Started");
144 }
145
146 @Deactivate
147 public void deactivate() {
148 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700149 trackerService.unsetDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700150 batchService.unsetDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700151 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700152 executor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800153 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700154 log.info("Stopped");
155 }
156
157 @Override
158 public void submit(Intent intent) {
159 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800160 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
161 //FIXME timestamp?
162 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700163 }
164
165 @Override
166 public void withdraw(Intent intent) {
167 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800168 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
169 //FIXME timestamp?
170 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700171 }
172
Brian O'Connor66630c82014-10-02 21:08:19 -0700173 @Override
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700174 public void replace(IntentId oldIntentId, Intent newIntent) {
Brian O'Connorcff03322015-02-03 15:28:59 -0800175 throw new UnsupportedOperationException("replace is not implemented");
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700176 }
177
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700178 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700179 public void execute(IntentOperations operations) {
Brian O'Connorcff03322015-02-03 15:28:59 -0800180 for (IntentOperation op : operations.operations()) {
181 switch (op.type()) {
182 case SUBMIT:
183 case UPDATE:
184 submit(op.intent());
185 break;
186 case WITHDRAW:
187 withdraw(op.intent());
188 break;
189 //fallthrough
190 case REPLACE:
191 default:
192 throw new UnsupportedOperationException("replace not supported");
193 }
Brian O'Connore2ff25a2014-11-18 19:25:43 -0800194 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700195 }
196
197 @Override
198 public Iterable<Intent> getIntents() {
199 return store.getIntents();
200 }
201
202 @Override
203 public long getIntentCount() {
204 return store.getIntentCount();
205 }
206
207 @Override
208 public Intent getIntent(IntentId id) {
209 checkNotNull(id, INTENT_ID_NULL);
210 return store.getIntent(id);
211 }
212
213 @Override
214 public IntentState getIntentState(IntentId id) {
215 checkNotNull(id, INTENT_ID_NULL);
216 return store.getIntentState(id);
217 }
218
219 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700220 public List<Intent> getInstallableIntents(IntentId intentId) {
221 checkNotNull(intentId, INTENT_ID_NULL);
222 return store.getInstallableIntents(intentId);
223 }
224
225 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700226 public void addListener(IntentListener listener) {
227 listenerRegistry.addListener(listener);
228 }
229
230 @Override
231 public void removeListener(IntentListener listener) {
232 listenerRegistry.removeListener(listener);
233 }
234
235 @Override
236 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
237 compilers.put(cls, compiler);
238 }
239
240 @Override
241 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
242 compilers.remove(cls);
243 }
244
245 @Override
246 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
247 return ImmutableMap.copyOf(compilers);
248 }
249
250 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700251 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700252 installers.put(cls, installer);
253 }
254
255 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700256 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700257 installers.remove(cls);
258 }
259
260 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700261 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700262 return ImmutableMap.copyOf(installers);
263 }
264
265 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700266 * Returns the corresponding intent compiler to the specified intent.
267 *
268 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700269 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700270 * @return intent compiler corresponding to the specified intent
271 */
272 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
273 @SuppressWarnings("unchecked")
274 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
275 if (compiler == null) {
276 throw new IntentException("no compiler for class " + intent.getClass());
277 }
278 return compiler;
279 }
280
281 /**
282 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700283 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700284 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700285 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700286 * @return intent installer corresponding to the specified installable intent
287 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700288 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700289 @SuppressWarnings("unchecked")
290 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
291 if (installer == null) {
292 throw new IntentException("no installer for class " + intent.getClass());
293 }
294 return installer;
295 }
296
297 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700298 * Compiles an intent recursively.
299 *
300 * @param intent intent
301 * @return result of compilation
302 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800303 private List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700304 if (intent.isInstallable()) {
305 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700306 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700307
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700308 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700309 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700310 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800311 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
312 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700313 }
tom85258ee2014-10-07 00:10:02 -0700314 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700315 }
316
317 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700318 * Uninstalls all installable intents associated with the given intent.
319 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800320 * @param intent intent
321 * @param installables installable intents
322 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700323 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800324 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700325 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800326 for (Intent installable : installables) {
327 trackerService.removeTrackedResources(intent.id(),
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800328 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700329 try {
330 batches.addAll(getInstaller(installable).uninstall(installable));
331 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800332 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700333 // TODO: this should never happen. but what if it does?
334 }
335 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800336 return batches;
tom85258ee2014-10-07 00:10:02 -0700337 }
338
339 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700340 * Registers an intent compiler of the specified intent if an intent compiler
341 * for the intent is not registered. This method traverses the class hierarchy of
342 * the intent. Once an intent compiler for a parent type is found, this method
343 * registers the found intent compiler.
344 *
345 * @param intent intent
346 */
347 private void registerSubclassCompilerIfNeeded(Intent intent) {
348 if (!compilers.containsKey(intent.getClass())) {
349 Class<?> cls = intent.getClass();
350 while (cls != Object.class) {
351 // As long as we're within the Intent class descendants
352 if (Intent.class.isAssignableFrom(cls)) {
353 IntentCompiler<?> compiler = compilers.get(cls);
354 if (compiler != null) {
355 compilers.put(intent.getClass(), compiler);
356 return;
357 }
358 }
359 cls = cls.getSuperclass();
360 }
361 }
362 }
363
364 /**
365 * Registers an intent installer of the specified intent if an intent installer
366 * for the intent is not registered. This method traverses the class hierarchy of
367 * the intent. Once an intent installer for a parent type is found, this method
368 * registers the found intent installer.
369 *
370 * @param intent intent
371 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700372 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700373 if (!installers.containsKey(intent.getClass())) {
374 Class<?> cls = intent.getClass();
375 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700376 // As long as we're within the Intent class descendants
377 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700378 IntentInstaller<?> installer = installers.get(cls);
379 if (installer != null) {
380 installers.put(intent.getClass(), installer);
381 return;
382 }
383 }
384 cls = cls.getSuperclass();
385 }
386 }
387 }
388
Brian O'Connor66630c82014-10-02 21:08:19 -0700389 // Store delegate to re-post events emitted from the store.
390 private class InternalStoreDelegate implements IntentStoreDelegate {
391 @Override
392 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700393 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700394 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800395
396 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800397 public void process(IntentData data) {
398 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800399 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700400 }
401
Brian O'Connor72a034c2014-11-26 18:24:23 -0800402 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
403 boolean compileAllFailed) {
404 Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
405 // Attempt recompilation of the specified intents first.
406 for (IntentId id : intentIds) {
407 Intent intent = store.getIntent(id);
408 if (intent == null) {
409 continue;
410 }
411 IntentOperations.Builder builder = batches.get(intent.appId());
412 if (builder == null) {
413 builder = IntentOperations.builder(intent.appId());
414 batches.put(intent.appId(), builder);
415 }
416 builder.addUpdateOperation(id);
417 }
418
419 if (compileAllFailed) {
420 // If required, compile all currently failed intents.
421 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800422 IntentState state = getIntentState(intent.id());
423 if (RECOMPILE.contains(state)) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800424 IntentOperations.Builder builder = batches.get(intent.appId());
425 if (builder == null) {
426 builder = IntentOperations.builder(intent.appId());
427 batches.put(intent.appId(), builder);
428 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800429 if (state == WITHDRAW_REQ) {
430 builder.addWithdrawOperation(intent.id());
431 } else {
432 builder.addUpdateOperation(intent.id());
433 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800434 }
435 }
436 }
437
438 for (ApplicationId appId : batches.keySet()) {
439 if (batchService.isLocalLeader(appId)) {
440 execute(batches.get(appId).build());
441 }
442 }
443 }
444
tom95329eb2014-10-06 08:40:06 -0700445 // Topology change delegate
446 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
447 @Override
tom85258ee2014-10-07 00:10:02 -0700448 public void triggerCompile(Iterable<IntentId> intentIds,
449 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800450 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700451 }
tom95329eb2014-10-06 08:40:06 -0700452 }
tom85258ee2014-10-07 00:10:02 -0700453
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800454 // TODO: simplify the branching statements
455 private IntentUpdate createIntentUpdate(IntentOperation operation) {
456 switch (operation.type()) {
457 case SUBMIT:
458 return new InstallRequest(operation.intent());
459 case WITHDRAW: {
460 Intent oldIntent = store.getIntent(operation.intentId());
461 if (oldIntent == null) {
462 return new DoNothing();
463 }
464 List<Intent> installables = store.getInstallableIntents(oldIntent.id());
465 if (installables == null) {
466 return new WithdrawStateChange1(oldIntent);
467 }
468 return new WithdrawRequest(oldIntent, installables);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700469 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800470 case REPLACE: {
471 Intent newIntent = operation.intent();
472 Intent oldIntent = store.getIntent(operation.intentId());
473 if (oldIntent == null) {
474 return new InstallRequest(newIntent);
475 }
476 List<Intent> installables = store.getInstallableIntents(oldIntent.id());
477 if (installables == null) {
478 if (newIntent.equals(oldIntent)) {
479 return new InstallRequest(newIntent);
480 } else {
481 return new WithdrawStateChange2(oldIntent);
482 }
483 }
484 return new ReplaceRequest(newIntent, oldIntent, installables);
485 }
486 case UPDATE: {
487 Intent oldIntent = store.getIntent(operation.intentId());
488 if (oldIntent == null) {
489 return new DoNothing();
490 }
491 List<Intent> installables = getInstallableIntents(oldIntent.id());
492 if (installables == null) {
493 return new InstallRequest(oldIntent);
494 }
495 return new ReplaceRequest(oldIntent, oldIntent, installables);
496 }
497 default:
498 // illegal state
499 return new DoNothing();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700500 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700501 }
502
Brian O'Connorcff03322015-02-03 15:28:59 -0800503 // TODO pull out the IntentUpdate inner classes
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800504 private class InstallRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800505
506 private final Intent intent;
507
508 InstallRequest(Intent intent) {
509 this.intent = checkNotNull(intent);
510 }
511
512 @Override
513 public void writeBeforeExecution(BatchWrite batchWrite) {
514 // TODO consider only "creating" intent if it does not exist
515 // Note: We need to set state to INSTALL_REQ regardless.
516 batchWrite.createIntent(intent);
517 }
518
519 @Override
520 public Optional<IntentUpdate> execute() {
521 return Optional.of(new Compiling(intent));
522 }
523 }
524
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800525 private class WithdrawRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800526
527 private final Intent intent;
528 private final List<Intent> installables;
529
530 WithdrawRequest(Intent intent, List<Intent> installables) {
531 this.intent = checkNotNull(intent);
532 this.installables = ImmutableList.copyOf(checkNotNull(installables));
533 }
534
535 @Override
536 public void writeBeforeExecution(BatchWrite batchWrite) {
537 batchWrite.setState(intent, WITHDRAW_REQ);
538 }
539
540 @Override
541 public Optional<IntentUpdate> execute() {
542 return Optional.of(new Withdrawing(intent, installables));
543 }
544 }
545
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800546 private class ReplaceRequest implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800547
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700548 private final Intent newIntent;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800549 private final Intent oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700550 private final List<Intent> oldInstallables;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700551
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800552 ReplaceRequest(Intent newIntent, Intent oldIntent, List<Intent> oldInstallables) {
553 this.newIntent = checkNotNull(newIntent);
554 this.oldIntent = checkNotNull(oldIntent);
555 this.oldInstallables = ImmutableList.copyOf(oldInstallables);
556 }
557
558 @Override
559 public void writeBeforeExecution(BatchWrite batchWrite) {
560 // TODO consider only "creating" intent if it does not exist
561 // Note: We need to set state to INSTALL_REQ regardless.
562 batchWrite.createIntent(newIntent);
563 }
564
565 @Override
566 public Optional<IntentUpdate> execute() {
567 try {
568 List<Intent> installables = compileIntent(newIntent, oldInstallables);
569 return Optional.of(new Replacing(newIntent, oldIntent, installables, oldInstallables));
570 } catch (PathNotFoundException e) {
571 log.debug("Path not found for intent {}", newIntent);
572 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
573 } catch (IntentException e) {
574 log.warn("Unable to compile intent {} due to:", newIntent.id(), e);
575 return Optional.of(new Withdrawing(oldIntent, oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700576 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800577 }
578 }
579
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800580 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800581 private class WithdrawStateChange1 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800582
583 private final Intent intent;
584
585 WithdrawStateChange1(Intent intent) {
586 this.intent = checkNotNull(intent);
587 }
588
589 @Override
590 public void writeBeforeExecution(BatchWrite batchWrite) {
591 batchWrite.setState(intent, WITHDRAW_REQ);
592 }
593
594 @Override
595 public void writeAfterExecution(BatchWrite batchWrite) {
596 batchWrite.setState(intent, WITHDRAWN);
597 batchWrite.removeInstalledIntents(intent.id());
598 batchWrite.removeIntent(intent.id());
599 }
600 }
601
602 // TODO: better naming
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800603 private class WithdrawStateChange2 implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800604
605 private final Intent intent;
606
607 WithdrawStateChange2(Intent intent) {
608 this.intent = checkNotNull(intent);
609 }
610
611 @Override
612 public void writeBeforeExecution(BatchWrite batchWrite) {
613 // TODO consider only "creating" intent if it does not exist
614 // Note: We need to set state to INSTALL_REQ regardless.
615 batchWrite.createIntent(intent);
616 }
617
618 @Override
619 public void writeAfterExecution(BatchWrite batchWrite) {
620 batchWrite.setState(intent, WITHDRAWN);
621 batchWrite.removeInstalledIntents(intent.id());
622 batchWrite.removeIntent(intent.id());
623 }
624 }
625
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800626 private class Compiling implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800627
628 private final Intent intent;
629
630 Compiling(Intent intent) {
631 this.intent = checkNotNull(intent);
632 }
633
634 @Override
635 public Optional<IntentUpdate> execute() {
636 try {
637 // Compile the intent into installable derivatives.
638 // If all went well, associate the resulting list of installable
639 // intents with the top-level intent and proceed to install.
640 return Optional.of(new Installing(intent, compileIntent(intent, null)));
641 } catch (PathNotFoundException e) {
Sho SHIMIZUfd0cd8c2015-01-28 08:16:58 -0800642 log.debug("Path not found for intent {}", intent);
643 return Optional.of(new CompilingFailed(intent));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800644 } catch (IntentException e) {
Sho SHIMIZUfd0cd8c2015-01-28 08:16:58 -0800645 log.warn("Unable to compile intent {} due to:", intent.id(), e);
646 return Optional.of(new CompilingFailed(intent));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800647 }
648 }
649 }
650
651 // TODO: better naming because install() method actually generate FlowRuleBatchOperations
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800652 private class Installing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800653
654 private final Intent intent;
655 private final List<Intent> installables;
656
657 Installing(Intent intent, List<Intent> installables) {
658 this.intent = checkNotNull(intent);
659 this.installables = ImmutableList.copyOf(checkNotNull(installables));
660 }
661
662 @Override
663 public Optional<IntentUpdate> execute() {
664 Exception exception = null;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800665
666 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
667 for (Intent installable : installables) {
668 registerSubclassInstallerIfNeeded(installable);
669 trackerService.addTrackedResources(intent.id(), installable.resources());
670 try {
671 batches.addAll(getInstaller(installable).install(installable));
672 } catch (Exception e) { // TODO this should be IntentException
673 log.warn("Unable to install intent {} due to:", intent.id(), e);
674 trackerService.removeTrackedResources(intent.id(), installable.resources());
675 //TODO we failed; intent should be recompiled
676 exception = e;
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800677 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700678 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700679
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800680 if (exception != null) {
681 return Optional.of(new InstallingFailed(intent, installables, batches));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800682 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800683
684 return Optional.of(new Installed(intent, installables, batches));
685 }
686 }
687
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800688 private class Withdrawing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800689
690 private final Intent intent;
691 private final List<Intent> installables;
692
693 Withdrawing(Intent intent, List<Intent> installables) {
694 this.intent = checkNotNull(intent);
695 this.installables = ImmutableList.copyOf(installables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800696 }
697
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800698 @Override
699 public Optional<IntentUpdate> execute() {
700 List<FlowRuleBatchOperation> batches = uninstallIntent(intent, installables);
701
702 return Optional.of(new Withdrawn(intent, installables, batches));
703 }
704 }
705
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800706 private class Replacing implements IntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800707
708 private final Intent newIntent;
709 private final Intent oldIntent;
710 private final List<Intent> newInstallables;
711 private final List<Intent> oldInstallables;
712
713 private Exception exception;
714
715 Replacing(Intent newIntent, Intent oldIntent,
716 List<Intent> newInstallables, List<Intent> oldInstallables) {
717 this.newIntent = checkNotNull(newIntent);
718 this.oldIntent = checkNotNull(oldIntent);
719 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
720 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700721 }
722
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800723 @Override
724 public Optional<IntentUpdate> execute() {
725 List<FlowRuleBatchOperation> batches = replace();
726
727 if (exception == null) {
728 return Optional.of(
729 new Replaced(newIntent, oldIntent, newInstallables, oldInstallables, batches));
730 }
731
732 return Optional.of(
733 new ReplacingFailed(newIntent, oldIntent, newInstallables, oldInstallables, batches));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700734 }
735
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800736 protected List<FlowRuleBatchOperation> replace() {
737 checkState(oldInstallables.size() == newInstallables.size(),
738 "Old and New Intent must have equivalent installable intents.");
739
740 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
741 for (int i = 0; i < oldInstallables.size(); i++) {
742 Intent oldInstallable = oldInstallables.get(i);
743 Intent newInstallable = newInstallables.get(i);
744 //FIXME revisit this
745// if (oldInstallable.equals(newInstallable)) {
746// continue;
747// }
748 checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
749 "Installable Intent type mismatch.");
750 trackerService.removeTrackedResources(oldIntent.id(), oldInstallable.resources());
751 trackerService.addTrackedResources(newIntent.id(), newInstallable.resources());
752 try {
753 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
754 } catch (IntentException e) {
755 log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
756 //FIXME... we failed. need to uninstall (if same) or revert (if different)
757 trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
758 exception = e;
759 batches = uninstallIntent(oldIntent, oldInstallables);
760 }
761 }
762 return batches;
763 }
764 }
765
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800766 private class Installed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800767
768 private final Intent intent;
769 private final List<Intent> installables;
770 private IntentState intentState;
771 private final List<FlowRuleBatchOperation> batches;
772 private int currentBatch = 0;
773
774 Installed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
775 this.intent = checkNotNull(intent);
776 this.installables = ImmutableList.copyOf(checkNotNull(installables));
777 this.batches = new LinkedList<>(checkNotNull(batches));
778 this.intentState = INSTALLING;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700779 }
780
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800781 @Override
782 public void batchSuccess() {
Ray Milkey93508c22014-12-02 11:35:56 -0800783 currentBatch++;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800784 }
785
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800786 @Override
787 public List<Intent> allInstallables() {
788 return installables;
789 }
790
791 @Override
792 public void writeAfterExecution(BatchWrite batchWrite) {
793 switch (intentState) {
794 case INSTALLING:
795 batchWrite.setState(intent, INSTALLED);
796 batchWrite.setInstallableIntents(intent.id(), this.installables);
797 break;
798 case FAILED:
799 batchWrite.setState(intent, FAILED);
800 batchWrite.removeInstalledIntents(intent.id());
801 break;
802 default:
803 break;
804 }
805 }
806
807 @Override
808 public FlowRuleBatchOperation currentBatch() {
809 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
810 }
811
812 @Override
813 public void batchFailed() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800814 // the current batch has failed, so recompile
815 // remove the current batch and all remaining
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800816 for (int i = batches.size() - 1; i >= currentBatch; i--) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800817 batches.remove(i);
818 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800819 intentState = FAILED;
820 batches.addAll(uninstallIntent(intent, installables));
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800821
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800822 // TODO we might want to try to recompile the new intent
Brian O'Connor427a1762014-11-19 18:40:32 -0800823 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800824 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800825
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800826 private class Withdrawn implements CompletedIntentUpdate {
Brian O'Connor427a1762014-11-19 18:40:32 -0800827
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800828 private final Intent intent;
829 private final List<Intent> installables;
830 private final List<FlowRuleBatchOperation> batches;
831 private int currentBatch;
832
833 Withdrawn(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
834 this.intent = checkNotNull(intent);
835 this.installables = ImmutableList.copyOf(installables);
836 this.batches = new LinkedList<>(batches);
837 this.currentBatch = 0;
838 }
839
840 @Override
841 public List<Intent> allInstallables() {
842 return installables;
843 }
844
845 @Override
846 public void batchSuccess() {
847 currentBatch++;
848 }
849
850 @Override
851 public void writeAfterExecution(BatchWrite batchWrite) {
852 batchWrite.setState(intent, WITHDRAWN);
853 batchWrite.removeInstalledIntents(intent.id());
854 batchWrite.removeIntent(intent.id());
855 }
856
857 @Override
858 public FlowRuleBatchOperation currentBatch() {
859 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
860 }
861
862 @Override
863 public void batchFailed() {
864 // the current batch has failed, so recompile
865 // remove the current batch and all remaining
866 for (int i = batches.size() - 1; i >= currentBatch; i--) {
867 batches.remove(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800868 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800869 batches.addAll(uninstallIntent(intent, installables));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700870 }
871 }
872
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800873 private class Replaced implements CompletedIntentUpdate {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700874
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800875 private final Intent newIntent;
876 private final Intent oldIntent;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700877
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800878 private final List<Intent> newInstallables;
879 private final List<Intent> oldInstallables;
880 private final List<FlowRuleBatchOperation> batches;
881 private int currentBatch;
Brian O'Connor427a1762014-11-19 18:40:32 -0800882
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800883 Replaced(Intent newIntent, Intent oldIntent,
884 List<Intent> newInstallables, List<Intent> oldInstallables,
885 List<FlowRuleBatchOperation> batches) {
886 this.newIntent = checkNotNull(newIntent);
887 this.oldIntent = checkNotNull(oldIntent);
888 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
889 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
890 this.batches = new LinkedList<>(batches);
891 this.currentBatch = 0;
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800892 }
893
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800894 @Override
895 public List<Intent> allInstallables() {
896 LinkedList<Intent> allInstallables = new LinkedList<>();
897 allInstallables.addAll(newInstallables);
898 allInstallables.addAll(oldInstallables);
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800899
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800900 return allInstallables;
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800901 }
902
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800903 @Override
904 public void batchSuccess() {
905 currentBatch++;
Brian O'Connor427a1762014-11-19 18:40:32 -0800906 }
907
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800908 @Override
909 public void writeAfterExecution(BatchWrite batchWrite) {
910 batchWrite.setState(newIntent, INSTALLED);
911 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800912
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800913 batchWrite.setState(oldIntent, WITHDRAWN);
914 batchWrite.removeInstalledIntents(oldIntent.id());
915 batchWrite.removeIntent(oldIntent.id());
916 }
917
918 @Override
919 public FlowRuleBatchOperation currentBatch() {
920 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
921 }
922
923 @Override
924 public void batchFailed() {
925 // the current batch has failed, so recompile
926 // remove the current batch and all remaining
927 for (int i = batches.size() - 1; i >= currentBatch; i--) {
928 batches.remove(i);
929 }
930 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
931
932 batches.addAll(uninstallIntent(newIntent, newInstallables));
933
934 // TODO we might want to try to recompile the new intent
935 }
936 }
937
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800938 private class InstallingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800939
940 private final Intent intent;
941 private final List<Intent> installables;
942 private final List<FlowRuleBatchOperation> batches;
943 private int currentBatch = 0;
944
945 InstallingFailed(Intent intent, List<Intent> installables, List<FlowRuleBatchOperation> batches) {
946 this.intent = checkNotNull(intent);
947 this.installables = ImmutableList.copyOf(checkNotNull(installables));
948 this.batches = new LinkedList<>(checkNotNull(batches));
949 }
950
951 @Override
952 public List<Intent> allInstallables() {
953 return installables;
954 }
955
956 @Override
957 public void batchSuccess() {
958 currentBatch++;
959 }
960
961 @Override
962 public void writeAfterExecution(BatchWrite batchWrite) {
963 batchWrite.setState(intent, FAILED);
964 batchWrite.removeInstalledIntents(intent.id());
965 }
966
967 @Override
968 public FlowRuleBatchOperation currentBatch() {
969 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
970 }
971
972 @Override
973 public void batchFailed() {
974 // the current batch has failed, so recompile
975 // remove the current batch and all remaining
976 for (int i = batches.size() - 1; i >= currentBatch; i--) {
977 batches.remove(i);
978 }
979 batches.addAll(uninstallIntent(intent, installables));
980
981 // TODO we might want to try to recompile the new intent
982 }
983 }
984
Sho SHIMIZU28e72712015-01-27 17:03:41 -0800985 private class ReplacingFailed implements CompletedIntentUpdate {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800986
987 private final Intent newIntent;
988 private final Intent oldIntent;
989 private final List<Intent> newInstallables;
990 private final List<Intent> oldInstallables;
991 private final List<FlowRuleBatchOperation> batches;
992 private int currentBatch;
993
994 ReplacingFailed(Intent newIntent, Intent oldIntent,
995 List<Intent> newInstallables, List<Intent> oldInstallables,
996 List<FlowRuleBatchOperation> batches) {
997 this.newIntent = checkNotNull(newIntent);
998 this.oldIntent = checkNotNull(oldIntent);
999 this.newInstallables = ImmutableList.copyOf(checkNotNull(newInstallables));
1000 this.oldInstallables = ImmutableList.copyOf(checkNotNull(oldInstallables));
1001 this.batches = new LinkedList<>(batches);
1002 this.currentBatch = 0;
1003 }
1004
1005 @Override
1006 public List<Intent> allInstallables() {
1007 LinkedList<Intent> allInstallables = new LinkedList<>();
1008 allInstallables.addAll(newInstallables);
1009 allInstallables.addAll(oldInstallables);
1010
1011 return allInstallables;
1012 }
1013
1014 @Override
1015 public void batchSuccess() {
1016 currentBatch++;
1017 }
1018
1019 @Override
1020 public void writeAfterExecution(BatchWrite batchWrite) {
1021 batchWrite.setState(newIntent, FAILED);
1022 batchWrite.removeInstalledIntents(newIntent.id());
1023
1024 batchWrite.setState(oldIntent, WITHDRAWN);
1025 batchWrite.removeInstalledIntents(oldIntent.id());
1026 batchWrite.removeIntent(oldIntent.id());
1027 }
1028
1029 @Override
1030 public FlowRuleBatchOperation currentBatch() {
1031 return currentBatch < batches.size() ? batches.get(currentBatch) : null;
1032 }
1033
1034 @Override
1035 public void batchFailed() {
1036 // the current batch has failed, so recompile
1037 // remove the current batch and all remaining
1038 for (int i = batches.size() - 1; i >= currentBatch; i--) {
1039 batches.remove(i);
1040 }
1041 batches.addAll(uninstallIntent(oldIntent, oldInstallables));
1042
1043 batches.addAll(uninstallIntent(newIntent, newInstallables));
1044
1045 // TODO we might want to try to recompile the new intent
1046 }
1047 }
1048
1049 private class IntentBatchPreprocess implements Runnable {
1050
1051 // TODO make this configurable
1052 private static final int TIMEOUT_PER_OP = 500; // ms
1053 protected static final int MAX_ATTEMPTS = 3;
1054
1055 protected final IntentOperations ops;
1056
1057 // future holding current FlowRuleBatch installation result
1058 protected final long startTime = System.currentTimeMillis();
1059 protected final long endTime;
1060
1061 private IntentBatchPreprocess(IntentOperations ops, long endTime) {
1062 this.ops = checkNotNull(ops);
1063 this.endTime = endTime;
1064 }
1065
1066 public IntentBatchPreprocess(IntentOperations ops) {
1067 this(ops, System.currentTimeMillis() + ops.operations().size() * TIMEOUT_PER_OP);
1068 }
1069
1070 // FIXME compute reasonable timeouts
1071 protected long calculateTimeoutLimit() {
1072 return System.currentTimeMillis() + ops.operations().size() * TIMEOUT_PER_OP;
1073 }
1074
1075 @Override
1076 public void run() {
1077 try {
1078 // this should only be called on the first iteration
1079 // note: this a "expensive", so it is not done in the constructor
1080
1081 // - creates per Intent installation context (IntentUpdate)
1082 // - write Intents to store
1083 // - process (compile, install, etc.) each Intents
1084 // - generate FlowRuleBatch for this phase
1085 // build IntentUpdates
1086 List<IntentUpdate> updates = createIntentUpdates();
1087
1088 // Write batch information
1089 BatchWrite batchWrite = createBatchWrite(updates);
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001090 store.batchWrite(batchWrite);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001091
1092 new IntentBatchApplyFirst(ops, processIntentUpdates(updates), endTime, 0, null).run();
1093 } catch (Exception e) {
1094 log.error("Error submitting batches:", e);
1095 // FIXME incomplete Intents should be cleaned up
1096 // (transition to FAILED, etc.)
1097
1098 // TODO: remove duplicate due to inlining
1099 // the batch has failed
1100 // TODO: maybe we should do more?
1101 log.error("Walk the plank, matey...");
1102 batchService.removeIntentOperations(ops);
1103 }
1104 }
1105
1106 private List<IntentUpdate> createIntentUpdates() {
1107 return ops.operations().stream()
1108 .map(IntentManager.this::createIntentUpdate)
1109 .collect(Collectors.toList());
1110 }
1111
1112 private BatchWrite createBatchWrite(List<IntentUpdate> updates) {
1113 BatchWrite batchWrite = BatchWrite.newInstance();
1114 updates.forEach(update -> update.writeBeforeExecution(batchWrite));
1115 return batchWrite;
1116 }
1117
1118 private List<CompletedIntentUpdate> processIntentUpdates(List<IntentUpdate> updates) {
1119 // start processing each Intents
1120 List<CompletedIntentUpdate> completed = new ArrayList<>();
1121 for (IntentUpdate update : updates) {
1122 Optional<IntentUpdate> phase = Optional.of(update);
1123 IntentUpdate previous = update;
1124 while (true) {
1125 if (!phase.isPresent()) {
1126 // FIXME: not type safe cast
1127 completed.add((CompletedIntentUpdate) previous);
1128 break;
1129 }
1130 previous = phase.get();
1131 phase = previous.execute();
1132 }
1133 }
1134
1135 return completed;
1136 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001137 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001138
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001139 // TODO: better naming
1140 private class IntentBatchApplyFirst extends IntentBatchPreprocess {
1141
1142 protected final List<CompletedIntentUpdate> intentUpdates;
1143 protected final int installAttempt;
1144 protected Future<CompletedBatchOperation> future;
1145
1146 IntentBatchApplyFirst(IntentOperations operations, List<CompletedIntentUpdate> intentUpdates,
1147 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1148 super(operations, endTime);
1149 this.intentUpdates = ImmutableList.copyOf(intentUpdates);
1150 this.future = future;
1151 this.installAttempt = installAttempt;
1152 }
1153
1154 @Override
1155 public void run() {
1156 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1157 new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future).run();
Brian O'Connorcb900f42014-10-07 21:55:33 -07001158 }
1159
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001160 /**
Brian O'Connor427a1762014-11-19 18:40:32 -08001161 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001162 *
1163 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -07001164 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001165 protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001166 //TODO test this. (also, maybe save this batch)
Brian O'Connor72cb19a2015-01-16 16:14:41 -08001167
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001168 FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
Ray Milkey93508c22014-12-02 11:35:56 -08001169 if (batch.size() > 0) {
1170 //FIXME apply batch might throw an exception
1171 return flowRuleService.applyBatch(batch);
1172 } else {
1173 // there are no flow rule batches; finalize the intent update
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001174 BatchWrite batchWrite = createFinalizedBatchWrite(updates);
1175
Sho SHIMIZU9ea34532015-01-28 12:28:00 -08001176 store.batchWrite(batchWrite);
Ray Milkey93508c22014-12-02 11:35:56 -08001177 return null;
1178 }
1179 }
1180
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001181 private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -08001182 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001183 for (CompletedIntentUpdate update : intentUpdates) {
1184 FlowRuleBatchOperation currentBatch = update.currentBatch();
1185 if (currentBatch != null) {
1186 batch.addAll(currentBatch);
1187 }
1188 }
1189 return batch;
1190 }
1191
1192 private BatchWrite createFinalizedBatchWrite(List<CompletedIntentUpdate> intentUpdates) {
1193 BatchWrite batchWrite = BatchWrite.newInstance();
1194 for (CompletedIntentUpdate update : intentUpdates) {
1195 update.writeAfterExecution(batchWrite);
1196 }
1197 return batchWrite;
1198 }
1199
1200 protected void abandonShip() {
1201 // the batch has failed
1202 // TODO: maybe we should do more?
1203 log.error("Walk the plank, matey...");
1204 future = null;
1205 batchService.removeIntentOperations(ops);
1206 }
1207 }
1208
1209 // TODO: better naming
1210 private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
1211
1212 IntentBatchProcessFutures(IntentOperations operations, List<CompletedIntentUpdate> intentUpdates,
1213 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
1214 super(operations, intentUpdates, endTime, installAttempt, future);
1215 }
1216
1217 @Override
1218 public void run() {
1219 try {
1220 // - peek if current FlowRuleBatch is complete
1221 // -- If complete OK:
1222 // step each IntentUpdate forward
1223 // If phase left: generate next FlowRuleBatch
1224 // If no more phase: write parking states
1225 // -- If complete FAIL:
1226 // Intent which failed: transition Intent to FAILED
1227 // Other Intents: resubmit same FlowRuleBatch for this phase
1228 Future<CompletedBatchOperation> future = processFutures();
1229 if (future == null) {
1230 // there are no outstanding batches; we are done
1231 batchService.removeIntentOperations(ops);
1232 } else if (System.currentTimeMillis() > endTime) {
1233 // - cancel current FlowRuleBatch and resubmit again
1234 retry();
1235 } else {
1236 // we are not done yet, yield the thread by resubmitting ourselves
1237 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future));
1238 }
1239 } catch (Exception e) {
1240 log.error("Error submitting batches:", e);
1241 // FIXME incomplete Intents should be cleaned up
1242 // (transition to FAILED, etc.)
1243 abandonShip();
1244 }
1245 }
1246
1247 /**
1248 * Iterate through the pending futures, and remove them when they have completed.
1249 */
1250 private Future<CompletedBatchOperation> processFutures() {
1251 try {
1252 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
1253 updateBatches(completed);
1254 return applyNextBatch(intentUpdates);
1255 } catch (TimeoutException | InterruptedException te) {
1256 log.trace("Installation of intents are still pending: {}", ops);
1257 return future;
1258 } catch (ExecutionException e) {
1259 log.warn("Execution of batch failed: {}", ops, e);
1260 abandonShip();
1261 return future;
1262 }
1263 }
1264
Ray Milkey93508c22014-12-02 11:35:56 -08001265 private void updateBatches(CompletedBatchOperation completed) {
1266 if (completed.isSuccess()) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001267 for (CompletedIntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -08001268 update.batchSuccess();
1269 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001270 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -08001271 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -08001272 log.debug("Failed items: {}", completed.failedItems());
1273 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -08001274
1275 for (Long id : completed.failedIds()) {
1276 IntentId targetId = IntentId.valueOf(id);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001277 for (CompletedIntentUpdate update : intentUpdates) {
1278 for (Intent intent : update.allInstallables()) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001279 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001280 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001281 break;
1282 }
1283 }
1284 }
1285 // don't increment the non-failed items, as they have been reverted.
1286 }
1287 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001288 }
1289
Brian O'Connor427a1762014-11-19 18:40:32 -08001290 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -08001291 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001292 if (future.cancel(true)) { // cancel success; batch is reverted
1293 // reset the timer
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001294 long timeLimit = calculateTimeoutLimit();
1295 int attempts = installAttempt + 1;
1296 if (attempts == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -08001297 log.warn("Install request timed out: {}", ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001298 for (CompletedIntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001299 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -08001300 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001301 } else if (attempts > MAX_ATTEMPTS) {
Ray Milkey93508c22014-12-02 11:35:56 -08001302 abandonShip();
1303 return;
Brian O'Connor427a1762014-11-19 18:40:32 -08001304 } // else just resubmit the work
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001305 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
1306 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, timeLimit, attempts, future));
Brian O'Connor427a1762014-11-19 18:40:32 -08001307 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001308 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -08001309 // FIXME
1310 // cancel failed... batch is broken; shouldn't happen!
1311 // we could manually reverse everything
1312 // ... or just core dump and send email to Ali
Ray Milkey93508c22014-12-02 11:35:56 -08001313 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -08001314 }
1315 }
Brian O'Connorcb900f42014-10-07 21:55:33 -07001316 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001317
1318 private class InternalBatchDelegate implements IntentBatchDelegate {
1319 @Override
1320 public void execute(IntentOperations operations) {
Yuta HIGUCHIfe4367a2014-11-24 19:19:09 -08001321 log.info("Execute {} operation(s).", operations.operations().size());
1322 log.debug("Execute operations: {}", operations.operations());
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001323 //FIXME: perhaps we want to track this task so that we can cancel it.
Sho SHIMIZUadf8c482014-12-12 18:23:29 -08001324 executor.execute(new IntentBatchPreprocess(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001325 }
1326
1327 @Override
1328 public void cancel(IntentOperations operations) {
1329 //FIXME: implement this
1330 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
1331 }
1332 }
Brian O'Connor66630c82014-10-02 21:08:19 -07001333}