blob: 70878c50c1ceafbf077c99682c964bb9d2ed2cbf [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
Brian O'Connorfa81eae2014-10-30 13:20:05 -070018import 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;
28import org.onlab.onos.event.AbstractListenerRegistry;
29import org.onlab.onos.event.EventDeliveryService;
Brian O'Connorcb900f42014-10-07 21:55:33 -070030import org.onlab.onos.net.flow.CompletedBatchOperation;
Brian O'Connorf2dbde52014-10-10 16:20:24 -070031import org.onlab.onos.net.flow.FlowRuleBatchOperation;
32import org.onlab.onos.net.flow.FlowRuleService;
Brian O'Connor66630c82014-10-02 21:08:19 -070033import org.onlab.onos.net.intent.Intent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070034import org.onlab.onos.net.intent.IntentBatchDelegate;
35import org.onlab.onos.net.intent.IntentBatchService;
Brian O'Connor66630c82014-10-02 21:08:19 -070036import org.onlab.onos.net.intent.IntentCompiler;
37import org.onlab.onos.net.intent.IntentEvent;
38import org.onlab.onos.net.intent.IntentException;
39import org.onlab.onos.net.intent.IntentExtensionService;
40import org.onlab.onos.net.intent.IntentId;
41import org.onlab.onos.net.intent.IntentInstaller;
42import org.onlab.onos.net.intent.IntentListener;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070043import org.onlab.onos.net.intent.IntentOperation;
Brian O'Connor66630c82014-10-02 21:08:19 -070044import org.onlab.onos.net.intent.IntentOperations;
45import org.onlab.onos.net.intent.IntentService;
46import org.onlab.onos.net.intent.IntentState;
47import org.onlab.onos.net.intent.IntentStore;
48import org.onlab.onos.net.intent.IntentStoreDelegate;
49import org.slf4j.Logger;
50
Brian O'Connorfa81eae2014-10-30 13:20:05 -070051import java.util.ArrayList;
52import java.util.List;
53import java.util.Map;
54import java.util.Objects;
55import java.util.concurrent.ConcurrentHashMap;
56import java.util.concurrent.ConcurrentMap;
57import java.util.concurrent.ExecutionException;
58import java.util.concurrent.ExecutorService;
59import java.util.concurrent.Future;
60import java.util.concurrent.TimeUnit;
61import java.util.concurrent.TimeoutException;
62
63import static com.google.common.base.Preconditions.checkArgument;
64import static com.google.common.base.Preconditions.checkNotNull;
65import static java.util.concurrent.Executors.newSingleThreadExecutor;
66import static org.onlab.onos.net.intent.IntentState.*;
67import static org.onlab.util.Tools.namedThreads;
68import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070069
70/**
71 * An implementation of Intent Manager.
72 */
73@Component(immediate = true)
74@Service
75public class IntentManager
76 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080077 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070078
79 public static final String INTENT_NULL = "Intent cannot be null";
80 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
81
82 // Collections for compiler, installer, and listener are ONOS instance local
83 private final ConcurrentMap<Class<? extends Intent>,
84 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070085 private final ConcurrentMap<Class<? extends Intent>,
86 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070087
88 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070089 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070090
Brian O'Connorcb900f42014-10-07 21:55:33 -070091 private ExecutorService executor;
92 private ExecutorService monitorExecutor;
tom85258ee2014-10-07 00:10:02 -070093
Brian O'Connor66630c82014-10-02 21:08:19 -070094 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
tom95329eb2014-10-06 08:40:06 -070095 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
Brian O'Connorfa81eae2014-10-30 13:20:05 -070096 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
Brian O'Connor66630c82014-10-02 21:08:19 -070097
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected IntentStore store;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700102 protected IntentBatchService batchService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700105 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700108 protected EventDeliveryService eventDispatcher;
109
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected FlowRuleService flowRuleService;
112
Brian O'Connor66630c82014-10-02 21:08:19 -0700113 @Activate
114 public void activate() {
115 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700116 trackerService.setDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700117 batchService.setDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700118 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700119 executor = newSingleThreadExecutor(namedThreads("onos-intents"));
120 monitorExecutor = newSingleThreadExecutor(namedThreads("onos-intent-monitor"));
Brian O'Connor66630c82014-10-02 21:08:19 -0700121 log.info("Started");
122 }
123
124 @Deactivate
125 public void deactivate() {
126 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700127 trackerService.unsetDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700128 batchService.unsetDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700129 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700130 executor.shutdown();
131 monitorExecutor.shutdown();
Brian O'Connor66630c82014-10-02 21:08:19 -0700132 log.info("Stopped");
133 }
134
135 @Override
136 public void submit(Intent intent) {
137 checkNotNull(intent, INTENT_NULL);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700138 execute(IntentOperations.builder().addSubmitOperation(intent).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700139 }
140
141 @Override
142 public void withdraw(Intent intent) {
143 checkNotNull(intent, INTENT_NULL);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700144 execute(IntentOperations.builder().addWithdrawOperation(intent.id()).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700145 }
146
Brian O'Connor66630c82014-10-02 21:08:19 -0700147 @Override
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700148 public void replace(IntentId oldIntentId, Intent newIntent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700149 checkNotNull(oldIntentId, INTENT_ID_NULL);
150 checkNotNull(newIntent, INTENT_NULL);
151 execute(IntentOperations.builder()
152 .addReplaceOperation(oldIntentId, newIntent)
153 .build());
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700154 }
155
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700156 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700157 public void execute(IntentOperations operations) {
Brian O'Connore2ff25a2014-11-18 19:25:43 -0800158 if (operations.operations().isEmpty()) {
159 return;
160 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700161 batchService.addIntentOperations(operations);
Brian O'Connor66630c82014-10-02 21:08:19 -0700162 }
163
164 @Override
165 public Iterable<Intent> getIntents() {
166 return store.getIntents();
167 }
168
169 @Override
170 public long getIntentCount() {
171 return store.getIntentCount();
172 }
173
174 @Override
175 public Intent getIntent(IntentId id) {
176 checkNotNull(id, INTENT_ID_NULL);
177 return store.getIntent(id);
178 }
179
180 @Override
181 public IntentState getIntentState(IntentId id) {
182 checkNotNull(id, INTENT_ID_NULL);
183 return store.getIntentState(id);
184 }
185
186 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700187 public List<Intent> getInstallableIntents(IntentId intentId) {
188 checkNotNull(intentId, INTENT_ID_NULL);
189 return store.getInstallableIntents(intentId);
190 }
191
192 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700193 public void addListener(IntentListener listener) {
194 listenerRegistry.addListener(listener);
195 }
196
197 @Override
198 public void removeListener(IntentListener listener) {
199 listenerRegistry.removeListener(listener);
200 }
201
202 @Override
203 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
204 compilers.put(cls, compiler);
205 }
206
207 @Override
208 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
209 compilers.remove(cls);
210 }
211
212 @Override
213 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
214 return ImmutableMap.copyOf(compilers);
215 }
216
217 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700218 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700219 installers.put(cls, installer);
220 }
221
222 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700223 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700224 installers.remove(cls);
225 }
226
227 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700228 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700229 return ImmutableMap.copyOf(installers);
230 }
231
232 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700233 * Returns the corresponding intent compiler to the specified intent.
234 *
235 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700236 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700237 * @return intent compiler corresponding to the specified intent
238 */
239 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
240 @SuppressWarnings("unchecked")
241 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
242 if (compiler == null) {
243 throw new IntentException("no compiler for class " + intent.getClass());
244 }
245 return compiler;
246 }
247
248 /**
249 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700250 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700251 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700252 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 * @return intent installer corresponding to the specified installable intent
254 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700255 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700256 @SuppressWarnings("unchecked")
257 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
258 if (installer == null) {
259 throw new IntentException("no installer for class " + intent.getClass());
260 }
261 return installer;
262 }
263
264 /**
tom85258ee2014-10-07 00:10:02 -0700265 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700266 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700267 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700268 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700269 private void executeCompilingPhase(IntentUpdate update) {
270 Intent intent = update.newIntent();
tom85258ee2014-10-07 00:10:02 -0700271 // Indicate that the intent is entering the compiling phase.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700272 update.setState(intent, COMPILING);
tom85258ee2014-10-07 00:10:02 -0700273
274 try {
275 // Compile the intent into installable derivatives.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700276 List<Intent> installables = compileIntent(intent, update);
tom85258ee2014-10-07 00:10:02 -0700277
278 // If all went well, associate the resulting list of installable
279 // intents with the top-level intent and proceed to install.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700280 update.setInstallables(installables);
281 } catch (IntentException e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700282 log.warn("Unable to compile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700283
tom85258ee2014-10-07 00:10:02 -0700284 // If compilation failed, mark the intent as failed.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700285 update.setState(intent, FAILED);
tom85258ee2014-10-07 00:10:02 -0700286 }
287 }
288
Brian O'Connorcb900f42014-10-07 21:55:33 -0700289 /**
290 * Compiles an intent recursively.
291 *
292 * @param intent intent
293 * @return result of compilation
294 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700295 private List<Intent> compileIntent(Intent intent, IntentUpdate update) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700296 if (intent.isInstallable()) {
297 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700298 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700299
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700300 registerSubclassCompilerIfNeeded(intent);
301 List<Intent> previous = update.oldInstallables();
302 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700303 List<Intent> installable = new ArrayList<>();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700304 for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) {
305 installable.addAll(compileIntent(compiled, update));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700306 }
tom85258ee2014-10-07 00:10:02 -0700307 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700308 }
309
310 /**
tom85258ee2014-10-07 00:10:02 -0700311 * Installs all installable intents associated with the specified top-level
312 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700313 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700314 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700315 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700316 private void executeInstallingPhase(IntentUpdate update) {
317 if (update.newInstallables() == null) {
318 //no failed intents allowed past this point...
319 return;
tom85258ee2014-10-07 00:10:02 -0700320 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700321 // Indicate that the intent is entering the installing phase.
322 update.setState(update.newIntent(), INSTALLING);
323
324 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
325 for (Intent installable : update.newInstallables()) {
326 registerSubclassInstallerIfNeeded(installable);
327 trackerService.addTrackedResources(update.newIntent().id(),
328 installable.resources());
329 try {
330 batches.addAll(getInstaller(installable).install(installable));
331 } catch (IntentException e) {
332 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
333 //FIXME we failed... intent should be recompiled
334 // TODO: remove resources
335 // recompile!!!
336 }
337 }
338 update.setBatches(batches);
339 }
340
341 /**
342 * Uninstalls the specified intent by uninstalling all of its associated
343 * installable derivatives.
344 *
345 * @param update intent update
346 */
347 private void executeWithdrawingPhase(IntentUpdate update) {
348 if (!update.oldIntent().equals(update.newIntent())) {
349 update.setState(update.oldIntent(), WITHDRAWING);
350 } // else newIntent is FAILED
351 uninstallIntent(update);
352
353 // If all went well, disassociate the top-level intent with its
354 // installable derivatives and mark it as withdrawn.
355 // FIXME need to clean up
356 //store.removeInstalledIntents(intent.id());
357 }
358
359 /**
360 * Uninstalls all installable intents associated with the given intent.
361 *
362 * @param update intent update
363 */
364 //FIXME: need to handle next state properly
365 private void uninstallIntent(IntentUpdate update) {
366 if (update.oldInstallables == null) {
367 return;
368 }
369 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
370 for (Intent installable : update.oldInstallables()) {
371 trackerService.removeTrackedResources(update.oldIntent().id(),
372 installable.resources());
373 try {
374 batches.addAll(getInstaller(installable).uninstall(installable));
375 } catch (IntentException e) {
376 log.warn("Unable to uninstall intent {} due to:", update.oldIntent().id(), e);
377 // TODO: this should never happen. but what if it does?
378 }
379 }
380 update.setBatches(batches);
381 // FIXME: next state for old is WITHDRAWN or FAILED
Brian O'Connor66630c82014-10-02 21:08:19 -0700382 }
383
384 /**
tom85258ee2014-10-07 00:10:02 -0700385 * Recompiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700386 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700387 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700388 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700389 // FIXME: update this to work
390 private void executeRecompilingPhase(IntentUpdate update) {
391 Intent intent = update.newIntent();
tom85258ee2014-10-07 00:10:02 -0700392 // Indicate that the intent is entering the recompiling phase.
393 store.setState(intent, RECOMPILING);
394
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700395 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
tom85258ee2014-10-07 00:10:02 -0700396 try {
397 // Compile the intent into installable derivatives.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700398 List<Intent> installable = compileIntent(intent, update);
tom85258ee2014-10-07 00:10:02 -0700399
400 // If all went well, compare the existing list of installable
401 // intents with the newly compiled list. If they are the same,
402 // bail, out since the previous approach was determined not to
403 // be viable.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700404 // FIXME do we need this?
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700405 List<Intent> originalInstallable = store.getInstallableIntents(intent.id());
tom85258ee2014-10-07 00:10:02 -0700406
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700407 //FIXME let's be smarter about how we perform the update
408 //batches.addAll(uninstallIntent(intent, null));
409
tom85258ee2014-10-07 00:10:02 -0700410 if (Objects.equals(originalInstallable, installable)) {
411 eventDispatcher.post(store.setState(intent, FAILED));
412 } else {
413 // Otherwise, re-associate the newly compiled installable intents
414 // with the top-level intent and kick off installing phase.
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700415 store.setInstallableIntents(intent.id(), installable);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700416 // FIXME commented out for now
417 //batches.addAll(executeInstallingPhase(update));
tom85258ee2014-10-07 00:10:02 -0700418 }
419 } catch (Exception e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700420 log.warn("Unable to recompile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700421
tom85258ee2014-10-07 00:10:02 -0700422 // If compilation failed, mark the intent as failed.
423 eventDispatcher.post(store.setState(intent, FAILED));
424 }
425 }
426
427 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700428 * Withdraws the old intent and installs the new intent as one operation.
tom85258ee2014-10-07 00:10:02 -0700429 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700430 * @param update intent update
tom85258ee2014-10-07 00:10:02 -0700431 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700432 private void executeReplacementPhase(IntentUpdate update) {
433 checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
434 "Old and New Intent must have equivalent installable intents.");
435 if (!update.oldIntent().equals(update.newIntent())) {
436 // only set the old intent's state if it is different
437 update.setState(update.oldIntent(), WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700438 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700439 update.setState(update.newIntent(), INSTALLING);
440
441 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
442 for (int i = 0; i < update.oldInstallables().size(); i++) {
443 Intent oldInstallable = update.oldInstallables().get(i);
444 Intent newInstallable = update.newInstallables().get(i);
445 if (oldInstallable.equals(newInstallable)) {
446 continue;
447 }
448 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
449 "Installable Intent type mismatch.");
450 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
451 trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
452 try {
453 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
454 } catch (IntentException e) {
455 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
456 //FIXME... we failed. need to uninstall (if same) or revert (if different)
457 }
458 }
459 update.setBatches(batches);
tom53945d52014-10-07 11:01:36 -0700460 }
461
462 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700463 * Registers an intent compiler of the specified intent if an intent compiler
464 * for the intent is not registered. This method traverses the class hierarchy of
465 * the intent. Once an intent compiler for a parent type is found, this method
466 * registers the found intent compiler.
467 *
468 * @param intent intent
469 */
470 private void registerSubclassCompilerIfNeeded(Intent intent) {
471 if (!compilers.containsKey(intent.getClass())) {
472 Class<?> cls = intent.getClass();
473 while (cls != Object.class) {
474 // As long as we're within the Intent class descendants
475 if (Intent.class.isAssignableFrom(cls)) {
476 IntentCompiler<?> compiler = compilers.get(cls);
477 if (compiler != null) {
478 compilers.put(intent.getClass(), compiler);
479 return;
480 }
481 }
482 cls = cls.getSuperclass();
483 }
484 }
485 }
486
487 /**
488 * Registers an intent installer of the specified intent if an intent installer
489 * for the intent is not registered. This method traverses the class hierarchy of
490 * the intent. Once an intent installer for a parent type is found, this method
491 * registers the found intent installer.
492 *
493 * @param intent intent
494 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700495 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700496 if (!installers.containsKey(intent.getClass())) {
497 Class<?> cls = intent.getClass();
498 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700499 // As long as we're within the Intent class descendants
500 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700501 IntentInstaller<?> installer = installers.get(cls);
502 if (installer != null) {
503 installers.put(intent.getClass(), installer);
504 return;
505 }
506 }
507 cls = cls.getSuperclass();
508 }
509 }
510 }
511
Brian O'Connor66630c82014-10-02 21:08:19 -0700512 // Store delegate to re-post events emitted from the store.
513 private class InternalStoreDelegate implements IntentStoreDelegate {
514 @Override
515 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700516 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700517 }
518 }
519
tom95329eb2014-10-06 08:40:06 -0700520 // Topology change delegate
521 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
522 @Override
tom85258ee2014-10-07 00:10:02 -0700523 public void triggerCompile(Iterable<IntentId> intentIds,
524 boolean compileAllFailed) {
525 // Attempt recompilation of the specified intents first.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700526 IntentOperations.Builder builder = IntentOperations.builder();
527 for (IntentId id : intentIds) {
528 builder.addUpdateOperation(id);
tom85258ee2014-10-07 00:10:02 -0700529 }
530
531 if (compileAllFailed) {
532 // If required, compile all currently failed intents.
533 for (Intent intent : getIntents()) {
534 if (getIntentState(intent.id()) == FAILED) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700535 builder.addUpdateOperation(intent.id());
tom85258ee2014-10-07 00:10:02 -0700536 }
537 }
tom95329eb2014-10-06 08:40:06 -0700538 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700539 execute(builder.build());
tom95329eb2014-10-06 08:40:06 -0700540 }
tom95329eb2014-10-06 08:40:06 -0700541 }
tom85258ee2014-10-07 00:10:02 -0700542
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700543 /**
544 * TODO.
545 * @param op intent operation
546 * @return intent update
547 */
548 private IntentUpdate processIntentOperation(IntentOperation op) {
549 IntentUpdate update = new IntentUpdate(op);
550
551 if (update.newIntent() != null) {
552 executeCompilingPhase(update);
553 }
554
555 if (update.oldInstallables() != null && update.newInstallables() != null) {
556 executeReplacementPhase(update);
557 } else if (update.newInstallables() != null) {
558 executeInstallingPhase(update);
559 } else if (update.oldInstallables() != null) {
560 executeWithdrawingPhase(update);
561 } else {
562 if (update.oldIntent() != null) {
563 // TODO this shouldn't happen
564 return update; //FIXME
565 }
566 if (update.newIntent() != null) {
567 // TODO assert that next state is failed
568 return update; //FIXME
569 }
570 }
571
572 return update;
573 }
574
575 // TODO comments...
576 private class IntentUpdate {
577 private final IntentOperation op;
578 private final Intent oldIntent;
579 private final Intent newIntent;
580 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
581
582 private final List<Intent> oldInstallables;
583 private List<Intent> newInstallables;
584 private List<FlowRuleBatchOperation> batches;
585
586 IntentUpdate(IntentOperation op) {
587 this.op = op;
588 switch (op.type()) {
589 case SUBMIT:
590 newIntent = op.intent();
591 oldIntent = null;
592 break;
593 case WITHDRAW:
594 newIntent = null;
595 oldIntent = store.getIntent(op.intentId());
596 break;
597 case REPLACE:
598 newIntent = op.intent();
599 oldIntent = store.getIntent(op.intentId());
600 break;
601 case UPDATE:
602 oldIntent = store.getIntent(op.intentId());
603 newIntent = oldIntent; //InnerAssignment: Inner assignments should be avoided.
604 break;
605 default:
606 oldIntent = null;
607 newIntent = null;
608 break;
609 }
610 // add new intent to store (if required)
611 if (newIntent != null) {
612 IntentEvent event = store.createIntent(newIntent);
613 if (event != null) {
614 eventDispatcher.post(event);
615 }
616 }
617 // fetch the old intent's installables from the store
618 if (oldIntent != null) {
619 oldInstallables = store.getInstallableIntents(oldIntent.id());
620 // TODO: remove intent from store after uninstall
621 } else {
622 oldInstallables = null;
623 }
624 }
625
626 Intent oldIntent() {
627 return oldIntent;
628 }
629
630 Intent newIntent() {
631 return newIntent;
632 }
633
634 List<Intent> oldInstallables() {
635 return oldInstallables;
636 }
637
638 List<Intent> newInstallables() {
639 return newInstallables;
640 }
641
642 void setInstallables(List<Intent> installables) {
643 newInstallables = installables;
644 store.setInstallableIntents(newIntent.id(), installables);
645 }
646
647 List<FlowRuleBatchOperation> batches() {
648 return batches;
649 }
650
651 void setBatches(List<FlowRuleBatchOperation> batches) {
652 this.batches = batches;
653 }
654
655 IntentState getState(Intent intent) {
656 return stateMap.get(intent);
657 }
658
659 void setState(Intent intent, IntentState newState) {
660 // TODO: clean this up, or set to debug
661 IntentState oldState = stateMap.get(intent);
662 log.info("intent id: {}, old state: {}, new state: {}",
663 intent.id(), oldState, newState);
664
665 stateMap.put(intent, newState);
666 IntentEvent event = store.setState(intent, newState);
667 if (event != null) {
668 eventDispatcher.post(event);
669 }
670 }
671
672 Map<Intent, IntentState> stateMap() {
673 return stateMap;
674 }
675 }
676
677 private static List<FlowRuleBatchOperation> mergeBatches(Map<IntentOperation,
678 IntentUpdate> intentUpdates) {
679 //TODO test this.
680 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
681 for (IntentUpdate update : intentUpdates.values()) {
682 if (update.batches() == null) {
683 continue;
684 }
685 int i = 0;
686 for (FlowRuleBatchOperation batch : update.batches()) {
687 if (i == batches.size()) {
688 batches.add(batch);
689 } else {
690 FlowRuleBatchOperation existing = batches.get(i);
691 existing.addAll(batch);
692 }
693 i++;
694 }
695 }
696 return batches;
697 }
698
tom85258ee2014-10-07 00:10:02 -0700699 // Auxiliary runnable to perform asynchronous tasks.
700 private class IntentTask implements Runnable {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700701 private final IntentOperations operations;
tom85258ee2014-10-07 00:10:02 -0700702
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700703 public IntentTask(IntentOperations operations) {
704 this.operations = operations;
tom85258ee2014-10-07 00:10:02 -0700705 }
706
707 @Override
708 public void run() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700709 Map<IntentOperation, IntentUpdate> intentUpdates = Maps.newHashMap();
710 for (IntentOperation op : operations.operations()) {
711 intentUpdates.put(op, processIntentOperation(op));
tom85258ee2014-10-07 00:10:02 -0700712 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700713 List<FlowRuleBatchOperation> batches = mergeBatches(intentUpdates);
714 monitorExecutor.execute(new IntentInstallMonitor(operations, intentUpdates, batches));
tom85258ee2014-10-07 00:10:02 -0700715 }
716 }
717
Brian O'Connorcb900f42014-10-07 21:55:33 -0700718 private class IntentInstallMonitor implements Runnable {
719
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700720 private static final long TIMEOUT = 5000; // ms
721 private final IntentOperations ops;
722 private final Map<IntentOperation, IntentUpdate> intentUpdateMap;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700723 private final List<FlowRuleBatchOperation> work;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700724 private Future<CompletedBatchOperation> future;
725 private final long startTime = System.currentTimeMillis();
726 private final long endTime = startTime + TIMEOUT;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700727
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700728 public IntentInstallMonitor(IntentOperations ops,
729 Map<IntentOperation, IntentUpdate> intentUpdateMap,
730 List<FlowRuleBatchOperation> work) {
731 this.ops = ops;
732 this.intentUpdateMap = intentUpdateMap;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700733 this.work = work;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700734 future = applyNextBatch();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700735 }
736
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700737 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700738 * Applies the next batch, and returns the future.
739 *
740 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700741 */
742 private Future<CompletedBatchOperation> applyNextBatch() {
743 if (work.isEmpty()) {
744 return null;
745 }
746 FlowRuleBatchOperation batch = work.remove(0);
747 return flowRuleService.applyBatch(batch);
748 }
749
750 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700751 * Update the intent store with the next status for this intent.
752 */
753 private void updateIntents() {
754 // FIXME we assume everything passes for now.
755 for (IntentUpdate update : intentUpdateMap.values()) {
756 for (Intent intent : update.stateMap().keySet()) {
757 switch (update.getState(intent)) {
758 case INSTALLING:
759 update.setState(intent, INSTALLED);
760 break;
761 case WITHDRAWING:
762 update.setState(intent, WITHDRAWN);
763 // Fall-through
764 case FAILED:
765 store.removeInstalledIntents(intent.id());
766 break;
767
768 case SUBMITTED:
769 case COMPILING:
770 case RECOMPILING:
771 case WITHDRAWN:
772 case INSTALLED:
773 default:
774 //FIXME clean this up (we shouldn't ever get here)
775 log.warn("Bad state: {} for {}", update.getState(intent), intent);
776 break;
777 }
778 }
779 }
780 /*
781 for (IntentOperation op : ops.operations()) {
782 switch (op.type()) {
783 case SUBMIT:
784 store.setState(op.intent(), INSTALLED);
785 break;
786 case WITHDRAW:
787 Intent intent = store.getIntent(op.intentId());
788 store.setState(intent, WITHDRAWN);
789 break;
790 case REPLACE:
791 store.setState(op.intent(), INSTALLED);
792 intent = store.getIntent(op.intentId());
793 store.setState(intent, WITHDRAWN);
794 break;
795 case UPDATE:
796 intent = store.getIntent(op.intentId());
797 store.setState(intent, INSTALLED);
798 break;
799 default:
800 break;
801 }
802 }
803 */
804 /*
805 if (nextState == RECOMPILING) {
806 eventDispatcher.post(store.setState(intent, FAILED));
807 // FIXME try to recompile
808// executor.execute(new IntentTask(nextState, intent));
809 } else if (nextState == INSTALLED || nextState == WITHDRAWN) {
810 eventDispatcher.post(store.setState(intent, nextState));
811 } else {
812 log.warn("Invalid next intent state {} for intent {}", nextState, intent);
813 }*/
814 }
815
816 /**
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700817 * Iterate through the pending futures, and remove them when they have completed.
818 */
819 private void processFutures() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700820 if (future == null) {
821 return; //FIXME look at this
Brian O'Connorcb900f42014-10-07 21:55:33 -0700822 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700823 try {
824 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
825 if (completed.isSuccess()) {
826 future = applyNextBatch();
827 } else {
828 // TODO check if future succeeded and if not report fail items
829 log.warn("Failed items: {}", completed.failedItems());
830 // FIXME revert.... by submitting a new batch
831 //uninstallIntent(intent, RECOMPILING);
832 }
833 } catch (TimeoutException | InterruptedException | ExecutionException te) {
834 //TODO look into error message
835 log.debug("Intallations of intent {} is still pending", ops);
836 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700837 }
838
839 @Override
840 public void run() {
841 processFutures();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700842 if (future == null) {
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700843 // woohoo! we are done!
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700844 updateIntents();
845 batchService.removeIntentOperations(ops);
846 } else if (endTime < System.currentTimeMillis()) {
847 log.warn("Install request timed out");
848// future.cancel(true);
849 // TODO retry and/or report the failure
Brian O'Connorcb900f42014-10-07 21:55:33 -0700850 } else {
851 // resubmit ourselves if we are not done yet
852 monitorExecutor.submit(this);
853 }
854 }
855 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700856
857 private class InternalBatchDelegate implements IntentBatchDelegate {
858 @Override
859 public void execute(IntentOperations operations) {
860 log.info("Execute operations: {}", operations);
861 //FIXME: perhaps we want to track this task so that we can cancel it.
862 executor.execute(new IntentTask(operations));
863 }
864
865 @Override
866 public void cancel(IntentOperations operations) {
867 //FIXME: implement this
868 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
869 }
870 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700871}