blob: 65da61a67941eab29cf19498d9574b96053dd7a5 [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'Connor7775bda2015-02-06 15:01:18 -080034import org.onosproject.net.flow.FlowRuleEvent;
35import org.onosproject.net.flow.FlowRuleListener;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080036import org.onosproject.net.flow.FlowRuleOperations;
Brian O'Connor7775bda2015-02-06 15:01:18 -080037import org.onosproject.net.flow.FlowRuleOperationsContext;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.flow.FlowRuleService;
39import org.onosproject.net.intent.Intent;
40import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080042import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.intent.IntentEvent;
44import org.onosproject.net.intent.IntentException;
45import org.onosproject.net.intent.IntentExtensionService;
46import org.onosproject.net.intent.IntentId;
47import org.onosproject.net.intent.IntentInstaller;
48import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080049import org.onosproject.net.intent.IntentService;
50import org.onosproject.net.intent.IntentState;
51import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070053import org.slf4j.Logger;
54
Brian O'Connor0e271dc2015-02-04 18:20:25 -080055import java.util.ArrayList;
56import java.util.Collection;
57import java.util.EnumSet;
Brian O'Connor7775bda2015-02-06 15:01:18 -080058import java.util.Iterator;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080059import java.util.List;
60import java.util.Map;
61import java.util.Optional;
62import java.util.concurrent.Callable;
63import java.util.concurrent.ConcurrentHashMap;
64import java.util.concurrent.ConcurrentMap;
65import java.util.concurrent.ExecutionException;
66import java.util.concurrent.ExecutorService;
67import java.util.concurrent.Future;
68import java.util.stream.Collectors;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070069
Brian O'Connorfa81eae2014-10-30 13:20:05 -070070import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080071import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connordb15b042015-02-04 14:59:28 -080072import static java.util.concurrent.Executors.newSingleThreadExecutor;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070073import static org.onlab.util.Tools.namedThreads;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080074import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070075import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070076
77/**
78 * An implementation of Intent Manager.
79 */
80@Component(immediate = true)
81@Service
82public class IntentManager
83 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080084 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070085
86 public static final String INTENT_NULL = "Intent cannot be null";
87 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
88
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080089 private static final int NUM_THREADS = 12;
90
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080091 private static final EnumSet<IntentState> RECOMPILE
92 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080093
94
Brian O'Connor66630c82014-10-02 21:08:19 -070095 // Collections for compiler, installer, and listener are ONOS instance local
96 private final ConcurrentMap<Class<? extends Intent>,
97 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070098 private final ConcurrentMap<Class<? extends Intent>,
99 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700100
101 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700102 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700103
Brian O'Connor520c0522014-11-23 23:50:47 -0800104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected IntentStore store;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700111 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700114 protected EventDeliveryService eventDispatcher;
115
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected FlowRuleService flowRuleService;
118
Brian O'Connor520c0522014-11-23 23:50:47 -0800119
Brian O'Connordb15b042015-02-04 14:59:28 -0800120 private ExecutorService batchExecutor;
121 private ExecutorService workerExecutor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800122
123 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
124 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
125 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
126 private IdGenerator idGenerator;
127
Brian O'Connorb499b352015-02-03 16:46:15 -0800128 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800129
Brian O'Connor66630c82014-10-02 21:08:19 -0700130 @Activate
131 public void activate() {
132 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700133 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700134 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connordb15b042015-02-04 14:59:28 -0800135 batchExecutor = newSingleThreadExecutor(namedThreads("onos-intent-batch"));
136 workerExecutor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-worker-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800137 idGenerator = coreService.getIdGenerator("intent-ids");
138 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700139 log.info("Started");
140 }
141
142 @Deactivate
143 public void deactivate() {
144 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700145 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700146 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connordb15b042015-02-04 14:59:28 -0800147 batchExecutor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800148 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700149 log.info("Stopped");
150 }
151
152 @Override
153 public void submit(Intent intent) {
154 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800155 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
156 //FIXME timestamp?
157 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700158 }
159
160 @Override
161 public void withdraw(Intent intent) {
162 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800163 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
164 //FIXME timestamp?
165 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700166 }
167
Brian O'Connor66630c82014-10-02 21:08:19 -0700168 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700169 public Iterable<Intent> getIntents() {
170 return store.getIntents();
171 }
172
173 @Override
174 public long getIntentCount() {
175 return store.getIntentCount();
176 }
177
178 @Override
179 public Intent getIntent(IntentId id) {
180 checkNotNull(id, INTENT_ID_NULL);
181 return store.getIntent(id);
182 }
183
184 @Override
185 public IntentState getIntentState(IntentId id) {
186 checkNotNull(id, INTENT_ID_NULL);
187 return store.getIntentState(id);
188 }
189
190 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700191 public List<Intent> getInstallableIntents(IntentId intentId) {
192 checkNotNull(intentId, INTENT_ID_NULL);
193 return store.getInstallableIntents(intentId);
194 }
195
196 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700197 public void addListener(IntentListener listener) {
198 listenerRegistry.addListener(listener);
199 }
200
201 @Override
202 public void removeListener(IntentListener listener) {
203 listenerRegistry.removeListener(listener);
204 }
205
206 @Override
207 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
208 compilers.put(cls, compiler);
209 }
210
211 @Override
212 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
213 compilers.remove(cls);
214 }
215
216 @Override
217 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
218 return ImmutableMap.copyOf(compilers);
219 }
220
221 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700222 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700223 installers.put(cls, installer);
224 }
225
226 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700227 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700228 installers.remove(cls);
229 }
230
231 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700232 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700233 return ImmutableMap.copyOf(installers);
234 }
235
236 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700237 * Returns the corresponding intent compiler to the specified intent.
238 *
239 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700240 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700241 * @return intent compiler corresponding to the specified intent
242 */
243 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
244 @SuppressWarnings("unchecked")
245 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
246 if (compiler == null) {
247 throw new IntentException("no compiler for class " + intent.getClass());
248 }
249 return compiler;
250 }
251
252 /**
253 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700254 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700255 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700256 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700257 * @return intent installer corresponding to the specified installable intent
258 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700259 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700260 @SuppressWarnings("unchecked")
261 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
262 if (installer == null) {
263 throw new IntentException("no installer for class " + intent.getClass());
264 }
265 return installer;
266 }
267
268 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700269 * Compiles an intent recursively.
270 *
271 * @param intent intent
272 * @return result of compilation
273 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800274 List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700275 if (intent.isInstallable()) {
276 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700277 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700278
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700279 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700280 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700281 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800282 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
283 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700284 }
tom85258ee2014-10-07 00:10:02 -0700285 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700286 }
287
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800288 //TODO javadoc
289 //FIXME
Brian O'Connor7775bda2015-02-06 15:01:18 -0800290 FlowRuleOperations coordinate(IntentData pending) {
291 List<Intent> installables = pending.installables();
292 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>(installables.size());
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800293 for (Intent installable : installables) {
294 try {
295 registerSubclassInstallerIfNeeded(installable);
296 //FIXME need to migrate installers to FlowRuleOperations
297 // FIXME need to aggregate the FlowRuleOperations across installables
Brian O'Connor7775bda2015-02-06 15:01:18 -0800298 plans.add(getInstaller(installable).install(installable));
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800299 } catch (Exception e) { // TODO this should be IntentException
300 throw new FlowRuleBatchOperationConversionException(null/*FIXME*/, e);
301 }
302 }
Brian O'Connor7775bda2015-02-06 15:01:18 -0800303
304 return merge(plans).build(new FlowRuleOperationsContext() { // FIXME move this out
305 @Override
306 public void onSuccess(FlowRuleOperations ops) {
307 log.info("Completed installing: {}", pending.key());
308 pending.setState(INSTALLED);
309 store.write(pending);
310 }
311
312 @Override
313 public void onError(FlowRuleOperations ops) {
314 //FIXME store.write(pending.setState(BROKEN));
315 }
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}