blob: 16b75f2ba6dcc27eeee40ab81e049bb5af2d6641 [file] [log] [blame]
Brian O'Connor66630c82014-10-02 21:08:19 -07001package org.onlab.onos.net.intent.impl;
2
alshabib8ca53902014-10-07 13:11:17 -07003import static com.google.common.base.Preconditions.checkNotNull;
4import static java.util.concurrent.Executors.newSingleThreadExecutor;
5import static org.onlab.onos.net.intent.IntentState.COMPILING;
6import static org.onlab.onos.net.intent.IntentState.FAILED;
7import static org.onlab.onos.net.intent.IntentState.INSTALLED;
8import static org.onlab.onos.net.intent.IntentState.INSTALLING;
9import static org.onlab.onos.net.intent.IntentState.RECOMPILING;
10import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
11import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
12import static org.onlab.util.Tools.namedThreads;
13import static org.slf4j.LoggerFactory.getLogger;
14
15import java.util.ArrayList;
16import java.util.List;
17import java.util.Map;
18import java.util.Objects;
19import java.util.concurrent.ConcurrentHashMap;
20import java.util.concurrent.ConcurrentMap;
21import java.util.concurrent.ExecutorService;
22
Brian O'Connor66630c82014-10-02 21:08:19 -070023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
26import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.apache.felix.scr.annotations.Service;
29import org.onlab.onos.event.AbstractListenerRegistry;
30import org.onlab.onos.event.EventDeliveryService;
31import org.onlab.onos.net.intent.InstallableIntent;
32import org.onlab.onos.net.intent.Intent;
33import org.onlab.onos.net.intent.IntentCompiler;
34import org.onlab.onos.net.intent.IntentEvent;
35import org.onlab.onos.net.intent.IntentException;
36import org.onlab.onos.net.intent.IntentExtensionService;
37import org.onlab.onos.net.intent.IntentId;
38import org.onlab.onos.net.intent.IntentInstaller;
39import org.onlab.onos.net.intent.IntentListener;
40import org.onlab.onos.net.intent.IntentOperations;
41import org.onlab.onos.net.intent.IntentService;
42import org.onlab.onos.net.intent.IntentState;
43import org.onlab.onos.net.intent.IntentStore;
44import org.onlab.onos.net.intent.IntentStoreDelegate;
45import org.slf4j.Logger;
46
alshabib8ca53902014-10-07 13:11:17 -070047import com.google.common.collect.ImmutableMap;
Brian O'Connor66630c82014-10-02 21:08:19 -070048
49/**
50 * An implementation of Intent Manager.
51 */
52@Component(immediate = true)
53@Service
54public class IntentManager
55 implements IntentService, IntentExtensionService {
56 private final Logger log = getLogger(getClass());
57
58 public static final String INTENT_NULL = "Intent cannot be null";
59 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
60
61 // Collections for compiler, installer, and listener are ONOS instance local
62 private final ConcurrentMap<Class<? extends Intent>,
63 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
64 private final ConcurrentMap<Class<? extends InstallableIntent>,
65 IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070066
67 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070068 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070069
alshabib8ca53902014-10-07 13:11:17 -070070 private final ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents"));
tom85258ee2014-10-07 00:10:02 -070071
Brian O'Connor66630c82014-10-02 21:08:19 -070072 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
tom95329eb2014-10-06 08:40:06 -070073 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
Brian O'Connor66630c82014-10-02 21:08:19 -070074
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected IntentStore store;
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -070079 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -070080
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -070082 protected EventDeliveryService eventDispatcher;
83
84 @Activate
85 public void activate() {
86 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -070087 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -070088 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connor66630c82014-10-02 21:08:19 -070089 log.info("Started");
90 }
91
92 @Deactivate
93 public void deactivate() {
94 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -070095 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -070096 eventDispatcher.removeSink(IntentEvent.class);
97 log.info("Stopped");
98 }
99
100 @Override
101 public void submit(Intent intent) {
102 checkNotNull(intent, INTENT_NULL);
103 registerSubclassCompilerIfNeeded(intent);
104 IntentEvent event = store.createIntent(intent);
tom85258ee2014-10-07 00:10:02 -0700105 if (event != null) {
106 eventDispatcher.post(event);
107 executor.execute(new IntentTask(COMPILING, intent));
108 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700109 }
110
111 @Override
112 public void withdraw(Intent intent) {
113 checkNotNull(intent, INTENT_NULL);
tom85258ee2014-10-07 00:10:02 -0700114 executor.execute(new IntentTask(WITHDRAWING, intent));
Brian O'Connor66630c82014-10-02 21:08:19 -0700115 }
116
117 // FIXME: implement this method
118 @Override
119 public void execute(IntentOperations operations) {
120 throw new UnsupportedOperationException("execute() is not implemented yet");
121 }
122
123 @Override
124 public Iterable<Intent> getIntents() {
125 return store.getIntents();
126 }
127
128 @Override
129 public long getIntentCount() {
130 return store.getIntentCount();
131 }
132
133 @Override
134 public Intent getIntent(IntentId id) {
135 checkNotNull(id, INTENT_ID_NULL);
136 return store.getIntent(id);
137 }
138
139 @Override
140 public IntentState getIntentState(IntentId id) {
141 checkNotNull(id, INTENT_ID_NULL);
142 return store.getIntentState(id);
143 }
144
145 @Override
146 public void addListener(IntentListener listener) {
147 listenerRegistry.addListener(listener);
148 }
149
150 @Override
151 public void removeListener(IntentListener listener) {
152 listenerRegistry.removeListener(listener);
153 }
154
155 @Override
156 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
157 compilers.put(cls, compiler);
158 }
159
160 @Override
161 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
162 compilers.remove(cls);
163 }
164
165 @Override
166 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
167 return ImmutableMap.copyOf(compilers);
168 }
169
170 @Override
171 public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
172 installers.put(cls, installer);
173 }
174
175 @Override
176 public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
177 installers.remove(cls);
178 }
179
180 @Override
181 public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
182 return ImmutableMap.copyOf(installers);
183 }
184
185 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700186 * Returns the corresponding intent compiler to the specified intent.
187 *
188 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700189 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700190 * @return intent compiler corresponding to the specified intent
191 */
192 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
193 @SuppressWarnings("unchecked")
194 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
195 if (compiler == null) {
196 throw new IntentException("no compiler for class " + intent.getClass());
197 }
198 return compiler;
199 }
200
201 /**
202 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700203 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700204 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700205 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700206 * @return intent installer corresponding to the specified installable intent
207 */
208 private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
209 @SuppressWarnings("unchecked")
210 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
211 if (installer == null) {
212 throw new IntentException("no installer for class " + intent.getClass());
213 }
214 return installer;
215 }
216
217 /**
tom85258ee2014-10-07 00:10:02 -0700218 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700219 *
tom85258ee2014-10-07 00:10:02 -0700220 * @param intent intent to be compiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700221 */
tom85258ee2014-10-07 00:10:02 -0700222 private void executeCompilingPhase(Intent intent) {
223 // Indicate that the intent is entering the compiling phase.
224 store.setState(intent, COMPILING);
225
226 try {
227 // Compile the intent into installable derivatives.
228 List<InstallableIntent> installable = compileIntent(intent);
229
230 // If all went well, associate the resulting list of installable
231 // intents with the top-level intent and proceed to install.
232 store.addInstallableIntents(intent.id(), installable);
233 executeInstallingPhase(intent);
234
235 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700236 log.warn("Unable to compile intent {} due to: {}", intent.id(), e);
237
tom85258ee2014-10-07 00:10:02 -0700238 // If compilation failed, mark the intent as failed.
239 store.setState(intent, FAILED);
240 }
241 }
242
243 // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
244 // TODO: implement compilation traversing tree structure
245 private List<InstallableIntent> compileIntent(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700246 List<InstallableIntent> installable = new ArrayList<>();
247 for (Intent compiled : getCompiler(intent).compile(intent)) {
tom95329eb2014-10-06 08:40:06 -0700248 InstallableIntent installableIntent = (InstallableIntent) compiled;
249 installable.add(installableIntent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700250 }
tom85258ee2014-10-07 00:10:02 -0700251 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700252 }
253
254 /**
tom85258ee2014-10-07 00:10:02 -0700255 * Installs all installable intents associated with the specified top-level
256 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700257 *
tom85258ee2014-10-07 00:10:02 -0700258 * @param intent intent to be installed
Brian O'Connor66630c82014-10-02 21:08:19 -0700259 */
tom85258ee2014-10-07 00:10:02 -0700260 private void executeInstallingPhase(Intent intent) {
261 // Indicate that the intent is entering the installing phase.
262 store.setState(intent, INSTALLING);
263
264 try {
265 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
266 if (installables != null) {
267 for (InstallableIntent installable : installables) {
268 registerSubclassInstallerIfNeeded(installable);
tom53945d52014-10-07 11:01:36 -0700269 trackerService.addTrackedResources(intent.id(),
270 installable.requiredLinks());
tom85258ee2014-10-07 00:10:02 -0700271 getInstaller(installable).install(installable);
272 }
tom95329eb2014-10-06 08:40:06 -0700273 }
tom85258ee2014-10-07 00:10:02 -0700274 eventDispatcher.post(store.setState(intent, INSTALLED));
Brian O'Connor66630c82014-10-02 21:08:19 -0700275
tom85258ee2014-10-07 00:10:02 -0700276 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700277 log.warn("Unable to install intent {} due to: {}", intent.id(), e);
278 uninstallIntent(intent);
279
tom85258ee2014-10-07 00:10:02 -0700280 // If compilation failed, kick off the recompiling phase.
281 executeRecompilingPhase(intent);
282 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700283 }
284
285 /**
tom85258ee2014-10-07 00:10:02 -0700286 * Recompiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700287 *
tom85258ee2014-10-07 00:10:02 -0700288 * @param intent intent to be recompiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700289 */
tom85258ee2014-10-07 00:10:02 -0700290 private void executeRecompilingPhase(Intent intent) {
291 // Indicate that the intent is entering the recompiling phase.
292 store.setState(intent, RECOMPILING);
293
294 try {
295 // Compile the intent into installable derivatives.
296 List<InstallableIntent> installable = compileIntent(intent);
297
298 // If all went well, compare the existing list of installable
299 // intents with the newly compiled list. If they are the same,
300 // bail, out since the previous approach was determined not to
301 // be viable.
302 List<InstallableIntent> originalInstallable =
303 store.getInstallableIntents(intent.id());
304
305 if (Objects.equals(originalInstallable, installable)) {
306 eventDispatcher.post(store.setState(intent, FAILED));
307 } else {
308 // Otherwise, re-associate the newly compiled installable intents
309 // with the top-level intent and kick off installing phase.
310 store.addInstallableIntents(intent.id(), installable);
311 executeInstallingPhase(intent);
312 }
313 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700314 log.warn("Unable to recompile intent {} due to: {}", intent.id(), e);
315
tom85258ee2014-10-07 00:10:02 -0700316 // If compilation failed, mark the intent as failed.
317 eventDispatcher.post(store.setState(intent, FAILED));
318 }
319 }
320
321 /**
322 * Uninstalls the specified intent by uninstalling all of its associated
323 * installable derivatives.
324 *
325 * @param intent intent to be installed
326 */
327 private void executeWithdrawingPhase(Intent intent) {
328 // Indicate that the intent is being withdrawn.
329 store.setState(intent, WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700330 uninstallIntent(intent);
tom85258ee2014-10-07 00:10:02 -0700331
332 // If all went well, disassociate the top-level intent with its
333 // installable derivatives and mark it as withdrawn.
334 store.removeInstalledIntents(intent.id());
335 eventDispatcher.post(store.setState(intent, WITHDRAWN));
Brian O'Connor66630c82014-10-02 21:08:19 -0700336 }
337
338 /**
tom53945d52014-10-07 11:01:36 -0700339 * Uninstalls all installable intents associated with the given intent.
340 *
341 * @param intent intent to be uninstalled
342 */
343 private void uninstallIntent(Intent intent) {
344 try {
345 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
346 if (installables != null) {
347 for (InstallableIntent installable : installables) {
348 getInstaller(installable).uninstall(installable);
349 }
350 }
351 } catch (IntentException e) {
352 log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
353 }
354 }
355
356 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700357 * Registers an intent compiler of the specified intent if an intent compiler
358 * for the intent is not registered. This method traverses the class hierarchy of
359 * the intent. Once an intent compiler for a parent type is found, this method
360 * registers the found intent compiler.
361 *
362 * @param intent intent
363 */
364 private void registerSubclassCompilerIfNeeded(Intent intent) {
365 if (!compilers.containsKey(intent.getClass())) {
366 Class<?> cls = intent.getClass();
367 while (cls != Object.class) {
368 // As long as we're within the Intent class descendants
369 if (Intent.class.isAssignableFrom(cls)) {
370 IntentCompiler<?> compiler = compilers.get(cls);
371 if (compiler != null) {
372 compilers.put(intent.getClass(), compiler);
373 return;
374 }
375 }
376 cls = cls.getSuperclass();
377 }
378 }
379 }
380
381 /**
382 * Registers an intent installer of the specified intent if an intent installer
383 * for the intent is not registered. This method traverses the class hierarchy of
384 * the intent. Once an intent installer for a parent type is found, this method
385 * registers the found intent installer.
386 *
387 * @param intent intent
388 */
389 private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
390 if (!installers.containsKey(intent.getClass())) {
391 Class<?> cls = intent.getClass();
392 while (cls != Object.class) {
393 // As long as we're within the InstallableIntent class descendants
394 if (InstallableIntent.class.isAssignableFrom(cls)) {
395 IntentInstaller<?> installer = installers.get(cls);
396 if (installer != null) {
397 installers.put(intent.getClass(), installer);
398 return;
399 }
400 }
401 cls = cls.getSuperclass();
402 }
403 }
404 }
405
Brian O'Connor66630c82014-10-02 21:08:19 -0700406 // Store delegate to re-post events emitted from the store.
407 private class InternalStoreDelegate implements IntentStoreDelegate {
408 @Override
409 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700410 eventDispatcher.post(event);
411 if (event.type() == IntentEvent.Type.SUBMITTED) {
412 executor.execute(new IntentTask(COMPILING, event.subject()));
413 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700414 }
415 }
416
tom95329eb2014-10-06 08:40:06 -0700417 // Topology change delegate
418 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
419 @Override
tom85258ee2014-10-07 00:10:02 -0700420 public void triggerCompile(Iterable<IntentId> intentIds,
421 boolean compileAllFailed) {
422 // Attempt recompilation of the specified intents first.
tom95329eb2014-10-06 08:40:06 -0700423 for (IntentId intentId : intentIds) {
tom53945d52014-10-07 11:01:36 -0700424 Intent intent = getIntent(intentId);
425 uninstallIntent(intent);
alshabib8ca53902014-10-07 13:11:17 -0700426
tom53945d52014-10-07 11:01:36 -0700427 executeRecompilingPhase(intent);
tom85258ee2014-10-07 00:10:02 -0700428 }
429
430 if (compileAllFailed) {
431 // If required, compile all currently failed intents.
432 for (Intent intent : getIntents()) {
433 if (getIntentState(intent.id()) == FAILED) {
434 executeCompilingPhase(intent);
435 }
436 }
tom95329eb2014-10-06 08:40:06 -0700437 }
438 }
tom95329eb2014-10-06 08:40:06 -0700439 }
tom85258ee2014-10-07 00:10:02 -0700440
441 // Auxiliary runnable to perform asynchronous tasks.
442 private class IntentTask implements Runnable {
443 private final IntentState state;
444 private final Intent intent;
445
446 public IntentTask(IntentState state, Intent intent) {
447 this.state = state;
448 this.intent = intent;
449 }
450
451 @Override
452 public void run() {
453 if (state == COMPILING) {
454 executeCompilingPhase(intent);
455 } else if (state == RECOMPILING) {
456 executeRecompilingPhase(intent);
457 } else if (state == WITHDRAWING) {
458 executeWithdrawingPhase(intent);
459 }
460 }
461 }
462
Brian O'Connor66630c82014-10-02 21:08:19 -0700463}