blob: c4008aa03a026b2b5b9eacb001329e255b8eaa4d [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'Connor66630c82014-10-02 21:08:19 -070016package org.onlab.onos.net.intent.impl;
17
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'Connor72a034c2014-11-26 18:24:23 -080028import org.onlab.onos.core.ApplicationId;
Brian O'Connor520c0522014-11-23 23:50:47 -080029import org.onlab.onos.core.CoreService;
30import org.onlab.onos.core.IdGenerator;
Brian O'Connor66630c82014-10-02 21:08:19 -070031import org.onlab.onos.event.AbstractListenerRegistry;
32import org.onlab.onos.event.EventDeliveryService;
Brian O'Connorcb900f42014-10-07 21:55:33 -070033import org.onlab.onos.net.flow.CompletedBatchOperation;
Brian O'Connorf2dbde52014-10-10 16:20:24 -070034import org.onlab.onos.net.flow.FlowRuleBatchOperation;
35import org.onlab.onos.net.flow.FlowRuleService;
Brian O'Connor66630c82014-10-02 21:08:19 -070036import org.onlab.onos.net.intent.Intent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070037import org.onlab.onos.net.intent.IntentBatchDelegate;
38import org.onlab.onos.net.intent.IntentBatchService;
Brian O'Connor66630c82014-10-02 21:08:19 -070039import org.onlab.onos.net.intent.IntentCompiler;
40import org.onlab.onos.net.intent.IntentEvent;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080041import org.onlab.onos.net.intent.IntentEvent.Type;
Brian O'Connor66630c82014-10-02 21:08:19 -070042import org.onlab.onos.net.intent.IntentException;
43import org.onlab.onos.net.intent.IntentExtensionService;
44import org.onlab.onos.net.intent.IntentId;
45import org.onlab.onos.net.intent.IntentInstaller;
46import org.onlab.onos.net.intent.IntentListener;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070047import org.onlab.onos.net.intent.IntentOperation;
Brian O'Connor66630c82014-10-02 21:08:19 -070048import org.onlab.onos.net.intent.IntentOperations;
49import org.onlab.onos.net.intent.IntentService;
50import org.onlab.onos.net.intent.IntentState;
51import org.onlab.onos.net.intent.IntentStore;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080052import org.onlab.onos.net.intent.IntentStore.BatchWrite;
Brian O'Connor66630c82014-10-02 21:08:19 -070053import org.onlab.onos.net.intent.IntentStoreDelegate;
54import org.slf4j.Logger;
55
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080056import java.util.ArrayList;
57import java.util.Collections;
58import java.util.EnumSet;
59import java.util.List;
60import java.util.Map;
61import java.util.concurrent.ConcurrentHashMap;
62import java.util.concurrent.ConcurrentMap;
63import java.util.concurrent.ExecutionException;
64import java.util.concurrent.ExecutorService;
65import java.util.concurrent.Future;
66import java.util.concurrent.TimeUnit;
67import java.util.concurrent.TimeoutException;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070068
69import static com.google.common.base.Preconditions.checkArgument;
70import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080071import static java.util.concurrent.Executors.newFixedThreadPool;
72import static org.onlab.onos.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070073import static org.onlab.util.Tools.namedThreads;
74import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070075
76/**
77 * An implementation of Intent Manager.
78 */
79@Component(immediate = true)
80@Service
81public class IntentManager
82 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080083 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070084
85 public static final String INTENT_NULL = "Intent cannot be null";
86 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
87
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080088 private static final int NUM_THREADS = 12;
89
Brian O'Connor66630c82014-10-02 21:08:19 -070090 // Collections for compiler, installer, and listener are ONOS instance local
91 private final ConcurrentMap<Class<? extends Intent>,
92 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070093 private final ConcurrentMap<Class<? extends Intent>,
94 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070095
96 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070097 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070098
Brian O'Connor520c0522014-11-23 23:50:47 -080099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected IntentStore store;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700106 protected IntentBatchService batchService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700109 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700112 protected EventDeliveryService eventDispatcher;
113
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected FlowRuleService flowRuleService;
116
Brian O'Connor520c0522014-11-23 23:50:47 -0800117
118 private ExecutorService executor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800119
120 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
121 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
122 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
123 private IdGenerator idGenerator;
124
Brian O'Connor66630c82014-10-02 21:08:19 -0700125 @Activate
126 public void activate() {
127 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700128 trackerService.setDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700129 batchService.setDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700130 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
alshabiba9819bf2014-11-30 18:15:52 -0800131 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800132 idGenerator = coreService.getIdGenerator("intent-ids");
133 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700134 log.info("Started");
135 }
136
137 @Deactivate
138 public void deactivate() {
139 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700140 trackerService.unsetDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700141 batchService.unsetDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700142 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700143 executor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800144 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700145 log.info("Stopped");
146 }
147
148 @Override
149 public void submit(Intent intent) {
150 checkNotNull(intent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800151 execute(IntentOperations.builder(intent.appId())
152 .addSubmitOperation(intent).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700153 }
154
155 @Override
156 public void withdraw(Intent intent) {
157 checkNotNull(intent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800158 execute(IntentOperations.builder(intent.appId())
159 .addWithdrawOperation(intent.id()).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700160 }
161
Brian O'Connor66630c82014-10-02 21:08:19 -0700162 @Override
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700163 public void replace(IntentId oldIntentId, Intent newIntent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700164 checkNotNull(oldIntentId, INTENT_ID_NULL);
165 checkNotNull(newIntent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800166 execute(IntentOperations.builder(newIntent.appId())
Ray Milkeye97ede92014-11-20 10:43:12 -0800167 .addReplaceOperation(oldIntentId, newIntent)
168 .build());
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700169 }
170
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700171 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700172 public void execute(IntentOperations operations) {
Brian O'Connore2ff25a2014-11-18 19:25:43 -0800173 if (operations.operations().isEmpty()) {
174 return;
175 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700176 batchService.addIntentOperations(operations);
Brian O'Connor66630c82014-10-02 21:08:19 -0700177 }
178
179 @Override
180 public Iterable<Intent> getIntents() {
181 return store.getIntents();
182 }
183
184 @Override
185 public long getIntentCount() {
186 return store.getIntentCount();
187 }
188
189 @Override
190 public Intent getIntent(IntentId id) {
191 checkNotNull(id, INTENT_ID_NULL);
192 return store.getIntent(id);
193 }
194
195 @Override
196 public IntentState getIntentState(IntentId id) {
197 checkNotNull(id, INTENT_ID_NULL);
198 return store.getIntentState(id);
199 }
200
201 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700202 public List<Intent> getInstallableIntents(IntentId intentId) {
203 checkNotNull(intentId, INTENT_ID_NULL);
204 return store.getInstallableIntents(intentId);
205 }
206
207 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700208 public void addListener(IntentListener listener) {
209 listenerRegistry.addListener(listener);
210 }
211
212 @Override
213 public void removeListener(IntentListener listener) {
214 listenerRegistry.removeListener(listener);
215 }
216
217 @Override
218 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
219 compilers.put(cls, compiler);
220 }
221
222 @Override
223 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
224 compilers.remove(cls);
225 }
226
227 @Override
228 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
229 return ImmutableMap.copyOf(compilers);
230 }
231
232 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700233 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700234 installers.put(cls, installer);
235 }
236
237 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700238 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700239 installers.remove(cls);
240 }
241
242 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700243 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700244 return ImmutableMap.copyOf(installers);
245 }
246
247 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700248 * Returns the corresponding intent compiler to the specified intent.
249 *
250 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700251 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700252 * @return intent compiler corresponding to the specified intent
253 */
254 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
255 @SuppressWarnings("unchecked")
256 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
257 if (compiler == null) {
258 throw new IntentException("no compiler for class " + intent.getClass());
259 }
260 return compiler;
261 }
262
263 /**
264 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700265 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700266 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700267 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700268 * @return intent installer corresponding to the specified installable intent
269 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700270 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700271 @SuppressWarnings("unchecked")
272 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
273 if (installer == null) {
274 throw new IntentException("no installer for class " + intent.getClass());
275 }
276 return installer;
277 }
278
279 /**
tom85258ee2014-10-07 00:10:02 -0700280 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700281 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700282 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700283 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700284 private void executeCompilingPhase(IntentUpdate update) {
285 Intent intent = update.newIntent();
tom85258ee2014-10-07 00:10:02 -0700286 // Indicate that the intent is entering the compiling phase.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800287 update.setInflightState(intent, COMPILING);
tom85258ee2014-10-07 00:10:02 -0700288
289 try {
290 // Compile the intent into installable derivatives.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700291 List<Intent> installables = compileIntent(intent, update);
tom85258ee2014-10-07 00:10:02 -0700292
293 // If all went well, associate the resulting list of installable
294 // intents with the top-level intent and proceed to install.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700295 update.setInstallables(installables);
Brian O'Connora44dda52014-12-01 17:42:41 -0800296 } catch (PathNotFoundException e) {
297 log.debug("Path not found for intent {}", intent.id());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700298 } catch (IntentException e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700299 log.warn("Unable to compile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700300
tom85258ee2014-10-07 00:10:02 -0700301 // If compilation failed, mark the intent as failed.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800302 update.setInflightState(intent, FAILED);
tom85258ee2014-10-07 00:10:02 -0700303 }
304 }
305
Brian O'Connorcb900f42014-10-07 21:55:33 -0700306 /**
307 * Compiles an intent recursively.
308 *
309 * @param intent intent
310 * @return result of compilation
311 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700312 private List<Intent> compileIntent(Intent intent, IntentUpdate update) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700313 if (intent.isInstallable()) {
314 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700315 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700316
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700317 registerSubclassCompilerIfNeeded(intent);
318 List<Intent> previous = update.oldInstallables();
319 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700320 List<Intent> installable = new ArrayList<>();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700321 for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) {
322 installable.addAll(compileIntent(compiled, update));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700323 }
tom85258ee2014-10-07 00:10:02 -0700324 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700325 }
326
327 /**
tom85258ee2014-10-07 00:10:02 -0700328 * Installs all installable intents associated with the specified top-level
329 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700330 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700331 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700332 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700333 private void executeInstallingPhase(IntentUpdate update) {
334 if (update.newInstallables() == null) {
335 //no failed intents allowed past this point...
336 return;
tom85258ee2014-10-07 00:10:02 -0700337 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700338 // Indicate that the intent is entering the installing phase.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800339 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700340
341 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
342 for (Intent installable : update.newInstallables()) {
343 registerSubclassInstallerIfNeeded(installable);
344 trackerService.addTrackedResources(update.newIntent().id(),
345 installable.resources());
346 try {
347 batches.addAll(getInstaller(installable).install(installable));
348 } catch (IntentException e) {
349 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
Brian O'Connor427a1762014-11-19 18:40:32 -0800350 trackerService.removeTrackedResources(update.newIntent().id(),
351 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700352 //FIXME we failed... intent should be recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700353 }
354 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800355 update.addBatches(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700356 }
357
358 /**
359 * Uninstalls the specified intent by uninstalling all of its associated
360 * installable derivatives.
361 *
362 * @param update intent update
363 */
364 private void executeWithdrawingPhase(IntentUpdate update) {
365 if (!update.oldIntent().equals(update.newIntent())) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800366 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700367 } // else newIntent is FAILED
Brian O'Connor427a1762014-11-19 18:40:32 -0800368 update.addBatches(uninstallIntent(update.oldIntent(), update.oldInstallables()));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700369 }
370
371 /**
372 * Uninstalls all installable intents associated with the given intent.
373 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800374 * @param intent intent
375 * @param installables installable intents
376 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700377 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800378 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
379 if (installables == null) {
380 return Collections.emptyList();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700381 }
382 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800383 for (Intent installable : installables) {
384 trackerService.removeTrackedResources(intent.id(),
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700385 installable.resources());
386 try {
387 batches.addAll(getInstaller(installable).uninstall(installable));
388 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800389 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700390 // TODO: this should never happen. but what if it does?
391 }
392 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800393 return batches;
tom85258ee2014-10-07 00:10:02 -0700394 }
395
396 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700397 * Withdraws the old intent and installs the new intent as one operation.
tom85258ee2014-10-07 00:10:02 -0700398 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700399 * @param update intent update
tom85258ee2014-10-07 00:10:02 -0700400 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700401 private void executeReplacementPhase(IntentUpdate update) {
402 checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
403 "Old and New Intent must have equivalent installable intents.");
404 if (!update.oldIntent().equals(update.newIntent())) {
405 // only set the old intent's state if it is different
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800406 update.setInflightState(update.oldIntent(), WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700407 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800408 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700409
410 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
411 for (int i = 0; i < update.oldInstallables().size(); i++) {
412 Intent oldInstallable = update.oldInstallables().get(i);
413 Intent newInstallable = update.newInstallables().get(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800414 //FIXME revisit this
415// if (oldInstallable.equals(newInstallable)) {
416// continue;
417// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700418 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
419 "Installable Intent type mismatch.");
420 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
421 trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
422 try {
423 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
424 } catch (IntentException e) {
425 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
426 //FIXME... we failed. need to uninstall (if same) or revert (if different)
Brian O'Connor427a1762014-11-19 18:40:32 -0800427 trackerService.removeTrackedResources(update.newIntent().id(), newInstallable.resources());
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800428 update.setInflightState(update.newIntent(), FAILED);
Brian O'Connor427a1762014-11-19 18:40:32 -0800429 batches = uninstallIntent(update.oldIntent(), update.oldInstallables());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700430 }
431 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800432 update.addBatches(batches);
tom53945d52014-10-07 11:01:36 -0700433 }
434
435 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700436 * Registers an intent compiler of the specified intent if an intent compiler
437 * for the intent is not registered. This method traverses the class hierarchy of
438 * the intent. Once an intent compiler for a parent type is found, this method
439 * registers the found intent compiler.
440 *
441 * @param intent intent
442 */
443 private void registerSubclassCompilerIfNeeded(Intent intent) {
444 if (!compilers.containsKey(intent.getClass())) {
445 Class<?> cls = intent.getClass();
446 while (cls != Object.class) {
447 // As long as we're within the Intent class descendants
448 if (Intent.class.isAssignableFrom(cls)) {
449 IntentCompiler<?> compiler = compilers.get(cls);
450 if (compiler != null) {
451 compilers.put(intent.getClass(), compiler);
452 return;
453 }
454 }
455 cls = cls.getSuperclass();
456 }
457 }
458 }
459
460 /**
461 * Registers an intent installer of the specified intent if an intent installer
462 * for the intent is not registered. This method traverses the class hierarchy of
463 * the intent. Once an intent installer for a parent type is found, this method
464 * registers the found intent installer.
465 *
466 * @param intent intent
467 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700468 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700469 if (!installers.containsKey(intent.getClass())) {
470 Class<?> cls = intent.getClass();
471 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700472 // As long as we're within the Intent class descendants
473 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700474 IntentInstaller<?> installer = installers.get(cls);
475 if (installer != null) {
476 installers.put(intent.getClass(), installer);
477 return;
478 }
479 }
480 cls = cls.getSuperclass();
481 }
482 }
483 }
484
Brian O'Connor66630c82014-10-02 21:08:19 -0700485 // Store delegate to re-post events emitted from the store.
486 private class InternalStoreDelegate implements IntentStoreDelegate {
487 @Override
488 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700489 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700490 }
491 }
492
Brian O'Connor72a034c2014-11-26 18:24:23 -0800493 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
494 boolean compileAllFailed) {
495 Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
496 // Attempt recompilation of the specified intents first.
497 for (IntentId id : intentIds) {
498 Intent intent = store.getIntent(id);
499 if (intent == null) {
500 continue;
501 }
502 IntentOperations.Builder builder = batches.get(intent.appId());
503 if (builder == null) {
504 builder = IntentOperations.builder(intent.appId());
505 batches.put(intent.appId(), builder);
506 }
507 builder.addUpdateOperation(id);
508 }
509
510 if (compileAllFailed) {
511 // If required, compile all currently failed intents.
512 for (Intent intent : getIntents()) {
513 if (getIntentState(intent.id()) == FAILED) {
514 IntentOperations.Builder builder = batches.get(intent.appId());
515 if (builder == null) {
516 builder = IntentOperations.builder(intent.appId());
517 batches.put(intent.appId(), builder);
518 }
519 builder.addUpdateOperation(intent.id());
520 }
521 }
522 }
523
524 for (ApplicationId appId : batches.keySet()) {
525 if (batchService.isLocalLeader(appId)) {
526 execute(batches.get(appId).build());
527 }
528 }
529 }
530
tom95329eb2014-10-06 08:40:06 -0700531 // Topology change delegate
532 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
533 @Override
tom85258ee2014-10-07 00:10:02 -0700534 public void triggerCompile(Iterable<IntentId> intentIds,
535 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800536 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700537 }
tom95329eb2014-10-06 08:40:06 -0700538 }
tom85258ee2014-10-07 00:10:02 -0700539
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800540 // TODO move this inside IntentUpdate?
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700541 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800542 * TODO. rename this...
543 * @param update intent update
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700544 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800545 private void processIntentUpdate(IntentUpdate update) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700546
Brian O'Connor427a1762014-11-19 18:40:32 -0800547 // check to see if the intent needs to be compiled or recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700548 if (update.newIntent() != null) {
549 executeCompilingPhase(update);
550 }
551
552 if (update.oldInstallables() != null && update.newInstallables() != null) {
553 executeReplacementPhase(update);
554 } else if (update.newInstallables() != null) {
555 executeInstallingPhase(update);
556 } else if (update.oldInstallables() != null) {
557 executeWithdrawingPhase(update);
558 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800559 if (update.oldIntent() != null &&
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800560 !update.oldIntent().equals(update.newIntent())) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800561 // removing failed intent
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800562 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700563 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800564// if (update.newIntent() != null) {
565// // TODO assert that next state is failed
566// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700567 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700568 }
569
570 // TODO comments...
571 private class IntentUpdate {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700572 private final Intent oldIntent;
573 private final Intent newIntent;
574 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
575
576 private final List<Intent> oldInstallables;
577 private List<Intent> newInstallables;
Brian O'Connor427a1762014-11-19 18:40:32 -0800578 private final List<FlowRuleBatchOperation> batches = Lists.newLinkedList();
579 private int currentBatch = 0; // TODO: maybe replace with an iterator
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700580
581 IntentUpdate(IntentOperation op) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700582 switch (op.type()) {
583 case SUBMIT:
584 newIntent = op.intent();
585 oldIntent = null;
586 break;
587 case WITHDRAW:
588 newIntent = null;
589 oldIntent = store.getIntent(op.intentId());
590 break;
591 case REPLACE:
592 newIntent = op.intent();
593 oldIntent = store.getIntent(op.intentId());
594 break;
595 case UPDATE:
596 oldIntent = store.getIntent(op.intentId());
Brian O'Connor427a1762014-11-19 18:40:32 -0800597 newIntent = oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700598 break;
599 default:
600 oldIntent = null;
601 newIntent = null;
602 break;
603 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700604 // fetch the old intent's installables from the store
605 if (oldIntent != null) {
606 oldInstallables = store.getInstallableIntents(oldIntent.id());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700607 } else {
608 oldInstallables = null;
609 }
610 }
611
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800612 void init(BatchWrite batchWrite) {
613 // add new intent to store (if required)
614 if (newIntent != null) {
615 batchWrite.createIntent(newIntent);
616 }
617 }
618
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700619 Intent oldIntent() {
620 return oldIntent;
621 }
622
623 Intent newIntent() {
624 return newIntent;
625 }
626
627 List<Intent> oldInstallables() {
628 return oldInstallables;
629 }
630
631 List<Intent> newInstallables() {
632 return newInstallables;
633 }
634
635 void setInstallables(List<Intent> installables) {
636 newInstallables = installables;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800637 //FIXME batch this
638
639 //store.setInstallableIntents(newIntent.id(), installables);
640
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700641 }
642
Brian O'Connor427a1762014-11-19 18:40:32 -0800643 boolean isComplete() {
644 return currentBatch >= batches.size();
645 }
646
647 FlowRuleBatchOperation currentBatch() {
648 return !isComplete() ? batches.get(currentBatch) : null;
649 }
650
alshabiba9819bf2014-11-30 18:15:52 -0800651 void batchSuccess(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800652 // move on to next Batch
653 if (++currentBatch == batches.size()) {
alshabiba9819bf2014-11-30 18:15:52 -0800654 finalizeStates(batchWrite);
Brian O'Connor427a1762014-11-19 18:40:32 -0800655 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800656 }
657
658 void batchFailed() {
659
660 // the current batch has failed, so recompile
661 // remove the current batch and all remaining
662 for (int i = currentBatch; i < batches.size(); i++) {
663 batches.remove(i);
664 }
665 if (oldIntent != null) {
666 executeWithdrawingPhase(this); // remove the old intent
667 }
668 if (newIntent != null) {
669 setInflightState(newIntent, FAILED);
670 batches.addAll(uninstallIntent(newIntent, newInstallables()));
671 }
672
673 // FIXME: should we try to recompile?
Brian O'Connor427a1762014-11-19 18:40:32 -0800674 }
675
676 // FIXME make sure this is called!!!
alshabiba9819bf2014-11-30 18:15:52 -0800677 private void finalizeStates(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800678 // events to be triggered on successful write
Brian O'Connor427a1762014-11-19 18:40:32 -0800679 for (Intent intent : stateMap.keySet()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800680 switch (getInflightState(intent)) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800681 case INSTALLING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800682 batchWrite.setState(intent, INSTALLED);
683 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Brian O'Connor427a1762014-11-19 18:40:32 -0800684 break;
685 case WITHDRAWING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800686 batchWrite.setState(intent, WITHDRAWN);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800687 batchWrite.removeInstalledIntents(intent.id());
688 batchWrite.removeIntent(intent.id());
Brian O'Connor427a1762014-11-19 18:40:32 -0800689 break;
690 case FAILED:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800691 batchWrite.removeInstalledIntents(intent.id());
Brian O'Connor427a1762014-11-19 18:40:32 -0800692 break;
693
694 // FALLTHROUGH to default from here
695 case SUBMITTED:
696 case COMPILING:
697 case RECOMPILING:
698 case WITHDRAWN:
699 case INSTALLED:
700 default:
701 //FIXME clean this up (we shouldn't ever get here)
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800702 log.warn("Bad state: {} for {}", getInflightState(intent), intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800703 break;
704 }
705 }
706 }
707
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700708 List<FlowRuleBatchOperation> batches() {
709 return batches;
710 }
711
Brian O'Connor427a1762014-11-19 18:40:32 -0800712 void addBatches(List<FlowRuleBatchOperation> batches) {
713 this.batches.addAll(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700714 }
715
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800716 IntentState getInflightState(Intent intent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700717 return stateMap.get(intent);
718 }
719
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800720
721 // set transient state during intent update process
722 void setInflightState(Intent intent, IntentState newState) {
723 // This method should be called for
724 // transition to non-parking or Failed only
725 EnumSet<IntentState> nonParkingOrFailed
726 = EnumSet.complementOf(EnumSet.of(SUBMITTED, INSTALLED, WITHDRAWN));
727 if (!nonParkingOrFailed.contains(newState)) {
728 log.error("Unexpected transition to {}", newState);
729 }
730
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700731 // TODO: clean this up, or set to debug
732 IntentState oldState = stateMap.get(intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800733 log.debug("intent id: {}, old state: {}, new state: {}",
Ray Milkeye97ede92014-11-20 10:43:12 -0800734 intent.id(), oldState, newState);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700735
736 stateMap.put(intent, newState);
alshabiba9819bf2014-11-30 18:15:52 -0800737// IntentEvent event = store.setState(intent, newState);
738// if (event != null) {
739// eventDispatcher.post(event);
740// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700741 }
742
743 Map<Intent, IntentState> stateMap() {
744 return stateMap;
745 }
746 }
747
Brian O'Connorcb900f42014-10-07 21:55:33 -0700748 private class IntentInstallMonitor implements Runnable {
749
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800750 // TODO make this configurable
751 private static final int TIMEOUT_PER_OP = 500; // ms
Brian O'Connor427a1762014-11-19 18:40:32 -0800752 private static final int MAX_ATTEMPTS = 3;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700753
Brian O'Connor427a1762014-11-19 18:40:32 -0800754 private final IntentOperations ops;
755 private final List<IntentUpdate> intentUpdates = Lists.newArrayList();
756
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800757 // future holding current FlowRuleBatch installation result
Brian O'Connor427a1762014-11-19 18:40:32 -0800758 private Future<CompletedBatchOperation> future;
759 private long startTime = System.currentTimeMillis();
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800760 private long endTime;
Brian O'Connor427a1762014-11-19 18:40:32 -0800761 private int installAttempt;
762
763 public IntentInstallMonitor(IntentOperations ops) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700764 this.ops = ops;
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800765 resetTimeoutLimit();
766 }
767
768 private void resetTimeoutLimit() {
769 // FIXME compute reasonable timeouts
770 this.endTime = System.currentTimeMillis()
771 + ops.operations().size() * TIMEOUT_PER_OP;
Brian O'Connor427a1762014-11-19 18:40:32 -0800772 }
773
774 private void buildIntentUpdates() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800775 BatchWrite batchWrite = store.newBatchWrite();
776
777 // create context and record new request to store
Brian O'Connor427a1762014-11-19 18:40:32 -0800778 for (IntentOperation op : ops.operations()) {
779 IntentUpdate update = new IntentUpdate(op);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800780 update.init(batchWrite);
Brian O'Connor427a1762014-11-19 18:40:32 -0800781 intentUpdates.add(update);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800782 }
783
784 if (!batchWrite.isEmpty()) {
785 store.batchWrite(batchWrite);
786 }
787
788 // start processing each Intents
789 for (IntentUpdate update : intentUpdates) {
790 if (update.newIntent() != null) {
791 IntentState state = store.getIntentState(update.newIntent().id());
792 if (state == SUBMITTED) {
793 eventDispatcher.post(new IntentEvent(Type.SUBMITTED, update.newIntent));
794 }
795 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800796 processIntentUpdate(update);
797 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700798 future = applyNextBatch();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700799 }
800
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700801 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800802 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700803 *
804 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700805 */
806 private Future<CompletedBatchOperation> applyNextBatch() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800807 //TODO test this. (also, maybe save this batch)
808 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList());
809 for (IntentUpdate update : intentUpdates) {
810 if (!update.isComplete()) {
811 batch.addAll(update.currentBatch());
812 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700813 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800814 return (batch.size() > 0) ? flowRuleService.applyBatch(batch) : null;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700815 }
816
Brian O'Connor427a1762014-11-19 18:40:32 -0800817 private void updateBatches(CompletedBatchOperation completed) {
818 if (completed.isSuccess()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800819 BatchWrite batchWrite = store.newBatchWrite();
820 List<IntentEvent> events = new ArrayList<>();
Brian O'Connor427a1762014-11-19 18:40:32 -0800821 for (IntentUpdate update : intentUpdates) {
alshabiba9819bf2014-11-30 18:15:52 -0800822 update.batchSuccess(batchWrite);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800823 }
824 if (!batchWrite.isEmpty()) {
825 store.batchWrite(batchWrite);
826 for (IntentEvent event : events) {
827 eventDispatcher.post(event);
828 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700829 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700830 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800831 // entire batch has been reverted...
832 log.warn("Failed items: {}", completed.failedItems());
833
834 for (Long id : completed.failedIds()) {
835 IntentId targetId = IntentId.valueOf(id);
836 for (IntentUpdate update : intentUpdates) {
837 List<Intent> installables = Lists.newArrayList(update.newInstallables());
838 installables.addAll(update.oldInstallables());
839 for (Intent intent : installables) {
840 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800841 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800842 break;
843 }
844 }
845 }
846 // don't increment the non-failed items, as they have been reverted.
847 }
848 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700849 }
850
851 /**
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700852 * Iterate through the pending futures, and remove them when they have completed.
853 */
854 private void processFutures() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700855 if (future == null) {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800856 log.warn("I have no Future.");
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700857 return; //FIXME look at this
Brian O'Connorcb900f42014-10-07 21:55:33 -0700858 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700859 try {
860 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
Brian O'Connor427a1762014-11-19 18:40:32 -0800861 updateBatches(completed);
862 future = applyNextBatch();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700863 } catch (TimeoutException | InterruptedException | ExecutionException te) {
864 //TODO look into error message
Brian O'Connor427a1762014-11-19 18:40:32 -0800865 log.debug("Installation of intents are still pending: {}", ops);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700866 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700867 }
868
Brian O'Connor427a1762014-11-19 18:40:32 -0800869 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800870 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800871 if (future.cancel(true)) { // cancel success; batch is reverted
872 // reset the timer
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800873 resetTimeoutLimit();
Brian O'Connor427a1762014-11-19 18:40:32 -0800874 if (installAttempt++ >= MAX_ATTEMPTS) {
875 log.warn("Install request timed out: {}", ops);
876 for (IntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800877 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800878 }
879 } // else just resubmit the work
880 future = applyNextBatch();
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800881 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -0800882 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800883 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800884 // FIXME
885 // cancel failed... batch is broken; shouldn't happen!
886 // we could manually reverse everything
887 // ... or just core dump and send email to Ali
888 batchService.removeIntentOperations(ops);
889 }
890 }
891
892 boolean isComplete() {
893 // TODO: actually check with the intent update?
894 return future == null;
895 }
896
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700897 @Override
898 public void run() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800899 try {
900 if (intentUpdates.isEmpty()) {
901 // this should only be called on the first iteration
902 // note: this a "expensive", so it is not done in the constructor
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800903
904 // - creates per Intent installation context (IntentUpdate)
905 // - write Intents to store
906 // - process (compile, install, etc.) each Intents
907 // - generate FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800908 buildIntentUpdates();
909 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800910
911 // - peek if current FlowRuleBatch is complete
912 // -- If complete OK:
913 // step each IntentUpdate forward
914 // If phase left: generate next FlowRuleBatch
915 // If no more phase: write parking states
916 // -- If complete FAIL:
917 // Intent which failed: transition Intent to FAILED
918 // Other Intents: resubmit same FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800919 processFutures();
920 if (isComplete()) {
921 // there are no outstanding batches; we are done
922 batchService.removeIntentOperations(ops);
923 } else if (endTime < System.currentTimeMillis()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800924 // - cancel current FlowRuleBatch and resubmit again
Brian O'Connor427a1762014-11-19 18:40:32 -0800925 retry();
926 } else {
927 // we are not done yet, yield the thread by resubmitting ourselves
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800928 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -0800929 }
930 } catch (Exception e) {
931 log.error("Error submitting batches:", e);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800932 // FIXME incomplete Intents should be cleaned up
933 // (transition to FAILED, etc.)
Brian O'Connorcb900f42014-10-07 21:55:33 -0700934 }
935 }
936 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700937
938 private class InternalBatchDelegate implements IntentBatchDelegate {
939 @Override
940 public void execute(IntentOperations operations) {
Yuta HIGUCHIfe4367a2014-11-24 19:19:09 -0800941 log.info("Execute {} operation(s).", operations.operations().size());
942 log.debug("Execute operations: {}", operations.operations());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700943 //FIXME: perhaps we want to track this task so that we can cancel it.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800944 executor.execute(new IntentInstallMonitor(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700945 }
946
947 @Override
948 public void cancel(IntentOperations operations) {
949 //FIXME: implement this
950 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
951 }
952 }
Ray Milkeye97ede92014-11-20 10:43:12 -0800953
Brian O'Connor66630c82014-10-02 21:08:19 -0700954}