blob: a0cc741eae80f957253e261cd86be97c07954f22 [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;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.intent.IntentInstaller;
44import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.intent.IntentService;
46import org.onosproject.net.intent.IntentState;
47import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.intent.IntentStoreDelegate;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080049import org.onosproject.net.intent.Key;
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";
Ray Milkeyf9af43c2015-02-09 16:45:48 -080084 public static final String INTENT_ID_NULL = "Intent key cannot be null";
Brian O'Connor66630c82014-10-02 21:08:19 -070085
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
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800166 public Intent getIntent(Key key) {
167 return store.getIntent(key);
168 }
169
170 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700171 public Iterable<Intent> getIntents() {
172 return store.getIntents();
173 }
174
175 @Override
176 public long getIntentCount() {
177 return store.getIntentCount();
178 }
179
180 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800181 public IntentState getIntentState(Key intentKey) {
182 checkNotNull(intentKey, INTENT_ID_NULL);
183 return store.getIntentState(intentKey);
Brian O'Connor66630c82014-10-02 21:08:19 -0700184 }
185
186 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800187 public List<Intent> getInstallableIntents(Key intentKey) {
188 checkNotNull(intentKey, INTENT_ID_NULL);
189 return store.getInstallableIntents(intentKey);
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700190 }
191
192 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700193 public void addListener(IntentListener listener) {
194 listenerRegistry.addListener(listener);
195 }
196
197 @Override
198 public void removeListener(IntentListener listener) {
199 listenerRegistry.removeListener(listener);
200 }
201
202 @Override
203 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
204 compilers.put(cls, compiler);
205 }
206
207 @Override
208 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
209 compilers.remove(cls);
210 }
211
212 @Override
213 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
214 return ImmutableMap.copyOf(compilers);
215 }
216
217 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700218 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700219 installers.put(cls, installer);
220 }
221
222 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700223 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700224 installers.remove(cls);
225 }
226
227 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700228 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700229 return ImmutableMap.copyOf(installers);
230 }
231
232 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700233 * Returns the corresponding intent compiler to the specified intent.
234 *
235 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700236 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700237 * @return intent compiler corresponding to the specified intent
238 */
239 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
240 @SuppressWarnings("unchecked")
241 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
242 if (compiler == null) {
243 throw new IntentException("no compiler for class " + intent.getClass());
244 }
245 return compiler;
246 }
247
248 /**
249 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700250 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700251 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700252 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 * @return intent installer corresponding to the specified installable intent
254 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700255 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700256 @SuppressWarnings("unchecked")
257 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
258 if (installer == null) {
259 throw new IntentException("no installer for class " + intent.getClass());
260 }
261 return installer;
262 }
263
264 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700265 * Compiles an intent recursively.
266 *
267 * @param intent intent
268 * @return result of compilation
269 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800270 List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700271 if (intent.isInstallable()) {
272 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700273 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700274
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700275 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700276 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700277 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800278 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
279 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700280 }
tom85258ee2014-10-07 00:10:02 -0700281 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700282 }
283
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800284 //TODO javadoc
285 //FIXME
Brian O'Connor7775bda2015-02-06 15:01:18 -0800286 FlowRuleOperations coordinate(IntentData pending) {
287 List<Intent> installables = pending.installables();
288 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>(installables.size());
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800289 for (Intent installable : installables) {
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800290 registerSubclassInstallerIfNeeded(installable);
291 //FIXME need to migrate installers to FlowRuleOperations
292 // FIXME need to aggregate the FlowRuleOperations across installables
293 plans.add(getInstaller(installable).install(installable));
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800294 }
Brian O'Connor7775bda2015-02-06 15:01:18 -0800295
296 return merge(plans).build(new FlowRuleOperationsContext() { // FIXME move this out
297 @Override
298 public void onSuccess(FlowRuleOperations ops) {
299 log.info("Completed installing: {}", pending.key());
300 pending.setState(INSTALLED);
301 store.write(pending);
302 }
303
304 @Override
305 public void onError(FlowRuleOperations ops) {
306 //FIXME store.write(pending.setState(BROKEN));
Brian O'Connorba1abbe2015-02-06 15:21:48 -0800307 log.warn("Failed installation: {} {} on {}", pending.key(),
308 pending.intent(), ops);
Brian O'Connor7775bda2015-02-06 15:01:18 -0800309 }
310 });
311 }
312
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800313 /**
314 * Generate a {@link FlowRuleOperations} instance from the specified intent data.
315 *
316 * @param current intent data stored in the store
317 * @return flow rule operations
318 */
319 FlowRuleOperations uninstallCoordinate(IntentData current) {
320 List<Intent> installables = current.installables();
321 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
322 for (Intent installable : installables) {
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800323 plans.add(getInstaller(installable).uninstall(installable));
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800324 }
325
326 return merge(plans).build(new FlowRuleOperationsContext() {
327 @Override
328 public void onSuccess(FlowRuleOperations ops) {
329 log.info("Completed withdrawing: {}", current.key());
330 current.setState(WITHDRAWN);
331 store.write(current);
332 }
333
334 @Override
335 public void onError(FlowRuleOperations ops) {
336 log.warn("Failed withdraw: {}", current.key());
337 current.setState(FAILED);
338 store.write(current);
339 }
340 });
341 }
342
343
Brian O'Connor7775bda2015-02-06 15:01:18 -0800344 // FIXME... needs tests... or maybe it's just perfect
345 private FlowRuleOperations.Builder merge(List<List<FlowRuleBatchOperation>> plans) {
346 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
347 // Build a batch one stage at a time
348 for (int stageNumber = 0;; stageNumber++) {
349 // Get the sub-stage from each plan (List<FlowRuleBatchOperation>)
350 for (Iterator<List<FlowRuleBatchOperation>> itr = plans.iterator(); itr.hasNext();) {
351 List<FlowRuleBatchOperation> plan = itr.next();
352 if (plan.size() <= stageNumber) {
353 // we have consumed all stages from this plan, so remove it
354 itr.remove();
355 continue;
356 }
357 // write operations from this sub-stage into the builder
358 FlowRuleBatchOperation stage = plan.get(stageNumber);
359 for (FlowRuleBatchEntry entry : stage.getOperations()) {
360 FlowRule rule = entry.target();
361 switch (entry.operator()) {
362 case ADD:
363 builder.add(rule);
364 break;
365 case REMOVE:
366 builder.remove(rule);
367 break;
368 case MODIFY:
369 builder.modify(rule);
370 break;
371 default:
372 break;
373 }
374 }
375 }
376 // we are done with the stage, start the next one...
377 if (plans.isEmpty()) {
378 break; // we don't need to start a new stage, we are done.
379 }
380 builder.newStage();
381 }
382 return builder;
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800383 }
384
Brian O'Connor66630c82014-10-02 21:08:19 -0700385 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700386 * Registers an intent compiler of the specified intent if an intent compiler
387 * for the intent is not registered. This method traverses the class hierarchy of
388 * the intent. Once an intent compiler for a parent type is found, this method
389 * registers the found intent compiler.
390 *
391 * @param intent intent
392 */
393 private void registerSubclassCompilerIfNeeded(Intent intent) {
394 if (!compilers.containsKey(intent.getClass())) {
395 Class<?> cls = intent.getClass();
396 while (cls != Object.class) {
397 // As long as we're within the Intent class descendants
398 if (Intent.class.isAssignableFrom(cls)) {
399 IntentCompiler<?> compiler = compilers.get(cls);
400 if (compiler != null) {
401 compilers.put(intent.getClass(), compiler);
402 return;
403 }
404 }
405 cls = cls.getSuperclass();
406 }
407 }
408 }
409
410 /**
411 * Registers an intent installer of the specified intent if an intent installer
412 * for the intent is not registered. This method traverses the class hierarchy of
413 * the intent. Once an intent installer for a parent type is found, this method
414 * registers the found intent installer.
415 *
416 * @param intent intent
417 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700418 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700419 if (!installers.containsKey(intent.getClass())) {
420 Class<?> cls = intent.getClass();
421 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700422 // As long as we're within the Intent class descendants
423 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700424 IntentInstaller<?> installer = installers.get(cls);
425 if (installer != null) {
426 installers.put(intent.getClass(), installer);
427 return;
428 }
429 }
430 cls = cls.getSuperclass();
431 }
432 }
433 }
434
Brian O'Connor66630c82014-10-02 21:08:19 -0700435 // Store delegate to re-post events emitted from the store.
436 private class InternalStoreDelegate implements IntentStoreDelegate {
437 @Override
438 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700439 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700440 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800441
442 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800443 public void process(IntentData data) {
444 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800445 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700446 }
447
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800448 private void buildAndSubmitBatches(Iterable<Key> intentKeys,
Brian O'Connor72a034c2014-11-26 18:24:23 -0800449 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800450 // Attempt recompilation of the specified intents first.
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800451 for (Key key : intentKeys) {
452 Intent intent = store.getIntent(key);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800453 if (intent == null) {
454 continue;
455 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800456 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800457 }
458
459 if (compileAllFailed) {
460 // If required, compile all currently failed intents.
461 for (Intent intent : getIntents()) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800462 IntentState state = getIntentState(intent.key());
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800463 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800464 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800465 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800466 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800467 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800468 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800469 }
470 }
471 }
472
Brian O'Connorb499b352015-02-03 16:46:15 -0800473 //FIXME
474// for (ApplicationId appId : batches.keySet()) {
475// if (batchService.isLocalLeader(appId)) {
476// execute(batches.get(appId).build());
477// }
478// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800479 }
480
tom95329eb2014-10-06 08:40:06 -0700481 // Topology change delegate
482 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
483 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800484 public void triggerCompile(Iterable<Key> intentKeys,
tom85258ee2014-10-07 00:10:02 -0700485 boolean compileAllFailed) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800486 buildAndSubmitBatches(intentKeys, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700487 }
tom95329eb2014-10-06 08:40:06 -0700488 }
tom85258ee2014-10-07 00:10:02 -0700489
Brian O'Connorb499b352015-02-03 16:46:15 -0800490 private IntentUpdate createIntentUpdate(IntentData intentData) {
Brian O'Connorb499b352015-02-03 16:46:15 -0800491 switch (intentData.state()) {
492 case INSTALL_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800493 return new InstallRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800494 case WITHDRAW_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800495 return new WithdrawRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800496 // fallthrough
497 case COMPILING:
498 case INSTALLING:
499 case INSTALLED:
500 case RECOMPILING:
501 case WITHDRAWING:
502 case WITHDRAWN:
503 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800504 default:
505 // illegal state
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800506 return new CompilingFailed(intentData);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700507 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700508 }
509
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800510 private Future<CompletedIntentUpdate> submitIntentData(IntentData data) {
511 return workerExecutor.submit(new IntentWorker(data));
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800512 }
513
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800514 private class IntentBatchPreprocess implements Runnable {
515
516 // TODO make this configurable
517 private static final int TIMEOUT_PER_OP = 500; // ms
518 protected static final int MAX_ATTEMPTS = 3;
519
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800520 protected final Collection<IntentData> data;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800521
522 // future holding current FlowRuleBatch installation result
523 protected final long startTime = System.currentTimeMillis();
524 protected final long endTime;
525
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800526 private IntentBatchPreprocess(Collection<IntentData> data, long endTime) {
527 this.data = checkNotNull(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800528 this.endTime = endTime;
529 }
530
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800531 public IntentBatchPreprocess(Collection<IntentData> data) {
532 this(data, System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800533 }
534
535 // FIXME compute reasonable timeouts
536 protected long calculateTimeoutLimit() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800537 return System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800538 }
539
540 @Override
541 public void run() {
542 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800543 /*
544 1. wrap each intentdata in a runnable and submit
545 2. wait for completion of all the work
546 3. accumulate results and submit batch write of IntentData to store
547 (we can also try to update these individually)
548 */
549 submitUpdates(waitForFutures(createIntentUpdates()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800550 } catch (Exception e) {
551 log.error("Error submitting batches:", e);
552 // FIXME incomplete Intents should be cleaned up
553 // (transition to FAILED, etc.)
554
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800555 // the batch has failed
556 // TODO: maybe we should do more?
557 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800558 //FIXME
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800559// batchService.removeIntentOperations(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800560 }
561 }
562
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800563 private List<Future<CompletedIntentUpdate>> createIntentUpdates() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800564 return data.stream()
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800565 .map(IntentManager.this::submitIntentData)
566 .collect(Collectors.toList());
567 }
568
569 private List<CompletedIntentUpdate> waitForFutures(List<Future<CompletedIntentUpdate>> futures) {
570 ImmutableList.Builder<CompletedIntentUpdate> updateBuilder = ImmutableList.builder();
571 for (Future<CompletedIntentUpdate> future : futures) {
572 try {
573 updateBuilder.add(future.get());
574 } catch (InterruptedException | ExecutionException e) {
575 //FIXME
576 log.warn("Future failed: {}", e);
577 }
578 }
579 return updateBuilder.build();
580 }
581
582 private void submitUpdates(List<CompletedIntentUpdate> updates) {
583 store.batchWrite(updates.stream()
584 .map(CompletedIntentUpdate::data)
585 .collect(Collectors.toList()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800586 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800587 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800588
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800589 private final class IntentWorker implements Callable<CompletedIntentUpdate> {
Brian O'Connordb15b042015-02-04 14:59:28 -0800590
591 private final IntentData data;
592
593 private IntentWorker(IntentData data) {
594 this.data = data;
595 }
596
597 @Override
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800598 public CompletedIntentUpdate call() throws Exception {
Brian O'Connordb15b042015-02-04 14:59:28 -0800599 IntentUpdate update = createIntentUpdate(data);
600 Optional<IntentUpdate> currentPhase = Optional.of(update);
601 IntentUpdate previousPhase = update;
602
603 while (currentPhase.isPresent()) {
604 previousPhase = currentPhase.get();
605 currentPhase = previousPhase.execute();
606 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800607 return (CompletedIntentUpdate) previousPhase;
Brian O'Connor427a1762014-11-19 18:40:32 -0800608 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700609 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700610
611 private class InternalBatchDelegate implements IntentBatchDelegate {
612 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800613 public void execute(Collection<IntentData> operations) {
614 log.info("Execute {} operation(s).", operations.size());
615 log.debug("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800616 batchExecutor.execute(new IntentBatchPreprocess(operations));
617 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700618 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700619 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800620
621// /////////**************************///////////////////
622// FIXME Need to build and monitor contexts from FlowRuleService
623//
624// // TODO: better naming
625// private class IntentBatchApplyFirst extends IntentBatchPreprocess {
626//
627// protected final List<CompletedIntentUpdate> intentUpdates;
628// protected final int installAttempt;
629// protected Future<CompletedBatchOperation> future;
630//
631// IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
632// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
633// super(operations, endTime);
634// this.intentUpdates = ImmutableList.copyOf(intentUpdates);
635// this.future = future;
636// this.installAttempt = installAttempt;
637// }
638//
639// @Override
640// public void run() {
641// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
642// new IntentBatchProcessFutures(data, intentUpdates, endTime, installAttempt, future).run();
643// }
644//
645// /**
646// * Builds and applies the next batch, and returns the future.
647// *
648// * @return Future for next batch
649// */
650// protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
651// //TODO test this. (also, maybe save this batch)
652//
653// FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
654// if (batch.size() > 0) {
655// //FIXME apply batch might throw an exception
656// return flowRuleService.applyBatch(batch);
657// } else {
658// return null;
659// }
660// }
661//
662// private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
663// FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
664// for (CompletedIntentUpdate update : intentUpdates) {
665// FlowRuleBatchOperation currentBatch = update.currentBatch();
666// if (currentBatch != null) {
667// batch.addAll(currentBatch);
668// }
669// }
670// return batch;
671// }
672//
673// protected void abandonShip() {
674// // the batch has failed
675// // TODO: maybe we should do more?
676// log.error("Walk the plank, matey...");
677// future = null;
678// //FIXME
679// //batchService.removeIntentOperations(data);
680// }
681// }
682//
683// // TODO: better naming
684// private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
685//
686// IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
687// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
688// super(operations, intentUpdates, endTime, installAttempt, future);
689// }
690//
691// @Override
692// public void run() {
693// try {
694// Future<CompletedBatchOperation> future = processFutures();
695// if (future == null) {
696// // there are no outstanding batches; we are done
697// //FIXME
698// return; //?
699// //batchService.removeIntentOperations(data);
700// } else if (System.currentTimeMillis() > endTime) {
701// // - cancel current FlowRuleBatch and resubmit again
702// retry();
703// } else {
704// // we are not done yet, yield the thread by resubmitting ourselves
705// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, endTime,
706// installAttempt, future));
707// }
708// } catch (Exception e) {
709// log.error("Error submitting batches:", e);
710// // FIXME incomplete Intents should be cleaned up
711// // (transition to FAILED, etc.)
712// abandonShip();
713// }
714// }
715//
716// /**
717// * Iterate through the pending futures, and remove them when they have completed.
718// */
719// private Future<CompletedBatchOperation> processFutures() {
720// try {
721// CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
722// updateBatches(completed);
723// return applyNextBatch(intentUpdates);
724// } catch (TimeoutException | InterruptedException te) {
725// log.trace("Installation of intents are still pending: {}", data);
726// return future;
727// } catch (ExecutionException e) {
728// log.warn("Execution of batch failed: {}", data, e);
729// abandonShip();
730// return future;
731// }
732// }
733//
734// private void updateBatches(CompletedBatchOperation completed) {
735// if (completed.isSuccess()) {
736// for (CompletedIntentUpdate update : intentUpdates) {
737// update.batchSuccess();
738// }
739// } else {
740// // entire batch has been reverted...
741// log.debug("Failed items: {}", completed.failedItems());
742// log.debug("Failed ids: {}", completed.failedIds());
743//
744// for (Long id : completed.failedIds()) {
745// IntentId targetId = IntentId.valueOf(id);
746// for (CompletedIntentUpdate update : intentUpdates) {
747// for (Intent intent : update.allInstallables()) {
748// if (intent.id().equals(targetId)) {
749// update.batchFailed();
750// break;
751// }
752// }
753// }
754// // don't increment the non-failed items, as they have been reverted.
755// }
756// }
757// }
758//
759// private void retry() {
760// log.debug("Execution timed out, retrying.");
761// if (future.cancel(true)) { // cancel success; batch is reverted
762// // reset the timer
763// long timeLimit = calculateTimeoutLimit();
764// int attempts = installAttempt + 1;
765// if (attempts == MAX_ATTEMPTS) {
766// log.warn("Install request timed out: {}", data);
767// for (CompletedIntentUpdate update : intentUpdates) {
768// update.batchFailed();
769// }
770// } else if (attempts > MAX_ATTEMPTS) {
771// abandonShip();
772// return;
773// }
774// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
775// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, timeLimit, attempts, future));
776// } else {
777// log.error("Cancelling FlowRuleBatch failed.");
778// abandonShip();
779// }
780// }
781// }
Brian O'Connor66630c82014-10-02 21:08:19 -0700782}