blob: 9a2039c9b5b9574fcc8b4f4b2c30e958d73acd72 [file] [log] [blame]
Brian O'Connor66630c82014-10-02 21:08:19 -07001package org.onlab.onos.net.intent.impl;
2
tom95329eb2014-10-06 08:40:06 -07003import com.google.common.collect.ImmutableMap;
Brian O'Connor66630c82014-10-02 21:08:19 -07004import org.apache.felix.scr.annotations.Activate;
5import org.apache.felix.scr.annotations.Component;
6import org.apache.felix.scr.annotations.Deactivate;
7import org.apache.felix.scr.annotations.Reference;
8import org.apache.felix.scr.annotations.ReferenceCardinality;
9import org.apache.felix.scr.annotations.Service;
10import org.onlab.onos.event.AbstractListenerRegistry;
11import org.onlab.onos.event.EventDeliveryService;
12import org.onlab.onos.net.intent.InstallableIntent;
13import org.onlab.onos.net.intent.Intent;
14import org.onlab.onos.net.intent.IntentCompiler;
15import org.onlab.onos.net.intent.IntentEvent;
16import org.onlab.onos.net.intent.IntentException;
17import org.onlab.onos.net.intent.IntentExtensionService;
18import org.onlab.onos.net.intent.IntentId;
19import org.onlab.onos.net.intent.IntentInstaller;
20import org.onlab.onos.net.intent.IntentListener;
21import org.onlab.onos.net.intent.IntentOperations;
22import org.onlab.onos.net.intent.IntentService;
23import org.onlab.onos.net.intent.IntentState;
24import org.onlab.onos.net.intent.IntentStore;
25import org.onlab.onos.net.intent.IntentStoreDelegate;
26import org.slf4j.Logger;
27
tom95329eb2014-10-06 08:40:06 -070028import java.util.ArrayList;
29import java.util.List;
30import java.util.Map;
tom85258ee2014-10-07 00:10:02 -070031import java.util.Objects;
tom95329eb2014-10-06 08:40:06 -070032import java.util.concurrent.ConcurrentHashMap;
33import java.util.concurrent.ConcurrentMap;
tom85258ee2014-10-07 00:10:02 -070034import java.util.concurrent.ExecutorService;
tom95329eb2014-10-06 08:40:06 -070035
36import static com.google.common.base.Preconditions.checkNotNull;
tom85258ee2014-10-07 00:10:02 -070037import static java.util.concurrent.Executors.newSingleThreadExecutor;
tom95329eb2014-10-06 08:40:06 -070038import static org.onlab.onos.net.intent.IntentState.*;
tom53945d52014-10-07 11:01:36 -070039import static org.onlab.util.Tools.delay;
tom85258ee2014-10-07 00:10:02 -070040import static org.onlab.util.Tools.namedThreads;
tom95329eb2014-10-06 08:40:06 -070041import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070042
43/**
44 * An implementation of Intent Manager.
45 */
46@Component(immediate = true)
47@Service
48public class IntentManager
49 implements IntentService, IntentExtensionService {
50 private final Logger log = getLogger(getClass());
51
52 public static final String INTENT_NULL = "Intent cannot be null";
53 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
54
55 // Collections for compiler, installer, and listener are ONOS instance local
56 private final ConcurrentMap<Class<? extends Intent>,
57 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
58 private final ConcurrentMap<Class<? extends InstallableIntent>,
59 IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070060
61 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070062 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070063
tom85258ee2014-10-07 00:10:02 -070064 private ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents"));
65
Brian O'Connor66630c82014-10-02 21:08:19 -070066 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
tom95329eb2014-10-06 08:40:06 -070067 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
Brian O'Connor66630c82014-10-02 21:08:19 -070068
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected IntentStore store;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -070073 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -070074
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -070076 protected EventDeliveryService eventDispatcher;
77
78 @Activate
79 public void activate() {
80 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -070081 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -070082 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connor66630c82014-10-02 21:08:19 -070083 log.info("Started");
84 }
85
86 @Deactivate
87 public void deactivate() {
88 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -070089 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -070090 eventDispatcher.removeSink(IntentEvent.class);
91 log.info("Stopped");
92 }
93
94 @Override
95 public void submit(Intent intent) {
96 checkNotNull(intent, INTENT_NULL);
97 registerSubclassCompilerIfNeeded(intent);
98 IntentEvent event = store.createIntent(intent);
tom85258ee2014-10-07 00:10:02 -070099 if (event != null) {
100 eventDispatcher.post(event);
101 executor.execute(new IntentTask(COMPILING, intent));
102 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700103 }
104
105 @Override
106 public void withdraw(Intent intent) {
107 checkNotNull(intent, INTENT_NULL);
tom85258ee2014-10-07 00:10:02 -0700108 executor.execute(new IntentTask(WITHDRAWING, intent));
Brian O'Connor66630c82014-10-02 21:08:19 -0700109 }
110
111 // FIXME: implement this method
112 @Override
113 public void execute(IntentOperations operations) {
114 throw new UnsupportedOperationException("execute() is not implemented yet");
115 }
116
117 @Override
118 public Iterable<Intent> getIntents() {
119 return store.getIntents();
120 }
121
122 @Override
123 public long getIntentCount() {
124 return store.getIntentCount();
125 }
126
127 @Override
128 public Intent getIntent(IntentId id) {
129 checkNotNull(id, INTENT_ID_NULL);
130 return store.getIntent(id);
131 }
132
133 @Override
134 public IntentState getIntentState(IntentId id) {
135 checkNotNull(id, INTENT_ID_NULL);
136 return store.getIntentState(id);
137 }
138
139 @Override
140 public void addListener(IntentListener listener) {
141 listenerRegistry.addListener(listener);
142 }
143
144 @Override
145 public void removeListener(IntentListener listener) {
146 listenerRegistry.removeListener(listener);
147 }
148
149 @Override
150 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
151 compilers.put(cls, compiler);
152 }
153
154 @Override
155 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
156 compilers.remove(cls);
157 }
158
159 @Override
160 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
161 return ImmutableMap.copyOf(compilers);
162 }
163
164 @Override
165 public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
166 installers.put(cls, installer);
167 }
168
169 @Override
170 public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
171 installers.remove(cls);
172 }
173
174 @Override
175 public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
176 return ImmutableMap.copyOf(installers);
177 }
178
179 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700180 * Returns the corresponding intent compiler to the specified intent.
181 *
182 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700183 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700184 * @return intent compiler corresponding to the specified intent
185 */
186 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
187 @SuppressWarnings("unchecked")
188 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
189 if (compiler == null) {
190 throw new IntentException("no compiler for class " + intent.getClass());
191 }
192 return compiler;
193 }
194
195 /**
196 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700197 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700198 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700199 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700200 * @return intent installer corresponding to the specified installable intent
201 */
202 private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
203 @SuppressWarnings("unchecked")
204 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
205 if (installer == null) {
206 throw new IntentException("no installer for class " + intent.getClass());
207 }
208 return installer;
209 }
210
211 /**
tom85258ee2014-10-07 00:10:02 -0700212 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700213 *
tom85258ee2014-10-07 00:10:02 -0700214 * @param intent intent to be compiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700215 */
tom85258ee2014-10-07 00:10:02 -0700216 private void executeCompilingPhase(Intent intent) {
217 // Indicate that the intent is entering the compiling phase.
218 store.setState(intent, COMPILING);
219
220 try {
221 // Compile the intent into installable derivatives.
222 List<InstallableIntent> installable = compileIntent(intent);
223
224 // If all went well, associate the resulting list of installable
225 // intents with the top-level intent and proceed to install.
226 store.addInstallableIntents(intent.id(), installable);
227 executeInstallingPhase(intent);
228
229 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700230 log.warn("Unable to compile intent {} due to: {}", intent.id(), e);
231
tom85258ee2014-10-07 00:10:02 -0700232 // If compilation failed, mark the intent as failed.
233 store.setState(intent, FAILED);
234 }
235 }
236
237 // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
238 // TODO: implement compilation traversing tree structure
239 private List<InstallableIntent> compileIntent(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700240 List<InstallableIntent> installable = new ArrayList<>();
241 for (Intent compiled : getCompiler(intent).compile(intent)) {
tom95329eb2014-10-06 08:40:06 -0700242 InstallableIntent installableIntent = (InstallableIntent) compiled;
243 installable.add(installableIntent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700244 }
tom85258ee2014-10-07 00:10:02 -0700245 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700246 }
247
248 /**
tom85258ee2014-10-07 00:10:02 -0700249 * Installs all installable intents associated with the specified top-level
250 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700251 *
tom85258ee2014-10-07 00:10:02 -0700252 * @param intent intent to be installed
Brian O'Connor66630c82014-10-02 21:08:19 -0700253 */
tom85258ee2014-10-07 00:10:02 -0700254 private void executeInstallingPhase(Intent intent) {
255 // Indicate that the intent is entering the installing phase.
256 store.setState(intent, INSTALLING);
257
258 try {
259 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
260 if (installables != null) {
261 for (InstallableIntent installable : installables) {
262 registerSubclassInstallerIfNeeded(installable);
tom53945d52014-10-07 11:01:36 -0700263 trackerService.addTrackedResources(intent.id(),
264 installable.requiredLinks());
tom85258ee2014-10-07 00:10:02 -0700265 getInstaller(installable).install(installable);
266 }
tom95329eb2014-10-06 08:40:06 -0700267 }
tom85258ee2014-10-07 00:10:02 -0700268 eventDispatcher.post(store.setState(intent, INSTALLED));
Brian O'Connor66630c82014-10-02 21:08:19 -0700269
tom85258ee2014-10-07 00:10:02 -0700270 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700271 log.warn("Unable to install intent {} due to: {}", intent.id(), e);
272 uninstallIntent(intent);
273
tom85258ee2014-10-07 00:10:02 -0700274 // If compilation failed, kick off the recompiling phase.
275 executeRecompilingPhase(intent);
276 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700277 }
278
279 /**
tom85258ee2014-10-07 00:10:02 -0700280 * Recompiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700281 *
tom85258ee2014-10-07 00:10:02 -0700282 * @param intent intent to be recompiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700283 */
tom85258ee2014-10-07 00:10:02 -0700284 private void executeRecompilingPhase(Intent intent) {
285 // Indicate that the intent is entering the recompiling phase.
286 store.setState(intent, RECOMPILING);
287
288 try {
289 // Compile the intent into installable derivatives.
290 List<InstallableIntent> installable = compileIntent(intent);
291
292 // If all went well, compare the existing list of installable
293 // intents with the newly compiled list. If they are the same,
294 // bail, out since the previous approach was determined not to
295 // be viable.
296 List<InstallableIntent> originalInstallable =
297 store.getInstallableIntents(intent.id());
298
299 if (Objects.equals(originalInstallable, installable)) {
300 eventDispatcher.post(store.setState(intent, FAILED));
301 } else {
302 // Otherwise, re-associate the newly compiled installable intents
303 // with the top-level intent and kick off installing phase.
304 store.addInstallableIntents(intent.id(), installable);
305 executeInstallingPhase(intent);
306 }
307 } catch (Exception e) {
tom53945d52014-10-07 11:01:36 -0700308 log.warn("Unable to recompile intent {} due to: {}", intent.id(), e);
309
tom85258ee2014-10-07 00:10:02 -0700310 // If compilation failed, mark the intent as failed.
311 eventDispatcher.post(store.setState(intent, FAILED));
312 }
313 }
314
315 /**
316 * Uninstalls the specified intent by uninstalling all of its associated
317 * installable derivatives.
318 *
319 * @param intent intent to be installed
320 */
321 private void executeWithdrawingPhase(Intent intent) {
322 // Indicate that the intent is being withdrawn.
323 store.setState(intent, WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700324 uninstallIntent(intent);
tom85258ee2014-10-07 00:10:02 -0700325
326 // If all went well, disassociate the top-level intent with its
327 // installable derivatives and mark it as withdrawn.
328 store.removeInstalledIntents(intent.id());
329 eventDispatcher.post(store.setState(intent, WITHDRAWN));
Brian O'Connor66630c82014-10-02 21:08:19 -0700330 }
331
332 /**
tom53945d52014-10-07 11:01:36 -0700333 * Uninstalls all installable intents associated with the given intent.
334 *
335 * @param intent intent to be uninstalled
336 */
337 private void uninstallIntent(Intent intent) {
338 try {
339 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
340 if (installables != null) {
341 for (InstallableIntent installable : installables) {
342 getInstaller(installable).uninstall(installable);
343 }
344 }
345 } catch (IntentException e) {
346 log.warn("Unable to uninstall intent {} due to: {}", intent.id(), e);
347 }
348 }
349
350 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700351 * Registers an intent compiler of the specified intent if an intent compiler
352 * for the intent is not registered. This method traverses the class hierarchy of
353 * the intent. Once an intent compiler for a parent type is found, this method
354 * registers the found intent compiler.
355 *
356 * @param intent intent
357 */
358 private void registerSubclassCompilerIfNeeded(Intent intent) {
359 if (!compilers.containsKey(intent.getClass())) {
360 Class<?> cls = intent.getClass();
361 while (cls != Object.class) {
362 // As long as we're within the Intent class descendants
363 if (Intent.class.isAssignableFrom(cls)) {
364 IntentCompiler<?> compiler = compilers.get(cls);
365 if (compiler != null) {
366 compilers.put(intent.getClass(), compiler);
367 return;
368 }
369 }
370 cls = cls.getSuperclass();
371 }
372 }
373 }
374
375 /**
376 * Registers an intent installer of the specified intent if an intent installer
377 * for the intent is not registered. This method traverses the class hierarchy of
378 * the intent. Once an intent installer for a parent type is found, this method
379 * registers the found intent installer.
380 *
381 * @param intent intent
382 */
383 private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
384 if (!installers.containsKey(intent.getClass())) {
385 Class<?> cls = intent.getClass();
386 while (cls != Object.class) {
387 // As long as we're within the InstallableIntent class descendants
388 if (InstallableIntent.class.isAssignableFrom(cls)) {
389 IntentInstaller<?> installer = installers.get(cls);
390 if (installer != null) {
391 installers.put(intent.getClass(), installer);
392 return;
393 }
394 }
395 cls = cls.getSuperclass();
396 }
397 }
398 }
399
Brian O'Connor66630c82014-10-02 21:08:19 -0700400 // Store delegate to re-post events emitted from the store.
401 private class InternalStoreDelegate implements IntentStoreDelegate {
402 @Override
403 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700404 eventDispatcher.post(event);
405 if (event.type() == IntentEvent.Type.SUBMITTED) {
406 executor.execute(new IntentTask(COMPILING, event.subject()));
407 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700408 }
409 }
410
tom95329eb2014-10-06 08:40:06 -0700411 // Topology change delegate
412 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
413 @Override
tom85258ee2014-10-07 00:10:02 -0700414 public void triggerCompile(Iterable<IntentId> intentIds,
415 boolean compileAllFailed) {
416 // Attempt recompilation of the specified intents first.
tom95329eb2014-10-06 08:40:06 -0700417 for (IntentId intentId : intentIds) {
tom53945d52014-10-07 11:01:36 -0700418 Intent intent = getIntent(intentId);
419 uninstallIntent(intent);
420 delay(1000);
421 executeRecompilingPhase(intent);
tom85258ee2014-10-07 00:10:02 -0700422 }
423
424 if (compileAllFailed) {
425 // If required, compile all currently failed intents.
426 for (Intent intent : getIntents()) {
427 if (getIntentState(intent.id()) == FAILED) {
428 executeCompilingPhase(intent);
429 }
430 }
tom95329eb2014-10-06 08:40:06 -0700431 }
432 }
tom95329eb2014-10-06 08:40:06 -0700433 }
tom85258ee2014-10-07 00:10:02 -0700434
435 // Auxiliary runnable to perform asynchronous tasks.
436 private class IntentTask implements Runnable {
437 private final IntentState state;
438 private final Intent intent;
439
440 public IntentTask(IntentState state, Intent intent) {
441 this.state = state;
442 this.intent = intent;
443 }
444
445 @Override
446 public void run() {
447 if (state == COMPILING) {
448 executeCompilingPhase(intent);
449 } else if (state == RECOMPILING) {
450 executeRecompilingPhase(intent);
451 } else if (state == WITHDRAWING) {
452 executeWithdrawingPhase(intent);
453 }
454 }
455 }
456
Brian O'Connor66630c82014-10-02 21:08:19 -0700457}