blob: 69d9b070f527361c56dde677bb1d0f6a88c413e4 [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
220 TopologyEvent topologyEvent = event.eventData();
221 OnosInstanceId onosInstanceId = topologyEvent.getOnosInstanceId();
222
223 log.debug("Topology event {}: {}", event.eventType(),
224 topologyEvent);
225
226 // Get the ONOS instance state
227 OnosInstanceLastAddEvents instance =
228 instanceState.get(onosInstanceId);
229 if (instance == null) {
230 instance = new OnosInstanceLastAddEvents(onosInstanceId);
231 instanceState.put(onosInstanceId, instance);
232 }
233
234 //
235 // Update the Switch Mastership state:
236 // - If ADD a MASTER and the Mastership is confirmed by the
237 // Registry Service, then add to the Mastership map and fetch
238 // the postponed events from the originating ONOS Instance.
239 // - Otherwise, remove from the Mastership map, but only if it is
240 // the current MASTER.
241 //
242 MastershipEvent mastershipEvent =
243 topologyEvent.getMastershipEvent();
244 if (mastershipEvent != null) {
245 Dpid dpid = mastershipEvent.getDpid();
246 boolean newMaster = false;
247
248 if ((event.eventType() == EventEntry.Type.ENTRY_ADD) &&
249 (mastershipEvent.getRole() == Role.MASTER)) {
250 //
251 // Check with the Registry Service as well
252 //
253 try {
254 String rc =
255 registryService.getControllerForSwitch(dpid.value());
256 if ((rc != null) &&
257 onosInstanceId.equals(new OnosInstanceId(rc))) {
258 newMaster = true;
259 }
260 } catch (RegistryException e) {
261 log.error("Caught RegistryException while pre-processing Mastership Event", e);
262 }
263 }
264
265 if (newMaster) {
266 // Add to the map
267 switchMastership.put(dpid, onosInstanceId);
268 postponedEvents = instance.getPostponedEvents(dpid);
269 } else {
270 // Eventually remove from the map
271 OnosInstanceId oldId = switchMastership.get(dpid);
272 if (onosInstanceId.equals(oldId)) {
273 switchMastership.remove(dpid);
274 }
275 }
276 }
277
278 //
279 // Process the event and eventually store it in the
280 // per-Instance state.
281 //
282 if (instance.processEvent(event)) {
283 result.add(event);
284 }
285
286 // Add the postponed events (if any)
287 if (postponedEvents != null) {
288 result.addAll(postponedEvents);
289 }
290 }
291
292 // Extract and add the previously reordered events
293 result.addAll(extractReorderedEvents());
294
295 return reorderEventsForTopology(result);
296 }
297
298 /**
299 * Classifies and reorders a list of events, and suppresses matching
300 * events.
301 * <p/>
302 * The result events can be applied to the Topology in the following
303 * order: REMOVE events followed by ADD events. The ADD events are in the
304 * natural order to build a Topology: MastershipEvent, SwitchEvent,
305 * PortEvent, LinkEvent, HostEvent. The REMOVE events are in the reverse
306 * order.
307 *
308 * @param events the events to classify and reorder.
309 * @return the classified and reordered events.
310 */
311 private List<EventEntry<TopologyEvent>> reorderEventsForTopology(
312 List<EventEntry<TopologyEvent>> events) {
313 // Local state for computing the final set of events
314 Map<ByteBuffer, EventEntry<TopologyEvent>> addedMastershipEvents =
315 new HashMap<>();
316 Map<ByteBuffer, EventEntry<TopologyEvent>> removedMastershipEvents =
317 new HashMap<>();
318 Map<ByteBuffer, EventEntry<TopologyEvent>> addedSwitchEvents =
319 new HashMap<>();
320 Map<ByteBuffer, EventEntry<TopologyEvent>> removedSwitchEvents =
321 new HashMap<>();
322 Map<ByteBuffer, EventEntry<TopologyEvent>> addedPortEvents =
323 new HashMap<>();
324 Map<ByteBuffer, EventEntry<TopologyEvent>> removedPortEvents =
325 new HashMap<>();
326 Map<ByteBuffer, EventEntry<TopologyEvent>> addedLinkEvents =
327 new HashMap<>();
328 Map<ByteBuffer, EventEntry<TopologyEvent>> removedLinkEvents =
329 new HashMap<>();
330 Map<ByteBuffer, EventEntry<TopologyEvent>> addedHostEvents =
331 new HashMap<>();
332 Map<ByteBuffer, EventEntry<TopologyEvent>> removedHostEvents =
333 new HashMap<>();
334
335 //
336 // Classify and suppress matching events
337 //
338 // NOTE: We intentionally use the event payload as the key ID
339 // (i.e., we exclude the ONOS Instance ID from the key),
340 // so we can suppress transient events across multiple ONOS instances.
341 //
342 for (EventEntry<TopologyEvent> event : events) {
343 TopologyEvent topologyEvent = event.eventData();
344
345 // Get the event itself
346 MastershipEvent mastershipEvent =
347 topologyEvent.getMastershipEvent();
348 SwitchEvent switchEvent = topologyEvent.getSwitchEvent();
349 PortEvent portEvent = topologyEvent.getPortEvent();
350 LinkEvent linkEvent = topologyEvent.getLinkEvent();
351 HostEvent hostEvent = topologyEvent.getHostEvent();
352
353 //
354 // Extract the events
355 //
356 switch (event.eventType()) {
357 case ENTRY_ADD:
358 if (mastershipEvent != null) {
359 ByteBuffer id = mastershipEvent.getIDasByteBuffer();
360 addedMastershipEvents.put(id, event);
361 removedMastershipEvents.remove(id);
362 }
363 if (switchEvent != null) {
364 ByteBuffer id = switchEvent.getIDasByteBuffer();
365 addedSwitchEvents.put(id, event);
366 removedSwitchEvents.remove(id);
367 }
368 if (portEvent != null) {
369 ByteBuffer id = portEvent.getIDasByteBuffer();
370 addedPortEvents.put(id, event);
371 removedPortEvents.remove(id);
372 }
373 if (linkEvent != null) {
374 ByteBuffer id = linkEvent.getIDasByteBuffer();
375 addedLinkEvents.put(id, event);
376 removedLinkEvents.remove(id);
377 }
378 if (hostEvent != null) {
379 ByteBuffer id = hostEvent.getIDasByteBuffer();
380 addedHostEvents.put(id, event);
381 removedHostEvents.remove(id);
382 }
383 break;
384 case ENTRY_REMOVE:
385 if (mastershipEvent != null) {
386 ByteBuffer id = mastershipEvent.getIDasByteBuffer();
387 addedMastershipEvents.remove(id);
388 removedMastershipEvents.put(id, event);
389 }
390 if (switchEvent != null) {
391 ByteBuffer id = switchEvent.getIDasByteBuffer();
392 addedSwitchEvents.remove(id);
393 removedSwitchEvents.put(id, event);
394 }
395 if (portEvent != null) {
396 ByteBuffer id = portEvent.getIDasByteBuffer();
397 addedPortEvents.remove(id);
398 removedPortEvents.put(id, event);
399 }
400 if (linkEvent != null) {
401 ByteBuffer id = linkEvent.getIDasByteBuffer();
402 addedLinkEvents.remove(id);
403 removedLinkEvents.put(id, event);
404 }
405 if (hostEvent != null) {
406 ByteBuffer id = hostEvent.getIDasByteBuffer();
407 addedHostEvents.remove(id);
408 removedHostEvents.put(id, event);
409 }
410 break;
411 default:
412 log.error("Unknown topology event {}", event.eventType());
413 }
414 }
415
416 //
417 // Prepare the result by adding the events in the appropriate order:
418 // - First REMOVE, then ADD
419 // - The REMOVE order is: Host, Link, Port, Switch, Mastership
420 // - The ADD order is the reverse: Mastership, Switch, Port, Link,
421 // Host
422 //
423 List<EventEntry<TopologyEvent>> result = new LinkedList<>();
424 result.addAll(removedHostEvents.values());
425 result.addAll(removedLinkEvents.values());
426 result.addAll(removedPortEvents.values());
427 result.addAll(removedSwitchEvents.values());
428 result.addAll(removedMastershipEvents.values());
429 //
430 result.addAll(addedMastershipEvents.values());
431 result.addAll(addedSwitchEvents.values());
432 result.addAll(addedPortEvents.values());
433 result.addAll(addedLinkEvents.values());
434 result.addAll(addedHostEvents.values());
435
436 return result;
437 }
438}