blob: 751f7ad61ccc2c64cd7a53e4fa04faf5df106c52 [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;
20import com.google.common.collect.Lists;
Brian O'Connor66630c82014-10-02 21:08:19 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080027import org.onosproject.core.CoreService;
28import org.onosproject.core.IdGenerator;
29import org.onosproject.event.AbstractListenerRegistry;
30import org.onosproject.event.EventDeliveryService;
Brian O'Connor7775bda2015-02-06 15:01:18 -080031import org.onosproject.net.flow.FlowRule;
32import org.onosproject.net.flow.FlowRuleBatchEntry;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.net.flow.FlowRuleBatchOperation;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080034import org.onosproject.net.flow.FlowRuleOperations;
Brian O'Connor7775bda2015-02-06 15:01:18 -080035import org.onosproject.net.flow.FlowRuleOperationsContext;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.flow.FlowRuleService;
37import org.onosproject.net.intent.Intent;
38import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080040import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.intent.IntentEvent;
42import org.onosproject.net.intent.IntentException;
43import org.onosproject.net.intent.IntentExtensionService;
44import org.onosproject.net.intent.IntentId;
45import org.onosproject.net.intent.IntentInstaller;
46import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.intent.IntentService;
48import org.onosproject.net.intent.IntentState;
49import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070051import org.slf4j.Logger;
52
Brian O'Connor0e271dc2015-02-04 18:20:25 -080053import java.util.ArrayList;
54import java.util.Collection;
55import java.util.EnumSet;
Brian O'Connor7775bda2015-02-06 15:01:18 -080056import java.util.Iterator;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080057import java.util.List;
58import java.util.Map;
59import java.util.Optional;
60import java.util.concurrent.Callable;
61import java.util.concurrent.ConcurrentHashMap;
62import java.util.concurrent.ConcurrentMap;
63import java.util.concurrent.ExecutionException;
64import java.util.concurrent.ExecutorService;
65import java.util.concurrent.Future;
66import java.util.stream.Collectors;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070067
Brian O'Connorfa81eae2014-10-30 13:20:05 -070068import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080069import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connordb15b042015-02-04 14:59:28 -080070import static java.util.concurrent.Executors.newSingleThreadExecutor;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070071import static org.onlab.util.Tools.namedThreads;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080072import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070073import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070074
75/**
76 * An implementation of Intent Manager.
77 */
78@Component(immediate = true)
79@Service
80public class IntentManager
81 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080082 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070083
84 public static final String INTENT_NULL = "Intent cannot be null";
85 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
86
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080087 private static final int NUM_THREADS = 12;
88
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080089 private static final EnumSet<IntentState> RECOMPILE
90 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080091
92
Brian O'Connor66630c82014-10-02 21:08:19 -070093 // Collections for compiler, installer, and listener are ONOS instance local
94 private final ConcurrentMap<Class<? extends Intent>,
95 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070096 private final ConcurrentMap<Class<? extends Intent>,
97 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070098
99 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700100 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700101
Brian O'Connor520c0522014-11-23 23:50:47 -0800102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected IntentStore store;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700109 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700112 protected EventDeliveryService eventDispatcher;
113
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected FlowRuleService flowRuleService;
116
Brian O'Connor520c0522014-11-23 23:50:47 -0800117
Brian O'Connordb15b042015-02-04 14:59:28 -0800118 private ExecutorService batchExecutor;
119 private ExecutorService workerExecutor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800120
121 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
122 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
123 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
124 private IdGenerator idGenerator;
125
Brian O'Connorb499b352015-02-03 16:46:15 -0800126 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800127
Brian O'Connor66630c82014-10-02 21:08:19 -0700128 @Activate
129 public void activate() {
130 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700131 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700132 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connordb15b042015-02-04 14:59:28 -0800133 batchExecutor = newSingleThreadExecutor(namedThreads("onos-intent-batch"));
134 workerExecutor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-worker-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800135 idGenerator = coreService.getIdGenerator("intent-ids");
136 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700137 log.info("Started");
138 }
139
140 @Deactivate
141 public void deactivate() {
142 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700143 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700144 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connordb15b042015-02-04 14:59:28 -0800145 batchExecutor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800146 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700147 log.info("Stopped");
148 }
149
150 @Override
151 public void submit(Intent intent) {
152 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800153 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
154 //FIXME timestamp?
155 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700156 }
157
158 @Override
159 public void withdraw(Intent intent) {
160 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800161 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
162 //FIXME timestamp?
163 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700164 }
165
Brian O'Connor66630c82014-10-02 21:08:19 -0700166 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700167 public Iterable<Intent> getIntents() {
168 return store.getIntents();
169 }
170
171 @Override
172 public long getIntentCount() {
173 return store.getIntentCount();
174 }
175
176 @Override
177 public Intent getIntent(IntentId id) {
178 checkNotNull(id, INTENT_ID_NULL);
179 return store.getIntent(id);
180 }
181
182 @Override
183 public IntentState getIntentState(IntentId id) {
184 checkNotNull(id, INTENT_ID_NULL);
185 return store.getIntentState(id);
186 }
187
188 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700189 public List<Intent> getInstallableIntents(IntentId intentId) {
190 checkNotNull(intentId, INTENT_ID_NULL);
191 return store.getInstallableIntents(intentId);
192 }
193
194 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700195 public void addListener(IntentListener listener) {
196 listenerRegistry.addListener(listener);
197 }
198
199 @Override
200 public void removeListener(IntentListener listener) {
201 listenerRegistry.removeListener(listener);
202 }
203
204 @Override
205 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
206 compilers.put(cls, compiler);
207 }
208
209 @Override
210 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
211 compilers.remove(cls);
212 }
213
214 @Override
215 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
216 return ImmutableMap.copyOf(compilers);
217 }
218
219 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700220 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700221 installers.put(cls, installer);
222 }
223
224 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700225 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700226 installers.remove(cls);
227 }
228
229 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700230 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700231 return ImmutableMap.copyOf(installers);
232 }
233
234 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700235 * Returns the corresponding intent compiler to the specified intent.
236 *
237 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700238 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700239 * @return intent compiler corresponding to the specified intent
240 */
241 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
242 @SuppressWarnings("unchecked")
243 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
244 if (compiler == null) {
245 throw new IntentException("no compiler for class " + intent.getClass());
246 }
247 return compiler;
248 }
249
250 /**
251 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700252 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700254 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700255 * @return intent installer corresponding to the specified installable intent
256 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700257 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700258 @SuppressWarnings("unchecked")
259 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
260 if (installer == null) {
261 throw new IntentException("no installer for class " + intent.getClass());
262 }
263 return installer;
264 }
265
266 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700267 * Compiles an intent recursively.
268 *
269 * @param intent intent
270 * @return result of compilation
271 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800272 List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700273 if (intent.isInstallable()) {
274 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700275 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700276
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700277 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700278 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700279 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800280 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
281 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700282 }
tom85258ee2014-10-07 00:10:02 -0700283 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700284 }
285
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800286 //TODO javadoc
287 //FIXME
Brian O'Connor7775bda2015-02-06 15:01:18 -0800288 FlowRuleOperations coordinate(IntentData pending) {
289 List<Intent> installables = pending.installables();
290 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>(installables.size());
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800291 for (Intent installable : installables) {
292 try {
293 registerSubclassInstallerIfNeeded(installable);
294 //FIXME need to migrate installers to FlowRuleOperations
295 // FIXME need to aggregate the FlowRuleOperations across installables
Brian O'Connor7775bda2015-02-06 15:01:18 -0800296 plans.add(getInstaller(installable).install(installable));
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800297 } catch (Exception e) { // TODO this should be IntentException
298 throw new FlowRuleBatchOperationConversionException(null/*FIXME*/, e);
299 }
300 }
Brian O'Connor7775bda2015-02-06 15:01:18 -0800301
302 return merge(plans).build(new FlowRuleOperationsContext() { // FIXME move this out
303 @Override
304 public void onSuccess(FlowRuleOperations ops) {
305 log.info("Completed installing: {}", pending.key());
306 pending.setState(INSTALLED);
307 store.write(pending);
308 }
309
310 @Override
311 public void onError(FlowRuleOperations ops) {
312 //FIXME store.write(pending.setState(BROKEN));
Brian O'Connorba1abbe2015-02-06 15:21:48 -0800313 log.warn("Failed installation: {} {} on {}", pending.key(),
314 pending.intent(), ops);
Brian O'Connor7775bda2015-02-06 15:01:18 -0800315 }
316 });
317 }
318
319 // FIXME... needs tests... or maybe it's just perfect
320 private FlowRuleOperations.Builder merge(List<List<FlowRuleBatchOperation>> plans) {
321 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
322 // Build a batch one stage at a time
323 for (int stageNumber = 0;; stageNumber++) {
324 // Get the sub-stage from each plan (List<FlowRuleBatchOperation>)
325 for (Iterator<List<FlowRuleBatchOperation>> itr = plans.iterator(); itr.hasNext();) {
326 List<FlowRuleBatchOperation> plan = itr.next();
327 if (plan.size() <= stageNumber) {
328 // we have consumed all stages from this plan, so remove it
329 itr.remove();
330 continue;
331 }
332 // write operations from this sub-stage into the builder
333 FlowRuleBatchOperation stage = plan.get(stageNumber);
334 for (FlowRuleBatchEntry entry : stage.getOperations()) {
335 FlowRule rule = entry.target();
336 switch (entry.operator()) {
337 case ADD:
338 builder.add(rule);
339 break;
340 case REMOVE:
341 builder.remove(rule);
342 break;
343 case MODIFY:
344 builder.modify(rule);
345 break;
346 default:
347 break;
348 }
349 }
350 }
351 // we are done with the stage, start the next one...
352 if (plans.isEmpty()) {
353 break; // we don't need to start a new stage, we are done.
354 }
355 builder.newStage();
356 }
357 return builder;
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800358 }
359
Brian O'Connor66630c82014-10-02 21:08:19 -0700360 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700361 * Uninstalls all installable intents associated with the given intent.
362 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800363 * @param intent intent
364 * @param installables installable intents
365 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700366 */
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800367 //FIXME
368 FlowRuleOperations uninstallIntent(Intent intent, List<Intent> installables) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700369 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800370 for (Intent installable : installables) {
371 trackerService.removeTrackedResources(intent.id(),
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800372 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700373 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800374 // FIXME need to aggregate the FlowRuleOperations across installables
Brian O'Connor7775bda2015-02-06 15:01:18 -0800375 getInstaller(installable).uninstall(installable); //.build(null/*FIXME*/);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700376 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800377 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700378 // TODO: this should never happen. but what if it does?
379 }
380 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800381 return null; //FIXME
tom85258ee2014-10-07 00:10:02 -0700382 }
383
384 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700385 * Registers an intent compiler of the specified intent if an intent compiler
386 * for the intent is not registered. This method traverses the class hierarchy of
387 * the intent. Once an intent compiler for a parent type is found, this method
388 * registers the found intent compiler.
389 *
390 * @param intent intent
391 */
392 private void registerSubclassCompilerIfNeeded(Intent intent) {
393 if (!compilers.containsKey(intent.getClass())) {
394 Class<?> cls = intent.getClass();
395 while (cls != Object.class) {
396 // As long as we're within the Intent class descendants
397 if (Intent.class.isAssignableFrom(cls)) {
398 IntentCompiler<?> compiler = compilers.get(cls);
399 if (compiler != null) {
400 compilers.put(intent.getClass(), compiler);
401 return;
402 }
403 }
404 cls = cls.getSuperclass();
405 }
406 }
407 }
408
409 /**
410 * Registers an intent installer of the specified intent if an intent installer
411 * for the intent is not registered. This method traverses the class hierarchy of
412 * the intent. Once an intent installer for a parent type is found, this method
413 * registers the found intent installer.
414 *
415 * @param intent intent
416 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700417 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700418 if (!installers.containsKey(intent.getClass())) {
419 Class<?> cls = intent.getClass();
420 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700421 // As long as we're within the Intent class descendants
422 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700423 IntentInstaller<?> installer = installers.get(cls);
424 if (installer != null) {
425 installers.put(intent.getClass(), installer);
426 return;
427 }
428 }
429 cls = cls.getSuperclass();
430 }
431 }
432 }
433
Brian O'Connor66630c82014-10-02 21:08:19 -0700434 // Store delegate to re-post events emitted from the store.
435 private class InternalStoreDelegate implements IntentStoreDelegate {
436 @Override
437 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700438 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700439 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800440
441 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800442 public void process(IntentData data) {
443 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800444 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700445 }
446
Brian O'Connor72a034c2014-11-26 18:24:23 -0800447 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
448 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800449 // Attempt recompilation of the specified intents first.
450 for (IntentId id : intentIds) {
451 Intent intent = store.getIntent(id);
452 if (intent == null) {
453 continue;
454 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800455 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800456 }
457
458 if (compileAllFailed) {
459 // If required, compile all currently failed intents.
460 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800461 IntentState state = getIntentState(intent.id());
462 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800463 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800464 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800465 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800466 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800467 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800468 }
469 }
470 }
471
Brian O'Connorb499b352015-02-03 16:46:15 -0800472 //FIXME
473// for (ApplicationId appId : batches.keySet()) {
474// if (batchService.isLocalLeader(appId)) {
475// execute(batches.get(appId).build());
476// }
477// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800478 }
479
tom95329eb2014-10-06 08:40:06 -0700480 // Topology change delegate
481 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
482 @Override
tom85258ee2014-10-07 00:10:02 -0700483 public void triggerCompile(Iterable<IntentId> intentIds,
484 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800485 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700486 }
tom95329eb2014-10-06 08:40:06 -0700487 }
tom85258ee2014-10-07 00:10:02 -0700488
Brian O'Connorb499b352015-02-03 16:46:15 -0800489 private IntentUpdate createIntentUpdate(IntentData intentData) {
Brian O'Connorb499b352015-02-03 16:46:15 -0800490 switch (intentData.state()) {
491 case INSTALL_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800492 return new InstallRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800493 case WITHDRAW_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800494 return new WithdrawRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800495 // fallthrough
496 case COMPILING:
497 case INSTALLING:
498 case INSTALLED:
499 case RECOMPILING:
500 case WITHDRAWING:
501 case WITHDRAWN:
502 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800503 default:
504 // illegal state
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800505 return new CompilingFailed(intentData);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700506 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700507 }
508
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800509 private Future<CompletedIntentUpdate> submitIntentData(IntentData data) {
510 return workerExecutor.submit(new IntentWorker(data));
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800511 }
512
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800513 private class IntentBatchPreprocess implements Runnable {
514
515 // TODO make this configurable
516 private static final int TIMEOUT_PER_OP = 500; // ms
517 protected static final int MAX_ATTEMPTS = 3;
518
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800519 protected final Collection<IntentData> data;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800520
521 // future holding current FlowRuleBatch installation result
522 protected final long startTime = System.currentTimeMillis();
523 protected final long endTime;
524
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800525 private IntentBatchPreprocess(Collection<IntentData> data, long endTime) {
526 this.data = checkNotNull(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800527 this.endTime = endTime;
528 }
529
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800530 public IntentBatchPreprocess(Collection<IntentData> data) {
531 this(data, System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800532 }
533
534 // FIXME compute reasonable timeouts
535 protected long calculateTimeoutLimit() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800536 return System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800537 }
538
539 @Override
540 public void run() {
541 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800542 /*
543 1. wrap each intentdata in a runnable and submit
544 2. wait for completion of all the work
545 3. accumulate results and submit batch write of IntentData to store
546 (we can also try to update these individually)
547 */
548 submitUpdates(waitForFutures(createIntentUpdates()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800549 } catch (Exception e) {
550 log.error("Error submitting batches:", e);
551 // FIXME incomplete Intents should be cleaned up
552 // (transition to FAILED, etc.)
553
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800554 // the batch has failed
555 // TODO: maybe we should do more?
556 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800557 //FIXME
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800558// batchService.removeIntentOperations(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800559 }
560 }
561
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800562 private List<Future<CompletedIntentUpdate>> createIntentUpdates() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800563 return data.stream()
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800564 .map(IntentManager.this::submitIntentData)
565 .collect(Collectors.toList());
566 }
567
568 private List<CompletedIntentUpdate> waitForFutures(List<Future<CompletedIntentUpdate>> futures) {
569 ImmutableList.Builder<CompletedIntentUpdate> updateBuilder = ImmutableList.builder();
570 for (Future<CompletedIntentUpdate> future : futures) {
571 try {
572 updateBuilder.add(future.get());
573 } catch (InterruptedException | ExecutionException e) {
574 //FIXME
575 log.warn("Future failed: {}", e);
576 }
577 }
578 return updateBuilder.build();
579 }
580
581 private void submitUpdates(List<CompletedIntentUpdate> updates) {
582 store.batchWrite(updates.stream()
583 .map(CompletedIntentUpdate::data)
584 .collect(Collectors.toList()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800585 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800586 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800587
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800588 private final class IntentWorker implements Callable<CompletedIntentUpdate> {
Brian O'Connordb15b042015-02-04 14:59:28 -0800589
590 private final IntentData data;
591
592 private IntentWorker(IntentData data) {
593 this.data = data;
594 }
595
596 @Override
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800597 public CompletedIntentUpdate call() throws Exception {
Brian O'Connordb15b042015-02-04 14:59:28 -0800598 IntentUpdate update = createIntentUpdate(data);
599 Optional<IntentUpdate> currentPhase = Optional.of(update);
600 IntentUpdate previousPhase = update;
601
602 while (currentPhase.isPresent()) {
603 previousPhase = currentPhase.get();
604 currentPhase = previousPhase.execute();
605 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800606 return (CompletedIntentUpdate) previousPhase;
Brian O'Connor427a1762014-11-19 18:40:32 -0800607 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700608 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700609
610 private class InternalBatchDelegate implements IntentBatchDelegate {
611 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800612 public void execute(Collection<IntentData> operations) {
613 log.info("Execute {} operation(s).", operations.size());
614 log.debug("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800615 batchExecutor.execute(new IntentBatchPreprocess(operations));
616 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700617 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700618 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800619
620// /////////**************************///////////////////
621// FIXME Need to build and monitor contexts from FlowRuleService
622//
623// // TODO: better naming
624// private class IntentBatchApplyFirst extends IntentBatchPreprocess {
625//
626// protected final List<CompletedIntentUpdate> intentUpdates;
627// protected final int installAttempt;
628// protected Future<CompletedBatchOperation> future;
629//
630// IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
631// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
632// super(operations, endTime);
633// this.intentUpdates = ImmutableList.copyOf(intentUpdates);
634// this.future = future;
635// this.installAttempt = installAttempt;
636// }
637//
638// @Override
639// public void run() {
640// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
641// new IntentBatchProcessFutures(data, intentUpdates, endTime, installAttempt, future).run();
642// }
643//
644// /**
645// * Builds and applies the next batch, and returns the future.
646// *
647// * @return Future for next batch
648// */
649// protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
650// //TODO test this. (also, maybe save this batch)
651//
652// FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
653// if (batch.size() > 0) {
654// //FIXME apply batch might throw an exception
655// return flowRuleService.applyBatch(batch);
656// } else {
657// return null;
658// }
659// }
660//
661// private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
662// FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
663// for (CompletedIntentUpdate update : intentUpdates) {
664// FlowRuleBatchOperation currentBatch = update.currentBatch();
665// if (currentBatch != null) {
666// batch.addAll(currentBatch);
667// }
668// }
669// return batch;
670// }
671//
672// protected void abandonShip() {
673// // the batch has failed
674// // TODO: maybe we should do more?
675// log.error("Walk the plank, matey...");
676// future = null;
677// //FIXME
678// //batchService.removeIntentOperations(data);
679// }
680// }
681//
682// // TODO: better naming
683// private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
684//
685// IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
686// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
687// super(operations, intentUpdates, endTime, installAttempt, future);
688// }
689//
690// @Override
691// public void run() {
692// try {
693// Future<CompletedBatchOperation> future = processFutures();
694// if (future == null) {
695// // there are no outstanding batches; we are done
696// //FIXME
697// return; //?
698// //batchService.removeIntentOperations(data);
699// } else if (System.currentTimeMillis() > endTime) {
700// // - cancel current FlowRuleBatch and resubmit again
701// retry();
702// } else {
703// // we are not done yet, yield the thread by resubmitting ourselves
704// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, endTime,
705// installAttempt, future));
706// }
707// } catch (Exception e) {
708// log.error("Error submitting batches:", e);
709// // FIXME incomplete Intents should be cleaned up
710// // (transition to FAILED, etc.)
711// abandonShip();
712// }
713// }
714//
715// /**
716// * Iterate through the pending futures, and remove them when they have completed.
717// */
718// private Future<CompletedBatchOperation> processFutures() {
719// try {
720// CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
721// updateBatches(completed);
722// return applyNextBatch(intentUpdates);
723// } catch (TimeoutException | InterruptedException te) {
724// log.trace("Installation of intents are still pending: {}", data);
725// return future;
726// } catch (ExecutionException e) {
727// log.warn("Execution of batch failed: {}", data, e);
728// abandonShip();
729// return future;
730// }
731// }
732//
733// private void updateBatches(CompletedBatchOperation completed) {
734// if (completed.isSuccess()) {
735// for (CompletedIntentUpdate update : intentUpdates) {
736// update.batchSuccess();
737// }
738// } else {
739// // entire batch has been reverted...
740// log.debug("Failed items: {}", completed.failedItems());
741// log.debug("Failed ids: {}", completed.failedIds());
742//
743// for (Long id : completed.failedIds()) {
744// IntentId targetId = IntentId.valueOf(id);
745// for (CompletedIntentUpdate update : intentUpdates) {
746// for (Intent intent : update.allInstallables()) {
747// if (intent.id().equals(targetId)) {
748// update.batchFailed();
749// break;
750// }
751// }
752// }
753// // don't increment the non-failed items, as they have been reverted.
754// }
755// }
756// }
757//
758// private void retry() {
759// log.debug("Execution timed out, retrying.");
760// if (future.cancel(true)) { // cancel success; batch is reverted
761// // reset the timer
762// long timeLimit = calculateTimeoutLimit();
763// int attempts = installAttempt + 1;
764// if (attempts == MAX_ATTEMPTS) {
765// log.warn("Install request timed out: {}", data);
766// for (CompletedIntentUpdate update : intentUpdates) {
767// update.batchFailed();
768// }
769// } else if (attempts > MAX_ATTEMPTS) {
770// abandonShip();
771// return;
772// }
773// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
774// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, timeLimit, attempts, future));
775// } else {
776// log.error("Cancelling FlowRuleBatch failed.");
777// abandonShip();
778// }
779// }
780// }
Brian O'Connor66630c82014-10-02 21:08:19 -0700781}