blob: e64fe82b26e8c0059dcedf128ce783bb8709a1dd [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) {
158 batchService.addIntentOperations(operations);
Brian O'Connor66630c82014-10-02 21:08:19 -0700159 }
160
161 @Override
162 public Iterable<Intent> getIntents() {
163 return store.getIntents();
164 }
165
166 @Override
167 public long getIntentCount() {
168 return store.getIntentCount();
169 }
170
171 @Override
172 public Intent getIntent(IntentId id) {
173 checkNotNull(id, INTENT_ID_NULL);
174 return store.getIntent(id);
175 }
176
177 @Override
178 public IntentState getIntentState(IntentId id) {
179 checkNotNull(id, INTENT_ID_NULL);
180 return store.getIntentState(id);
181 }
182
183 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700184 public List<Intent> getInstallableIntents(IntentId intentId) {
185 checkNotNull(intentId, INTENT_ID_NULL);
186 return store.getInstallableIntents(intentId);
187 }
188
189 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700190 public void addListener(IntentListener listener) {
191 listenerRegistry.addListener(listener);
192 }
193
194 @Override
195 public void removeListener(IntentListener listener) {
196 listenerRegistry.removeListener(listener);
197 }
198
199 @Override
200 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
201 compilers.put(cls, compiler);
202 }
203
204 @Override
205 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
206 compilers.remove(cls);
207 }
208
209 @Override
210 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
211 return ImmutableMap.copyOf(compilers);
212 }
213
214 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700215 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700216 installers.put(cls, installer);
217 }
218
219 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700220 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700221 installers.remove(cls);
222 }
223
224 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700225 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700226 return ImmutableMap.copyOf(installers);
227 }
228
229 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700230 * Returns the corresponding intent compiler to the specified intent.
231 *
232 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700233 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700234 * @return intent compiler corresponding to the specified intent
235 */
236 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
237 @SuppressWarnings("unchecked")
238 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
239 if (compiler == null) {
240 throw new IntentException("no compiler for class " + intent.getClass());
241 }
242 return compiler;
243 }
244
245 /**
246 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700247 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700248 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700249 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700250 * @return intent installer corresponding to the specified installable intent
251 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700252 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 @SuppressWarnings("unchecked")
254 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
255 if (installer == null) {
256 throw new IntentException("no installer for class " + intent.getClass());
257 }
258 return installer;
259 }
260
261 /**
tom85258ee2014-10-07 00:10:02 -0700262 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700263 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700264 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700265 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700266 private void executeCompilingPhase(IntentUpdate update) {
267 Intent intent = update.newIntent();
tom85258ee2014-10-07 00:10:02 -0700268 // Indicate that the intent is entering the compiling phase.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700269 update.setState(intent, COMPILING);
tom85258ee2014-10-07 00:10:02 -0700270
271 try {
272 // Compile the intent into installable derivatives.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700273 List<Intent> installables = compileIntent(intent, update);
tom85258ee2014-10-07 00:10:02 -0700274
275 // If all went well, associate the resulting list of installable
276 // intents with the top-level intent and proceed to install.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700277 update.setInstallables(installables);
278 } catch (IntentException e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700279 log.warn("Unable to compile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700280
tom85258ee2014-10-07 00:10:02 -0700281 // If compilation failed, mark the intent as failed.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700282 update.setState(intent, FAILED);
tom85258ee2014-10-07 00:10:02 -0700283 }
284 }
285
Brian O'Connorcb900f42014-10-07 21:55:33 -0700286 /**
287 * Compiles an intent recursively.
288 *
289 * @param intent intent
290 * @return result of compilation
291 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700292 private List<Intent> compileIntent(Intent intent, IntentUpdate update) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700293 if (intent.isInstallable()) {
294 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700295 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700296
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700297 registerSubclassCompilerIfNeeded(intent);
298 List<Intent> previous = update.oldInstallables();
299 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700300 List<Intent> installable = new ArrayList<>();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700301 for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) {
302 installable.addAll(compileIntent(compiled, update));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700303 }
tom85258ee2014-10-07 00:10:02 -0700304 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700305 }
306
307 /**
tom85258ee2014-10-07 00:10:02 -0700308 * Installs all installable intents associated with the specified top-level
309 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700310 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700311 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700312 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700313 private void executeInstallingPhase(IntentUpdate update) {
314 if (update.newInstallables() == null) {
315 //no failed intents allowed past this point...
316 return;
tom85258ee2014-10-07 00:10:02 -0700317 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700318 // Indicate that the intent is entering the installing phase.
319 update.setState(update.newIntent(), INSTALLING);
320
321 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
322 for (Intent installable : update.newInstallables()) {
323 registerSubclassInstallerIfNeeded(installable);
324 trackerService.addTrackedResources(update.newIntent().id(),
325 installable.resources());
326 try {
327 batches.addAll(getInstaller(installable).install(installable));
328 } catch (IntentException e) {
329 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
330 //FIXME we failed... intent should be recompiled
331 // TODO: remove resources
332 // recompile!!!
333 }
334 }
335 update.setBatches(batches);
336 }
337
338 /**
339 * Uninstalls the specified intent by uninstalling all of its associated
340 * installable derivatives.
341 *
342 * @param update intent update
343 */
344 private void executeWithdrawingPhase(IntentUpdate update) {
345 if (!update.oldIntent().equals(update.newIntent())) {
346 update.setState(update.oldIntent(), WITHDRAWING);
347 } // else newIntent is FAILED
348 uninstallIntent(update);
349
350 // If all went well, disassociate the top-level intent with its
351 // installable derivatives and mark it as withdrawn.
352 // FIXME need to clean up
353 //store.removeInstalledIntents(intent.id());
354 }
355
356 /**
357 * Uninstalls all installable intents associated with the given intent.
358 *
359 * @param update intent update
360 */
361 //FIXME: need to handle next state properly
362 private void uninstallIntent(IntentUpdate update) {
363 if (update.oldInstallables == null) {
364 return;
365 }
366 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
367 for (Intent installable : update.oldInstallables()) {
368 trackerService.removeTrackedResources(update.oldIntent().id(),
369 installable.resources());
370 try {
371 batches.addAll(getInstaller(installable).uninstall(installable));
372 } catch (IntentException e) {
373 log.warn("Unable to uninstall intent {} due to:", update.oldIntent().id(), e);
374 // TODO: this should never happen. but what if it does?
375 }
376 }
377 update.setBatches(batches);
378 // FIXME: next state for old is WITHDRAWN or FAILED
Brian O'Connor66630c82014-10-02 21:08:19 -0700379 }
380
381 /**
tom85258ee2014-10-07 00:10:02 -0700382 * Recompiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700383 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700384 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700385 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700386 // FIXME: update this to work
387 private void executeRecompilingPhase(IntentUpdate update) {
388 Intent intent = update.newIntent();
tom85258ee2014-10-07 00:10:02 -0700389 // Indicate that the intent is entering the recompiling phase.
390 store.setState(intent, RECOMPILING);
391
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700392 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
tom85258ee2014-10-07 00:10:02 -0700393 try {
394 // Compile the intent into installable derivatives.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700395 List<Intent> installable = compileIntent(intent, update);
tom85258ee2014-10-07 00:10:02 -0700396
397 // If all went well, compare the existing list of installable
398 // intents with the newly compiled list. If they are the same,
399 // bail, out since the previous approach was determined not to
400 // be viable.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700401 // FIXME do we need this?
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700402 List<Intent> originalInstallable = store.getInstallableIntents(intent.id());
tom85258ee2014-10-07 00:10:02 -0700403
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700404 //FIXME let's be smarter about how we perform the update
405 //batches.addAll(uninstallIntent(intent, null));
406
tom85258ee2014-10-07 00:10:02 -0700407 if (Objects.equals(originalInstallable, installable)) {
408 eventDispatcher.post(store.setState(intent, FAILED));
409 } else {
410 // Otherwise, re-associate the newly compiled installable intents
411 // with the top-level intent and kick off installing phase.
Yuta HIGUCHI10a31c32014-10-28 14:42:06 -0700412 store.setInstallableIntents(intent.id(), installable);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700413 // FIXME commented out for now
414 //batches.addAll(executeInstallingPhase(update));
tom85258ee2014-10-07 00:10:02 -0700415 }
416 } catch (Exception e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700417 log.warn("Unable to recompile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700418
tom85258ee2014-10-07 00:10:02 -0700419 // If compilation failed, mark the intent as failed.
420 eventDispatcher.post(store.setState(intent, FAILED));
421 }
422 }
423
424 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700425 * Withdraws the old intent and installs the new intent as one operation.
tom85258ee2014-10-07 00:10:02 -0700426 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700427 * @param update intent update
tom85258ee2014-10-07 00:10:02 -0700428 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700429 private void executeReplacementPhase(IntentUpdate update) {
430 checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
431 "Old and New Intent must have equivalent installable intents.");
432 if (!update.oldIntent().equals(update.newIntent())) {
433 // only set the old intent's state if it is different
434 update.setState(update.oldIntent(), WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700435 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700436 update.setState(update.newIntent(), INSTALLING);
437
438 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
439 for (int i = 0; i < update.oldInstallables().size(); i++) {
440 Intent oldInstallable = update.oldInstallables().get(i);
441 Intent newInstallable = update.newInstallables().get(i);
442 if (oldInstallable.equals(newInstallable)) {
443 continue;
444 }
445 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
446 "Installable Intent type mismatch.");
447 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
448 trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
449 try {
450 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
451 } catch (IntentException e) {
452 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
453 //FIXME... we failed. need to uninstall (if same) or revert (if different)
454 }
455 }
456 update.setBatches(batches);
tom53945d52014-10-07 11:01:36 -0700457 }
458
459 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700460 * Registers an intent compiler of the specified intent if an intent compiler
461 * for the intent is not registered. This method traverses the class hierarchy of
462 * the intent. Once an intent compiler for a parent type is found, this method
463 * registers the found intent compiler.
464 *
465 * @param intent intent
466 */
467 private void registerSubclassCompilerIfNeeded(Intent intent) {
468 if (!compilers.containsKey(intent.getClass())) {
469 Class<?> cls = intent.getClass();
470 while (cls != Object.class) {
471 // As long as we're within the Intent class descendants
472 if (Intent.class.isAssignableFrom(cls)) {
473 IntentCompiler<?> compiler = compilers.get(cls);
474 if (compiler != null) {
475 compilers.put(intent.getClass(), compiler);
476 return;
477 }
478 }
479 cls = cls.getSuperclass();
480 }
481 }
482 }
483
484 /**
485 * Registers an intent installer of the specified intent if an intent installer
486 * for the intent is not registered. This method traverses the class hierarchy of
487 * the intent. Once an intent installer for a parent type is found, this method
488 * registers the found intent installer.
489 *
490 * @param intent intent
491 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700492 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700493 if (!installers.containsKey(intent.getClass())) {
494 Class<?> cls = intent.getClass();
495 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700496 // As long as we're within the Intent class descendants
497 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700498 IntentInstaller<?> installer = installers.get(cls);
499 if (installer != null) {
500 installers.put(intent.getClass(), installer);
501 return;
502 }
503 }
504 cls = cls.getSuperclass();
505 }
506 }
507 }
508
Brian O'Connor66630c82014-10-02 21:08:19 -0700509 // Store delegate to re-post events emitted from the store.
510 private class InternalStoreDelegate implements IntentStoreDelegate {
511 @Override
512 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700513 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700514 }
515 }
516
tom95329eb2014-10-06 08:40:06 -0700517 // Topology change delegate
518 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
519 @Override
tom85258ee2014-10-07 00:10:02 -0700520 public void triggerCompile(Iterable<IntentId> intentIds,
521 boolean compileAllFailed) {
522 // Attempt recompilation of the specified intents first.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700523 IntentOperations.Builder builder = IntentOperations.builder();
524 for (IntentId id : intentIds) {
525 builder.addUpdateOperation(id);
tom85258ee2014-10-07 00:10:02 -0700526 }
527
528 if (compileAllFailed) {
529 // If required, compile all currently failed intents.
530 for (Intent intent : getIntents()) {
531 if (getIntentState(intent.id()) == FAILED) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700532 builder.addUpdateOperation(intent.id());
tom85258ee2014-10-07 00:10:02 -0700533 }
534 }
tom95329eb2014-10-06 08:40:06 -0700535 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700536 execute(builder.build());
tom95329eb2014-10-06 08:40:06 -0700537 }
tom95329eb2014-10-06 08:40:06 -0700538 }
tom85258ee2014-10-07 00:10:02 -0700539
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700540 /**
541 * TODO.
542 * @param op intent operation
543 * @return intent update
544 */
545 private IntentUpdate processIntentOperation(IntentOperation op) {
546 IntentUpdate update = new IntentUpdate(op);
547
548 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 {
559 if (update.oldIntent() != null) {
560 // TODO this shouldn't happen
561 return update; //FIXME
562 }
563 if (update.newIntent() != null) {
564 // TODO assert that next state is failed
565 return update; //FIXME
566 }
567 }
568
569 return update;
570 }
571
572 // TODO comments...
573 private class IntentUpdate {
574 private final IntentOperation op;
575 private final Intent oldIntent;
576 private final Intent newIntent;
577 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
578
579 private final List<Intent> oldInstallables;
580 private List<Intent> newInstallables;
581 private List<FlowRuleBatchOperation> batches;
582
583 IntentUpdate(IntentOperation op) {
584 this.op = op;
585 switch (op.type()) {
586 case SUBMIT:
587 newIntent = op.intent();
588 oldIntent = null;
589 break;
590 case WITHDRAW:
591 newIntent = null;
592 oldIntent = store.getIntent(op.intentId());
593 break;
594 case REPLACE:
595 newIntent = op.intent();
596 oldIntent = store.getIntent(op.intentId());
597 break;
598 case UPDATE:
599 oldIntent = store.getIntent(op.intentId());
600 newIntent = oldIntent; //InnerAssignment: Inner assignments should be avoided.
601 break;
602 default:
603 oldIntent = null;
604 newIntent = null;
605 break;
606 }
607 // add new intent to store (if required)
608 if (newIntent != null) {
609 IntentEvent event = store.createIntent(newIntent);
610 if (event != null) {
611 eventDispatcher.post(event);
612 }
613 }
614 // fetch the old intent's installables from the store
615 if (oldIntent != null) {
616 oldInstallables = store.getInstallableIntents(oldIntent.id());
617 // TODO: remove intent from store after uninstall
618 } else {
619 oldInstallables = null;
620 }
621 }
622
623 Intent oldIntent() {
624 return oldIntent;
625 }
626
627 Intent newIntent() {
628 return newIntent;
629 }
630
631 List<Intent> oldInstallables() {
632 return oldInstallables;
633 }
634
635 List<Intent> newInstallables() {
636 return newInstallables;
637 }
638
639 void setInstallables(List<Intent> installables) {
640 newInstallables = installables;
641 store.setInstallableIntents(newIntent.id(), installables);
642 }
643
644 List<FlowRuleBatchOperation> batches() {
645 return batches;
646 }
647
648 void setBatches(List<FlowRuleBatchOperation> batches) {
649 this.batches = batches;
650 }
651
652 IntentState getState(Intent intent) {
653 return stateMap.get(intent);
654 }
655
656 void setState(Intent intent, IntentState newState) {
657 // TODO: clean this up, or set to debug
658 IntentState oldState = stateMap.get(intent);
659 log.info("intent id: {}, old state: {}, new state: {}",
660 intent.id(), oldState, newState);
661
662 stateMap.put(intent, newState);
663 IntentEvent event = store.setState(intent, newState);
664 if (event != null) {
665 eventDispatcher.post(event);
666 }
667 }
668
669 Map<Intent, IntentState> stateMap() {
670 return stateMap;
671 }
672 }
673
674 private static List<FlowRuleBatchOperation> mergeBatches(Map<IntentOperation,
675 IntentUpdate> intentUpdates) {
676 //TODO test this.
677 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
678 for (IntentUpdate update : intentUpdates.values()) {
679 if (update.batches() == null) {
680 continue;
681 }
682 int i = 0;
683 for (FlowRuleBatchOperation batch : update.batches()) {
684 if (i == batches.size()) {
685 batches.add(batch);
686 } else {
687 FlowRuleBatchOperation existing = batches.get(i);
688 existing.addAll(batch);
689 }
690 i++;
691 }
692 }
693 return batches;
694 }
695
tom85258ee2014-10-07 00:10:02 -0700696 // Auxiliary runnable to perform asynchronous tasks.
697 private class IntentTask implements Runnable {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700698 private final IntentOperations operations;
tom85258ee2014-10-07 00:10:02 -0700699
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700700 public IntentTask(IntentOperations operations) {
701 this.operations = operations;
tom85258ee2014-10-07 00:10:02 -0700702 }
703
704 @Override
705 public void run() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700706 Map<IntentOperation, IntentUpdate> intentUpdates = Maps.newHashMap();
707 for (IntentOperation op : operations.operations()) {
708 intentUpdates.put(op, processIntentOperation(op));
tom85258ee2014-10-07 00:10:02 -0700709 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700710 List<FlowRuleBatchOperation> batches = mergeBatches(intentUpdates);
711 monitorExecutor.execute(new IntentInstallMonitor(operations, intentUpdates, batches));
tom85258ee2014-10-07 00:10:02 -0700712 }
713 }
714
Brian O'Connorcb900f42014-10-07 21:55:33 -0700715 private class IntentInstallMonitor implements Runnable {
716
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700717 private static final long TIMEOUT = 5000; // ms
718 private final IntentOperations ops;
719 private final Map<IntentOperation, IntentUpdate> intentUpdateMap;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700720 private final List<FlowRuleBatchOperation> work;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700721 private Future<CompletedBatchOperation> future;
722 private final long startTime = System.currentTimeMillis();
723 private final long endTime = startTime + TIMEOUT;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700724
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700725 public IntentInstallMonitor(IntentOperations ops,
726 Map<IntentOperation, IntentUpdate> intentUpdateMap,
727 List<FlowRuleBatchOperation> work) {
728 this.ops = ops;
729 this.intentUpdateMap = intentUpdateMap;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700730 this.work = work;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700731 future = applyNextBatch();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700732 }
733
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700734 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700735 * Applies the next batch, and returns the future.
736 *
737 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700738 */
739 private Future<CompletedBatchOperation> applyNextBatch() {
740 if (work.isEmpty()) {
741 return null;
742 }
743 FlowRuleBatchOperation batch = work.remove(0);
744 return flowRuleService.applyBatch(batch);
745 }
746
747 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700748 * Update the intent store with the next status for this intent.
749 */
750 private void updateIntents() {
751 // FIXME we assume everything passes for now.
752 for (IntentUpdate update : intentUpdateMap.values()) {
753 for (Intent intent : update.stateMap().keySet()) {
754 switch (update.getState(intent)) {
755 case INSTALLING:
756 update.setState(intent, INSTALLED);
757 break;
758 case WITHDRAWING:
759 update.setState(intent, WITHDRAWN);
760 // Fall-through
761 case FAILED:
762 store.removeInstalledIntents(intent.id());
763 break;
764
765 case SUBMITTED:
766 case COMPILING:
767 case RECOMPILING:
768 case WITHDRAWN:
769 case INSTALLED:
770 default:
771 //FIXME clean this up (we shouldn't ever get here)
772 log.warn("Bad state: {} for {}", update.getState(intent), intent);
773 break;
774 }
775 }
776 }
777 /*
778 for (IntentOperation op : ops.operations()) {
779 switch (op.type()) {
780 case SUBMIT:
781 store.setState(op.intent(), INSTALLED);
782 break;
783 case WITHDRAW:
784 Intent intent = store.getIntent(op.intentId());
785 store.setState(intent, WITHDRAWN);
786 break;
787 case REPLACE:
788 store.setState(op.intent(), INSTALLED);
789 intent = store.getIntent(op.intentId());
790 store.setState(intent, WITHDRAWN);
791 break;
792 case UPDATE:
793 intent = store.getIntent(op.intentId());
794 store.setState(intent, INSTALLED);
795 break;
796 default:
797 break;
798 }
799 }
800 */
801 /*
802 if (nextState == RECOMPILING) {
803 eventDispatcher.post(store.setState(intent, FAILED));
804 // FIXME try to recompile
805// executor.execute(new IntentTask(nextState, intent));
806 } else if (nextState == INSTALLED || nextState == WITHDRAWN) {
807 eventDispatcher.post(store.setState(intent, nextState));
808 } else {
809 log.warn("Invalid next intent state {} for intent {}", nextState, intent);
810 }*/
811 }
812
813 /**
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700814 * Iterate through the pending futures, and remove them when they have completed.
815 */
816 private void processFutures() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700817 if (future == null) {
818 return; //FIXME look at this
Brian O'Connorcb900f42014-10-07 21:55:33 -0700819 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700820 try {
821 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
822 if (completed.isSuccess()) {
823 future = applyNextBatch();
824 } else {
825 // TODO check if future succeeded and if not report fail items
826 log.warn("Failed items: {}", completed.failedItems());
827 // FIXME revert.... by submitting a new batch
828 //uninstallIntent(intent, RECOMPILING);
829 }
830 } catch (TimeoutException | InterruptedException | ExecutionException te) {
831 //TODO look into error message
832 log.debug("Intallations of intent {} is still pending", ops);
833 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700834 }
835
836 @Override
837 public void run() {
838 processFutures();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700839 if (future == null) {
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700840 // woohoo! we are done!
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700841 updateIntents();
842 batchService.removeIntentOperations(ops);
843 } else if (endTime < System.currentTimeMillis()) {
844 log.warn("Install request timed out");
845// future.cancel(true);
846 // TODO retry and/or report the failure
Brian O'Connorcb900f42014-10-07 21:55:33 -0700847 } else {
848 // resubmit ourselves if we are not done yet
849 monitorExecutor.submit(this);
850 }
851 }
852 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700853
854 private class InternalBatchDelegate implements IntentBatchDelegate {
855 @Override
856 public void execute(IntentOperations operations) {
857 log.info("Execute operations: {}", operations);
858 //FIXME: perhaps we want to track this task so that we can cancel it.
859 executor.execute(new IntentTask(operations));
860 }
861
862 @Override
863 public void cancel(IntentOperations operations) {
864 //FIXME: implement this
865 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
866 }
867 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700868}