blob: 05efb222d18f978baf141b0f5b263b0d91cb19cb [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;
25import java.util.concurrent.ConcurrentHashMap;
26import java.util.concurrent.ConcurrentMap;
27import java.util.concurrent.ExecutionException;
28import java.util.concurrent.ExecutorService;
29import java.util.concurrent.Future;
30import java.util.concurrent.TimeUnit;
31import java.util.concurrent.TimeoutException;
32import java.util.stream.Collectors;
33
Brian O'Connor66630c82014-10-02 21:08:19 -070034import org.apache.felix.scr.annotations.Activate;
35import org.apache.felix.scr.annotations.Component;
36import org.apache.felix.scr.annotations.Deactivate;
37import org.apache.felix.scr.annotations.Reference;
38import org.apache.felix.scr.annotations.ReferenceCardinality;
39import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.core.CoreService;
41import org.onosproject.core.IdGenerator;
42import org.onosproject.event.AbstractListenerRegistry;
43import org.onosproject.event.EventDeliveryService;
44import org.onosproject.net.flow.CompletedBatchOperation;
45import org.onosproject.net.flow.FlowRuleBatchOperation;
46import org.onosproject.net.flow.FlowRuleService;
47import org.onosproject.net.intent.Intent;
48import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080049import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080050import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080051import org.onosproject.net.intent.IntentEvent;
52import org.onosproject.net.intent.IntentException;
53import org.onosproject.net.intent.IntentExtensionService;
54import org.onosproject.net.intent.IntentId;
55import org.onosproject.net.intent.IntentInstaller;
56import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080057import org.onosproject.net.intent.IntentService;
58import org.onosproject.net.intent.IntentState;
59import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080060import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070061import org.slf4j.Logger;
62
Brian O'Connor03406a42015-02-03 17:28:57 -080063import com.google.common.collect.ImmutableList;
64import com.google.common.collect.ImmutableMap;
65import com.google.common.collect.Lists;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070066
Brian O'Connorfa81eae2014-10-30 13:20:05 -070067import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080068import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070069import static org.onlab.util.Tools.namedThreads;
Brian O'Connor03406a42015-02-03 17:28:57 -080070import static org.onosproject.net.intent.IntentState.FAILED;
Brian O'Connor03406a42015-02-03 17:28:57 -080071import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
Brian O'Connor03406a42015-02-03 17:28:57 -080072import static org.onosproject.net.intent.IntentState.WITHDRAW_REQ;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070073import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070074
75/**
76 * An implementation of Intent Manager.
77 */
78@Component(immediate = true)
79@Service
80public class IntentManager
81 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080082 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070083
84 public static final String INTENT_NULL = "Intent cannot be null";
85 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
86
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080087 private static final int NUM_THREADS = 12;
88
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080089 private static final EnumSet<IntentState> RECOMPILE
90 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080091
92
Brian O'Connor66630c82014-10-02 21:08:19 -070093 // Collections for compiler, installer, and listener are ONOS instance local
94 private final ConcurrentMap<Class<? extends Intent>,
95 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070096 private final ConcurrentMap<Class<? extends Intent>,
97 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070098
99 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700100 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700101
Brian O'Connor520c0522014-11-23 23:50:47 -0800102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected IntentStore store;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700109 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700112 protected EventDeliveryService eventDispatcher;
113
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected FlowRuleService flowRuleService;
116
Brian O'Connor520c0522014-11-23 23:50:47 -0800117
118 private ExecutorService executor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800119
120 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
121 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
122 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
123 private IdGenerator idGenerator;
124
Brian O'Connorb499b352015-02-03 16:46:15 -0800125 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800126
Brian O'Connor66630c82014-10-02 21:08:19 -0700127 @Activate
128 public void activate() {
129 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700130 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700131 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Yuta HIGUCHI7a9fddd2014-12-03 15:30:25 -0800132 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800133 idGenerator = coreService.getIdGenerator("intent-ids");
134 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700135 log.info("Started");
136 }
137
138 @Deactivate
139 public void deactivate() {
140 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700141 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700142 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700143 executor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800144 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700145 log.info("Stopped");
146 }
147
148 @Override
149 public void submit(Intent intent) {
150 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800151 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
152 //FIXME timestamp?
153 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700154 }
155
156 @Override
157 public void withdraw(Intent intent) {
158 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800159 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
160 //FIXME timestamp?
161 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700162 }
163
Brian O'Connor66630c82014-10-02 21:08:19 -0700164 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700165 public Iterable<Intent> getIntents() {
166 return store.getIntents();
167 }
168
169 @Override
170 public long getIntentCount() {
171 return store.getIntentCount();
172 }
173
174 @Override
175 public Intent getIntent(IntentId id) {
176 checkNotNull(id, INTENT_ID_NULL);
177 return store.getIntent(id);
178 }
179
180 @Override
181 public IntentState getIntentState(IntentId id) {
182 checkNotNull(id, INTENT_ID_NULL);
183 return store.getIntentState(id);
184 }
185
186 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700187 public List<Intent> getInstallableIntents(IntentId intentId) {
188 checkNotNull(intentId, INTENT_ID_NULL);
189 return store.getInstallableIntents(intentId);
190 }
191
192 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700193 public void addListener(IntentListener listener) {
194 listenerRegistry.addListener(listener);
195 }
196
197 @Override
198 public void removeListener(IntentListener listener) {
199 listenerRegistry.removeListener(listener);
200 }
201
202 @Override
203 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
204 compilers.put(cls, compiler);
205 }
206
207 @Override
208 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
209 compilers.remove(cls);
210 }
211
212 @Override
213 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
214 return ImmutableMap.copyOf(compilers);
215 }
216
217 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700218 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700219 installers.put(cls, installer);
220 }
221
222 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700223 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700224 installers.remove(cls);
225 }
226
227 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700228 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700229 return ImmutableMap.copyOf(installers);
230 }
231
232 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700233 * Returns the corresponding intent compiler to the specified intent.
234 *
235 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700236 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700237 * @return intent compiler corresponding to the specified intent
238 */
239 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
240 @SuppressWarnings("unchecked")
241 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
242 if (compiler == null) {
243 throw new IntentException("no compiler for class " + intent.getClass());
244 }
245 return compiler;
246 }
247
248 /**
249 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700250 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700251 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700252 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 * @return intent installer corresponding to the specified installable intent
254 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700255 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700256 @SuppressWarnings("unchecked")
257 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
258 if (installer == null) {
259 throw new IntentException("no installer for class " + intent.getClass());
260 }
261 return installer;
262 }
263
264 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700265 * Compiles an intent recursively.
266 *
267 * @param intent intent
268 * @return result of compilation
269 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800270 List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700271 if (intent.isInstallable()) {
272 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700273 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700274
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700275 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700276 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700277 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800278 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
279 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700280 }
tom85258ee2014-10-07 00:10:02 -0700281 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700282 }
283
284 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700285 * Uninstalls all installable intents associated with the given intent.
286 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800287 * @param intent intent
288 * @param installables installable intents
289 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700290 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800291 List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700292 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800293 for (Intent installable : installables) {
294 trackerService.removeTrackedResources(intent.id(),
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800295 installable.resources());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700296 try {
297 batches.addAll(getInstaller(installable).uninstall(installable));
298 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800299 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700300 // TODO: this should never happen. but what if it does?
301 }
302 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800303 return batches;
tom85258ee2014-10-07 00:10:02 -0700304 }
305
306 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700307 * Registers an intent compiler of the specified intent if an intent compiler
308 * for the intent is not registered. This method traverses the class hierarchy of
309 * the intent. Once an intent compiler for a parent type is found, this method
310 * registers the found intent compiler.
311 *
312 * @param intent intent
313 */
314 private void registerSubclassCompilerIfNeeded(Intent intent) {
315 if (!compilers.containsKey(intent.getClass())) {
316 Class<?> cls = intent.getClass();
317 while (cls != Object.class) {
318 // As long as we're within the Intent class descendants
319 if (Intent.class.isAssignableFrom(cls)) {
320 IntentCompiler<?> compiler = compilers.get(cls);
321 if (compiler != null) {
322 compilers.put(intent.getClass(), compiler);
323 return;
324 }
325 }
326 cls = cls.getSuperclass();
327 }
328 }
329 }
330
331 /**
332 * Registers an intent installer of the specified intent if an intent installer
333 * for the intent is not registered. This method traverses the class hierarchy of
334 * the intent. Once an intent installer for a parent type is found, this method
335 * registers the found intent installer.
336 *
337 * @param intent intent
338 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700339 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700340 if (!installers.containsKey(intent.getClass())) {
341 Class<?> cls = intent.getClass();
342 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700343 // As long as we're within the Intent class descendants
344 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700345 IntentInstaller<?> installer = installers.get(cls);
346 if (installer != null) {
347 installers.put(intent.getClass(), installer);
348 return;
349 }
350 }
351 cls = cls.getSuperclass();
352 }
353 }
354 }
355
Brian O'Connor66630c82014-10-02 21:08:19 -0700356 // Store delegate to re-post events emitted from the store.
357 private class InternalStoreDelegate implements IntentStoreDelegate {
358 @Override
359 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700360 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700361 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800362
363 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800364 public void process(IntentData data) {
365 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800366 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700367 }
368
Brian O'Connor72a034c2014-11-26 18:24:23 -0800369 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
370 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800371 // Attempt recompilation of the specified intents first.
372 for (IntentId id : intentIds) {
373 Intent intent = store.getIntent(id);
374 if (intent == null) {
375 continue;
376 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800377 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800378 }
379
380 if (compileAllFailed) {
381 // If required, compile all currently failed intents.
382 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800383 IntentState state = getIntentState(intent.id());
384 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800385 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800386 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800387 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800388 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800389 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800390 }
391 }
392 }
393
Brian O'Connorb499b352015-02-03 16:46:15 -0800394 //FIXME
395// for (ApplicationId appId : batches.keySet()) {
396// if (batchService.isLocalLeader(appId)) {
397// execute(batches.get(appId).build());
398// }
399// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800400 }
401
tom95329eb2014-10-06 08:40:06 -0700402 // Topology change delegate
403 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
404 @Override
tom85258ee2014-10-07 00:10:02 -0700405 public void triggerCompile(Iterable<IntentId> intentIds,
406 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800407 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700408 }
tom95329eb2014-10-06 08:40:06 -0700409 }
tom85258ee2014-10-07 00:10:02 -0700410
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800411 // TODO: simplify the branching statements
Brian O'Connorb499b352015-02-03 16:46:15 -0800412 private IntentUpdate createIntentUpdate(IntentData intentData) {
413 IntentData currentState = store.getIntentData(intentData.key());
414 switch (intentData.state()) {
415 case INSTALL_REQ:
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800416 return new InstallRequest(this, intentData.intent(), currentState);
Brian O'Connorb499b352015-02-03 16:46:15 -0800417 case WITHDRAW_REQ:
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800418 return new WithdrawRequest(this, intentData.intent(), currentState);
Brian O'Connorb499b352015-02-03 16:46:15 -0800419 // fallthrough
420 case COMPILING:
421 case INSTALLING:
422 case INSTALLED:
423 case RECOMPILING:
424 case WITHDRAWING:
425 case WITHDRAWN:
426 case FAILED:
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800427 default:
428 // illegal state
429 return new DoNothing();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700430 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700431 }
432
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800433 List<FlowRuleBatchOperation> convert(List<Intent> installables) {
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800434 List<FlowRuleBatchOperation> batches = new ArrayList<>(installables.size());
435 for (Intent installable : installables) {
436 try {
437 registerSubclassInstallerIfNeeded(installable);
438 batches.addAll(getInstaller(installable).install(installable));
439 } catch (Exception e) { // TODO this should be IntentException
Brian O'Connor098da072015-02-04 14:07:59 -0800440 throw new FlowRuleBatchOperationConversionException(batches, e);
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800441 }
442 }
443 return batches;
444 }
445
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800446 private class IntentBatchPreprocess implements Runnable {
447
448 // TODO make this configurable
449 private static final int TIMEOUT_PER_OP = 500; // ms
450 protected static final int MAX_ATTEMPTS = 3;
451
Brian O'Connorb499b352015-02-03 16:46:15 -0800452 protected final Collection<IntentData> ops;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800453
454 // future holding current FlowRuleBatch installation result
455 protected final long startTime = System.currentTimeMillis();
456 protected final long endTime;
457
Brian O'Connorb499b352015-02-03 16:46:15 -0800458 private IntentBatchPreprocess(Collection<IntentData> ops, long endTime) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800459 this.ops = checkNotNull(ops);
460 this.endTime = endTime;
461 }
462
Brian O'Connorb499b352015-02-03 16:46:15 -0800463 public IntentBatchPreprocess(Collection<IntentData> ops) {
464 this(ops, System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800465 }
466
467 // FIXME compute reasonable timeouts
468 protected long calculateTimeoutLimit() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800469 return System.currentTimeMillis() + ops.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800470 }
471
472 @Override
473 public void run() {
474 try {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800475 List<IntentUpdate> updates = createIntentUpdates();
476
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800477 new IntentBatchApplyFirst(ops, processIntentUpdates(updates), endTime, 0, null).run();
478 } catch (Exception e) {
479 log.error("Error submitting batches:", e);
480 // FIXME incomplete Intents should be cleaned up
481 // (transition to FAILED, etc.)
482
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800483 // the batch has failed
484 // TODO: maybe we should do more?
485 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800486 //FIXME
487// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800488 }
489 }
490
491 private List<IntentUpdate> createIntentUpdates() {
Brian O'Connorb499b352015-02-03 16:46:15 -0800492 return ops.stream()
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800493 .map(IntentManager.this::createIntentUpdate)
494 .collect(Collectors.toList());
495 }
496
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800497 private List<CompletedIntentUpdate> processIntentUpdates(List<IntentUpdate> updates) {
498 // start processing each Intents
499 List<CompletedIntentUpdate> completed = new ArrayList<>();
500 for (IntentUpdate update : updates) {
501 Optional<IntentUpdate> phase = Optional.of(update);
502 IntentUpdate previous = update;
503 while (true) {
504 if (!phase.isPresent()) {
505 // FIXME: not type safe cast
506 completed.add((CompletedIntentUpdate) previous);
507 break;
508 }
509 previous = phase.get();
510 phase = previous.execute();
511 }
512 }
513
514 return completed;
515 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800516 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800517
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800518 // TODO: better naming
519 private class IntentBatchApplyFirst extends IntentBatchPreprocess {
520
521 protected final List<CompletedIntentUpdate> intentUpdates;
522 protected final int installAttempt;
523 protected Future<CompletedBatchOperation> future;
524
Brian O'Connorb499b352015-02-03 16:46:15 -0800525 IntentBatchApplyFirst(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800526 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
527 super(operations, endTime);
528 this.intentUpdates = ImmutableList.copyOf(intentUpdates);
529 this.future = future;
530 this.installAttempt = installAttempt;
531 }
532
533 @Override
534 public void run() {
535 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
536 new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future).run();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700537 }
538
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700539 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800540 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700541 *
542 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700543 */
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800544 protected Future<CompletedBatchOperation> applyNextBatch(List<CompletedIntentUpdate> updates) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800545 //TODO test this. (also, maybe save this batch)
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800546
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800547 FlowRuleBatchOperation batch = createFlowRuleBatchOperation(updates);
Ray Milkey93508c22014-12-02 11:35:56 -0800548 if (batch.size() > 0) {
549 //FIXME apply batch might throw an exception
550 return flowRuleService.applyBatch(batch);
551 } else {
Ray Milkey93508c22014-12-02 11:35:56 -0800552 return null;
553 }
554 }
555
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800556 private FlowRuleBatchOperation createFlowRuleBatchOperation(List<CompletedIntentUpdate> intentUpdates) {
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800557 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList(), null, 0);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800558 for (CompletedIntentUpdate update : intentUpdates) {
559 FlowRuleBatchOperation currentBatch = update.currentBatch();
560 if (currentBatch != null) {
561 batch.addAll(currentBatch);
562 }
563 }
564 return batch;
565 }
566
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800567 protected void abandonShip() {
568 // the batch has failed
569 // TODO: maybe we should do more?
570 log.error("Walk the plank, matey...");
571 future = null;
Brian O'Connorb499b352015-02-03 16:46:15 -0800572 //FIXME
573// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800574 }
575 }
576
577 // TODO: better naming
578 private class IntentBatchProcessFutures extends IntentBatchApplyFirst {
579
Brian O'Connorb499b352015-02-03 16:46:15 -0800580 IntentBatchProcessFutures(Collection<IntentData> operations, List<CompletedIntentUpdate> intentUpdates,
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800581 long endTime, int installAttempt, Future<CompletedBatchOperation> future) {
582 super(operations, intentUpdates, endTime, installAttempt, future);
583 }
584
585 @Override
586 public void run() {
587 try {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800588 Future<CompletedBatchOperation> future = processFutures();
589 if (future == null) {
590 // there are no outstanding batches; we are done
Brian O'Connorb499b352015-02-03 16:46:15 -0800591 //FIXME
592 return; //?
593// batchService.removeIntentOperations(ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800594 } else if (System.currentTimeMillis() > endTime) {
595 // - cancel current FlowRuleBatch and resubmit again
596 retry();
597 } else {
598 // we are not done yet, yield the thread by resubmitting ourselves
599 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, endTime, installAttempt, future));
600 }
601 } catch (Exception e) {
602 log.error("Error submitting batches:", e);
603 // FIXME incomplete Intents should be cleaned up
604 // (transition to FAILED, etc.)
605 abandonShip();
606 }
607 }
608
609 /**
610 * Iterate through the pending futures, and remove them when they have completed.
611 */
612 private Future<CompletedBatchOperation> processFutures() {
613 try {
614 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
615 updateBatches(completed);
616 return applyNextBatch(intentUpdates);
617 } catch (TimeoutException | InterruptedException te) {
618 log.trace("Installation of intents are still pending: {}", ops);
619 return future;
620 } catch (ExecutionException e) {
621 log.warn("Execution of batch failed: {}", ops, e);
622 abandonShip();
623 return future;
624 }
625 }
626
Ray Milkey93508c22014-12-02 11:35:56 -0800627 private void updateBatches(CompletedBatchOperation completed) {
628 if (completed.isSuccess()) {
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800629 for (CompletedIntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -0800630 update.batchSuccess();
631 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700632 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800633 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -0800634 log.debug("Failed items: {}", completed.failedItems());
635 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -0800636
637 for (Long id : completed.failedIds()) {
638 IntentId targetId = IntentId.valueOf(id);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800639 for (CompletedIntentUpdate update : intentUpdates) {
640 for (Intent intent : update.allInstallables()) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800641 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800642 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800643 break;
644 }
645 }
646 }
647 // don't increment the non-failed items, as they have been reverted.
648 }
649 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700650 }
651
Brian O'Connor427a1762014-11-19 18:40:32 -0800652 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800653 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800654 if (future.cancel(true)) { // cancel success; batch is reverted
655 // reset the timer
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800656 long timeLimit = calculateTimeoutLimit();
657 int attempts = installAttempt + 1;
658 if (attempts == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800659 log.warn("Install request timed out: {}", ops);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800660 for (CompletedIntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800661 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800662 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800663 } else if (attempts > MAX_ATTEMPTS) {
Ray Milkey93508c22014-12-02 11:35:56 -0800664 abandonShip();
665 return;
Sho SHIMIZU55a55182015-02-03 22:32:56 -0800666 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800667 Future<CompletedBatchOperation> future = applyNextBatch(intentUpdates);
668 executor.submit(new IntentBatchProcessFutures(ops, intentUpdates, timeLimit, attempts, future));
Brian O'Connor427a1762014-11-19 18:40:32 -0800669 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800670 log.error("Cancelling FlowRuleBatch failed.");
Ray Milkey93508c22014-12-02 11:35:56 -0800671 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -0800672 }
673 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700674 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700675
676 private class InternalBatchDelegate implements IntentBatchDelegate {
677 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800678 public void execute(Collection<IntentData> operations) {
679 log.info("Execute {} operation(s).", operations.size());
680 log.debug("Execute operations: {}", operations);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800681 executor.execute(new IntentBatchPreprocess(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700682 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700683 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700684}