blob: fb6aa40ac67ed2092ae28876e43e010c63bce0dd [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
Ray Milkeye97ede92014-11-20 10:43:12 -080018import java.util.ArrayList;
19import java.util.Collections;
20import java.util.List;
21import java.util.Map;
22import java.util.concurrent.ConcurrentHashMap;
23import java.util.concurrent.ConcurrentMap;
24import java.util.concurrent.ExecutionException;
25import java.util.concurrent.ExecutorService;
26import java.util.concurrent.Future;
27import java.util.concurrent.TimeUnit;
28import java.util.concurrent.TimeoutException;
29
Brian O'Connor66630c82014-10-02 21:08:19 -070030import org.apache.felix.scr.annotations.Activate;
31import org.apache.felix.scr.annotations.Component;
32import org.apache.felix.scr.annotations.Deactivate;
33import org.apache.felix.scr.annotations.Reference;
34import org.apache.felix.scr.annotations.ReferenceCardinality;
35import org.apache.felix.scr.annotations.Service;
Brian O'Connor520c0522014-11-23 23:50:47 -080036import org.onlab.onos.core.CoreService;
37import org.onlab.onos.core.IdGenerator;
Brian O'Connor66630c82014-10-02 21:08:19 -070038import org.onlab.onos.event.AbstractListenerRegistry;
39import org.onlab.onos.event.EventDeliveryService;
Brian O'Connorcb900f42014-10-07 21:55:33 -070040import org.onlab.onos.net.flow.CompletedBatchOperation;
Brian O'Connorf2dbde52014-10-10 16:20:24 -070041import org.onlab.onos.net.flow.FlowRuleBatchOperation;
42import org.onlab.onos.net.flow.FlowRuleService;
Brian O'Connor66630c82014-10-02 21:08:19 -070043import org.onlab.onos.net.intent.Intent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070044import org.onlab.onos.net.intent.IntentBatchDelegate;
45import org.onlab.onos.net.intent.IntentBatchService;
Brian O'Connor66630c82014-10-02 21:08:19 -070046import org.onlab.onos.net.intent.IntentCompiler;
47import org.onlab.onos.net.intent.IntentEvent;
48import org.onlab.onos.net.intent.IntentException;
49import org.onlab.onos.net.intent.IntentExtensionService;
50import org.onlab.onos.net.intent.IntentId;
51import org.onlab.onos.net.intent.IntentInstaller;
52import org.onlab.onos.net.intent.IntentListener;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070053import org.onlab.onos.net.intent.IntentOperation;
Brian O'Connor66630c82014-10-02 21:08:19 -070054import org.onlab.onos.net.intent.IntentOperations;
55import org.onlab.onos.net.intent.IntentService;
56import org.onlab.onos.net.intent.IntentState;
57import org.onlab.onos.net.intent.IntentStore;
58import org.onlab.onos.net.intent.IntentStoreDelegate;
59import org.slf4j.Logger;
60
Ray Milkeye97ede92014-11-20 10:43:12 -080061import com.google.common.collect.ImmutableList;
62import com.google.common.collect.ImmutableMap;
63import com.google.common.collect.Lists;
64import com.google.common.collect.Maps;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070065
66import static com.google.common.base.Preconditions.checkArgument;
67import static com.google.common.base.Preconditions.checkNotNull;
68import static java.util.concurrent.Executors.newSingleThreadExecutor;
Ray Milkeye97ede92014-11-20 10:43:12 -080069import static org.onlab.onos.net.intent.IntentState.COMPILING;
70import static org.onlab.onos.net.intent.IntentState.FAILED;
71import static org.onlab.onos.net.intent.IntentState.INSTALLED;
72import static org.onlab.onos.net.intent.IntentState.INSTALLING;
73import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
74import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070075import static org.onlab.util.Tools.namedThreads;
76import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070077
78/**
79 * An implementation of Intent Manager.
80 */
81@Component(immediate = true)
82@Service
83public class IntentManager
84 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080085 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070086
87 public static final String INTENT_NULL = "Intent cannot be null";
88 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
89
90 // Collections for compiler, installer, and listener are ONOS instance local
91 private final ConcurrentMap<Class<? extends Intent>,
92 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070093 private final ConcurrentMap<Class<? extends Intent>,
94 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070095
96 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070097 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070098
Brian O'Connor520c0522014-11-23 23:50:47 -080099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected IntentStore store;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700106 protected IntentBatchService batchService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700109 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700112 protected EventDeliveryService eventDispatcher;
113
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected FlowRuleService flowRuleService;
116
Brian O'Connor520c0522014-11-23 23:50:47 -0800117
118 private ExecutorService executor;
119 private ExecutorService monitorExecutor;
120
121 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
122 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
123 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
124 private IdGenerator idGenerator;
125
Brian O'Connor66630c82014-10-02 21:08:19 -0700126 @Activate
127 public void activate() {
128 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700129 trackerService.setDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700130 batchService.setDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700131 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700132 executor = newSingleThreadExecutor(namedThreads("onos-intents"));
133 monitorExecutor = newSingleThreadExecutor(namedThreads("onos-intent-monitor"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800134 idGenerator = coreService.getIdGenerator("intent-ids");
135 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700136 log.info("Started");
137 }
138
139 @Deactivate
140 public void deactivate() {
141 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700142 trackerService.unsetDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700143 batchService.unsetDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700144 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700145 executor.shutdown();
146 monitorExecutor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800147 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700148 log.info("Stopped");
149 }
150
151 @Override
152 public void submit(Intent intent) {
153 checkNotNull(intent, INTENT_NULL);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700154 execute(IntentOperations.builder().addSubmitOperation(intent).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700155 }
156
157 @Override
158 public void withdraw(Intent intent) {
159 checkNotNull(intent, INTENT_NULL);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700160 execute(IntentOperations.builder().addWithdrawOperation(intent.id()).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700161 }
162
Brian O'Connor66630c82014-10-02 21:08:19 -0700163 @Override
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700164 public void replace(IntentId oldIntentId, Intent newIntent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700165 checkNotNull(oldIntentId, INTENT_ID_NULL);
166 checkNotNull(newIntent, INTENT_NULL);
167 execute(IntentOperations.builder()
Ray Milkeye97ede92014-11-20 10:43:12 -0800168 .addReplaceOperation(oldIntentId, newIntent)
169 .build());
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700170 }
171
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700172 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700173 public void execute(IntentOperations operations) {
Brian O'Connore2ff25a2014-11-18 19:25:43 -0800174 if (operations.operations().isEmpty()) {
175 return;
176 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700177 batchService.addIntentOperations(operations);
Brian O'Connor66630c82014-10-02 21:08:19 -0700178 }
179
180 @Override
181 public Iterable<Intent> getIntents() {
182 return store.getIntents();
183 }
184
185 @Override
186 public long getIntentCount() {
187 return store.getIntentCount();
188 }
189
190 @Override
191 public Intent getIntent(IntentId id) {
192 checkNotNull(id, INTENT_ID_NULL);
193 return store.getIntent(id);
194 }
195
196 @Override
197 public IntentState getIntentState(IntentId id) {
198 checkNotNull(id, INTENT_ID_NULL);
199 return store.getIntentState(id);
200 }
201
202 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700203 public List<Intent> getInstallableIntents(IntentId intentId) {
204 checkNotNull(intentId, INTENT_ID_NULL);
205 return store.getInstallableIntents(intentId);
206 }
207
208 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700209 public void addListener(IntentListener listener) {
210 listenerRegistry.addListener(listener);
211 }
212
213 @Override
214 public void removeListener(IntentListener listener) {
215 listenerRegistry.removeListener(listener);
216 }
217
218 @Override
219 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
220 compilers.put(cls, compiler);
221 }
222
223 @Override
224 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
225 compilers.remove(cls);
226 }
227
228 @Override
229 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
230 return ImmutableMap.copyOf(compilers);
231 }
232
233 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700234 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700235 installers.put(cls, installer);
236 }
237
238 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700239 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700240 installers.remove(cls);
241 }
242
243 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700244 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700245 return ImmutableMap.copyOf(installers);
246 }
247
248 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700249 * Returns the corresponding intent compiler to the specified intent.
250 *
251 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700252 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 * @return intent compiler corresponding to the specified intent
254 */
255 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
256 @SuppressWarnings("unchecked")
257 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
258 if (compiler == null) {
259 throw new IntentException("no compiler for class " + intent.getClass());
260 }
261 return compiler;
262 }
263
264 /**
265 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700266 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700267 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700268 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700269 * @return intent installer corresponding to the specified installable intent
270 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700271 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700272 @SuppressWarnings("unchecked")
273 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
274 if (installer == null) {
275 throw new IntentException("no installer for class " + intent.getClass());
276 }
277 return installer;
278 }
279
280 /**
tom85258ee2014-10-07 00:10:02 -0700281 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700282 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700283 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700284 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700285 private void executeCompilingPhase(IntentUpdate update) {
286 Intent intent = update.newIntent();
tom85258ee2014-10-07 00:10:02 -0700287 // Indicate that the intent is entering the compiling phase.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700288 update.setState(intent, COMPILING);
tom85258ee2014-10-07 00:10:02 -0700289
290 try {
291 // Compile the intent into installable derivatives.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700292 List<Intent> installables = compileIntent(intent, update);
tom85258ee2014-10-07 00:10:02 -0700293
294 // If all went well, associate the resulting list of installable
295 // intents with the top-level intent and proceed to install.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700296 update.setInstallables(installables);
297 } catch (IntentException e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700298 log.warn("Unable to compile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700299
tom85258ee2014-10-07 00:10:02 -0700300 // If compilation failed, mark the intent as failed.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700301 update.setState(intent, FAILED);
tom85258ee2014-10-07 00:10:02 -0700302 }
303 }
304
Brian O'Connorcb900f42014-10-07 21:55:33 -0700305 /**
306 * Compiles an intent recursively.
307 *
308 * @param intent intent
309 * @return result of compilation
310 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700311 private List<Intent> compileIntent(Intent intent, IntentUpdate update) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700312 if (intent.isInstallable()) {
313 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700314 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700315
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700316 registerSubclassCompilerIfNeeded(intent);
317 List<Intent> previous = update.oldInstallables();
318 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700319 List<Intent> installable = new ArrayList<>();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700320 for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) {
321 installable.addAll(compileIntent(compiled, update));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700322 }
tom85258ee2014-10-07 00:10:02 -0700323 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700324 }
325
326 /**
tom85258ee2014-10-07 00:10:02 -0700327 * Installs all installable intents associated with the specified top-level
328 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700329 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700330 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700331 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700332 private void executeInstallingPhase(IntentUpdate update) {
333 if (update.newInstallables() == null) {
334 //no failed intents allowed past this point...
335 return;
tom85258ee2014-10-07 00:10:02 -0700336 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700337 // Indicate that the intent is entering the installing phase.
338 update.setState(update.newIntent(), INSTALLING);
339
340 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
341 for (Intent installable : update.newInstallables()) {
342 registerSubclassInstallerIfNeeded(installable);
343 trackerService.addTrackedResources(update.newIntent().id(),
344 installable.resources());
345 try {
346 batches.addAll(getInstaller(installable).install(installable));
347 } catch (IntentException e) {
348 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
Brian O'Connor427a1762014-11-19 18:40:32 -0800349 trackerService.removeTrackedResources(update.newIntent().id(),
350 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700351 //FIXME we failed... intent should be recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700352 }
353 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800354 update.addBatches(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700355 }
356
357 /**
358 * Uninstalls the specified intent by uninstalling all of its associated
359 * installable derivatives.
360 *
361 * @param update intent update
362 */
363 private void executeWithdrawingPhase(IntentUpdate update) {
364 if (!update.oldIntent().equals(update.newIntent())) {
365 update.setState(update.oldIntent(), WITHDRAWING);
366 } // else newIntent is FAILED
Brian O'Connor427a1762014-11-19 18:40:32 -0800367 update.addBatches(uninstallIntent(update.oldIntent(), update.oldInstallables()));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700368 }
369
370 /**
371 * Uninstalls all installable intents associated with the given intent.
372 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800373 * @param intent intent
374 * @param installables installable intents
375 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700376 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800377 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
378 if (installables == null) {
379 return Collections.emptyList();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700380 }
381 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800382 for (Intent installable : installables) {
383 trackerService.removeTrackedResources(intent.id(),
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700384 installable.resources());
385 try {
386 batches.addAll(getInstaller(installable).uninstall(installable));
387 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800388 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700389 // TODO: this should never happen. but what if it does?
390 }
391 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800392 return batches;
tom85258ee2014-10-07 00:10:02 -0700393 }
394
395 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700396 * Withdraws the old intent and installs the new intent as one operation.
tom85258ee2014-10-07 00:10:02 -0700397 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700398 * @param update intent update
tom85258ee2014-10-07 00:10:02 -0700399 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700400 private void executeReplacementPhase(IntentUpdate update) {
401 checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
402 "Old and New Intent must have equivalent installable intents.");
403 if (!update.oldIntent().equals(update.newIntent())) {
404 // only set the old intent's state if it is different
405 update.setState(update.oldIntent(), WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700406 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700407 update.setState(update.newIntent(), INSTALLING);
408
409 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
410 for (int i = 0; i < update.oldInstallables().size(); i++) {
411 Intent oldInstallable = update.oldInstallables().get(i);
412 Intent newInstallable = update.newInstallables().get(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800413 //FIXME revisit this
414// if (oldInstallable.equals(newInstallable)) {
415// continue;
416// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700417 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
418 "Installable Intent type mismatch.");
419 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
420 trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
421 try {
422 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
423 } catch (IntentException e) {
424 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
425 //FIXME... we failed. need to uninstall (if same) or revert (if different)
Brian O'Connor427a1762014-11-19 18:40:32 -0800426 trackerService.removeTrackedResources(update.newIntent().id(), newInstallable.resources());
427 update.setState(update.newIntent(), FAILED);
428 batches = uninstallIntent(update.oldIntent(), update.oldInstallables());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700429 }
430 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800431 update.addBatches(batches);
tom53945d52014-10-07 11:01:36 -0700432 }
433
434 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700435 * Registers an intent compiler of the specified intent if an intent compiler
436 * for the intent is not registered. This method traverses the class hierarchy of
437 * the intent. Once an intent compiler for a parent type is found, this method
438 * registers the found intent compiler.
439 *
440 * @param intent intent
441 */
442 private void registerSubclassCompilerIfNeeded(Intent intent) {
443 if (!compilers.containsKey(intent.getClass())) {
444 Class<?> cls = intent.getClass();
445 while (cls != Object.class) {
446 // As long as we're within the Intent class descendants
447 if (Intent.class.isAssignableFrom(cls)) {
448 IntentCompiler<?> compiler = compilers.get(cls);
449 if (compiler != null) {
450 compilers.put(intent.getClass(), compiler);
451 return;
452 }
453 }
454 cls = cls.getSuperclass();
455 }
456 }
457 }
458
459 /**
460 * Registers an intent installer of the specified intent if an intent installer
461 * for the intent is not registered. This method traverses the class hierarchy of
462 * the intent. Once an intent installer for a parent type is found, this method
463 * registers the found intent installer.
464 *
465 * @param intent intent
466 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700467 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700468 if (!installers.containsKey(intent.getClass())) {
469 Class<?> cls = intent.getClass();
470 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700471 // As long as we're within the Intent class descendants
472 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700473 IntentInstaller<?> installer = installers.get(cls);
474 if (installer != null) {
475 installers.put(intent.getClass(), installer);
476 return;
477 }
478 }
479 cls = cls.getSuperclass();
480 }
481 }
482 }
483
Brian O'Connor66630c82014-10-02 21:08:19 -0700484 // Store delegate to re-post events emitted from the store.
485 private class InternalStoreDelegate implements IntentStoreDelegate {
486 @Override
487 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700488 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700489 }
490 }
491
tom95329eb2014-10-06 08:40:06 -0700492 // Topology change delegate
493 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
494 @Override
tom85258ee2014-10-07 00:10:02 -0700495 public void triggerCompile(Iterable<IntentId> intentIds,
496 boolean compileAllFailed) {
497 // Attempt recompilation of the specified intents first.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700498 IntentOperations.Builder builder = IntentOperations.builder();
499 for (IntentId id : intentIds) {
500 builder.addUpdateOperation(id);
tom85258ee2014-10-07 00:10:02 -0700501 }
502
503 if (compileAllFailed) {
504 // If required, compile all currently failed intents.
505 for (Intent intent : getIntents()) {
506 if (getIntentState(intent.id()) == FAILED) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700507 builder.addUpdateOperation(intent.id());
tom85258ee2014-10-07 00:10:02 -0700508 }
509 }
tom95329eb2014-10-06 08:40:06 -0700510 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700511 execute(builder.build());
tom95329eb2014-10-06 08:40:06 -0700512 }
tom95329eb2014-10-06 08:40:06 -0700513 }
tom85258ee2014-10-07 00:10:02 -0700514
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700515 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800516 * TODO. rename this...
517 * @param update intent update
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700518 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800519 private void processIntentUpdate(IntentUpdate update) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700520
Brian O'Connor427a1762014-11-19 18:40:32 -0800521 // check to see if the intent needs to be compiled or recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700522 if (update.newIntent() != null) {
523 executeCompilingPhase(update);
524 }
525
526 if (update.oldInstallables() != null && update.newInstallables() != null) {
527 executeReplacementPhase(update);
528 } else if (update.newInstallables() != null) {
529 executeInstallingPhase(update);
530 } else if (update.oldInstallables() != null) {
531 executeWithdrawingPhase(update);
532 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800533 if (update.oldIntent() != null &&
534 !update.oldIntent().equals(update.newIntent())) {
535 // removing failed intent
536 update.setState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700537 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800538// if (update.newIntent() != null) {
539// // TODO assert that next state is failed
540// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700541 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700542 }
543
544 // TODO comments...
545 private class IntentUpdate {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700546 private final Intent oldIntent;
547 private final Intent newIntent;
548 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
549
550 private final List<Intent> oldInstallables;
551 private List<Intent> newInstallables;
Brian O'Connor427a1762014-11-19 18:40:32 -0800552 private final List<FlowRuleBatchOperation> batches = Lists.newLinkedList();
553 private int currentBatch = 0; // TODO: maybe replace with an iterator
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700554
555 IntentUpdate(IntentOperation op) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700556 switch (op.type()) {
557 case SUBMIT:
558 newIntent = op.intent();
559 oldIntent = null;
560 break;
561 case WITHDRAW:
562 newIntent = null;
563 oldIntent = store.getIntent(op.intentId());
564 break;
565 case REPLACE:
566 newIntent = op.intent();
567 oldIntent = store.getIntent(op.intentId());
568 break;
569 case UPDATE:
570 oldIntent = store.getIntent(op.intentId());
Brian O'Connor427a1762014-11-19 18:40:32 -0800571 newIntent = oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700572 break;
573 default:
574 oldIntent = null;
575 newIntent = null;
576 break;
577 }
578 // add new intent to store (if required)
579 if (newIntent != null) {
580 IntentEvent event = store.createIntent(newIntent);
581 if (event != null) {
582 eventDispatcher.post(event);
583 }
584 }
585 // fetch the old intent's installables from the store
586 if (oldIntent != null) {
587 oldInstallables = store.getInstallableIntents(oldIntent.id());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700588 } else {
589 oldInstallables = null;
590 }
591 }
592
593 Intent oldIntent() {
594 return oldIntent;
595 }
596
597 Intent newIntent() {
598 return newIntent;
599 }
600
601 List<Intent> oldInstallables() {
602 return oldInstallables;
603 }
604
605 List<Intent> newInstallables() {
606 return newInstallables;
607 }
608
609 void setInstallables(List<Intent> installables) {
610 newInstallables = installables;
611 store.setInstallableIntents(newIntent.id(), installables);
612 }
613
Brian O'Connor427a1762014-11-19 18:40:32 -0800614 boolean isComplete() {
615 return currentBatch >= batches.size();
616 }
617
618 FlowRuleBatchOperation currentBatch() {
619 return !isComplete() ? batches.get(currentBatch) : null;
620 }
621
622 void incrementBatch(boolean success) {
623 if (success) { // actually increment
624 if (++currentBatch == batches.size()) {
625 finalizeStates();
626 }
627 } else { // the current batch has failed, so recompile
628 // remove the current batch and all remaining
629 for (int i = currentBatch; i < batches.size(); i++) {
630 batches.remove(i);
631 }
632 if (oldIntent != null) {
633 executeWithdrawingPhase(this); // remove the old intent
634 }
635 if (newIntent != null) {
636 setState(newIntent, FAILED);
637 batches.addAll(uninstallIntent(newIntent, newInstallables()));
638 }
639
640 // FIXME: should we try to recompile?
641 }
642 }
643
644 // FIXME make sure this is called!!!
645 private void finalizeStates() {
646 for (Intent intent : stateMap.keySet()) {
647 switch (getState(intent)) {
648 case INSTALLING:
649 setState(intent, INSTALLED);
650 break;
651 case WITHDRAWING:
652 setState(intent, WITHDRAWN);
653 store.removeInstalledIntents(intent.id());
654 //store.removeIntent(intent.id()); // FIXME we die a horrible death here
655 break;
656 case FAILED:
657 store.removeInstalledIntents(intent.id());
658 break;
659
660 // FALLTHROUGH to default from here
661 case SUBMITTED:
662 case COMPILING:
663 case RECOMPILING:
664 case WITHDRAWN:
665 case INSTALLED:
666 default:
667 //FIXME clean this up (we shouldn't ever get here)
668 log.warn("Bad state: {} for {}", getState(intent), intent);
669 break;
670 }
671 }
672 }
673
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700674 List<FlowRuleBatchOperation> batches() {
675 return batches;
676 }
677
Brian O'Connor427a1762014-11-19 18:40:32 -0800678 void addBatches(List<FlowRuleBatchOperation> batches) {
679 this.batches.addAll(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700680 }
681
682 IntentState getState(Intent intent) {
683 return stateMap.get(intent);
684 }
685
686 void setState(Intent intent, IntentState newState) {
687 // TODO: clean this up, or set to debug
688 IntentState oldState = stateMap.get(intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800689 log.debug("intent id: {}, old state: {}, new state: {}",
Ray Milkeye97ede92014-11-20 10:43:12 -0800690 intent.id(), oldState, newState);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700691
692 stateMap.put(intent, newState);
693 IntentEvent event = store.setState(intent, newState);
694 if (event != null) {
695 eventDispatcher.post(event);
696 }
697 }
698
699 Map<Intent, IntentState> stateMap() {
700 return stateMap;
701 }
702 }
703
Brian O'Connorcb900f42014-10-07 21:55:33 -0700704 private class IntentInstallMonitor implements Runnable {
705
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700706 private static final long TIMEOUT = 5000; // ms
Brian O'Connor427a1762014-11-19 18:40:32 -0800707 private static final int MAX_ATTEMPTS = 3;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700708
Brian O'Connor427a1762014-11-19 18:40:32 -0800709 private final IntentOperations ops;
710 private final List<IntentUpdate> intentUpdates = Lists.newArrayList();
711
712 private Future<CompletedBatchOperation> future;
713 private long startTime = System.currentTimeMillis();
714 private long endTime = startTime + TIMEOUT;
715 private int installAttempt;
716
717 public IntentInstallMonitor(IntentOperations ops) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700718 this.ops = ops;
Brian O'Connor427a1762014-11-19 18:40:32 -0800719 }
720
721 private void buildIntentUpdates() {
722 for (IntentOperation op : ops.operations()) {
723 IntentUpdate update = new IntentUpdate(op);
724 intentUpdates.add(update);
725 processIntentUpdate(update);
726 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700727 future = applyNextBatch();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700728 }
729
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700730 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800731 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700732 *
733 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700734 */
735 private Future<CompletedBatchOperation> applyNextBatch() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800736 //TODO test this. (also, maybe save this batch)
737 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList());
738 for (IntentUpdate update : intentUpdates) {
739 if (!update.isComplete()) {
740 batch.addAll(update.currentBatch());
741 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700742 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800743 return (batch.size() > 0) ? flowRuleService.applyBatch(batch) : null;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700744 }
745
Brian O'Connor427a1762014-11-19 18:40:32 -0800746 private void updateBatches(CompletedBatchOperation completed) {
747 if (completed.isSuccess()) {
748 for (IntentUpdate update : intentUpdates) {
749 update.incrementBatch(true);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700750 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700751 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800752 // entire batch has been reverted...
753 log.warn("Failed items: {}", completed.failedItems());
754
755 for (Long id : completed.failedIds()) {
756 IntentId targetId = IntentId.valueOf(id);
757 for (IntentUpdate update : intentUpdates) {
758 List<Intent> installables = Lists.newArrayList(update.newInstallables());
759 installables.addAll(update.oldInstallables());
760 for (Intent intent : installables) {
761 if (intent.id().equals(targetId)) {
762 update.incrementBatch(false);
763 break;
764 }
765 }
766 }
767 // don't increment the non-failed items, as they have been reverted.
768 }
769 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700770 }
771
772 /**
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700773 * Iterate through the pending futures, and remove them when they have completed.
774 */
775 private void processFutures() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700776 if (future == null) {
777 return; //FIXME look at this
Brian O'Connorcb900f42014-10-07 21:55:33 -0700778 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700779 try {
780 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
Brian O'Connor427a1762014-11-19 18:40:32 -0800781 updateBatches(completed);
782 future = applyNextBatch();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700783 } catch (TimeoutException | InterruptedException | ExecutionException te) {
784 //TODO look into error message
Brian O'Connor427a1762014-11-19 18:40:32 -0800785 log.debug("Installation of intents are still pending: {}", ops);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700786 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700787 }
788
Brian O'Connor427a1762014-11-19 18:40:32 -0800789 private void retry() {
790 if (future.cancel(true)) { // cancel success; batch is reverted
791 // reset the timer
792 endTime = System.currentTimeMillis() + TIMEOUT;
793 if (installAttempt++ >= MAX_ATTEMPTS) {
794 log.warn("Install request timed out: {}", ops);
795 for (IntentUpdate update : intentUpdates) {
796 update.incrementBatch(false);
797 }
798 } // else just resubmit the work
799 future = applyNextBatch();
800 monitorExecutor.submit(this);
801 } else {
802 // FIXME
803 // cancel failed... batch is broken; shouldn't happen!
804 // we could manually reverse everything
805 // ... or just core dump and send email to Ali
806 batchService.removeIntentOperations(ops);
807 }
808 }
809
810 boolean isComplete() {
811 // TODO: actually check with the intent update?
812 return future == null;
813 }
814
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700815 @Override
816 public void run() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800817 try {
818 if (intentUpdates.isEmpty()) {
819 // this should only be called on the first iteration
820 // note: this a "expensive", so it is not done in the constructor
821 buildIntentUpdates();
822 }
823 processFutures();
824 if (isComplete()) {
825 // there are no outstanding batches; we are done
826 batchService.removeIntentOperations(ops);
827 } else if (endTime < System.currentTimeMillis()) {
828 retry();
829 } else {
830 // we are not done yet, yield the thread by resubmitting ourselves
831 monitorExecutor.submit(this);
832 }
833 } catch (Exception e) {
834 log.error("Error submitting batches:", e);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700835 }
836 }
837 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700838
839 private class InternalBatchDelegate implements IntentBatchDelegate {
840 @Override
841 public void execute(IntentOperations operations) {
Yuta HIGUCHIfe4367a2014-11-24 19:19:09 -0800842 log.info("Execute {} operation(s).", operations.operations().size());
843 log.debug("Execute operations: {}", operations.operations());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700844 //FIXME: perhaps we want to track this task so that we can cancel it.
Brian O'Connor427a1762014-11-19 18:40:32 -0800845 monitorExecutor.execute(new IntentInstallMonitor(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700846 }
847
848 @Override
849 public void cancel(IntentOperations operations) {
850 //FIXME: implement this
851 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
852 }
853 }
Ray Milkeye97ede92014-11-20 10:43:12 -0800854
Brian O'Connor66630c82014-10-02 21:08:19 -0700855}