blob: 475dca1fe98e9f2ce8204d87164018db87e31300 [file] [log] [blame]
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -07001package net.onrc.onos.core.topology;
2
3import java.nio.ByteBuffer;
4import java.util.HashMap;
5import java.util.LinkedList;
6import java.util.List;
7import java.util.Map;
8
9import net.floodlightcontroller.core.IFloodlightProviderService.Role;
10import net.onrc.onos.core.registry.IControllerRegistryService;
11import net.onrc.onos.core.registry.RegistryException;
12import net.onrc.onos.core.util.Dpid;
13import net.onrc.onos.core.util.EventEntry;
14import net.onrc.onos.core.util.OnosInstanceId;
15
16import static com.google.common.base.Preconditions.checkNotNull;
17
18import org.slf4j.Logger;
19import org.slf4j.LoggerFactory;
20
21/**
22 * Topology Event pre-processor. It is used by the Topology Manager for
23 * pre-processing Topology events before applying them to the Topology.
24 * <p/>
25 * The pre-processor itself keeps internal state about the most recent
26 * ADD events. It also might keep state about reordered events that cannot
27 * be applied.
28 * <p/>
29 * As part of the pre-processing logic, a previously suppressed event might
30 * be genenerated later because of some other event.
31 */
32public class TopologyEventPreprocessor {
33 private static final Logger log = LoggerFactory
34 .getLogger(TopologyEventPreprocessor.class);
35 private final IControllerRegistryService registryService;
36
37 //
38 // Reordered ADD events that need to be reapplied
39 //
40 // TODO: For now, this field is accessed by the TopologyManager as well
41 // This should be refactored, and change them to private.
42 //
43 Map<ByteBuffer, TopologyEvent> reorderedEvents = new HashMap<>();
44
45 //
46 // Topology ADD event state per ONOS instance
47 //
48 private Map<OnosInstanceId, OnosInstanceLastAddEvents> instanceState =
49 new HashMap<>();
50
51 //
52 // Switch mastership state (updated by the topology events)
53 //
54 Map<Dpid, OnosInstanceId> switchMastership = new HashMap<>();
55
56 /**
57 * Constructor for a given Registry Service.
58 *
59 * @param registryService the Registry Service to use.
60 */
61 TopologyEventPreprocessor(IControllerRegistryService registryService) {
62 this.registryService = registryService;
63 }
64
65 /**
66 * Class to store the last ADD Topology Events per ONOS Instance.
67 */
68 private final class OnosInstanceLastAddEvents {
69 private final OnosInstanceId onosInstanceId;
70
71 // The last ADD events received from this ONOS instance
72 Map<ByteBuffer, TopologyEvent> topologyEvents = new HashMap<>();
73
74 /**
75 * Constructor for a given ONOS Instance ID.
76 *
77 * @param onosInstanceId the ONOS Instance ID.
78 */
79 OnosInstanceLastAddEvents(OnosInstanceId onosInstanceId) {
80 this.onosInstanceId = checkNotNull(onosInstanceId);
81 }
82
83 /**
84 * Processes an event originated by this ONOS instance.
85 *
86 * @param event the event to process.
87 * @return true if the event should be applied to the final Topology
88 * as well, otherwise false.
89 */
90 boolean processEvent(EventEntry<TopologyEvent> event) {
91 TopologyEvent topologyEvent = event.eventData();
92 ByteBuffer id = topologyEvent.getIDasByteBuffer();
93 OnosInstanceId masterId = null;
94
95 // Get the Master of the Origin DPID
96 Dpid dpid = topologyEvent.getOriginDpid();
97 if (dpid != null) {
98 masterId = switchMastership.get(dpid);
99 }
100
101 //
102 // Apply the event based on its type
103 //
104 switch (event.eventType()) {
105 case ENTRY_ADD:
106 topologyEvents.put(id, topologyEvent);
107 reorderedEvents.remove(id);
108 // Allow the ADD only if the event was originated by the Master
109 return onosInstanceId.equals(masterId);
110
111 case ENTRY_REMOVE:
112 reorderedEvents.remove(id);
113 // Don't allow the REMOVE event if there was no ADD before
114 if (topologyEvents.remove(id) == null) {
115 return false;
116 }
117 //
118 // Allow the REMOVE if the event was originated by the Master,
119 // or there is no Master at all.
120 //
121 if (masterId == null) {
122 return true;
123 }
124 return onosInstanceId.equals(masterId);
125
126 default:
127 log.error("Unknown topology event {}", event.eventType());
128 }
129
130 return false;
131 }
132
133 /**
134 * Gets the postponed events for a given DPID.
135 * Those are the events that couldn't be applied earlier to the
136 * Topology, because the ONOS Instance originating the events
137 * was not the Master for the Switch.
138 *
139 * @param dpid the DPID to use.
140 * @return a list of postponed events for the given DPID.
141 */
142 List<EventEntry<TopologyEvent>> getPostponedEvents(Dpid dpid) {
143 List<EventEntry<TopologyEvent>> result = new LinkedList<>();
144
145 //
146 // Search all events, and keep only those that match the DPID
147 //
148 // TODO: This could be slow, and the code should be optimized
149 // for speed. The processing complexity is O(N*N) where N is
150 // the number of Switches: for each Switch Mastership we call
151 // getPostponedEvents(), and then for each call we
152 // search all previously added events.
153 // The code can be optimized by adding additional lookup map:
154 // Dpid -> List<TopologyEvent>
155 //
156 for (TopologyEvent te : topologyEvents.values()) {
157 if (dpid.equals(te.getOriginDpid())) {
158 result.add(new EventEntry<TopologyEvent>(EventEntry.Type.ENTRY_ADD, te));
159 }
160 }
161
162 return result;
163 }
164 }
165
166 /**
167 * Extracts previously reordered events that should be applied again
168 * to the Topology.
169 *
170 * @return a list of previously reordered events.
171 */
172 List<EventEntry<TopologyEvent>> extractReorderedEvents() {
173 List<EventEntry<TopologyEvent>> result = new LinkedList<>();
174
175 //
176 // Search all previously reordered events, and extract only if
177 // the originator is the Master.
178 //
179 List<TopologyEvent> leftoverEvents = new LinkedList<>();
180 for (TopologyEvent te : reorderedEvents.values()) {
181 Dpid dpid = te.getOriginDpid();
182 OnosInstanceId masterId = null;
183 if (dpid != null) {
184 masterId = switchMastership.get(dpid);
185 }
186 if (te.getOnosInstanceId().equals(masterId)) {
187 result.add(new EventEntry<TopologyEvent>(EventEntry.Type.ENTRY_ADD, te));
188 } else {
189 leftoverEvents.add(te);
190 }
191 }
192
193 //
194 // Add back the leftover events
195 //
196 reorderedEvents.clear();
197 for (TopologyEvent te : leftoverEvents) {
198 reorderedEvents.put(te.getIDasByteBuffer(), te);
199 }
200
201 return result;
202 }
203
204 /**
205 * Pre-processes a list of events.
206 *
207 * @param events the events to pre-process.
208 * @return a list of pre-processed events.
209 */
210 List<EventEntry<TopologyEvent>> processEvents(
211 List<EventEntry<TopologyEvent>> events) {
212 List<EventEntry<TopologyEvent>> result = new LinkedList<>();
213
214 //
215 // Process the events
216 //
217 for (EventEntry<TopologyEvent> event : events) {
218 List<EventEntry<TopologyEvent>> postponedEvents = null;
219
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700220 // Ignore NO-OP events
221 if (event.isNoop()) {
222 continue;
223 }
224
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700225 TopologyEvent topologyEvent = event.eventData();
226 OnosInstanceId onosInstanceId = topologyEvent.getOnosInstanceId();
227
228 log.debug("Topology event {}: {}", event.eventType(),
229 topologyEvent);
230
231 // Get the ONOS instance state
232 OnosInstanceLastAddEvents instance =
233 instanceState.get(onosInstanceId);
234 if (instance == null) {
235 instance = new OnosInstanceLastAddEvents(onosInstanceId);
236 instanceState.put(onosInstanceId, instance);
237 }
238
239 //
240 // Update the Switch Mastership state:
241 // - If ADD a MASTER and the Mastership is confirmed by the
242 // Registry Service, then add to the Mastership map and fetch
243 // the postponed events from the originating ONOS Instance.
244 // - Otherwise, remove from the Mastership map, but only if it is
245 // the current MASTER.
246 //
247 MastershipEvent mastershipEvent =
248 topologyEvent.getMastershipEvent();
249 if (mastershipEvent != null) {
250 Dpid dpid = mastershipEvent.getDpid();
251 boolean newMaster = false;
252
253 if ((event.eventType() == EventEntry.Type.ENTRY_ADD) &&
254 (mastershipEvent.getRole() == Role.MASTER)) {
255 //
256 // Check with the Registry Service as well
257 //
258 try {
259 String rc =
260 registryService.getControllerForSwitch(dpid.value());
261 if ((rc != null) &&
262 onosInstanceId.equals(new OnosInstanceId(rc))) {
263 newMaster = true;
264 }
265 } catch (RegistryException e) {
266 log.error("Caught RegistryException while pre-processing Mastership Event", e);
267 }
268 }
269
270 if (newMaster) {
271 // Add to the map
272 switchMastership.put(dpid, onosInstanceId);
273 postponedEvents = instance.getPostponedEvents(dpid);
274 } else {
275 // Eventually remove from the map
276 OnosInstanceId oldId = switchMastership.get(dpid);
277 if (onosInstanceId.equals(oldId)) {
278 switchMastership.remove(dpid);
279 }
280 }
281 }
282
283 //
284 // Process the event and eventually store it in the
285 // per-Instance state.
286 //
287 if (instance.processEvent(event)) {
288 result.add(event);
289 }
290
291 // Add the postponed events (if any)
292 if (postponedEvents != null) {
293 result.addAll(postponedEvents);
294 }
295 }
296
297 // Extract and add the previously reordered events
298 result.addAll(extractReorderedEvents());
299
300 return reorderEventsForTopology(result);
301 }
302
303 /**
304 * Classifies and reorders a list of events, and suppresses matching
305 * events.
306 * <p/>
307 * The result events can be applied to the Topology in the following
308 * order: REMOVE events followed by ADD events. The ADD events are in the
309 * natural order to build a Topology: MastershipEvent, SwitchEvent,
310 * PortEvent, LinkEvent, HostEvent. The REMOVE events are in the reverse
311 * order.
312 *
313 * @param events the events to classify and reorder.
314 * @return the classified and reordered events.
315 */
316 private List<EventEntry<TopologyEvent>> reorderEventsForTopology(
317 List<EventEntry<TopologyEvent>> events) {
318 // Local state for computing the final set of events
319 Map<ByteBuffer, EventEntry<TopologyEvent>> addedMastershipEvents =
320 new HashMap<>();
321 Map<ByteBuffer, EventEntry<TopologyEvent>> removedMastershipEvents =
322 new HashMap<>();
323 Map<ByteBuffer, EventEntry<TopologyEvent>> addedSwitchEvents =
324 new HashMap<>();
325 Map<ByteBuffer, EventEntry<TopologyEvent>> removedSwitchEvents =
326 new HashMap<>();
327 Map<ByteBuffer, EventEntry<TopologyEvent>> addedPortEvents =
328 new HashMap<>();
329 Map<ByteBuffer, EventEntry<TopologyEvent>> removedPortEvents =
330 new HashMap<>();
331 Map<ByteBuffer, EventEntry<TopologyEvent>> addedLinkEvents =
332 new HashMap<>();
333 Map<ByteBuffer, EventEntry<TopologyEvent>> removedLinkEvents =
334 new HashMap<>();
335 Map<ByteBuffer, EventEntry<TopologyEvent>> addedHostEvents =
336 new HashMap<>();
337 Map<ByteBuffer, EventEntry<TopologyEvent>> removedHostEvents =
338 new HashMap<>();
339
340 //
341 // Classify and suppress matching events
342 //
343 // NOTE: We intentionally use the event payload as the key ID
344 // (i.e., we exclude the ONOS Instance ID from the key),
345 // so we can suppress transient events across multiple ONOS instances.
346 //
347 for (EventEntry<TopologyEvent> event : events) {
348 TopologyEvent topologyEvent = event.eventData();
349
350 // Get the event itself
351 MastershipEvent mastershipEvent =
352 topologyEvent.getMastershipEvent();
353 SwitchEvent switchEvent = topologyEvent.getSwitchEvent();
354 PortEvent portEvent = topologyEvent.getPortEvent();
355 LinkEvent linkEvent = topologyEvent.getLinkEvent();
356 HostEvent hostEvent = topologyEvent.getHostEvent();
357
358 //
359 // Extract the events
360 //
361 switch (event.eventType()) {
362 case ENTRY_ADD:
363 if (mastershipEvent != null) {
364 ByteBuffer id = mastershipEvent.getIDasByteBuffer();
365 addedMastershipEvents.put(id, event);
366 removedMastershipEvents.remove(id);
367 }
368 if (switchEvent != null) {
369 ByteBuffer id = switchEvent.getIDasByteBuffer();
370 addedSwitchEvents.put(id, event);
371 removedSwitchEvents.remove(id);
372 }
373 if (portEvent != null) {
374 ByteBuffer id = portEvent.getIDasByteBuffer();
375 addedPortEvents.put(id, event);
376 removedPortEvents.remove(id);
377 }
378 if (linkEvent != null) {
379 ByteBuffer id = linkEvent.getIDasByteBuffer();
380 addedLinkEvents.put(id, event);
381 removedLinkEvents.remove(id);
382 }
383 if (hostEvent != null) {
384 ByteBuffer id = hostEvent.getIDasByteBuffer();
385 addedHostEvents.put(id, event);
386 removedHostEvents.remove(id);
387 }
388 break;
389 case ENTRY_REMOVE:
390 if (mastershipEvent != null) {
391 ByteBuffer id = mastershipEvent.getIDasByteBuffer();
392 addedMastershipEvents.remove(id);
393 removedMastershipEvents.put(id, event);
394 }
395 if (switchEvent != null) {
396 ByteBuffer id = switchEvent.getIDasByteBuffer();
397 addedSwitchEvents.remove(id);
398 removedSwitchEvents.put(id, event);
399 }
400 if (portEvent != null) {
401 ByteBuffer id = portEvent.getIDasByteBuffer();
402 addedPortEvents.remove(id);
403 removedPortEvents.put(id, event);
404 }
405 if (linkEvent != null) {
406 ByteBuffer id = linkEvent.getIDasByteBuffer();
407 addedLinkEvents.remove(id);
408 removedLinkEvents.put(id, event);
409 }
410 if (hostEvent != null) {
411 ByteBuffer id = hostEvent.getIDasByteBuffer();
412 addedHostEvents.remove(id);
413 removedHostEvents.put(id, event);
414 }
415 break;
416 default:
417 log.error("Unknown topology event {}", event.eventType());
418 }
419 }
420
421 //
422 // Prepare the result by adding the events in the appropriate order:
423 // - First REMOVE, then ADD
424 // - The REMOVE order is: Host, Link, Port, Switch, Mastership
425 // - The ADD order is the reverse: Mastership, Switch, Port, Link,
426 // Host
427 //
428 List<EventEntry<TopologyEvent>> result = new LinkedList<>();
429 result.addAll(removedHostEvents.values());
430 result.addAll(removedLinkEvents.values());
431 result.addAll(removedPortEvents.values());
432 result.addAll(removedSwitchEvents.values());
433 result.addAll(removedMastershipEvents.values());
434 //
435 result.addAll(addedMastershipEvents.values());
436 result.addAll(addedSwitchEvents.values());
437 result.addAll(addedPortEvents.values());
438 result.addAll(addedLinkEvents.values());
439 result.addAll(addedHostEvents.values());
440
441 return result;
442 }
443}