blob: 3603a50c0b607f26b5c93c4663f8c4b47830182c [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'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.intent.impl;
Brian O'Connor66630c82014-10-02 21:08:19 -070017
Brian O'Connor0e271dc2015-02-04 18:20:25 -080018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableMap;
Brian O'Connor66630c82014-10-02 21:08:19 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080026import org.onosproject.core.CoreService;
27import org.onosproject.core.IdGenerator;
28import org.onosproject.event.AbstractListenerRegistry;
29import org.onosproject.event.EventDeliveryService;
Brian O'Connor7775bda2015-02-06 15:01:18 -080030import org.onosproject.net.flow.FlowRule;
31import org.onosproject.net.flow.FlowRuleBatchEntry;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.net.flow.FlowRuleBatchOperation;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080033import org.onosproject.net.flow.FlowRuleOperations;
Brian O'Connor7775bda2015-02-06 15:01:18 -080034import org.onosproject.net.flow.FlowRuleOperationsContext;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.net.flow.FlowRuleService;
36import org.onosproject.net.intent.Intent;
37import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080039import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.net.intent.IntentEvent;
41import org.onosproject.net.intent.IntentException;
42import org.onosproject.net.intent.IntentExtensionService;
43import org.onosproject.net.intent.IntentId;
44import org.onosproject.net.intent.IntentInstaller;
45import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.intent.IntentService;
47import org.onosproject.net.intent.IntentState;
48import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080049import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070050import org.slf4j.Logger;
51
Brian O'Connor0e271dc2015-02-04 18:20:25 -080052import java.util.ArrayList;
53import java.util.Collection;
54import java.util.EnumSet;
Brian O'Connor7775bda2015-02-06 15:01:18 -080055import java.util.Iterator;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080056import java.util.List;
57import java.util.Map;
58import java.util.Optional;
59import java.util.concurrent.Callable;
60import java.util.concurrent.ConcurrentHashMap;
61import java.util.concurrent.ConcurrentMap;
62import java.util.concurrent.ExecutionException;
63import java.util.concurrent.ExecutorService;
64import java.util.concurrent.Future;
65import java.util.stream.Collectors;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070066
Brian O'Connorfa81eae2014-10-30 13:20:05 -070067import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080068import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connordb15b042015-02-04 14:59:28 -080069import static java.util.concurrent.Executors.newSingleThreadExecutor;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070070import static org.onlab.util.Tools.namedThreads;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080071import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070072import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070073
74/**
75 * An implementation of Intent Manager.
76 */
77@Component(immediate = true)
78@Service
79public class IntentManager
80 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080081 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070082
83 public static final String INTENT_NULL = "Intent cannot be null";
84 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
85
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080086 private static final int NUM_THREADS = 12;
87
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080088 private static final EnumSet<IntentState> RECOMPILE
89 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080090
91
Brian O'Connor66630c82014-10-02 21:08:19 -070092 // Collections for compiler, installer, and listener are ONOS instance local
93 private final ConcurrentMap<Class<? extends Intent>,
94 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070095 private final ConcurrentMap<Class<? extends Intent>,
96 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070097
98 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070099 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700100
Brian O'Connor520c0522014-11-23 23:50:47 -0800101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected IntentStore store;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700108 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700111 protected EventDeliveryService eventDispatcher;
112
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected FlowRuleService flowRuleService;
115
Brian O'Connor520c0522014-11-23 23:50:47 -0800116
Brian O'Connordb15b042015-02-04 14:59:28 -0800117 private ExecutorService batchExecutor;
118 private ExecutorService workerExecutor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800119
120 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
121 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
122 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
123 private IdGenerator idGenerator;
124
Brian O'Connorb499b352015-02-03 16:46:15 -0800125 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800126
Brian O'Connor66630c82014-10-02 21:08:19 -0700127 @Activate
128 public void activate() {
129 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700130 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700131 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connordb15b042015-02-04 14:59:28 -0800132 batchExecutor = newSingleThreadExecutor(namedThreads("onos-intent-batch"));
133 workerExecutor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-worker-%d"));
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'Connor66630c82014-10-02 21:08:19 -0700143 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connordb15b042015-02-04 14:59:28 -0800144 batchExecutor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800145 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700146 log.info("Stopped");
147 }
148
149 @Override
150 public void submit(Intent intent) {
151 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800152 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
153 //FIXME timestamp?
154 store.addPending(data);
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'Connorcff03322015-02-03 15:28:59 -0800160 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
161 //FIXME timestamp?
162 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700163 }
164
Brian O'Connor66630c82014-10-02 21:08:19 -0700165 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700166 public Iterable<Intent> getIntents() {
167 return store.getIntents();
168 }
169
170 @Override
171 public long getIntentCount() {
172 return store.getIntentCount();
173 }
174
175 @Override
176 public Intent getIntent(IntentId id) {
177 checkNotNull(id, INTENT_ID_NULL);
178 return store.getIntent(id);
179 }
180
181 @Override
182 public IntentState getIntentState(IntentId id) {
183 checkNotNull(id, INTENT_ID_NULL);
184 return store.getIntentState(id);
185 }
186
187 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700188 public List<Intent> getInstallableIntents(IntentId intentId) {
189 checkNotNull(intentId, INTENT_ID_NULL);
190 return store.getInstallableIntents(intentId);
191 }
192
193 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700194 public void addListener(IntentListener listener) {
195 listenerRegistry.addListener(listener);
196 }
197
198 @Override
199 public void removeListener(IntentListener listener) {
200 listenerRegistry.removeListener(listener);
201 }
202
203 @Override
204 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
205 compilers.put(cls, compiler);
206 }
207
208 @Override
209 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
210 compilers.remove(cls);
211 }
212
213 @Override
214 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
215 return ImmutableMap.copyOf(compilers);
216 }
217
218 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700219 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700220 installers.put(cls, installer);
221 }
222
223 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700224 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700225 installers.remove(cls);
226 }
227
228 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700229 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700230 return ImmutableMap.copyOf(installers);
231 }
232
233 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700234 * Returns the corresponding intent compiler to the specified intent.
235 *
236 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700237 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700238 * @return intent compiler corresponding to the specified intent
239 */
240 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
241 @SuppressWarnings("unchecked")
242 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
243 if (compiler == null) {
244 throw new IntentException("no compiler for class " + intent.getClass());
245 }
246 return compiler;
247 }
248
249 /**
250 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700251 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700252 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700253 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700254 * @return intent installer corresponding to the specified installable intent
255 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700256 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700257 @SuppressWarnings("unchecked")
258 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
259 if (installer == null) {
260 throw new IntentException("no installer for class " + intent.getClass());
261 }
262 return installer;
263 }
264
265 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700266 * Compiles an intent recursively.
267 *
268 * @param intent intent
269 * @return result of compilation
270 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800271 List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700272 if (intent.isInstallable()) {
273 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700274 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700275
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700276 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700277 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700278 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800279 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
280 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700281 }
tom85258ee2014-10-07 00:10:02 -0700282 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700283 }
284
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800285 //TODO javadoc
286 //FIXME
Brian O'Connor7775bda2015-02-06 15:01:18 -0800287 FlowRuleOperations coordinate(IntentData pending) {
288 List<Intent> installables = pending.installables();
289 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>(installables.size());
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800290 for (Intent installable : installables) {
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800291 registerSubclassInstallerIfNeeded(installable);
292 //FIXME need to migrate installers to FlowRuleOperations
293 // FIXME need to aggregate the FlowRuleOperations across installables
294 plans.add(getInstaller(installable).install(installable));
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800295 }
Brian O'Connor7775bda2015-02-06 15:01:18 -0800296
297 return merge(plans).build(new FlowRuleOperationsContext() { // FIXME move this out
298 @Override
299 public void onSuccess(FlowRuleOperations ops) {
300 log.info("Completed installing: {}", pending.key());
301 pending.setState(INSTALLED);
302 store.write(pending);
303 }
304
305 @Override
306 public void onError(FlowRuleOperations ops) {
307 //FIXME store.write(pending.setState(BROKEN));
Brian O'Connorba1abbe2015-02-06 15:21:48 -0800308 log.warn("Failed installation: {} {} on {}", pending.key(),
309 pending.intent(), ops);
Brian O'Connor7775bda2015-02-06 15:01:18 -0800310 }
311 });
312 }
313
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800314 /**
315 * Generate a {@link FlowRuleOperations} instance from the specified intent data.
316 *
317 * @param current intent data stored in the store
318 * @return flow rule operations
319 */
320 FlowRuleOperations uninstallCoordinate(IntentData current) {
321 List<Intent> installables = current.installables();
322 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
323 for (Intent installable : installables) {
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800324 plans.add(getInstaller(installable).uninstall(installable));
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800325 }
326
327 return merge(plans).build(new FlowRuleOperationsContext() {
328 @Override
329 public void onSuccess(FlowRuleOperations ops) {
330 log.info("Completed withdrawing: {}", current.key());
331 current.setState(WITHDRAWN);
332 store.write(current);
333 }
334
335 @Override
336 public void onError(FlowRuleOperations ops) {
337 log.warn("Failed withdraw: {}", current.key());
338 current.setState(FAILED);
339 store.write(current);
340 }
341 });
342 }
343
344
Brian O'Connor7775bda2015-02-06 15:01:18 -0800345 // FIXME... needs tests... or maybe it's just perfect
346 private FlowRuleOperations.Builder merge(List<List<FlowRuleBatchOperation>> plans) {
347 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
348 // Build a batch one stage at a time
349 for (int stageNumber = 0;; stageNumber++) {
350 // Get the sub-stage from each plan (List<FlowRuleBatchOperation>)
351 for (Iterator<List<FlowRuleBatchOperation>> itr = plans.iterator(); itr.hasNext();) {
352 List<FlowRuleBatchOperation> plan = itr.next();
353 if (plan.size() <= stageNumber) {
354 // we have consumed all stages from this plan, so remove it
355 itr.remove();
356 continue;
357 }
358 // write operations from this sub-stage into the builder
359 FlowRuleBatchOperation stage = plan.get(stageNumber);
360 for (FlowRuleBatchEntry entry : stage.getOperations()) {
361 FlowRule rule = entry.target();
362 switch (entry.operator()) {
363 case ADD:
364 builder.add(rule);
365 break;
366 case REMOVE:
367 builder.remove(rule);
368 break;
369 case MODIFY:
370 builder.modify(rule);
371 break;
372 default:
373 break;
374 }
375 }
376 }
377 // we are done with the stage, start the next one...
378 if (plans.isEmpty()) {
379 break; // we don't need to start a new stage, we are done.
380 }
381 builder.newStage();
382 }
383 return builder;
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800384 }
385
Brian O'Connor66630c82014-10-02 21:08:19 -0700386 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700387 * Registers an intent compiler of the specified intent if an intent compiler
388 * for the intent is not registered. This method traverses the class hierarchy of
389 * the intent. Once an intent compiler for a parent type is found, this method
390 * registers the found intent compiler.
391 *
392 * @param intent intent
393 */
394 private void registerSubclassCompilerIfNeeded(Intent intent) {
395 if (!compilers.containsKey(intent.getClass())) {
396 Class<?> cls = intent.getClass();
397 while (cls != Object.class) {
398 // As long as we're within the Intent class descendants
399 if (Intent.class.isAssignableFrom(cls)) {
400 IntentCompiler<?> compiler = compilers.get(cls);
401 if (compiler != null) {
402 compilers.put(intent.getClass(), compiler);
403 return;
404 }
405 }
406 cls = cls.getSuperclass();
407 }
408 }
409 }
410
411 /**
412 * Registers an intent installer of the specified intent if an intent installer
413 * for the intent is not registered. This method traverses the class hierarchy of
414 * the intent. Once an intent installer for a parent type is found, this method
415 * registers the found intent installer.
416 *
417 * @param intent intent
418 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700419 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700420 if (!installers.containsKey(intent.getClass())) {
421 Class<?> cls = intent.getClass();
422 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700423 // As long as we're within the Intent class descendants
424 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700425 IntentInstaller<?> installer = installers.get(cls);
426 if (installer != null) {
427 installers.put(intent.getClass(), installer);
428 return;
429 }
430 }
431 cls = cls.getSuperclass();
432 }
433 }
434 }
435
Brian O'Connor66630c82014-10-02 21:08:19 -0700436 // Store delegate to re-post events emitted from the store.
437 private class InternalStoreDelegate implements IntentStoreDelegate {
438 @Override
439 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700440 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700441 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800442
443 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800444 public void process(IntentData data) {
445 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800446 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700447 }
448
Brian O'Connor72a034c2014-11-26 18:24:23 -0800449 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
450 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800451 // Attempt recompilation of the specified intents first.
452 for (IntentId id : intentIds) {
453 Intent intent = store.getIntent(id);
454 if (intent == null) {
455 continue;
456 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800457 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800458 }
459
460 if (compileAllFailed) {
461 // If required, compile all currently failed intents.
462 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800463 IntentState state = getIntentState(intent.id());
464 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800465 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800466 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800467 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800468 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800469 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800470 }
471 }
472 }
473
Brian O'Connorb499b352015-02-03 16:46:15 -0800474 //FIXME
475// for (ApplicationId appId : batches.keySet()) {
476// if (batchService.isLocalLeader(appId)) {
477// execute(batches.get(appId).build());
478// }
479// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800480 }
481
tom95329eb2014-10-06 08:40:06 -0700482 // Topology change delegate
483 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
484 @Override
tom85258ee2014-10-07 00:10:02 -0700485 public void triggerCompile(Iterable<IntentId> intentIds,
486 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800487 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700488 }
tom95329eb2014-10-06 08:40:06 -0700489 }
tom85258ee2014-10-07 00:10:02 -0700490
Brian O'Connorb499b352015-02-03 16:46:15 -0800491 private IntentUpdate createIntentUpdate(IntentData intentData) {
Brian O'Connorb499b352015-02-03 16:46:15 -0800492 switch (intentData.state()) {
493 case INSTALL_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800494 return new InstallRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800495 case WITHDRAW_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800496 return new WithdrawRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800497 // fallthrough
498 case COMPILING:
499 case INSTALLING:
500 case INSTALLED:
501 case RECOMPILING:
502 case WITHDRAWING:
503 case WITHDRAWN:
504 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800505 default:
506 // illegal state
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800507 return new CompilingFailed(intentData);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700508 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700509 }
510
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800511 private Future<CompletedIntentUpdate> submitIntentData(IntentData data) {
512 return workerExecutor.submit(new IntentWorker(data));
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800513 }
514
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800515 private class IntentBatchPreprocess implements Runnable {
516
517 // TODO make this configurable
518 private static final int TIMEOUT_PER_OP = 500; // ms
519 protected static final int MAX_ATTEMPTS = 3;
520
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800521 protected final Collection<IntentData> data;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800522
523 // future holding current FlowRuleBatch installation result
524 protected final long startTime = System.currentTimeMillis();
525 protected final long endTime;
526
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800527 private IntentBatchPreprocess(Collection<IntentData> data, long endTime) {
528 this.data = checkNotNull(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800529 this.endTime = endTime;
530 }
531
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800532 public IntentBatchPreprocess(Collection<IntentData> data) {
533 this(data, System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800534 }
535
536 // FIXME compute reasonable timeouts
537 protected long calculateTimeoutLimit() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800538 return System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800539 }
540
541 @Override
542 public void run() {
543 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800544 /*
545 1. wrap each intentdata in a runnable and submit
546 2. wait for completion of all the work
547 3. accumulate results and submit batch write of IntentData to store
548 (we can also try to update these individually)
549 */
550 submitUpdates(waitForFutures(createIntentUpdates()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800551 } catch (Exception e) {
552 log.error("Error submitting batches:", e);
553 // FIXME incomplete Intents should be cleaned up
554 // (transition to FAILED, etc.)
555
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800556 // the batch has failed
557 // TODO: maybe we should do more?
558 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800559 //FIXME
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800560// batchService.removeIntentOperations(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800561 }
562 }
563
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800564 private List<Future<CompletedIntentUpdate>> createIntentUpdates() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800565 return data.stream()
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800566 .map(IntentManager.this::submitIntentData)
567 .collect(Collectors.toList());
568 }
569
570 private List<CompletedIntentUpdate> waitForFutures(List<Future<CompletedIntentUpdate>> futures) {
571 ImmutableList.Builder<CompletedIntentUpdate> updateBuilder = ImmutableList.builder();
572 for (Future<CompletedIntentUpdate> future : futures) {
573 try {
574 updateBuilder.add(future.get());
575 } catch (InterruptedException | ExecutionException e) {
576 //FIXME
577 log.warn("Future failed: {}", e);
578 }
579 }
580 return updateBuilder.build();
581 }
582
583 private void submitUpdates(List<CompletedIntentUpdate> updates) {
584 store.batchWrite(updates.stream()
585 .map(CompletedIntentUpdate::data)
586 .collect(Collectors.toList()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800587 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800588 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800589
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800590 private final class IntentWorker implements Callable<CompletedIntentUpdate> {
Brian O'Connordb15b042015-02-04 14:59:28 -0800591
592 private final IntentData data;
593
594 private IntentWorker(IntentData data) {
595 this.data = data;
596 }
597
598 @Override
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800599 public CompletedIntentUpdate call() throws Exception {
Brian O'Connordb15b042015-02-04 14:59:28 -0800600 IntentUpdate update = createIntentUpdate(data);
601 Optional<IntentUpdate> currentPhase = Optional.of(update);
602 IntentUpdate previousPhase = update;
603
604 while (currentPhase.isPresent()) {
605 previousPhase = currentPhase.get();
606 currentPhase = previousPhase.execute();
607 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800608 return (CompletedIntentUpdate) previousPhase;
Brian O'Connor427a1762014-11-19 18:40:32 -0800609 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700610 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700611
612 private class InternalBatchDelegate implements IntentBatchDelegate {
613 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800614 public void execute(Collection<IntentData> operations) {
615 log.info("Execute {} operation(s).", operations.size());
616 log.debug("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800617 batchExecutor.execute(new IntentBatchPreprocess(operations));
618 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700619 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700620 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800621
622// /////////**************************///////////////////
623// FIXME Need to build and monitor contexts from FlowRuleService
624//
625// // TODO: better naming
626// private class IntentBatchApplyFirst extends IntentBatchPreprocess {
627//
628// protected final List<CompletedIntentUpdate> intentUpdates;
629// protected final int installAttempt;
630// protected Future<CompletedBatchOperation> future;
631//
632// IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
633// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
634// super(operations, endTime);
635// this.intentUpdates = ImmutableList.copyOf(intentUpdates);
636// this.future = future;
637// this.installAttempt = installAttempt;
638// }
639//
640// @Override
641// public void run() {
642// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
643// new IntentBatchProcessFutures(data, intentUpdates, endTime, installAttempt, future).run();
644// }
645//
646// /**
647// * Builds and applies the next batch, and returns the future.
648// *
649// * @return Future for next batch
650// */
651// protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
652// //TODO test this. (also, maybe save this batch)
653//
654// FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
655// if (batch.size() > 0) {
656// //FIXME apply batch might throw an exception
657// return flowRuleService.applyBatch(batch);
658// } else {
659// return null;
660// }
661// }
662//
663// private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
664// FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
665// for (CompletedIntentUpdate update : intentUpdates) {
666// FlowRuleBatchOperation currentBatch = update.currentBatch();
667// if (currentBatch != null) {
668// batch.addAll(currentBatch);
669// }
670// }
671// return batch;
672// }
673//
674// protected void abandonShip() {
675// // the batch has failed
676// // TODO: maybe we should do more?
677// log.error("Walk the plank, matey...");
678// future = null;
679// //FIXME
680// //batchService.removeIntentOperations(data);
681// }
682// }
683//
684// // TODO: better naming
685// private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
686//
687// IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
688// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
689// super(operations, intentUpdates, endTime, installAttempt, future);
690// }
691//
692// @Override
693// public void run() {
694// try {
695// Future<CompletedBatchOperation> future = processFutures();
696// if (future == null) {
697// // there are no outstanding batches; we are done
698// //FIXME
699// return; //?
700// //batchService.removeIntentOperations(data);
701// } else if (System.currentTimeMillis() > endTime) {
702// // - cancel current FlowRuleBatch and resubmit again
703// retry();
704// } else {
705// // we are not done yet, yield the thread by resubmitting ourselves
706// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, endTime,
707// installAttempt, future));
708// }
709// } catch (Exception e) {
710// log.error("Error submitting batches:", e);
711// // FIXME incomplete Intents should be cleaned up
712// // (transition to FAILED, etc.)
713// abandonShip();
714// }
715// }
716//
717// /**
718// * Iterate through the pending futures, and remove them when they have completed.
719// */
720// private Future<CompletedBatchOperation> processFutures() {
721// try {
722// CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
723// updateBatches(completed);
724// return applyNextBatch(intentUpdates);
725// } catch (TimeoutException | InterruptedException te) {
726// log.trace("Installation of intents are still pending: {}", data);
727// return future;
728// } catch (ExecutionException e) {
729// log.warn("Execution of batch failed: {}", data, e);
730// abandonShip();
731// return future;
732// }
733// }
734//
735// private void updateBatches(CompletedBatchOperation completed) {
736// if (completed.isSuccess()) {
737// for (CompletedIntentUpdate update : intentUpdates) {
738// update.batchSuccess();
739// }
740// } else {
741// // entire batch has been reverted...
742// log.debug("Failed items: {}", completed.failedItems());
743// log.debug("Failed ids: {}", completed.failedIds());
744//
745// for (Long id : completed.failedIds()) {
746// IntentId targetId = IntentId.valueOf(id);
747// for (CompletedIntentUpdate update : intentUpdates) {
748// for (Intent intent : update.allInstallables()) {
749// if (intent.id().equals(targetId)) {
750// update.batchFailed();
751// break;
752// }
753// }
754// }
755// // don't increment the non-failed items, as they have been reverted.
756// }
757// }
758// }
759//
760// private void retry() {
761// log.debug("Execution timed out, retrying.");
762// if (future.cancel(true)) { // cancel success; batch is reverted
763// // reset the timer
764// long timeLimit = calculateTimeoutLimit();
765// int attempts = installAttempt + 1;
766// if (attempts == MAX_ATTEMPTS) {
767// log.warn("Install request timed out: {}", data);
768// for (CompletedIntentUpdate update : intentUpdates) {
769// update.batchFailed();
770// }
771// } else if (attempts > MAX_ATTEMPTS) {
772// abandonShip();
773// return;
774// }
775// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
776// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, timeLimit, attempts, future));
777// } else {
778// log.error("Cancelling FlowRuleBatch failed.");
779// abandonShip();
780// }
781// }
782// }
Brian O'Connor66630c82014-10-02 21:08:19 -0700783}