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