blob: fb772fde6cc34513f3164427f7bf741216f53893 [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;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080069import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connordb15b042015-02-04 14:59:28 -080070import static java.util.concurrent.Executors.newSingleThreadExecutor;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070071import static org.onlab.util.Tools.namedThreads;
Brian O'Connor0e271dc2015-02-04 18:20:25 -080072import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070073import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070074
75/**
76 * An implementation of Intent Manager.
77 */
78@Component(immediate = true)
79@Service
80public class IntentManager
81 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080082 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070083
84 public static final String INTENT_NULL = "Intent cannot be null";
Ray Milkeyf9af43c2015-02-09 16:45:48 -080085 public static final String INTENT_ID_NULL = "Intent key cannot be null";
Brian O'Connor66630c82014-10-02 21:08:19 -070086
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080087 private static final int NUM_THREADS = 12;
88
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080089 private static final EnumSet<IntentState> RECOMPILE
90 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080091
Brian O'Connor66630c82014-10-02 21:08:19 -070092 // Collections for compiler, installer, and listener are ONOS instance local
93 private final ConcurrentMap<Class<? extends Intent>,
94 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070095 private final ConcurrentMap<Class<? extends Intent>,
96 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070097
98 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070099 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700100
Brian O'Connor520c0522014-11-23 23:50:47 -0800101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected IntentStore store;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700108 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700111 protected EventDeliveryService eventDispatcher;
112
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected FlowRuleService flowRuleService;
115
Brian O'Connor520c0522014-11-23 23:50:47 -0800116
Brian O'Connordb15b042015-02-04 14:59:28 -0800117 private ExecutorService batchExecutor;
118 private ExecutorService workerExecutor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800119
120 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
121 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
122 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
123 private IdGenerator idGenerator;
124
Brian O'Connorb499b352015-02-03 16:46:15 -0800125 private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
Brian O'Connorcff03322015-02-03 15:28:59 -0800126
Brian O'Connor66630c82014-10-02 21:08:19 -0700127 @Activate
128 public void activate() {
129 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700130 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700131 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connordb15b042015-02-04 14:59:28 -0800132 batchExecutor = newSingleThreadExecutor(namedThreads("onos-intent-batch"));
133 workerExecutor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-worker-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800134 idGenerator = coreService.getIdGenerator("intent-ids");
135 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700136 log.info("Started");
137 }
138
139 @Deactivate
140 public void deactivate() {
141 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700142 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700143 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connordb15b042015-02-04 14:59:28 -0800144 batchExecutor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800145 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700146 log.info("Stopped");
147 }
148
149 @Override
150 public void submit(Intent intent) {
151 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800152 IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
Brian O'Connorcff03322015-02-03 15:28:59 -0800153 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700154 }
155
156 @Override
157 public void withdraw(Intent intent) {
158 checkNotNull(intent, INTENT_NULL);
Brian O'Connorcff03322015-02-03 15:28:59 -0800159 IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
Brian O'Connorcff03322015-02-03 15:28:59 -0800160 store.addPending(data);
Brian O'Connor66630c82014-10-02 21:08:19 -0700161 }
162
Brian O'Connor66630c82014-10-02 21:08:19 -0700163 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800164 public Intent getIntent(Key key) {
165 return store.getIntent(key);
166 }
167
168 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700169 public Iterable<Intent> getIntents() {
170 return store.getIntents();
171 }
172
173 @Override
174 public long getIntentCount() {
175 return store.getIntentCount();
176 }
177
178 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800179 public IntentState getIntentState(Key intentKey) {
180 checkNotNull(intentKey, INTENT_ID_NULL);
181 return store.getIntentState(intentKey);
Brian O'Connor66630c82014-10-02 21:08:19 -0700182 }
183
184 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800185 public List<Intent> getInstallableIntents(Key intentKey) {
186 checkNotNull(intentKey, INTENT_ID_NULL);
187 return store.getInstallableIntents(intentKey);
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700188 }
189
190 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700191 public void addListener(IntentListener listener) {
192 listenerRegistry.addListener(listener);
193 }
194
195 @Override
196 public void removeListener(IntentListener listener) {
197 listenerRegistry.removeListener(listener);
198 }
199
200 @Override
201 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
202 compilers.put(cls, compiler);
203 }
204
205 @Override
206 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
207 compilers.remove(cls);
208 }
209
210 @Override
211 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
212 return ImmutableMap.copyOf(compilers);
213 }
214
215 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700216 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700217 installers.put(cls, installer);
218 }
219
220 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700221 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700222 installers.remove(cls);
223 }
224
225 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700226 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700227 return ImmutableMap.copyOf(installers);
228 }
229
230 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700231 * Returns the corresponding intent compiler to the specified intent.
232 *
233 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700234 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700235 * @return intent compiler corresponding to the specified intent
236 */
237 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
238 @SuppressWarnings("unchecked")
239 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
240 if (compiler == null) {
241 throw new IntentException("no compiler for class " + intent.getClass());
242 }
243 return compiler;
244 }
245
246 /**
247 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700248 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700249 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700250 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700251 * @return intent installer corresponding to the specified installable intent
252 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700253 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700254 @SuppressWarnings("unchecked")
255 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
256 if (installer == null) {
257 throw new IntentException("no installer for class " + intent.getClass());
258 }
259 return installer;
260 }
261
262 /**
Brian O'Connorcb900f42014-10-07 21:55:33 -0700263 * Compiles an intent recursively.
264 *
265 * @param intent intent
Jonathan Hartd0ba2172015-02-11 13:54:33 -0800266 * @param previousInstallables previous intent installables
Brian O'Connorcb900f42014-10-07 21:55:33 -0700267 * @return result of compilation
268 */
Sho SHIMIZU5cb438e2015-02-04 13:46:00 -0800269 List<Intent> compileIntent(Intent intent, List<Intent> previousInstallables) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700270 if (intent.isInstallable()) {
271 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700272 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700273
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700274 registerSubclassCompilerIfNeeded(intent);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700275 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700276 List<Intent> installable = new ArrayList<>();
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800277 for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
278 installable.addAll(compileIntent(compiled, previousInstallables));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700279 }
tom85258ee2014-10-07 00:10:02 -0700280 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700281 }
282
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800283 //TODO javadoc
284 //FIXME
Brian O'Connor7775bda2015-02-06 15:01:18 -0800285 FlowRuleOperations coordinate(IntentData pending) {
286 List<Intent> installables = pending.installables();
287 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>(installables.size());
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800288 for (Intent installable : installables) {
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800289 registerSubclassInstallerIfNeeded(installable);
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800290 //TODO consider migrating installers to FlowRuleOperations
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800291 plans.add(getInstaller(installable).install(installable));
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800292 }
Brian O'Connor7775bda2015-02-06 15:01:18 -0800293
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800294 return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out
Brian O'Connor7775bda2015-02-06 15:01:18 -0800295 @Override
296 public void onSuccess(FlowRuleOperations ops) {
297 log.info("Completed installing: {}", pending.key());
298 pending.setState(INSTALLED);
299 store.write(pending);
300 }
301
302 @Override
303 public void onError(FlowRuleOperations ops) {
Brian O'Connorba1abbe2015-02-06 15:21:48 -0800304 log.warn("Failed installation: {} {} on {}", pending.key(),
305 pending.intent(), ops);
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800306 //TODO store.write(pending.setState(BROKEN));
Ray Milkey9f74c082015-02-11 15:40:16 -0800307 pending.setState(FAILED);
308 store.write(pending);
Brian O'Connor7775bda2015-02-06 15:01:18 -0800309 }
310 });
311 }
312
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800313 /**
314 * Generate a {@link FlowRuleOperations} instance from the specified intent data.
315 *
316 * @param current intent data stored in the store
317 * @return flow rule operations
318 */
Ray Milkey9f74c082015-02-11 15:40:16 -0800319 FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending) {
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800320 List<Intent> installables = current.installables();
321 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
322 for (Intent installable : installables) {
Sho SHIMIZUb5cd5812015-02-09 14:25:13 -0800323 plans.add(getInstaller(installable).uninstall(installable));
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800324 }
325
326 return merge(plans).build(new FlowRuleOperationsContext() {
327 @Override
328 public void onSuccess(FlowRuleOperations ops) {
Ray Milkey9f74c082015-02-11 15:40:16 -0800329 log.info("Completed withdrawing: {}", pending.key());
330 pending.setState(WITHDRAWN);
331 pending.setInstallables(Collections.emptyList());
332 store.write(pending);
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800333 }
334
335 @Override
336 public void onError(FlowRuleOperations ops) {
Ray Milkey9f74c082015-02-11 15:40:16 -0800337 log.warn("Failed withdraw: {}", pending.key());
338 pending.setState(FAILED);
339 store.write(pending);
Sho SHIMIZU37a24a82015-02-09 09:12:21 -0800340 }
341 });
342 }
343
344
Brian O'Connorea2ba3e2015-02-12 11:27:55 -0800345 // TODO needs tests... or maybe it's just perfect
Brian O'Connor7775bda2015-02-06 15:01:18 -0800346 private FlowRuleOperations.Builder merge(List<List<FlowRuleBatchOperation>> plans) {
347 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
348 // Build a batch one stage at a time
349 for (int stageNumber = 0;; stageNumber++) {
350 // Get the sub-stage from each plan (List<FlowRuleBatchOperation>)
351 for (Iterator<List<FlowRuleBatchOperation>> itr = plans.iterator(); itr.hasNext();) {
352 List<FlowRuleBatchOperation> plan = itr.next();
353 if (plan.size() <= stageNumber) {
354 // we have consumed all stages from this plan, so remove it
355 itr.remove();
356 continue;
357 }
358 // write operations from this sub-stage into the builder
359 FlowRuleBatchOperation stage = plan.get(stageNumber);
360 for (FlowRuleBatchEntry entry : stage.getOperations()) {
361 FlowRule rule = entry.target();
362 switch (entry.operator()) {
363 case ADD:
364 builder.add(rule);
365 break;
366 case REMOVE:
367 builder.remove(rule);
368 break;
369 case MODIFY:
370 builder.modify(rule);
371 break;
372 default:
373 break;
374 }
375 }
376 }
377 // we are done with the stage, start the next one...
378 if (plans.isEmpty()) {
379 break; // we don't need to start a new stage, we are done.
380 }
381 builder.newStage();
382 }
383 return builder;
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800384 }
385
Brian O'Connor66630c82014-10-02 21:08:19 -0700386 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700387 * Registers an intent compiler of the specified intent if an intent compiler
388 * for the intent is not registered. This method traverses the class hierarchy of
389 * the intent. Once an intent compiler for a parent type is found, this method
390 * registers the found intent compiler.
391 *
392 * @param intent intent
393 */
394 private void registerSubclassCompilerIfNeeded(Intent intent) {
395 if (!compilers.containsKey(intent.getClass())) {
396 Class<?> cls = intent.getClass();
397 while (cls != Object.class) {
398 // As long as we're within the Intent class descendants
399 if (Intent.class.isAssignableFrom(cls)) {
400 IntentCompiler<?> compiler = compilers.get(cls);
401 if (compiler != null) {
402 compilers.put(intent.getClass(), compiler);
403 return;
404 }
405 }
406 cls = cls.getSuperclass();
407 }
408 }
409 }
410
411 /**
412 * Registers an intent installer of the specified intent if an intent installer
413 * for the intent is not registered. This method traverses the class hierarchy of
414 * the intent. Once an intent installer for a parent type is found, this method
415 * registers the found intent installer.
416 *
417 * @param intent intent
418 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700419 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700420 if (!installers.containsKey(intent.getClass())) {
421 Class<?> cls = intent.getClass();
422 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700423 // As long as we're within the Intent class descendants
424 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700425 IntentInstaller<?> installer = installers.get(cls);
426 if (installer != null) {
427 installers.put(intent.getClass(), installer);
428 return;
429 }
430 }
431 cls = cls.getSuperclass();
432 }
433 }
434 }
435
Brian O'Connor66630c82014-10-02 21:08:19 -0700436 // Store delegate to re-post events emitted from the store.
437 private class InternalStoreDelegate implements IntentStoreDelegate {
438 @Override
439 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700440 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700441 }
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800442
443 @Override
Brian O'Connorcff03322015-02-03 15:28:59 -0800444 public void process(IntentData data) {
445 accumulator.add(data);
Brian O'Connorea4d7d12015-01-28 16:37:46 -0800446 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700447 }
448
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800449 private void buildAndSubmitBatches(Iterable<Key> intentKeys,
Brian O'Connor72a034c2014-11-26 18:24:23 -0800450 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800451 // Attempt recompilation of the specified intents first.
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800452 for (Key key : intentKeys) {
453 Intent intent = store.getIntent(key);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800454 if (intent == null) {
455 continue;
456 }
Brian O'Connor03406a42015-02-03 17:28:57 -0800457 submit(intent);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800458 }
459
460 if (compileAllFailed) {
461 // If required, compile all currently failed intents.
462 for (Intent intent : getIntents()) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800463 IntentState state = getIntentState(intent.key());
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800464 if (RECOMPILE.contains(state)) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800465 if (state == WITHDRAW_REQ) {
Brian O'Connor03406a42015-02-03 17:28:57 -0800466 withdraw(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800467 } else {
Brian O'Connor03406a42015-02-03 17:28:57 -0800468 submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800469 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800470 }
471 }
472 }
473
Brian O'Connorb499b352015-02-03 16:46:15 -0800474 //FIXME
475// for (ApplicationId appId : batches.keySet()) {
476// if (batchService.isLocalLeader(appId)) {
477// execute(batches.get(appId).build());
478// }
479// }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800480 }
481
tom95329eb2014-10-06 08:40:06 -0700482 // Topology change delegate
483 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
484 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800485 public void triggerCompile(Iterable<Key> intentKeys,
tom85258ee2014-10-07 00:10:02 -0700486 boolean compileAllFailed) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800487 buildAndSubmitBatches(intentKeys, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700488 }
tom95329eb2014-10-06 08:40:06 -0700489 }
tom85258ee2014-10-07 00:10:02 -0700490
Brian O'Connorb499b352015-02-03 16:46:15 -0800491 private IntentUpdate createIntentUpdate(IntentData intentData) {
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800492 IntentData current = store.getIntentData(intentData.key());
Brian O'Connorb499b352015-02-03 16:46:15 -0800493 switch (intentData.state()) {
494 case INSTALL_REQ:
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800495 return new InstallRequest(this, intentData, Optional.ofNullable(current));
Brian O'Connorb499b352015-02-03 16:46:15 -0800496 case WITHDRAW_REQ:
Sho SHIMIZU95a7baf2015-02-12 09:15:57 -0800497 if (current == null) {
498 return new Withdrawn(current, WITHDRAWN);
499 } else {
500 return new WithdrawRequest(this, intentData, current);
501 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800502 default:
503 // illegal state
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800504 return new CompilingFailed(intentData);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700505 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700506 }
507
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800508 private Future<CompletedIntentUpdate> submitIntentData(IntentData data) {
509 return workerExecutor.submit(new IntentWorker(data));
Sho SHIMIZU8d9d1362015-02-04 12:28:15 -0800510 }
511
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800512 private class IntentBatchPreprocess implements Runnable {
513
514 // TODO make this configurable
515 private static final int TIMEOUT_PER_OP = 500; // ms
516 protected static final int MAX_ATTEMPTS = 3;
517
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800518 protected final Collection<IntentData> data;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800519
520 // future holding current FlowRuleBatch installation result
521 protected final long startTime = System.currentTimeMillis();
522 protected final long endTime;
523
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800524 private IntentBatchPreprocess(Collection<IntentData> data, long endTime) {
525 this.data = checkNotNull(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800526 this.endTime = endTime;
527 }
528
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800529 public IntentBatchPreprocess(Collection<IntentData> data) {
530 this(data, System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800531 }
532
533 // FIXME compute reasonable timeouts
534 protected long calculateTimeoutLimit() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800535 return System.currentTimeMillis() + data.size() * TIMEOUT_PER_OP;
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800536 }
537
538 @Override
539 public void run() {
540 try {
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800541 /*
542 1. wrap each intentdata in a runnable and submit
543 2. wait for completion of all the work
544 3. accumulate results and submit batch write of IntentData to store
545 (we can also try to update these individually)
546 */
547 submitUpdates(waitForFutures(createIntentUpdates()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800548 } catch (Exception e) {
549 log.error("Error submitting batches:", e);
550 // FIXME incomplete Intents should be cleaned up
551 // (transition to FAILED, etc.)
552
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800553 // the batch has failed
554 // TODO: maybe we should do more?
555 log.error("Walk the plank, matey...");
Brian O'Connorb499b352015-02-03 16:46:15 -0800556 //FIXME
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800557// batchService.removeIntentOperations(data);
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800558 }
559 }
560
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800561 private List<Future<CompletedIntentUpdate>> createIntentUpdates() {
Sho SHIMIZU5f281a42015-02-04 15:29:11 -0800562 return data.stream()
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800563 .map(IntentManager.this::submitIntentData)
564 .collect(Collectors.toList());
565 }
566
567 private List<CompletedIntentUpdate> waitForFutures(List<Future<CompletedIntentUpdate>> futures) {
568 ImmutableList.Builder<CompletedIntentUpdate> updateBuilder = ImmutableList.builder();
569 for (Future<CompletedIntentUpdate> future : futures) {
570 try {
571 updateBuilder.add(future.get());
572 } catch (InterruptedException | ExecutionException e) {
573 //FIXME
574 log.warn("Future failed: {}", e);
575 }
576 }
577 return updateBuilder.build();
578 }
579
580 private void submitUpdates(List<CompletedIntentUpdate> updates) {
581 store.batchWrite(updates.stream()
582 .map(CompletedIntentUpdate::data)
583 .collect(Collectors.toList()));
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800584 }
Sho SHIMIZUadf8c482014-12-12 18:23:29 -0800585 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800586
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800587 private final class IntentWorker implements Callable<CompletedIntentUpdate> {
Brian O'Connordb15b042015-02-04 14:59:28 -0800588
589 private final IntentData data;
590
591 private IntentWorker(IntentData data) {
592 this.data = data;
593 }
594
595 @Override
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800596 public CompletedIntentUpdate call() throws Exception {
Brian O'Connordb15b042015-02-04 14:59:28 -0800597 IntentUpdate update = createIntentUpdate(data);
598 Optional<IntentUpdate> currentPhase = Optional.of(update);
599 IntentUpdate previousPhase = update;
600
601 while (currentPhase.isPresent()) {
602 previousPhase = currentPhase.get();
603 currentPhase = previousPhase.execute();
604 }
Brian O'Connor0e271dc2015-02-04 18:20:25 -0800605 return (CompletedIntentUpdate) previousPhase;
Brian O'Connor427a1762014-11-19 18:40:32 -0800606 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700607 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700608
609 private class InternalBatchDelegate implements IntentBatchDelegate {
610 @Override
Brian O'Connorb499b352015-02-03 16:46:15 -0800611 public void execute(Collection<IntentData> operations) {
612 log.info("Execute {} operation(s).", operations.size());
613 log.debug("Execute operations: {}", operations);
Brian O'Connordb15b042015-02-04 14:59:28 -0800614 batchExecutor.execute(new IntentBatchPreprocess(operations));
615 // TODO ensure that only one batch is in flight at a time
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700616 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700617 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700618}