blob: f1c9de309a90b95e1e4076a07cbfd5cca82bd7da [file] [log] [blame]
Brian O'Connor66630c82014-10-02 21:08:19 -07001package org.onlab.onos.net.intent.impl;
2
3import static com.google.common.base.Preconditions.checkNotNull;
4import static org.onlab.onos.net.intent.IntentState.FAILED;
5import static org.onlab.onos.net.intent.IntentState.INSTALLED;
6import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
7import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
8import static org.slf4j.LoggerFactory.getLogger;
9
10import java.util.ArrayList;
11import java.util.List;
12import java.util.Map;
13import java.util.concurrent.ConcurrentHashMap;
14import java.util.concurrent.ConcurrentMap;
15import java.util.concurrent.CopyOnWriteArrayList;
16
17import org.apache.felix.scr.annotations.Activate;
18import org.apache.felix.scr.annotations.Component;
19import org.apache.felix.scr.annotations.Deactivate;
20import org.apache.felix.scr.annotations.Reference;
21import org.apache.felix.scr.annotations.ReferenceCardinality;
22import org.apache.felix.scr.annotations.Service;
23import org.onlab.onos.event.AbstractListenerRegistry;
24import org.onlab.onos.event.EventDeliveryService;
25import org.onlab.onos.net.intent.InstallableIntent;
26import org.onlab.onos.net.intent.Intent;
27import org.onlab.onos.net.intent.IntentCompiler;
28import org.onlab.onos.net.intent.IntentEvent;
29import org.onlab.onos.net.intent.IntentException;
30import org.onlab.onos.net.intent.IntentExtensionService;
31import org.onlab.onos.net.intent.IntentId;
32import org.onlab.onos.net.intent.IntentInstaller;
33import org.onlab.onos.net.intent.IntentListener;
34import org.onlab.onos.net.intent.IntentOperations;
35import org.onlab.onos.net.intent.IntentService;
36import org.onlab.onos.net.intent.IntentState;
37import org.onlab.onos.net.intent.IntentStore;
38import org.onlab.onos.net.intent.IntentStoreDelegate;
39import org.slf4j.Logger;
40
41import com.google.common.collect.ImmutableMap;
42
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<>();
60 private final CopyOnWriteArrayList<IntentListener> listeners = new CopyOnWriteArrayList<>();
61
62 private final AbstractListenerRegistry<IntentEvent, IntentListener>
63 listenerRegistry = new AbstractListenerRegistry<>();
64
65 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
66
67 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 protected IntentStore store;
69
70 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected EventDeliveryService eventDispatcher;
72
73 @Activate
74 public void activate() {
75 store.setDelegate(delegate);
76 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
77
78// this.intentEvents = new IntentMap<>("intentState", IntentEvent.class, collectionsService);
79// this.installableIntents =
80// new IntentMap<>("installableIntents", IntentCompilationResult.class, collectionsService);
81//
82//
83// this.intentEvents.addListener(new InternalEntryListener(new InternalIntentEventListener()));
84
85 log.info("Started");
86 }
87
88 @Deactivate
89 public void deactivate() {
90 store.unsetDelegate(delegate);
91 eventDispatcher.removeSink(IntentEvent.class);
92 log.info("Stopped");
93 }
94
95 @Override
96 public void submit(Intent intent) {
97 checkNotNull(intent, INTENT_NULL);
98 registerSubclassCompilerIfNeeded(intent);
99 IntentEvent event = store.createIntent(intent);
100 eventDispatcher.post(event);
101 processStoreEvent(event);
102 }
103
104 @Override
105 public void withdraw(Intent intent) {
106 checkNotNull(intent, INTENT_NULL);
107 IntentEvent event = store.setState(intent, WITHDRAWING);
108 eventDispatcher.post(event);
109 processStoreEvent(event);
110 }
111
112 // FIXME: implement this method
113 @Override
114 public void execute(IntentOperations operations) {
115 throw new UnsupportedOperationException("execute() is not implemented yet");
116 }
117
118 @Override
119 public Iterable<Intent> getIntents() {
120 return store.getIntents();
121 }
122
123 @Override
124 public long getIntentCount() {
125 return store.getIntentCount();
126 }
127
128 @Override
129 public Intent getIntent(IntentId id) {
130 checkNotNull(id, INTENT_ID_NULL);
131 return store.getIntent(id);
132 }
133
134 @Override
135 public IntentState getIntentState(IntentId id) {
136 checkNotNull(id, INTENT_ID_NULL);
137 return store.getIntentState(id);
138 }
139
140 @Override
141 public void addListener(IntentListener listener) {
142 listenerRegistry.addListener(listener);
143 }
144
145 @Override
146 public void removeListener(IntentListener listener) {
147 listenerRegistry.removeListener(listener);
148 }
149
150 @Override
151 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
152 compilers.put(cls, compiler);
153 }
154
155 @Override
156 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
157 compilers.remove(cls);
158 }
159
160 @Override
161 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
162 return ImmutableMap.copyOf(compilers);
163 }
164
165 @Override
166 public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
167 installers.put(cls, installer);
168 }
169
170 @Override
171 public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
172 installers.remove(cls);
173 }
174
175 @Override
176 public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
177 return ImmutableMap.copyOf(installers);
178 }
179
180 /**
181 * Invokes all of registered intent event listener.
182 *
183 * @param event event supplied to a listener as an argument
184 */
185 private void invokeListeners(IntentEvent event) {
186 for (IntentListener listener : listeners) {
187 listener.event(event);
188 }
189 }
190
191 /**
192 * Returns the corresponding intent compiler to the specified intent.
193 *
194 * @param intent intent
195 * @param <T> the type of intent
196 * @return intent compiler corresponding to the specified intent
197 */
198 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
199 @SuppressWarnings("unchecked")
200 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
201 if (compiler == null) {
202 throw new IntentException("no compiler for class " + intent.getClass());
203 }
204 return compiler;
205 }
206
207 /**
208 * Returns the corresponding intent installer to the specified installable intent.
209 * @param intent intent
210 * @param <T> the type of installable intent
211 * @return intent installer corresponding to the specified installable intent
212 */
213 private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
214 @SuppressWarnings("unchecked")
215 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
216 if (installer == null) {
217 throw new IntentException("no installer for class " + intent.getClass());
218 }
219 return installer;
220 }
221
222 /**
223 * Compiles an intent.
224 *
225 * @param intent intent
226 */
227 private void compileIntent(Intent intent) {
228 // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
229 // TODO: implement compilation traversing tree structure
230 List<InstallableIntent> installable = new ArrayList<>();
231 for (Intent compiled : getCompiler(intent).compile(intent)) {
232 installable.add((InstallableIntent) compiled);
233 }
234 IntentEvent event = store.addInstallableIntents(intent.getId(), installable);
235 eventDispatcher.post(event);
236 processStoreEvent(event);
237 }
238
239 /**
240 * Installs an intent.
241 *
242 * @param intent intent
243 */
244 private void installIntent(Intent intent) {
245 for (InstallableIntent installable : store.getInstallableIntents(intent.getId())) {
246 registerSubclassInstallerIfNeeded(installable);
247 getInstaller(installable).install(installable);
248 }
249
250 IntentEvent event = store.setState(intent, INSTALLED);
251 eventDispatcher.post(event);
252 processStoreEvent(event);
253
254 }
255
256 /**
257 * Uninstalls an intent.
258 *
259 * @param intent intent
260 */
261 private void uninstallIntent(Intent intent) {
262 for (InstallableIntent installable : store.getInstallableIntents(intent.getId())) {
263 getInstaller(installable).uninstall(installable);
264 }
265
266 store.removeInstalledIntents(intent.getId());
267 store.setState(intent, WITHDRAWN);
268 }
269
270 /**
271 * Registers an intent compiler of the specified intent if an intent compiler
272 * for the intent is not registered. This method traverses the class hierarchy of
273 * the intent. Once an intent compiler for a parent type is found, this method
274 * registers the found intent compiler.
275 *
276 * @param intent intent
277 */
278 private void registerSubclassCompilerIfNeeded(Intent intent) {
279 if (!compilers.containsKey(intent.getClass())) {
280 Class<?> cls = intent.getClass();
281 while (cls != Object.class) {
282 // As long as we're within the Intent class descendants
283 if (Intent.class.isAssignableFrom(cls)) {
284 IntentCompiler<?> compiler = compilers.get(cls);
285 if (compiler != null) {
286 compilers.put(intent.getClass(), compiler);
287 return;
288 }
289 }
290 cls = cls.getSuperclass();
291 }
292 }
293 }
294
295 /**
296 * Registers an intent installer of the specified intent if an intent installer
297 * for the intent is not registered. This method traverses the class hierarchy of
298 * the intent. Once an intent installer for a parent type is found, this method
299 * registers the found intent installer.
300 *
301 * @param intent intent
302 */
303 private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
304 if (!installers.containsKey(intent.getClass())) {
305 Class<?> cls = intent.getClass();
306 while (cls != Object.class) {
307 // As long as we're within the InstallableIntent class descendants
308 if (InstallableIntent.class.isAssignableFrom(cls)) {
309 IntentInstaller<?> installer = installers.get(cls);
310 if (installer != null) {
311 installers.put(intent.getClass(), installer);
312 return;
313 }
314 }
315 cls = cls.getSuperclass();
316 }
317 }
318 }
319
320 /**
321 * Handles state transition of submitted intents.
322 */
323 private void processStoreEvent(IntentEvent event) {
324 invokeListeners(event);
325 Intent intent = event.getIntent();
326
327 try {
328 switch (event.getState()) {
329 case SUBMITTED:
330 compileIntent(intent);
331 break;
332 case COMPILED:
333 installIntent(intent);
334 break;
335 case INSTALLED:
336 break;
337 case WITHDRAWING:
338 uninstallIntent(intent);
339 break;
340 case WITHDRAWN:
341 break;
342 case FAILED:
343 break;
344 default:
345 throw new IllegalStateException(
346 "the state of IntentEvent is illegal: " + event.getState());
347 }
348 } catch (IntentException e) {
349 store.setState(intent, FAILED);
350 }
351
352 }
353
354 // Store delegate to re-post events emitted from the store.
355 private class InternalStoreDelegate implements IntentStoreDelegate {
356 @Override
357 public void notify(IntentEvent event) {
358 processStoreEvent(event);
359 eventDispatcher.post(event);
360 }
361 }
362
363}