blob: ebdde8f55484d047efac4bab2a7424bc1bd9a227 [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 */
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700218 private List<EventEntry<TopologyEvent>> processMastershipData(
Pavlin Radoslavovae7e8482014-08-11 16:58:19 -0700219 OnosInstanceLastAddEvents instance,
220 EventEntry<TopologyEvent> event) {
221 TopologyEvent topologyEvent = event.eventData();
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700222 MastershipData mastershipData = topologyEvent.getMastershipData();
Pavlin Radoslavovae7e8482014-08-11 16:58:19 -0700223
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700224 if (mastershipData == null) {
Pavlin Radoslavovae7e8482014-08-11 16:58:19 -0700225 return Collections.emptyList(); // Not a Mastership Event
226 }
227
228 OnosInstanceId onosInstanceId = topologyEvent.getOnosInstanceId();
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700229 Dpid dpid = mastershipData.getDpid();
Pavlin Radoslavovae7e8482014-08-11 16:58:19 -0700230 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) &&
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700242 (mastershipData.getRole() == Role.MASTER)) {
Pavlin Radoslavovae7e8482014-08-11 16:58:19 -0700243
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
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700316 postponedEvents = processMastershipData(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
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700342 * natural order to build a Topology: MastershipData, SwitchData,
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700343 * PortData, LinkData, HostData. The REMOVE events are in the reverse
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700344 * 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
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700352 Map<ByteBuffer, EventEntry<TopologyEvent>> addedMastershipDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700353 new HashMap<>();
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700354 Map<ByteBuffer, EventEntry<TopologyEvent>> removedMastershipDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700355 new HashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700356 Map<ByteBuffer, EventEntry<TopologyEvent>> addedSwitchDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700357 new HashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700358 Map<ByteBuffer, EventEntry<TopologyEvent>> removedSwitchDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700359 new HashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700360 Map<ByteBuffer, EventEntry<TopologyEvent>> addedPortDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700361 new HashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700362 Map<ByteBuffer, EventEntry<TopologyEvent>> removedPortDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700363 new HashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700364 Map<ByteBuffer, EventEntry<TopologyEvent>> addedLinkDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700365 new HashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700366 Map<ByteBuffer, EventEntry<TopologyEvent>> removedLinkDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700367 new HashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700368 Map<ByteBuffer, EventEntry<TopologyEvent>> addedHostDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700369 new HashMap<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700370 Map<ByteBuffer, EventEntry<TopologyEvent>> removedHostDataEntries =
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700371 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
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700384 MastershipData mastershipData =
385 topologyEvent.getMastershipData();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700386 SwitchData switchData = topologyEvent.getSwitchData();
387 PortData portData = topologyEvent.getPortData();
388 LinkData linkData = topologyEvent.getLinkData();
389 HostData hostData = topologyEvent.getHostData();
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700390
391 //
392 // Extract the events
393 //
394 switch (event.eventType()) {
395 case ENTRY_ADD:
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700396 if (mastershipData != null) {
397 ByteBuffer id = mastershipData.getIDasByteBuffer();
398 addedMastershipDataEntries.put(id, event);
399 removedMastershipDataEntries.remove(id);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700400 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700401 if (switchData != null) {
402 ByteBuffer id = switchData.getIDasByteBuffer();
403 addedSwitchDataEntries.put(id, event);
404 removedSwitchDataEntries.remove(id);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700405 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700406 if (portData != null) {
407 ByteBuffer id = portData.getIDasByteBuffer();
408 addedPortDataEntries.put(id, event);
409 removedPortDataEntries.remove(id);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700410 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700411 if (linkData != null) {
412 ByteBuffer id = linkData.getIDasByteBuffer();
413 addedLinkDataEntries.put(id, event);
414 removedLinkDataEntries.remove(id);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700415 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700416 if (hostData != null) {
417 ByteBuffer id = hostData.getIDasByteBuffer();
418 addedHostDataEntries.put(id, event);
419 removedHostDataEntries.remove(id);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700420 }
421 break;
422 case ENTRY_REMOVE:
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700423 if (mastershipData != null) {
424 ByteBuffer id = mastershipData.getIDasByteBuffer();
425 addedMastershipDataEntries.remove(id);
426 removedMastershipDataEntries.put(id, event);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700427 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700428 if (switchData != null) {
429 ByteBuffer id = switchData.getIDasByteBuffer();
430 addedSwitchDataEntries.remove(id);
431 removedSwitchDataEntries.put(id, event);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700432 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700433 if (portData != null) {
434 ByteBuffer id = portData.getIDasByteBuffer();
435 addedPortDataEntries.remove(id);
436 removedPortDataEntries.put(id, event);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700437 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700438 if (linkData != null) {
439 ByteBuffer id = linkData.getIDasByteBuffer();
440 addedLinkDataEntries.remove(id);
441 removedLinkDataEntries.put(id, event);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700442 }
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700443 if (hostData != null) {
444 ByteBuffer id = hostData.getIDasByteBuffer();
445 addedHostDataEntries.remove(id);
446 removedHostDataEntries.put(id, event);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700447 }
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<>();
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700462 result.addAll(removedHostDataEntries.values());
463 result.addAll(removedLinkDataEntries.values());
464 result.addAll(removedPortDataEntries.values());
465 result.addAll(removedSwitchDataEntries.values());
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700466 result.addAll(removedMastershipDataEntries.values());
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700467 //
Yuta HIGUCHId8fd2f52014-09-01 23:19:45 -0700468 result.addAll(addedMastershipDataEntries.values());
Yuta HIGUCHI93d35ea2014-08-31 23:26:13 -0700469 result.addAll(addedSwitchDataEntries.values());
470 result.addAll(addedPortDataEntries.values());
471 result.addAll(addedLinkDataEntries.values());
472 result.addAll(addedHostDataEntries.values());
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700473
474 return result;
475 }
476}