blob: 361c2eaaf9c9a5585d10f9ec556392f5f10c2c62 [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;
Brian O'Connor427a1762014-11-19 18:40:32 -080052import java.util.Collections;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070053import java.util.List;
54import java.util.Map;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070055import 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);
Brian O'Connor427a1762014-11-19 18:40:32 -0800333 trackerService.removeTrackedResources(update.newIntent().id(),
334 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700335 //FIXME we failed... intent should be recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700336 }
337 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800338 update.addBatches(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700339 }
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
Brian O'Connor427a1762014-11-19 18:40:32 -0800351 update.addBatches(uninstallIntent(update.oldIntent(), update.oldInstallables()));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700352 }
353
354 /**
355 * Uninstalls all installable intents associated with the given intent.
356 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800357 * @param intent intent
358 * @param installables installable intents
359 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700360 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800361 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
362 if (installables == null) {
363 return Collections.emptyList();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700364 }
365 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800366 for (Intent installable : installables) {
367 trackerService.removeTrackedResources(intent.id(),
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700368 installable.resources());
369 try {
370 batches.addAll(getInstaller(installable).uninstall(installable));
371 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800372 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700373 // TODO: this should never happen. but what if it does?
374 }
375 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800376 return batches;
tom85258ee2014-10-07 00:10:02 -0700377 }
378
379 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700380 * Withdraws the old intent and installs the new intent as one operation.
tom85258ee2014-10-07 00:10:02 -0700381 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700382 * @param update intent update
tom85258ee2014-10-07 00:10:02 -0700383 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700384 private void executeReplacementPhase(IntentUpdate update) {
385 checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
386 "Old and New Intent must have equivalent installable intents.");
387 if (!update.oldIntent().equals(update.newIntent())) {
388 // only set the old intent's state if it is different
389 update.setState(update.oldIntent(), WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700390 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700391 update.setState(update.newIntent(), INSTALLING);
392
393 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
394 for (int i = 0; i < update.oldInstallables().size(); i++) {
395 Intent oldInstallable = update.oldInstallables().get(i);
396 Intent newInstallable = update.newInstallables().get(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800397 //FIXME revisit this
398// if (oldInstallable.equals(newInstallable)) {
399// continue;
400// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700401 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
402 "Installable Intent type mismatch.");
403 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
404 trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
405 try {
406 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
407 } catch (IntentException e) {
408 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
409 //FIXME... we failed. need to uninstall (if same) or revert (if different)
Brian O'Connor427a1762014-11-19 18:40:32 -0800410 trackerService.removeTrackedResources(update.newIntent().id(), newInstallable.resources());
411 update.setState(update.newIntent(), FAILED);
412 batches = uninstallIntent(update.oldIntent(), update.oldInstallables());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700413 }
414 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800415 update.addBatches(batches);
tom53945d52014-10-07 11:01:36 -0700416 }
417
418 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700419 * Registers an intent compiler of the specified intent if an intent compiler
420 * for the intent is not registered. This method traverses the class hierarchy of
421 * the intent. Once an intent compiler for a parent type is found, this method
422 * registers the found intent compiler.
423 *
424 * @param intent intent
425 */
426 private void registerSubclassCompilerIfNeeded(Intent intent) {
427 if (!compilers.containsKey(intent.getClass())) {
428 Class<?> cls = intent.getClass();
429 while (cls != Object.class) {
430 // As long as we're within the Intent class descendants
431 if (Intent.class.isAssignableFrom(cls)) {
432 IntentCompiler<?> compiler = compilers.get(cls);
433 if (compiler != null) {
434 compilers.put(intent.getClass(), compiler);
435 return;
436 }
437 }
438 cls = cls.getSuperclass();
439 }
440 }
441 }
442
443 /**
444 * Registers an intent installer of the specified intent if an intent installer
445 * for the intent is not registered. This method traverses the class hierarchy of
446 * the intent. Once an intent installer for a parent type is found, this method
447 * registers the found intent installer.
448 *
449 * @param intent intent
450 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700451 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700452 if (!installers.containsKey(intent.getClass())) {
453 Class<?> cls = intent.getClass();
454 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700455 // As long as we're within the Intent class descendants
456 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700457 IntentInstaller<?> installer = installers.get(cls);
458 if (installer != null) {
459 installers.put(intent.getClass(), installer);
460 return;
461 }
462 }
463 cls = cls.getSuperclass();
464 }
465 }
466 }
467
Brian O'Connor66630c82014-10-02 21:08:19 -0700468 // Store delegate to re-post events emitted from the store.
469 private class InternalStoreDelegate implements IntentStoreDelegate {
470 @Override
471 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700472 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700473 }
474 }
475
tom95329eb2014-10-06 08:40:06 -0700476 // Topology change delegate
477 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
478 @Override
tom85258ee2014-10-07 00:10:02 -0700479 public void triggerCompile(Iterable<IntentId> intentIds,
480 boolean compileAllFailed) {
481 // Attempt recompilation of the specified intents first.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700482 IntentOperations.Builder builder = IntentOperations.builder();
483 for (IntentId id : intentIds) {
484 builder.addUpdateOperation(id);
tom85258ee2014-10-07 00:10:02 -0700485 }
486
487 if (compileAllFailed) {
488 // If required, compile all currently failed intents.
489 for (Intent intent : getIntents()) {
490 if (getIntentState(intent.id()) == FAILED) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700491 builder.addUpdateOperation(intent.id());
tom85258ee2014-10-07 00:10:02 -0700492 }
493 }
tom95329eb2014-10-06 08:40:06 -0700494 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700495 execute(builder.build());
tom95329eb2014-10-06 08:40:06 -0700496 }
tom95329eb2014-10-06 08:40:06 -0700497 }
tom85258ee2014-10-07 00:10:02 -0700498
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700499 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800500 * TODO. rename this...
501 * @param update intent update
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700502 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800503 private void processIntentUpdate(IntentUpdate update) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700504
Brian O'Connor427a1762014-11-19 18:40:32 -0800505 // check to see if the intent needs to be compiled or recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700506 if (update.newIntent() != null) {
507 executeCompilingPhase(update);
508 }
509
510 if (update.oldInstallables() != null && update.newInstallables() != null) {
511 executeReplacementPhase(update);
512 } else if (update.newInstallables() != null) {
513 executeInstallingPhase(update);
514 } else if (update.oldInstallables() != null) {
515 executeWithdrawingPhase(update);
516 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800517 if (update.oldIntent() != null &&
518 !update.oldIntent().equals(update.newIntent())) {
519 // removing failed intent
520 update.setState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700521 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800522// if (update.newIntent() != null) {
523// // TODO assert that next state is failed
524// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700525 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700526 }
527
528 // TODO comments...
529 private class IntentUpdate {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700530 private final Intent oldIntent;
531 private final Intent newIntent;
532 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
533
534 private final List<Intent> oldInstallables;
535 private List<Intent> newInstallables;
Brian O'Connor427a1762014-11-19 18:40:32 -0800536 private final List<FlowRuleBatchOperation> batches = Lists.newLinkedList();
537 private int currentBatch = 0; // TODO: maybe replace with an iterator
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700538
539 IntentUpdate(IntentOperation op) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700540 switch (op.type()) {
541 case SUBMIT:
542 newIntent = op.intent();
543 oldIntent = null;
544 break;
545 case WITHDRAW:
546 newIntent = null;
547 oldIntent = store.getIntent(op.intentId());
548 break;
549 case REPLACE:
550 newIntent = op.intent();
551 oldIntent = store.getIntent(op.intentId());
552 break;
553 case UPDATE:
554 oldIntent = store.getIntent(op.intentId());
Brian O'Connor427a1762014-11-19 18:40:32 -0800555 newIntent = oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700556 break;
557 default:
558 oldIntent = null;
559 newIntent = null;
560 break;
561 }
562 // add new intent to store (if required)
563 if (newIntent != null) {
564 IntentEvent event = store.createIntent(newIntent);
565 if (event != null) {
566 eventDispatcher.post(event);
567 }
568 }
569 // fetch the old intent's installables from the store
570 if (oldIntent != null) {
571 oldInstallables = store.getInstallableIntents(oldIntent.id());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700572 } else {
573 oldInstallables = null;
574 }
575 }
576
577 Intent oldIntent() {
578 return oldIntent;
579 }
580
581 Intent newIntent() {
582 return newIntent;
583 }
584
585 List<Intent> oldInstallables() {
586 return oldInstallables;
587 }
588
589 List<Intent> newInstallables() {
590 return newInstallables;
591 }
592
593 void setInstallables(List<Intent> installables) {
594 newInstallables = installables;
595 store.setInstallableIntents(newIntent.id(), installables);
596 }
597
Brian O'Connor427a1762014-11-19 18:40:32 -0800598 boolean isComplete() {
599 return currentBatch >= batches.size();
600 }
601
602 FlowRuleBatchOperation currentBatch() {
603 return !isComplete() ? batches.get(currentBatch) : null;
604 }
605
606 void incrementBatch(boolean success) {
607 if (success) { // actually increment
608 if (++currentBatch == batches.size()) {
609 finalizeStates();
610 }
611 } else { // the current batch has failed, so recompile
612 // remove the current batch and all remaining
613 for (int i = currentBatch; i < batches.size(); i++) {
614 batches.remove(i);
615 }
616 if (oldIntent != null) {
617 executeWithdrawingPhase(this); // remove the old intent
618 }
619 if (newIntent != null) {
620 setState(newIntent, FAILED);
621 batches.addAll(uninstallIntent(newIntent, newInstallables()));
622 }
623
624 // FIXME: should we try to recompile?
625 }
626 }
627
628 // FIXME make sure this is called!!!
629 private void finalizeStates() {
630 for (Intent intent : stateMap.keySet()) {
631 switch (getState(intent)) {
632 case INSTALLING:
633 setState(intent, INSTALLED);
634 break;
635 case WITHDRAWING:
636 setState(intent, WITHDRAWN);
637 store.removeInstalledIntents(intent.id());
638 //store.removeIntent(intent.id()); // FIXME we die a horrible death here
639 break;
640 case FAILED:
641 store.removeInstalledIntents(intent.id());
642 break;
643
644 // FALLTHROUGH to default from here
645 case SUBMITTED:
646 case COMPILING:
647 case RECOMPILING:
648 case WITHDRAWN:
649 case INSTALLED:
650 default:
651 //FIXME clean this up (we shouldn't ever get here)
652 log.warn("Bad state: {} for {}", getState(intent), intent);
653 break;
654 }
655 }
656 }
657
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700658 List<FlowRuleBatchOperation> batches() {
659 return batches;
660 }
661
Brian O'Connor427a1762014-11-19 18:40:32 -0800662 void addBatches(List<FlowRuleBatchOperation> batches) {
663 this.batches.addAll(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700664 }
665
666 IntentState getState(Intent intent) {
667 return stateMap.get(intent);
668 }
669
670 void setState(Intent intent, IntentState newState) {
671 // TODO: clean this up, or set to debug
672 IntentState oldState = stateMap.get(intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800673 log.debug("intent id: {}, old state: {}, new state: {}",
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700674 intent.id(), oldState, newState);
675
676 stateMap.put(intent, newState);
677 IntentEvent event = store.setState(intent, newState);
678 if (event != null) {
679 eventDispatcher.post(event);
680 }
681 }
682
683 Map<Intent, IntentState> stateMap() {
684 return stateMap;
685 }
686 }
687
Brian O'Connorcb900f42014-10-07 21:55:33 -0700688 private class IntentInstallMonitor implements Runnable {
689
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700690 private static final long TIMEOUT = 5000; // ms
Brian O'Connor427a1762014-11-19 18:40:32 -0800691 private static final int MAX_ATTEMPTS = 3;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700692
Brian O'Connor427a1762014-11-19 18:40:32 -0800693 private final IntentOperations ops;
694 private final List<IntentUpdate> intentUpdates = Lists.newArrayList();
695
696 private Future<CompletedBatchOperation> future;
697 private long startTime = System.currentTimeMillis();
698 private long endTime = startTime + TIMEOUT;
699 private int installAttempt;
700
701 public IntentInstallMonitor(IntentOperations ops) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700702 this.ops = ops;
Brian O'Connor427a1762014-11-19 18:40:32 -0800703 }
704
705 private void buildIntentUpdates() {
706 for (IntentOperation op : ops.operations()) {
707 IntentUpdate update = new IntentUpdate(op);
708 intentUpdates.add(update);
709 processIntentUpdate(update);
710 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700711 future = applyNextBatch();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700712 }
713
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700714 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800715 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700716 *
717 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700718 */
719 private Future<CompletedBatchOperation> applyNextBatch() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800720 //TODO test this. (also, maybe save this batch)
721 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList());
722 for (IntentUpdate update : intentUpdates) {
723 if (!update.isComplete()) {
724 batch.addAll(update.currentBatch());
725 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700726 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800727 return (batch.size() > 0) ? flowRuleService.applyBatch(batch) : null;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700728 }
729
Brian O'Connor427a1762014-11-19 18:40:32 -0800730 private void updateBatches(CompletedBatchOperation completed) {
731 if (completed.isSuccess()) {
732 for (IntentUpdate update : intentUpdates) {
733 update.incrementBatch(true);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700734 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700735 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800736 // entire batch has been reverted...
737 log.warn("Failed items: {}", completed.failedItems());
738
739 for (Long id : completed.failedIds()) {
740 IntentId targetId = IntentId.valueOf(id);
741 for (IntentUpdate update : intentUpdates) {
742 List<Intent> installables = Lists.newArrayList(update.newInstallables());
743 installables.addAll(update.oldInstallables());
744 for (Intent intent : installables) {
745 if (intent.id().equals(targetId)) {
746 update.incrementBatch(false);
747 break;
748 }
749 }
750 }
751 // don't increment the non-failed items, as they have been reverted.
752 }
753 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700754 }
755
756 /**
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700757 * Iterate through the pending futures, and remove them when they have completed.
758 */
759 private void processFutures() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700760 if (future == null) {
761 return; //FIXME look at this
Brian O'Connorcb900f42014-10-07 21:55:33 -0700762 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700763 try {
764 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
Brian O'Connor427a1762014-11-19 18:40:32 -0800765 updateBatches(completed);
766 future = applyNextBatch();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700767 } catch (TimeoutException | InterruptedException | ExecutionException te) {
768 //TODO look into error message
Brian O'Connor427a1762014-11-19 18:40:32 -0800769 log.debug("Installation of intents are still pending: {}", ops);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700770 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700771 }
772
Brian O'Connor427a1762014-11-19 18:40:32 -0800773 private void retry() {
774 if (future.cancel(true)) { // cancel success; batch is reverted
775 // reset the timer
776 endTime = System.currentTimeMillis() + TIMEOUT;
777 if (installAttempt++ >= MAX_ATTEMPTS) {
778 log.warn("Install request timed out: {}", ops);
779 for (IntentUpdate update : intentUpdates) {
780 update.incrementBatch(false);
781 }
782 } // else just resubmit the work
783 future = applyNextBatch();
784 monitorExecutor.submit(this);
785 } else {
786 // FIXME
787 // cancel failed... batch is broken; shouldn't happen!
788 // we could manually reverse everything
789 // ... or just core dump and send email to Ali
790 batchService.removeIntentOperations(ops);
791 }
792 }
793
794 boolean isComplete() {
795 // TODO: actually check with the intent update?
796 return future == null;
797 }
798
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700799 @Override
800 public void run() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800801 try {
802 if (intentUpdates.isEmpty()) {
803 // this should only be called on the first iteration
804 // note: this a "expensive", so it is not done in the constructor
805 buildIntentUpdates();
806 }
807 processFutures();
808 if (isComplete()) {
809 // there are no outstanding batches; we are done
810 batchService.removeIntentOperations(ops);
811 } else if (endTime < System.currentTimeMillis()) {
812 retry();
813 } else {
814 // we are not done yet, yield the thread by resubmitting ourselves
815 monitorExecutor.submit(this);
816 }
817 } catch (Exception e) {
818 log.error("Error submitting batches:", e);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700819 }
820 }
821 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700822
823 private class InternalBatchDelegate implements IntentBatchDelegate {
824 @Override
825 public void execute(IntentOperations operations) {
826 log.info("Execute operations: {}", operations);
827 //FIXME: perhaps we want to track this task so that we can cancel it.
Brian O'Connor427a1762014-11-19 18:40:32 -0800828 monitorExecutor.execute(new IntentInstallMonitor(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700829 }
830
831 @Override
832 public void cancel(IntentOperations operations) {
833 //FIXME: implement this
834 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
835 }
836 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700837}