blob: 8b6f5838acbe0b711468ff1aed8ccc937ade1e21 [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
Ray Milkey71ade562015-02-18 15:08:07 -080018import java.util.ArrayList;
19import java.util.Collection;
20import java.util.Collections;
21import java.util.EnumSet;
22import java.util.Iterator;
23import java.util.List;
24import java.util.Map;
25import java.util.Optional;
26import java.util.Set;
27import java.util.concurrent.Callable;
28import java.util.concurrent.ConcurrentHashMap;
29import java.util.concurrent.ConcurrentMap;
30import java.util.concurrent.ExecutionException;
31import java.util.concurrent.ExecutorService;
32import java.util.concurrent.Future;
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;
Ray Milkey71ade562015-02-18 15:08:07 -080045import org.onosproject.net.flow.FlowRuleOperation;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080046import org.onosproject.net.flow.FlowRuleOperations;
Brian O'Connor7775bda2015-02-06 15:01:18 -080047import org.onosproject.net.flow.FlowRuleOperationsContext;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.flow.FlowRuleService;
49import org.onosproject.net.intent.Intent;
50import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080051import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080052import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080053import org.onosproject.net.intent.IntentEvent;
54import org.onosproject.net.intent.IntentException;
55import org.onosproject.net.intent.IntentExtensionService;
Brian O'Connorabafb502014-12-02 22:26:20 -080056import 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;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080062import org.onosproject.net.intent.Key;
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -080063import org.onosproject.net.intent.impl.phase.CompilingFailed;
64import org.onosproject.net.intent.impl.phase.FinalIntentProcessPhase;
65import org.onosproject.net.intent.impl.phase.InstallRequest;
66import org.onosproject.net.intent.impl.phase.IntentProcessPhase;
67import org.onosproject.net.intent.impl.phase.WithdrawRequest;
68import org.onosproject.net.intent.impl.phase.Withdrawn;
Brian O'Connor66630c82014-10-02 21:08:19 -070069import org.slf4j.Logger;
70
Ray Milkey71ade562015-02-18 15:08:07 -080071import com.google.common.collect.ImmutableList;
72import com.google.common.collect.ImmutableMap;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070073
Brian O'Connorfa81eae2014-10-30 13:20:05 -070074import static com.google.common.base.Preconditions.checkNotNull;
Brian O'Connore2eac102015-02-12 18:30:22 -080075import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080076import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connordb15b042015-02-04 14:59:28 -080077import static java.util.concurrent.Executors.newSingleThreadExecutor;
Brian O'Connorbdc7f002015-02-18 20:49:41 -080078import static org.onlab.util.Tools.groupedThreads;
Brian O'Connore2eac102015-02-12 18:30:22 -080079import static org.onlab.util.Tools.isNullOrEmpty;
Ray Milkey71ade562015-02-18 15:08:07 -080080import static org.onosproject.net.intent.IntentState.FAILED;
81import static org.onosproject.net.intent.IntentState.INSTALLED;
82import static org.onosproject.net.intent.IntentState.INSTALL_REQ;
83import static org.onosproject.net.intent.IntentState.WITHDRAWN;
84import static org.onosproject.net.intent.IntentState.WITHDRAW_REQ;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070085import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070086
87/**
88 * An implementation of Intent Manager.
89 */
90@Component(immediate = true)
91@Service
92public class IntentManager
93 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080094 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070095
96 public static final String INTENT_NULL = "Intent cannot be null";
Ray Milkeyf9af43c2015-02-09 16:45:48 -080097 public static final String INTENT_ID_NULL = "Intent key cannot be null";
Brian O'Connor66630c82014-10-02 21:08:19 -070098
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080099 private static final int NUM_THREADS = 12;
100
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800101 private static final EnumSet<IntentState> RECOMPILE
102 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800103
Brian O'Connor66630c82014-10-02 21:08:19 -0700104 // Collections for compiler, installer, and listener are ONOS instance local
105 private final ConcurrentMap<Class<? extends Intent>,
106 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700107 private final ConcurrentMap<Class<? extends Intent>,
108 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700109
110 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700111 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700112
Brian O'Connor520c0522014-11-23 23:50:47 -0800113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected IntentStore store;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700120 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700123 protected EventDeliveryService eventDispatcher;
124
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800125 // TODO: make this protected due to short term hack for ONOS-1051
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800127 public FlowRuleService flowRuleService;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700128
Brian O'Connor520c0522014-11-23 23:50:47 -0800129
Brian O'Connordb15b042015-02-04 14:59:28 -0800130 private ExecutorService batchExecutor;
131 private ExecutorService workerExecutor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800132
133 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
134 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
135 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
136 private IdGenerator idGenerator;
137
Brian O'Connorb499b352015-02-03 16:46:15 -0800138 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800139
Brian O'Connor66630c82014-10-02 21:08:19 -0700140 @Activate
141 public void activate() {
142 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700143 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700144 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connorbdc7f002015-02-18 20:49:41 -0800145 batchExecutor = newSingleThreadExecutor(groupedThreads("onos/intent", "batch"));
146 workerExecutor = newFixedThreadPool(NUM_THREADS, groupedThreads("onos/intent", "worker-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800147 idGenerator = coreService.getIdGenerator("intent-ids");
148 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700149 log.info("Started");
150 }
151
152 @Deactivate
153 public void deactivate() {
154 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700155 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700156 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connordb15b042015-02-04 14:59:28 -0800157 batchExecutor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800158 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700159 log.info("Stopped");
160 }
161
162 @Override
163 public void submit(Intent intent) {
164 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800165 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
Brian O'Connorcff03322015-02-03 15:28:59 -0800166 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700167 }
168
169 @Override
170 public void withdraw(Intent intent) {
171 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800172 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
Brian O'Connorcff03322015-02-03 15:28:59 -0800173 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700174 }
175
Brian O'Connor66630c82014-10-02 21:08:19 -0700176 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800177 public Intent getIntent(Key key) {
178 return store.getIntent(key);
179 }
180
181 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700182 public Iterable<Intent> getIntents() {
183 return store.getIntents();
184 }
185
186 @Override
187 public long getIntentCount() {
188 return store.getIntentCount();
189 }
190
191 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800192 public IntentState getIntentState(Key intentKey) {
193 checkNotNull(intentKey, INTENT_ID_NULL);
194 return store.getIntentState(intentKey);
Brian O'Connor66630c82014-10-02 21:08:19 -0700195 }
196
197 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800198 public List<Intent> getInstallableIntents(Key intentKey) {
199 checkNotNull(intentKey, INTENT_ID_NULL);
200 return store.getInstallableIntents(intentKey);
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700201 }
202
203 @Override
Brian O'Connorbe28a872015-02-19 21:44:37 -0800204 public boolean isLocal(Key intentKey) {
205 return store.isMaster(intentKey);
206 }
207
208 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700209 public void addListener(IntentListener listener) {
210 listenerRegistry.addListener(listener);
211 }
212
213 @Override
214 public void removeListener(IntentListener listener) {
215 listenerRegistry.removeListener(listener);
216 }
217
218 @Override
219 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
220 compilers.put(cls, compiler);
221 }
222
223 @Override
224 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
225 compilers.remove(cls);
226 }
227
228 @Override
229 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
230 return ImmutableMap.copyOf(compilers);
231 }
232
233 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700234 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700235 installers.put(cls, installer);
236 }
237
238 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700239 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700240 installers.remove(cls);
241 }
242
243 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700244 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700245 return ImmutableMap.copyOf(installers);
246 }
247
248 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700249 * Returns the corresponding intent compiler to the specified intent.
250 *
251 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700252 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 * @return intent compiler corresponding to the specified intent
254 */
255 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
256 @SuppressWarnings("unchecked")
257 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
258 if (compiler == null) {
259 throw new IntentException("no compiler for class " + intent.getClass());
260 }
261 return compiler;
262 }
263
264 /**
265 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700266 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700267 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700268 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700269 * @return intent installer corresponding to the specified installable intent
270 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700271 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700272 @SuppressWarnings("unchecked")
273 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
274 if (installer == null) {
275 throw new IntentException("no installer for class " + intent.getClass());
276 }
277 return installer;
278 }
279
280 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700281 * Compiles an intent recursively.
282 *
283 * @param intent intent
Jonathan Hartd0ba2172015-02-11 13:54:33 -0800284 * @param previousInstallables previous intent installables
Brian O'Connorcb900f42014-10-07 21:55:33 -0700285 * @return result of compilation
286 */
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800287 // TODO: make this non-public due to short term hack for ONOS-1051
288 public List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700289 if (intent.isInstallable()) {
290 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700291 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700292
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700293 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700294 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700295 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800296 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
297 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700298 }
tom85258ee2014-10-07 00:10:02 -0700299 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700300 }
301
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800302 //TODO javadoc
303 //FIXME
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800304 // TODO: make this non-public due to short term hack for ONOS-1051
305 public FlowRuleOperations coordinate(IntentData current, IntentData pending) {
Brian O'Connore2eac102015-02-12 18:30:22 -0800306 List<Intent> oldInstallables = (current != null) ? current.installables() : null;
307 List<Intent> newInstallables = pending.installables();
308
309 checkState(isNullOrEmpty(oldInstallables) ||
310 oldInstallables.size() == newInstallables.size(),
311 "Old and New Intent must have equivalent installable intents.");
312
Ray Milkey71ade562015-02-18 15:08:07 -0800313 List<List<Set<FlowRuleOperation>>> plans = new ArrayList<>();
Brian O'Connore2eac102015-02-12 18:30:22 -0800314 for (int i = 0; i < newInstallables.size(); i++) {
315 Intent newInstallable = newInstallables.get(i);
316 registerSubclassInstallerIfNeeded(newInstallable);
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800317 //TODO consider migrating installers to FlowRuleOperations
Brian O'Connore2eac102015-02-12 18:30:22 -0800318 /* FIXME
319 - we need to do another pass on this method about that doesn't
320 require the length of installables to be equal, and also doesn't
321 depend on ordering
322 - we should also reconsider when to start/stop tracking resources
323 */
324 if (isNullOrEmpty(oldInstallables)) {
325 plans.add(getInstaller(newInstallable).install(newInstallable));
326 } else {
327 Intent oldInstallable = oldInstallables.get(i);
328 checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
329 "Installable Intent type mismatch.");
330 trackerService.removeTrackedResources(pending.key(), oldInstallable.resources());
331 plans.add(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
332 }
333 trackerService.addTrackedResources(pending.key(), newInstallable.resources());
334// } catch (IntentException e) {
335// log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
336// //FIXME... we failed. need to uninstall (if same) or revert (if different)
337// trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
338// exception = e;
339// batches = uninstallIntent(oldIntent, oldInstallables);
340// }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800341 }
Brian O'Connor7775bda2015-02-06 15:01:18 -0800342
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800343 return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out
Brian O'Connor7775bda2015-02-06 15:01:18 -0800344 @Override
345 public void onSuccess(FlowRuleOperations ops) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800346 log.debug("Completed installing: {}", pending.key());
Brian O'Connor7775bda2015-02-06 15:01:18 -0800347 pending.setState(INSTALLED);
348 store.write(pending);
349 }
350
351 @Override
352 public void onError(FlowRuleOperations ops) {
Brian O'Connorba1abbe2015-02-06 15:21:48 -0800353 log.warn("Failed installation: {} {} on {}", pending.key(),
354 pending.intent(), ops);
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800355 //TODO store.write(pending.setState(BROKEN));
Ray Milkey9f74c082015-02-11 15:40:16 -0800356 pending.setState(FAILED);
357 store.write(pending);
Brian O'Connor7775bda2015-02-06 15:01:18 -0800358 }
359 });
360 }
361
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800362 /**
363 * Generate a {@link FlowRuleOperations} instance from the specified intent data.
364 *
365 * @param current intent data stored in the store
366 * @return flow rule operations
367 */
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800368 // TODO: make this non-public due to short term hack for ONOS-1051
369 public FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending) {
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800370 List<Intent> installables = current.installables();
Ray Milkey71ade562015-02-18 15:08:07 -0800371 List<List<Set<FlowRuleOperation>>> plans = new ArrayList<>();
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800372 for (Intent installable : installables) {
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800373 plans.add(getInstaller(installable).uninstall(installable));
Brian O'Connore2eac102015-02-12 18:30:22 -0800374 trackerService.removeTrackedResources(pending.key(), installable.resources());
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800375 }
376
377 return merge(plans).build(new FlowRuleOperationsContext() {
378 @Override
379 public void onSuccess(FlowRuleOperations ops) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800380 log.debug("Completed withdrawing: {}", pending.key());
Ray Milkey9f74c082015-02-11 15:40:16 -0800381 pending.setState(WITHDRAWN);
382 pending.setInstallables(Collections.emptyList());
383 store.write(pending);
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800384 }
385
386 @Override
387 public void onError(FlowRuleOperations ops) {
Ray Milkey9f74c082015-02-11 15:40:16 -0800388 log.warn("Failed withdraw: {}", pending.key());
389 pending.setState(FAILED);
390 store.write(pending);
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800391 }
392 });
393 }
394
395
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800396 // TODO needs tests... or maybe it's just perfect
Ray Milkey71ade562015-02-18 15:08:07 -0800397 private FlowRuleOperations.Builder merge(List<List<Set<FlowRuleOperation>>> plans) {
Brian O'Connor7775bda2015-02-06 15:01:18 -0800398 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
399 // Build a batch one stage at a time
400 for (int stageNumber = 0;; stageNumber++) {
Ray Milkey71ade562015-02-18 15:08:07 -0800401 // Get the sub-stage from each plan (List<Set<FlowRuleOperation>)
402 for (Iterator<List<Set<FlowRuleOperation>>> itr = plans.iterator(); itr.hasNext();) {
403 List<Set<FlowRuleOperation>> plan = itr.next();
Brian O'Connor7775bda2015-02-06 15:01:18 -0800404 if (plan.size() <= stageNumber) {
405 // we have consumed all stages from this plan, so remove it
406 itr.remove();
407 continue;
408 }
409 // write operations from this sub-stage into the builder
Ray Milkey71ade562015-02-18 15:08:07 -0800410 Set<FlowRuleOperation> stage = plan.get(stageNumber);
411 for (FlowRuleOperation entry : stage) {
412 builder.operation(entry);
Brian O'Connor7775bda2015-02-06 15:01:18 -0800413 }
414 }
415 // we are done with the stage, start the next one...
416 if (plans.isEmpty()) {
417 break; // we don't need to start a new stage, we are done.
418 }
419 builder.newStage();
420 }
421 return builder;
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800422 }
423
Brian O'Connor66630c82014-10-02 21:08:19 -0700424 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700425 * Registers an intent compiler of the specified intent if an intent compiler
426 * for the intent is not registered. This method traverses the class hierarchy of
427 * the intent. Once an intent compiler for a parent type is found, this method
428 * registers the found intent compiler.
429 *
430 * @param intent intent
431 */
432 private void registerSubclassCompilerIfNeeded(Intent intent) {
433 if (!compilers.containsKey(intent.getClass())) {
434 Class<?> cls = intent.getClass();
435 while (cls != Object.class) {
436 // As long as we're within the Intent class descendants
437 if (Intent.class.isAssignableFrom(cls)) {
438 IntentCompiler<?> compiler = compilers.get(cls);
439 if (compiler != null) {
440 compilers.put(intent.getClass(), compiler);
441 return;
442 }
443 }
444 cls = cls.getSuperclass();
445 }
446 }
447 }
448
449 /**
450 * Registers an intent installer of the specified intent if an intent installer
451 * for the intent is not registered. This method traverses the class hierarchy of
452 * the intent. Once an intent installer for a parent type is found, this method
453 * registers the found intent installer.
454 *
455 * @param intent intent
456 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700457 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700458 if (!installers.containsKey(intent.getClass())) {
459 Class<?> cls = intent.getClass();
460 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700461 // As long as we're within the Intent class descendants
462 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700463 IntentInstaller<?> installer = installers.get(cls);
464 if (installer != null) {
465 installers.put(intent.getClass(), installer);
466 return;
467 }
468 }
469 cls = cls.getSuperclass();
470 }
471 }
472 }
473
Brian O'Connor66630c82014-10-02 21:08:19 -0700474 // Store delegate to re-post events emitted from the store.
475 private class InternalStoreDelegate implements IntentStoreDelegate {
476 @Override
477 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700478 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700479 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800480
481 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800482 public void process(IntentData data) {
483 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800484 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700485 }
486
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800487 private void buildAndSubmitBatches(Iterable<Key> intentKeys,
Brian O'Connor72a034c2014-11-26 18:24:23 -0800488 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800489 // Attempt recompilation of the specified intents first.
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800490 for (Key key : intentKeys) {
491 Intent intent = store.getIntent(key);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800492 if (intent == null) {
493 continue;
494 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800495 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800496 }
497
498 if (compileAllFailed) {
499 // If required, compile all currently failed intents.
500 for (Intent intent : getIntents()) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800501 IntentState state = getIntentState(intent.key());
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800502 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800503 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800504 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800505 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800506 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800507 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800508 }
509 }
510 }
511
Brian O'Connorb499b352015-02-03 16:46:15 -0800512 //FIXME
513// for (ApplicationId appId : batches.keySet()) {
514// if (batchService.isLocalLeader(appId)) {
515// execute(batches.get(appId).build());
516// }
517// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800518 }
519
tom95329eb2014-10-06 08:40:06 -0700520 // Topology change delegate
521 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
522 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800523 public void triggerCompile(Iterable<Key> intentKeys,
tom85258ee2014-10-07 00:10:02 -0700524 boolean compileAllFailed) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800525 buildAndSubmitBatches(intentKeys, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700526 }
tom95329eb2014-10-06 08:40:06 -0700527 }
tom85258ee2014-10-07 00:10:02 -0700528
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800529 private IntentProcessPhase createIntentUpdate(IntentData intentData) {
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800530 IntentData current = store.getIntentData(intentData.key());
Brian O'Connorb499b352015-02-03 16:46:15 -0800531 switch (intentData.state()) {
532 case INSTALL_REQ:
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800533 return new InstallRequest(this, intentData, Optional.ofNullable(current));
Brian O'Connorb499b352015-02-03 16:46:15 -0800534 case WITHDRAW_REQ:
Brian O'Connore2eac102015-02-12 18:30:22 -0800535 if (current == null || isNullOrEmpty(current.installables())) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800536 return new Withdrawn(intentData, WITHDRAWN);
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800537 } else {
538 return new WithdrawRequest(this, intentData, current);
539 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800540 default:
541 // illegal state
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800542 return new CompilingFailed(intentData);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700543 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700544 }
545
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800546 private Future<FinalIntentProcessPhase> submitIntentData(IntentData data) {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800547 return workerExecutor.submit(new IntentWorker(data));
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800548 }
549
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800550 private class IntentBatchPreprocess implements Runnable {
551
552 // TODO make this configurable
553 private static final int TIMEOUT_PER_OP = 500; // ms
554 protected static final int MAX_ATTEMPTS = 3;
555
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800556 protected final Collection<IntentData> data;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800557
558 // future holding current FlowRuleBatch installation result
559 protected final long startTime = System.currentTimeMillis();
560 protected final long endTime;
561
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800562 private IntentBatchPreprocess(Collection<IntentData> data, long endTime) {
563 this.data = checkNotNull(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800564 this.endTime = endTime;
565 }
566
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800567 public IntentBatchPreprocess(Collection<IntentData> data) {
568 this(data, System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800569 }
570
571 // FIXME compute reasonable timeouts
572 protected long calculateTimeoutLimit() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800573 return System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800574 }
575
576 @Override
577 public void run() {
578 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800579 /*
580 1. wrap each intentdata in a runnable and submit
581 2. wait for completion of all the work
582 3. accumulate results and submit batch write of IntentData to store
583 (we can also try to update these individually)
584 */
585 submitUpdates(waitForFutures(createIntentUpdates()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800586 } catch (Exception e) {
587 log.error("Error submitting batches:", e);
588 // FIXME incomplete Intents should be cleaned up
589 // (transition to FAILED, etc.)
590
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800591 // the batch has failed
592 // TODO: maybe we should do more?
593 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800594 //FIXME
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800595// batchService.removeIntentOperations(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800596 }
597 }
598
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800599 private List<Future<FinalIntentProcessPhase>> createIntentUpdates() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800600 return data.stream()
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800601 .map(IntentManager.this::submitIntentData)
602 .collect(Collectors.toList());
603 }
604
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800605 private List<FinalIntentProcessPhase> waitForFutures(List<Future<FinalIntentProcessPhase>> futures) {
606 ImmutableList.Builder<FinalIntentProcessPhase> updateBuilder = ImmutableList.builder();
607 for (Future<FinalIntentProcessPhase> future : futures) {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800608 try {
609 updateBuilder.add(future.get());
610 } catch (InterruptedException | ExecutionException e) {
611 //FIXME
612 log.warn("Future failed: {}", e);
613 }
614 }
615 return updateBuilder.build();
616 }
617
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800618 private void submitUpdates(List<FinalIntentProcessPhase> updates) {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800619 store.batchWrite(updates.stream()
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800620 .map(FinalIntentProcessPhase::data)
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800621 .collect(Collectors.toList()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800622 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800623 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800624
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800625 private final class IntentWorker implements Callable<FinalIntentProcessPhase> {
Brian O'Connordb15b042015-02-04 14:59:28 -0800626
627 private final IntentData data;
628
629 private IntentWorker(IntentData data) {
630 this.data = data;
631 }
632
633 @Override
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800634 public FinalIntentProcessPhase call() throws Exception {
635 IntentProcessPhase update = createIntentUpdate(data);
636 Optional<IntentProcessPhase> currentPhase = Optional.of(update);
637 IntentProcessPhase previousPhase = update;
Brian O'Connordb15b042015-02-04 14:59:28 -0800638
639 while (currentPhase.isPresent()) {
640 previousPhase = currentPhase.get();
641 currentPhase = previousPhase.execute();
642 }
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800643 return (FinalIntentProcessPhase) previousPhase;
Brian O'Connor427a1762014-11-19 18:40:32 -0800644 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700645 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700646
647 private class InternalBatchDelegate implements IntentBatchDelegate {
648 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800649 public void execute(Collection<IntentData> operations) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800650 log.debug("Execute {} operation(s).", operations.size());
651 log.trace("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800652 batchExecutor.execute(new IntentBatchPreprocess(operations));
653 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700654 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700655 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700656}