blob: cb807d53e805475918f6ba2e661f06b1865b6e50 [file] [log] [blame]
Brian O'Connor66630c82014-10-02 21:08:19 -07001package org.onlab.onos.net.intent.impl;
2
alshabib8ca53902014-10-07 13:11:17 -07003import static com.google.common.base.Preconditions.checkNotNull;
4import static java.util.concurrent.Executors.newSingleThreadExecutor;
5import static org.onlab.onos.net.intent.IntentState.COMPILING;
6import static org.onlab.onos.net.intent.IntentState.FAILED;
7import static org.onlab.onos.net.intent.IntentState.INSTALLED;
8import static org.onlab.onos.net.intent.IntentState.INSTALLING;
9import static org.onlab.onos.net.intent.IntentState.RECOMPILING;
10import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
11import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
12import static org.onlab.util.Tools.namedThreads;
13import static org.slf4j.LoggerFactory.getLogger;
14
15import java.util.ArrayList;
Brian O'Connorcb900f42014-10-07 21:55:33 -070016import java.util.Iterator;
alshabib8ca53902014-10-07 13:11:17 -070017import java.util.List;
18import java.util.Map;
19import java.util.Objects;
20import java.util.concurrent.ConcurrentHashMap;
21import java.util.concurrent.ConcurrentMap;
alshabib26834582014-10-08 20:15:46 -070022import java.util.concurrent.ExecutionException;
alshabib8ca53902014-10-07 13:11:17 -070023import java.util.concurrent.ExecutorService;
Brian O'Connorcb900f42014-10-07 21:55:33 -070024import java.util.concurrent.Future;
alshabib26834582014-10-08 20:15:46 -070025import java.util.concurrent.TimeUnit;
26import java.util.concurrent.TimeoutException;
alshabib8ca53902014-10-07 13:11:17 -070027
Brian O'Connor66630c82014-10-02 21:08:19 -070028import org.apache.felix.scr.annotations.Activate;
29import org.apache.felix.scr.annotations.Component;
30import org.apache.felix.scr.annotations.Deactivate;
31import org.apache.felix.scr.annotations.Reference;
32import org.apache.felix.scr.annotations.ReferenceCardinality;
33import org.apache.felix.scr.annotations.Service;
34import org.onlab.onos.event.AbstractListenerRegistry;
35import org.onlab.onos.event.EventDeliveryService;
Brian O'Connorcb900f42014-10-07 21:55:33 -070036import org.onlab.onos.net.flow.CompletedBatchOperation;
Brian O'Connorf2dbde52014-10-10 16:20:24 -070037import org.onlab.onos.net.flow.FlowRuleBatchOperation;
38import org.onlab.onos.net.flow.FlowRuleService;
Brian O'Connor66630c82014-10-02 21:08:19 -070039import org.onlab.onos.net.intent.InstallableIntent;
40import org.onlab.onos.net.intent.Intent;
41import org.onlab.onos.net.intent.IntentCompiler;
42import org.onlab.onos.net.intent.IntentEvent;
43import org.onlab.onos.net.intent.IntentException;
44import org.onlab.onos.net.intent.IntentExtensionService;
45import org.onlab.onos.net.intent.IntentId;
46import org.onlab.onos.net.intent.IntentInstaller;
47import org.onlab.onos.net.intent.IntentListener;
48import 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;
52import org.onlab.onos.net.intent.IntentStoreDelegate;
53import org.slf4j.Logger;
54
Brian O'Connorcb900f42014-10-07 21:55:33 -070055import com.google.common.collect.ImmutableList;
alshabib8ca53902014-10-07 13:11:17 -070056import com.google.common.collect.ImmutableMap;
Brian O'Connorcb900f42014-10-07 21:55:33 -070057import com.google.common.collect.Lists;
Brian O'Connor66630c82014-10-02 21:08:19 -070058
59/**
60 * An implementation of Intent Manager.
61 */
62@Component(immediate = true)
63@Service
64public class IntentManager
65 implements IntentService, IntentExtensionService {
66 private final Logger log = getLogger(getClass());
67
68 public static final String INTENT_NULL = "Intent cannot be null";
69 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
70
71 // Collections for compiler, installer, and listener are ONOS instance local
72 private final ConcurrentMap<Class<? extends Intent>,
73 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
74 private final ConcurrentMap<Class<? extends InstallableIntent>,
75 IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070076
77 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070078 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070079
Brian O'Connorcb900f42014-10-07 21:55:33 -070080 private ExecutorService executor;
81 private ExecutorService monitorExecutor;
tom85258ee2014-10-07 00:10:02 -070082
Brian O'Connor66630c82014-10-02 21:08:19 -070083 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
tom95329eb2014-10-06 08:40:06 -070084 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
Brian O'Connor66630c82014-10-02 21:08:19 -070085
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected IntentStore store;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -070090 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -070091
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -070093 protected EventDeliveryService eventDispatcher;
94
Brian O'Connorf2dbde52014-10-10 16:20:24 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected FlowRuleService flowRuleService;
97
Brian O'Connor66630c82014-10-02 21:08:19 -070098 @Activate
99 public void activate() {
100 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700101 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700102 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700103 executor = newSingleThreadExecutor(namedThreads("onos-intents"));
104 monitorExecutor = newSingleThreadExecutor(namedThreads("onos-intent-monitor"));
Brian O'Connor66630c82014-10-02 21:08:19 -0700105 log.info("Started");
106 }
107
108 @Deactivate
109 public void deactivate() {
110 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700111 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700112 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700113 executor.shutdown();
114 monitorExecutor.shutdown();
Brian O'Connor66630c82014-10-02 21:08:19 -0700115 log.info("Stopped");
116 }
117
118 @Override
119 public void submit(Intent intent) {
120 checkNotNull(intent, INTENT_NULL);
121 registerSubclassCompilerIfNeeded(intent);
122 IntentEvent event = store.createIntent(intent);
tom85258ee2014-10-07 00:10:02 -0700123 if (event != null) {
124 eventDispatcher.post(event);
125 executor.execute(new IntentTask(COMPILING, intent));
126 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700127 }
128
129 @Override
130 public void withdraw(Intent intent) {
131 checkNotNull(intent, INTENT_NULL);
tom85258ee2014-10-07 00:10:02 -0700132 executor.execute(new IntentTask(WITHDRAWING, intent));
Brian O'Connor66630c82014-10-02 21:08:19 -0700133 }
134
135 // FIXME: implement this method
136 @Override
137 public void execute(IntentOperations operations) {
138 throw new UnsupportedOperationException("execute() is not implemented yet");
139 }
140
141 @Override
142 public Iterable<Intent> getIntents() {
143 return store.getIntents();
144 }
145
146 @Override
147 public long getIntentCount() {
148 return store.getIntentCount();
149 }
150
151 @Override
152 public Intent getIntent(IntentId id) {
153 checkNotNull(id, INTENT_ID_NULL);
154 return store.getIntent(id);
155 }
156
157 @Override
158 public IntentState getIntentState(IntentId id) {
159 checkNotNull(id, INTENT_ID_NULL);
160 return store.getIntentState(id);
161 }
162
163 @Override
164 public void addListener(IntentListener listener) {
165 listenerRegistry.addListener(listener);
166 }
167
168 @Override
169 public void removeListener(IntentListener listener) {
170 listenerRegistry.removeListener(listener);
171 }
172
173 @Override
174 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
175 compilers.put(cls, compiler);
176 }
177
178 @Override
179 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
180 compilers.remove(cls);
181 }
182
183 @Override
184 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
185 return ImmutableMap.copyOf(compilers);
186 }
187
188 @Override
189 public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
190 installers.put(cls, installer);
191 }
192
193 @Override
194 public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
195 installers.remove(cls);
196 }
197
198 @Override
199 public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
200 return ImmutableMap.copyOf(installers);
201 }
202
203 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700204 * Returns the corresponding intent compiler to the specified intent.
205 *
206 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700207 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700208 * @return intent compiler corresponding to the specified intent
209 */
210 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
211 @SuppressWarnings("unchecked")
212 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
213 if (compiler == null) {
214 throw new IntentException("no compiler for class " + intent.getClass());
215 }
216 return compiler;
217 }
218
219 /**
220 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700221 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700222 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700223 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700224 * @return intent installer corresponding to the specified installable intent
225 */
226 private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
227 @SuppressWarnings("unchecked")
228 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
229 if (installer == null) {
230 throw new IntentException("no installer for class " + intent.getClass());
231 }
232 return installer;
233 }
234
235 /**
tom85258ee2014-10-07 00:10:02 -0700236 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700237 *
tom85258ee2014-10-07 00:10:02 -0700238 * @param intent intent to be compiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700239 */
tom85258ee2014-10-07 00:10:02 -0700240 private void executeCompilingPhase(Intent intent) {
241 // Indicate that the intent is entering the compiling phase.
242 store.setState(intent, COMPILING);
243
244 try {
245 // Compile the intent into installable derivatives.
246 List<InstallableIntent> installable = compileIntent(intent);
247
248 // If all went well, associate the resulting list of installable
249 // intents with the top-level intent and proceed to install.
250 store.addInstallableIntents(intent.id(), installable);
251 executeInstallingPhase(intent);
252
253 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700254 log.warn("Unable to compile intent {} due to: {}", intent.id(), e);
255
tom85258ee2014-10-07 00:10:02 -0700256 // If compilation failed, mark the intent as failed.
257 store.setState(intent, FAILED);
258 }
259 }
260
Brian O'Connorcb900f42014-10-07 21:55:33 -0700261 /**
262 * Compiles an intent recursively.
263 *
264 * @param intent intent
265 * @return result of compilation
266 */
tom85258ee2014-10-07 00:10:02 -0700267 private List<InstallableIntent> compileIntent(Intent intent) {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700268 if (intent instanceof InstallableIntent) {
269 return ImmutableList.of((InstallableIntent) intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700270 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700271
272 List<InstallableIntent> installable = new ArrayList<>();
273 // TODO do we need to registerSubclassCompiler?
274 for (Intent compiled : getCompiler(intent).compile(intent)) {
275 installable.addAll(compileIntent(compiled));
276 }
277
tom85258ee2014-10-07 00:10:02 -0700278 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700279 }
280
281 /**
tom85258ee2014-10-07 00:10:02 -0700282 * Installs all installable intents associated with the specified top-level
283 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700284 *
tom85258ee2014-10-07 00:10:02 -0700285 * @param intent intent to be installed
Brian O'Connor66630c82014-10-02 21:08:19 -0700286 */
tom85258ee2014-10-07 00:10:02 -0700287 private void executeInstallingPhase(Intent intent) {
288 // Indicate that the intent is entering the installing phase.
289 store.setState(intent, INSTALLING);
290
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700291 List<FlowRuleBatchOperation> installWork = Lists.newArrayList();
tom85258ee2014-10-07 00:10:02 -0700292 try {
293 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
294 if (installables != null) {
295 for (InstallableIntent installable : installables) {
296 registerSubclassInstallerIfNeeded(installable);
tom53945d52014-10-07 11:01:36 -0700297 trackerService.addTrackedResources(intent.id(),
298 installable.requiredLinks());
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700299 List<FlowRuleBatchOperation> batch = getInstaller(installable).install(installable);
300 installWork.addAll(batch);
tom85258ee2014-10-07 00:10:02 -0700301 }
tom95329eb2014-10-06 08:40:06 -0700302 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700303 // FIXME we have to wait for the installable intents
304 //eventDispatcher.post(store.setState(intent, INSTALLED));
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700305 monitorExecutor.execute(new IntentInstallMonitor(intent, installWork, INSTALLED));
tom85258ee2014-10-07 00:10:02 -0700306 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700307 log.warn("Unable to install intent {} due to: {}", intent.id(), e);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700308 uninstallIntent(intent, RECOMPILING);
tom53945d52014-10-07 11:01:36 -0700309
tom85258ee2014-10-07 00:10:02 -0700310 // If compilation failed, kick off the recompiling phase.
Brian O'Connorcb900f42014-10-07 21:55:33 -0700311 // FIXME
312 //executeRecompilingPhase(intent);
tom85258ee2014-10-07 00:10:02 -0700313 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700314 }
315
316 /**
tom85258ee2014-10-07 00:10:02 -0700317 * Recompiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700318 *
tom85258ee2014-10-07 00:10:02 -0700319 * @param intent intent to be recompiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700320 */
tom85258ee2014-10-07 00:10:02 -0700321 private void executeRecompilingPhase(Intent intent) {
322 // Indicate that the intent is entering the recompiling phase.
323 store.setState(intent, RECOMPILING);
324
325 try {
326 // Compile the intent into installable derivatives.
327 List<InstallableIntent> installable = compileIntent(intent);
328
329 // If all went well, compare the existing list of installable
330 // intents with the newly compiled list. If they are the same,
331 // bail, out since the previous approach was determined not to
332 // be viable.
333 List<InstallableIntent> originalInstallable =
334 store.getInstallableIntents(intent.id());
335
336 if (Objects.equals(originalInstallable, installable)) {
337 eventDispatcher.post(store.setState(intent, FAILED));
338 } else {
339 // Otherwise, re-associate the newly compiled installable intents
340 // with the top-level intent and kick off installing phase.
341 store.addInstallableIntents(intent.id(), installable);
342 executeInstallingPhase(intent);
343 }
344 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700345 log.warn("Unable to recompile intent {} due to: {}", intent.id(), e);
346
tom85258ee2014-10-07 00:10:02 -0700347 // If compilation failed, mark the intent as failed.
348 eventDispatcher.post(store.setState(intent, FAILED));
349 }
350 }
351
352 /**
353 * Uninstalls the specified intent by uninstalling all of its associated
354 * installable derivatives.
355 *
356 * @param intent intent to be installed
357 */
358 private void executeWithdrawingPhase(Intent intent) {
359 // Indicate that the intent is being withdrawn.
360 store.setState(intent, WITHDRAWING);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700361 uninstallIntent(intent, WITHDRAWN);
tom85258ee2014-10-07 00:10:02 -0700362
363 // If all went well, disassociate the top-level intent with its
364 // installable derivatives and mark it as withdrawn.
Brian O'Connorcb900f42014-10-07 21:55:33 -0700365 // FIXME need to clean up
366 //store.removeInstalledIntents(intent.id());
367 // FIXME
368 //eventDispatcher.post(store.setState(intent, WITHDRAWN));
Brian O'Connor66630c82014-10-02 21:08:19 -0700369 }
370
371 /**
tom53945d52014-10-07 11:01:36 -0700372 * Uninstalls all installable intents associated with the given intent.
373 *
374 * @param intent intent to be uninstalled
375 */
Brian O'Connorcb900f42014-10-07 21:55:33 -0700376 private void uninstallIntent(Intent intent, IntentState nextState) {
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700377 List<FlowRuleBatchOperation> uninstallWork = Lists.newArrayList();
tom53945d52014-10-07 11:01:36 -0700378 try {
379 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
380 if (installables != null) {
381 for (InstallableIntent installable : installables) {
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700382 List<FlowRuleBatchOperation> batches = getInstaller(installable).uninstall(installable);
383 uninstallWork.addAll(batches);
tom53945d52014-10-07 11:01:36 -0700384 }
385 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700386 monitorExecutor.execute(new IntentInstallMonitor(intent, uninstallWork, nextState));
tom53945d52014-10-07 11:01:36 -0700387 } catch (IntentException e) {
388 log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
389 }
390 }
391
392 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700393 * Registers an intent compiler of the specified intent if an intent compiler
394 * for the intent is not registered. This method traverses the class hierarchy of
395 * the intent. Once an intent compiler for a parent type is found, this method
396 * registers the found intent compiler.
397 *
398 * @param intent intent
399 */
400 private void registerSubclassCompilerIfNeeded(Intent intent) {
401 if (!compilers.containsKey(intent.getClass())) {
402 Class<?> cls = intent.getClass();
403 while (cls != Object.class) {
404 // As long as we're within the Intent class descendants
405 if (Intent.class.isAssignableFrom(cls)) {
406 IntentCompiler<?> compiler = compilers.get(cls);
407 if (compiler != null) {
408 compilers.put(intent.getClass(), compiler);
409 return;
410 }
411 }
412 cls = cls.getSuperclass();
413 }
414 }
415 }
416
417 /**
418 * Registers an intent installer of the specified intent if an intent installer
419 * for the intent is not registered. This method traverses the class hierarchy of
420 * the intent. Once an intent installer for a parent type is found, this method
421 * registers the found intent installer.
422 *
423 * @param intent intent
424 */
425 private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
426 if (!installers.containsKey(intent.getClass())) {
427 Class<?> cls = intent.getClass();
428 while (cls != Object.class) {
429 // As long as we're within the InstallableIntent class descendants
430 if (InstallableIntent.class.isAssignableFrom(cls)) {
431 IntentInstaller<?> installer = installers.get(cls);
432 if (installer != null) {
433 installers.put(intent.getClass(), installer);
434 return;
435 }
436 }
437 cls = cls.getSuperclass();
438 }
439 }
440 }
441
Brian O'Connor66630c82014-10-02 21:08:19 -0700442 // Store delegate to re-post events emitted from the store.
443 private class InternalStoreDelegate implements IntentStoreDelegate {
444 @Override
445 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700446 eventDispatcher.post(event);
447 if (event.type() == IntentEvent.Type.SUBMITTED) {
448 executor.execute(new IntentTask(COMPILING, event.subject()));
449 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700450 }
451 }
452
tom95329eb2014-10-06 08:40:06 -0700453 // Topology change delegate
454 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
455 @Override
tom85258ee2014-10-07 00:10:02 -0700456 public void triggerCompile(Iterable<IntentId> intentIds,
457 boolean compileAllFailed) {
458 // Attempt recompilation of the specified intents first.
tom95329eb2014-10-06 08:40:06 -0700459 for (IntentId intentId : intentIds) {
tom53945d52014-10-07 11:01:36 -0700460 Intent intent = getIntent(intentId);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700461 uninstallIntent(intent, RECOMPILING);
alshabib8ca53902014-10-07 13:11:17 -0700462
Brian O'Connorcb900f42014-10-07 21:55:33 -0700463 //FIXME
464 //executeRecompilingPhase(intent);
tom85258ee2014-10-07 00:10:02 -0700465 }
466
467 if (compileAllFailed) {
468 // If required, compile all currently failed intents.
469 for (Intent intent : getIntents()) {
470 if (getIntentState(intent.id()) == FAILED) {
471 executeCompilingPhase(intent);
472 }
473 }
tom95329eb2014-10-06 08:40:06 -0700474 }
475 }
tom95329eb2014-10-06 08:40:06 -0700476 }
tom85258ee2014-10-07 00:10:02 -0700477
478 // Auxiliary runnable to perform asynchronous tasks.
479 private class IntentTask implements Runnable {
480 private final IntentState state;
481 private final Intent intent;
482
483 public IntentTask(IntentState state, Intent intent) {
484 this.state = state;
485 this.intent = intent;
486 }
487
488 @Override
489 public void run() {
490 if (state == COMPILING) {
491 executeCompilingPhase(intent);
492 } else if (state == RECOMPILING) {
493 executeRecompilingPhase(intent);
494 } else if (state == WITHDRAWING) {
495 executeWithdrawingPhase(intent);
496 }
497 }
498 }
499
Brian O'Connorcb900f42014-10-07 21:55:33 -0700500 private class IntentInstallMonitor implements Runnable {
501
502 private final Intent intent;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700503 private final List<FlowRuleBatchOperation> work;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700504 private final List<Future<CompletedBatchOperation>> futures;
505 private final IntentState nextState;
506
507 public IntentInstallMonitor(Intent intent,
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700508 List<FlowRuleBatchOperation> work,
509 IntentState nextState) {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700510 this.intent = intent;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700511 this.work = work;
512 // TODO how many Futures can be outstanding? one?
513 this.futures = Lists.newLinkedList();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700514 this.nextState = nextState;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700515
516 // TODO need to kick off the first batch sometime, why not now?
517 futures.add(applyNextBatch());
Brian O'Connorcb900f42014-10-07 21:55:33 -0700518 }
519
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700520 /**
521 * Update the intent store with the next status for this intent.
522 */
523 private void updateIntent() {
Brian O'Connorcb900f42014-10-07 21:55:33 -0700524 if (nextState == RECOMPILING) {
525 executor.execute(new IntentTask(nextState, intent));
526 } else if (nextState == INSTALLED || nextState == WITHDRAWN) {
527 eventDispatcher.post(store.setState(intent, nextState));
528 } else {
529 log.warn("Invalid next intent state {} for intent {}", nextState, intent);
530 }
531 }
532
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700533 /**
534 * Apply a list of FlowRules.
535 *
536 * @param rules rules to apply
537 */
538 private Future<CompletedBatchOperation> applyNextBatch() {
539 if (work.isEmpty()) {
540 return null;
541 }
542 FlowRuleBatchOperation batch = work.remove(0);
543 return flowRuleService.applyBatch(batch);
544 }
545
546 /**
547 * Iterate through the pending futures, and remove them when they have completed.
548 */
549 private void processFutures() {
550 List<Future<CompletedBatchOperation>> newFutures = Lists.newArrayList();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700551 for (Iterator<Future<CompletedBatchOperation>> i = futures.iterator(); i.hasNext();) {
552 Future<CompletedBatchOperation> future = i.next();
alshabib26834582014-10-08 20:15:46 -0700553 try {
554 // TODO: we may want to get the future here and go back to the future.
alshabibc7e1cb62014-10-08 20:20:03 -0700555 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700556 if (completed.isSuccess()) {
557 Future<CompletedBatchOperation> newFuture = applyNextBatch();
558 if (newFuture != null) {
559 // we'll add this later so that we don't get a ConcurrentModException
560 newFutures.add(newFuture);
561 }
562 } else {
563 // TODO check if future succeeded and if not report fail items
564 log.warn("Failed items: {}", completed.failedItems());
565 // TODO revert....
566 //uninstallIntent(intent, RECOMPILING);
567 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700568 i.remove();
alshabib26834582014-10-08 20:15:46 -0700569 } catch (TimeoutException | InterruptedException | ExecutionException te) {
570 log.debug("Intallations of intent {} is still pending", intent);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700571 }
572 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700573 futures.addAll(newFutures);
574 }
575
576 @Override
577 public void run() {
578 processFutures();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700579 if (futures.isEmpty()) {
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700580 // woohoo! we are done!
581 updateIntent();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700582 } else {
583 // resubmit ourselves if we are not done yet
584 monitorExecutor.submit(this);
585 }
586 }
587 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700588}