blob: 6551c858541a2f35eaa31bb34bc4fd550298313d [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'Connor03406a42015-02-03 17:28:57 -080018import java.util.ArrayList;
19import java.util.Collection;
20import java.util.Collections;
21import java.util.EnumSet;
Brian O'Connor03406a42015-02-03 17:28:57 -080022import java.util.List;
23import java.util.Map;
24import java.util.Optional;
Brian O'Connordb15b042015-02-04 14:59:28 -080025import java.util.concurrent.Callable;
Brian O'Connor03406a42015-02-03 17:28:57 -080026import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.ConcurrentMap;
28import java.util.concurrent.ExecutionException;
29import java.util.concurrent.ExecutorService;
30import java.util.concurrent.Future;
31import java.util.concurrent.TimeUnit;
32import java.util.concurrent.TimeoutException;
33import java.util.stream.Collectors;
34
Brian O'Connor66630c82014-10-02 21:08:19 -070035import org.apache.felix.scr.annotations.Activate;
36import org.apache.felix.scr.annotations.Component;
37import org.apache.felix.scr.annotations.Deactivate;
38import org.apache.felix.scr.annotations.Reference;
39import org.apache.felix.scr.annotations.ReferenceCardinality;
40import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.core.CoreService;
42import org.onosproject.core.IdGenerator;
43import org.onosproject.event.AbstractListenerRegistry;
44import org.onosproject.event.EventDeliveryService;
45import org.onosproject.net.flow.CompletedBatchOperation;
46import org.onosproject.net.flow.FlowRuleBatchOperation;
47import org.onosproject.net.flow.FlowRuleService;
48import org.onosproject.net.intent.Intent;
49import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080051import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.intent.IntentEvent;
53import org.onosproject.net.intent.IntentException;
54import org.onosproject.net.intent.IntentExtensionService;
55import org.onosproject.net.intent.IntentId;
56import org.onosproject.net.intent.IntentInstaller;
57import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080058import org.onosproject.net.intent.IntentService;
59import org.onosproject.net.intent.IntentState;
60import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080061import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070062import org.slf4j.Logger;
63
Brian O'Connor03406a42015-02-03 17:28:57 -080064import com.google.common.collect.ImmutableList;
65import com.google.common.collect.ImmutableMap;
66import com.google.common.collect.Lists;
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'Connor03406a42015-02-03 17:28:57 -080072import static org.onosproject.net.intent.IntentState.FAILED;
Brian O'Connor03406a42015-02-03 17:28:57 -080073import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
Brian O'Connor03406a42015-02-03 17:28:57 -080074import static org.onosproject.net.intent.IntentState.WITHDRAW_REQ;
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
288 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700289 * Uninstalls all installable intents associated with the given intent.
290 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800291 * @param intent intent
292 * @param installables installable intents
293 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700294 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800295 List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700296 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800297 for (Intent installable : installables) {
298 trackerService.removeTrackedResources(intent.id(),
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800299 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700300 try {
301 batches.addAll(getInstaller(installable).uninstall(installable));
302 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800303 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700304 // TODO: this should never happen. but what if it does?
305 }
306 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800307 return batches;
tom85258ee2014-10-07 00:10:02 -0700308 }
309
310 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700311 * Registers an intent compiler of the specified intent if an intent compiler
312 * for the intent is not registered. This method traverses the class hierarchy of
313 * the intent. Once an intent compiler for a parent type is found, this method
314 * registers the found intent compiler.
315 *
316 * @param intent intent
317 */
318 private void registerSubclassCompilerIfNeeded(Intent intent) {
319 if (!compilers.containsKey(intent.getClass())) {
320 Class<?> cls = intent.getClass();
321 while (cls != Object.class) {
322 // As long as we're within the Intent class descendants
323 if (Intent.class.isAssignableFrom(cls)) {
324 IntentCompiler<?> compiler = compilers.get(cls);
325 if (compiler != null) {
326 compilers.put(intent.getClass(), compiler);
327 return;
328 }
329 }
330 cls = cls.getSuperclass();
331 }
332 }
333 }
334
335 /**
336 * Registers an intent installer of the specified intent if an intent installer
337 * for the intent is not registered. This method traverses the class hierarchy of
338 * the intent. Once an intent installer for a parent type is found, this method
339 * registers the found intent installer.
340 *
341 * @param intent intent
342 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700343 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700344 if (!installers.containsKey(intent.getClass())) {
345 Class<?> cls = intent.getClass();
346 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700347 // As long as we're within the Intent class descendants
348 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700349 IntentInstaller<?> installer = installers.get(cls);
350 if (installer != null) {
351 installers.put(intent.getClass(), installer);
352 return;
353 }
354 }
355 cls = cls.getSuperclass();
356 }
357 }
358 }
359
Brian O'Connor66630c82014-10-02 21:08:19 -0700360 // Store delegate to re-post events emitted from the store.
361 private class InternalStoreDelegate implements IntentStoreDelegate {
362 @Override
363 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700364 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700365 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800366
367 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800368 public void process(IntentData data) {
369 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800370 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700371 }
372
Brian O'Connor72a034c2014-11-26 18:24:23 -0800373 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
374 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800375 // Attempt recompilation of the specified intents first.
376 for (IntentId id : intentIds) {
377 Intent intent = store.getIntent(id);
378 if (intent == null) {
379 continue;
380 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800381 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800382 }
383
384 if (compileAllFailed) {
385 // If required, compile all currently failed intents.
386 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800387 IntentState state = getIntentState(intent.id());
388 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800389 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800390 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800391 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800392 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800393 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800394 }
395 }
396 }
397
Brian O'Connorb499b352015-02-03 16:46:15 -0800398 //FIXME
399// for (ApplicationId appId : batches.keySet()) {
400// if (batchService.isLocalLeader(appId)) {
401// execute(batches.get(appId).build());
402// }
403// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800404 }
405
tom95329eb2014-10-06 08:40:06 -0700406 // Topology change delegate
407 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
408 @Override
tom85258ee2014-10-07 00:10:02 -0700409 public void triggerCompile(Iterable<IntentId> intentIds,
410 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800411 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700412 }
tom95329eb2014-10-06 08:40:06 -0700413 }
tom85258ee2014-10-07 00:10:02 -0700414
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800415 // TODO: simplify the branching statements
Brian O'Connorb499b352015-02-03 16:46:15 -0800416 private IntentUpdate createIntentUpdate(IntentData intentData) {
417 IntentData currentState = store.getIntentData(intentData.key());
418 switch (intentData.state()) {
419 case INSTALL_REQ:
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800420 return new InstallRequest(this, intentData.intent(), currentState);
Brian O'Connorb499b352015-02-03 16:46:15 -0800421 case WITHDRAW_REQ:
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800422 return new WithdrawRequest(this, intentData.intent(), currentState);
Brian O'Connorb499b352015-02-03 16:46:15 -0800423 // fallthrough
424 case COMPILING:
425 case INSTALLING:
426 case INSTALLED:
427 case RECOMPILING:
428 case WITHDRAWING:
429 case WITHDRAWN:
430 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800431 default:
432 // illegal state
433 return new DoNothing();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700434 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700435 }
436
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800437 List<FlowRuleBatchOperation> convert(List<Intent> installables) {
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800438 List<FlowRuleBatchOperation> batches = new ArrayList<>(installables.size());
439 for (Intent installable : installables) {
440 try {
441 registerSubclassInstallerIfNeeded(installable);
442 batches.addAll(getInstaller(installable).install(installable));
443 } catch (Exception e) { // TODO this should be IntentException
Brian O'Connor098da072015-02-04 14:07:59 -0800444 throw new FlowRuleBatchOperationConversionException(batches, e);
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800445 }
446 }
447 return batches;
448 }
449
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800450 private class IntentBatchPreprocess implements Runnable {
451
452 // TODO make this configurable
453 private static final int TIMEOUT_PER_OP = 500; // ms
454 protected static final int MAX_ATTEMPTS = 3;
455
Brian O'Connorb499b352015-02-03 16:46:15 -0800456 protected final Collection<IntentData> ops;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800457
458 // future holding current FlowRuleBatch installation result
459 protected final long startTime = System.currentTimeMillis();
460 protected final long endTime;
461
Brian O'Connorb499b352015-02-03 16:46:15 -0800462 private IntentBatchPreprocess(Collection<IntentData> ops, long endTime) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800463 this.ops = checkNotNull(ops);
464 this.endTime = endTime;
465 }
466
Brian O'Connorb499b352015-02-03 16:46:15 -0800467 public IntentBatchPreprocess(Collection<IntentData> ops) {
468 this(ops, System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800469 }
470
471 // FIXME compute reasonable timeouts
472 protected long calculateTimeoutLimit() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800473 return System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800474 }
475
476 @Override
477 public void run() {
478 try {
Brian O'Connordb15b042015-02-04 14:59:28 -0800479 // 1. wrap each intentdata in a runnable and submit
480 List<Future<IntentUpdate>> updates = createIntentUpdates();
481 // TODO
482 // 2. wait for completion of all the work
483 // 3. accumulate results and submit batch write of IntentData to store
484 // (we can also try to update these individually)
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800485
Brian O'Connordb15b042015-02-04 14:59:28 -0800486
487 //new IntentBatchApplyFirst(ops, processIntentUpdates(updates), endTime, 0, null).run();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800488 } catch (Exception e) {
489 log.error("Error submitting batches:", e);
490 // FIXME incomplete Intents should be cleaned up
491 // (transition to FAILED, etc.)
492
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800493 // the batch has failed
494 // TODO: maybe we should do more?
495 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800496 //FIXME
497// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800498 }
499 }
500
Brian O'Connordb15b042015-02-04 14:59:28 -0800501 private List<Future<IntentUpdate>> createIntentUpdates() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800502 return ops.stream()
Brian O'Connordb15b042015-02-04 14:59:28 -0800503 .map(IntentManager.this::submitIntentData)
504 .collect(Collectors.toList());
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800505 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800506 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800507
Brian O'Connordb15b042015-02-04 14:59:28 -0800508 private Future<IntentUpdate> submitIntentData(IntentData data) {
509 return workerExecutor.submit(new IntentWorker(data));
510 }
511
512 private class IntentWorker implements Callable<IntentUpdate> {
513
514 private final IntentData data;
515
516 private IntentWorker(IntentData data) {
517 this.data = data;
518 }
519
520 @Override
521 public IntentUpdate call() throws Exception {
522 IntentUpdate update = createIntentUpdate(data);
523 Optional<IntentUpdate> currentPhase = Optional.of(update);
524 IntentUpdate previousPhase = update;
525
526 while (currentPhase.isPresent()) {
527 previousPhase = currentPhase.get();
528 currentPhase = previousPhase.execute();
529 }
530 return previousPhase;
531 }
532 }
533
534
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800535 // TODO: better naming
536 private class IntentBatchApplyFirst extends IntentBatchPreprocess {
537
538 protected final List<CompletedIntentUpdate> intentUpdates;
539 protected final int installAttempt;
540 protected Future<CompletedBatchOperation> future;
541
Brian O'Connorb499b352015-02-03 16:46:15 -0800542 IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800543 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
544 super(operations, endTime);
545 this.intentUpdates = ImmutableList.copyOf(intentUpdates);
546 this.future = future;
547 this.installAttempt = installAttempt;
548 }
549
550 @Override
551 public void run() {
552 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
553 new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future).run();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700554 }
555
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700556 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800557 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700558 *
559 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700560 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800561 protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800562 //TODO test this. (also, maybe save this batch)
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800563
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800564 FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
Ray Milkey93508c22014-12-02 11:35:56 -0800565 if (batch.size() > 0) {
566 //FIXME apply batch might throw an exception
567 return flowRuleService.applyBatch(batch);
568 } else {
Ray Milkey93508c22014-12-02 11:35:56 -0800569 return null;
570 }
571 }
572
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800573 private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800574 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800575 for (CompletedIntentUpdate update : intentUpdates) {
576 FlowRuleBatchOperation currentBatch = update.currentBatch();
577 if (currentBatch != null) {
578 batch.addAll(currentBatch);
579 }
580 }
581 return batch;
582 }
583
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800584 protected void abandonShip() {
585 // the batch has failed
586 // TODO: maybe we should do more?
587 log.error("Walk the plank, matey...");
588 future = null;
Brian O'Connorb499b352015-02-03 16:46:15 -0800589 //FIXME
590// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800591 }
592 }
593
594 // TODO: better naming
595 private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
596
Brian O'Connorb499b352015-02-03 16:46:15 -0800597 IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800598 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
599 super(operations, intentUpdates, endTime, installAttempt, future);
600 }
601
602 @Override
603 public void run() {
604 try {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800605 Future<CompletedBatchOperation> future = processFutures();
606 if (future == null) {
607 // there are no outstanding batches; we are done
Brian O'Connorb499b352015-02-03 16:46:15 -0800608 //FIXME
609 return; //?
610// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800611 } else if (System.currentTimeMillis() > endTime) {
612 // - cancel current FlowRuleBatch and resubmit again
613 retry();
614 } else {
615 // we are not done yet, yield the thread by resubmitting ourselves
Brian O'Connordb15b042015-02-04 14:59:28 -0800616 batchExecutor.submit(new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800617 }
618 } catch (Exception e) {
619 log.error("Error submitting batches:", e);
620 // FIXME incomplete Intents should be cleaned up
621 // (transition to FAILED, etc.)
622 abandonShip();
623 }
624 }
625
626 /**
627 * Iterate through the pending futures, and remove them when they have completed.
628 */
629 private Future<CompletedBatchOperation> processFutures() {
630 try {
631 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
632 updateBatches(completed);
633 return applyNextBatch(intentUpdates);
634 } catch (TimeoutException | InterruptedException te) {
635 log.trace("Installation of intents are still pending: {}", ops);
636 return future;
637 } catch (ExecutionException e) {
638 log.warn("Execution of batch failed: {}", ops, e);
639 abandonShip();
640 return future;
641 }
642 }
643
Ray Milkey93508c22014-12-02 11:35:56 -0800644 private void updateBatches(CompletedBatchOperation completed) {
645 if (completed.isSuccess()) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800646 for (CompletedIntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -0800647 update.batchSuccess();
648 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700649 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800650 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -0800651 log.debug("Failed items: {}", completed.failedItems());
652 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -0800653
654 for (Long id : completed.failedIds()) {
655 IntentId targetId = IntentId.valueOf(id);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800656 for (CompletedIntentUpdate update : intentUpdates) {
657 for (Intent intent : update.allInstallables()) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800658 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800659 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800660 break;
661 }
662 }
663 }
664 // don't increment the non-failed items, as they have been reverted.
665 }
666 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700667 }
668
Brian O'Connor427a1762014-11-19 18:40:32 -0800669 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800670 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800671 if (future.cancel(true)) { // cancel success; batch is reverted
672 // reset the timer
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800673 long timeLimit = calculateTimeoutLimit();
674 int attempts = installAttempt + 1;
675 if (attempts == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800676 log.warn("Install request timed out: {}", ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800677 for (CompletedIntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800678 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800679 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800680 } else if (attempts > MAX_ATTEMPTS) {
Ray Milkey93508c22014-12-02 11:35:56 -0800681 abandonShip();
682 return;
Sho SHIMIZU55a55182015-02-03 22:32:56 -0800683 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800684 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
Brian O'Connordb15b042015-02-04 14:59:28 -0800685 batchExecutor.submit(new IntentBatchProcessFutures(ops, intentUpdates, timeLimit, attempts, future));
Brian O'Connor427a1762014-11-19 18:40:32 -0800686 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800687 log.error("Cancelling FlowRuleBatch failed.");
Ray Milkey93508c22014-12-02 11:35:56 -0800688 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -0800689 }
690 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700691 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700692
693 private class InternalBatchDelegate implements IntentBatchDelegate {
694 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800695 public void execute(Collection<IntentData> operations) {
696 log.info("Execute {} operation(s).", operations.size());
697 log.debug("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800698 batchExecutor.execute(new IntentBatchPreprocess(operations));
699 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700700 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700701 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700702}