blob: 197c2b27ae5be1ba4e83327a3f7db6e6f7e8f083 [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.*;
tom85258ee2014-10-07 00:10:02 -070039import static org.onlab.util.Tools.namedThreads;
tom95329eb2014-10-06 08:40:06 -070040import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070041
42/**
43 * An implementation of Intent Manager.
44 */
45@Component(immediate = true)
46@Service
47public class IntentManager
48 implements IntentService, IntentExtensionService {
49 private final Logger log = getLogger(getClass());
50
51 public static final String INTENT_NULL = "Intent cannot be null";
52 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
53
54 // Collections for compiler, installer, and listener are ONOS instance local
55 private final ConcurrentMap<Class<? extends Intent>,
56 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
57 private final ConcurrentMap<Class<? extends InstallableIntent>,
58 IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070059
60 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -070061 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -070062
tom85258ee2014-10-07 00:10:02 -070063 private ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents"));
64
Brian O'Connor66630c82014-10-02 21:08:19 -070065 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
tom95329eb2014-10-06 08:40:06 -070066 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
Brian O'Connor66630c82014-10-02 21:08:19 -070067
68 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected IntentStore store;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -070072 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -070073
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -070075 protected EventDeliveryService eventDispatcher;
76
77 @Activate
78 public void activate() {
79 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -070080 trackerService.setDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -070081 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Brian O'Connor66630c82014-10-02 21:08:19 -070082 log.info("Started");
83 }
84
85 @Deactivate
86 public void deactivate() {
87 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -070088 trackerService.unsetDelegate(topoDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -070089 eventDispatcher.removeSink(IntentEvent.class);
90 log.info("Stopped");
91 }
92
93 @Override
94 public void submit(Intent intent) {
95 checkNotNull(intent, INTENT_NULL);
96 registerSubclassCompilerIfNeeded(intent);
97 IntentEvent event = store.createIntent(intent);
tom85258ee2014-10-07 00:10:02 -070098 if (event != null) {
99 eventDispatcher.post(event);
100 executor.execute(new IntentTask(COMPILING, intent));
101 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700102 }
103
104 @Override
105 public void withdraw(Intent intent) {
106 checkNotNull(intent, INTENT_NULL);
tom85258ee2014-10-07 00:10:02 -0700107 executor.execute(new IntentTask(WITHDRAWING, intent));
Brian O'Connor66630c82014-10-02 21:08:19 -0700108 }
109
110 // FIXME: implement this method
111 @Override
112 public void execute(IntentOperations operations) {
113 throw new UnsupportedOperationException("execute() is not implemented yet");
114 }
115
116 @Override
117 public Iterable<Intent> getIntents() {
118 return store.getIntents();
119 }
120
121 @Override
122 public long getIntentCount() {
123 return store.getIntentCount();
124 }
125
126 @Override
127 public Intent getIntent(IntentId id) {
128 checkNotNull(id, INTENT_ID_NULL);
129 return store.getIntent(id);
130 }
131
132 @Override
133 public IntentState getIntentState(IntentId id) {
134 checkNotNull(id, INTENT_ID_NULL);
135 return store.getIntentState(id);
136 }
137
138 @Override
139 public void addListener(IntentListener listener) {
140 listenerRegistry.addListener(listener);
141 }
142
143 @Override
144 public void removeListener(IntentListener listener) {
145 listenerRegistry.removeListener(listener);
146 }
147
148 @Override
149 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
150 compilers.put(cls, compiler);
151 }
152
153 @Override
154 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
155 compilers.remove(cls);
156 }
157
158 @Override
159 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
160 return ImmutableMap.copyOf(compilers);
161 }
162
163 @Override
164 public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
165 installers.put(cls, installer);
166 }
167
168 @Override
169 public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
170 installers.remove(cls);
171 }
172
173 @Override
174 public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
175 return ImmutableMap.copyOf(installers);
176 }
177
178 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700179 * Returns the corresponding intent compiler to the specified intent.
180 *
181 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700182 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700183 * @return intent compiler corresponding to the specified intent
184 */
185 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
186 @SuppressWarnings("unchecked")
187 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
188 if (compiler == null) {
189 throw new IntentException("no compiler for class " + intent.getClass());
190 }
191 return compiler;
192 }
193
194 /**
195 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700196 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700197 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700198 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700199 * @return intent installer corresponding to the specified installable intent
200 */
201 private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
202 @SuppressWarnings("unchecked")
203 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
204 if (installer == null) {
205 throw new IntentException("no installer for class " + intent.getClass());
206 }
207 return installer;
208 }
209
210 /**
tom85258ee2014-10-07 00:10:02 -0700211 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700212 *
tom85258ee2014-10-07 00:10:02 -0700213 * @param intent intent to be compiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700214 */
tom85258ee2014-10-07 00:10:02 -0700215 private void executeCompilingPhase(Intent intent) {
216 // Indicate that the intent is entering the compiling phase.
217 store.setState(intent, COMPILING);
218
219 try {
220 // Compile the intent into installable derivatives.
221 List<InstallableIntent> installable = compileIntent(intent);
222
223 // If all went well, associate the resulting list of installable
224 // intents with the top-level intent and proceed to install.
225 store.addInstallableIntents(intent.id(), installable);
226 executeInstallingPhase(intent);
227
228 } catch (Exception e) {
229 // If compilation failed, mark the intent as failed.
230 store.setState(intent, FAILED);
231 }
232 }
233
234 // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
235 // TODO: implement compilation traversing tree structure
236 private List<InstallableIntent> compileIntent(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700237 List<InstallableIntent> installable = new ArrayList<>();
238 for (Intent compiled : getCompiler(intent).compile(intent)) {
tom95329eb2014-10-06 08:40:06 -0700239 InstallableIntent installableIntent = (InstallableIntent) compiled;
240 installable.add(installableIntent);
tom85258ee2014-10-07 00:10:02 -0700241 trackerService.addTrackedResources(intent.id(),
tom95329eb2014-10-06 08:40:06 -0700242 installableIntent.requiredLinks());
Brian O'Connor66630c82014-10-02 21:08:19 -0700243 }
tom85258ee2014-10-07 00:10:02 -0700244 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700245 }
246
247 /**
tom85258ee2014-10-07 00:10:02 -0700248 * Installs all installable intents associated with the specified top-level
249 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700250 *
tom85258ee2014-10-07 00:10:02 -0700251 * @param intent intent to be installed
Brian O'Connor66630c82014-10-02 21:08:19 -0700252 */
tom85258ee2014-10-07 00:10:02 -0700253 private void executeInstallingPhase(Intent intent) {
254 // Indicate that the intent is entering the installing phase.
255 store.setState(intent, INSTALLING);
256
257 try {
258 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
259 if (installables != null) {
260 for (InstallableIntent installable : installables) {
261 registerSubclassInstallerIfNeeded(installable);
262 getInstaller(installable).install(installable);
263 }
tom95329eb2014-10-06 08:40:06 -0700264 }
tom85258ee2014-10-07 00:10:02 -0700265 eventDispatcher.post(store.setState(intent, INSTALLED));
Brian O'Connor66630c82014-10-02 21:08:19 -0700266
tom85258ee2014-10-07 00:10:02 -0700267 } catch (Exception e) {
268 // If compilation failed, kick off the recompiling phase.
269 executeRecompilingPhase(intent);
270 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700271 }
272
273 /**
tom85258ee2014-10-07 00:10:02 -0700274 * Recompiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700275 *
tom85258ee2014-10-07 00:10:02 -0700276 * @param intent intent to be recompiled
Brian O'Connor66630c82014-10-02 21:08:19 -0700277 */
tom85258ee2014-10-07 00:10:02 -0700278 private void executeRecompilingPhase(Intent intent) {
279 // Indicate that the intent is entering the recompiling phase.
280 store.setState(intent, RECOMPILING);
281
282 try {
283 // Compile the intent into installable derivatives.
284 List<InstallableIntent> installable = compileIntent(intent);
285
286 // If all went well, compare the existing list of installable
287 // intents with the newly compiled list. If they are the same,
288 // bail, out since the previous approach was determined not to
289 // be viable.
290 List<InstallableIntent> originalInstallable =
291 store.getInstallableIntents(intent.id());
292
293 if (Objects.equals(originalInstallable, installable)) {
294 eventDispatcher.post(store.setState(intent, FAILED));
295 } else {
296 // Otherwise, re-associate the newly compiled installable intents
297 // with the top-level intent and kick off installing phase.
298 store.addInstallableIntents(intent.id(), installable);
299 executeInstallingPhase(intent);
300 }
301 } catch (Exception e) {
302 // If compilation failed, mark the intent as failed.
303 eventDispatcher.post(store.setState(intent, FAILED));
304 }
305 }
306
307 /**
308 * Uninstalls the specified intent by uninstalling all of its associated
309 * installable derivatives.
310 *
311 * @param intent intent to be installed
312 */
313 private void executeWithdrawingPhase(Intent intent) {
314 // Indicate that the intent is being withdrawn.
315 store.setState(intent, WITHDRAWING);
316 List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
tom95329eb2014-10-06 08:40:06 -0700317 if (installables != null) {
318 for (InstallableIntent installable : installables) {
319 getInstaller(installable).uninstall(installable);
320 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700321 }
tom85258ee2014-10-07 00:10:02 -0700322
323 // If all went well, disassociate the top-level intent with its
324 // installable derivatives and mark it as withdrawn.
325 store.removeInstalledIntents(intent.id());
326 eventDispatcher.post(store.setState(intent, WITHDRAWN));
Brian O'Connor66630c82014-10-02 21:08:19 -0700327 }
328
329 /**
330 * Registers an intent compiler of the specified intent if an intent compiler
331 * for the intent is not registered. This method traverses the class hierarchy of
332 * the intent. Once an intent compiler for a parent type is found, this method
333 * registers the found intent compiler.
334 *
335 * @param intent intent
336 */
337 private void registerSubclassCompilerIfNeeded(Intent intent) {
338 if (!compilers.containsKey(intent.getClass())) {
339 Class<?> cls = intent.getClass();
340 while (cls != Object.class) {
341 // As long as we're within the Intent class descendants
342 if (Intent.class.isAssignableFrom(cls)) {
343 IntentCompiler<?> compiler = compilers.get(cls);
344 if (compiler != null) {
345 compilers.put(intent.getClass(), compiler);
346 return;
347 }
348 }
349 cls = cls.getSuperclass();
350 }
351 }
352 }
353
354 /**
355 * Registers an intent installer of the specified intent if an intent installer
356 * for the intent is not registered. This method traverses the class hierarchy of
357 * the intent. Once an intent installer for a parent type is found, this method
358 * registers the found intent installer.
359 *
360 * @param intent intent
361 */
362 private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
363 if (!installers.containsKey(intent.getClass())) {
364 Class<?> cls = intent.getClass();
365 while (cls != Object.class) {
366 // As long as we're within the InstallableIntent class descendants
367 if (InstallableIntent.class.isAssignableFrom(cls)) {
368 IntentInstaller<?> installer = installers.get(cls);
369 if (installer != null) {
370 installers.put(intent.getClass(), installer);
371 return;
372 }
373 }
374 cls = cls.getSuperclass();
375 }
376 }
377 }
378
Brian O'Connor66630c82014-10-02 21:08:19 -0700379 // Store delegate to re-post events emitted from the store.
380 private class InternalStoreDelegate implements IntentStoreDelegate {
381 @Override
382 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700383 eventDispatcher.post(event);
384 if (event.type() == IntentEvent.Type.SUBMITTED) {
385 executor.execute(new IntentTask(COMPILING, event.subject()));
386 }
Brian O'Connor66630c82014-10-02 21:08:19 -0700387 }
388 }
389
tom95329eb2014-10-06 08:40:06 -0700390 // Topology change delegate
391 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
392 @Override
tom85258ee2014-10-07 00:10:02 -0700393 public void triggerCompile(Iterable<IntentId> intentIds,
394 boolean compileAllFailed) {
395 // Attempt recompilation of the specified intents first.
tom95329eb2014-10-06 08:40:06 -0700396 for (IntentId intentId : intentIds) {
tom85258ee2014-10-07 00:10:02 -0700397 executeRecompilingPhase(getIntent(intentId));
398 }
399
400 if (compileAllFailed) {
401 // If required, compile all currently failed intents.
402 for (Intent intent : getIntents()) {
403 if (getIntentState(intent.id()) == FAILED) {
404 executeCompilingPhase(intent);
405 }
406 }
tom95329eb2014-10-06 08:40:06 -0700407 }
408 }
tom95329eb2014-10-06 08:40:06 -0700409 }
tom85258ee2014-10-07 00:10:02 -0700410
411 // Auxiliary runnable to perform asynchronous tasks.
412 private class IntentTask implements Runnable {
413 private final IntentState state;
414 private final Intent intent;
415
416 public IntentTask(IntentState state, Intent intent) {
417 this.state = state;
418 this.intent = intent;
419 }
420
421 @Override
422 public void run() {
423 if (state == COMPILING) {
424 executeCompilingPhase(intent);
425 } else if (state == RECOMPILING) {
426 executeRecompilingPhase(intent);
427 } else if (state == WITHDRAWING) {
428 executeWithdrawingPhase(intent);
429 }
430 }
431 }
432
Brian O'Connor66630c82014-10-02 21:08:19 -0700433}