blob: b39d2807ec533338ecda921bcd11ef8588c9fcbf [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;
Brian O'Connor66630c82014-10-02 21:08:19 -070050import org.slf4j.Logger;
51
Brian O'Connor0e271dc2015-02-04 18:20:25 -080052import java.util.ArrayList;
53import java.util.Collection;
Ray Milkey9f74c082015-02-11 15:40:16 -080054import java.util.Collections;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080055import java.util.EnumSet;
Brian O'Connor7775bda2015-02-06 15:01:18 -080056import java.util.Iterator;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080057import java.util.List;
58import java.util.Map;
59import java.util.Optional;
60import java.util.concurrent.Callable;
61import java.util.concurrent.ConcurrentHashMap;
62import java.util.concurrent.ConcurrentMap;
63import java.util.concurrent.ExecutionException;
64import java.util.concurrent.ExecutorService;
65import java.util.concurrent.Future;
66import java.util.stream.Collectors;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070067
Brian O'Connorfa81eae2014-10-30 13:20:05 -070068import static com.google.common.base.Preconditions.checkNotNull;
Brian O'Connore2eac102015-02-12 18:30:22 -080069import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080070import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connordb15b042015-02-04 14:59:28 -080071import static java.util.concurrent.Executors.newSingleThreadExecutor;
Brian O'Connore2eac102015-02-12 18:30:22 -080072import static org.onlab.util.Tools.isNullOrEmpty;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070073import static org.onlab.util.Tools.namedThreads;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080074import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070075import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070076
77/**
78 * An implementation of Intent Manager.
79 */
80@Component(immediate = true)
81@Service
82public class IntentManager
83 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080084 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070085
86 public static final String INTENT_NULL = "Intent cannot be null";
Ray Milkeyf9af43c2015-02-09 16:45:48 -080087 public static final String INTENT_ID_NULL = "Intent key cannot be null";
Brian O'Connor66630c82014-10-02 21:08:19 -070088
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080089 private static final int NUM_THREADS = 12;
90
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080091 private static final EnumSet<IntentState> RECOMPILE
92 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080093
Brian O'Connor66630c82014-10-02 21:08:19 -070094 // Collections for compiler, installer, and listener are ONOS instance local
95 private final ConcurrentMap<Class<? extends Intent>,
96 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070097 private final ConcurrentMap<Class<? extends Intent>,
98 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070099
100 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700101 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700102
Brian O'Connor520c0522014-11-23 23:50:47 -0800103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected IntentStore store;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700110 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700113 protected EventDeliveryService eventDispatcher;
114
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected FlowRuleService flowRuleService;
117
Brian O'Connor520c0522014-11-23 23:50:47 -0800118
Brian O'Connordb15b042015-02-04 14:59:28 -0800119 private ExecutorService batchExecutor;
120 private ExecutorService workerExecutor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800121
122 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
123 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
124 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
125 private IdGenerator idGenerator;
126
Brian O'Connorb499b352015-02-03 16:46:15 -0800127 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800128
Brian O'Connor66630c82014-10-02 21:08:19 -0700129 @Activate
130 public void activate() {
131 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700132 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700133 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connordb15b042015-02-04 14:59:28 -0800134 batchExecutor = newSingleThreadExecutor(namedThreads("onos-intent-batch"));
135 workerExecutor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-worker-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800136 idGenerator = coreService.getIdGenerator("intent-ids");
137 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700138 log.info("Started");
139 }
140
141 @Deactivate
142 public void deactivate() {
143 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700144 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700145 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connordb15b042015-02-04 14:59:28 -0800146 batchExecutor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800147 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700148 log.info("Stopped");
149 }
150
151 @Override
152 public void submit(Intent intent) {
153 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800154 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
Brian O'Connorcff03322015-02-03 15:28:59 -0800155 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700156 }
157
158 @Override
159 public void withdraw(Intent intent) {
160 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800161 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
Brian O'Connorcff03322015-02-03 15:28:59 -0800162 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700163 }
164
Brian O'Connor66630c82014-10-02 21:08:19 -0700165 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800166 public Intent getIntent(Key key) {
167 return store.getIntent(key);
168 }
169
170 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700171 public Iterable<Intent> getIntents() {
172 return store.getIntents();
173 }
174
175 @Override
176 public long getIntentCount() {
177 return store.getIntentCount();
178 }
179
180 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800181 public IntentState getIntentState(Key intentKey) {
182 checkNotNull(intentKey, INTENT_ID_NULL);
183 return store.getIntentState(intentKey);
Brian O'Connor66630c82014-10-02 21:08:19 -0700184 }
185
186 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800187 public List<Intent> getInstallableIntents(Key intentKey) {
188 checkNotNull(intentKey, INTENT_ID_NULL);
189 return store.getInstallableIntents(intentKey);
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700190 }
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
Jonathan Hartd0ba2172015-02-11 13:54:33 -0800268 * @param previousInstallables previous intent installables
Brian O'Connorcb900f42014-10-07 21:55:33 -0700269 * @return result of compilation
270 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800271 List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700272 if (intent.isInstallable()) {
273 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700274 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700275
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700276 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700277 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700278 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800279 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
280 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700281 }
tom85258ee2014-10-07 00:10:02 -0700282 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700283 }
284
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800285 //TODO javadoc
286 //FIXME
Brian O'Connore2eac102015-02-12 18:30:22 -0800287 FlowRuleOperations coordinate(IntentData current, IntentData pending) {
288 List<Intent> oldInstallables = (current != null) ? current.installables() : null;
289 List<Intent> newInstallables = pending.installables();
290
291 checkState(isNullOrEmpty(oldInstallables) ||
292 oldInstallables.size() == newInstallables.size(),
293 "Old and New Intent must have equivalent installable intents.");
294
295 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
296 for (int i = 0; i < newInstallables.size(); i++) {
297 Intent newInstallable = newInstallables.get(i);
298 registerSubclassInstallerIfNeeded(newInstallable);
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800299 //TODO consider migrating installers to FlowRuleOperations
Brian O'Connore2eac102015-02-12 18:30:22 -0800300 /* FIXME
301 - we need to do another pass on this method about that doesn't
302 require the length of installables to be equal, and also doesn't
303 depend on ordering
304 - we should also reconsider when to start/stop tracking resources
305 */
306 if (isNullOrEmpty(oldInstallables)) {
307 plans.add(getInstaller(newInstallable).install(newInstallable));
308 } else {
309 Intent oldInstallable = oldInstallables.get(i);
310 checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
311 "Installable Intent type mismatch.");
312 trackerService.removeTrackedResources(pending.key(), oldInstallable.resources());
313 plans.add(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
314 }
315 trackerService.addTrackedResources(pending.key(), newInstallable.resources());
316// } catch (IntentException e) {
317// log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
318// //FIXME... we failed. need to uninstall (if same) or revert (if different)
319// trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
320// exception = e;
321// batches = uninstallIntent(oldIntent, oldInstallables);
322// }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800323 }
Brian O'Connor7775bda2015-02-06 15:01:18 -0800324
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800325 return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out
Brian O'Connor7775bda2015-02-06 15:01:18 -0800326 @Override
327 public void onSuccess(FlowRuleOperations ops) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800328 log.debug("Completed installing: {}", pending.key());
Brian O'Connor7775bda2015-02-06 15:01:18 -0800329 pending.setState(INSTALLED);
330 store.write(pending);
331 }
332
333 @Override
334 public void onError(FlowRuleOperations ops) {
Brian O'Connorba1abbe2015-02-06 15:21:48 -0800335 log.warn("Failed installation: {} {} on {}", pending.key(),
336 pending.intent(), ops);
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800337 //TODO store.write(pending.setState(BROKEN));
Ray Milkey9f74c082015-02-11 15:40:16 -0800338 pending.setState(FAILED);
339 store.write(pending);
Brian O'Connor7775bda2015-02-06 15:01:18 -0800340 }
341 });
342 }
343
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800344 /**
345 * Generate a {@link FlowRuleOperations} instance from the specified intent data.
346 *
347 * @param current intent data stored in the store
348 * @return flow rule operations
349 */
Ray Milkey9f74c082015-02-11 15:40:16 -0800350 FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending) {
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800351 List<Intent> installables = current.installables();
352 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
353 for (Intent installable : installables) {
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800354 plans.add(getInstaller(installable).uninstall(installable));
Brian O'Connore2eac102015-02-12 18:30:22 -0800355 trackerService.removeTrackedResources(pending.key(), installable.resources());
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800356 }
357
358 return merge(plans).build(new FlowRuleOperationsContext() {
359 @Override
360 public void onSuccess(FlowRuleOperations ops) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800361 log.debug("Completed withdrawing: {}", pending.key());
Ray Milkey9f74c082015-02-11 15:40:16 -0800362 pending.setState(WITHDRAWN);
363 pending.setInstallables(Collections.emptyList());
364 store.write(pending);
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800365 }
366
367 @Override
368 public void onError(FlowRuleOperations ops) {
Ray Milkey9f74c082015-02-11 15:40:16 -0800369 log.warn("Failed withdraw: {}", pending.key());
370 pending.setState(FAILED);
371 store.write(pending);
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800372 }
373 });
374 }
375
376
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800377 // TODO needs tests... or maybe it's just perfect
Brian O'Connor7775bda2015-02-06 15:01:18 -0800378 private FlowRuleOperations.Builder merge(List<List<FlowRuleBatchOperation>> plans) {
379 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
380 // Build a batch one stage at a time
381 for (int stageNumber = 0;; stageNumber++) {
382 // Get the sub-stage from each plan (List<FlowRuleBatchOperation>)
383 for (Iterator<List<FlowRuleBatchOperation>> itr = plans.iterator(); itr.hasNext();) {
384 List<FlowRuleBatchOperation> plan = itr.next();
385 if (plan.size() <= stageNumber) {
386 // we have consumed all stages from this plan, so remove it
387 itr.remove();
388 continue;
389 }
390 // write operations from this sub-stage into the builder
391 FlowRuleBatchOperation stage = plan.get(stageNumber);
392 for (FlowRuleBatchEntry entry : stage.getOperations()) {
393 FlowRule rule = entry.target();
394 switch (entry.operator()) {
395 case ADD:
396 builder.add(rule);
397 break;
398 case REMOVE:
399 builder.remove(rule);
400 break;
401 case MODIFY:
402 builder.modify(rule);
403 break;
404 default:
405 break;
406 }
407 }
408 }
409 // we are done with the stage, start the next one...
410 if (plans.isEmpty()) {
411 break; // we don't need to start a new stage, we are done.
412 }
413 builder.newStage();
414 }
415 return builder;
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800416 }
417
Brian O'Connor66630c82014-10-02 21:08:19 -0700418 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700419 * Registers an intent compiler of the specified intent if an intent compiler
420 * for the intent is not registered. This method traverses the class hierarchy of
421 * the intent. Once an intent compiler for a parent type is found, this method
422 * registers the found intent compiler.
423 *
424 * @param intent intent
425 */
426 private void registerSubclassCompilerIfNeeded(Intent intent) {
427 if (!compilers.containsKey(intent.getClass())) {
428 Class<?> cls = intent.getClass();
429 while (cls != Object.class) {
430 // As long as we're within the Intent class descendants
431 if (Intent.class.isAssignableFrom(cls)) {
432 IntentCompiler<?> compiler = compilers.get(cls);
433 if (compiler != null) {
434 compilers.put(intent.getClass(), compiler);
435 return;
436 }
437 }
438 cls = cls.getSuperclass();
439 }
440 }
441 }
442
443 /**
444 * Registers an intent installer of the specified intent if an intent installer
445 * for the intent is not registered. This method traverses the class hierarchy of
446 * the intent. Once an intent installer for a parent type is found, this method
447 * registers the found intent installer.
448 *
449 * @param intent intent
450 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700451 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700452 if (!installers.containsKey(intent.getClass())) {
453 Class<?> cls = intent.getClass();
454 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700455 // As long as we're within the Intent class descendants
456 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700457 IntentInstaller<?> installer = installers.get(cls);
458 if (installer != null) {
459 installers.put(intent.getClass(), installer);
460 return;
461 }
462 }
463 cls = cls.getSuperclass();
464 }
465 }
466 }
467
Brian O'Connor66630c82014-10-02 21:08:19 -0700468 // Store delegate to re-post events emitted from the store.
469 private class InternalStoreDelegate implements IntentStoreDelegate {
470 @Override
471 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700472 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700473 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800474
475 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800476 public void process(IntentData data) {
477 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800478 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700479 }
480
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800481 private void buildAndSubmitBatches(Iterable<Key> intentKeys,
Brian O'Connor72a034c2014-11-26 18:24:23 -0800482 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800483 // Attempt recompilation of the specified intents first.
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800484 for (Key key : intentKeys) {
485 Intent intent = store.getIntent(key);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800486 if (intent == null) {
487 continue;
488 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800489 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800490 }
491
492 if (compileAllFailed) {
493 // If required, compile all currently failed intents.
494 for (Intent intent : getIntents()) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800495 IntentState state = getIntentState(intent.key());
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800496 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800497 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800498 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800499 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800500 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800501 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800502 }
503 }
504 }
505
Brian O'Connorb499b352015-02-03 16:46:15 -0800506 //FIXME
507// for (ApplicationId appId : batches.keySet()) {
508// if (batchService.isLocalLeader(appId)) {
509// execute(batches.get(appId).build());
510// }
511// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800512 }
513
tom95329eb2014-10-06 08:40:06 -0700514 // Topology change delegate
515 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
516 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800517 public void triggerCompile(Iterable<Key> intentKeys,
tom85258ee2014-10-07 00:10:02 -0700518 boolean compileAllFailed) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800519 buildAndSubmitBatches(intentKeys, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700520 }
tom95329eb2014-10-06 08:40:06 -0700521 }
tom85258ee2014-10-07 00:10:02 -0700522
Brian O'Connorb499b352015-02-03 16:46:15 -0800523 private IntentUpdate createIntentUpdate(IntentData intentData) {
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800524 IntentData current = store.getIntentData(intentData.key());
Brian O'Connorb499b352015-02-03 16:46:15 -0800525 switch (intentData.state()) {
526 case INSTALL_REQ:
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800527 return new InstallRequest(this, intentData, Optional.ofNullable(current));
Brian O'Connorb499b352015-02-03 16:46:15 -0800528 case WITHDRAW_REQ:
Brian O'Connore2eac102015-02-12 18:30:22 -0800529 if (current == null || isNullOrEmpty(current.installables())) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800530 return new Withdrawn(intentData, WITHDRAWN);
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800531 } else {
532 return new WithdrawRequest(this, intentData, current);
533 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800534 default:
535 // illegal state
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800536 return new CompilingFailed(intentData);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700537 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700538 }
539
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800540 private Future<CompletedIntentUpdate> submitIntentData(IntentData data) {
541 return workerExecutor.submit(new IntentWorker(data));
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800542 }
543
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800544 private class IntentBatchPreprocess implements Runnable {
545
546 // TODO make this configurable
547 private static final int TIMEOUT_PER_OP = 500; // ms
548 protected static final int MAX_ATTEMPTS = 3;
549
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800550 protected final Collection<IntentData> data;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800551
552 // future holding current FlowRuleBatch installation result
553 protected final long startTime = System.currentTimeMillis();
554 protected final long endTime;
555
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800556 private IntentBatchPreprocess(Collection<IntentData> data, long endTime) {
557 this.data = checkNotNull(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800558 this.endTime = endTime;
559 }
560
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800561 public IntentBatchPreprocess(Collection<IntentData> data) {
562 this(data, System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800563 }
564
565 // FIXME compute reasonable timeouts
566 protected long calculateTimeoutLimit() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800567 return System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800568 }
569
570 @Override
571 public void run() {
572 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800573 /*
574 1. wrap each intentdata in a runnable and submit
575 2. wait for completion of all the work
576 3. accumulate results and submit batch write of IntentData to store
577 (we can also try to update these individually)
578 */
579 submitUpdates(waitForFutures(createIntentUpdates()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800580 } catch (Exception e) {
581 log.error("Error submitting batches:", e);
582 // FIXME incomplete Intents should be cleaned up
583 // (transition to FAILED, etc.)
584
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800585 // the batch has failed
586 // TODO: maybe we should do more?
587 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800588 //FIXME
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800589// batchService.removeIntentOperations(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800590 }
591 }
592
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800593 private List<Future<CompletedIntentUpdate>> createIntentUpdates() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800594 return data.stream()
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800595 .map(IntentManager.this::submitIntentData)
596 .collect(Collectors.toList());
597 }
598
599 private List<CompletedIntentUpdate> waitForFutures(List<Future<CompletedIntentUpdate>> futures) {
600 ImmutableList.Builder<CompletedIntentUpdate> updateBuilder = ImmutableList.builder();
601 for (Future<CompletedIntentUpdate> future : futures) {
602 try {
603 updateBuilder.add(future.get());
604 } catch (InterruptedException | ExecutionException e) {
605 //FIXME
606 log.warn("Future failed: {}", e);
607 }
608 }
609 return updateBuilder.build();
610 }
611
612 private void submitUpdates(List<CompletedIntentUpdate> updates) {
613 store.batchWrite(updates.stream()
614 .map(CompletedIntentUpdate::data)
615 .collect(Collectors.toList()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800616 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800617 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800618
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800619 private final class IntentWorker implements Callable<CompletedIntentUpdate> {
Brian O'Connordb15b042015-02-04 14:59:28 -0800620
621 private final IntentData data;
622
623 private IntentWorker(IntentData data) {
624 this.data = data;
625 }
626
627 @Override
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800628 public CompletedIntentUpdate call() throws Exception {
Brian O'Connordb15b042015-02-04 14:59:28 -0800629 IntentUpdate update = createIntentUpdate(data);
630 Optional<IntentUpdate> currentPhase = Optional.of(update);
631 IntentUpdate previousPhase = update;
632
633 while (currentPhase.isPresent()) {
634 previousPhase = currentPhase.get();
635 currentPhase = previousPhase.execute();
636 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800637 return (CompletedIntentUpdate) previousPhase;
Brian O'Connor427a1762014-11-19 18:40:32 -0800638 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700639 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700640
641 private class InternalBatchDelegate implements IntentBatchDelegate {
642 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800643 public void execute(Collection<IntentData> operations) {
Brian O'Connorab8ef822015-02-17 18:08:54 -0800644 log.debug("Execute {} operation(s).", operations.size());
645 log.trace("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800646 batchExecutor.execute(new IntentBatchPreprocess(operations));
647 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700648 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700649 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700650}