blob: 8fbf252f7b1a0d8e731c9d7be07f4d936945545a [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) {
291 try {
292 registerSubclassInstallerIfNeeded(installable);
293 //FIXME need to migrate installers to FlowRuleOperations
294 // FIXME need to aggregate the FlowRuleOperations across installables
Brian O'Connor7775bda2015-02-06 15:01:18 -0800295 plans.add(getInstaller(installable).install(installable));
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800296 } catch (Exception e) { // TODO this should be IntentException
297 throw new FlowRuleBatchOperationConversionException(null/*FIXME*/, e);
298 }
299 }
Brian O'Connor7775bda2015-02-06 15:01:18 -0800300
301 return merge(plans).build(new FlowRuleOperationsContext() { // FIXME move this out
302 @Override
303 public void onSuccess(FlowRuleOperations ops) {
304 log.info("Completed installing: {}", pending.key());
305 pending.setState(INSTALLED);
306 store.write(pending);
307 }
308
309 @Override
310 public void onError(FlowRuleOperations ops) {
311 //FIXME store.write(pending.setState(BROKEN));
Brian O'Connorba1abbe2015-02-06 15:21:48 -0800312 log.warn("Failed installation: {} {} on {}", pending.key(),
313 pending.intent(), ops);
Brian O'Connor7775bda2015-02-06 15:01:18 -0800314 }
315 });
316 }
317
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800318 /**
319 * Generate a {@link FlowRuleOperations} instance from the specified intent data.
320 *
321 * @param current intent data stored in the store
322 * @return flow rule operations
323 */
324 FlowRuleOperations uninstallCoordinate(IntentData current) {
325 List<Intent> installables = current.installables();
326 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
327 for (Intent installable : installables) {
328 try {
329 plans.add(getInstaller(installable).uninstall(installable));
330 } catch (IntentException e) {
331 log.warn("Unable to uninstall intent {} due to:", current.intent().id(), e);
332 throw new FlowRuleBatchOperationConversionException(null/*FIXME*/, e);
333 }
334 }
335
336 return merge(plans).build(new FlowRuleOperationsContext() {
337 @Override
338 public void onSuccess(FlowRuleOperations ops) {
339 log.info("Completed withdrawing: {}", current.key());
340 current.setState(WITHDRAWN);
341 store.write(current);
342 }
343
344 @Override
345 public void onError(FlowRuleOperations ops) {
346 log.warn("Failed withdraw: {}", current.key());
347 current.setState(FAILED);
348 store.write(current);
349 }
350 });
351 }
352
353
Brian O'Connor7775bda2015-02-06 15:01:18 -0800354 // FIXME... needs tests... or maybe it's just perfect
355 private FlowRuleOperations.Builder merge(List<List<FlowRuleBatchOperation>> plans) {
356 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
357 // Build a batch one stage at a time
358 for (int stageNumber = 0;; stageNumber++) {
359 // Get the sub-stage from each plan (List<FlowRuleBatchOperation>)
360 for (Iterator<List<FlowRuleBatchOperation>> itr = plans.iterator(); itr.hasNext();) {
361 List<FlowRuleBatchOperation> plan = itr.next();
362 if (plan.size() <= stageNumber) {
363 // we have consumed all stages from this plan, so remove it
364 itr.remove();
365 continue;
366 }
367 // write operations from this sub-stage into the builder
368 FlowRuleBatchOperation stage = plan.get(stageNumber);
369 for (FlowRuleBatchEntry entry : stage.getOperations()) {
370 FlowRule rule = entry.target();
371 switch (entry.operator()) {
372 case ADD:
373 builder.add(rule);
374 break;
375 case REMOVE:
376 builder.remove(rule);
377 break;
378 case MODIFY:
379 builder.modify(rule);
380 break;
381 default:
382 break;
383 }
384 }
385 }
386 // we are done with the stage, start the next one...
387 if (plans.isEmpty()) {
388 break; // we don't need to start a new stage, we are done.
389 }
390 builder.newStage();
391 }
392 return builder;
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800393 }
394
Brian O'Connor66630c82014-10-02 21:08:19 -0700395 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700396 * Registers an intent compiler of the specified intent if an intent compiler
397 * for the intent is not registered. This method traverses the class hierarchy of
398 * the intent. Once an intent compiler for a parent type is found, this method
399 * registers the found intent compiler.
400 *
401 * @param intent intent
402 */
403 private void registerSubclassCompilerIfNeeded(Intent intent) {
404 if (!compilers.containsKey(intent.getClass())) {
405 Class<?> cls = intent.getClass();
406 while (cls != Object.class) {
407 // As long as we're within the Intent class descendants
408 if (Intent.class.isAssignableFrom(cls)) {
409 IntentCompiler<?> compiler = compilers.get(cls);
410 if (compiler != null) {
411 compilers.put(intent.getClass(), compiler);
412 return;
413 }
414 }
415 cls = cls.getSuperclass();
416 }
417 }
418 }
419
420 /**
421 * Registers an intent installer of the specified intent if an intent installer
422 * for the intent is not registered. This method traverses the class hierarchy of
423 * the intent. Once an intent installer for a parent type is found, this method
424 * registers the found intent installer.
425 *
426 * @param intent intent
427 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700428 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700429 if (!installers.containsKey(intent.getClass())) {
430 Class<?> cls = intent.getClass();
431 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700432 // As long as we're within the Intent class descendants
433 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700434 IntentInstaller<?> installer = installers.get(cls);
435 if (installer != null) {
436 installers.put(intent.getClass(), installer);
437 return;
438 }
439 }
440 cls = cls.getSuperclass();
441 }
442 }
443 }
444
Brian O'Connor66630c82014-10-02 21:08:19 -0700445 // Store delegate to re-post events emitted from the store.
446 private class InternalStoreDelegate implements IntentStoreDelegate {
447 @Override
448 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700449 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700450 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800451
452 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800453 public void process(IntentData data) {
454 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800455 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700456 }
457
Brian O'Connor72a034c2014-11-26 18:24:23 -0800458 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
459 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800460 // Attempt recompilation of the specified intents first.
461 for (IntentId id : intentIds) {
462 Intent intent = store.getIntent(id);
463 if (intent == null) {
464 continue;
465 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800466 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800467 }
468
469 if (compileAllFailed) {
470 // If required, compile all currently failed intents.
471 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800472 IntentState state = getIntentState(intent.id());
473 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800474 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800475 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800476 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800477 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800478 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800479 }
480 }
481 }
482
Brian O'Connorb499b352015-02-03 16:46:15 -0800483 //FIXME
484// for (ApplicationId appId : batches.keySet()) {
485// if (batchService.isLocalLeader(appId)) {
486// execute(batches.get(appId).build());
487// }
488// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800489 }
490
tom95329eb2014-10-06 08:40:06 -0700491 // Topology change delegate
492 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
493 @Override
tom85258ee2014-10-07 00:10:02 -0700494 public void triggerCompile(Iterable<IntentId> intentIds,
495 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800496 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700497 }
tom95329eb2014-10-06 08:40:06 -0700498 }
tom85258ee2014-10-07 00:10:02 -0700499
Brian O'Connorb499b352015-02-03 16:46:15 -0800500 private IntentUpdate createIntentUpdate(IntentData intentData) {
Brian O'Connorb499b352015-02-03 16:46:15 -0800501 switch (intentData.state()) {
502 case INSTALL_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800503 return new InstallRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800504 case WITHDRAW_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800505 return new WithdrawRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800506 // fallthrough
507 case COMPILING:
508 case INSTALLING:
509 case INSTALLED:
510 case RECOMPILING:
511 case WITHDRAWING:
512 case WITHDRAWN:
513 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800514 default:
515 // illegal state
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800516 return new CompilingFailed(intentData);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700517 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700518 }
519
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800520 private Future<CompletedIntentUpdate> submitIntentData(IntentData data) {
521 return workerExecutor.submit(new IntentWorker(data));
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800522 }
523
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800524 private class IntentBatchPreprocess implements Runnable {
525
526 // TODO make this configurable
527 private static final int TIMEOUT_PER_OP = 500; // ms
528 protected static final int MAX_ATTEMPTS = 3;
529
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800530 protected final Collection<IntentData> data;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800531
532 // future holding current FlowRuleBatch installation result
533 protected final long startTime = System.currentTimeMillis();
534 protected final long endTime;
535
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800536 private IntentBatchPreprocess(Collection<IntentData> data, long endTime) {
537 this.data = checkNotNull(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800538 this.endTime = endTime;
539 }
540
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800541 public IntentBatchPreprocess(Collection<IntentData> data) {
542 this(data, System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800543 }
544
545 // FIXME compute reasonable timeouts
546 protected long calculateTimeoutLimit() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800547 return System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800548 }
549
550 @Override
551 public void run() {
552 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800553 /*
554 1. wrap each intentdata in a runnable and submit
555 2. wait for completion of all the work
556 3. accumulate results and submit batch write of IntentData to store
557 (we can also try to update these individually)
558 */
559 submitUpdates(waitForFutures(createIntentUpdates()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800560 } catch (Exception e) {
561 log.error("Error submitting batches:", e);
562 // FIXME incomplete Intents should be cleaned up
563 // (transition to FAILED, etc.)
564
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800565 // the batch has failed
566 // TODO: maybe we should do more?
567 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800568 //FIXME
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800569// batchService.removeIntentOperations(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800570 }
571 }
572
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800573 private List<Future<CompletedIntentUpdate>> createIntentUpdates() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800574 return data.stream()
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800575 .map(IntentManager.this::submitIntentData)
576 .collect(Collectors.toList());
577 }
578
579 private List<CompletedIntentUpdate> waitForFutures(List<Future<CompletedIntentUpdate>> futures) {
580 ImmutableList.Builder<CompletedIntentUpdate> updateBuilder = ImmutableList.builder();
581 for (Future<CompletedIntentUpdate> future : futures) {
582 try {
583 updateBuilder.add(future.get());
584 } catch (InterruptedException | ExecutionException e) {
585 //FIXME
586 log.warn("Future failed: {}", e);
587 }
588 }
589 return updateBuilder.build();
590 }
591
592 private void submitUpdates(List<CompletedIntentUpdate> updates) {
593 store.batchWrite(updates.stream()
594 .map(CompletedIntentUpdate::data)
595 .collect(Collectors.toList()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800596 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800597 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800598
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800599 private final class IntentWorker implements Callable<CompletedIntentUpdate> {
Brian O'Connordb15b042015-02-04 14:59:28 -0800600
601 private final IntentData data;
602
603 private IntentWorker(IntentData data) {
604 this.data = data;
605 }
606
607 @Override
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800608 public CompletedIntentUpdate call() throws Exception {
Brian O'Connordb15b042015-02-04 14:59:28 -0800609 IntentUpdate update = createIntentUpdate(data);
610 Optional<IntentUpdate> currentPhase = Optional.of(update);
611 IntentUpdate previousPhase = update;
612
613 while (currentPhase.isPresent()) {
614 previousPhase = currentPhase.get();
615 currentPhase = previousPhase.execute();
616 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800617 return (CompletedIntentUpdate) previousPhase;
Brian O'Connor427a1762014-11-19 18:40:32 -0800618 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700619 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700620
621 private class InternalBatchDelegate implements IntentBatchDelegate {
622 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800623 public void execute(Collection<IntentData> operations) {
624 log.info("Execute {} operation(s).", operations.size());
625 log.debug("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800626 batchExecutor.execute(new IntentBatchPreprocess(operations));
627 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700628 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700629 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800630
631// /////////**************************///////////////////
632// FIXME Need to build and monitor contexts from FlowRuleService
633//
634// // TODO: better naming
635// private class IntentBatchApplyFirst extends IntentBatchPreprocess {
636//
637// protected final List<CompletedIntentUpdate> intentUpdates;
638// protected final int installAttempt;
639// protected Future<CompletedBatchOperation> future;
640//
641// IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
642// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
643// super(operations, endTime);
644// this.intentUpdates = ImmutableList.copyOf(intentUpdates);
645// this.future = future;
646// this.installAttempt = installAttempt;
647// }
648//
649// @Override
650// public void run() {
651// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
652// new IntentBatchProcessFutures(data, intentUpdates, endTime, installAttempt, future).run();
653// }
654//
655// /**
656// * Builds and applies the next batch, and returns the future.
657// *
658// * @return Future for next batch
659// */
660// protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
661// //TODO test this. (also, maybe save this batch)
662//
663// FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
664// if (batch.size() > 0) {
665// //FIXME apply batch might throw an exception
666// return flowRuleService.applyBatch(batch);
667// } else {
668// return null;
669// }
670// }
671//
672// private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
673// FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
674// for (CompletedIntentUpdate update : intentUpdates) {
675// FlowRuleBatchOperation currentBatch = update.currentBatch();
676// if (currentBatch != null) {
677// batch.addAll(currentBatch);
678// }
679// }
680// return batch;
681// }
682//
683// protected void abandonShip() {
684// // the batch has failed
685// // TODO: maybe we should do more?
686// log.error("Walk the plank, matey...");
687// future = null;
688// //FIXME
689// //batchService.removeIntentOperations(data);
690// }
691// }
692//
693// // TODO: better naming
694// private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
695//
696// IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
697// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
698// super(operations, intentUpdates, endTime, installAttempt, future);
699// }
700//
701// @Override
702// public void run() {
703// try {
704// Future<CompletedBatchOperation> future = processFutures();
705// if (future == null) {
706// // there are no outstanding batches; we are done
707// //FIXME
708// return; //?
709// //batchService.removeIntentOperations(data);
710// } else if (System.currentTimeMillis() > endTime) {
711// // - cancel current FlowRuleBatch and resubmit again
712// retry();
713// } else {
714// // we are not done yet, yield the thread by resubmitting ourselves
715// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, endTime,
716// installAttempt, future));
717// }
718// } catch (Exception e) {
719// log.error("Error submitting batches:", e);
720// // FIXME incomplete Intents should be cleaned up
721// // (transition to FAILED, etc.)
722// abandonShip();
723// }
724// }
725//
726// /**
727// * Iterate through the pending futures, and remove them when they have completed.
728// */
729// private Future<CompletedBatchOperation> processFutures() {
730// try {
731// CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
732// updateBatches(completed);
733// return applyNextBatch(intentUpdates);
734// } catch (TimeoutException | InterruptedException te) {
735// log.trace("Installation of intents are still pending: {}", data);
736// return future;
737// } catch (ExecutionException e) {
738// log.warn("Execution of batch failed: {}", data, e);
739// abandonShip();
740// return future;
741// }
742// }
743//
744// private void updateBatches(CompletedBatchOperation completed) {
745// if (completed.isSuccess()) {
746// for (CompletedIntentUpdate update : intentUpdates) {
747// update.batchSuccess();
748// }
749// } else {
750// // entire batch has been reverted...
751// log.debug("Failed items: {}", completed.failedItems());
752// log.debug("Failed ids: {}", completed.failedIds());
753//
754// for (Long id : completed.failedIds()) {
755// IntentId targetId = IntentId.valueOf(id);
756// for (CompletedIntentUpdate update : intentUpdates) {
757// for (Intent intent : update.allInstallables()) {
758// if (intent.id().equals(targetId)) {
759// update.batchFailed();
760// break;
761// }
762// }
763// }
764// // don't increment the non-failed items, as they have been reverted.
765// }
766// }
767// }
768//
769// private void retry() {
770// log.debug("Execution timed out, retrying.");
771// if (future.cancel(true)) { // cancel success; batch is reverted
772// // reset the timer
773// long timeLimit = calculateTimeoutLimit();
774// int attempts = installAttempt + 1;
775// if (attempts == MAX_ATTEMPTS) {
776// log.warn("Install request timed out: {}", data);
777// for (CompletedIntentUpdate update : intentUpdates) {
778// update.batchFailed();
779// }
780// } else if (attempts > MAX_ATTEMPTS) {
781// abandonShip();
782// return;
783// }
784// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
785// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, timeLimit, attempts, future));
786// } else {
787// log.error("Cancelling FlowRuleBatch failed.");
788// abandonShip();
789// }
790// }
791// }
Brian O'Connor66630c82014-10-02 21:08:19 -0700792}