blob: 7325dd51b054e56717f5bbd78ef6d300e9bbbebd [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'Connorabafb502014-12-02 22:26:20 -080031import org.onosproject.net.flow.FlowRuleBatchOperation;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080032import org.onosproject.net.flow.FlowRuleOperations;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.net.flow.FlowRuleService;
34import org.onosproject.net.intent.Intent;
35import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080037import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.intent.IntentEvent;
39import org.onosproject.net.intent.IntentException;
40import org.onosproject.net.intent.IntentExtensionService;
41import org.onosproject.net.intent.IntentId;
42import org.onosproject.net.intent.IntentInstaller;
43import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.intent.IntentService;
45import org.onosproject.net.intent.IntentState;
46import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070048import org.slf4j.Logger;
49
Brian O'Connor0e271dc2015-02-04 18:20:25 -080050import java.util.ArrayList;
51import java.util.Collection;
52import java.util.EnumSet;
53import java.util.List;
54import java.util.Map;
55import java.util.Optional;
56import java.util.concurrent.Callable;
57import java.util.concurrent.ConcurrentHashMap;
58import java.util.concurrent.ConcurrentMap;
59import java.util.concurrent.ExecutionException;
60import java.util.concurrent.ExecutorService;
61import java.util.concurrent.Future;
62import java.util.stream.Collectors;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070063
Brian O'Connorfa81eae2014-10-30 13:20:05 -070064import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080065import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connordb15b042015-02-04 14:59:28 -080066import static java.util.concurrent.Executors.newSingleThreadExecutor;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070067import static org.onlab.util.Tools.namedThreads;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080068import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070069import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070070
71/**
72 * An implementation of Intent Manager.
73 */
74@Component(immediate = true)
75@Service
76public class IntentManager
77 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080078 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070079
80 public static final String INTENT_NULL = "Intent cannot be null";
81 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
82
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080083 private static final int NUM_THREADS = 12;
84
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080085 private static final EnumSet<IntentState> RECOMPILE
86 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080087
88
Brian O'Connor66630c82014-10-02 21:08:19 -070089 // Collections for compiler, installer, and listener are ONOS instance local
90 private final ConcurrentMap<Class<? extends Intent>,
91 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070092 private final ConcurrentMap<Class<? extends Intent>,
93 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070094
95 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070096 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070097
Brian O'Connor520c0522014-11-23 23:50:47 -080098 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected IntentStore store;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700105 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700108 protected EventDeliveryService eventDispatcher;
109
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected FlowRuleService flowRuleService;
112
Brian O'Connor520c0522014-11-23 23:50:47 -0800113
Brian O'Connordb15b042015-02-04 14:59:28 -0800114 private ExecutorService batchExecutor;
115 private ExecutorService workerExecutor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800116
117 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
118 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
119 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
120 private IdGenerator idGenerator;
121
Brian O'Connorb499b352015-02-03 16:46:15 -0800122 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800123
Brian O'Connor66630c82014-10-02 21:08:19 -0700124 @Activate
125 public void activate() {
126 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700127 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700128 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connordb15b042015-02-04 14:59:28 -0800129 batchExecutor = newSingleThreadExecutor(namedThreads("onos-intent-batch"));
130 workerExecutor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-worker-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800131 idGenerator = coreService.getIdGenerator("intent-ids");
132 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700133 log.info("Started");
134 }
135
136 @Deactivate
137 public void deactivate() {
138 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700139 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700140 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connordb15b042015-02-04 14:59:28 -0800141 batchExecutor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800142 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700143 log.info("Stopped");
144 }
145
146 @Override
147 public void submit(Intent intent) {
148 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800149 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
150 //FIXME timestamp?
151 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700152 }
153
154 @Override
155 public void withdraw(Intent intent) {
156 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800157 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
158 //FIXME timestamp?
159 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700160 }
161
Brian O'Connor66630c82014-10-02 21:08:19 -0700162 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700163 public Iterable<Intent> getIntents() {
164 return store.getIntents();
165 }
166
167 @Override
168 public long getIntentCount() {
169 return store.getIntentCount();
170 }
171
172 @Override
173 public Intent getIntent(IntentId id) {
174 checkNotNull(id, INTENT_ID_NULL);
175 return store.getIntent(id);
176 }
177
178 @Override
179 public IntentState getIntentState(IntentId id) {
180 checkNotNull(id, INTENT_ID_NULL);
181 return store.getIntentState(id);
182 }
183
184 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700185 public List<Intent> getInstallableIntents(IntentId intentId) {
186 checkNotNull(intentId, INTENT_ID_NULL);
187 return store.getInstallableIntents(intentId);
188 }
189
190 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700191 public void addListener(IntentListener listener) {
192 listenerRegistry.addListener(listener);
193 }
194
195 @Override
196 public void removeListener(IntentListener listener) {
197 listenerRegistry.removeListener(listener);
198 }
199
200 @Override
201 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
202 compilers.put(cls, compiler);
203 }
204
205 @Override
206 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
207 compilers.remove(cls);
208 }
209
210 @Override
211 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
212 return ImmutableMap.copyOf(compilers);
213 }
214
215 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700216 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700217 installers.put(cls, installer);
218 }
219
220 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700221 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700222 installers.remove(cls);
223 }
224
225 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700226 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700227 return ImmutableMap.copyOf(installers);
228 }
229
230 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700231 * Returns the corresponding intent compiler to the specified intent.
232 *
233 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700234 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700235 * @return intent compiler corresponding to the specified intent
236 */
237 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
238 @SuppressWarnings("unchecked")
239 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
240 if (compiler == null) {
241 throw new IntentException("no compiler for class " + intent.getClass());
242 }
243 return compiler;
244 }
245
246 /**
247 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700248 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700249 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700250 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700251 * @return intent installer corresponding to the specified installable intent
252 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700253 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700254 @SuppressWarnings("unchecked")
255 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
256 if (installer == null) {
257 throw new IntentException("no installer for class " + intent.getClass());
258 }
259 return installer;
260 }
261
262 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700263 * Compiles an intent recursively.
264 *
265 * @param intent intent
266 * @return result of compilation
267 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800268 List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700269 if (intent.isInstallable()) {
270 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700271 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700272
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700273 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700274 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700275 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800276 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
277 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700278 }
tom85258ee2014-10-07 00:10:02 -0700279 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700280 }
281
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800282 //TODO javadoc
283 //FIXME
284 FlowRuleOperations coordinate(List<Intent> installables) {
285 //List<FlowRuleBatchOperation> batches = new ArrayList<>(installables.size());
286 for (Intent installable : installables) {
287 try {
288 registerSubclassInstallerIfNeeded(installable);
289 //FIXME need to migrate installers to FlowRuleOperations
290 // FIXME need to aggregate the FlowRuleOperations across installables
291 getInstaller(installable).install2(installable).build(null/*FIXME*/);
292 } catch (Exception e) { // TODO this should be IntentException
293 throw new FlowRuleBatchOperationConversionException(null/*FIXME*/, e);
294 }
295 }
296 return null;
297 }
298
Brian O'Connor66630c82014-10-02 21:08:19 -0700299 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700300 * Uninstalls all installable intents associated with the given intent.
301 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800302 * @param intent intent
303 * @param installables installable intents
304 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700305 */
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800306 //FIXME
307 FlowRuleOperations uninstallIntent(Intent intent, List<Intent> installables) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700308 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800309 for (Intent installable : installables) {
310 trackerService.removeTrackedResources(intent.id(),
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800311 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700312 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800313 // FIXME need to aggregate the FlowRuleOperations across installables
314 getInstaller(installable).uninstall2(installable).build(null/*FIXME*/);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700315 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800316 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700317 // TODO: this should never happen. but what if it does?
318 }
319 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800320 return null; //FIXME
tom85258ee2014-10-07 00:10:02 -0700321 }
322
323 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700324 * Registers an intent compiler of the specified intent if an intent compiler
325 * for the intent is not registered. This method traverses the class hierarchy of
326 * the intent. Once an intent compiler for a parent type is found, this method
327 * registers the found intent compiler.
328 *
329 * @param intent intent
330 */
331 private void registerSubclassCompilerIfNeeded(Intent intent) {
332 if (!compilers.containsKey(intent.getClass())) {
333 Class<?> cls = intent.getClass();
334 while (cls != Object.class) {
335 // As long as we're within the Intent class descendants
336 if (Intent.class.isAssignableFrom(cls)) {
337 IntentCompiler<?> compiler = compilers.get(cls);
338 if (compiler != null) {
339 compilers.put(intent.getClass(), compiler);
340 return;
341 }
342 }
343 cls = cls.getSuperclass();
344 }
345 }
346 }
347
348 /**
349 * Registers an intent installer of the specified intent if an intent installer
350 * for the intent is not registered. This method traverses the class hierarchy of
351 * the intent. Once an intent installer for a parent type is found, this method
352 * registers the found intent installer.
353 *
354 * @param intent intent
355 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700356 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700357 if (!installers.containsKey(intent.getClass())) {
358 Class<?> cls = intent.getClass();
359 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700360 // As long as we're within the Intent class descendants
361 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700362 IntentInstaller<?> installer = installers.get(cls);
363 if (installer != null) {
364 installers.put(intent.getClass(), installer);
365 return;
366 }
367 }
368 cls = cls.getSuperclass();
369 }
370 }
371 }
372
Brian O'Connor66630c82014-10-02 21:08:19 -0700373 // Store delegate to re-post events emitted from the store.
374 private class InternalStoreDelegate implements IntentStoreDelegate {
375 @Override
376 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700377 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700378 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800379
380 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800381 public void process(IntentData data) {
382 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800383 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700384 }
385
Brian O'Connor72a034c2014-11-26 18:24:23 -0800386 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
387 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800388 // Attempt recompilation of the specified intents first.
389 for (IntentId id : intentIds) {
390 Intent intent = store.getIntent(id);
391 if (intent == null) {
392 continue;
393 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800394 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800395 }
396
397 if (compileAllFailed) {
398 // If required, compile all currently failed intents.
399 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800400 IntentState state = getIntentState(intent.id());
401 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800402 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800403 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800404 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800405 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800406 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800407 }
408 }
409 }
410
Brian O'Connorb499b352015-02-03 16:46:15 -0800411 //FIXME
412// for (ApplicationId appId : batches.keySet()) {
413// if (batchService.isLocalLeader(appId)) {
414// execute(batches.get(appId).build());
415// }
416// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800417 }
418
tom95329eb2014-10-06 08:40:06 -0700419 // Topology change delegate
420 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
421 @Override
tom85258ee2014-10-07 00:10:02 -0700422 public void triggerCompile(Iterable<IntentId> intentIds,
423 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800424 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700425 }
tom95329eb2014-10-06 08:40:06 -0700426 }
tom85258ee2014-10-07 00:10:02 -0700427
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800428 // TODO: simplify the branching statements
Brian O'Connorb499b352015-02-03 16:46:15 -0800429 private IntentUpdate createIntentUpdate(IntentData intentData) {
Brian O'Connorb499b352015-02-03 16:46:15 -0800430 switch (intentData.state()) {
431 case INSTALL_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800432 return new InstallRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800433 case WITHDRAW_REQ:
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800434 return new WithdrawRequest(this, intentData);
Brian O'Connorb499b352015-02-03 16:46:15 -0800435 // fallthrough
436 case COMPILING:
437 case INSTALLING:
438 case INSTALLED:
439 case RECOMPILING:
440 case WITHDRAWING:
441 case WITHDRAWN:
442 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800443 default:
444 // illegal state
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800445 return new CompilingFailed(intentData);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700446 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700447 }
448
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800449 private Future<CompletedIntentUpdate> submitIntentData(IntentData data) {
450 return workerExecutor.submit(new IntentWorker(data));
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800451 }
452
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800453 private class IntentBatchPreprocess implements Runnable {
454
455 // TODO make this configurable
456 private static final int TIMEOUT_PER_OP = 500; // ms
457 protected static final int MAX_ATTEMPTS = 3;
458
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800459 protected final Collection<IntentData> data;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800460
461 // future holding current FlowRuleBatch installation result
462 protected final long startTime = System.currentTimeMillis();
463 protected final long endTime;
464
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800465 private IntentBatchPreprocess(Collection<IntentData> data, long endTime) {
466 this.data = checkNotNull(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800467 this.endTime = endTime;
468 }
469
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800470 public IntentBatchPreprocess(Collection<IntentData> data) {
471 this(data, System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800472 }
473
474 // FIXME compute reasonable timeouts
475 protected long calculateTimeoutLimit() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800476 return System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800477 }
478
479 @Override
480 public void run() {
481 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800482 /*
483 1. wrap each intentdata in a runnable and submit
484 2. wait for completion of all the work
485 3. accumulate results and submit batch write of IntentData to store
486 (we can also try to update these individually)
487 */
488 submitUpdates(waitForFutures(createIntentUpdates()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800489 } catch (Exception e) {
490 log.error("Error submitting batches:", e);
491 // FIXME incomplete Intents should be cleaned up
492 // (transition to FAILED, etc.)
493
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800494 // the batch has failed
495 // TODO: maybe we should do more?
496 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800497 //FIXME
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800498// batchService.removeIntentOperations(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800499 }
500 }
501
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800502 private List<Future<CompletedIntentUpdate>> createIntentUpdates() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800503 return data.stream()
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800504 .map(IntentManager.this::submitIntentData)
505 .collect(Collectors.toList());
506 }
507
508 private List<CompletedIntentUpdate> waitForFutures(List<Future<CompletedIntentUpdate>> futures) {
509 ImmutableList.Builder<CompletedIntentUpdate> updateBuilder = ImmutableList.builder();
510 for (Future<CompletedIntentUpdate> future : futures) {
511 try {
512 updateBuilder.add(future.get());
513 } catch (InterruptedException | ExecutionException e) {
514 //FIXME
515 log.warn("Future failed: {}", e);
516 }
517 }
518 return updateBuilder.build();
519 }
520
521 private void submitUpdates(List<CompletedIntentUpdate> updates) {
522 store.batchWrite(updates.stream()
523 .map(CompletedIntentUpdate::data)
524 .collect(Collectors.toList()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800525 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800526 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800527
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800528 private final class IntentWorker implements Callable<CompletedIntentUpdate> {
Brian O'Connordb15b042015-02-04 14:59:28 -0800529
530 private final IntentData data;
531
532 private IntentWorker(IntentData data) {
533 this.data = data;
534 }
535
536 @Override
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800537 public CompletedIntentUpdate call() throws Exception {
Brian O'Connordb15b042015-02-04 14:59:28 -0800538 IntentUpdate update = createIntentUpdate(data);
539 Optional<IntentUpdate> currentPhase = Optional.of(update);
540 IntentUpdate previousPhase = update;
541
542 while (currentPhase.isPresent()) {
543 previousPhase = currentPhase.get();
544 currentPhase = previousPhase.execute();
545 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800546 return (CompletedIntentUpdate) previousPhase;
Brian O'Connor427a1762014-11-19 18:40:32 -0800547 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700548 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700549
550 private class InternalBatchDelegate implements IntentBatchDelegate {
551 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800552 public void execute(Collection<IntentData> operations) {
553 log.info("Execute {} operation(s).", operations.size());
554 log.debug("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800555 batchExecutor.execute(new IntentBatchPreprocess(operations));
556 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700557 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700558 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800559
560// /////////**************************///////////////////
561// FIXME Need to build and monitor contexts from FlowRuleService
562//
563// // TODO: better naming
564// private class IntentBatchApplyFirst extends IntentBatchPreprocess {
565//
566// protected final List<CompletedIntentUpdate> intentUpdates;
567// protected final int installAttempt;
568// protected Future<CompletedBatchOperation> future;
569//
570// IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
571// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
572// super(operations, endTime);
573// this.intentUpdates = ImmutableList.copyOf(intentUpdates);
574// this.future = future;
575// this.installAttempt = installAttempt;
576// }
577//
578// @Override
579// public void run() {
580// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
581// new IntentBatchProcessFutures(data, intentUpdates, endTime, installAttempt, future).run();
582// }
583//
584// /**
585// * Builds and applies the next batch, and returns the future.
586// *
587// * @return Future for next batch
588// */
589// protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
590// //TODO test this. (also, maybe save this batch)
591//
592// FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
593// if (batch.size() > 0) {
594// //FIXME apply batch might throw an exception
595// return flowRuleService.applyBatch(batch);
596// } else {
597// return null;
598// }
599// }
600//
601// private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
602// FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
603// for (CompletedIntentUpdate update : intentUpdates) {
604// FlowRuleBatchOperation currentBatch = update.currentBatch();
605// if (currentBatch != null) {
606// batch.addAll(currentBatch);
607// }
608// }
609// return batch;
610// }
611//
612// protected void abandonShip() {
613// // the batch has failed
614// // TODO: maybe we should do more?
615// log.error("Walk the plank, matey...");
616// future = null;
617// //FIXME
618// //batchService.removeIntentOperations(data);
619// }
620// }
621//
622// // TODO: better naming
623// private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
624//
625// IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
626// long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
627// super(operations, intentUpdates, endTime, installAttempt, future);
628// }
629//
630// @Override
631// public void run() {
632// try {
633// Future<CompletedBatchOperation> future = processFutures();
634// if (future == null) {
635// // there are no outstanding batches; we are done
636// //FIXME
637// return; //?
638// //batchService.removeIntentOperations(data);
639// } else if (System.currentTimeMillis() > endTime) {
640// // - cancel current FlowRuleBatch and resubmit again
641// retry();
642// } else {
643// // we are not done yet, yield the thread by resubmitting ourselves
644// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, endTime,
645// installAttempt, future));
646// }
647// } catch (Exception e) {
648// log.error("Error submitting batches:", e);
649// // FIXME incomplete Intents should be cleaned up
650// // (transition to FAILED, etc.)
651// abandonShip();
652// }
653// }
654//
655// /**
656// * Iterate through the pending futures, and remove them when they have completed.
657// */
658// private Future<CompletedBatchOperation> processFutures() {
659// try {
660// CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
661// updateBatches(completed);
662// return applyNextBatch(intentUpdates);
663// } catch (TimeoutException | InterruptedException te) {
664// log.trace("Installation of intents are still pending: {}", data);
665// return future;
666// } catch (ExecutionException e) {
667// log.warn("Execution of batch failed: {}", data, e);
668// abandonShip();
669// return future;
670// }
671// }
672//
673// private void updateBatches(CompletedBatchOperation completed) {
674// if (completed.isSuccess()) {
675// for (CompletedIntentUpdate update : intentUpdates) {
676// update.batchSuccess();
677// }
678// } else {
679// // entire batch has been reverted...
680// log.debug("Failed items: {}", completed.failedItems());
681// log.debug("Failed ids: {}", completed.failedIds());
682//
683// for (Long id : completed.failedIds()) {
684// IntentId targetId = IntentId.valueOf(id);
685// for (CompletedIntentUpdate update : intentUpdates) {
686// for (Intent intent : update.allInstallables()) {
687// if (intent.id().equals(targetId)) {
688// update.batchFailed();
689// break;
690// }
691// }
692// }
693// // don't increment the non-failed items, as they have been reverted.
694// }
695// }
696// }
697//
698// private void retry() {
699// log.debug("Execution timed out, retrying.");
700// if (future.cancel(true)) { // cancel success; batch is reverted
701// // reset the timer
702// long timeLimit = calculateTimeoutLimit();
703// int attempts = installAttempt + 1;
704// if (attempts == MAX_ATTEMPTS) {
705// log.warn("Install request timed out: {}", data);
706// for (CompletedIntentUpdate update : intentUpdates) {
707// update.batchFailed();
708// }
709// } else if (attempts > MAX_ATTEMPTS) {
710// abandonShip();
711// return;
712// }
713// Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
714// batchExecutor.submit(new IntentBatchProcessFutures(data, intentUpdates, timeLimit, attempts, future));
715// } else {
716// log.error("Cancelling FlowRuleBatch failed.");
717// abandonShip();
718// }
719// }
720// }
Brian O'Connor66630c82014-10-02 21:08:19 -0700721}