blob: 0a2316d1d7a6bca012629a94397ecfebfba92527 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.intent.impl;
Brian O'Connor66630c82014-10-02 21:08:19 -070017
Brian O'Connor0e271dc2015-02-04 18:20:25 -080018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableMap;
Brian O'Connor66630c82014-10-02 21:08:19 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080026import org.onosproject.core.CoreService;
27import org.onosproject.core.IdGenerator;
28import org.onosproject.event.AbstractListenerRegistry;
29import org.onosproject.event.EventDeliveryService;
Brian O'Connor7775bda2015-02-06 15:01:18 -080030import org.onosproject.net.flow.FlowRule;
31import org.onosproject.net.flow.FlowRuleBatchEntry;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.net.flow.FlowRuleBatchOperation;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080033import org.onosproject.net.flow.FlowRuleOperations;
Brian O'Connor7775bda2015-02-06 15:01:18 -080034import org.onosproject.net.flow.FlowRuleOperationsContext;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.net.flow.FlowRuleService;
36import org.onosproject.net.intent.Intent;
37import org.onosproject.net.intent.IntentBatchDelegate;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.intent.IntentCompiler;
Brian O'Connorcff03322015-02-03 15:28:59 -080039import org.onosproject.net.intent.IntentData;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.net.intent.IntentEvent;
41import org.onosproject.net.intent.IntentException;
42import org.onosproject.net.intent.IntentExtensionService;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.intent.IntentInstaller;
44import org.onosproject.net.intent.IntentListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.intent.IntentService;
46import org.onosproject.net.intent.IntentState;
47import org.onosproject.net.intent.IntentStore;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.intent.IntentStoreDelegate;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080049import org.onosproject.net.intent.Key;
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -080050import org.onosproject.net.intent.impl.phase.CompilingFailed;
51import org.onosproject.net.intent.impl.phase.FinalIntentProcessPhase;
52import org.onosproject.net.intent.impl.phase.InstallRequest;
53import org.onosproject.net.intent.impl.phase.IntentProcessPhase;
54import org.onosproject.net.intent.impl.phase.WithdrawRequest;
55import org.onosproject.net.intent.impl.phase.Withdrawn;
Brian O'Connor66630c82014-10-02 21:08:19 -070056import org.slf4j.Logger;
57
Brian O'Connor0e271dc2015-02-04 18:20:25 -080058import java.util.ArrayList;
59import java.util.Collection;
Ray Milkey9f74c082015-02-11 15:40:16 -080060import java.util.Collections;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080061import java.util.EnumSet;
Brian O'Connor7775bda2015-02-06 15:01:18 -080062import java.util.Iterator;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080063import java.util.List;
64import java.util.Map;
65import java.util.Optional;
66import java.util.concurrent.Callable;
67import java.util.concurrent.ConcurrentHashMap;
68import java.util.concurrent.ConcurrentMap;
69import java.util.concurrent.ExecutionException;
70import java.util.concurrent.ExecutorService;
71import java.util.concurrent.Future;
72import java.util.stream.Collectors;
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'Connore2eac102015-02-12 18:30:22 -080078import static org.onlab.util.Tools.isNullOrEmpty;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070079import static org.onlab.util.Tools.namedThreads;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080080import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070081import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070082
83/**
84 * An implementation of Intent Manager.
85 */
86@Component(immediate = true)
87@Service
88public class IntentManager
89 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080090 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070091
92 public static final String INTENT_NULL = "Intent cannot be null";
Ray Milkeyf9af43c2015-02-09 16:45:48 -080093 public static final String INTENT_ID_NULL = "Intent key cannot be null";
Brian O'Connor66630c82014-10-02 21:08:19 -070094
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080095 private static final int NUM_THREADS = 12;
96
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080097 private static final EnumSet<IntentState> RECOMPILE
98 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080099
Brian O'Connor66630c82014-10-02 21:08:19 -0700100 // Collections for compiler, installer, and listener are ONOS instance local
101 private final ConcurrentMap<Class<? extends Intent>,
102 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700103 private final ConcurrentMap<Class<? extends Intent>,
104 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700105
106 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700107 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700108
Brian O'Connor520c0522014-11-23 23:50:47 -0800109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected IntentStore store;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700116 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700119 protected EventDeliveryService eventDispatcher;
120
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800121 // TODO: make this protected due to short term hack for ONOS-1051
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800123 public FlowRuleService flowRuleService;
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700124
Brian O'Connor520c0522014-11-23 23:50:47 -0800125
Brian O'Connordb15b042015-02-04 14:59:28 -0800126 private ExecutorService batchExecutor;
127 private ExecutorService workerExecutor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800128
129 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
130 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
131 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
132 private IdGenerator idGenerator;
133
Brian O'Connorb499b352015-02-03 16:46:15 -0800134 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800135
Brian O'Connor66630c82014-10-02 21:08:19 -0700136 @Activate
137 public void activate() {
138 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700139 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700140 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connordb15b042015-02-04 14:59:28 -0800141 batchExecutor = newSingleThreadExecutor(namedThreads("onos-intent-batch"));
142 workerExecutor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-worker-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800143 idGenerator = coreService.getIdGenerator("intent-ids");
144 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700145 log.info("Started");
146 }
147
148 @Deactivate
149 public void deactivate() {
150 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700151 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700152 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connordb15b042015-02-04 14:59:28 -0800153 batchExecutor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800154 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700155 log.info("Stopped");
156 }
157
158 @Override
159 public void submit(Intent intent) {
160 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800161 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
Brian O'Connorcff03322015-02-03 15:28:59 -0800162 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700163 }
164
165 @Override
166 public void withdraw(Intent intent) {
167 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800168 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
Brian O'Connorcff03322015-02-03 15:28:59 -0800169 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700170 }
171
Brian O'Connor66630c82014-10-02 21:08:19 -0700172 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800173 public Intent getIntent(Key key) {
174 return store.getIntent(key);
175 }
176
177 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700178 public Iterable<Intent> getIntents() {
179 return store.getIntents();
180 }
181
182 @Override
183 public long getIntentCount() {
184 return store.getIntentCount();
185 }
186
187 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800188 public IntentState getIntentState(Key intentKey) {
189 checkNotNull(intentKey, INTENT_ID_NULL);
190 return store.getIntentState(intentKey);
Brian O'Connor66630c82014-10-02 21:08:19 -0700191 }
192
193 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800194 public List<Intent> getInstallableIntents(Key intentKey) {
195 checkNotNull(intentKey, INTENT_ID_NULL);
196 return store.getInstallableIntents(intentKey);
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700197 }
198
199 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700200 public void addListener(IntentListener listener) {
201 listenerRegistry.addListener(listener);
202 }
203
204 @Override
205 public void removeListener(IntentListener listener) {
206 listenerRegistry.removeListener(listener);
207 }
208
209 @Override
210 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
211 compilers.put(cls, compiler);
212 }
213
214 @Override
215 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
216 compilers.remove(cls);
217 }
218
219 @Override
220 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
221 return ImmutableMap.copyOf(compilers);
222 }
223
224 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700225 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700226 installers.put(cls, installer);
227 }
228
229 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700230 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700231 installers.remove(cls);
232 }
233
234 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700235 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700236 return ImmutableMap.copyOf(installers);
237 }
238
239 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700240 * Returns the corresponding intent compiler to the specified intent.
241 *
242 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700243 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700244 * @return intent compiler corresponding to the specified intent
245 */
246 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
247 @SuppressWarnings("unchecked")
248 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
249 if (compiler == null) {
250 throw new IntentException("no compiler for class " + intent.getClass());
251 }
252 return compiler;
253 }
254
255 /**
256 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700257 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700258 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700259 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700260 * @return intent installer corresponding to the specified installable intent
261 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700262 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700263 @SuppressWarnings("unchecked")
264 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
265 if (installer == null) {
266 throw new IntentException("no installer for class " + intent.getClass());
267 }
268 return installer;
269 }
270
271 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700272 * Compiles an intent recursively.
273 *
274 * @param intent intent
Jonathan Hartd0ba2172015-02-11 13:54:33 -0800275 * @param previousInstallables previous intent installables
Brian O'Connorcb900f42014-10-07 21:55:33 -0700276 * @return result of compilation
277 */
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800278 // TODO: make this non-public due to short term hack for ONOS-1051
279 public List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700280 if (intent.isInstallable()) {
281 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700282 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700283
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700284 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700285 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700286 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800287 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
288 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700289 }
tom85258ee2014-10-07 00:10:02 -0700290 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700291 }
292
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800293 //TODO javadoc
294 //FIXME
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800295 // TODO: make this non-public due to short term hack for ONOS-1051
296 public FlowRuleOperations coordinate(IntentData current, IntentData pending) {
Brian O'Connore2eac102015-02-12 18:30:22 -0800297 List<Intent> oldInstallables = (current != null) ? current.installables() : null;
298 List<Intent> newInstallables = pending.installables();
299
300 checkState(isNullOrEmpty(oldInstallables) ||
301 oldInstallables.size() == newInstallables.size(),
302 "Old and New Intent must have equivalent installable intents.");
303
304 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
305 for (int i = 0; i < newInstallables.size(); i++) {
306 Intent newInstallable = newInstallables.get(i);
307 registerSubclassInstallerIfNeeded(newInstallable);
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800308 //TODO consider migrating installers to FlowRuleOperations
Brian O'Connore2eac102015-02-12 18:30:22 -0800309 /* FIXME
310 - we need to do another pass on this method about that doesn't
311 require the length of installables to be equal, and also doesn't
312 depend on ordering
313 - we should also reconsider when to start/stop tracking resources
314 */
315 if (isNullOrEmpty(oldInstallables)) {
316 plans.add(getInstaller(newInstallable).install(newInstallable));
317 } else {
318 Intent oldInstallable = oldInstallables.get(i);
319 checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
320 "Installable Intent type mismatch.");
321 trackerService.removeTrackedResources(pending.key(), oldInstallable.resources());
322 plans.add(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
323 }
324 trackerService.addTrackedResources(pending.key(), newInstallable.resources());
325// } catch (IntentException e) {
326// log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
327// //FIXME... we failed. need to uninstall (if same) or revert (if different)
328// trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
329// exception = e;
330// batches = uninstallIntent(oldIntent, oldInstallables);
331// }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800332 }
Brian O'Connor7775bda2015-02-06 15:01:18 -0800333
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800334 return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out
Brian O'Connor7775bda2015-02-06 15:01:18 -0800335 @Override
336 public void onSuccess(FlowRuleOperations ops) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800337 log.debug("Completed installing: {}", pending.key());
Brian O'Connor7775bda2015-02-06 15:01:18 -0800338 pending.setState(INSTALLED);
339 store.write(pending);
340 }
341
342 @Override
343 public void onError(FlowRuleOperations ops) {
Brian O'Connorba1abbe2015-02-06 15:21:48 -0800344 log.warn("Failed installation: {} {} on {}", pending.key(),
345 pending.intent(), ops);
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800346 //TODO store.write(pending.setState(BROKEN));
Ray Milkey9f74c082015-02-11 15:40:16 -0800347 pending.setState(FAILED);
348 store.write(pending);
Brian O'Connor7775bda2015-02-06 15:01:18 -0800349 }
350 });
351 }
352
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800353 /**
354 * Generate a {@link FlowRuleOperations} instance from the specified intent data.
355 *
356 * @param current intent data stored in the store
357 * @return flow rule operations
358 */
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800359 // TODO: make this non-public due to short term hack for ONOS-1051
360 public FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending) {
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800361 List<Intent> installables = current.installables();
362 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
363 for (Intent installable : installables) {
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800364 plans.add(getInstaller(installable).uninstall(installable));
Brian O'Connore2eac102015-02-12 18:30:22 -0800365 trackerService.removeTrackedResources(pending.key(), installable.resources());
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800366 }
367
368 return merge(plans).build(new FlowRuleOperationsContext() {
369 @Override
370 public void onSuccess(FlowRuleOperations ops) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800371 log.debug("Completed withdrawing: {}", pending.key());
Ray Milkey9f74c082015-02-11 15:40:16 -0800372 pending.setState(WITHDRAWN);
373 pending.setInstallables(Collections.emptyList());
374 store.write(pending);
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800375 }
376
377 @Override
378 public void onError(FlowRuleOperations ops) {
Ray Milkey9f74c082015-02-11 15:40:16 -0800379 log.warn("Failed withdraw: {}", pending.key());
380 pending.setState(FAILED);
381 store.write(pending);
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800382 }
383 });
384 }
385
386
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800387 // TODO needs tests... or maybe it's just perfect
Brian O'Connor7775bda2015-02-06 15:01:18 -0800388 private FlowRuleOperations.Builder merge(List<List<FlowRuleBatchOperation>> plans) {
389 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
390 // Build a batch one stage at a time
391 for (int stageNumber = 0;; stageNumber++) {
392 // Get the sub-stage from each plan (List<FlowRuleBatchOperation>)
393 for (Iterator<List<FlowRuleBatchOperation>> itr = plans.iterator(); itr.hasNext();) {
394 List<FlowRuleBatchOperation> plan = itr.next();
395 if (plan.size() <= stageNumber) {
396 // we have consumed all stages from this plan, so remove it
397 itr.remove();
398 continue;
399 }
400 // write operations from this sub-stage into the builder
401 FlowRuleBatchOperation stage = plan.get(stageNumber);
402 for (FlowRuleBatchEntry entry : stage.getOperations()) {
403 FlowRule rule = entry.target();
404 switch (entry.operator()) {
405 case ADD:
406 builder.add(rule);
407 break;
408 case REMOVE:
409 builder.remove(rule);
410 break;
411 case MODIFY:
412 builder.modify(rule);
413 break;
414 default:
415 break;
416 }
417 }
418 }
419 // we are done with the stage, start the next one...
420 if (plans.isEmpty()) {
421 break; // we don't need to start a new stage, we are done.
422 }
423 builder.newStage();
424 }
425 return builder;
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800426 }
427
Brian O'Connor66630c82014-10-02 21:08:19 -0700428 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700429 * Registers an intent compiler of the specified intent if an intent compiler
430 * for the intent is not registered. This method traverses the class hierarchy of
431 * the intent. Once an intent compiler for a parent type is found, this method
432 * registers the found intent compiler.
433 *
434 * @param intent intent
435 */
436 private void registerSubclassCompilerIfNeeded(Intent intent) {
437 if (!compilers.containsKey(intent.getClass())) {
438 Class<?> cls = intent.getClass();
439 while (cls != Object.class) {
440 // As long as we're within the Intent class descendants
441 if (Intent.class.isAssignableFrom(cls)) {
442 IntentCompiler<?> compiler = compilers.get(cls);
443 if (compiler != null) {
444 compilers.put(intent.getClass(), compiler);
445 return;
446 }
447 }
448 cls = cls.getSuperclass();
449 }
450 }
451 }
452
453 /**
454 * Registers an intent installer of the specified intent if an intent installer
455 * for the intent is not registered. This method traverses the class hierarchy of
456 * the intent. Once an intent installer for a parent type is found, this method
457 * registers the found intent installer.
458 *
459 * @param intent intent
460 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700461 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700462 if (!installers.containsKey(intent.getClass())) {
463 Class<?> cls = intent.getClass();
464 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700465 // As long as we're within the Intent class descendants
466 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700467 IntentInstaller<?> installer = installers.get(cls);
468 if (installer != null) {
469 installers.put(intent.getClass(), installer);
470 return;
471 }
472 }
473 cls = cls.getSuperclass();
474 }
475 }
476 }
477
Brian O'Connor66630c82014-10-02 21:08:19 -0700478 // Store delegate to re-post events emitted from the store.
479 private class InternalStoreDelegate implements IntentStoreDelegate {
480 @Override
481 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700482 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700483 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800484
485 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800486 public void process(IntentData data) {
487 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800488 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700489 }
490
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800491 private void buildAndSubmitBatches(Iterable<Key> intentKeys,
Brian O'Connor72a034c2014-11-26 18:24:23 -0800492 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800493 // Attempt recompilation of the specified intents first.
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800494 for (Key key : intentKeys) {
495 Intent intent = store.getIntent(key);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800496 if (intent == null) {
497 continue;
498 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800499 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800500 }
501
502 if (compileAllFailed) {
503 // If required, compile all currently failed intents.
504 for (Intent intent : getIntents()) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800505 IntentState state = getIntentState(intent.key());
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800506 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800507 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800508 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800509 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800510 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800511 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800512 }
513 }
514 }
515
Brian O'Connorb499b352015-02-03 16:46:15 -0800516 //FIXME
517// for (ApplicationId appId : batches.keySet()) {
518// if (batchService.isLocalLeader(appId)) {
519// execute(batches.get(appId).build());
520// }
521// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800522 }
523
tom95329eb2014-10-06 08:40:06 -0700524 // Topology change delegate
525 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
526 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800527 public void triggerCompile(Iterable<Key> intentKeys,
tom85258ee2014-10-07 00:10:02 -0700528 boolean compileAllFailed) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800529 buildAndSubmitBatches(intentKeys, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700530 }
tom95329eb2014-10-06 08:40:06 -0700531 }
tom85258ee2014-10-07 00:10:02 -0700532
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800533 private IntentProcessPhase createIntentUpdate(IntentData intentData) {
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800534 IntentData current = store.getIntentData(intentData.key());
Brian O'Connorb499b352015-02-03 16:46:15 -0800535 switch (intentData.state()) {
536 case INSTALL_REQ:
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800537 return new InstallRequest(this, intentData, Optional.ofNullable(current));
Brian O'Connorb499b352015-02-03 16:46:15 -0800538 case WITHDRAW_REQ:
Brian O'Connore2eac102015-02-12 18:30:22 -0800539 if (current == null || isNullOrEmpty(current.installables())) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800540 return new Withdrawn(intentData, WITHDRAWN);
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800541 } else {
542 return new WithdrawRequest(this, intentData, current);
543 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800544 default:
545 // illegal state
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800546 return new CompilingFailed(intentData);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700547 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700548 }
549
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800550 private Future<FinalIntentProcessPhase> submitIntentData(IntentData data) {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800551 return workerExecutor.submit(new IntentWorker(data));
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800552 }
553
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800554 private class IntentBatchPreprocess implements Runnable {
555
556 // TODO make this configurable
557 private static final int TIMEOUT_PER_OP = 500; // ms
558 protected static final int MAX_ATTEMPTS = 3;
559
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800560 protected final Collection<IntentData> data;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800561
562 // future holding current FlowRuleBatch installation result
563 protected final long startTime = System.currentTimeMillis();
564 protected final long endTime;
565
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800566 private IntentBatchPreprocess(Collection<IntentData> data, long endTime) {
567 this.data = checkNotNull(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800568 this.endTime = endTime;
569 }
570
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800571 public IntentBatchPreprocess(Collection<IntentData> data) {
572 this(data, System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800573 }
574
575 // FIXME compute reasonable timeouts
576 protected long calculateTimeoutLimit() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800577 return System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800578 }
579
580 @Override
581 public void run() {
582 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800583 /*
584 1. wrap each intentdata in a runnable and submit
585 2. wait for completion of all the work
586 3. accumulate results and submit batch write of IntentData to store
587 (we can also try to update these individually)
588 */
589 submitUpdates(waitForFutures(createIntentUpdates()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800590 } catch (Exception e) {
591 log.error("Error submitting batches:", e);
592 // FIXME incomplete Intents should be cleaned up
593 // (transition to FAILED, etc.)
594
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800595 // the batch has failed
596 // TODO: maybe we should do more?
597 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800598 //FIXME
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800599// batchService.removeIntentOperations(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800600 }
601 }
602
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800603 private List<Future<FinalIntentProcessPhase>> createIntentUpdates() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800604 return data.stream()
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800605 .map(IntentManager.this::submitIntentData)
606 .collect(Collectors.toList());
607 }
608
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800609 private List<FinalIntentProcessPhase> waitForFutures(List<Future<FinalIntentProcessPhase>> futures) {
610 ImmutableList.Builder<FinalIntentProcessPhase> updateBuilder = ImmutableList.builder();
611 for (Future<FinalIntentProcessPhase> future : futures) {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800612 try {
613 updateBuilder.add(future.get());
614 } catch (InterruptedException | ExecutionException e) {
615 //FIXME
616 log.warn("Future failed: {}", e);
617 }
618 }
619 return updateBuilder.build();
620 }
621
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800622 private void submitUpdates(List<FinalIntentProcessPhase> updates) {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800623 store.batchWrite(updates.stream()
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800624 .map(FinalIntentProcessPhase::data)
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800625 .collect(Collectors.toList()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800626 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800627 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800628
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800629 private final class IntentWorker implements Callable<FinalIntentProcessPhase> {
Brian O'Connordb15b042015-02-04 14:59:28 -0800630
631 private final IntentData data;
632
633 private IntentWorker(IntentData data) {
634 this.data = data;
635 }
636
637 @Override
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800638 public FinalIntentProcessPhase call() throws Exception {
639 IntentProcessPhase update = createIntentUpdate(data);
640 Optional<IntentProcessPhase> currentPhase = Optional.of(update);
641 IntentProcessPhase previousPhase = update;
Brian O'Connordb15b042015-02-04 14:59:28 -0800642
643 while (currentPhase.isPresent()) {
644 previousPhase = currentPhase.get();
645 currentPhase = previousPhase.execute();
646 }
Sho SHIMIZU36a8a6e2015-02-13 15:38:45 -0800647 return (FinalIntentProcessPhase) previousPhase;
Brian O'Connor427a1762014-11-19 18:40:32 -0800648 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700649 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700650
651 private class InternalBatchDelegate implements IntentBatchDelegate {
652 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800653 public void execute(Collection<IntentData> operations) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800654 log.debug("Execute {} operation(s).", operations.size());
655 log.trace("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800656 batchExecutor.execute(new IntentBatchPreprocess(operations));
657 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700658 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700659 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700660}